Improve handling variables, which can be either string or string array.
[build.git] / src / thread.c
blobbed47a0d98f8a6bf9c2a5d6904b358a3e8bcf88b
1 /* thread.c -- system-independent thread types and functions.
3 Copyright (C) 2022 Sergey Sushilin <sergeysushilin@protonmail.com>
5 This file is part of Build.
7 Build is free software: you can redistribute it and/or
8 modify it under the terms of either the GNU General Public License
9 as published by the Free Software Foundation;
10 either version 2 of the License, or version 3 of the License,
11 or both in parallel, as here.
13 Build is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received copies of the GNU General Public License
19 version 2 and 3 along with this program.
20 If not, see http://www.gnu.org/licenses/. */
22 #ifndef _WIN32
23 # include <errno.h>
24 # include <string.h>
25 #endif
27 #include "diagnostic.h"
28 #include "thread.h"
29 #include "uniqstr.h"
30 #include "utils.h"
32 #ifdef _WIN32
33 static DWORD __stdcall
34 win32_worker (LPVOID p)
36 struct thread *thread = p;
37 thread->result = thread->routine (thread->argument);
38 return 0;
40 #endif
42 bool
43 thread_create (struct thread *thread, thread_routine_t routine, void *argument)
45 #ifdef _WIN32
46 thread->routine = routine;
47 thread->argument = argument;
48 thread->result = NULL;
49 thread->thread = CreateThread (NULL, /* Default security attributes. */
50 0, /* Default stack size. */
51 win32_worker, /* Thread function. */
52 thread, /* Thread function arguments. */
53 0, /* Default creation flags. */
54 NULL); /* Receive thread identifier. */
56 if (UNLIKELY (thread->thread == NULL))
58 error (_("failed to create thread: %s"), GetLastErrorString ());
59 return false;
62 return true;
63 #else
64 if (UNLIKELY (pthread_create (&thread->thread, NULL, routine, argument) != 0))
66 error (_("failed to create thread: %s"), strerror (errno));
67 return false;
70 return true;
71 #endif
74 bool
75 thread_join (struct thread *thread, void **result)
77 #ifdef _WIN32
78 if (UNLIKELY (WaitForSingleObjectEx (thread->thread, INFINITE, FALSE) != WAIT_OBJECT_0))
80 error (_("failed to join thread: %s"), GetLastErrorString ());
81 return false;
84 CloseHandle (thread->thread);
86 if (result != NULL)
87 *result = thread->result;
89 return true;
90 #else
91 if (UNLIKELY (pthread_join (thread->thread, result) != 0))
93 error (_("failed to join thread: %s"), strerror (errno));
94 return false;
97 return true;
98 #endif
101 void
102 mutex_create (struct mutex *mutex)
104 #ifdef _WIN32
105 InitializeSRWLock (&mutex->mutex);
106 #else
107 if (UNLIKELY (pthread_mutex_init (&mutex->mutex, NULL) != 0))
108 fatal (_("failed to create mutex: %s"), strerror (errno));
109 #endif
112 void
113 mutex_acquire (struct mutex *mutex)
115 #ifdef _WIN32
116 AcquireSRWLockExclusive (&mutex->mutex);
117 #else
118 if (UNLIKELY (pthread_mutex_lock (&mutex->mutex) != 0))
119 fatal (_("failed to acquire mutex: %s"), strerror (errno));
120 #endif
123 void
124 mutex_release (struct mutex *mutex)
126 #ifdef _WIN32
127 ReleaseSRWLockExclusive (&mutex->mutex);
128 #else
129 if (UNLIKELY (pthread_mutex_unlock (&mutex->mutex) != 0))
130 fatal (_("failed to release mutex: %s"), strerror (errno));
131 #endif
134 void
135 mutex_destroy (UNUSED struct mutex *mutex)
137 #ifdef _WIN32
138 /* Unlocked SWR locks use no resources. */
139 #else
140 pthread_mutex_destroy (&mutex->mutex);
141 #endif
144 #if USE_OWN_SEMAPHORE
145 static inline void
146 condition_variable_create (struct condition_variable *condition_variable)
148 #ifdef _WIN32
149 InitializeConditionVariable (&condition_variable->condition_variable);
150 #else
151 if (UNLIKELY (pthread_cond_init (&condition_variable->condition_variable, NULL) != 0))
152 fatal (_("failed to create condition variable: %s"), strerror (errno));
153 #endif
156 static inline void
157 condition_variable_wait (struct condition_variable *condition_variable, struct mutex *mutex)
159 #ifdef _WIN32
160 if (UNLIKELY (!SleepConditionVariableSRW (&condition_variable->condition_variable, &mutex->mutex,
161 INFINITE, 0)))
162 fatal (_("failed to wait condition variable: %s"), GetLastErrorString ());
163 #else
164 if (UNLIKELY (pthread_cond_wait (&condition_variable->condition_variable, &mutex->mutex) != 0))
165 fatal (_("failed to wait condition variable: %s"), strerror (errno));
166 #endif
169 static inline void
170 condition_variable_wake (struct condition_variable *condition_variable)
172 #ifdef _WIN32
173 WakeConditionVariable (&condition_variable->condition_variable);
174 #else
175 if (UNLIKELY (pthread_cond_signal (&condition_variable->condition_variable) != 0))
176 fatal (_("failed to signal condition variable: %s"), strerror (errno));
177 #endif
180 static inline void
181 condition_variable_wake_all (struct condition_variable *condition_variable)
183 #ifdef _WIN32
184 WakeAllConditionVariable (&condition_variable->condition_variable);
185 #else
186 if (UNLIKELY (pthread_cond_broadcast (&condition_variable->condition_variable) != 0))
187 fatal (_("failed to signal condition variable: %s"), strerror (errno));
188 #endif
191 static inline void
192 condition_variable_destroy (struct condition_variable *condition_variable)
194 #ifdef _WIN32
195 /* No resources to be released. */
196 #else
197 if (UNLIKELY (pthread_cond_destroy (&condition_variable->condition_variable) != 0))
198 fatal (_("failed to destroy condition variable: %s"), strerror (errno));
199 #endif
203 // Implementation of the semaphore taken from here:
204 // https://stackoverflow.com/a/6002408
206 struct semaphore
208 uint64_t count;
209 uint64_t wait_count;
210 struct mutex mutex;
211 struct condition_variable condition_variable;
214 void
215 semaphore_create (struct semaphore *semaphore, uint64_t initial_value)
217 semaphore->count = initial_value;
218 semaphore->wait_count = 0;
219 mutex_create (&semaphore->mutex);
220 condition_variable_create (&semaphore->condition_variable);
223 void
224 semaphore_wait (struct semaphore *semaphore)
226 mutex_acquire (&semaphore->mutex);
228 while (semaphore->count == 0)
230 semaphore->wait_count++;
231 condition_variable_wait (&semaphore->condition_variable, &semaphore->mutex);
233 // A call to wait() unlocks the mutex and suspends the thread.
234 // It does not busy wait the thread is suspended.
236 // When a condition variable receives a condition_variable_wake() a random thread
237 // is un-suspended. But it is not released from the call to wait
238 // until the mutex can be reacquired by the thread.
240 // Thus we get here only after the mutex has been locked.
242 // You need to use a while loop above because of this potential situation.
243 // Thread A: Suspended waiting on condition variable.
244 // Thread B: Working somewhere else.
245 // Thread C: calls signal() below (incrementing count to 1)
246 // This results in A being awakened but it can not exit wait()
247 // until it requires the mutex with a lock. While it tries to
248 // do that thread B finishes what it was doing and calls wait()
249 // Thread C has incremented the count to 1 so thread B does not
250 // suspend but decrements the count to zero and exits.
251 // Thread B now aquires the mutex but the count has been decremented to
252 // zero so it must immediately re-suspend on the condition variable.
254 // Note a thread will not be released from wait until
255 // it receives a signal and the mustex lock can be re-established.
257 semaphore->wait_count--;
260 semaphore->count--;
261 mutex_release (&semaphore->mutex);
264 void
265 semaphore_post (struct semaphore *semaphore)
267 // You could optimize this part with interlocked increment.
268 mutex_acquire (&semaphore->mutex);
269 semaphore->count++;
271 // This Comment based on using `interlocked increment` rather than mutex.
273 // As this part does not modify anything you do not actually need the lock.
274 // Potentially this will release more threads than you need (as you do not
275 // have exclusivity on reading 'wait_count' but that will not matter as the
276 // wait() method does and any extra woken threads will be put back to sleep.
278 // If there are any waiting threads let them out.
279 if (semaphore->wait_count != 0)
280 condition_variable_wake (&semaphore->condition_variable);
282 mutex_release (&semaphore->mutex);
285 /* Unconditionally wake up all waiter to do something. */
286 void
287 semaphore_post_all (struct semaphore *semaphore)
289 mutex_acquire (&semaphore->mutex);
290 semaphore->count += semaphore->wait_count;
291 condition_variable_wake_all (&semaphore->condition_variable);
292 mutex_release (&semaphore->mutex);
295 void
296 semaphore_destroy (struct semaphore *semaphore)
298 mutex_destroy (&semaphore->mutex);
299 condition_variable_destroy (&semaphore->condition_variable);
301 #else
302 void
303 semaphore_create (struct semaphore *semaphore, size_t initial_value)
305 #ifdef _WIN32
306 semaphore->semaphore = CreateSemaphoreA (NULL, (LONG) initial_value, real_maximum, NULL);
308 if (UNLIKELY (semaphore->semaphore == NULL))
309 fatal (_("failed to create semaphore: %s"), GetLastErrorString ());
310 #else
311 if (UNLIKELY (sem_init (&semaphore->semaphore, 0 /* Not shared. */, (unsigned int) initial_value) != 0))
312 fatal (_("failed to create semaphore: %s"), strerror (errno));
313 #endif
316 void
317 semaphore_wait (struct semaphore *semaphore)
319 #ifdef _WIN32
320 if (UNLIKELY (WaitForSingleObject (semaphore->semaphore, INFINITE) != WAIT_OBJECT_0))
321 fatal (_("failed to wait semaphore: %s"), GetLastErrorString ());
322 #else
323 while (UNLIKELY (sem_wait (&semaphore->semaphore) < 0))
324 if (UNLIKELY (errno != EINTR))
325 fatal (_("failed to wait semaphore: %s"), strerror (errno));
326 #endif
329 void
330 semaphore_post (struct semaphore *semaphore)
332 #ifdef _WIN32
333 if (UNLIKELY (!ReleaseSemaphore (semaphore->semaphore, 1, NULL)))
334 fatal (_("failed to post semaphore: %s"), GetLastErrorString ());
335 #else
336 if (UNLIKELY (sem_post (&semaphore->semaphore) < 0))
337 fatal (_("failed to post semaphore: %s"), strerror (errno));
338 #endif
341 void
342 semaphore_destroy (struct semaphore *semaphore)
344 #ifdef _WIN32
345 CloseHandle (semaphore->semaphore);
346 #else
347 if (UNLIKELY (sem_destroy (&semaphore->semaphore) < 0))
348 fatal (_("failed to destroy semaphore: %s"), strerror (errno));
349 #endif
351 #endif
353 void
354 rwlock_create (struct rwlock *rwlock)
356 #ifdef _WIN32
357 InitializeSRWLock (&rwlock->rwlock);
358 #else
359 if (UNLIKELY (pthread_rwlock_init (&rwlock->rwlock, NULL) != 0))
360 fatal (_("failed to create read-write lock: %s"), strerror (errno));
361 #endif
364 void
365 rwlock_read_acquire (struct rwlock *rwlock)
367 #ifdef _WIN32
368 AcquireSRWLockShared (&rwlock->rwlock);
369 #else
370 if (UNLIKELY (pthread_rwlock_rdlock (&rwlock->rwlock) != 0))
371 fatal (_("failed to acquire for reading read-write lock: %s"), strerror (errno));
372 #endif
375 void
376 rwlock_read_release (struct rwlock *rwlock)
378 #ifdef _WIN32
379 ReleaseSRWLockShared (&rwlock->rwlock);
380 #else
381 if (UNLIKELY (pthread_rwlock_unlock (&rwlock->rwlock) != 0))
382 fatal (_("failed to release read-write lock: %s"), strerror (errno));
383 #endif
386 void
387 rwlock_write_acquire (struct rwlock *rwlock)
389 #ifdef _WIN32
390 AcquireSRWLockExclusive (&rwlock->rwlock);
391 #else
392 if (UNLIKELY (pthread_rwlock_wrlock (&rwlock->rwlock) != 0))
393 fatal (_("failed to acquire for writing read-write lock: %s"), strerror (errno));
394 #endif
397 void
398 rwlock_write_release (struct rwlock *rwlock)
400 #ifdef _WIN32
401 ReleaseSRWLockExclusive (&rwlock->rwlock);
402 #else
403 if (UNLIKELY (pthread_rwlock_unlock (&rwlock->rwlock) != 0))
404 fatal (_("failed to release read-write lock: %s"), strerror (errno));
405 #endif
408 void
409 rwlock_destroy (struct rwlock *rwlock)
411 #ifdef _WIN32
412 return; /* Nothing required. */
413 #else
414 if (UNLIKELY (pthread_rwlock_destroy (&rwlock->rwlock) != 0))
415 fatal (_("failed to destroy read-write lock: %s"), strerror (errno));
416 #endif