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 | |
9 | Module Name: |
10 | |
11 | mutex.ccpp |
12 | |
13 | Abstract: |
14 | |
15 | Implementation of mutex synchroniztion object as described in |
16 | the WIN32 API |
17 | |
18 | Revision History: |
19 | |
20 | |
21 | |
22 | --*/ |
23 | |
24 | #include "pal/dbgmsg.h" |
25 | |
26 | SET_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 | |
43 | using namespace CorUnix; |
44 | |
45 | /* ------------------- Definitions ------------------------------*/ |
46 | |
47 | CObjectType 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 | |
68 | static CAllowedObjectTypes aotMutex(otiMutex); |
69 | |
70 | CObjectType 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 | |
91 | static CAllowedObjectTypes aotNamedMutex(otiNamedMutex); |
92 | |
93 | static PalObjectTypeId anyMutexTypeIds[] = {otiMutex, otiNamedMutex}; |
94 | static CAllowedObjectTypes aotAnyMutex(anyMutexTypeIds, _countof(anyMutexTypeIds)); |
95 | |
96 | /*++ |
97 | Function: |
98 | CreateMutexA |
99 | |
100 | Note: |
101 | lpMutexAttributes currentely ignored: |
102 | -- Win32 object security not supported |
103 | -- handles to mutex objects are not inheritable |
104 | |
105 | Parameters: |
106 | See MSDN doc. |
107 | --*/ |
108 | |
109 | HANDLE |
110 | PALAPI |
111 | CreateMutexA( |
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 | /*++ |
150 | Function: |
151 | CreateMutexW |
152 | |
153 | Note: |
154 | lpMutexAttributes currentely ignored: |
155 | -- Win32 object security not supported |
156 | -- handles to mutex objects are not inheritable |
157 | |
158 | Parameters: |
159 | See MSDN doc. |
160 | --*/ |
161 | |
162 | HANDLE |
163 | PALAPI |
164 | CreateMutexW( |
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 | |
207 | CreateMutexWExit: |
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 | /*++ |
223 | Function: |
224 | CreateMutexW |
225 | |
226 | Note: |
227 | lpMutexAttributes currentely ignored: |
228 | -- Win32 object security not supported |
229 | -- handles to mutex objects are not inheritable |
230 | |
231 | Parameters: |
232 | See MSDN doc. |
233 | --*/ |
234 | |
235 | HANDLE |
236 | PALAPI |
237 | CreateMutexExW( |
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 | /*++ |
247 | Function: |
248 | InternalCreateMutex |
249 | |
250 | Note: |
251 | lpMutexAttributes currentely ignored: |
252 | -- Win32 object security not supported |
253 | -- handles to mutex objects are not inheritable |
254 | |
255 | Parameters: |
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 | |
262 | PAL_ERROR |
263 | CorUnix::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 *; |
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 | |
393 | InternalCreateMutexExit: |
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 | /*++ |
411 | Function: |
412 | ReleaseMutex |
413 | |
414 | Parameters: |
415 | See MSDN doc. |
416 | --*/ |
417 | |
418 | BOOL |
419 | PALAPI |
420 | ReleaseMutex( 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 | /*++ |
443 | Function: |
444 | InternalReleaseMutex |
445 | |
446 | Parameters: |
447 | pthr -- thread data for calling thread |
448 | |
449 | See MSDN docs on ReleaseMutex for all other parameters |
450 | --*/ |
451 | |
452 | PAL_ERROR |
453 | CorUnix::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 * = |
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 | |
524 | InternalReleaseMutexExit: |
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 | /*++ |
542 | Function: |
543 | OpenMutexA |
544 | |
545 | Note: |
546 | dwDesiredAccess is currently ignored (no Win32 object security support) |
547 | bInheritHandle is currently ignored (handles to mutexes are not inheritable) |
548 | |
549 | See MSDN doc. |
550 | --*/ |
551 | |
552 | HANDLE |
553 | PALAPI |
554 | OpenMutexA ( |
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 | |
579 | OpenMutexAExit: |
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 | /*++ |
591 | Function: |
592 | OpenMutexW |
593 | |
594 | Note: |
595 | dwDesiredAccess is currently ignored (no Win32 object security support) |
596 | bInheritHandle is currently ignored (handles to mutexes are not inheritable) |
597 | |
598 | See MSDN doc. |
599 | --*/ |
600 | |
601 | HANDLE |
602 | PALAPI |
603 | OpenMutexW( |
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 | |
647 | OpenMutexWExit: |
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 | /*++ |
660 | Function: |
661 | InternalOpenMutex |
662 | |
663 | Note: |
664 | dwDesiredAccess is currently ignored (no Win32 object security support) |
665 | bInheritHandle is currently ignored (handles to mutexes are not inheritable) |
666 | |
667 | Parameters: |
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 | |
674 | PAL_ERROR |
675 | CorUnix::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 *; |
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 | |
761 | InternalOpenMutexExit: |
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 */ |
780 | void 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 | |
805 | void SPINLOCKRelease (LONG * lock) |
806 | { |
807 | VolatileStore(lock, 0); |
808 | } |
809 | |
810 | DWORD 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 |
820 | void 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 | |
866 | void 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 | |
874 | MutexTryAcquireLockResult 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 | |
928 | void 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 | |
940 | NamedMutexSharedData::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 | |
961 | NamedMutexSharedData::~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 |
972 | pthread_mutex_t *NamedMutexSharedData::GetLock() |
973 | { |
974 | return &m_lock; |
975 | } |
976 | #else // !NAMED_MUTEX_USE_PTHREAD_MUTEX |
977 | bool 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 | |
986 | void 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 | |
995 | void 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 | |
1002 | bool NamedMutexSharedData::IsAbandoned() const |
1003 | { |
1004 | _ASSERTE(IsLockOwnedByCurrentThread()); |
1005 | return m_isAbandoned; |
1006 | } |
1007 | |
1008 | void NamedMutexSharedData::SetIsAbandoned(bool isAbandoned) |
1009 | { |
1010 | _ASSERTE(IsLockOwnedByCurrentThread()); |
1011 | _ASSERTE(m_isAbandoned != isAbandoned); |
1012 | |
1013 | m_isAbandoned = isAbandoned; |
1014 | } |
1015 | |
1016 | bool NamedMutexSharedData::IsLockOwnedByAnyThread() const |
1017 | { |
1018 | return |
1019 | m_lockOwnerProcessId != SharedMemoryHelpers::InvalidProcessId || |
1020 | m_lockOwnerThreadId != SharedMemoryHelpers::InvalidSharedThreadId; |
1021 | } |
1022 | |
1023 | bool NamedMutexSharedData::IsLockOwnedByCurrentThread() const |
1024 | { |
1025 | return m_lockOwnerProcessId == GetCurrentProcessId() && m_lockOwnerThreadId == THREADSilentGetCurrentThreadId(); |
1026 | } |
1027 | |
1028 | void 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 | |
1036 | void 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. |
1049 | const UINT8 NamedMutexProcessData::SyncSystemVersion = 1; |
1050 | |
1051 | const DWORD NamedMutexProcessData::PollLoopMaximumSleepMilliseconds = 100; |
1052 | |
1053 | SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(LPCSTR name, bool acquireLockIfCreated, bool *createdRef) |
1054 | { |
1055 | return CreateOrOpen(name, true /* createIfNotExist */, acquireLockIfCreated, createdRef); |
1056 | } |
1057 | |
1058 | SharedMemoryProcessDataHeader *NamedMutexProcessData::Open(LPCSTR name) |
1059 | { |
1060 | return CreateOrOpen(name, false /* createIfNotExist */, false /* acquireLockIfCreated */, nullptr /* createdRef */); |
1061 | } |
1062 | |
1063 | SharedMemoryProcessDataHeader *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 *; |
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 * = |
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 | |
1247 | NamedMutexProcessData::( |
1248 | SharedMemoryProcessDataHeader * |
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 | |
1277 | void 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 | |
1342 | NamedMutexSharedData *NamedMutexProcessData::GetSharedData() const |
1343 | { |
1344 | return reinterpret_cast<NamedMutexSharedData *>(m_processDataHeader->GetSharedDataHeader()->GetData()); |
1345 | } |
1346 | |
1347 | void NamedMutexProcessData::SetLockOwnerThread(CorUnix::CPalThread *lockOwnerThread) |
1348 | { |
1349 | _ASSERTE(lockOwnerThread == nullptr || lockOwnerThread == GetCurrentPalThread()); |
1350 | _ASSERTE(GetSharedData()->IsLockOwnedByCurrentThread()); |
1351 | |
1352 | m_lockOwnerThread = lockOwnerThread; |
1353 | } |
1354 | |
1355 | NamedMutexProcessData *NamedMutexProcessData::GetNextInThreadOwnedNamedMutexList() const |
1356 | { |
1357 | return m_nextInThreadOwnedNamedMutexList; |
1358 | } |
1359 | |
1360 | void NamedMutexProcessData::SetNextInThreadOwnedNamedMutexList(NamedMutexProcessData *next) |
1361 | { |
1362 | m_nextInThreadOwnedNamedMutexList = next; |
1363 | } |
1364 | |
1365 | MutexTryAcquireLockResult 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 | |
1576 | void 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 | |
1597 | void 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 | |
1609 | void 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 | |