cppthread 1.1.16
C++ Thread Library
mutex.cpp
Go to the documentation of this file.
1// Copyright (c) 2013-2025 Made to Order Software Corp. All Rights Reserved
2//
3// https://snapwebsites.org/project/cppthread
4// contact@m2osw.com
5//
6// This program is free software; you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation; either version 2 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License along
17// with this program; if not, write to the Free Software Foundation, Inc.,
18// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
27// self
28//
29#include "cppthread/mutex.h"
30
31#include "cppthread/exception.h"
32#include "cppthread/guard.h"
33#include "cppthread/log.h"
34
35
36// snapdev
37//
38#include <snapdev/timespec_ex.h>
39
40
41// C
42//
43#include <string.h>
44#include <sys/time.h>
45
46
47// last include
48//
49#include <snapdev/poison.h>
50
51
52
53
54namespace cppthread
55{
56
57
58
59namespace detail
60{
61
62
63
73{
74public:
75 pthread_mutex_t f_mutex = pthread_mutex_t();
76 pthread_cond_t f_condition = pthread_cond_t();
77};
78
79
106}
107
108
109
230 : f_impl(std::make_shared<detail::mutex_impl>())
231{
232 // initialize the mutex
233 pthread_mutexattr_t mattr;
234 int err(pthread_mutexattr_init(&mattr));
235 if(err != 0)
236 {
237 log << log_level_t::fatal
238 << "a mutex attribute structure could not be initialized, error #"
239 << err
240 << end;
241 throw invalid_error("pthread_muteattr_init() failed");
242 }
243 err = pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
244 if(err != 0)
245 {
246 log << log_level_t::fatal
247 << "a mutex attribute structure type could not be setup, error #"
248 << err
249 << end;
250 pthread_mutexattr_destroy(&mattr);
251 throw invalid_error("pthread_muteattr_settype() failed");
252 }
253 err = pthread_mutex_init(&f_impl->f_mutex, &mattr);
254 if(err != 0)
255 {
256 log << log_level_t::fatal
257 << "a mutex structure could not be initialized, error #"
258 << err
259 << end;
260 pthread_mutexattr_destroy(&mattr);
261 throw invalid_error("pthread_mutex_init() failed");
262 }
263 err = pthread_mutexattr_destroy(&mattr);
264 if(err != 0)
265 {
266 log << log_level_t::fatal
267 << "a mutex attribute structure could not be destroyed, error #"
268 << err
269 << end;
270 pthread_mutex_destroy(&f_impl->f_mutex);
271 throw invalid_error("pthread_mutexattr_destroy() failed");
272 }
273
274 // initialize the condition
275 pthread_condattr_t cattr;
276 err = pthread_condattr_init(&cattr);
277 if(err != 0)
278 {
279 log << log_level_t::fatal
280 << "a mutex condition attribute structure could not be initialized, error #"
281 << err
282 << end;
283 pthread_mutex_destroy(&f_impl->f_mutex);
284 throw invalid_error("pthread_condattr_init() failed");
285 }
286 err = pthread_cond_init(&f_impl->f_condition, &cattr);
287 if(err != 0)
288 {
289 log << log_level_t::fatal
290 << "a mutex condition structure could not be initialized, error #"
291 << err
292 << end;
293 pthread_condattr_destroy(&cattr);
294 pthread_mutex_destroy(&f_impl->f_mutex);
295 throw invalid_error("pthread_cond_init() failed");
296 }
297 err = pthread_condattr_destroy(&cattr);
298 if(err != 0)
299 {
300 log << log_level_t::fatal
301 << "a mutex condition attribute structure could not be destroyed, error #"
302 << err
303 << end;
304 pthread_mutex_destroy(&f_impl->f_mutex);
305 throw invalid_error("pthread_condattr_destroy() failed");
306 }
307}
308
309
320{
321 // Note that the following reference count test only ensure that
322 // you don't delete a mutex which is still locked; however, if
323 // you still have multiple threads running, we can't really know
324 // if another thread is not just about to use this thread...
325 //
326 if(f_reference_count != 0UL)
327 {
328 // we cannot legally throw in a destructor so we instead generate a fatal error
329 //
330 log << log_level_t::fatal
331 << "a mutex is being destroyed when its reference count is "
333 << " instead of zero."
334 << end;
335 std::terminate();
336 }
337 int err(pthread_cond_destroy(&f_impl->f_condition));
338 if(err != 0)
339 {
340 log << log_level_t::error
341 << "a mutex condition destruction generated error #"
342 << err
343 << end;
344 }
345 err = pthread_mutex_destroy(&f_impl->f_mutex);
346 if(err != 0)
347 {
348 log << log_level_t::fatal
349 << "a mutex destruction generated error #"
350 << err
351 << end;
352 }
353}
354
355
370{
371 int const err(pthread_mutex_lock(&f_impl->f_mutex));
372 if(err != 0)
373 {
374 log << log_level_t::error
375 << "a mutex lock generated error #"
376 << err
377 << " -- "
378 << strerror(err)
379 << end;
380 throw invalid_error("pthread_mutex_lock() failed");
381 }
382
383 // note: we do not need an atomic call since we
384 // already know we are running alone here...
386}
387
388
401{
402 int const err(pthread_mutex_trylock(&f_impl->f_mutex));
403 if(err == 0)
404 {
405 // note: we do not need an atomic call since we
406 // already know we are running alone here...
408 return true;
409 }
410
411 // failed because another thread has the lock?
412 if(err == EBUSY)
413 {
414 return false;
415 }
416
417 // another type of failure
418 log << log_level_t::error
419 << "a mutex try lock generated error #"
420 << err
421 << " -- "
422 << strerror(err)
423 << end;
424 throw invalid_error("pthread_mutex_trylock() failed");
425}
426
427
444{
445 // We can't unlock if it wasn't locked before!
446 if(f_reference_count <= 0UL)
447 {
448 log << log_level_t::fatal
449 << "attempting to unlock a mutex when it is still locked "
451 << " times"
452 << end;
453 throw not_locked_error("unlock was called too many times");
454 }
455
456 // NOTE: we do not need an atomic call since we
457 // already know we are running alone here...
459
460 int const err(pthread_mutex_unlock(&f_impl->f_mutex));
461 if(err != 0)
462 {
463 log << log_level_t::fatal
464 << "a mutex unlock generated error #"
465 << err
466 << " -- "
467 << strerror(err)
468 << end;
469 throw invalid_error("pthread_mutex_unlock() failed");
470 }
471}
472
473
495{
496 // For any mutex wait to work, we MUST have the
497 // mutex locked already and just one time.
498 //
499 // note: the 1 time is just for assurance that it will
500 // work in most cases; it should work even when locked
501 // multiple times, but it is less likely. For sure, it
502 // has to be at least once.
503 //if(f_reference_count != 1UL)
504 //{
505 // log << log_level_t::fatal
506 // << "attempting to wait on a mutex when it is not locked exactly once, current count is "
507 // << f_reference_count
508 // << end;
509 // throw exception_not_locked_once_error();
510 //}
511 int const err(pthread_cond_wait(&f_impl->f_condition, &f_impl->f_mutex));
512 if(err != 0)
513 {
514 // an error occurred!
515 log << log_level_t::fatal
516 << "a mutex conditional wait generated error #"
517 << err
518 << " -- "
519 << strerror(err)
520 << end;
521 throw mutex_failed_error("pthread_cond_wait() failed");
522 }
523}
524
525
549bool mutex::timed_wait(std::uint64_t const usecs)
550{
551 return timed_wait(timespec{
552 static_cast<time_t>(usecs / 1'000'000ULL)
553 , static_cast<long>((usecs % 1'000'000ULL) * 1'000ULL)
554 });
555}
556
557
583bool mutex::timed_wait(timespec const & nsecs)
584{
585 int err(0);
586
587 // get clock time (a.k.a. now)
588 //
589 snapdev::timespec_ex abstime(snapdev::timespec_ex::gettime());
590
591 // now + user specified nsecs
592 //
593 abstime += nsecs;
594
595 err = pthread_cond_timedwait(
596 &f_impl->f_condition
597 , &f_impl->f_mutex
598 , &abstime);
599 if(err != 0)
600 {
601 if(err == ETIMEDOUT)
602 {
603 return false;
604 }
605
606 // an error occurred!
607 log << log_level_t::fatal
608 << "a mutex conditional timed wait generated error #"
609 << err
610 << " -- "
611 << strerror(err)
612 << " (time out sec = "
613 << abstime.tv_sec
614 << ", nsec = "
615 << abstime.tv_nsec
616 << ")"
617 << end;
618 throw mutex_failed_error("pthread_cond_timedwait() failed");
619 }
620
621 return true;
622}
623
624
642bool mutex::dated_wait(std::uint64_t const usec)
643{
644 return dated_wait(timespec{
645 static_cast<long>(usec / 1'000'000ULL)
646 , static_cast<long>((usec % 1'000'000ULL) * 1'000ULL)
647 });
648}
649
650
668bool mutex::dated_wait(timespec const & date)
669{
670 // For any mutex wait to work, we MUST have the
671 // mutex locked already and just one time.
672 //
673 // note: the 1 time is just for assurance that it will
674 // work in most cases; it should work even when locked
675 // multiple times, but it is less likely. For sure, it
676 // has to be at least once.
677 //if(f_reference_count != 1UL)
678 //{
679 // log << log_level_t::fatal
680 // << "attempting to dated wait until "
681 // << usec
682 // << " msec on a mutex when it is not locked exactly once, current count is "
683 // << f_reference_count
684 // << end;
685 // throw exception_not_locked_once_error();
686 //}
687
688 int const err(pthread_cond_timedwait(
689 &f_impl->f_condition
690 , &f_impl->f_mutex
691 , &date));
692 if(err != 0)
693 {
694 if(err == ETIMEDOUT)
695 {
696 return false;
697 }
698
699 // an error occurred!
700 log << log_level_t::error
701 << "a mutex conditional wait generated error #"
702 << err
703 << " -- "
704 << strerror(err)
705 << " (time out sec = "
706 << date.tv_sec
707 << ", nsec = "
708 << date.tv_nsec
709 << ")"
710 << end;
711 throw mutex_failed_error("pthread_cond_timedwait() failed");
712 }
713
714 return true;
715}
716
717
738{
739 int const err(pthread_cond_signal(&f_impl->f_condition));
740 if(err != 0)
741 {
742 log << log_level_t::fatal
743 << "a mutex condition signal generated error #"
744 << err
745 << end;
746 throw invalid_error("pthread_cond_signal() failed");
747 }
748}
749
750
766{
767 guard lock(*this);
768
769 int const err(pthread_cond_signal(&f_impl->f_condition));
770 if(err != 0)
771 {
772 log << log_level_t::fatal
773 << "a mutex condition signal generated error #"
774 << err
775 << end;
776 throw invalid_error("pthread_cond_signal() failed");
777 }
778}
779
780
800{
801 guard lock(*this);
802
803 int const err(pthread_cond_broadcast(&f_impl->f_condition));
804 if(err != 0)
805 {
806 log << log_level_t::fatal
807 << "a mutex signal broadcast generated error #"
808 << err
809 << end;
810 throw invalid_error("pthread_cond_broadcast() failed");
811 }
812}
813
814
839{
840 guard lock(*this);
841
842 int const err(pthread_cond_broadcast(&f_impl->f_condition));
843 if(err != 0)
844 {
845 log << log_level_t::fatal
846 << "a mutex signal broadcast generated error #"
847 << err
848 << end;
849 throw invalid_error("pthread_cond_broadcast() failed");
850 }
851}
852
853
889
890
927{
928 if(g_system_mutex != nullptr)
929 {
930 std::cerr << "fatal: create_system_mutex() called twice." << std::endl;
931 std::terminate();
932 }
933
934 g_system_mutex = new mutex;
935}
936
937
938
939
986} // namespace cppthread
987// vim: ts=4 sw=4 et
The implementation of the mutex.
Definition mutex.cpp:73
pthread_cond_t f_condition
Condition linked to the mutex to support signalling.
Definition mutex.cpp:76
pthread_mutex_t f_mutex
Mutex to support guards & signals.
Definition mutex.cpp:75
Lock a mutex in an RAII manner.
Definition guard.h:42
A mutex object to ensures atomicity.
Definition mutex.h:55
void lock()
Lock a mutex.
Definition mutex.cpp:369
bool dated_wait(std::uint64_t const date)
Wait on a mutex until the specified date.
Definition mutex.cpp:642
bool timed_wait(std::uint64_t const usec)
Wait on a mutex condition with a time limit.
Definition mutex.cpp:549
void wait()
Wait on a mutex condition.
Definition mutex.cpp:494
void safe_signal()
Signal a mutex.
Definition mutex.cpp:765
mutex()
An inter-thread mutex to ensure unicity of execution.
Definition mutex.cpp:229
void unlock()
Unlock a mutex.
Definition mutex.cpp:443
void signal()
Signal at least one mutex.
Definition mutex.cpp:737
std::shared_ptr< detail::mutex_impl > f_impl
The pthread mutex implementation.
Definition mutex.h:82
std::uint32_t f_reference_count
The lock reference count.
Definition mutex.h:84
void broadcast()
Broadcast a mutex signal.
Definition mutex.cpp:838
void safe_broadcast()
Broadcast a mutex signal.
Definition mutex.cpp:799
bool try_lock()
Try locking the mutex.
Definition mutex.cpp:400
~mutex()
Clean up a mutex object.
Definition mutex.cpp:319
Exceptions for the thread environment.
Thread Runner and Managers.
void create_system_mutex()
Function used to initialize the system mutex.
Definition mutex.cpp:926
logger log
The logger object used to send logs out.
Definition log.cpp:90
Declaration of the log class used to send error messages.
logger & end(logger &l)
Close a log statement.
Definition log.h:90
mutex * g_system_mutex
The system mutex.
Definition mutex.cpp:888
Thread Runner and Managers.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.