@@ -21,23 +21,18 @@ Module Name:
2121#define _PAL_MUTEX_H_
2222
2323#include " corunix.hpp"
24- #include " sharedmemory.h"
2524
2625#include < pthread.h>
2726
2827namespace CorUnix
2928{
3029 extern CObjectType otMutex;
31- extern CObjectType otNamedMutex;
3230
3331 PAL_ERROR
3432 InternalCreateMutex (
35- SharedMemorySystemCallErrors *errors,
3633 CPalThread *pThread,
3734 LPSECURITY_ATTRIBUTES lpMutexAttributes,
3835 BOOL bInitialOwner,
39- LPCSTR lpName,
40- BOOL bCurrentUserOnly,
4136 HANDLE *phMutex
4237 );
4338
@@ -46,16 +41,6 @@ namespace CorUnix
4641 CPalThread *pThread,
4742 HANDLE hMutex
4843 );
49-
50- PAL_ERROR
51- InternalOpenMutex (
52- SharedMemorySystemCallErrors *errors,
53- CPalThread *pThread,
54- LPCSTR lpName,
55- BOOL bCurrentUserOnly,
56- HANDLE *phMutex
57- );
58-
5944}
6045
6146#define SYNCSPINLOCK_F_ASYMMETRIC 1
@@ -66,197 +51,4 @@ namespace CorUnix
6651void SPINLOCKAcquire (LONG * lock, unsigned int flags);
6752void SPINLOCKRelease (LONG * lock);
6853DWORD SPINLOCKTryAcquire (LONG * lock);
69-
70- // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
71- // Named mutex
72-
73- /*
74- Design
75-
76- - On systems that support pthread process-shared robust recursive mutexes, they will be used
77- - On other systems, file locks are used. File locks unfortunately don't have a timeout in the blocking wait call, and I didn't
78- find any other sync object with a timed wait with the necessary properties, so polling is done for timed waits.
79-
80- Shared memory files
81- - Session-scoped mutexes (name not prefixed, or prefixed with Local) go in /tmp/.dotnet/shm/session<sessionId>/<mutexName>
82- - Globally-scoped mutexes (name prefixed with Global) go in /tmp/.dotnet/shm/global/<mutexName>
83- - Contains shared state, and is mmap'ped into the process, see SharedMemorySharedDataHeader and NamedMutexSharedData for data
84- stored
85- - Creation and deletion is synchronized using an exclusive file lock on the shm directory
86- - Any process using the shared memory file holds a shared file lock on the shared memory file
87- - Upon creation, if the shared memory file already exists, an exclusive file lock is attempted on it, to see if the file data is
88- valid. If no other processes have the mutex open, the file is reinitialized.
89- - Upon releasing the last reference to a mutex in a process, it will try to get an exclusive lock on the shared memory file to
90- see if any other processes have the mutex opened. If not, the file is deleted, along with the session directory if it's empty.
91- The .dotnet and shm directories are not deleted.
92- - This allows managing the lifetime of mutex state based on active processes that have the mutex open. Depending on how the
93- process terminated, the file may still be left over in the tmp directory, I haven't found anything that can be done about
94- that.
95-
96- Lock files when using file locks:
97- - In addition to the shared memory file, we need another file for the actual synchronization file lock, since a file lock on the
98- shared memory file is used for lifetime purposes.
99- - These files go in /tmp/.dotnet/lockfiles/session<sessionId>|global/<mutexName>
100- - The file is empty, and is only used for file locks
101-
102- Process data
103- - See SharedMemoryProcessDataHeader and NamedMutexProcessData for data stored
104- - Per mutex name, there is only one instance of process data that is ref-counted. They are currently stored in a linked list in
105- SharedMemoryManager. It should use a hash table, but of the many hash table implementations that are already there, none seem
106- to be easily usable in the PAL. I'll look into that and will fix later.
107- - Refers to the associated shared memory, and knows how to clean up both the process data and shared data
108- - When using file locks for synchronization, a process-local mutex is also created for synchronizing threads, since file locks
109- are owned at the file descriptor level and there is only one open file descriptor in the process per mutex name. The
110- process-local mutex is locked around the file lock, so that only one thread per process is ever trying to flock on a given
111- file descriptor.
112-
113- Abandon detection
114- - When a lock is acquired, the process data is added to a linked list on the owning thread
115- - When a thread exits, the list is walked, each mutex is flagged as abandoned and released
116- - For detecting process abruptly terminating, pthread robust mutexes give us that. When using file locks, the file lock is
117- automatically released by the system. Upon acquiring a lock, the lock owner info in the shared memory is checked to see if the
118- mutex was abandoned.
119-
120- Miscellaneous
121- - CreateMutex and OpenMutex both create new handles for each mutex opened. Each handle just refers to the process data header
122- for the mutex name.
123- - Some of the above features are already available in the PAL, but not quite in a way that I can use for this purpose. The
124- existing shared memory, naming, and waiting infrastructure is not suitable for this purpose, and is not used.
125- */
126-
127- // - On FreeBSD, pthread process-shared robust mutexes cannot be placed in shared memory mapped independently by the processes
128- // involved. See https://github.com/dotnet/runtime/issues/10519.
129- // - On OSX, pthread robust mutexes were/are not available at the time of this writing. In case they are made available in the
130- // future, their use is disabled for compatibility.
131- #if HAVE_FULLY_FEATURED_PTHREAD_MUTEXES && \
132- HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES && \
133- !(defined (__FreeBSD__) || defined (TARGET_OSX))
134-
135- #define NAMED_MUTEX_USE_PTHREAD_MUTEX 1
136- #else
137- #define NAMED_MUTEX_USE_PTHREAD_MUTEX 0
138- #endif
139-
140- enum class NamedMutexError : DWORD
141- {
142- MaximumRecursiveLocksReached = ERROR_NOT_ENOUGH_MEMORY,
143- ThreadHasNotAcquiredMutex = ERROR_NOT_OWNER,
144- Unknown = ERROR_NOT_ENOUGH_MEMORY
145- };
146-
147- enum class MutexTryAcquireLockResult
148- {
149- AcquiredLock,
150- AcquiredLockButMutexWasAbandoned,
151- TimedOut
152- };
153-
154- #if NAMED_MUTEX_USE_PTHREAD_MUTEX
155- class MutexHelpers
156- {
157- public:
158- static void InitializeProcessSharedRobustRecursiveMutex (SharedMemorySystemCallErrors *errors, pthread_mutex_t *mutex);
159- static void DestroyMutex (pthread_mutex_t *mutex);
160-
161- static MutexTryAcquireLockResult TryAcquireLock (SharedMemorySystemCallErrors *errors, pthread_mutex_t *mutex, DWORD timeoutMilliseconds);
162- static void ReleaseLock (pthread_mutex_t *mutex);
163- };
164- #endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
165-
166- class NamedMutexSharedData
167- {
168- private:
169- #if NAMED_MUTEX_USE_PTHREAD_MUTEX
170- pthread_mutex_t m_lock;
171- #else // !NAMED_MUTEX_USE_PTHREAD_MUTEX
172- UINT32 m_timedWaiterCount;
173- #endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
174- UINT32 m_lockOwnerProcessId;
175- UINT64 m_lockOwnerThreadId;
176- bool m_isAbandoned;
177-
178- public:
179- NamedMutexSharedData (SharedMemorySystemCallErrors *errors);
180- ~NamedMutexSharedData ();
181-
182- #if NAMED_MUTEX_USE_PTHREAD_MUTEX
183- public:
184- pthread_mutex_t *GetLock ();
185- #else // !NAMED_MUTEX_USE_PTHREAD_MUTEX
186- public:
187- bool HasAnyTimedWaiters () const ;
188- void IncTimedWaiterCount ();
189- void DecTimedWaiterCount ();
190- #endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
191-
192- public:
193- bool IsAbandoned () const ;
194- void SetIsAbandoned (bool isAbandoned);
195-
196- public:
197- bool IsLockOwnedByAnyThread () const ;
198- bool IsLockOwnedByCurrentThread () const ;
199- void SetLockOwnerToCurrentThread ();
200- void ClearLockOwner ();
201- };
202-
203- class NamedMutexProcessData : public SharedMemoryProcessDataBase
204- {
205- private:
206- static const UINT8 SyncSystemVersion;
207- static const DWORD PollLoopMaximumSleepMilliseconds;
208-
209- private:
210- SharedMemoryProcessDataHeader *m_processDataHeader;
211- SIZE_T m_lockCount;
212- #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
213- HANDLE m_processLockHandle;
214- int m_sharedLockFileDescriptor;
215- #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
216- CorUnix::CPalThread *m_lockOwnerThread;
217- NamedMutexProcessData *m_nextInThreadOwnedNamedMutexList;
218- bool m_hasRefFromLockOwnerThread;
219-
220- public:
221- static SharedMemoryProcessDataHeader *CreateOrOpen (SharedMemorySystemCallErrors *errors, LPCSTR name, bool isUserScope, bool acquireLockIfCreated, bool *createdRef);
222- static SharedMemoryProcessDataHeader *Open (SharedMemorySystemCallErrors *errors, LPCSTR name, bool isUserScope);
223- private:
224- static SharedMemoryProcessDataHeader *CreateOrOpen (SharedMemorySystemCallErrors *errors, LPCSTR name, bool isUserScope, bool createIfNotExist, bool acquireLockIfCreated, bool *createdRef);
225-
226- public:
227- NamedMutexProcessData (
228- SharedMemoryProcessDataHeader *processDataHeader
229- #if !NAMED_MUTEX_USE_PTHREAD_MUTEX
230- ,
231- int sharedLockFileDescriptor
232- #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX
233- );
234-
235- public:
236- virtual bool CanClose () const override ;
237- virtual bool HasImplicitRef () const override ;
238- virtual void SetHasImplicitRef (bool value) override ;
239- virtual void Close (bool isAbruptShutdown, bool releaseSharedData) override ;
240-
241- public:
242- bool IsLockOwnedByCurrentThread () const
243- {
244- return GetSharedData ()->IsLockOwnedByCurrentThread ();
245- }
246-
247- private:
248- NamedMutexSharedData *GetSharedData () const ;
249- void SetLockOwnerThread (CorUnix::CPalThread *lockOwnerThread);
250- public:
251- NamedMutexProcessData *GetNextInThreadOwnedNamedMutexList () const ;
252- void SetNextInThreadOwnedNamedMutexList (NamedMutexProcessData *next);
253-
254- public:
255- MutexTryAcquireLockResult TryAcquireLock (SharedMemorySystemCallErrors *errors, DWORD timeoutMilliseconds);
256- void ReleaseLock ();
257- void Abandon ();
258- private:
259- void ActuallyReleaseLock ();
260- };
261-
26254#endif // _PAL_MUTEX_H_
0 commit comments