diff options
Diffstat (limited to 'src/thread/win32')
-rw-r--r-- | src/thread/win32/SDL_sysmutex.c | 95 | ||||
-rw-r--r-- | src/thread/win32/SDL_syssem.c | 164 | ||||
-rw-r--r-- | src/thread/win32/SDL_systhread.c | 150 | ||||
-rw-r--r-- | src/thread/win32/SDL_systhread_c.h | 28 | ||||
-rw-r--r-- | src/thread/win32/win_ce_semaphore.c | 216 | ||||
-rw-r--r-- | src/thread/win32/win_ce_semaphore.h | 22 |
6 files changed, 675 insertions, 0 deletions
diff --git a/src/thread/win32/SDL_sysmutex.c b/src/thread/win32/SDL_sysmutex.c new file mode 100644 index 0000000..d9a8161 --- /dev/null +++ b/src/thread/win32/SDL_sysmutex.c @@ -0,0 +1,95 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2009 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Mutex functions using the Win32 API */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include "SDL_mutex.h" + + +struct SDL_mutex { + HANDLE id; +}; + +/* Create a mutex */ +SDL_mutex *SDL_CreateMutex(void) +{ + SDL_mutex *mutex; + + /* Allocate mutex memory */ + mutex = (SDL_mutex *)SDL_malloc(sizeof(*mutex)); + if ( mutex ) { + /* Create the mutex, with initial value signaled */ + mutex->id = CreateMutex(NULL, FALSE, NULL); + if ( ! mutex->id ) { + SDL_SetError("Couldn't create mutex"); + SDL_free(mutex); + mutex = NULL; + } + } else { + SDL_OutOfMemory(); + } + return(mutex); +} + +/* Free the mutex */ +void SDL_DestroyMutex(SDL_mutex *mutex) +{ + if ( mutex ) { + if ( mutex->id ) { + CloseHandle(mutex->id); + mutex->id = 0; + } + SDL_free(mutex); + } +} + +/* Lock the mutex */ +int SDL_mutexP(SDL_mutex *mutex) +{ + if ( mutex == NULL ) { + SDL_SetError("Passed a NULL mutex"); + return -1; + } + if ( WaitForSingleObject(mutex->id, INFINITE) == WAIT_FAILED ) { + SDL_SetError("Couldn't wait on mutex"); + return -1; + } + return(0); +} + +/* Unlock the mutex */ +int SDL_mutexV(SDL_mutex *mutex) +{ + if ( mutex == NULL ) { + SDL_SetError("Passed a NULL mutex"); + return -1; + } + if ( ReleaseMutex(mutex->id) == FALSE ) { + SDL_SetError("Couldn't release mutex"); + return -1; + } + return(0); +} diff --git a/src/thread/win32/SDL_syssem.c b/src/thread/win32/SDL_syssem.c new file mode 100644 index 0000000..43c2f95 --- /dev/null +++ b/src/thread/win32/SDL_syssem.c @@ -0,0 +1,164 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2009 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Semaphore functions using the Win32 API */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include "SDL_thread.h" +#if defined(_WIN32_WCE) && (_WIN32_WCE < 300) +#include "win_ce_semaphore.h" +#endif + + +struct SDL_semaphore { +#if defined(_WIN32_WCE) && (_WIN32_WCE < 300) + SYNCHHANDLE id; +#else + HANDLE id; +#endif + Uint32 volatile count; +}; + + +/* Create a semaphore */ +SDL_sem *SDL_CreateSemaphore(Uint32 initial_value) +{ + SDL_sem *sem; + + /* Allocate sem memory */ + sem = (SDL_sem *)SDL_malloc(sizeof(*sem)); + if ( sem ) { + /* Create the semaphore, with max value 32K */ +#if defined(_WIN32_WCE) && (_WIN32_WCE < 300) + sem->id = CreateSemaphoreCE(NULL, initial_value, 32*1024, NULL); +#else + sem->id = CreateSemaphore(NULL, initial_value, 32*1024, NULL); +#endif + sem->count = initial_value; + if ( ! sem->id ) { + SDL_SetError("Couldn't create semaphore"); + SDL_free(sem); + sem = NULL; + } + } else { + SDL_OutOfMemory(); + } + return(sem); +} + +/* Free the semaphore */ +void SDL_DestroySemaphore(SDL_sem *sem) +{ + if ( sem ) { + if ( sem->id ) { +#if defined(_WIN32_WCE) && (_WIN32_WCE < 300) + CloseSynchHandle(sem->id); +#else + CloseHandle(sem->id); +#endif + sem->id = 0; + } + SDL_free(sem); + } +} + +int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout) +{ + int retval; + DWORD dwMilliseconds; + + if ( ! sem ) { + SDL_SetError("Passed a NULL sem"); + return -1; + } + + if ( timeout == SDL_MUTEX_MAXWAIT ) { + dwMilliseconds = INFINITE; + } else { + dwMilliseconds = (DWORD)timeout; + } +#if defined(_WIN32_WCE) && (_WIN32_WCE < 300) + switch (WaitForSemaphoreCE(sem->id, dwMilliseconds)) { +#else + switch (WaitForSingleObject(sem->id, dwMilliseconds)) { +#endif + case WAIT_OBJECT_0: + InterlockedDecrement(&sem->count); + retval = 0; + break; + case WAIT_TIMEOUT: + retval = SDL_MUTEX_TIMEDOUT; + break; + default: + SDL_SetError("WaitForSingleObject() failed"); + retval = -1; + break; + } + return retval; +} + +int SDL_SemTryWait(SDL_sem *sem) +{ + return SDL_SemWaitTimeout(sem, 0); +} + +int SDL_SemWait(SDL_sem *sem) +{ + return SDL_SemWaitTimeout(sem, SDL_MUTEX_MAXWAIT); +} + +/* Returns the current count of the semaphore */ +Uint32 SDL_SemValue(SDL_sem *sem) +{ + if ( ! sem ) { + SDL_SetError("Passed a NULL sem"); + return 0; + } + return sem->count; +} + +int SDL_SemPost(SDL_sem *sem) +{ + if ( ! sem ) { + SDL_SetError("Passed a NULL sem"); + return -1; + } + /* Increase the counter in the first place, because + * after a successful release the semaphore may + * immediately get destroyed by another thread which + * is waiting for this semaphore. + */ + InterlockedIncrement(&sem->count); +#if defined(_WIN32_WCE) && (_WIN32_WCE < 300) + if ( ReleaseSemaphoreCE(sem->id, 1, NULL) == FALSE ) { +#else + if ( ReleaseSemaphore(sem->id, 1, NULL) == FALSE ) { +#endif + InterlockedDecrement(&sem->count); /* restore */ + SDL_SetError("ReleaseSemaphore() failed"); + return -1; + } + return 0; +} diff --git a/src/thread/win32/SDL_systhread.c b/src/thread/win32/SDL_systhread.c new file mode 100644 index 0000000..a0fb2f5 --- /dev/null +++ b/src/thread/win32/SDL_systhread.c @@ -0,0 +1,150 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2009 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Win32 thread management routines for SDL */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include "SDL_thread.h" +#include "../SDL_thread_c.h" +#include "../SDL_systhread.h" + +#ifndef SDL_PASSED_BEGINTHREAD_ENDTHREAD +#ifndef _WIN32_WCE +/* We'll use the C library from this DLL */ +#include <process.h> +#endif + +#if __GNUC__ +typedef unsigned long (__cdecl *pfnSDL_CurrentBeginThread) (void *, unsigned, + unsigned (__stdcall *func)(void *), void *arg, + unsigned, unsigned *threadID); +typedef void (__cdecl *pfnSDL_CurrentEndThread)(unsigned code); +#elif defined(__WATCOMC__) +/* This is for Watcom targets except OS2 */ +#if __WATCOMC__ < 1240 +#define __watcall +#endif +typedef unsigned long (__watcall *pfnSDL_CurrentBeginThread) (void *, unsigned, + unsigned (__stdcall *func)(void *), void *arg, + unsigned, unsigned *threadID); +typedef void (__watcall *pfnSDL_CurrentEndThread)(unsigned code); +#else +typedef uintptr_t (__cdecl *pfnSDL_CurrentBeginThread) (void *, unsigned, + unsigned (__stdcall *func)(void *), void *arg, + unsigned, unsigned *threadID); +typedef void (__cdecl *pfnSDL_CurrentEndThread)(unsigned code); +#endif +#endif /* !SDL_PASSED_BEGINTHREAD_ENDTHREAD */ + + +typedef struct ThreadStartParms +{ + void *args; + pfnSDL_CurrentEndThread pfnCurrentEndThread; +} tThreadStartParms, *pThreadStartParms; + +static unsigned __stdcall RunThread(void *data) +{ + pThreadStartParms pThreadParms = (pThreadStartParms)data; + pfnSDL_CurrentEndThread pfnCurrentEndThread = NULL; + + // Call the thread function! + SDL_RunThread(pThreadParms->args); + + // Get the current endthread we have to use! + if (pThreadParms) + { + pfnCurrentEndThread = pThreadParms->pfnCurrentEndThread; + SDL_free(pThreadParms); + } + // Call endthread! + if (pfnCurrentEndThread) + (*pfnCurrentEndThread)(0); + return(0); +} + +#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD +int SDL_SYS_CreateThread(SDL_Thread *thread, void *args, pfnSDL_CurrentBeginThread pfnBeginThread, pfnSDL_CurrentEndThread pfnEndThread) +{ +#else +int SDL_SYS_CreateThread(SDL_Thread *thread, void *args) +{ +#ifdef _WIN32_WCE + pfnSDL_CurrentBeginThread pfnBeginThread = NULL; + pfnSDL_CurrentEndThread pfnEndThread = NULL; +#else + pfnSDL_CurrentBeginThread pfnBeginThread = _beginthreadex; + pfnSDL_CurrentEndThread pfnEndThread = _endthreadex; +#endif +#endif /* SDL_PASSED_BEGINTHREAD_ENDTHREAD */ + unsigned threadid; + pThreadStartParms pThreadParms = (pThreadStartParms)SDL_malloc(sizeof(tThreadStartParms)); + if (!pThreadParms) { + SDL_OutOfMemory(); + return(-1); + } + + // Save the function which we will have to call to clear the RTL of calling app! + pThreadParms->pfnCurrentEndThread = pfnEndThread; + // Also save the real parameters we have to pass to thread function + pThreadParms->args = args; + + if (pfnBeginThread) { + thread->handle = (SYS_ThreadHandle) pfnBeginThread(NULL, 0, RunThread, + pThreadParms, 0, &threadid); + } else { + thread->handle = CreateThread(NULL, 0, RunThread, pThreadParms, 0, &threadid); + } + if (thread->handle == NULL) { + SDL_SetError("Not enough resources to create thread"); + return(-1); + } + return(0); +} + +void SDL_SYS_SetupThread(void) +{ + return; +} + +Uint32 SDL_ThreadID(void) +{ + return((Uint32)GetCurrentThreadId()); +} + +void SDL_SYS_WaitThread(SDL_Thread *thread) +{ + WaitForSingleObject(thread->handle, INFINITE); + CloseHandle(thread->handle); +} + +/* WARNING: This function is really a last resort. + * Threads should be signaled and then exit by themselves. + * TerminateThread() doesn't perform stack and DLL cleanup. + */ +void SDL_SYS_KillThread(SDL_Thread *thread) +{ + TerminateThread(thread->handle, FALSE); +} diff --git a/src/thread/win32/SDL_systhread_c.h b/src/thread/win32/SDL_systhread_c.h new file mode 100644 index 0000000..ab6124e --- /dev/null +++ b/src/thread/win32/SDL_systhread_c.h @@ -0,0 +1,28 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2009 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +typedef HANDLE SYS_ThreadHandle; + diff --git a/src/thread/win32/win_ce_semaphore.c b/src/thread/win32/win_ce_semaphore.c new file mode 100644 index 0000000..9db45c4 --- /dev/null +++ b/src/thread/win32/win_ce_semaphore.c @@ -0,0 +1,216 @@ +/* win_ce_semaphore.c + + Copyright (c) 1998, Johnson M. Hart + (with corrections 2001 by Rainer Loritz) + Permission is granted for any and all use providing that this + copyright is properly acknowledged. + There are no assurances of suitability for any use whatsoever. + + WINDOWS CE: There is a collection of Windows CE functions to simulate + semaphores using only a mutex and an event. As Windows CE events cannot + be named, these simulated semaphores cannot be named either. + + Implementation notes: + 1. All required internal data structures are allocated on the process's heap. + 2. Where appropriate, a new error code is returned (see the header + file), or, if the error is a Win32 error, that code is unchanged. + 3. Notice the new handle type "SYNCHHANDLE" that has handles, counters, + and other information. This structure will grow as new objects are added + to this set; some members are specific to only one or two of the objects. + 4. Mutexes are used for critical sections. These could be replaced with + CRITICAL_SECTION objects but then this would give up the time out + capability. + 5. The implementation shows several interesting aspects of synchronization, some + of which are specific to Win32 and some of which are general. These are pointed + out in the comments as appropriate. + 6. The wait function emulates WaitForSingleObject only. An emulation of + WaitForMultipleObjects is much harder to implement outside the kernel, + and it is not clear how to handle a mixture of WCE semaphores and normal + events and mutexes. */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include "win_ce_semaphore.h" + +static SYNCHHANDLE CleanUp (SYNCHHANDLE hSynch, DWORD Flags); + +SYNCHHANDLE CreateSemaphoreCE ( + + LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, /* pointer to security attributes */ + LONG lInitialCount, /* initial count */ + LONG lMaximumCount, /* maximum count */ + LPCTSTR lpName ) + +/* Semaphore for use with Windows CE that does not support them directly. + Requires a counter, a mutex to protect the counter, and an + autoreset event. + + Here are the rules that must always hold between the autoreset event + and the mutex (any violation of these rules by the CE semaphore functions + will, in all likelihood, result in a defect): + 1. No thread can set, pulse, or reset the event, + nor can it access any part of the SYNCHHANDLE structure, + without first gaining ownership of the mutex. + BUT, a thread can wait on the event without owning the mutex + (this is clearly necessary or else the event could never be set). + 2. The event is in a signaled state if and only if the current semaphore + count ("CurCount") is greater than zero. + 3. The semaphore count is always >= 0 and <= the maximum count */ + +{ + SYNCHHANDLE hSynch = NULL, result = NULL; + + __try + { + if (lInitialCount > lMaximumCount || lMaximumCount < 0 || lInitialCount < 0) + { + /* Bad parameters */ + SetLastError (SYNCH_ERROR); + __leave; + } + + hSynch = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, SYNCH_HANDLE_SIZE); + if (hSynch == NULL) __leave; + + hSynch->MaxCount = lMaximumCount; + hSynch->CurCount = lInitialCount; + hSynch->lpName = lpName; + + hSynch->hMutex = CreateMutex (lpSemaphoreAttributes, FALSE, NULL); + + WaitForSingleObject (hSynch->hMutex, INFINITE); + /* Create the event. It is initially signaled if and only if the + initial count is > 0 */ + hSynch->hEvent = CreateEvent (lpSemaphoreAttributes, FALSE, + lInitialCount > 0, NULL); + ReleaseMutex (hSynch->hMutex); + hSynch->hSemph = NULL; + } + __finally + { + /* Return with the handle, or, if there was any error, return + a null after closing any open handles and freeing any allocated memory. */ + result=CleanUp(hSynch, 6 /* An event and a mutex, but no semaphore. */); + } + + return result; +} + +BOOL ReleaseSemaphoreCE (SYNCHHANDLE hSemCE, LONG cReleaseCount, LPLONG lpPreviousCount) +/* Windows CE equivalent to ReleaseSemaphore. */ +{ + BOOL Result = TRUE; + + /* Gain access to the object to assure that the release count + would not cause the total count to exceed the maximum. */ + + __try + { + WaitForSingleObject (hSemCE->hMutex, INFINITE); + /* reply only if asked to */ + if (lpPreviousCount!=NULL) + *lpPreviousCount = hSemCE->CurCount; + if (hSemCE->CurCount + cReleaseCount > hSemCE->MaxCount || cReleaseCount <= 0) + { + SetLastError (SYNCH_ERROR); + Result = FALSE; + __leave; + } + hSemCE->CurCount += cReleaseCount; + + /* Set the autoreset event, releasing exactly one waiting thread, now or + in the future. */ + + SetEvent (hSemCE->hEvent); + } + __finally + { + ReleaseMutex (hSemCE->hMutex); + } + + return Result; +} + +DWORD WaitForSemaphoreCE (SYNCHHANDLE hSemCE, DWORD dwMilliseconds) + /* Windows CE semaphore equivalent of WaitForSingleObject. */ +{ + DWORD WaitResult; + + WaitResult = WaitForSingleObject (hSemCE->hMutex, dwMilliseconds); + if (WaitResult != WAIT_OBJECT_0 && WaitResult != WAIT_ABANDONED_0) return WaitResult; + while (hSemCE->CurCount <= 0) + { + + /* The count is 0, and the thread must wait on the event (which, by + the rules, is currently reset) for semaphore resources to become + available. First, of course, the mutex must be released so that another + thread will be capable of setting the event. */ + + ReleaseMutex (hSemCE->hMutex); + + /* Wait for the event to be signaled, indicating a semaphore state change. + The event is autoreset and signaled with a SetEvent (not PulseEvent) + so exactly one waiting thread (whether or not there is currently + a waiting thread) is released as a result of the SetEvent. */ + + WaitResult = WaitForSingleObject (hSemCE->hEvent, dwMilliseconds); + if (WaitResult != WAIT_OBJECT_0) return WaitResult; + + /* This is where the properties of setting of an autoreset event is critical + to assure that, even if the semaphore state changes between the + preceding Wait and the next, and even if NO threads are waiting + on the event at the time of the SetEvent, at least one thread + will be released. + Pulsing a manual reset event would appear to work, but it would have + a defect which could appear if the semaphore state changed between + the two waits. */ + + WaitResult = WaitForSingleObject (hSemCE->hMutex, dwMilliseconds); + if (WaitResult != WAIT_OBJECT_0 && WaitResult != WAIT_ABANDONED_0) return WaitResult; + + } + /* The count is not zero and this thread owns the mutex. */ + + hSemCE->CurCount--; + /* The event is now unsignaled, BUT, the semaphore count may not be + zero, in which case the event should be signaled again + before releasing the mutex. */ + + if (hSemCE->CurCount > 0) SetEvent (hSemCE->hEvent); + ReleaseMutex (hSemCE->hMutex); + return WaitResult; +} + +BOOL CloseSynchHandle (SYNCHHANDLE hSynch) +/* Close a synchronization handle. + Improvement: Test for a valid handle before dereferencing the handle. */ +{ + BOOL Result = TRUE; + if (hSynch->hEvent != NULL) Result = Result && CloseHandle (hSynch->hEvent); + if (hSynch->hMutex != NULL) Result = Result && CloseHandle (hSynch->hMutex); + if (hSynch->hSemph != NULL) Result = Result && CloseHandle (hSynch->hSemph); + HeapFree (GetProcessHeap (), 0, hSynch); + return (Result); +} + +static SYNCHHANDLE CleanUp (SYNCHHANDLE hSynch, DWORD Flags) +{ /* Prepare to return from a create of a synchronization handle. + If there was any failure, free any allocated resources. + "Flags" indicates which Win32 objects are required in the + synchronization handle. */ + + BOOL ok = TRUE; + + if (hSynch == NULL) return NULL; + if ((Flags & 4) == 1 && (hSynch->hEvent == NULL)) ok = FALSE; + if ((Flags & 2) == 1 && (hSynch->hMutex == NULL)) ok = FALSE; + if ((Flags & 1) == 1 && (hSynch->hEvent == NULL)) ok = FALSE; + if (!ok) + { + CloseSynchHandle (hSynch); + return NULL; + } + /* Everything worked */ + return hSynch; +} diff --git a/src/thread/win32/win_ce_semaphore.h b/src/thread/win32/win_ce_semaphore.h new file mode 100644 index 0000000..af2d7b6 --- /dev/null +++ b/src/thread/win32/win_ce_semaphore.h @@ -0,0 +1,22 @@ +/* win_ce_semaphore.h - header file to go with win_ce_semaphore.c */ + +typedef struct _SYNCH_HANDLE_STRUCTURE { + HANDLE hEvent; + HANDLE hMutex; + HANDLE hSemph; + LONG MaxCount; + volatile LONG CurCount; + LPCTSTR lpName; +} SYNCH_HANDLE_STRUCTURE, *SYNCHHANDLE; + +#define SYNCH_HANDLE_SIZE sizeof (SYNCH_HANDLE_STRUCTURE) + + /* Error codes - all must have bit 29 set */ +#define SYNCH_ERROR 0X20000000 /* EXERCISE - REFINE THE ERROR NUMBERS */ + +extern SYNCHHANDLE CreateSemaphoreCE (LPSECURITY_ATTRIBUTES, LONG, LONG, LPCTSTR); + +extern BOOL ReleaseSemaphoreCE (SYNCHHANDLE, LONG, LPLONG); +extern DWORD WaitForSemaphoreCE (SYNCHHANDLE, DWORD); + +extern BOOL CloseSynchHandle (SYNCHHANDLE); |