blob: e6f2bfa2e027c2975c00db7228060e022448f4fc [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001// Copyright (C) 1999-2005 Open Source Telecom Corporation.
2// Copyright (C) 2006-2010 David Sugar, Tycho Softworks
3//
4// This program is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation; either version 2 of the License, or
7// (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 General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17//
18// As a special exception, you may use this file as part of a free software
19// library without restriction. Specifically, if other files instantiate
20// templates or use macros or inline functions from this file, or you compile
21// this file and link it with other files to produce an executable, this
22// file does not by itself cause the resulting executable to be covered by
23// the GNU General Public License. This exception does not however
24// invalidate any other reasons why the executable file might be covered by
25// the GNU General Public License.
26//
27// This exception applies only to the code released under the name GNU
28// Common C++. If you copy code from other releases into a copy of GNU
29// Common C++, as the General Public License permits, the exception does
30// not apply to the code that you add in this way. To avoid misleading
31// anyone as to the status of such modified files, you must delete
32// this exception notice from them.
33//
34// If you write modifications of your own for GNU Common C++, it is your choice
35// whether to permit this exception to apply to your modifications.
36// If you do not wish that, delete this exception notice.
37//
38
39#include <cc++/config.h>
40#include <cc++/export.h>
41#include <cc++/exception.h>
42#include <cc++/thread.h>
43#include "private.h"
44#include <cc++/slog.h>
45#include <iostream>
46#include <cerrno>
47#include <cstdlib>
48
49#ifdef HAVE_GCC_CXX_BITS_ATOMIC
50using namespace __gnu_cxx;
51#endif
52
53#ifdef CCXX_NAMESPACES
54namespace ost {
55using namespace std;
56#endif
57
58bool Mutex::_debug = false;
59
60ThreadLock::ThreadLock()
61{
62#ifdef HAVE_PTHREAD_RWLOCK
63 pthread_rwlockattr_t attr;
64
65 pthread_rwlockattr_init(&attr);
66 if(pthread_rwlock_init(&_lock, &attr)) {
67 pthread_rwlockattr_destroy(&attr);
68#ifdef CCXX_EXCEPTIONS
69 if(Thread::getException() == Thread::throwObject)
70 throw(this);
71#ifdef COMMON_STD_EXCEPTION
72 else if(Thread::getException() == Thread::throwException)
73 throw(SyncException("Mutex constructor failure"));
74#endif
75#endif
76 }
77 else
78 pthread_rwlockattr_destroy(&attr);
79#endif
80}
81
82ThreadLock::~ThreadLock()
83{
84#ifdef HAVE_PTHREAD_RWLOCK
85 pthread_rwlock_destroy(&_lock);
86#endif
87}
88
89void ThreadLock::readLock(void)
90{
91#ifdef HAVE_PTHREAD_RWLOCK
92 pthread_rwlock_rdlock(&_lock);
93#else
94 mutex.enterMutex();
95#endif
96}
97
98void ThreadLock::writeLock(void)
99{
100#ifdef HAVE_PTHREAD_RWLOCK
101 pthread_rwlock_wrlock(&_lock);
102#else
103 mutex.enterMutex();
104#endif
105}
106
107void ThreadLock::unlock(void)
108{
109#ifdef HAVE_PTHREAD_RWLOCK
110 pthread_rwlock_unlock(&_lock);
111#else
112 mutex.leaveMutex();
113#endif
114}
115
116bool ThreadLock::tryReadLock(void)
117{
118#ifdef HAVE_PTHREAD_RWLOCK
119 if(pthread_rwlock_tryrdlock(&_lock))
120 return false;
121 return true;
122#else
123 return mutex.tryEnterMutex();
124#endif
125}
126
127bool ThreadLock::tryWriteLock(void)
128{
129#ifdef HAVE_PTHREAD_RWLOCK
130 if(pthread_rwlock_trywrlock(&_lock))
131 return false;
132 return true;
133#else
134 return mutex.tryEnterMutex();
135#endif
136}
137
138#ifndef WIN32
139Conditional::Conditional(const char *id)
140{
141 pthread_mutexattr_t attr;
142 pthread_mutexattr_init(&attr);
143 pthread_mutex_init(&_mutex, &attr);
144 pthread_mutexattr_destroy(&attr);
145 if(pthread_cond_init(&_cond, NULL) && Thread::getException() == Thread::throwObject)
146 THROW(this);
147}
148
149Conditional::~Conditional()
150{
151 pthread_cond_destroy(&_cond);
152 pthread_mutex_destroy(&_mutex);
153}
154
155bool Conditional::tryEnterMutex(void)
156{
157 if(pthread_mutex_trylock(&_mutex) != 0)
158 return false;
159 return true;
160}
161
162void Conditional::enterMutex(void)
163{
164 pthread_mutex_lock(&_mutex);
165}
166
167void Conditional::leaveMutex(void)
168{
169 pthread_mutex_unlock(&_mutex);
170}
171
172void Conditional::signal(bool broadcast)
173{
174 if(broadcast)
175 pthread_cond_broadcast(&_cond);
176 else
177 pthread_cond_signal(&_cond);
178}
179
180bool Conditional::wait(timeout_t timeout, bool locked)
181{
182 struct timespec ts;
183 int rc;
184
185 if(!locked)
186 enterMutex();
187 if(!timeout) {
188 pthread_cond_wait(&_cond, &_mutex);
189 if(!locked)
190 leaveMutex();
191 return true;
192 }
193 getTimeout(&ts, timeout);
194 rc = pthread_cond_timedwait(&_cond, &_mutex, &ts);
195 if(!locked)
196 leaveMutex();
197 if(rc == ETIMEDOUT)
198 return false;
199 return true;
200}
201
202#endif
203
204#ifndef WIN32
205Mutex::Mutex(const char *name)
206{
207 pthread_mutexattr_t _attr;
208
209 pthread_mutexattr_init(&_attr);
210#ifdef PTHREAD_MUTEXTYPE_RECURSIVE
211 pthread_mutexattr_settype(&_attr, PTHREAD_MUTEXTYPE_RECURSIVE);
212#endif
213 pthread_mutex_init(&_mutex, &_attr);
214 pthread_mutexattr_destroy(&_attr);
215
216#ifndef PTHREAD_MUTEXTYPE_RECURSIVE
217 _level = 0;
218 _tid = NULL;
219#endif
220 _name = name;
221}
222
223Mutex::~Mutex()
224{
225 pthread_mutex_destroy(&_mutex);
226}
227
228#ifdef PTHREAD_MUTEXTYPE_RECURSIVE
229
230bool Mutex::tryEnterMutex(void)
231{
232 return (pthread_mutex_trylock(&_mutex) == 0) ? true : false;
233}
234
235void Mutex::enterMutex(void)
236{
237 if(_debug && _name)
238 slog.debug() << Thread::get()->getName()
239 << ": entering " << _name << std::endl;
240
241 pthread_mutex_lock(&_mutex);
242}
243
244void Mutex::leaveMutex(void)
245{
246 pthread_mutex_unlock(&_mutex);
247 if(_debug && _name)
248 slog.debug() << Thread::get()->getName()
249 << ": leaving" << _name << std::endl;
250
251}
252
253#else // !PTHREAD_MUTEXTYPE_RECURSIVE
254
255void Mutex::enterMutex(void)
256{
257 if(_tid == Thread::get()) {
258 ++_level;
259 return;
260 }
261 if(_debug && _name)
262 std::cerr << Thread::get()->getName() << ": entering" << _name << std::endl;
263
264 pthread_mutex_lock(&_mutex);
265 ++_level;
266 _tid = Thread::get();
267}
268
269void Mutex::leaveMutex(void)
270{
271 if(_tid != Thread::get())
272 return;
273 if(--_level > 0)
274 return;
275 _tid = NULL;
276 _level = 0;
277 pthread_mutex_unlock(&_mutex);
278 if(_debug && _name)
279 std::cerr << Thread::get()->getName() << ": leaving" << _name << std::endl;
280}
281
282bool Mutex::tryEnterMutex(void)
283{
284 if(_tid == Thread::get()) {
285 ++_level;
286 return true;
287 }
288 if ( pthread_mutex_trylock(&_mutex) != 0 )
289 return false;
290 _tid = Thread::get();
291 ++_level;
292 return true;
293}
294#endif
295
296#else // WIN32
297
298Mutex::Mutex(const char *name)
299#ifdef MUTEX_UNDERGROUND_WIN32_MUTEX
300:_mutex(0)
301#endif // MUTEX_UNDERGROUND_WIN32_MUTEX
302{
303#ifdef MUTEX_UNDERGROUND_WIN32_CRITICALSECTION
304#if _WIN32_WINNT >= 0x0403
305 if(!InitializeCriticalSectionAndSpinCount(&_criticalSection, 4000)) {
306 THROW(this);
307 }
308#elif _WIN32_WINNT >= 0x0400
309 // can rise STATUS_NO_MEMORY exception in low memory situations.
310 InitializeCriticalSection(&_criticalSection);
311#else
312#error "Not supported Windows version"
313#endif
314#endif // MUTEX_UNDERGROUND_WIN32_CRITICALSECTION
315
316#ifdef MUTEX_UNDERGROUND_WIN32_MUTEX
317 _mutex = ::CreateMutex(NULL,FALSE,NULL);
318 if(!_mutex)
319 THROW(this);
320#endif // MUTEX_UNDERGROUND_WIN32_MUTEX
321
322
323 _name = name;
324}
325
326void Mutex::enterMutex(void)
327{
328 if(_debug && _name)
329 slog.debug() << Thread::get()->getName()
330 << ": entering " << _name << std::endl;
331
332#ifdef MUTEX_UNDERGROUND_WIN32_CRITICALSECTION
333 ::EnterCriticalSection(&_criticalSection);
334#endif // MUTEX_UNDERGROUND_WIN32_CRITICALSECTION
335
336#ifdef MUTEX_UNDERGROUND_WIN32_MUTEX
337 Thread::waitThread(_mutex, INFINITE);
338#endif // MUTEX_UNDERGROUND_WIN32_MUTEX
339
340}
341
342bool Mutex::tryEnterMutex(void)
343{
344#ifdef MUTEX_UNDERGROUND_WIN32_CRITICALSECTION
345 return (::TryEnterCriticalSection(&_criticalSection) == TRUE);
346#endif // MUTEX_UNDERGROUND_WIN32_CRITICALSECTION
347
348#ifdef MUTEX_UNDERGROUND_WIN32_MUTEX
349 return (Thread::waitThread(_mutex, 0) == WAIT_OBJECT_0);
350#endif // MUTEX_UNDERGROUND_WIN32_MUTEX
351}
352
353Mutex::~Mutex()
354{
355#ifdef MUTEX_UNDERGROUND_WIN32_CRITICALSECTION
356 ::DeleteCriticalSection(&_criticalSection);
357#endif // MUTEX_UNDERGROUND_WIN32_CRITICALSECTION
358
359#ifdef MUTEX_UNDERGROUND_WIN32_MUTEX
360 ::CloseHandle(_mutex);
361#endif // MUTEX_UNDERGROUND_WIN32_MUTEX
362
363}
364
365void Mutex::leaveMutex(void)
366{
367#ifdef MUTEX_UNDERGROUND_WIN32_CRITICALSECTION
368 ::LeaveCriticalSection(&_criticalSection);
369#endif // MUTEX_UNDERGROUND_WIN32_CRITICALSECTION
370
371#ifdef MUTEX_UNDERGROUND_WIN32_MUTEX
372 if (!ReleaseMutex(_mutex))
373 THROW(this);
374#endif // MUTEX_UNDERGROUND_WIN32_MUTEX
375
376 if(_debug && _name)
377 slog.debug() << Thread::get()->getName()
378 << ": leaving" << _name << std::endl;
379}
380#endif // WIN32 MUTEX
381
382#ifdef WIN32
383MutexCounter::MutexCounter(const char *id) : Mutex(id)
384{
385 counter = 0;
386};
387#endif
388
389MutexCounter::MutexCounter(int initial, const char *id) : Mutex(id)
390{
391 counter = initial;
392}
393
394int operator++(MutexCounter &mc)
395{
396 int rtn;
397
398 mc.enterMutex();
399 rtn = mc.counter++;
400 mc.leaveMutex();
401 return rtn;
402}
403
404// ??? why cannot be < 0 ???
405int operator--(MutexCounter &mc)
406{
407 int rtn = 0;
408
409 mc.enterMutex();
410 if(mc.counter) {
411 rtn = --mc.counter;
412 if(!rtn) {
413 mc.leaveMutex();
414 THROW(mc);
415 }
416 }
417 mc.leaveMutex();
418 return rtn;
419}
420
421#ifndef CCXX_USE_WIN32_ATOMIC
422#ifdef HAVE_ATOMIC
423AtomicCounter::AtomicCounter()
424{
425#if defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
426 || defined(HAVE_GCC_CXX_BITS_ATOMIC)
427 counter = 0;
428#else
429 atomic.counter = 0;
430#endif
431}
432
433AtomicCounter::AtomicCounter(int value)
434{
435#if defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
436 || defined(HAVE_GCC_CXX_BITS_ATOMIC)
437 counter = 0;
438#else
439 atomic.counter = value;
440#endif
441}
442
443AtomicCounter::~AtomicCounter() {};
444
445int AtomicCounter::operator++(void)
446{
447#ifdef HAVE_ATOMIC_AIX
448 return fetch_and_add((atomic_p)&counter, 1);
449#elif defined(HAVE_GCC_BITS_ATOMIC) || defined(HAVE_GCC_CXX_BITS_ATOMIC)
450 // Modified by JCE from 2v1.3.8 source, 30/Mar/2005
451 // BUG FIX: __exchange_and_add() does not seem to return updated <counter>
452 __exchange_and_add(&counter, 1);
453 return counter;
454 // end modification by JCE
455#else
456 atomic_inc(&atomic);
457 return atomic_read(&atomic);
458#endif
459}
460
461int AtomicCounter::operator--(void)
462{
463#ifdef HAVE_ATOMIC_AIX
464 return fetch_and_add((atomic_p)&counter, -1);
465#elif defined(HAVE_GCC_BITS_ATOMIC) || defined(HAVE_GCC_CXX_BITS_ATOMIC)
466 // Modified by JCE from 2v1.3.8 source, 30/Mar/2005
467 // BUG FIX: __exchange_and_add() does not seem to return updated <counter>
468 __exchange_and_add(&counter, -1);
469 return counter;
470 // end modification by JCE
471#else
472 int chk = atomic_dec_and_test(&atomic);
473 if(chk)
474 return 0;
475 chk = atomic_read(&atomic);
476 if(!chk)
477 ++chk;
478 return chk;
479#endif
480}
481
482int AtomicCounter::operator+=(int change)
483{
484#ifdef HAVE_ATOMIC_AIX
485 return fetch_and_add((atomic_p)&counter, change);
486#elif defined(HAVE_GCC_BITS_ATOMIC) || defined(HAVE_GCC_CXX_BITS_ATOMIC)
487 // Modified by JCE from 2v1.3.8 source, 30/Mar/2005
488 // BUG FIX: __exchange_and_add() does not seem to return updated <counter>
489 __exchange_and_add(&counter, change);
490 return counter;
491 // end modification by JCE
492#else
493 atomic_add(change, &atomic);
494 return atomic_read(&atomic);
495#endif
496}
497
498int AtomicCounter::operator-=(int change)
499{
500#ifdef HAVE_ATOMIC_AIX
501 return fetch_and_add((atomic_p)&counter, -change);
502#elif defined(HAVE_GCC_BITS_ATOMIC) || defined(HAVE_GCC_CXX_BITS_ATOMIC)
503 // Modified by JCE from 2v1.3.8 source, 30/Mar/2005
504 // BUG FIX: __exchange_and_add() does not seem to return updated <counter>
505 __exchange_and_add(&counter, -change);
506 return counter;
507 // end modification by JCE
508#else
509 atomic_sub(change, &atomic);
510 return atomic_read(&atomic);
511#endif
512}
513
514int AtomicCounter::operator+(int change)
515{
516#if defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
517 || defined(HAVE_GCC_CXX_BITS_ATOMIC)
518 return counter + change;
519#else
520 return atomic_read(&atomic) + change;
521#endif
522}
523
524int AtomicCounter::operator-(int change)
525{
526#if defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
527 || defined(HAVE_GCC_CXX_BITS_ATOMIC)
528 return counter - change;
529#else
530 return atomic_read(&atomic) - change;
531#endif
532}
533
534int AtomicCounter::operator=(int value)
535{
536#if defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
537 || defined(HAVE_GCC_CXX_BITS_ATOMIC)
538 return counter = value;
539#else
540 atomic_set(&atomic, value);
541 return atomic_read(&atomic);
542#endif
543}
544
545bool AtomicCounter::operator!(void)
546{
547#if defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
548 || defined(HAVE_GCC_CXX_BITS_ATOMIC)
549 int value = counter;
550#else
551 int value = atomic_read(&atomic);
552#endif
553 if(value)
554 return false;
555 return true;
556}
557
558AtomicCounter::operator int()
559{
560#if defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
561 || defined(HAVE_GCC_CXX_BITS_ATOMIC)
562 return counter;
563#else
564 return atomic_read(&atomic);
565#endif
566}
567
568#else // !HAVE_ATOMIC
569
570AtomicCounter::AtomicCounter()
571{
572 counter = 0;
573
574 pthread_mutexattr_t _attr;
575 pthread_mutexattr_init(&_attr);
576 pthread_mutex_init(&_mutex, &_attr);
577 pthread_mutexattr_destroy(&_attr);
578}
579
580AtomicCounter::AtomicCounter(int value)
581{
582 counter = value;
583
584 pthread_mutexattr_t _attr;
585 pthread_mutexattr_init(&_attr);
586 pthread_mutex_init(&_mutex, &_attr);
587 pthread_mutexattr_destroy(&_attr);
588}
589
590AtomicCounter::~AtomicCounter()
591{
592 pthread_mutex_destroy(&_mutex);
593}
594
595int AtomicCounter::operator++(void)
596{
597 int value;
598
599 pthread_mutex_lock(&_mutex);
600 value = ++counter;
601 pthread_mutex_unlock(&_mutex);
602 return value;
603}
604
605int AtomicCounter::operator--(void)
606{
607 int value;
608 pthread_mutex_lock(&_mutex);
609 value = --counter;
610 pthread_mutex_unlock(&_mutex);
611 return value;
612}
613
614int AtomicCounter::operator+=(int change)
615{
616 int value;
617 pthread_mutex_lock(&_mutex);
618 counter += change;
619 value = counter;
620 pthread_mutex_unlock(&_mutex);
621 return value;
622}
623
624int AtomicCounter::operator-=(int change)
625{
626 int value;
627 pthread_mutex_lock(&_mutex);
628 counter -= change;
629 value = counter;
630 pthread_mutex_unlock(&_mutex);
631 return value;
632}
633
634int AtomicCounter::operator+(int change)
635{
636 int value;
637 pthread_mutex_lock(&_mutex);
638 value = counter + change;
639 pthread_mutex_unlock(&_mutex);
640 return value;
641}
642
643int AtomicCounter::operator-(int change)
644{
645 int value;
646 pthread_mutex_lock(&_mutex);
647 value = counter - change;
648 pthread_mutex_unlock(&_mutex);
649 return value;
650}
651
652AtomicCounter::operator int()
653{
654 int value;
655 pthread_mutex_lock(&_mutex);
656 value = counter;
657 pthread_mutex_unlock(&_mutex);
658 return value;
659}
660
661int AtomicCounter::operator=(int value)
662{
663 int ret;
664 pthread_mutex_lock(&_mutex);
665 ret = counter;
666 counter = value;
667 pthread_mutex_unlock(&_mutex);
668 return ret;
669}
670
671bool AtomicCounter::operator!(void)
672{
673 int value;
674 pthread_mutex_lock(&_mutex);
675 value = counter;
676 pthread_mutex_unlock(&_mutex);
677 if(value)
678 return false;
679 return true;
680}
681#endif // HAVE_ATOMIC
682#else // WIN32
683int AtomicCounter::operator+=(int change)
684{
685 // FIXME: enhance with InterlockExchangeAdd
686 while(--change>=0)
687 InterlockedIncrement(&atomic);
688
689 return atomic;
690}
691
692int AtomicCounter::operator-=(int change)
693{
694 // FIXME: enhance with InterlockExchangeAdd
695 while(--change>=0)
696 InterlockedDecrement(&atomic);
697
698 return atomic;
699}
700#endif // !WIN32
701
702#ifdef CCXX_NAMESPACES
703}
704#endif
705
706/** EMACS **
707 * Local variables:
708 * mode: c++
709 * c-basic-offset: 4
710 * End:
711 */
712