1 /* Timed recursive mutexes (native Windows implementation).
2 Copyright (C) 2005-2020 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005, 2019.
18 Based on GCC's gthr-win32.h. */
23 #include "windows-timedrecmutex.h"
30 glwthread_timedrecmutex_init (glwthread_timedrecmutex_t
*mutex
)
34 /* Attempt to allocate an auto-reset event object. */
36 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createeventa> */
37 HANDLE event
= CreateEvent (NULL
, FALSE
, FALSE
, NULL
);
38 if (event
== INVALID_HANDLE_VALUE
)
41 InitializeCriticalSection (&mutex
->lock
);
42 mutex
->guard
.done
= 1;
47 glwthread_timedrecmutex_lock (glwthread_timedrecmutex_t
*mutex
)
49 if (!mutex
->guard
.done
)
51 if (InterlockedIncrement (&mutex
->guard
.started
) == 0)
53 /* This thread is the first one to need this mutex.
55 int err
= glwthread_timedrecmutex_init (mutex
);
59 InterlockedDecrement (&mutex
->guard
.started
);
65 /* Don't let mutex->guard.started grow and wrap around. */
66 InterlockedDecrement (&mutex
->guard
.started
);
67 /* Yield the CPU while waiting for another thread to finish
68 initializing this mutex. */
69 while (!mutex
->guard
.done
)
74 DWORD self
= GetCurrentThreadId ();
75 if (mutex
->owner
!= self
)
77 EnterCriticalSection (&mutex
->lock
);
80 if (++(mutex
->depth
) == 0) /* wraparound? */
90 glwthread_timedrecmutex_trylock (glwthread_timedrecmutex_t
*mutex
)
92 if (!mutex
->guard
.done
)
94 if (InterlockedIncrement (&mutex
->guard
.started
) == 0)
96 /* This thread is the first one to need this mutex.
98 int err
= glwthread_timedrecmutex_init (mutex
);
101 /* Undo increment. */
102 InterlockedDecrement (&mutex
->guard
.started
);
108 /* Don't let mutex->guard.started grow and wrap around. */
109 InterlockedDecrement (&mutex
->guard
.started
);
110 /* Let another thread finish initializing this mutex, and let it also
116 DWORD self
= GetCurrentThreadId ();
117 if (mutex
->owner
!= self
)
119 if (!TryEnterCriticalSection (&mutex
->lock
))
123 if (++(mutex
->depth
) == 0) /* wraparound? */
133 glwthread_timedrecmutex_timedlock (glwthread_timedrecmutex_t
*mutex
,
134 const struct timespec
*abstime
)
136 if (!mutex
->guard
.done
)
138 if (InterlockedIncrement (&mutex
->guard
.started
) == 0)
140 /* This thread is the first one to need this mutex.
142 int err
= glwthread_timedrecmutex_init (mutex
);
145 /* Undo increment. */
146 InterlockedDecrement (&mutex
->guard
.started
);
152 /* Don't let mutex->guard.started grow and wrap around. */
153 InterlockedDecrement (&mutex
->guard
.started
);
154 /* Yield the CPU while waiting for another thread to finish
155 initializing this mutex. */
156 while (!mutex
->guard
.done
)
162 DWORD self
= GetCurrentThreadId ();
163 if (mutex
->owner
!= self
)
166 "Under no circumstance shall the function fail with a timeout if
167 the mutex can be locked immediately. The validity of the abstime
168 parameter need not be checked if the mutex can be locked
170 Therefore start the loop with a TryEnterCriticalSection call. */
173 if (TryEnterCriticalSection (&mutex
->lock
))
177 struct timeval currtime
;
181 gettimeofday (&currtime
, NULL
);
183 /* Wait until another thread signals the event or until the
185 if (currtime
.tv_sec
> abstime
->tv_sec
)
189 unsigned long seconds
= abstime
->tv_sec
- currtime
.tv_sec
;
190 timeout
= seconds
* 1000;
191 if (timeout
/ 1000 != seconds
) /* overflow? */
196 abstime
->tv_nsec
/ 1000000 - currtime
.tv_usec
/ 1000;
197 if (milliseconds
>= 0)
199 timeout
+= milliseconds
;
200 if (timeout
< milliseconds
) /* overflow? */
205 if (timeout
>= - milliseconds
)
206 timeout
-= (- milliseconds
);
215 /* WaitForSingleObject
216 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject> */
217 result
= WaitForSingleObject (mutex
->event
, timeout
);
218 if (result
== WAIT_FAILED
)
220 if (result
== WAIT_TIMEOUT
)
222 /* Another thread has just unlocked the mutex. We have good chances at
228 if (++(mutex
->depth
) == 0) /* wraparound? */
238 glwthread_timedrecmutex_unlock (glwthread_timedrecmutex_t
*mutex
)
240 if (mutex
->owner
!= GetCurrentThreadId ())
242 if (mutex
->depth
== 0)
244 if (--(mutex
->depth
) == 0)
247 LeaveCriticalSection (&mutex
->lock
);
248 /* Notify one of the threads that were waiting with a timeout. */
250 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-setevent> */
251 SetEvent (mutex
->event
);
257 glwthread_timedrecmutex_destroy (glwthread_timedrecmutex_t
*mutex
)
259 if (mutex
->owner
!= 0)
261 DeleteCriticalSection (&mutex
->lock
);
263 <https://docs.microsoft.com/en-us/windows/desktop/api/handleapi/nf-handleapi-closehandle> */
264 CloseHandle (mutex
->event
);
265 mutex
->guard
.done
= 0;