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