1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*++
6
7
8
9Module Name:
10
11 mutex.ccpp
12
13Abstract:
14
15 Implementation of mutex synchroniztion object as described in
16 the WIN32 API
17
18Revision History:
19
20
21
22--*/
23
24#include "pal/dbgmsg.h"
25
26SET_DEFAULT_DEBUG_CHANNEL(SYNC); // some headers have code with asserts, so do this first
27
28#include "pal/mutex.hpp"
29#include "pal/file.hpp"
30#include "pal/thread.hpp"
31
32#include "../synchmgr/synchmanager.hpp"
33
34#include <sys/file.h>
35#include <sys/types.h>
36
37#include <errno.h>
38#include <time.h>
39#include <unistd.h>
40
41#include "pal/sharedmemory.inl"
42
43using namespace CorUnix;
44
45/* ------------------- Definitions ------------------------------*/
46
47CObjectType CorUnix::otMutex(
48 otiMutex,
49 NULL, // No cleanup routine
50 NULL, // No initialization routine
51 0, // No immutable data
52 NULL, // No immutable data copy routine
53 NULL, // No immutable data cleanup routine
54 0, // No process local data
55 NULL, // No process local data cleanup routine
56 0, // No shared data
57 0, // Should be MUTEX_ALL_ACCESS; currently ignored (no Win32 security)
58 CObjectType::SecuritySupported,
59 CObjectType::SecurityInfoNotPersisted,
60 CObjectType::UnnamedObject,
61 CObjectType::LocalDuplicationOnly,
62 CObjectType::WaitableObject,
63 CObjectType::ObjectCanBeUnsignaled,
64 CObjectType::ThreadReleaseAltersSignalCount,
65 CObjectType::OwnershipTracked
66 );
67
68static CAllowedObjectTypes aotMutex(otiMutex);
69
70CObjectType CorUnix::otNamedMutex(
71 otiNamedMutex,
72 &SharedMemoryProcessDataHeader::PalObject_Close, // Cleanup routine
73 NULL, // No initialization routine
74 sizeof(SharedMemoryProcessDataHeader *), // Immutable data
75 NULL, // No immutable data copy routine
76 NULL, // No immutable data cleanup routine
77 0, // No process local data
78 NULL, // No process local data cleanup routine
79 0, // No shared data
80 0, // Should be MUTEX_ALL_ACCESS; currently ignored (no Win32 security)
81 CObjectType::SecuritySupported,
82 CObjectType::SecurityInfoNotPersisted,
83 CObjectType::UnnamedObject, // PAL's naming infrastructure is not used
84 CObjectType::LocalDuplicationOnly,
85 CObjectType::UnwaitableObject, // PAL's waiting infrastructure is not used
86 CObjectType::SignalingNotApplicable, // PAL's signaling infrastructure is not used
87 CObjectType::ThreadReleaseNotApplicable, // PAL's signaling infrastructure is not used
88 CObjectType::OwnershipNotApplicable // PAL's ownership infrastructure is not used
89 );
90
91static CAllowedObjectTypes aotNamedMutex(otiNamedMutex);
92
93static PalObjectTypeId anyMutexTypeIds[] = {otiMutex, otiNamedMutex};
94static CAllowedObjectTypes aotAnyMutex(anyMutexTypeIds, _countof(anyMutexTypeIds));
95
96/*++
97Function:
98 CreateMutexA
99
100Note:
101 lpMutexAttributes currentely ignored:
102 -- Win32 object security not supported
103 -- handles to mutex objects are not inheritable
104
105Parameters:
106 See MSDN doc.
107--*/
108
109HANDLE
110PALAPI
111CreateMutexA(
112 IN LPSECURITY_ATTRIBUTES lpMutexAttributes,
113 IN BOOL bInitialOwner,
114 IN LPCSTR lpName)
115{
116 HANDLE hMutex = NULL;
117 CPalThread *pthr = NULL;
118 PAL_ERROR palError;
119
120 PERF_ENTRY(CreateMutexA);
121 ENTRY("CreateMutexA(lpMutexAttr=%p, bInitialOwner=%d, lpName=%p (%s)\n",
122 lpMutexAttributes, bInitialOwner, lpName, lpName?lpName:"NULL");
123
124 pthr = InternalGetCurrentThread();
125
126 palError = InternalCreateMutex(
127 pthr,
128 lpMutexAttributes,
129 bInitialOwner,
130 lpName,
131 &hMutex
132 );
133
134 //
135 // We always need to set last error, even on success:
136 // we need to protect ourselves from the situation
137 // where last error is set to ERROR_ALREADY_EXISTS on
138 // entry to the function
139 //
140
141 pthr->SetLastError(palError);
142
143 LOGEXIT("CreateMutexA returns HANDLE %p\n", hMutex);
144 PERF_EXIT(CreateMutexA);
145 return hMutex;
146}
147
148
149/*++
150Function:
151 CreateMutexW
152
153Note:
154 lpMutexAttributes currentely ignored:
155 -- Win32 object security not supported
156 -- handles to mutex objects are not inheritable
157
158Parameters:
159 See MSDN doc.
160--*/
161
162HANDLE
163PALAPI
164CreateMutexW(
165 IN LPSECURITY_ATTRIBUTES lpMutexAttributes,
166 IN BOOL bInitialOwner,
167 IN LPCWSTR lpName)
168{
169 HANDLE hMutex = NULL;
170 PAL_ERROR palError;
171 CPalThread *pthr = NULL;
172 char utf8Name[SHARED_MEMORY_MAX_NAME_CHAR_COUNT + 1];
173
174 PERF_ENTRY(CreateMutexW);
175 ENTRY("CreateMutexW(lpMutexAttr=%p, bInitialOwner=%d, lpName=%p (%S)\n",
176 lpMutexAttributes, bInitialOwner, lpName, lpName?lpName:W16_NULLSTRING);
177
178 pthr = InternalGetCurrentThread();
179
180 if (lpName != nullptr)
181 {
182 int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, _countof(utf8Name), nullptr, nullptr);
183 if (bytesWritten == 0)
184 {
185 DWORD errorCode = GetLastError();
186 if (errorCode == ERROR_INSUFFICIENT_BUFFER)
187 {
188 palError = static_cast<DWORD>(SharedMemoryError::NameTooLong);
189 }
190 else
191 {
192 ASSERT("WideCharToMultiByte failed (%u)\n", errorCode);
193 palError = errorCode;
194 }
195 goto CreateMutexWExit;
196 }
197 }
198
199 palError = InternalCreateMutex(
200 pthr,
201 lpMutexAttributes,
202 bInitialOwner,
203 lpName == nullptr ? nullptr : utf8Name,
204 &hMutex
205 );
206
207CreateMutexWExit:
208 //
209 // We always need to set last error, even on success:
210 // we need to protect ourselves from the situation
211 // where last error is set to ERROR_ALREADY_EXISTS on
212 // entry to the function
213 //
214
215 pthr->SetLastError(palError);
216
217 LOGEXIT("CreateMutexW returns HANDLE %p\n", hMutex);
218 PERF_EXIT(CreateMutexW);
219 return hMutex;
220}
221
222/*++
223Function:
224CreateMutexW
225
226Note:
227lpMutexAttributes currentely ignored:
228-- Win32 object security not supported
229-- handles to mutex objects are not inheritable
230
231Parameters:
232See MSDN doc.
233--*/
234
235HANDLE
236PALAPI
237CreateMutexExW(
238 IN LPSECURITY_ATTRIBUTES lpMutexAttributes,
239 IN LPCWSTR lpName,
240 IN DWORD dwFlags,
241 IN DWORD dwDesiredAccess)
242{
243 return CreateMutexW(lpMutexAttributes, (dwFlags & CREATE_MUTEX_INITIAL_OWNER) != 0, lpName);
244}
245
246/*++
247Function:
248 InternalCreateMutex
249
250Note:
251 lpMutexAttributes currentely ignored:
252 -- Win32 object security not supported
253 -- handles to mutex objects are not inheritable
254
255Parameters:
256 pthr -- thread data for calling thread
257 phEvent -- on success, receives the allocated mutex handle
258
259 See MSDN docs on CreateMutex for all other parameters
260--*/
261
262PAL_ERROR
263CorUnix::InternalCreateMutex(
264 CPalThread *pthr,
265 LPSECURITY_ATTRIBUTES lpMutexAttributes,
266 BOOL bInitialOwner,
267 LPCSTR lpName,
268 HANDLE *phMutex
269 )
270{
271 CObjectAttributes oa(nullptr, lpMutexAttributes);
272 PAL_ERROR palError = NO_ERROR;
273 IPalObject *pobjMutex = NULL;
274 IPalObject *pobjRegisteredMutex = NULL;
275 ISynchStateController *pssc = NULL;
276 HANDLE hMutex = nullptr;
277
278 _ASSERTE(NULL != pthr);
279 _ASSERTE(NULL != phMutex);
280
281 ENTRY("InternalCreateMutex(pthr=%p, lpMutexAttributes=%p, bInitialOwner=%d"
282 ", lpName=%p, phMutex=%p)\n",
283 pthr,
284 lpMutexAttributes,
285 bInitialOwner,
286 lpName,
287 phMutex
288 );
289
290 if (lpName != nullptr && lpName[0] == '\0')
291 {
292 // Empty name is treated as a request for an unnamed process-local mutex
293 lpName = nullptr;
294 }
295
296 CObjectType *ot = lpName == nullptr ? &otMutex : &otNamedMutex;
297 CAllowedObjectTypes *aot = lpName == nullptr ? &aotMutex : &aotNamedMutex;
298
299 palError = g_pObjectManager->AllocateObject(
300 pthr,
301 ot,
302 &oa,
303 &pobjMutex
304 );
305
306 if (NO_ERROR != palError)
307 {
308 goto InternalCreateMutexExit;
309 }
310
311 if (lpName == nullptr)
312 {
313 palError = pobjMutex->GetSynchStateController(
314 pthr,
315 &pssc
316 );
317
318 if (NO_ERROR != palError)
319 {
320 ASSERT("Unable to create state controller (%d)\n", palError);
321 goto InternalCreateMutexExit;
322 }
323
324 if (bInitialOwner)
325 {
326 palError = pssc->SetOwner(pthr);
327 }
328 else
329 {
330 palError = pssc->SetSignalCount(1);
331 }
332
333 pssc->ReleaseController();
334
335 if (NO_ERROR != palError)
336 {
337 ASSERT("Unable to set initial mutex state (%d)\n", palError);
338 goto InternalCreateMutexExit;
339 }
340 }
341
342 palError = g_pObjectManager->RegisterObject(
343 pthr,
344 pobjMutex,
345 aot,
346 0, // should be MUTEX_ALL_ACCESS -- currently ignored (no Win32 security)
347 &hMutex,
348 &pobjRegisteredMutex
349 );
350
351 if (palError != NO_ERROR)
352 {
353 _ASSERTE(palError != ERROR_ALREADY_EXISTS); // PAL's naming infrastructure is not used for named mutexes
354 _ASSERTE(pobjRegisteredMutex == nullptr);
355 _ASSERTE(hMutex == nullptr);
356 goto InternalCreateMutexExit;
357 }
358
359 // Now that the object has been registered successfully, it would have a reference associated with the handle, so release
360 // the initial reference. Any errors from now on need to revoke the handle.
361 _ASSERTE(pobjRegisteredMutex == pobjMutex);
362 _ASSERTE(hMutex != nullptr);
363 pobjMutex->ReleaseReference(pthr);
364 pobjRegisteredMutex = nullptr;
365
366 if (lpName != nullptr)
367 {
368 SharedMemoryProcessDataHeader *processDataHeader;
369 bool created = false;
370 try
371 {
372 processDataHeader = NamedMutexProcessData::CreateOrOpen(lpName, !!bInitialOwner, &created);
373 }
374 catch (SharedMemoryException ex)
375 {
376 palError = ex.GetErrorCode();
377 goto InternalCreateMutexExit;
378 }
379 SharedMemoryProcessDataHeader::PalObject_SetProcessDataHeader(pobjMutex, processDataHeader);
380
381 if (!created)
382 {
383 // Indicate to the caller that an existing mutex was opened, and hence the caller will not have initial ownership
384 // of the mutex if requested through bInitialOwner
385 palError = ERROR_ALREADY_EXISTS;
386 }
387 }
388
389 *phMutex = hMutex;
390 hMutex = nullptr;
391 pobjMutex = nullptr;
392
393InternalCreateMutexExit:
394
395 _ASSERTE(pobjRegisteredMutex == nullptr);
396 if (hMutex != nullptr)
397 {
398 g_pObjectManager->RevokeHandle(pthr, hMutex);
399 }
400 else if (NULL != pobjMutex)
401 {
402 pobjMutex->ReleaseReference(pthr);
403 }
404
405 LOGEXIT("InternalCreateMutex returns %i\n", palError);
406
407 return palError;
408}
409
410/*++
411Function:
412 ReleaseMutex
413
414Parameters:
415 See MSDN doc.
416--*/
417
418BOOL
419PALAPI
420ReleaseMutex( IN HANDLE hMutex )
421{
422 PAL_ERROR palError = NO_ERROR;
423 CPalThread *pthr = NULL;
424
425 PERF_ENTRY(ReleaseMutex);
426 ENTRY("ReleaseMutex(hMutex=%p)\n", hMutex);
427
428 pthr = InternalGetCurrentThread();
429
430 palError = InternalReleaseMutex(pthr, hMutex);
431
432 if (NO_ERROR != palError)
433 {
434 pthr->SetLastError(palError);
435 }
436
437 LOGEXIT("ReleaseMutex returns BOOL %d\n", (NO_ERROR == palError));
438 PERF_EXIT(ReleaseMutex);
439 return (NO_ERROR == palError);
440}
441
442/*++
443Function:
444 InternalReleaseMutex
445
446Parameters:
447 pthr -- thread data for calling thread
448
449 See MSDN docs on ReleaseMutex for all other parameters
450--*/
451
452PAL_ERROR
453CorUnix::InternalReleaseMutex(
454 CPalThread *pthr,
455 HANDLE hMutex
456 )
457{
458 PAL_ERROR palError = NO_ERROR;
459 IPalObject *pobjMutex = NULL;
460 ISynchStateController *pssc = NULL;
461 PalObjectTypeId objectTypeId;
462
463 _ASSERTE(NULL != pthr);
464
465 ENTRY("InternalReleaseMutex(pthr=%p, hMutex=%p)\n",
466 pthr,
467 hMutex
468 );
469
470 palError = g_pObjectManager->ReferenceObjectByHandle(
471 pthr,
472 hMutex,
473 &aotAnyMutex,
474 0, // should be MUTEX_MODIFY_STATE -- current ignored (no Win32 security)
475 &pobjMutex
476 );
477
478 if (NO_ERROR != palError)
479 {
480 ERROR("Unable to obtain object for handle %p (error %d)!\n", hMutex, palError);
481 goto InternalReleaseMutexExit;
482 }
483
484 objectTypeId = pobjMutex->GetObjectType()->GetId();
485 if (objectTypeId == otiMutex)
486 {
487 palError = pobjMutex->GetSynchStateController(
488 pthr,
489 &pssc
490 );
491
492 if (NO_ERROR != palError)
493 {
494 ASSERT("Error %d obtaining synch state controller\n", palError);
495 goto InternalReleaseMutexExit;
496 }
497
498 palError = pssc->DecrementOwnershipCount();
499
500 if (NO_ERROR != palError)
501 {
502 ERROR("Error %d decrementing mutex ownership count\n", palError);
503 goto InternalReleaseMutexExit;
504 }
505 }
506 else
507 {
508 _ASSERTE(objectTypeId == otiNamedMutex);
509
510 SharedMemoryProcessDataHeader *processDataHeader =
511 SharedMemoryProcessDataHeader::PalObject_GetProcessDataHeader(pobjMutex);
512 _ASSERTE(processDataHeader != nullptr);
513 try
514 {
515 static_cast<NamedMutexProcessData *>(processDataHeader->GetData())->ReleaseLock();
516 }
517 catch (SharedMemoryException ex)
518 {
519 palError = ex.GetErrorCode();
520 goto InternalReleaseMutexExit;
521 }
522 }
523
524InternalReleaseMutexExit:
525
526 if (NULL != pssc)
527 {
528 pssc->ReleaseController();
529 }
530
531 if (NULL != pobjMutex)
532 {
533 pobjMutex->ReleaseReference(pthr);
534 }
535
536 LOGEXIT("InternalReleaseMutex returns %i\n", palError);
537
538 return palError;
539}
540
541/*++
542Function:
543 OpenMutexA
544
545Note:
546 dwDesiredAccess is currently ignored (no Win32 object security support)
547 bInheritHandle is currently ignored (handles to mutexes are not inheritable)
548
549See MSDN doc.
550--*/
551
552HANDLE
553PALAPI
554OpenMutexA (
555 IN DWORD dwDesiredAccess,
556 IN BOOL bInheritHandle,
557 IN LPCSTR lpName)
558{
559 HANDLE hMutex = NULL;
560 CPalThread *pthr = NULL;
561 PAL_ERROR palError;
562
563 PERF_ENTRY(OpenMutexA);
564 ENTRY("OpenMutexA(dwDesiredAccess=%#x, bInheritHandle=%d, lpName=%p (%s))\n",
565 dwDesiredAccess, bInheritHandle, lpName, lpName?lpName:"NULL");
566
567 pthr = InternalGetCurrentThread();
568
569 /* validate parameters */
570 if (lpName == nullptr)
571 {
572 ERROR("name is NULL\n");
573 palError = ERROR_INVALID_PARAMETER;
574 goto OpenMutexAExit;
575 }
576
577 palError = InternalOpenMutex(pthr, dwDesiredAccess, bInheritHandle, lpName, &hMutex);
578
579OpenMutexAExit:
580 if (NO_ERROR != palError)
581 {
582 pthr->SetLastError(palError);
583 }
584
585 LOGEXIT("OpenMutexA returns HANDLE %p\n", hMutex);
586 PERF_EXIT(OpenMutexA);
587 return hMutex;
588}
589
590/*++
591Function:
592 OpenMutexW
593
594Note:
595 dwDesiredAccess is currently ignored (no Win32 object security support)
596 bInheritHandle is currently ignored (handles to mutexes are not inheritable)
597
598See MSDN doc.
599--*/
600
601HANDLE
602PALAPI
603OpenMutexW(
604 IN DWORD dwDesiredAccess,
605 IN BOOL bInheritHandle,
606 IN LPCWSTR lpName)
607{
608 HANDLE hMutex = NULL;
609 PAL_ERROR palError = NO_ERROR;
610 CPalThread *pthr = NULL;
611 char utf8Name[SHARED_MEMORY_MAX_NAME_CHAR_COUNT + 1];
612
613 PERF_ENTRY(OpenMutexW);
614 ENTRY("OpenMutexW(dwDesiredAccess=%#x, bInheritHandle=%d, lpName=%p (%S))\n",
615 dwDesiredAccess, bInheritHandle, lpName, lpName?lpName:W16_NULLSTRING);
616
617 pthr = InternalGetCurrentThread();
618
619 /* validate parameters */
620 if (lpName == nullptr)
621 {
622 ERROR("name is NULL\n");
623 palError = ERROR_INVALID_PARAMETER;
624 goto OpenMutexWExit;
625 }
626
627 {
628 int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, _countof(utf8Name), nullptr, nullptr);
629 if (bytesWritten == 0)
630 {
631 DWORD errorCode = GetLastError();
632 if (errorCode == ERROR_INSUFFICIENT_BUFFER)
633 {
634 palError = static_cast<DWORD>(SharedMemoryError::NameTooLong);
635 }
636 else
637 {
638 ASSERT("WideCharToMultiByte failed (%u)\n", errorCode);
639 palError = errorCode;
640 }
641 goto OpenMutexWExit;
642 }
643 }
644
645 palError = InternalOpenMutex(pthr, dwDesiredAccess, bInheritHandle, lpName == nullptr ? nullptr : utf8Name, &hMutex);
646
647OpenMutexWExit:
648 if (NO_ERROR != palError)
649 {
650 pthr->SetLastError(palError);
651 }
652
653 LOGEXIT("OpenMutexW returns HANDLE %p\n", hMutex);
654 PERF_EXIT(OpenMutexW);
655
656 return hMutex;
657}
658
659/*++
660Function:
661 InternalOpenMutex
662
663Note:
664 dwDesiredAccess is currently ignored (no Win32 object security support)
665 bInheritHandle is currently ignored (handles to mutexes are not inheritable)
666
667Parameters:
668 pthr -- thread data for calling thread
669 phEvent -- on success, receives the allocated mutex handle
670
671 See MSDN docs on OpenMutex for all other parameters.
672--*/
673
674PAL_ERROR
675CorUnix::InternalOpenMutex(
676 CPalThread *pthr,
677 DWORD dwDesiredAccess,
678 BOOL bInheritHandle,
679 LPCSTR lpName,
680 HANDLE *phMutex
681 )
682{
683 CObjectAttributes oa;
684 PAL_ERROR palError = NO_ERROR;
685 IPalObject *pobjMutex = NULL;
686 IPalObject *pobjRegisteredMutex = NULL;
687 HANDLE hMutex = nullptr;
688
689 _ASSERTE(NULL != pthr);
690 _ASSERTE(NULL != lpName);
691 _ASSERTE(NULL != phMutex);
692
693 ENTRY("InternalOpenMutex(pthr=%p, dwDesiredAccess=%d, bInheritHandle=%d, "
694 "lpName=%p, phMutex=%p)\n",
695 pthr,
696 dwDesiredAccess,
697 bInheritHandle,
698 lpName,
699 phMutex
700 );
701
702 palError = g_pObjectManager->AllocateObject(
703 pthr,
704 &otNamedMutex,
705 &oa,
706 &pobjMutex
707 );
708
709 if (NO_ERROR != palError)
710 {
711 goto InternalOpenMutexExit;
712 }
713
714 palError = g_pObjectManager->RegisterObject(
715 pthr,
716 pobjMutex,
717 &aotNamedMutex,
718 dwDesiredAccess,
719 &hMutex,
720 &pobjRegisteredMutex
721 );
722
723 if (palError != NO_ERROR)
724 {
725 _ASSERTE(palError != ERROR_ALREADY_EXISTS); // PAL's naming infrastructure is not used for named mutexes
726 _ASSERTE(pobjRegisteredMutex == nullptr);
727 _ASSERTE(hMutex == nullptr);
728 goto InternalOpenMutexExit;
729 }
730
731 // Now that the object has been registered successfully, it would have a reference associated with the handle, so release
732 // the initial reference. Any errors from now on need to revoke the handle.
733 _ASSERTE(pobjRegisteredMutex == pobjMutex);
734 _ASSERTE(hMutex != nullptr);
735 pobjMutex->ReleaseReference(pthr);
736 pobjRegisteredMutex = nullptr;
737
738 {
739 SharedMemoryProcessDataHeader *processDataHeader;
740 try
741 {
742 processDataHeader = NamedMutexProcessData::Open(lpName);
743 }
744 catch (SharedMemoryException ex)
745 {
746 palError = ex.GetErrorCode();
747 goto InternalOpenMutexExit;
748 }
749 if (processDataHeader == nullptr)
750 {
751 palError = ERROR_FILE_NOT_FOUND;
752 goto InternalOpenMutexExit;
753 }
754 SharedMemoryProcessDataHeader::PalObject_SetProcessDataHeader(pobjMutex, processDataHeader);
755 }
756
757 *phMutex = hMutex;
758 hMutex = nullptr;
759 pobjMutex = nullptr;
760
761InternalOpenMutexExit:
762
763 _ASSERTE(pobjRegisteredMutex == nullptr);
764 if (hMutex != nullptr)
765 {
766 g_pObjectManager->RevokeHandle(pthr, hMutex);
767 }
768 else if (NULL != pobjMutex)
769 {
770 pobjMutex->ReleaseReference(pthr);
771 }
772
773 LOGEXIT("InternalCreateMutex returns %i\n", palError);
774
775 return palError;
776}
777
778
779/* Basic spinlock implementation */
780void SPINLOCKAcquire (LONG * lock, unsigned int flags)
781{
782 size_t loop_seed = 1, loop_count = 0;
783
784 if (flags & SYNCSPINLOCK_F_ASYMMETRIC)
785 {
786 loop_seed = ((size_t)pthread_self() % 10) + 1;
787 }
788 while (InterlockedCompareExchange(lock, 1, 0))
789 {
790 if (!(flags & SYNCSPINLOCK_F_ASYMMETRIC) || (++loop_count % loop_seed))
791 {
792#if PAL_IGNORE_NORMAL_THREAD_PRIORITY
793 struct timespec tsSleepTime;
794 tsSleepTime.tv_sec = 0;
795 tsSleepTime.tv_nsec = 1;
796 nanosleep(&tsSleepTime, NULL);
797#else
798 sched_yield();
799#endif
800 }
801 }
802
803}
804
805void SPINLOCKRelease (LONG * lock)
806{
807 VolatileStore(lock, 0);
808}
809
810DWORD SPINLOCKTryAcquire (LONG * lock)
811{
812 return InterlockedCompareExchange(lock, 1, 0);
813 // only returns 0 or 1.
814}
815
816////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
817// MutexHelpers
818
819#if NAMED_MUTEX_USE_PTHREAD_MUTEX
820void MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(pthread_mutex_t *mutex)
821{
822 _ASSERTE(mutex != nullptr);
823
824 struct AutoCleanup
825 {
826 pthread_mutexattr_t *m_mutexAttributes;
827
828 AutoCleanup() : m_mutexAttributes(nullptr)
829 {
830 }
831
832 ~AutoCleanup()
833 {
834 if (m_mutexAttributes != nullptr)
835 {
836 int error = pthread_mutexattr_destroy(m_mutexAttributes);
837 _ASSERTE(error == 0);
838 }
839 }
840 } autoCleanup;
841
842 pthread_mutexattr_t mutexAttributes;
843 int error = pthread_mutexattr_init(&mutexAttributes);
844 if (error != 0)
845 {
846 throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::OutOfMemory));
847 }
848 autoCleanup.m_mutexAttributes = &mutexAttributes;
849
850 error = pthread_mutexattr_setpshared(&mutexAttributes, PTHREAD_PROCESS_SHARED);
851 _ASSERTE(error == 0);
852
853 error = pthread_mutexattr_setrobust(&mutexAttributes, PTHREAD_MUTEX_ROBUST);
854 _ASSERTE(error == 0);
855
856 error = pthread_mutexattr_settype(&mutexAttributes, PTHREAD_MUTEX_RECURSIVE);
857 _ASSERTE(error == 0);
858
859 error = pthread_mutex_init(mutex, &mutexAttributes);
860 if (error != 0)
861 {
862 throw SharedMemoryException(static_cast<DWORD>(error == EPERM ? SharedMemoryError::IO : SharedMemoryError::OutOfMemory));
863 }
864}
865
866void MutexHelpers::DestroyMutex(pthread_mutex_t *mutex)
867{
868 _ASSERTE(mutex != nullptr);
869
870 int error = pthread_mutex_destroy(mutex);
871 _ASSERTE(error == 0 || error == EBUSY); // the error will be EBUSY if the mutex is locked
872}
873
874MutexTryAcquireLockResult MutexHelpers::TryAcquireLock(pthread_mutex_t *mutex, DWORD timeoutMilliseconds)
875{
876 _ASSERTE(mutex != nullptr);
877
878 int lockResult;
879 switch (timeoutMilliseconds)
880 {
881 case static_cast<DWORD>(-1):
882 lockResult = pthread_mutex_lock(mutex);
883 break;
884
885 case 0:
886 lockResult = pthread_mutex_trylock(mutex);
887 break;
888
889 default:
890 {
891 struct timespec timeoutTime;
892 PAL_ERROR palError = CPalSynchronizationManager::GetAbsoluteTimeout(timeoutMilliseconds, &timeoutTime, /*fPreferMonotonicClock*/ FALSE);
893 _ASSERTE(palError == NO_ERROR);
894 lockResult = pthread_mutex_timedlock(mutex, &timeoutTime);
895 break;
896 }
897 }
898
899 switch (lockResult)
900 {
901 case 0:
902 return MutexTryAcquireLockResult::AcquiredLock;
903
904 case EBUSY:
905 _ASSERTE(timeoutMilliseconds == 0);
906 return MutexTryAcquireLockResult::TimedOut;
907
908 case ETIMEDOUT:
909 _ASSERTE(timeoutMilliseconds != static_cast<DWORD>(-1));
910 _ASSERTE(timeoutMilliseconds != 0);
911 return MutexTryAcquireLockResult::TimedOut;
912
913 case EOWNERDEAD:
914 {
915 int setConsistentResult = pthread_mutex_consistent(mutex);
916 _ASSERTE(setConsistentResult == 0);
917 return MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned;
918 }
919
920 case EAGAIN:
921 throw SharedMemoryException(static_cast<DWORD>(NamedMutexError::MaximumRecursiveLocksReached));
922
923 default:
924 throw SharedMemoryException(static_cast<DWORD>(NamedMutexError::Unknown));
925 }
926}
927
928void MutexHelpers::ReleaseLock(pthread_mutex_t *mutex)
929{
930 _ASSERTE(mutex != nullptr);
931
932 int unlockResult = pthread_mutex_unlock(mutex);
933 _ASSERTE(unlockResult == 0);
934}
935#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
936
937////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
938// NamedMutexSharedData
939
940NamedMutexSharedData::NamedMutexSharedData()
941 :
942#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
943 m_timedWaiterCount(0),
944#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
945 m_lockOwnerProcessId(SharedMemoryHelpers::InvalidProcessId),
946 m_lockOwnerThreadId(SharedMemoryHelpers::InvalidSharedThreadId),
947 m_isAbandoned(false)
948{
949#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
950 static_assert_no_msg(sizeof(m_timedWaiterCount) == sizeof(LONG)); // for interlocked operations
951#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
952
953 _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
954 _ASSERTE(SharedMemoryManager::IsCreationDeletionFileLockAcquired());
955
956#if NAMED_MUTEX_USE_PTHREAD_MUTEX
957 MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(&m_lock);
958#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
959}
960
961NamedMutexSharedData::~NamedMutexSharedData()
962{
963 _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
964 _ASSERTE(SharedMemoryManager::IsCreationDeletionFileLockAcquired());
965
966#if NAMED_MUTEX_USE_PTHREAD_MUTEX
967 MutexHelpers::DestroyMutex(&m_lock);
968#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
969}
970
971#if NAMED_MUTEX_USE_PTHREAD_MUTEX
972pthread_mutex_t *NamedMutexSharedData::GetLock()
973{
974 return &m_lock;
975}
976#else // !NAMED_MUTEX_USE_PTHREAD_MUTEX
977bool NamedMutexSharedData::HasAnyTimedWaiters() const
978{
979 return
980 InterlockedCompareExchange(
981 const_cast<LONG *>(reinterpret_cast<const LONG *>(&m_timedWaiterCount)),
982 -1 /* Exchange */,
983 -1 /* Comparand */) != 0;
984}
985
986void NamedMutexSharedData::IncTimedWaiterCount()
987{
988 ULONG newValue = InterlockedIncrement(reinterpret_cast<LONG *>(&m_timedWaiterCount));
989 if (newValue == 0)
990 {
991 throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::OutOfMemory));
992 }
993}
994
995void NamedMutexSharedData::DecTimedWaiterCount()
996{
997 ULONG newValue = InterlockedDecrement(reinterpret_cast<LONG *>(&m_timedWaiterCount));
998 _ASSERTE(newValue + 1 != 0);
999}
1000#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
1001
1002bool NamedMutexSharedData::IsAbandoned() const
1003{
1004 _ASSERTE(IsLockOwnedByCurrentThread());
1005 return m_isAbandoned;
1006}
1007
1008void NamedMutexSharedData::SetIsAbandoned(bool isAbandoned)
1009{
1010 _ASSERTE(IsLockOwnedByCurrentThread());
1011 _ASSERTE(m_isAbandoned != isAbandoned);
1012
1013 m_isAbandoned = isAbandoned;
1014}
1015
1016bool NamedMutexSharedData::IsLockOwnedByAnyThread() const
1017{
1018 return
1019 m_lockOwnerProcessId != SharedMemoryHelpers::InvalidProcessId ||
1020 m_lockOwnerThreadId != SharedMemoryHelpers::InvalidSharedThreadId;
1021}
1022
1023bool NamedMutexSharedData::IsLockOwnedByCurrentThread() const
1024{
1025 return m_lockOwnerProcessId == GetCurrentProcessId() && m_lockOwnerThreadId == THREADSilentGetCurrentThreadId();
1026}
1027
1028void NamedMutexSharedData::SetLockOwnerToCurrentThread()
1029{
1030 m_lockOwnerProcessId = GetCurrentProcessId();
1031 _ASSERTE(m_lockOwnerProcessId != SharedMemoryHelpers::InvalidProcessId);
1032 m_lockOwnerThreadId = THREADSilentGetCurrentThreadId();
1033 _ASSERTE(m_lockOwnerThreadId != SharedMemoryHelpers::InvalidSharedThreadId);
1034}
1035
1036void NamedMutexSharedData::ClearLockOwner()
1037{
1038 _ASSERTE(IsLockOwnedByCurrentThread());
1039
1040 m_lockOwnerProcessId = SharedMemoryHelpers::InvalidProcessId;
1041 m_lockOwnerThreadId = SharedMemoryHelpers::InvalidSharedThreadId;
1042}
1043
1044////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1045// NamedMutexProcessData
1046
1047// This value should only be incremented if a non-backward-compatible change to the sync system is made. A process would fail to
1048// open a mutex created with a different sync system version.
1049const UINT8 NamedMutexProcessData::SyncSystemVersion = 1;
1050
1051const DWORD NamedMutexProcessData::PollLoopMaximumSleepMilliseconds = 100;
1052
1053SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(LPCSTR name, bool acquireLockIfCreated, bool *createdRef)
1054{
1055 return CreateOrOpen(name, true /* createIfNotExist */, acquireLockIfCreated, createdRef);
1056}
1057
1058SharedMemoryProcessDataHeader *NamedMutexProcessData::Open(LPCSTR name)
1059{
1060 return CreateOrOpen(name, false /* createIfNotExist */, false /* acquireLockIfCreated */, nullptr /* createdRef */);
1061}
1062
1063SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(
1064 LPCSTR name,
1065 bool createIfNotExist,
1066 bool acquireLockIfCreated,
1067 bool *createdRef)
1068{
1069 _ASSERTE(name != nullptr);
1070 _ASSERTE(createIfNotExist || !acquireLockIfCreated);
1071
1072#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
1073 PathCharString lockFilePath;
1074#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
1075
1076 struct AutoCleanup
1077 {
1078 bool m_acquiredCreationDeletionProcessLock;
1079 bool m_acquiredCreationDeletionFileLock;
1080 SharedMemoryProcessDataHeader *m_processDataHeader;
1081 #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
1082 PathCharString *m_lockFilePath;
1083 SIZE_T m_sessionDirectoryPathCharCount;
1084 bool m_createdLockFile;
1085 int m_lockFileDescriptor;
1086 #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
1087 bool m_cancel;
1088
1089 AutoCleanup()
1090 : m_acquiredCreationDeletionProcessLock(false),
1091 m_acquiredCreationDeletionFileLock(false),
1092 m_processDataHeader(nullptr),
1093 #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
1094 m_lockFilePath(nullptr),
1095 m_sessionDirectoryPathCharCount(0),
1096 m_createdLockFile(false),
1097 m_lockFileDescriptor(-1),
1098 #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
1099 m_cancel(false)
1100 {
1101 }
1102
1103 ~AutoCleanup()
1104 {
1105 #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
1106 if (!m_cancel)
1107 {
1108 if (m_lockFileDescriptor != -1)
1109 {
1110 SharedMemoryHelpers::CloseFile(m_lockFileDescriptor);
1111 }
1112
1113 if (m_createdLockFile)
1114 {
1115 _ASSERTE(m_lockFilePath != nullptr);
1116 unlink(*m_lockFilePath);
1117 }
1118
1119 if (m_sessionDirectoryPathCharCount != 0)
1120 {
1121 _ASSERTE(m_lockFilePath != nullptr);
1122 m_lockFilePath->CloseBuffer(m_sessionDirectoryPathCharCount);
1123 rmdir(*m_lockFilePath);
1124 }
1125 }
1126 #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
1127
1128 if (m_acquiredCreationDeletionFileLock)
1129 {
1130 SharedMemoryManager::ReleaseCreationDeletionFileLock();
1131 }
1132
1133 if (!m_cancel && m_processDataHeader != nullptr)
1134 {
1135 _ASSERTE(m_acquiredCreationDeletionProcessLock);
1136 m_processDataHeader->DecRefCount();
1137 }
1138
1139 if (m_acquiredCreationDeletionProcessLock)
1140 {
1141 SharedMemoryManager::ReleaseCreationDeletionProcessLock();
1142 }
1143 }
1144 } autoCleanup;
1145
1146 SharedMemoryManager::AcquireCreationDeletionProcessLock();
1147 autoCleanup.m_acquiredCreationDeletionProcessLock = true;
1148
1149 // Create or open the shared memory
1150 bool created;
1151 SharedMemoryProcessDataHeader *processDataHeader =
1152 SharedMemoryProcessDataHeader::CreateOrOpen(
1153 name,
1154 SharedMemorySharedDataHeader(SharedMemoryType::Mutex, SyncSystemVersion),
1155 sizeof(NamedMutexSharedData),
1156 createIfNotExist,
1157 &created);
1158 if (createdRef != nullptr)
1159 {
1160 *createdRef = created;
1161 }
1162 if (created)
1163 {
1164 // If the shared memory file was created, the creation/deletion file lock would have been acquired so that we can
1165 // initialize the shared data
1166 _ASSERTE(SharedMemoryManager::IsCreationDeletionFileLockAcquired());
1167 autoCleanup.m_acquiredCreationDeletionFileLock = true;
1168 }
1169 if (processDataHeader == nullptr)
1170 {
1171 _ASSERTE(!createIfNotExist);
1172 return nullptr;
1173 }
1174 autoCleanup.m_processDataHeader = processDataHeader;
1175
1176 if (created)
1177 {
1178 // Initialize the shared data
1179 new(processDataHeader->GetSharedDataHeader()->GetData()) NamedMutexSharedData;
1180 }
1181
1182 if (processDataHeader->GetData() == nullptr)
1183 {
1184 #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
1185 // Create the lock files directory
1186 SharedMemoryHelpers::BuildSharedFilesPath(lockFilePath, SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME);
1187 if (created)
1188 {
1189 SharedMemoryHelpers::EnsureDirectoryExists(lockFilePath, true /* isGlobalLockAcquired */);
1190 }
1191
1192 // Create the session directory
1193 SharedMemoryId *id = processDataHeader->GetId();
1194 SharedMemoryHelpers::VerifyStringOperation(lockFilePath.Append('/'));
1195 SharedMemoryHelpers::VerifyStringOperation(id->AppendSessionDirectoryName(lockFilePath));
1196 if (created)
1197 {
1198 SharedMemoryHelpers::EnsureDirectoryExists(lockFilePath, true /* isGlobalLockAcquired */);
1199 autoCleanup.m_lockFilePath = &lockFilePath;
1200 autoCleanup.m_sessionDirectoryPathCharCount = lockFilePath.GetCount();
1201 }
1202
1203 // Create or open the lock file
1204 SharedMemoryHelpers::VerifyStringOperation(lockFilePath.Append('/'));
1205 SharedMemoryHelpers::VerifyStringOperation(lockFilePath.Append(id->GetName(), id->GetNameCharCount()));
1206 int lockFileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(lockFilePath, created);
1207 if (lockFileDescriptor == -1)
1208 {
1209 _ASSERTE(!created);
1210 if (createIfNotExist)
1211 {
1212 throw SharedMemoryException(static_cast<DWORD>(SharedMemoryError::IO));
1213 }
1214 return nullptr;
1215 }
1216 autoCleanup.m_createdLockFile = created;
1217 autoCleanup.m_lockFileDescriptor = lockFileDescriptor;
1218 #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
1219
1220 // Create the process data
1221 void *processDataBuffer = SharedMemoryHelpers::Alloc(sizeof(NamedMutexProcessData));
1222 AutoFreeBuffer autoFreeProcessDataBuffer(processDataBuffer);
1223 NamedMutexProcessData *processData =
1224 new(processDataBuffer)
1225 NamedMutexProcessData(
1226 processDataHeader
1227 #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
1228 ,
1229 lockFileDescriptor
1230 #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
1231 );
1232 autoFreeProcessDataBuffer.Cancel();
1233 processDataHeader->SetData(processData);
1234
1235 // If the mutex was created and if requested, acquire the lock initially while holding the creation/deletion locks
1236 if (created && acquireLockIfCreated)
1237 {
1238 MutexTryAcquireLockResult tryAcquireLockResult = processData->TryAcquireLock(0);
1239 _ASSERTE(tryAcquireLockResult == MutexTryAcquireLockResult::AcquiredLock);
1240 }
1241 }
1242
1243 autoCleanup.m_cancel = true;
1244 return processDataHeader;
1245}
1246
1247NamedMutexProcessData::NamedMutexProcessData(
1248 SharedMemoryProcessDataHeader *processDataHeader
1249#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
1250 ,
1251 int sharedLockFileDescriptor
1252#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
1253)
1254 :
1255 m_processDataHeader(processDataHeader),
1256 m_lockCount(0),
1257#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
1258 m_sharedLockFileDescriptor(sharedLockFileDescriptor),
1259#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
1260 m_lockOwnerThread(nullptr),
1261 m_nextInThreadOwnedNamedMutexList(nullptr)
1262{
1263 _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
1264 _ASSERTE(processDataHeader != nullptr);
1265
1266#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
1267 _ASSERTE(sharedLockFileDescriptor != -1);
1268
1269 m_processLockHandle = CreateMutex(nullptr /* lpMutexAttributes */, false /* bInitialOwner */, nullptr /* lpName */);
1270 if (m_processLockHandle == nullptr)
1271 {
1272 throw SharedMemoryException(GetLastError());
1273 }
1274#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
1275}
1276
1277void NamedMutexProcessData::Close(bool isAbruptShutdown, bool releaseSharedData)
1278{
1279 _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired());
1280 _ASSERTE(!releaseSharedData || SharedMemoryManager::IsCreationDeletionFileLockAcquired());
1281
1282 // If the process is shutting down abruptly without having closed some mutexes, there could still be threads running with
1283 // active references to the mutex. So when shutting down abruptly, don't clean up any object or global process-local state.
1284 if (!isAbruptShutdown)
1285 {
1286 CPalThread *lockOwnerThread = m_lockOwnerThread;
1287 if (lockOwnerThread != nullptr)
1288 {
1289 // The mutex was not released before it was closed. If the lock is owned by the current thread, abandon the mutex.
1290 // In both cases, clean up the owner thread's list of owned mutexes.
1291 lockOwnerThread->synchronizationInfo.RemoveOwnedNamedMutex(this);
1292 if (lockOwnerThread == GetCurrentPalThread())
1293 {
1294 Abandon();
1295 }
1296 else
1297 {
1298 m_lockOwnerThread = nullptr;
1299 }
1300 }
1301
1302 if (releaseSharedData)
1303 {
1304 GetSharedData()->~NamedMutexSharedData();
1305 }
1306 }
1307
1308#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
1309 if (!isAbruptShutdown)
1310 {
1311 CloseHandle(m_processLockHandle);
1312 SharedMemoryHelpers::CloseFile(m_sharedLockFileDescriptor);
1313 }
1314
1315 if (!releaseSharedData)
1316 {
1317 return;
1318 }
1319
1320 try
1321 {
1322 // Delete the lock file, and the session directory if it's not empty
1323 PathCharString path;
1324 SharedMemoryHelpers::BuildSharedFilesPath(path, SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME);
1325 SharedMemoryId *id = m_processDataHeader->GetId();
1326 SharedMemoryHelpers::VerifyStringOperation(path.Append('/'));
1327 SharedMemoryHelpers::VerifyStringOperation(id->AppendSessionDirectoryName(path));
1328 SharedMemoryHelpers::VerifyStringOperation(path.Append('/'));
1329 SIZE_T sessionDirectoryPathCharCount = path.GetCount();
1330 SharedMemoryHelpers::VerifyStringOperation(path.Append(id->GetName(), id->GetNameCharCount()));
1331 unlink(path);
1332 path.CloseBuffer(sessionDirectoryPathCharCount);
1333 rmdir(path);
1334 }
1335 catch (SharedMemoryException)
1336 {
1337 // Ignore the error, just don't release shared data
1338 }
1339#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
1340}
1341
1342NamedMutexSharedData *NamedMutexProcessData::GetSharedData() const
1343{
1344 return reinterpret_cast<NamedMutexSharedData *>(m_processDataHeader->GetSharedDataHeader()->GetData());
1345}
1346
1347void NamedMutexProcessData::SetLockOwnerThread(CorUnix::CPalThread *lockOwnerThread)
1348{
1349 _ASSERTE(lockOwnerThread == nullptr || lockOwnerThread == GetCurrentPalThread());
1350 _ASSERTE(GetSharedData()->IsLockOwnedByCurrentThread());
1351
1352 m_lockOwnerThread = lockOwnerThread;
1353}
1354
1355NamedMutexProcessData *NamedMutexProcessData::GetNextInThreadOwnedNamedMutexList() const
1356{
1357 return m_nextInThreadOwnedNamedMutexList;
1358}
1359
1360void NamedMutexProcessData::SetNextInThreadOwnedNamedMutexList(NamedMutexProcessData *next)
1361{
1362 m_nextInThreadOwnedNamedMutexList = next;
1363}
1364
1365MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(DWORD timeoutMilliseconds)
1366{
1367 NamedMutexSharedData *sharedData = GetSharedData();
1368
1369#if NAMED_MUTEX_USE_PTHREAD_MUTEX
1370 MutexTryAcquireLockResult result = MutexHelpers::TryAcquireLock(sharedData->GetLock(), timeoutMilliseconds);
1371 if (result == MutexTryAcquireLockResult::TimedOut)
1372 {
1373 return result;
1374 }
1375
1376 // Check if a recursive lock was just taken. The recursion level is tracked manually so that the lock owner can be cleared
1377 // at the appropriate time, see ReleaseLock().
1378 if (m_lockCount != 0)
1379 {
1380 _ASSERTE(sharedData->IsLockOwnedByCurrentThread()); // otherwise, this thread would not have acquired the lock
1381 _ASSERTE(GetCurrentPalThread()->synchronizationInfo.OwnsNamedMutex(this));
1382
1383 if (m_lockCount + 1 < m_lockCount)
1384 {
1385 MutexHelpers::ReleaseLock(sharedData->GetLock());
1386 throw SharedMemoryException(static_cast<DWORD>(NamedMutexError::MaximumRecursiveLocksReached));
1387 }
1388 ++m_lockCount;
1389
1390 // The lock is released upon acquiring a recursive lock from the thread that already owns the lock
1391 MutexHelpers::ReleaseLock(sharedData->GetLock());
1392
1393 _ASSERTE(result != MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned);
1394 _ASSERTE(!sharedData->IsAbandoned());
1395 return result;
1396 }
1397
1398 // The non-recursive case is handled below (skip the #else and see below that)
1399#else // !NAMED_MUTEX_USE_PTHREAD_MUTEX
1400 // If a timeout is specified, determine the start time
1401 DWORD startTime = 0;
1402 if (timeoutMilliseconds != static_cast<DWORD>(-1) && timeoutMilliseconds != 0)
1403 {
1404 startTime = GetTickCount();
1405 }
1406
1407 // Acquire the process lock. A file lock can only be acquired once per file descriptor, so to synchronize the threads of
1408 // this process, the process lock is used.
1409 while (true)
1410 {
1411 DWORD waitResult = WaitForSingleObject(m_processLockHandle, timeoutMilliseconds);
1412 switch (waitResult)
1413 {
1414 case WAIT_OBJECT_0:
1415 case WAIT_ABANDONED: // abandoned state for the process lock is irrelevant, the shared lock will also have been abandoned
1416 break;
1417
1418 case WAIT_TIMEOUT:
1419 return MutexTryAcquireLockResult::TimedOut;
1420
1421 case WAIT_IO_COMPLETION:
1422 continue;
1423
1424 case WAIT_FAILED:
1425 throw SharedMemoryException(GetLastError());
1426
1427 default:
1428 _ASSERTE(false);
1429 break;
1430 }
1431 break;
1432 }
1433
1434 struct AutoReleaseProcessLock
1435 {
1436 HANDLE m_processLockHandle;
1437 bool m_cancel;
1438
1439 AutoReleaseProcessLock(HANDLE processLockHandle) : m_processLockHandle(processLockHandle), m_cancel(false)
1440 {
1441 }
1442
1443 ~AutoReleaseProcessLock()
1444 {
1445 if (!m_cancel)
1446 {
1447 ReleaseMutex(m_processLockHandle);
1448 }
1449 }
1450 } autoReleaseProcessLock(m_processLockHandle);
1451
1452 // Check if it's a recursive lock attempt
1453 if (m_lockCount != 0)
1454 {
1455 _ASSERTE(sharedData->IsLockOwnedByCurrentThread()); // otherwise, this thread would not have acquired the process lock
1456 _ASSERTE(GetCurrentPalThread()->synchronizationInfo.OwnsNamedMutex(this));
1457
1458 if (m_lockCount + 1 < m_lockCount)
1459 {
1460 throw SharedMemoryException(static_cast<DWORD>(NamedMutexError::MaximumRecursiveLocksReached));
1461 }
1462 ++m_lockCount;
1463
1464 // The process lock is released upon acquiring a recursive lock from the thread that already owns the lock
1465 return MutexTryAcquireLockResult::AcquiredLock;
1466 }
1467
1468 switch (timeoutMilliseconds)
1469 {
1470 case static_cast<DWORD>(-1):
1471 {
1472 // The file lock API does not have a timeout on the wait, so timed waiters will poll the file lock in a loop,
1473 // sleeping for a short duration in-between. Due to the polling nature of a timed wait, timed waiters will almost
1474 // never acquire the file lock as long as there are also untimed waiters. So, in order to make the file lock
1475 // acquisition reasonable, when there are timed waiters, have untimed waiters also use polling.
1476 bool acquiredFileLock = false;
1477 while (sharedData->HasAnyTimedWaiters())
1478 {
1479 if (SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB))
1480 {
1481 acquiredFileLock = true;
1482 break;
1483 }
1484 Sleep(PollLoopMaximumSleepMilliseconds);
1485 }
1486 if (acquiredFileLock)
1487 {
1488 break;
1489 }
1490
1491 acquiredFileLock = SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX);
1492 _ASSERTE(acquiredFileLock);
1493 break;
1494 }
1495
1496 case 0:
1497 if (!SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB))
1498 {
1499 return MutexTryAcquireLockResult::TimedOut;
1500 }
1501 break;
1502
1503 default:
1504 {
1505 // Try to acquire the file lock without waiting
1506 if (SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB))
1507 {
1508 break;
1509 }
1510
1511 // The file lock API does not have a timeout on the wait, so timed waiters need to poll the file lock in a loop,
1512 // sleeping for a short duration in-between. Due to the polling nature of a timed wait, timed waiters will almost
1513 // never acquire the file lock as long as there are also untimed waiters. So, in order to make the file lock
1514 // acquisition reasonable, record that there is a timed waiter, to have untimed waiters also use polling.
1515 sharedData->IncTimedWaiterCount();
1516 struct AutoDecTimedWaiterCount
1517 {
1518 NamedMutexSharedData *m_sharedData;
1519
1520 AutoDecTimedWaiterCount(NamedMutexSharedData *sharedData) : m_sharedData(sharedData)
1521 {
1522 }
1523
1524 ~AutoDecTimedWaiterCount()
1525 {
1526 m_sharedData->DecTimedWaiterCount();
1527 }
1528 } autoDecTimedWaiterCount(sharedData);
1529
1530 // Poll for the file lock
1531 do
1532 {
1533 DWORD elapsedMilliseconds = GetTickCount() - startTime;
1534 if (elapsedMilliseconds >= timeoutMilliseconds)
1535 {
1536 return MutexTryAcquireLockResult::TimedOut;
1537 }
1538
1539 DWORD remainingMilliseconds = timeoutMilliseconds - elapsedMilliseconds;
1540 DWORD sleepMilliseconds =
1541 remainingMilliseconds < PollLoopMaximumSleepMilliseconds
1542 ? remainingMilliseconds
1543 : PollLoopMaximumSleepMilliseconds;
1544 Sleep(sleepMilliseconds);
1545 } while (!SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB));
1546 break;
1547 }
1548 }
1549
1550 // There cannot be any exceptions after this
1551 autoReleaseProcessLock.m_cancel = true;
1552
1553 // After acquiring the file lock, if we find that a lock owner is already designated, the process that previously owned the
1554 // lock must have terminated while holding the lock.
1555 MutexTryAcquireLockResult result =
1556 sharedData->IsLockOwnedByAnyThread()
1557 ? MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned
1558 : MutexTryAcquireLockResult::AcquiredLock;
1559#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
1560
1561 sharedData->SetLockOwnerToCurrentThread();
1562 m_lockCount = 1;
1563 CPalThread *currentThread = GetCurrentPalThread();
1564 SetLockOwnerThread(currentThread);
1565 currentThread->synchronizationInfo.AddOwnedNamedMutex(this);
1566
1567 if (sharedData->IsAbandoned())
1568 {
1569 // The thread that previously owned the lock did not release it before exiting
1570 sharedData->SetIsAbandoned(false);
1571 result = MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned;
1572 }
1573 return result;
1574}
1575
1576void NamedMutexProcessData::ReleaseLock()
1577{
1578 if (!GetSharedData()->IsLockOwnedByCurrentThread())
1579 {
1580 throw SharedMemoryException(static_cast<DWORD>(NamedMutexError::ThreadHasNotAcquiredMutex));
1581 }
1582
1583 _ASSERTE(GetCurrentPalThread()->synchronizationInfo.OwnsNamedMutex(this));
1584
1585 _ASSERTE(m_lockCount != 0);
1586 --m_lockCount;
1587 if (m_lockCount != 0)
1588 {
1589 return;
1590 }
1591
1592 GetCurrentPalThread()->synchronizationInfo.RemoveOwnedNamedMutex(this);
1593 SetLockOwnerThread(nullptr);
1594 ActuallyReleaseLock();
1595}
1596
1597void NamedMutexProcessData::Abandon()
1598{
1599 NamedMutexSharedData *sharedData = GetSharedData();
1600 _ASSERTE(sharedData->IsLockOwnedByCurrentThread());
1601 _ASSERTE(m_lockCount != 0);
1602
1603 sharedData->SetIsAbandoned(true);
1604 m_lockCount = 0;
1605 SetLockOwnerThread(nullptr);
1606 ActuallyReleaseLock();
1607}
1608
1609void NamedMutexProcessData::ActuallyReleaseLock()
1610{
1611 NamedMutexSharedData *sharedData = GetSharedData();
1612 _ASSERTE(sharedData->IsLockOwnedByCurrentThread());
1613 _ASSERTE(!GetCurrentPalThread()->synchronizationInfo.OwnsNamedMutex(this));
1614 _ASSERTE(m_lockCount == 0);
1615
1616 sharedData->ClearLockOwner();
1617
1618#if NAMED_MUTEX_USE_PTHREAD_MUTEX
1619 MutexHelpers::ReleaseLock(sharedData->GetLock());
1620#else // !NAMED_MUTEX_USE_PTHREAD_MUTEX
1621 SharedMemoryHelpers::ReleaseFileLock(m_sharedLockFileDescriptor);
1622 ReleaseMutex(m_processLockHandle);
1623#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
1624}
1625