Path: blob/master/thirdparty/sdl/thread/generic/SDL_syscond.c
10279 views
/*1Simple DirectMedia Layer2Copyright (C) 1997-2025 Sam Lantinga <[email protected]>34This software is provided 'as-is', without any express or implied5warranty. In no event will the authors be held liable for any damages6arising from the use of this software.78Permission is granted to anyone to use this software for any purpose,9including commercial applications, and to alter it and redistribute it10freely, subject to the following restrictions:11121. The origin of this software must not be misrepresented; you must not13claim that you wrote the original software. If you use this software14in a product, an acknowledgment in the product documentation would be15appreciated but is not required.162. Altered source versions must be plainly marked as such, and must not be17misrepresented as being the original software.183. This notice may not be removed or altered from any source distribution.19*/20#include "SDL_internal.h"2122// An implementation of condition variables using semaphores and mutexes23/*24This implementation borrows heavily from the BeOS condition variable25implementation, written by Christopher Tate and Owen Smith. Thanks!26*/2728#include "../generic/SDL_syscond_c.h"2930/* If two implementations are to be compiled into SDL (the active one31* will be chosen at runtime), the function names need to be32* suffixed33*/34#ifndef SDL_THREAD_GENERIC_COND_SUFFIX35#define SDL_CreateCondition_generic SDL_CreateCondition36#define SDL_DestroyCondition_generic SDL_DestroyCondition37#define SDL_SignalCondition_generic SDL_SignalCondition38#define SDL_BroadcastCondition_generic SDL_BroadcastCondition39#endif4041typedef struct SDL_cond_generic42{43SDL_Semaphore *sem;44SDL_Semaphore *handshake_sem;45SDL_Semaphore *signal_sem;46int num_waiting;47int num_signals;48} SDL_cond_generic;4950// Create a condition variable51SDL_Condition *SDL_CreateCondition_generic(void)52{53SDL_cond_generic *cond = (SDL_cond_generic *)SDL_calloc(1, sizeof(*cond));5455#ifndef SDL_THREADS_DISABLED56if (cond) {57cond->sem = SDL_CreateSemaphore(0);58cond->handshake_sem = SDL_CreateSemaphore(0);59cond->signal_sem = SDL_CreateSemaphore(1);60if (!cond->sem || !cond->handshake_sem || !cond->signal_sem) {61SDL_DestroyCondition_generic((SDL_Condition *)cond);62cond = NULL;63}64}65#endif6667return (SDL_Condition *)cond;68}6970// Destroy a condition variable71void SDL_DestroyCondition_generic(SDL_Condition *_cond)72{73SDL_cond_generic *cond = (SDL_cond_generic *)_cond;74if (cond) {75if (cond->sem) {76SDL_DestroySemaphore(cond->sem);77}78if (cond->handshake_sem) {79SDL_DestroySemaphore(cond->handshake_sem);80}81if (cond->signal_sem) {82SDL_DestroySemaphore(cond->signal_sem);83}84SDL_free(cond);85}86}8788// Restart one of the threads that are waiting on the condition variable89void SDL_SignalCondition_generic(SDL_Condition *_cond)90{91SDL_cond_generic *cond = (SDL_cond_generic *)_cond;92if (!cond) {93return;94}9596#ifndef SDL_THREADS_DISABLED97/* If there are waiting threads not already signalled, then98signal the condition and wait for the thread to respond.99*/100SDL_WaitSemaphore(cond->signal_sem);101if (cond->num_waiting > cond->num_signals) {102cond->num_signals++;103SDL_SignalSemaphore(cond->sem);104SDL_SignalSemaphore(cond->signal_sem);105SDL_WaitSemaphore(cond->handshake_sem);106} else {107SDL_SignalSemaphore(cond->signal_sem);108}109#endif110}111112// Restart all threads that are waiting on the condition variable113void SDL_BroadcastCondition_generic(SDL_Condition *_cond)114{115SDL_cond_generic *cond = (SDL_cond_generic *)_cond;116if (!cond) {117return;118}119120#ifndef SDL_THREADS_DISABLED121/* If there are waiting threads not already signalled, then122signal the condition and wait for the thread to respond.123*/124SDL_WaitSemaphore(cond->signal_sem);125if (cond->num_waiting > cond->num_signals) {126const int num_waiting = (cond->num_waiting - cond->num_signals);127cond->num_signals = cond->num_waiting;128for (int i = 0; i < num_waiting; i++) {129SDL_SignalSemaphore(cond->sem);130}131/* Now all released threads are blocked here, waiting for us.132Collect them all (and win fabulous prizes!) :-)133*/134SDL_SignalSemaphore(cond->signal_sem);135for (int i = 0; i < num_waiting; i++) {136SDL_WaitSemaphore(cond->handshake_sem);137}138} else {139SDL_SignalSemaphore(cond->signal_sem);140}141#endif142}143144/* Wait on the condition variable for at most 'timeoutNS' nanoseconds.145The mutex must be locked before entering this function!146The mutex is unlocked during the wait, and locked again after the wait.147148Typical use:149150Thread A:151SDL_LockMutex(lock);152while ( ! condition ) {153SDL_WaitCondition(cond, lock);154}155SDL_UnlockMutex(lock);156157Thread B:158SDL_LockMutex(lock);159...160condition = true;161...162SDL_SignalCondition(cond);163SDL_UnlockMutex(lock);164*/165bool SDL_WaitConditionTimeoutNS_generic(SDL_Condition *_cond, SDL_Mutex *mutex, Sint64 timeoutNS)166{167SDL_cond_generic *cond = (SDL_cond_generic *)_cond;168bool result = true;169170if (!cond || !mutex) {171return true;172}173174#ifndef SDL_THREADS_DISABLED175/* Obtain the protection mutex, and increment the number of waiters.176This allows the signal mechanism to only perform a signal if there177are waiting threads.178*/179SDL_WaitSemaphore(cond->signal_sem);180cond->num_waiting++;181SDL_SignalSemaphore(cond->signal_sem);182183// Unlock the mutex, as is required by condition variable semantics184SDL_UnlockMutex(mutex);185186// Wait for a signal187result = SDL_WaitSemaphoreTimeoutNS(cond->sem, timeoutNS);188189/* Let the signaler know we have completed the wait, otherwise190the signaler can race ahead and get the condition semaphore191if we are stopped between the mutex unlock and semaphore wait,192giving a deadlock. See the following URL for details:193http://web.archive.org/web/20010914175514/http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html#Workshop194*/195SDL_WaitSemaphore(cond->signal_sem);196if (cond->num_signals > 0) {197SDL_SignalSemaphore(cond->handshake_sem);198cond->num_signals--;199}200cond->num_waiting--;201SDL_SignalSemaphore(cond->signal_sem);202203// Lock the mutex, as is required by condition variable semantics204SDL_LockMutex(mutex);205#endif206207return result;208}209210#ifndef SDL_THREAD_GENERIC_COND_SUFFIX211bool SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)212{213return SDL_WaitConditionTimeoutNS_generic(cond, mutex, timeoutNS);214}215#endif216217218