1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
23 #include "nel/misc/mutex.h"
24 #include "nel/misc/time_nl.h"
25 #include "nel/misc/debug.h"
30 #define debugCreateMutex() ;
31 #define debugBeginEnter() ;
32 #define debugEndEnter() ;
33 #define debugLeave() ;
34 #define debugDeleteMutex() ;
52 inline void EnterMutex( void *handle
)
56 if ( IsDebuggerPresent() )
61 // Request ownership of mutex
62 DWORD dwWaitResult
= WaitForSingleObject (handle
, timeout
);
64 // Request ownership of mutex during 10s
65 DWORD dwWaitResult
= WaitForSingleObject (handle
, 10000);
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
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)
117 CUnfairMutex::~CUnfairMutex()
119 CloseHandle( _Mutex
);
126 void CUnfairMutex::enter()
128 EnterMutex( _Mutex
);
135 void CUnfairMutex::leave()
137 LeaveMutex( _Mutex
);
141 /////////////////////////// CSharedMutexW
147 CSharedMutex::CSharedMutex()
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
)
160 nlassert( _Mutex
== NULL
);
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
);
180 void CSharedMutex::enter()
182 EnterMutex( _Mutex
);
189 void CSharedMutex::leave()
191 LeaveMutex( _Mutex
);
195 /////////////////////////// CFairMutex
201 CFairMutex::CFairMutex()
203 #ifdef STORE_MUTEX_NAME
204 Name
= "unset mutex name";
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" );
219 InitializeCriticalSection( (CRITICAL_SECTION
*)&_Cs
);
224 CFairMutex::CFairMutex(const string
&name
)
226 #ifdef STORE_MUTEX_NAME
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" );
244 InitializeCriticalSection( (CRITICAL_SECTION
*)&_Cs
);
253 CFairMutex::~CFairMutex()
255 DeleteCriticalSection( (CRITICAL_SECTION
*)&_Cs
);
264 void CFairMutex::enter()
268 EnterCriticalSection( (CRITICAL_SECTION
*)&_Cs
);
277 void CFairMutex::leave()
279 LeaveCriticalSection( (CRITICAL_SECTION
*)&_Cs
);
288 #elif defined NL_OS_UNIX
294 #include <sys/types.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:"
306 int pthread_mutexattr_setkind_np( pthread_mutexattr_t
*attr
, int kind
);
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
);
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
);
341 CUnfairMutex::~CUnfairMutex()
343 pthread_mutex_destroy( &mutex
);
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" );
360 cout << getpid() << ": Owning " << &mutex << endl;
368 void CUnfairMutex::leave()
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" );
386 cout << getpid() << ": Released " << &mutex << endl;
394 CFairMutex::CFairMutex()
397 _Sem
= dispatch_semaphore_create(1);
399 sem_init( const_cast<sem_t
*>(&_Sem
), 0, 1 );
404 CFairMutex::CFairMutex( const std::string
&name
)
407 _Sem
= dispatch_semaphore_create(1);
409 sem_init( const_cast<sem_t
*>(&_Sem
), 0, 1 );
417 CFairMutex::~CFairMutex()
420 dispatch_release(_Sem
);
422 sem_destroy( const_cast<sem_t
*>(&_Sem
) ); // needs that no thread is waiting on the semaphore
430 void CFairMutex::enter()
433 dispatch_semaphore_wait(_Sem
, DISPATCH_TIME_FOREVER
);
435 sem_wait( const_cast<sem_t
*>(&_Sem
) );
443 void CFairMutex::leave()
446 dispatch_semaphore_signal(_Sem
);
448 sem_post( const_cast<sem_t
*>(&_Sem
) );
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> */
466 /* according to X/OPEN we have to define it ourselves */
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 */
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 );*/
486 _SemId
= semget( key
, 1, IPC_CREAT
| IPC_EXCL
| 0666 );
488 _SemId
= semget( key
, 1, 0666 );
489 nldebug( "Got semid %d", _SemId
);
493 // Initialise the semaphore to 1
496 if ( semctl( _SemId
, 0, SETVAL
, arg
) == -1 )
498 nlwarning( "semid=%d, err=%s", _SemId
, strerror(errno
) );
508 void CSharedMutex::destroy()
510 // Destroy the semaphore
512 nlverifyex( semctl( _SemId
, 0, IPC_RMID
, arg
) != -1, ("semid=%d, err=%s", _SemId
, strerror(errno
) ) );
520 void CSharedMutex::enter()
522 // Decrement the semaphore
526 nlverify( semop( _SemId
, &buf
, 1 ) != -1);
533 void CSharedMutex::leave()
535 // Increment the semaphore
539 nlverify( semop( _SemId
, &buf
, 1 ) != -1);
543 #endif // NL_OS_WINDOWS/NL_OS_UNIX
557 map
<CFairMutex
*,TMutexLocks
> *AcquireTime
= NULL
;
558 uint32 NbMutexes
= 0;
559 CFairMutex
*ATMutex
= NULL
;
563 /// Inits the "mutex debugging info system"
564 void initAcquireTimeMap()
568 ATMutex
= new CFairMutex("ATMutex");
569 AcquireTime
= new map
<CFairMutex
*,TMutexLocks
>;
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
;
585 /* map<CMutex*,TMutexLocks>::iterator im;
586 for ( im=AcquireTime->begin(); im!=AcquireTime->end(); ++im )
588 (*im).second.Time = 0;
590 (*im).second.Locked = false;
598 ////////////////////////
599 ////////////////////////
601 void CFairMutex::debugCreateMutex()
606 AcquireTime->insert( make_pair( this, TMutexLocks(NbMutexes) ) );
610 #ifdef STORE_MUTEX_NAME
611 smprintf( dbgstr, 256, "MUTEX: Creating mutex %p %s (number %u)\n", this, Name.c_str(), NbMutexes-1 );
613 smprintf( dbgstr, 256, "MUTEX: Creating mutex %p (number %u)\n", this, NbMutexes-1 );
616 if ( IsDebuggerPresent() )
617 OutputDebugString( dbgstr );
619 cout << dbgstr << endl;
623 void CFairMutex::debugDeleteMutex()
625 if ( (this!=ATMutex
) && (ATMutex
!=NULL
) )
628 (*AcquireTime
)[this].Dead
= true;
633 void CFairMutex::debugBeginEnter()
635 if ( (this!=ATMutex
) && (ATMutex
!=NULL
) )
637 TTicks t
= CTime::getPerformanceTime();
640 std::map
<CMutex
*,TMutexLocks
>::iterator it
= (*AcquireTime
).find (this);
641 if (it
== (*AcquireTime
).end())
643 AcquireTime
->insert( make_pair( this, TMutexLocks(NbMutexes
++) ) );
645 #ifdef STORE_MUTEX_NAME
646 smprintf( dbgstr
, 256, "MUTEX: Creating mutex %p %s (number %u)\n", this, Name
.c_str(), NbMutexes
-1 );
648 smprintf( dbgstr
, 256, "MUTEX: Creating mutex %p (number %u)\n", this, NbMutexes
-1 );
652 if ( IsDebuggerPresent() ) OutputDebugString( dbgstr
);
654 cout
<< dbgstr
<< endl
;
656 it
= (*AcquireTime
).find (this);
657 #ifdef STORE_MUTEX_NAME
658 (*it
).second
.MutexName
= Name
;
661 (*it
).second
.WaitingMutex
++;
662 (*it
).second
.BeginEnter
= t
;
668 void CFairMutex::debugEndEnter()
672 sprintf(str, "enter %8p %8p %8p\n", this, _Mutex, getThreadId ());
673 if (_Mutex == (void*)0x88)
675 OutputDebugString (str);
676 if (entered) __debugbreak();
680 if ( ( this != ATMutex
) && ( ATMutex
!= NULL
) )
682 TTicks t
= CTime::getPerformanceTime();
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
;
695 void CFairMutex::debugLeave()
699 sprintf(str, "leave %8p %8p %8p\n", this, _Mutex, getThreadId ());
700 if (_Mutex == (void*)0x88)
702 OutputDebugString (str);
703 if (!entered) __debugbreak();
708 if ( ( this != ATMutex
) && ( ATMutex
!= NULL
) )
710 TTicks Leave
= CTime::getPerformanceTime();
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;
722 #endif // MUTEX_DEBUG