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/. */
27 #include "diagnostic.h"
33 static DWORD __stdcall
34 win32_worker (LPVOID p
)
36 struct thread
*thread
= p
;
37 thread
->result
= thread
->routine (thread
->argument
);
43 thread_create (struct thread
*thread
, thread_routine_t routine
, void *argument
)
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 ());
64 if (UNLIKELY (pthread_create (&thread
->thread
, NULL
, routine
, argument
) != 0))
66 error (_("failed to create thread: %s"), strerror (errno
));
75 thread_join (struct thread
*thread
, void **result
)
78 if (UNLIKELY (WaitForSingleObjectEx (thread
->thread
, INFINITE
, FALSE
) != WAIT_OBJECT_0
))
80 error (_("failed to join thread: %s"), GetLastErrorString ());
84 CloseHandle (thread
->thread
);
87 *result
= thread
->result
;
91 if (UNLIKELY (pthread_join (thread
->thread
, result
) != 0))
93 error (_("failed to join thread: %s"), strerror (errno
));
102 mutex_create (struct mutex
*mutex
)
105 InitializeSRWLock (&mutex
->mutex
);
107 if (UNLIKELY (pthread_mutex_init (&mutex
->mutex
, NULL
) != 0))
108 fatal (_("failed to create mutex: %s"), strerror (errno
));
113 mutex_acquire (struct mutex
*mutex
)
116 AcquireSRWLockExclusive (&mutex
->mutex
);
118 if (UNLIKELY (pthread_mutex_lock (&mutex
->mutex
) != 0))
119 fatal (_("failed to acquire mutex: %s"), strerror (errno
));
124 mutex_release (struct mutex
*mutex
)
127 ReleaseSRWLockExclusive (&mutex
->mutex
);
129 if (UNLIKELY (pthread_mutex_unlock (&mutex
->mutex
) != 0))
130 fatal (_("failed to release mutex: %s"), strerror (errno
));
135 mutex_destroy (UNUSED
struct mutex
*mutex
)
138 /* Unlocked SWR locks use no resources. */
140 pthread_mutex_destroy (&mutex
->mutex
);
144 #if USE_OWN_SEMAPHORE
146 condition_variable_create (struct condition_variable
*condition_variable
)
149 InitializeConditionVariable (&condition_variable
->condition_variable
);
151 if (UNLIKELY (pthread_cond_init (&condition_variable
->condition_variable
, NULL
) != 0))
152 fatal (_("failed to create condition variable: %s"), strerror (errno
));
157 condition_variable_wait (struct condition_variable
*condition_variable
, struct mutex
*mutex
)
160 if (UNLIKELY (!SleepConditionVariableSRW (&condition_variable
->condition_variable
, &mutex
->mutex
,
162 fatal (_("failed to wait condition variable: %s"), GetLastErrorString ());
164 if (UNLIKELY (pthread_cond_wait (&condition_variable
->condition_variable
, &mutex
->mutex
) != 0))
165 fatal (_("failed to wait condition variable: %s"), strerror (errno
));
170 condition_variable_wake (struct condition_variable
*condition_variable
)
173 WakeConditionVariable (&condition_variable
->condition_variable
);
175 if (UNLIKELY (pthread_cond_signal (&condition_variable
->condition_variable
) != 0))
176 fatal (_("failed to signal condition variable: %s"), strerror (errno
));
181 condition_variable_wake_all (struct condition_variable
*condition_variable
)
184 WakeAllConditionVariable (&condition_variable
->condition_variable
);
186 if (UNLIKELY (pthread_cond_broadcast (&condition_variable
->condition_variable
) != 0))
187 fatal (_("failed to signal condition variable: %s"), strerror (errno
));
192 condition_variable_destroy (struct condition_variable
*condition_variable
)
195 /* No resources to be released. */
197 if (UNLIKELY (pthread_cond_destroy (&condition_variable
->condition_variable
) != 0))
198 fatal (_("failed to destroy condition variable: %s"), strerror (errno
));
203 // Implementation of the semaphore taken from here:
204 // https://stackoverflow.com/a/6002408
211 struct condition_variable condition_variable;
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
);
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
--;
261 mutex_release (&semaphore
->mutex
);
265 semaphore_post (struct semaphore
*semaphore
)
267 // You could optimize this part with interlocked increment.
268 mutex_acquire (&semaphore
->mutex
);
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. */
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
);
296 semaphore_destroy (struct semaphore
*semaphore
)
298 mutex_destroy (&semaphore
->mutex
);
299 condition_variable_destroy (&semaphore
->condition_variable
);
303 semaphore_create (struct semaphore
*semaphore
, size_t initial_value
)
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 ());
311 if (UNLIKELY (sem_init (&semaphore
->semaphore
, 0 /* Not shared. */, (unsigned int) initial_value
) != 0))
312 fatal (_("failed to create semaphore: %s"), strerror (errno
));
317 semaphore_wait (struct semaphore
*semaphore
)
320 if (UNLIKELY (WaitForSingleObject (semaphore
->semaphore
, INFINITE
) != WAIT_OBJECT_0
))
321 fatal (_("failed to wait semaphore: %s"), GetLastErrorString ());
323 while (UNLIKELY (sem_wait (&semaphore
->semaphore
) < 0))
324 if (UNLIKELY (errno
!= EINTR
))
325 fatal (_("failed to wait semaphore: %s"), strerror (errno
));
330 semaphore_post (struct semaphore
*semaphore
)
333 if (UNLIKELY (!ReleaseSemaphore (semaphore
->semaphore
, 1, NULL
)))
334 fatal (_("failed to post semaphore: %s"), GetLastErrorString ());
336 if (UNLIKELY (sem_post (&semaphore
->semaphore
) < 0))
337 fatal (_("failed to post semaphore: %s"), strerror (errno
));
342 semaphore_destroy (struct semaphore
*semaphore
)
345 CloseHandle (semaphore
->semaphore
);
347 if (UNLIKELY (sem_destroy (&semaphore
->semaphore
) < 0))
348 fatal (_("failed to destroy semaphore: %s"), strerror (errno
));
354 rwlock_create (struct rwlock
*rwlock
)
357 InitializeSRWLock (&rwlock
->rwlock
);
359 if (UNLIKELY (pthread_rwlock_init (&rwlock
->rwlock
, NULL
) != 0))
360 fatal (_("failed to create read-write lock: %s"), strerror (errno
));
365 rwlock_read_acquire (struct rwlock
*rwlock
)
368 AcquireSRWLockShared (&rwlock
->rwlock
);
370 if (UNLIKELY (pthread_rwlock_rdlock (&rwlock
->rwlock
) != 0))
371 fatal (_("failed to acquire for reading read-write lock: %s"), strerror (errno
));
376 rwlock_read_release (struct rwlock
*rwlock
)
379 ReleaseSRWLockShared (&rwlock
->rwlock
);
381 if (UNLIKELY (pthread_rwlock_unlock (&rwlock
->rwlock
) != 0))
382 fatal (_("failed to release read-write lock: %s"), strerror (errno
));
387 rwlock_write_acquire (struct rwlock
*rwlock
)
390 AcquireSRWLockExclusive (&rwlock
->rwlock
);
392 if (UNLIKELY (pthread_rwlock_wrlock (&rwlock
->rwlock
) != 0))
393 fatal (_("failed to acquire for writing read-write lock: %s"), strerror (errno
));
398 rwlock_write_release (struct rwlock
*rwlock
)
401 ReleaseSRWLockExclusive (&rwlock
->rwlock
);
403 if (UNLIKELY (pthread_rwlock_unlock (&rwlock
->rwlock
) != 0))
404 fatal (_("failed to release read-write lock: %s"), strerror (errno
));
409 rwlock_destroy (struct rwlock
*rwlock
)
412 return; /* Nothing required. */
414 if (UNLIKELY (pthread_rwlock_destroy (&rwlock
->rwlock
) != 0))
415 fatal (_("failed to destroy read-write lock: %s"), strerror (errno
));