//======================================================================== // GLFW - An OpenGL framework // File: win32_thread.c // Platform: Windows // API version: 2.6 // WWW: http://glfw.sourceforge.net //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Camilla Berglund // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include "internal.h" //************************************************************************ // This is an implementation of POSIX "compatible" condition variables for // Win32, as described by Douglas C. Schmidt and Irfan Pyarali: // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html //************************************************************************ enum { _GLFW_COND_SIGNAL = 0, _GLFW_COND_BROADCAST = 1 }; typedef struct { // Signal and broadcast event HANDLEs HANDLE events[ 2 ]; // Count of the number of waiters unsigned int waiters_count; // Serialize access to CRITICAL_SECTION waiters_count_lock; } _GLFWcond; //************************************************************************ //**** GLFW internal functions **** //************************************************************************ //======================================================================== // _glfwNewThread() - This is simply a "wrapper" for calling the user // thread function. //======================================================================== DWORD WINAPI _glfwNewThread( LPVOID lpParam ) { GLFWthreadfun threadfun; _GLFWthread *t; // Get pointer to thread information for current thread t = _glfwGetThreadPointer( _glfwPlatformGetThreadID() ); if( t == NULL ) { return 0; } // Get user thread function pointer threadfun = t->Function; // Call the user thread function threadfun( (void *) lpParam ); // Remove thread from thread list ENTER_THREAD_CRITICAL_SECTION _glfwRemoveThread( t ); LEAVE_THREAD_CRITICAL_SECTION // When the thread function returns, the thread will die... return 0; } //************************************************************************ //**** GLFW user functions **** //************************************************************************ //======================================================================== // _glfwPlatformCreateThread() - Create a new thread //======================================================================== GLFWthread _glfwPlatformCreateThread( GLFWthreadfun fun, void *arg ) { GLFWthread ID; _GLFWthread *t, *t_tmp; HANDLE hThread; DWORD dwThreadId; // Enter critical section ENTER_THREAD_CRITICAL_SECTION // Create a new thread information memory area t = (_GLFWthread *) malloc( sizeof(_GLFWthread) ); if( t == NULL ) { // Leave critical section LEAVE_THREAD_CRITICAL_SECTION return -1; } // Get a new unique thread id ID = _glfwThrd.NextID ++; // Store thread information in the thread list t->Function = fun; t->ID = ID; // Create thread hThread = CreateThread( NULL, // Default security attributes 0, // Default stack size (1 MB) _glfwNewThread, // Thread function (a wrapper function) (LPVOID)arg, // Argument to thread is the user argument 0, // Default creation flags &dwThreadId // Returned thread identifier ); // Did the thread creation fail? if( hThread == NULL ) { free( (void *) t ); LEAVE_THREAD_CRITICAL_SECTION return -1; } // Store more thread information in the thread list t->Handle = hThread; t->WinID = dwThreadId; // Append thread to thread list t_tmp = &_glfwThrd.First; while( t_tmp->Next != NULL ) { t_tmp = t_tmp->Next; } t_tmp->Next = t; t->Previous = t_tmp; t->Next = NULL; // Leave critical section LEAVE_THREAD_CRITICAL_SECTION // Return the GLFW thread ID return ID; } //======================================================================== // _glfwPlatformDestroyThread() - Kill a thread. NOTE: THIS IS A VERY // DANGEROUS OPERATION, AND SHOULD NOT BE USED EXCEPT IN EXTREME // SITUATIONS! //======================================================================== void _glfwPlatformDestroyThread( GLFWthread ID ) { _GLFWthread *t; // Enter critical section ENTER_THREAD_CRITICAL_SECTION // Get thread information pointer t = _glfwGetThreadPointer( ID ); if( t == NULL ) { LEAVE_THREAD_CRITICAL_SECTION return; } // Simply murder the process, no mercy! if( TerminateThread( t->Handle, 0 ) ) { // Close thread handle CloseHandle( t->Handle ); // Remove thread from thread list _glfwRemoveThread( t ); } // Leave critical section LEAVE_THREAD_CRITICAL_SECTION } //======================================================================== // _glfwPlatformWaitThread() - Wait for a thread to die //======================================================================== int _glfwPlatformWaitThread( GLFWthread ID, int waitmode ) { DWORD result; HANDLE hThread; _GLFWthread *t; // Enter critical section ENTER_THREAD_CRITICAL_SECTION // Get thread information pointer t = _glfwGetThreadPointer( ID ); // Is the thread already dead? if( t == NULL ) { LEAVE_THREAD_CRITICAL_SECTION return GL_TRUE; } // Get thread handle hThread = t->Handle; // Leave critical section LEAVE_THREAD_CRITICAL_SECTION // Wait for thread to die if( waitmode == GLFW_WAIT ) { result = WaitForSingleObject( hThread, INFINITE ); } else if( waitmode == GLFW_NOWAIT ) { result = WaitForSingleObject( hThread, 0 ); } else { return GL_FALSE; } // Did we have a time-out? if( result == WAIT_TIMEOUT ) { return GL_FALSE; } return GL_TRUE; } //======================================================================== // _glfwPlatformGetThreadID() - Return the thread ID for the current // thread //======================================================================== GLFWthread _glfwPlatformGetThreadID( void ) { _GLFWthread *t; GLFWthread ID = -1; DWORD WinID; // Get Windows thread ID WinID = GetCurrentThreadId(); // Enter critical section (to avoid an inconsistent thread list) ENTER_THREAD_CRITICAL_SECTION // Loop through entire list of threads to find the matching Windows // thread ID for( t = &_glfwThrd.First; t != NULL; t = t->Next ) { if( t->WinID == WinID ) { ID = t->ID; break; } } // Leave critical section LEAVE_THREAD_CRITICAL_SECTION // Return the found GLFW thread identifier return ID; } //======================================================================== // _glfwPlatformCreateMutex() - Create a mutual exclusion object //======================================================================== GLFWmutex _glfwPlatformCreateMutex( void ) { CRITICAL_SECTION *mutex; // Allocate memory for mutex mutex = (CRITICAL_SECTION *) malloc( sizeof(CRITICAL_SECTION) ); if( !mutex ) { return NULL; } // Initialize mutex InitializeCriticalSection( mutex ); // Cast to GLFWmutex and return return (GLFWmutex) mutex; } //======================================================================== // glfwDestroyMutex() - Destroy a mutual exclusion object //======================================================================== void _glfwPlatformDestroyMutex( GLFWmutex mutex ) { // Destroy mutex DeleteCriticalSection( (CRITICAL_SECTION *) mutex ); free( mutex ); } //======================================================================== // _glfwPlatformLockMutex() - Request access to a mutex //======================================================================== void _glfwPlatformLockMutex( GLFWmutex mutex ) { // Wait for mutex to be released EnterCriticalSection( (CRITICAL_SECTION *) mutex ); } //======================================================================== // _glfwPlatformUnlockMutex() - Release a mutex //======================================================================== void _glfwPlatformUnlockMutex( GLFWmutex mutex ) { // Release mutex LeaveCriticalSection( (CRITICAL_SECTION *) mutex ); } //======================================================================== // _glfwPlatformCreateCond() - Create a new condition variable object //======================================================================== GLFWcond _glfwPlatformCreateCond( void ) { _GLFWcond *cond; // Allocate memory for condition variable cond = (_GLFWcond *) malloc( sizeof(_GLFWcond) ); if( !cond ) { return NULL; } // Initialize condition variable cond->waiters_count = 0; cond->events[ _GLFW_COND_SIGNAL ] = CreateEvent( NULL, FALSE, FALSE, NULL ); cond->events[ _GLFW_COND_BROADCAST ] = CreateEvent( NULL, TRUE, FALSE, NULL ); InitializeCriticalSection( &cond->waiters_count_lock ); // Cast to GLFWcond and return return (GLFWcond) cond; } //======================================================================== // _glfwPlatformDestroyCond() - Destroy a condition variable object //======================================================================== void _glfwPlatformDestroyCond( GLFWcond cond ) { // Close the condition variable handles CloseHandle( ((_GLFWcond *)cond)->events[ _GLFW_COND_SIGNAL ] ); CloseHandle( ((_GLFWcond *)cond)->events[ _GLFW_COND_BROADCAST ] ); // Delete critical section DeleteCriticalSection( &((_GLFWcond *)cond)->waiters_count_lock ); // Free memory for condition variable free( (void *) cond ); } //======================================================================== // _glfwPlatformWaitCond() - Wait for a condition to be raised //======================================================================== void _glfwPlatformWaitCond( GLFWcond cond, GLFWmutex mutex, double timeout ) { _GLFWcond *cv = (_GLFWcond *) cond; int result, last_waiter; DWORD timeout_ms; // Avoid race conditions EnterCriticalSection( &cv->waiters_count_lock ); cv->waiters_count ++; LeaveCriticalSection( &cv->waiters_count_lock ); // It's ok to release the mutex here since Win32 manual-reset events // maintain state when used with SetEvent() LeaveCriticalSection( (CRITICAL_SECTION *) mutex ); // Translate timeout into milliseconds if( timeout >= GLFW_INFINITY ) { timeout_ms = INFINITE; } else { timeout_ms = (DWORD) (1000.0 * timeout + 0.5); if( timeout_ms <= 0 ) { timeout_ms = 1; } } // Wait for either event to become signaled due to glfwSignalCond or // glfwBroadcastCond being called result = WaitForMultipleObjects( 2, cv->events, FALSE, timeout_ms ); // Check if we are the last waiter EnterCriticalSection( &cv->waiters_count_lock ); cv->waiters_count --; last_waiter = (result == WAIT_OBJECT_0 + _GLFW_COND_BROADCAST) && (cv->waiters_count == 0); LeaveCriticalSection( &cv->waiters_count_lock ); // Some thread called glfwBroadcastCond if( last_waiter ) { // We're the last waiter to be notified or to stop waiting, so // reset the manual event ResetEvent( cv->events[ _GLFW_COND_BROADCAST ] ); } // Reacquire the mutex EnterCriticalSection( (CRITICAL_SECTION *) mutex ); } //======================================================================== // _glfwPlatformSignalCond() - Signal a condition to one waiting thread //======================================================================== void _glfwPlatformSignalCond( GLFWcond cond ) { _GLFWcond *cv = (_GLFWcond *) cond; int have_waiters; // Avoid race conditions EnterCriticalSection( &cv->waiters_count_lock ); have_waiters = cv->waiters_count > 0; LeaveCriticalSection( &cv->waiters_count_lock ); if( have_waiters ) { SetEvent( cv->events[ _GLFW_COND_SIGNAL ] ); } } //======================================================================== // _glfwPlatformBroadcastCond() - Broadcast a condition to all waiting // threads //======================================================================== void _glfwPlatformBroadcastCond( GLFWcond cond ) { _GLFWcond *cv = (_GLFWcond *) cond; int have_waiters; // Avoid race conditions EnterCriticalSection( &cv->waiters_count_lock ); have_waiters = cv->waiters_count > 0; LeaveCriticalSection( &cv->waiters_count_lock ); if( have_waiters ) { SetEvent( cv->events[ _GLFW_COND_BROADCAST ] ); } } //======================================================================== // _glfwPlatformGetNumberOfProcessors() - Return the number of processors // in the system. //======================================================================== int _glfwPlatformGetNumberOfProcessors( void ) { SYSTEM_INFO si; // Get hardware system information GetSystemInfo( &si ); return (int) si.dwNumberOfProcessors; }