1 // SPDX-License-Identifier: Apache-2.0
2 // ----------------------------------------------------------------------------
3 // Copyright 2011-2021 Arm Limited
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 // use this file except in compliance with the License. You may obtain a copy
7 // of the License at:
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 // License for the specific language governing permissions and limitations
15 // under the License.
16 // ----------------------------------------------------------------------------
17
18 /**
19 * @brief Platform-specific function implementations.
20 *
21 * This module contains functions with strongly OS-dependent implementations:
22 *
23 * * CPU count queries
24 * * Threading
25 * * Time
26 *
27 * In addition to the basic thread abstraction (which is native pthreads on
28 * all platforms, except Windows where it is an emulation of pthreads), a
29 * utility function to create N threads and wait for them to complete a batch
30 * task has also been provided.
31 */
32
33 #include "astcenccli_internal.h"
34
35 /* ============================================================================
36 Platform code for Windows using the Win32 APIs.
37 ============================================================================ */
38 #if defined(_WIN32) && !defined(__CYGWIN__)
39
40 #define WIN32_LEAN_AND_MEAN
41 #include <Windows.h>
42
43 /** @brief Alias pthread_t to one of the internal Windows types. */
44 typedef HANDLE pthread_t;
45
46 /** @brief Alias pthread_attr_t to one of the internal Windows types. */
47 typedef int pthread_attr_t;
48
49 /**
50 * @brief Proxy Windows @c CreateThread underneath a pthreads-like wrapper.
51 */
pthread_create(pthread_t * thread,const pthread_attr_t * attribs,void * (* threadfunc)(void *),void * thread_arg)52 static int pthread_create(
53 pthread_t* thread,
54 const pthread_attr_t* attribs,
55 void* (*threadfunc)(void*),
56 void* thread_arg
57 ) {
58 static_cast<void>(attribs);
59 LPTHREAD_START_ROUTINE func = reinterpret_cast<LPTHREAD_START_ROUTINE>(threadfunc);
60 *thread = CreateThread(nullptr, 0, func, thread_arg, 0, nullptr);
61 return 0;
62 }
63
64 /**
65 * @brief Proxy Windows @c WaitForSingleObject underneath a pthreads-like wrapper.
66 */
pthread_join(pthread_t thread,void ** value)67 static int pthread_join(
68 pthread_t thread,
69 void** value
70 ) {
71 static_cast<void>(value);
72 WaitForSingleObject(thread, INFINITE);
73 return 0;
74 }
75
76 /* See header for documentation */
get_cpu_count()77 int get_cpu_count()
78 {
79 SYSTEM_INFO sysinfo;
80 GetSystemInfo(&sysinfo);
81 return sysinfo.dwNumberOfProcessors;
82 }
83
84 /* See header for documentation */
get_time()85 double get_time()
86 {
87 FILETIME tv;
88 GetSystemTimePreciseAsFileTime(&tv);
89 unsigned long long ticks = tv.dwHighDateTime;
90 ticks = (ticks << 32) | tv.dwLowDateTime;
91 return static_cast<double>(ticks) / 1.0e7;
92 }
93
94 /* ============================================================================
95 Platform code for an platform using POSIX APIs.
96 ============================================================================ */
97 #else
98
99 #include <pthread.h>
100 #include <sys/time.h>
101 #include <unistd.h>
102
103 /* See header for documentation */
get_cpu_count()104 int get_cpu_count()
105 {
106 return static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN));
107 }
108
109 /* See header for documentation */
get_time()110 double get_time()
111 {
112 timeval tv;
113 gettimeofday(&tv, 0);
114 return static_cast<double>(tv.tv_sec) + static_cast<double>(tv.tv_usec) * 1.0e-6;
115 }
116
117 #endif
118
119 /**
120 * @brief Worker thread helper payload for launch_threads.
121 */
122 struct launch_desc
123 {
124 /** @brief The native thread handle. */
125 pthread_t thread_handle;
126 /** @brief The total number of threads in the thread pool. */
127 int thread_count;
128 /** @brief The thread index in the thread pool. */
129 int thread_id;
130 /** @brief The user thread function to execute. */
131 void (*func)(int, int, void*);
132 /** @brief The user thread payload. */
133 void* payload;
134 };
135
136 /**
137 * @brief Helper function to translate thread entry points.
138 *
139 * Convert a (void*) thread entry to an (int, void*) thread entry, where the
140 * integer contains the thread ID in the thread pool.
141 *
142 * @param p The thread launch helper payload.
143 */
launch_threads_helper(void * p)144 static void* launch_threads_helper(
145 void *p
146 ) {
147 launch_desc* ltd = reinterpret_cast<launch_desc*>(p);
148 ltd->func(ltd->thread_count, ltd->thread_id, ltd->payload);
149 return nullptr;
150 }
151
152 /* See header for documentation */
launch_threads(int thread_count,void (* func)(int,int,void *),void * payload)153 void launch_threads(
154 int thread_count,
155 void (*func)(int, int, void*),
156 void *payload
157 ) {
158 // Directly execute single threaded workloads on this thread
159 if (thread_count <= 1)
160 {
161 func(1, 0, payload);
162 return;
163 }
164
165 // Otherwise spawn worker threads
166 launch_desc *thread_descs = new launch_desc[thread_count];
167 for (int i = 0; i < thread_count; i++)
168 {
169 thread_descs[i].thread_count = thread_count;
170 thread_descs[i].thread_id = i;
171 thread_descs[i].payload = payload;
172 thread_descs[i].func = func;
173
174 pthread_create(&(thread_descs[i].thread_handle), nullptr,
175 launch_threads_helper, reinterpret_cast<void*>(thread_descs + i));
176 }
177
178 // ... and then wait for them to complete
179 for (int i = 0; i < thread_count; i++)
180 {
181 pthread_join(thread_descs[i].thread_handle, nullptr);
182 }
183
184 delete[] thread_descs;
185 }
186