Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / misc / mutex.cpp
blobef0cb4216fb253f7c2d92750215b479f5c9ad4a6
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdmisc.h"
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif // _GNU_SOURCE
23 #include "nel/misc/mutex.h"
24 #include "nel/misc/time_nl.h"
25 #include "nel/misc/debug.h"
27 using namespace std;
29 #ifndef MUTEX_DEBUG
30 #define debugCreateMutex() ;
31 #define debugBeginEnter() ;
32 #define debugEndEnter() ;
33 #define debugLeave() ;
34 #define debugDeleteMutex() ;
35 #endif
38 /****************
39 * Windows code *
40 ****************/
42 #ifdef NL_OS_WINDOWS
44 #ifdef DEBUG_NEW
45 #define new DEBUG_NEW
46 #endif
48 namespace NLMISC {
52 inline void EnterMutex( void *handle )
54 #ifdef NL_DEBUG
55 DWORD timeout;
56 if ( IsDebuggerPresent() )
57 timeout = INFINITE;
58 else
59 timeout = 10000;
61 // Request ownership of mutex
62 DWORD dwWaitResult = WaitForSingleObject (handle, timeout);
63 #else
64 // Request ownership of mutex during 10s
65 DWORD dwWaitResult = WaitForSingleObject (handle, 10000);
66 #endif // NL_DEBUG
67 switch (dwWaitResult)
69 // The thread got mutex ownership.
70 case WAIT_OBJECT_0: break;
71 // Cannot get mutex ownership due to time-out.
72 case WAIT_TIMEOUT: nlerror ("Dead lock in a mutex (or more that 10s for the critical section");
73 // Got ownership of the abandoned mutex object.
74 case WAIT_ABANDONED: nlerror ("A thread forgot to release the mutex");
75 default: nlerror ("EnterMutex failed");
80 inline void LeaveMutex( void *handle )
82 if (ReleaseMutex(handle) == 0)
84 nlerror ("error while releasing the mutex (0x%x %d), %p", GetLastError(), GetLastError(), handle);
89 /////////////////////////// CUnfairMutex
93 * Windows version
96 CUnfairMutex::CUnfairMutex()
98 // Create a mutex with no initial owner.
99 _Mutex = (void *) CreateMutex( NULL, FALSE, NULL );
100 nlassert( _Mutex != NULL );
104 CUnfairMutex::CUnfairMutex( const std::string & /* name */ )
106 // Create a mutex with no initial owner.
107 _Mutex = (void *) CreateMutex( NULL, FALSE, NULL );
108 nlassert( _Mutex != NULL );
110 // (Does not use name, only provided for debug compatibility with CFairMutex)
115 * Windows version
117 CUnfairMutex::~CUnfairMutex()
119 CloseHandle( _Mutex );
124 * Windows version
126 void CUnfairMutex::enter()
128 EnterMutex( _Mutex );
133 * Windows version
135 void CUnfairMutex::leave()
137 LeaveMutex( _Mutex );
141 /////////////////////////// CSharedMutexW
147 CSharedMutex::CSharedMutex()
149 _Mutex = NULL;
154 * Create or use an existing mutex (created by another process) with a specific object name (createNow must be false in the constructor)
155 * Returns false if it failed.
157 bool CSharedMutex::createByName( const char *objectName )
159 #ifdef NL_DEBUG
160 nlassert( _Mutex == NULL );
161 #endif
162 _Mutex = (void *) CreateMutexA( NULL, FALSE, objectName );
163 //nldebug( "Creating mutex %s: handle %p", objectName, _Mutex );
164 return ( _Mutex != NULL );
171 void CSharedMutex::destroy()
173 CloseHandle( _Mutex );
174 _Mutex = NULL;
180 void CSharedMutex::enter()
182 EnterMutex( _Mutex );
189 void CSharedMutex::leave()
191 LeaveMutex( _Mutex );
195 /////////////////////////// CFairMutex
199 * Windows version
201 CFairMutex::CFairMutex()
203 #ifdef STORE_MUTEX_NAME
204 Name = "unset mutex name";
205 #endif
207 debugCreateMutex();
209 // Check that the CRITICAL_SECTION structure has not changed
210 nlassert( sizeof(TNelRtlCriticalSection)==sizeof(CRITICAL_SECTION) );
212 #if (_WIN32_WINNT >= 0x0500)
213 DWORD dwSpinCount = 0x80000000; // set high-order bit to use preallocation
214 if ( ! InitializeCriticalSectionAndSpinCount( (CRITICAL_SECTION*)&_Cs, dwSpinCount ) )
216 nlerror( "Error entering critical section" );
218 #else
219 InitializeCriticalSection( (CRITICAL_SECTION*)&_Cs );
220 #endif
224 CFairMutex::CFairMutex(const string &name)
226 #ifdef STORE_MUTEX_NAME
227 Name = name;
228 #endif
230 #ifdef MUTEX_DEBUG
231 debugCreateMutex();
232 #endif
234 // Check that the CRITICAL_SECTION structure has not changed
235 nlassert( sizeof(TNelRtlCriticalSection)==sizeof(CRITICAL_SECTION) );
237 #if (_WIN32_WINNT >= 0x0500)
238 DWORD dwSpinCount = 0x80000000; // set high-order bit to use preallocation
239 if ( ! InitializeCriticalSectionAndSpinCount( (CRITICAL_SECTION*)&_Cs, dwSpinCount ) )
241 nlerror( "Error entering critical section" );
243 #else
244 InitializeCriticalSection( (CRITICAL_SECTION*)&_Cs );
245 #endif
251 * Windows version
253 CFairMutex::~CFairMutex()
255 DeleteCriticalSection( (CRITICAL_SECTION*)&_Cs );
257 debugDeleteMutex();
262 * Windows version
264 void CFairMutex::enter()
266 debugBeginEnter();
268 EnterCriticalSection( (CRITICAL_SECTION*)&_Cs );
270 debugEndEnter();
275 * Windows version
277 void CFairMutex::leave()
279 LeaveCriticalSection( (CRITICAL_SECTION*)&_Cs );
281 debugLeave();
284 /*************
285 * Unix code *
286 *************/
288 #elif defined NL_OS_UNIX
290 #include <pthread.h>
291 #include <cerrno>
292 #include <unistd.h>
294 #include <sys/types.h>
295 #include <sys/ipc.h>
296 #include <sys/sem.h> // System V semaphores
301 * Clanlib authors say: "We need to do this because the posix threads library
302 * under linux obviously suck:"
304 extern "C"
306 int pthread_mutexattr_setkind_np( pthread_mutexattr_t *attr, int kind );
310 namespace NLMISC {
313 CUnfairMutex::CUnfairMutex()
315 pthread_mutexattr_t attr;
316 pthread_mutexattr_init( &attr );
317 // Fast mutex. Note: on Windows all mutexes are recursive
318 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
319 pthread_mutex_init( &mutex, &attr );
320 pthread_mutexattr_destroy( &attr );
325 * Unix version
327 CUnfairMutex::CUnfairMutex(const std::string &name)
329 pthread_mutexattr_t attr;
330 pthread_mutexattr_init( &attr );
331 // Fast mutex. Note: on Windows all mutexes are recursive
332 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
333 pthread_mutex_init( &mutex, &attr );
334 pthread_mutexattr_destroy( &attr );
339 * Unix version
341 CUnfairMutex::~CUnfairMutex()
343 pthread_mutex_destroy( &mutex );
348 * Unix version
350 void CUnfairMutex::enter()
352 //cout << getpid() << ": Locking " << &mutex << endl;
353 if ( pthread_mutex_lock( &mutex ) != 0 )
355 //cout << "Error locking a mutex " << endl;
356 nlerror( "Error locking a mutex" );
358 /*else
360 cout << getpid() << ": Owning " << &mutex << endl;
366 * Unix version
368 void CUnfairMutex::leave()
370 //int errcode;
371 //cout << getpid() << ": Unlocking " << &mutex << endl;
372 if ( (/*errcode=*/pthread_mutex_unlock( &mutex )) != 0 )
374 /* switch ( errcode )
376 case EINVAL: cout << "INVAL" << endl; break;
377 case EPERM: cout << "PERM" << endl; break;
378 default: cout << "OTHER" << endl;
381 //cout << "Error unlocking a mutex " /*<< &mutex*/ << endl;
382 nlerror( "Error unlocking a mutex" );
384 /*else
386 cout << getpid() << ": Released " << &mutex << endl;
392 * Unix version
394 CFairMutex::CFairMutex()
396 #ifdef NL_OS_MAC
397 _Sem = dispatch_semaphore_create(1);
398 #else
399 sem_init( const_cast<sem_t*>(&_Sem), 0, 1 );
400 #endif
404 CFairMutex::CFairMutex( const std::string &name )
406 #ifdef NL_OS_MAC
407 _Sem = dispatch_semaphore_create(1);
408 #else
409 sem_init( const_cast<sem_t*>(&_Sem), 0, 1 );
410 #endif
415 * Unix version
417 CFairMutex::~CFairMutex()
419 #ifdef NL_OS_MAC
420 dispatch_release(_Sem);
421 #else
422 sem_destroy( const_cast<sem_t*>(&_Sem) ); // needs that no thread is waiting on the semaphore
423 #endif
428 * Unix version
430 void CFairMutex::enter()
432 #ifdef NL_OS_MAC
433 dispatch_semaphore_wait(_Sem, DISPATCH_TIME_FOREVER);
434 #else
435 sem_wait( const_cast<sem_t*>(&_Sem) );
436 #endif
441 * Unix version
443 void CFairMutex::leave()
445 #ifdef NL_OS_MAC
446 dispatch_semaphore_signal(_Sem);
447 #else
448 sem_post( const_cast<sem_t*>(&_Sem) );
449 #endif
453 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
458 CSharedMutex::CSharedMutex() : _SemId(-1)
463 #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
464 /* union semun is defined by including <sys/sem.h> */
465 #else
466 /* according to X/OPEN we have to define it ourselves */
467 union semun {
468 int val; /* value for SETVAL */
469 struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
470 unsigned short int *array; /* array for GETALL, SETALL */
471 struct seminfo *__buf; /* buffer for IPC_INFO */
473 #endif
479 bool CSharedMutex::createByKey( int key, bool createNew )
481 // Create a semaphore set containing one semaphore
482 /*key_t mykey = ftok(".", 'n');
483 _SemId = semget( mykey, 1, IPC_CREAT | IPC_EXCL | 0666 );*/
485 if ( createNew )
486 _SemId = semget( key, 1, IPC_CREAT | IPC_EXCL | 0666 );
487 else
488 _SemId = semget( key, 1, 0666 );
489 nldebug( "Got semid %d", _SemId );
490 if( _SemId == -1 )
491 return false;
493 // Initialise the semaphore to 1
494 union semun arg;
495 arg.val = 1;
496 if ( semctl( _SemId, 0, SETVAL, arg ) == -1 )
498 nlwarning( "semid=%d, err=%s", _SemId, strerror(errno) );
499 return false;
501 return true;
508 void CSharedMutex::destroy()
510 // Destroy the semaphore
511 union semun arg;
512 nlverifyex( semctl( _SemId, 0, IPC_RMID, arg ) != -1, ("semid=%d, err=%s", _SemId, strerror(errno) ) );
513 _SemId = -1;
520 void CSharedMutex::enter()
522 // Decrement the semaphore
523 sembuf buf;
524 buf.sem_num = 0;
525 buf.sem_op = -1;
526 nlverify( semop( _SemId, &buf, 1 ) != -1);
533 void CSharedMutex::leave()
535 // Increment the semaphore
536 sembuf buf;
537 buf.sem_num = 0;
538 buf.sem_op = 1;
539 nlverify( semop( _SemId, &buf, 1 ) != -1);
543 #endif // NL_OS_WINDOWS/NL_OS_UNIX
551 /******************
552 * Debugging code *
553 ******************/
555 #ifdef MUTEX_DEBUG
557 map<CFairMutex*,TMutexLocks> *AcquireTime = NULL;
558 uint32 NbMutexes = 0;
559 CFairMutex *ATMutex = NULL;
560 bool InitAT = true;
563 /// Inits the "mutex debugging info system"
564 void initAcquireTimeMap()
566 if ( InitAT )
568 ATMutex = new CFairMutex("ATMutex");
569 AcquireTime = new map<CFairMutex*,TMutexLocks>;
570 InitAT = false;
575 /// Gets the debugging info for all mutexes (call it evenly, e.g. once per second)
576 map<CFairMutex*,TMutexLocks> getNewAcquireTimes()
578 map<CMutex*,TMutexLocks> m;
579 ATMutex->enter();
581 // Copy map
582 m = *AcquireTime;
584 // Reset map
585 /* map<CMutex*,TMutexLocks>::iterator im;
586 for ( im=AcquireTime->begin(); im!=AcquireTime->end(); ++im )
588 (*im).second.Time = 0;
589 (*im).second.Nb = 0;
590 (*im).second.Locked = false;
593 ATMutex->leave();
594 return m;
598 ////////////////////////
599 ////////////////////////
601 void CFairMutex::debugCreateMutex()
603 /* if ( ! InitAT )
605 ATMutex->enter();
606 AcquireTime->insert( make_pair( this, TMutexLocks(NbMutexes) ) );
607 NbMutexes++;
608 ATMutex->leave();
609 char dbgstr [256];
610 #ifdef STORE_MUTEX_NAME
611 smprintf( dbgstr, 256, "MUTEX: Creating mutex %p %s (number %u)\n", this, Name.c_str(), NbMutexes-1 );
612 #else
613 smprintf( dbgstr, 256, "MUTEX: Creating mutex %p (number %u)\n", this, NbMutexes-1 );
614 #endif
615 #ifdef NL_OS_WINDOWS
616 if ( IsDebuggerPresent() )
617 OutputDebugString( dbgstr );
618 #endif
619 cout << dbgstr << endl;
623 void CFairMutex::debugDeleteMutex()
625 if ( (this!=ATMutex ) && (ATMutex!=NULL) )
627 ATMutex->enter();
628 (*AcquireTime)[this].Dead = true;
629 ATMutex->leave();
633 void CFairMutex::debugBeginEnter()
635 if ( (this!=ATMutex ) && (ATMutex!=NULL) )
637 TTicks t = CTime::getPerformanceTime();
639 ATMutex->enter();
640 std::map<CMutex*,TMutexLocks>::iterator it = (*AcquireTime).find (this);
641 if (it == (*AcquireTime).end())
643 AcquireTime->insert( make_pair( this, TMutexLocks(NbMutexes++) ) );
644 char dbgstr [256];
645 #ifdef STORE_MUTEX_NAME
646 smprintf( dbgstr, 256, "MUTEX: Creating mutex %p %s (number %u)\n", this, Name.c_str(), NbMutexes-1 );
647 #else
648 smprintf( dbgstr, 256, "MUTEX: Creating mutex %p (number %u)\n", this, NbMutexes-1 );
649 #endif
651 #ifdef NL_OS_WINDOWS
652 if ( IsDebuggerPresent() ) OutputDebugString( dbgstr );
653 #endif
654 cout << dbgstr << endl;
656 it = (*AcquireTime).find (this);
657 #ifdef STORE_MUTEX_NAME
658 (*it).second.MutexName = Name;
659 #endif
661 (*it).second.WaitingMutex++;
662 (*it).second.BeginEnter = t;
663 ATMutex->leave();
668 void CFairMutex::debugEndEnter()
670 // printf("1");
671 /* char str[1024];
672 sprintf(str, "enter %8p %8p %8p\n", this, _Mutex, getThreadId ());
673 if (_Mutex == (void*)0x88)
675 OutputDebugString (str);
676 if (entered) __debugbreak();
677 entered = true;
680 if ( ( this != ATMutex ) && ( ATMutex != NULL ) )
682 TTicks t = CTime::getPerformanceTime();
683 ATMutex->enter();
684 if ((uint32)(t-(*AcquireTime)[this].BeginEnter) > (*AcquireTime)[this].TimeToEnter)
685 (*AcquireTime)[this].TimeToEnter = (uint32)(t-(*AcquireTime)[this].BeginEnter);
686 (*AcquireTime)[this].Nb += 1;
687 (*AcquireTime)[this].WaitingMutex--;
688 (*AcquireTime)[this].ThreadHavingTheMutex = getThreadId();
689 (*AcquireTime)[this].EndEnter = t;
690 ATMutex->leave();
695 void CFairMutex::debugLeave()
697 // printf( "0" );
698 /* char str[1024];
699 sprintf(str, "leave %8p %8p %8p\n", this, _Mutex, getThreadId ());
700 if (_Mutex == (void*)0x88)
702 OutputDebugString (str);
703 if (!entered) __debugbreak();
704 entered = false;
708 if ( ( this != ATMutex ) && ( ATMutex != NULL ) )
710 TTicks Leave = CTime::getPerformanceTime();
711 ATMutex->enter();
712 if ((uint32)(Leave-(*AcquireTime)[this].EndEnter) > (*AcquireTime)[this].TimeInMutex)
713 (*AcquireTime)[this].TimeInMutex = (uint32)(Leave-(*AcquireTime)[this].EndEnter);
714 (*AcquireTime)[this].Nb += 1;
715 (*AcquireTime)[this].WaitingMutex = false;
716 (*AcquireTime)[this].ThreadHavingTheMutex = 0xFFFFFFFF;
717 ATMutex->leave();
722 #endif // MUTEX_DEBUG
724 } // NLMISC