Jamoma API  0.6.0.a19
CAMutex.cpp
1 /* Copyright © 2007 Apple Inc. All Rights Reserved.
2 
3  Disclaimer: IMPORTANT: This Apple software is supplied to you by
4  Apple Inc. ("Apple") in consideration of your agreement to the
5  following terms, and your use, installation, modification or
6  redistribution of this Apple software constitutes acceptance of these
7  terms. If you do not agree with these terms, please do not use,
8  install, modify or redistribute this Apple software.
9 
10  In consideration of your agreement to abide by the following terms, and
11  subject to these terms, Apple grants you a personal, non-exclusive
12  license, under Apple's copyrights in this original Apple software (the
13  "Apple Software"), to use, reproduce, modify and redistribute the Apple
14  Software, with or without modifications, in source and/or binary forms;
15  provided that if you redistribute the Apple Software in its entirety and
16  without modifications, you must retain this notice and the following
17  text and disclaimers in all such redistributions of the Apple Software.
18  Neither the name, trademarks, service marks or logos of Apple Inc.
19  may be used to endorse or promote products derived from the Apple
20  Software without specific prior written permission from Apple. Except
21  as expressly stated in this notice, no other rights or licenses, express
22  or implied, are granted by Apple herein, including but not limited to
23  any patent rights that may be infringed by your derivative works or by
24  other works in which the Apple Software may be incorporated.
25 
26  The Apple Software is provided by Apple on an "AS IS" basis. APPLE
27  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
28  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
29  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
30  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
31 
32  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
33  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
36  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
37  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
38  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
39  POSSIBILITY OF SUCH DAMAGE.
40 */
41 //==================================================================================================
42 // Includes
43 //==================================================================================================
44 
45 // Self Include
46 #include "CAMutex.h"
47 
48 #if TARGET_OS_MAC
49  #include <errno.h>
50 #endif
51 
52 // PublicUtility Includes
53 #include "CADebugMacros.h"
54 #include "CAException.h"
55 #include "CAHostTimeBase.h"
56 
57 //==================================================================================================
58 // Logging
59 //==================================================================================================
60 
61 #if CoreAudio_Debug
62 // #define Log_Ownership 1
63 // #define Log_Errors 1
64 // #define Log_LongLatencies 1
65 // #define LongLatencyThreshholdNS 1000000ULL // nanoseconds
66 #endif
67 
68 //==================================================================================================
69 // CAMutex
70 //==================================================================================================
71 
72 CAMutex::CAMutex(const char* inName)
73 :
74  mName(inName),
75  mOwner(0)
76 {
77 #if TARGET_OS_MAC
78  OSStatus theError = pthread_mutex_init(&mMutex, NULL);
79  ThrowIf(theError != 0, CAException(theError), "CAMutex::CAMutex: Could not init the mutex");
80 
81  #if Log_Ownership
82  DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::CAMutex: creating %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner);
83  #endif
84 #elif TARGET_OS_WIN32
85  mMutex = CreateMutex(NULL, false, NULL);
86  ThrowIfNULL(mMutex, CAException(GetLastError()), "CAMutex::CAMutex: could not create the mutex.");
87 
88  #if Log_Ownership
89  DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::CAMutex: creating %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner);
90  #endif
91 #endif
92 }
93 
94 CAMutex::~CAMutex()
95 {
96 #if TARGET_OS_MAC
97  #if Log_Ownership
98  DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::~CAMutex: destroying %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner);
99  #endif
100  pthread_mutex_destroy(&mMutex);
101 #elif TARGET_OS_WIN32
102  #if Log_Ownership
103  DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::~CAMutex: destroying %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner);
104  #endif
105  if(mMutex != NULL)
106  {
107  CloseHandle(mMutex);
108  }
109 #endif
110 }
111 
112 bool CAMutex::Lock()
113 {
114  bool theAnswer = false;
115 
116 #if TARGET_OS_MAC
117  pthread_t theCurrentThread = pthread_self();
118  if(!pthread_equal(theCurrentThread, mOwner))
119  {
120  #if Log_Ownership
121  DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Lock: thread %p is locking %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner);
122  #endif
123 
124  #if Log_LongLatencies
125  UInt64 lockTryTime = CAHostTimeBase::GetCurrentTimeInNanos();
126  #endif
127 
128  OSStatus theError = pthread_mutex_lock(&mMutex);
129  ThrowIf(theError != 0, CAException(theError), "CAMutex::Lock: Could not lock the mutex");
130  mOwner = theCurrentThread;
131  theAnswer = true;
132 
133  #if Log_LongLatencies
134  UInt64 lockAcquireTime = CAHostTimeBase::GetCurrentTimeInNanos();
135  if (lockAcquireTime - lockTryTime >= LongLatencyThresholdNS)
136  DebugPrintfRtn(DebugPrintfFileComma "Thread %p took %.6fs to acquire the lock %s\n", theCurrentThread, (lockAcquireTime - lockTryTime) * 1.0e-9 /* nanos to seconds */, mName);
137  #endif
138 
139  #if Log_Ownership
140  DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Lock: thread %p has locked %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner);
141  #endif
142  }
143 #elif TARGET_OS_WIN32
144  if(mOwner != GetCurrentThreadId())
145  {
146  #if Log_Ownership
147  DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Lock: thread %lu is locking %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
148  #endif
149 
150  OSStatus theError = WaitForSingleObject(mMutex, INFINITE);
151  ThrowIfError(theError, CAException(theError), "CAMutex::Lock: could not lock the mutex");
152  mOwner = GetCurrentThreadId();
153  theAnswer = true;
154 
155  #if Log_Ownership
156  DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Lock: thread %lu has locked %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
157  #endif
158  }
159 #endif
160 
161  return theAnswer;
162 }
163 
164 void CAMutex::Unlock()
165 {
166 #if TARGET_OS_MAC
167  if(pthread_equal(pthread_self(), mOwner))
168  {
169  #if Log_Ownership
170  DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Unlock: thread %p is unlocking %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner);
171  #endif
172 
173  mOwner = 0;
174  OSStatus theError = pthread_mutex_unlock(&mMutex);
175  ThrowIf(theError != 0, CAException(theError), "CAMutex::Unlock: Could not unlock the mutex");
176 
177  #if Log_Ownership
178  DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Unlock: thread %p has unlocked %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner);
179  #endif
180  }
181  else
182  {
183  DebugMessage("CAMutex::Unlock: A thread is attempting to unlock a Mutex it doesn't own");
184  }
185 #elif TARGET_OS_WIN32
186  if(mOwner == GetCurrentThreadId())
187  {
188  #if Log_Ownership
189  DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Unlock: thread %lu is unlocking %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
190  #endif
191 
192  mOwner = 0;
193  bool wasReleased = ReleaseMutex(mMutex);
194  ThrowIf(!wasReleased, CAException(GetLastError()), "CAMutex::Unlock: Could not unlock the mutex");
195 
196  #if Log_Ownership
197  DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Unlock: thread %lu has unlocked %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
198  #endif
199  }
200  else
201  {
202  DebugMessage("CAMutex::Unlock: A thread is attempting to unlock a Mutex it doesn't own");
203  }
204 #endif
205 }
206 
207 bool CAMutex::Try(bool& outWasLocked)
208 {
209  bool theAnswer = false;
210  outWasLocked = false;
211 
212 #if TARGET_OS_MAC
213  pthread_t theCurrentThread = pthread_self();
214  if(!pthread_equal(theCurrentThread, mOwner))
215  {
216  // this means the current thread doesn't already own the lock
217  #if Log_Ownership
218  DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Try: thread %p is try-locking %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner);
219  #endif
220 
221  // go ahead and call trylock to see if we can lock it.
222  int theError = pthread_mutex_trylock(&mMutex);
223  if(theError == 0)
224  {
225  // return value of 0 means we successfully locked the lock
226  mOwner = theCurrentThread;
227  theAnswer = true;
228  outWasLocked = true;
229 
230  #if Log_Ownership
231  DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Try: thread %p has locked %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner);
232  #endif
233  }
234  else if(theError == EBUSY)
235  {
236  // return value of EBUSY means that the lock was already locked by another thread
237  theAnswer = false;
238  outWasLocked = false;
239 
240  #if Log_Ownership
241  DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Try: thread %p failed to lock %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner);
242  #endif
243  }
244  else
245  {
246  // any other return value means something really bad happenned
247  ThrowIfError(theError, CAException(theError), "CAMutex::Try: call to pthread_mutex_trylock failed");
248  }
249  }
250  else
251  {
252  // this means the current thread already owns the lock
253  theAnswer = true;
254  outWasLocked = false;
255  }
256 #elif TARGET_OS_WIN32
257  if(mOwner != GetCurrentThreadId())
258  {
259  // this means the current thread doesn't own the lock
260  #if Log_Ownership
261  DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Try: thread %lu is try-locking %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
262  #endif
263 
264  // try to acquire the mutex
265  OSStatus theError = WaitForSingleObject(mMutex, 0);
266  if(theError == WAIT_OBJECT_0)
267  {
268  // this means we successfully locked the lock
269  mOwner = GetCurrentThreadId();
270  theAnswer = true;
271  outWasLocked = true;
272 
273  #if Log_Ownership
274  DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Try: thread %lu has locked %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
275  #endif
276  }
277  else if(theError == WAIT_TIMEOUT)
278  {
279  // this means that the lock was already locked by another thread
280  theAnswer = false;
281  outWasLocked = false;
282 
283  #if Log_Ownership
284  DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Try: thread %lu failed to lock %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
285  #endif
286  }
287  else
288  {
289  // any other return value means something really bad happenned
290  ThrowIfError(theError, CAException(GetLastError()), "CAMutex::Try: call to lock the mutex failed");
291  }
292  }
293  else
294  {
295  // this means the current thread already owns the lock
296  theAnswer = true;
297  outWasLocked = false;
298  }
299 #endif
300 
301  return theAnswer;
302 }
303 
304 bool CAMutex::IsFree() const
305 {
306  return mOwner == 0;
307 }
308 
309 bool CAMutex::IsOwnedByCurrentThread() const
310 {
311  bool theAnswer = true;
312 
313 #if TARGET_OS_MAC
314  theAnswer = pthread_equal(pthread_self(), mOwner);
315 #elif TARGET_OS_WIN32
316  theAnswer = (mOwner == GetCurrentThreadId());
317 #endif
318 
319  return theAnswer;
320 }