// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL // For more information, see LICENCE in the main folder #define HERCULES_CORE #include "mutex.h" #include "../common/cbasetypes.h" // for WIN32 #ifdef WIN32 #include "../common/winapi.h" #else #include #include #include #endif #include "../common/malloc.h" #include "../common/showmsg.h" #include "../common/timer.h" struct ramutex{ #ifdef WIN32 CRITICAL_SECTION hMutex; #else pthread_mutex_t hMutex; #endif }; struct racond{ #ifdef WIN32 HANDLE events[2]; ra_align(8) volatile LONG nWaiters; CRITICAL_SECTION waiters_lock; #define EVENT_COND_SIGNAL 0 #define EVENT_COND_BROADCAST 1 #else pthread_cond_t hCond; #endif }; //////////////////// // Mutex // // Implementation: // ramutex *ramutex_create(void) { struct ramutex *m; m = (struct ramutex*)aMalloc( sizeof(struct ramutex) ); if (m == NULL) { ShowFatalError("ramutex_create: OOM while allocating %"PRIuS" bytes.\n", sizeof(struct ramutex)); return NULL; } #ifdef WIN32 InitializeCriticalSection(&m->hMutex); #else pthread_mutex_init(&m->hMutex, NULL); #endif return m; }//end: ramutex_create() void ramutex_destroy(ramutex *m) { #ifdef WIN32 DeleteCriticalSection(&m->hMutex); #else pthread_mutex_destroy(&m->hMutex); #endif aFree(m); }//end: ramutex_destroy() void ramutex_lock(ramutex *m) { #ifdef WIN32 EnterCriticalSection(&m->hMutex); #else pthread_mutex_lock(&m->hMutex); #endif }//end: ramutex_lock bool ramutex_trylock(ramutex *m) { #ifdef WIN32 if(TryEnterCriticalSection(&m->hMutex) == TRUE) return true; return false; #else if(pthread_mutex_trylock(&m->hMutex) == 0) return true; return false; #endif }//end: ramutex_trylock() void ramutex_unlock(ramutex *m) { #ifdef WIN32 LeaveCriticalSection(&m->hMutex); #else pthread_mutex_unlock(&m->hMutex); #endif }//end: ramutex_unlock() /////////////// // Condition Variables // // Implementation: // racond *racond_create(void) { struct racond *c; c = (struct racond*)aMalloc( sizeof(struct racond) ); if (c == NULL) { ShowFatalError("racond_create: OOM while allocating %"PRIuS" bytes\n", sizeof(struct racond)); return NULL; } #ifdef WIN32 c->nWaiters = 0; c->events[ EVENT_COND_SIGNAL ] = CreateEvent( NULL, FALSE, FALSE, NULL ); c->events[ EVENT_COND_BROADCAST ] = CreateEvent( NULL, TRUE, FALSE, NULL ); InitializeCriticalSection( &c->waiters_lock ); #else pthread_cond_init(&c->hCond, NULL); #endif return c; }//end: racond_create() void racond_destroy(racond *c) { #ifdef WIN32 CloseHandle( c->events[ EVENT_COND_SIGNAL ] ); CloseHandle( c->events[ EVENT_COND_BROADCAST ] ); DeleteCriticalSection( &c->waiters_lock ); #else pthread_cond_destroy(&c->hCond); #endif aFree(c); }//end: racond_destroy() void racond_wait(racond *c, ramutex *m, sysint timeout_ticks) { #ifdef WIN32 register DWORD ms; int result; bool is_last = false; EnterCriticalSection(&c->waiters_lock); c->nWaiters++; LeaveCriticalSection(&c->waiters_lock); if(timeout_ticks < 0) ms = INFINITE; else ms = (timeout_ticks > MAXDWORD) ? (MAXDWORD - 1) : (DWORD)timeout_ticks; // we can release the mutex (m) here, cause win's // manual reset events maintain state when used with // SetEvent() ramutex_unlock(m); result = WaitForMultipleObjects(2, c->events, FALSE, ms); EnterCriticalSection(&c->waiters_lock); c->nWaiters--; if( (result == WAIT_OBJECT_0 + EVENT_COND_BROADCAST) && (c->nWaiters == 0) ) is_last = true; // Broadcast called! LeaveCriticalSection(&c->waiters_lock); // we are the last waiter that has to be notified, or to stop waiting // so we have to do a manual reset if(is_last == true) ResetEvent( c->events[EVENT_COND_BROADCAST] ); ramutex_lock(m); #else if(timeout_ticks < 0){ pthread_cond_wait( &c->hCond, &m->hMutex ); }else{ struct timespec wtime; int64 exact_timeout = timer->gettick() + timeout_ticks; wtime.tv_sec = exact_timeout/1000; wtime.tv_nsec = (exact_timeout%1000)*1000000; pthread_cond_timedwait( &c->hCond, &m->hMutex, &wtime); } #endif }//end: racond_wait() void racond_signal(racond *c) { #ifdef WIN32 // bool has_waiters = false; // EnterCriticalSection(&c->waiters_lock); // if(c->nWaiters > 0) // has_waiters = true; // LeaveCriticalSection(&c->waiters_lock); // if(has_waiters == true) SetEvent( c->events[ EVENT_COND_SIGNAL ] ); #else pthread_cond_signal(&c->hCond); #endif }//end: racond_signal() void racond_broadcast(racond *c) { #ifdef WIN32 // bool has_waiters = false; // EnterCriticalSection(&c->waiters_lock); // if(c->nWaiters > 0) // has_waiters = true; // LeaveCriticalSection(&c->waiters_lock); // if(has_waiters == true) SetEvent( c->events[ EVENT_COND_BROADCAST ] ); #else pthread_cond_broadcast(&c->hCond); #endif }//end: racond_broadcast()