1 /***************************************************************************
2  *                                                                         *
3  *   LinuxSampler - modular, streaming capable sampler                     *
4  *                                                                         *
5  *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *
6  *   Copyright (C) 2005 - 2020 Christian Schoenebeck                       *
7  *                                                                         *
8  *   This program is free software; you can redistribute it and/or modify  *
9  *   it under the terms of the GNU General Public License as published by  *
10  *   the Free Software Foundation; either version 2 of the License, or     *
11  *   (at your option) any later version.                                   *
12  *                                                                         *
13  *   This program is distributed in the hope that it will be useful,       *
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
16  *   GNU General Public License for more details.                          *
17  *                                                                         *
18  *   You should have received a copy of the GNU General Public License     *
19  *   along with this program; if not, write to the Free Software           *
20  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
21  *   MA  02111-1307  USA                                                   *
22  ***************************************************************************/
23 
24 #ifndef __MUTEX_H__
25 #define __MUTEX_H__
26 
27 // enable this for detecting mutex misusage and for debugging dead locks
28 // (these features then still need to be enabled by Mutex::setDebugEnabled())
29 //#define DEBUG_MUTEX 1
30 
31 #if defined(WIN32)
32 #include <windows.h>
33 #else
34 #include <pthread.h>
35 #endif
36 
37 namespace LinuxSampler {
38 
39 /** @brief Mutual exclusive objects
40  *
41  * This class provides the classical thread / process synchronisation
42  * technique called Mutex. It is used to protect critical sections, that is
43  * resources (typically data structures) from being used at the same time by
44  * different threads or processes which otherwise might turn into undefined
45  * and of course undesired behavior.
46  *
47  * Note: as this technique might block the calling thread and also implies
48  * a system call, this should not be used directly in realtime sensitive
49  * threads!
50  */
51 class Mutex {
52     public:
53         enum type_t {
54             RECURSIVE,
55             NON_RECURSIVE
56         };
57 
58         /** @brief Constructor
59          *
60          * Creates a new Mutex object. The optional @a type argument defines
61          * the fundamental behavior of the Mutex object:
62          *
63          * - If @c RECURSIVE is passed (which is the default type) then the
64          *   mutex will manage an additional lock count such that it allows the
65          *   same thread to call Lock() multiple times; each time that thread
66          *   calls Lock() the lock count will be increased by one, each time it
67          *   calls Unlock() it will be decreased by one, and other threads will
68          *   only be unblocked once the lock count fell to zero again.
69          *
70          * - If @c NON_RECURSIVE is passed then it is considered to be an error
71          *   if the same thread calls Lock() while already owning the lock, and
72          *   likewise it is considered to be an error if Unlock() is called if
73          *   the calling thread hasn't locked the mutex.
74          *
75          * You should invest the required time to review your design in order to
76          * decide which mutex behavior fits to your design. Even though it might
77          * be tempting to stick with the lazy approach by using the @c RECURSIVE
78          * type, using the @c NON_RECURSIVE type does make sense if your design
79          * does not require a recursive mutex, because modern developer tools
80          * assist you spotting potential threading bugs in your code while using
81          * the @c NON_RECURSIVE type which can avoid developers' biggest fear of
82          * undefined behavior, plus also keep in mind that certain OS APIs are
83          * not compatible with recursive mutexes at all!
84          *
85          * @param type - optional: the fundamental behavior type for this mutex
86          *               (default: @c RECURSIVE)
87          */
88         Mutex(type_t type = RECURSIVE);
89 
90         /**
91          * Destructor
92          */
93         virtual ~Mutex();
94 
95         /** @brief Lock this Mutex.
96          *
97          * If this Mutex object is currently be locked by another thread,
98          * then the calling thread will be blocked until the other thread
99          * unlocks this Mutex object. The calling thread though can safely
100          * call this method several times without danger to be blocked
101          * himself.
102          *
103          * The calling thread should call Unlock() as soon as the critical
104          * section was left.
105          */
106         void Lock();
107 
108         /** @brief Try to lock this Mutex.
109          *
110          * Same as Lock() except that this method won't block the calling
111          * thread in case this Mutex object is currently locked by another
112          * thread. So this call will always immediately return and the
113          * return value has to be checked if the locking request was
114          * successful or not.
115          *
116          * @returns  true if the Mutex object could be locked, false if the
117          *           Mutex is currently locked by another thread
118          */
119         bool Trylock();
120 
121         /** @brief Unlock this Mutex.
122          *
123          * If other threads are currently blocked and waiting due to a
124          * Lock() call, one of them will be awaken.
125          */
126         void Unlock();
127 
128 #if DEBUG_MUTEX
129     /** @brief Enable bug detection and debugging features.
130      *
131      * By passing @c true to this method, bug detection and debugging features
132      * will be enabled for this Mutex object. For instance this will trigger an
133      * assertion fault if a thread attempts to Unlock() a thread it does not own
134      * a lock on, or when locking more than once while not using mutex type
135      * @c RECURSIVE and much more. Additionally this will also record the name
136      * of the thread currently holding a lock, and the backtrace of that
137      * thread's lock. The latter information can then be used to debug
138      * deadlocks.
139      *
140      * By default this is turned off and must be enabled for individual Mutex
141      * objects, because otherwise it would cause a large number of false
142      * positives (i.e. in certain edge cases like thread constructors /
143      * destructors for instance).
144      *
145      * @param b - whether to enable bug detection / debugging features
146      */
setDebugEnabled(bool b)147     void setDebugEnabled(bool b) {
148         debugSelf = b;
149     }
150 #endif
151 
152     protected:
153     #if defined(WIN32)
154         HANDLE hMutex;
155     #else
156         pthread_mutex_t     __posix_mutex;
157         pthread_mutexattr_t __posix_mutexattr;
158     #endif
159         type_t type;
160     #if DEBUG_MUTEX
161         std::string owner; ///< Name of the thread owning the current lock.
162         long long int count; ///< How many times the owner currently acquired the lock recursively.
163         std::string backtrace; ///< Call stack trace of (last) lock.
164         bool debugSelf; ///< Whether bug detection and debugging features are enabled.
165     #endif
166 };
167 
168 // Lock guard for exception safe locking
169 class LockGuard {
170 public:
LockGuard(Mutex & m)171     LockGuard(Mutex& m) : pm(&m) {
172         m.Lock();
173     }
174 
175     /**
176      * Empty LockGuard. This constructor can be used to implement conditional
177      * mutex protection like:
178      * @code
179      * Mutex m;
180      * LockGuard g;
181      * if (requiresMutexProtection()) g = LockGuard(m);
182      * @endcode
183      */
LockGuard()184     LockGuard() : pm(NULL) {
185     }
186 
LockGuard(const LockGuard & g)187     LockGuard(const LockGuard& g) : pm(g.pm) {
188         if (pm) pm->Lock();
189     }
190 
~LockGuard()191     ~LockGuard() {
192         if (pm) pm->Unlock();
193     }
194 private:
195     Mutex* pm;
196 };
197 
198 } // namespace LinuxSampler
199 
200 #endif // __MUTEX_H__
201