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"
29 /* Don't assume that UNICODE is not defined. */
31 #define CreateEvent CreateEventA
34 glwthread_timedrecmutex_init (glwthread_timedrecmutex_t
*mutex
)
38 /* Attempt to allocate an auto-reset event object. */
40 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createeventa> */
41 HANDLE event
= CreateEvent (NULL
, FALSE
, FALSE
, NULL
);
42 if (event
== INVALID_HANDLE_VALUE
)
45 InitializeCriticalSection (&mutex
->lock
);
46 mutex
->guard
.done
= 1;
51 glwthread_timedrecmutex_lock (glwthread_timedrecmutex_t
*mutex
)
53 if (!mutex
->guard
.done
)
55 if (InterlockedIncrement (&mutex
->guard
.started
) == 0)
57 /* This thread is the first one to need this mutex.
59 int err
= glwthread_timedrecmutex_init (mutex
);
63 InterlockedDecrement (&mutex
->guard
.started
);
69 /* Don't let mutex->guard.started grow and wrap around. */
70 InterlockedDecrement (&mutex
->guard
.started
);
71 /* Yield the CPU while waiting for another thread to finish
72 initializing this mutex. */
73 while (!mutex
->guard
.done
)
78 DWORD self
= GetCurrentThreadId ();
79 if (mutex
->owner
!= self
)
81 EnterCriticalSection (&mutex
->lock
);
84 if (++(mutex
->depth
) == 0) /* wraparound? */
94 glwthread_timedrecmutex_trylock (glwthread_timedrecmutex_t
*mutex
)
96 if (!mutex
->guard
.done
)
98 if (InterlockedIncrement (&mutex
->guard
.started
) == 0)
100 /* This thread is the first one to need this mutex.
102 int err
= glwthread_timedrecmutex_init (mutex
);
105 /* Undo increment. */
106 InterlockedDecrement (&mutex
->guard
.started
);
112 /* Don't let mutex->guard.started grow and wrap around. */
113 InterlockedDecrement (&mutex
->guard
.started
);
114 /* Let another thread finish initializing this mutex, and let it also
120 DWORD self
= GetCurrentThreadId ();
121 if (mutex
->owner
!= self
)
123 if (!TryEnterCriticalSection (&mutex
->lock
))
127 if (++(mutex
->depth
) == 0) /* wraparound? */
137 glwthread_timedrecmutex_timedlock (glwthread_timedrecmutex_t
*mutex
,
138 const struct timespec
*abstime
)
140 if (!mutex
->guard
.done
)
142 if (InterlockedIncrement (&mutex
->guard
.started
) == 0)
144 /* This thread is the first one to need this mutex.
146 int err
= glwthread_timedrecmutex_init (mutex
);
149 /* Undo increment. */
150 InterlockedDecrement (&mutex
->guard
.started
);
156 /* Don't let mutex->guard.started grow and wrap around. */
157 InterlockedDecrement (&mutex
->guard
.started
);
158 /* Yield the CPU while waiting for another thread to finish
159 initializing this mutex. */
160 while (!mutex
->guard
.done
)
166 DWORD self
= GetCurrentThreadId ();
167 if (mutex
->owner
!= self
)
170 "Under no circumstance shall the function fail with a timeout if
171 the mutex can be locked immediately. The validity of the abstime
172 parameter need not be checked if the mutex can be locked
174 Therefore start the loop with a TryEnterCriticalSection call. */
177 if (TryEnterCriticalSection (&mutex
->lock
))
181 struct timeval currtime
;
185 gettimeofday (&currtime
, NULL
);
187 /* Wait until another thread signals the event or until the
189 if (currtime
.tv_sec
> abstime
->tv_sec
)
193 unsigned long seconds
= abstime
->tv_sec
- currtime
.tv_sec
;
194 timeout
= seconds
* 1000;
195 if (timeout
/ 1000 != seconds
) /* overflow? */
200 abstime
->tv_nsec
/ 1000000 - currtime
.tv_usec
/ 1000;
201 if (milliseconds
>= 0)
203 timeout
+= milliseconds
;
204 if (timeout
< milliseconds
) /* overflow? */
209 if (timeout
>= - milliseconds
)
210 timeout
-= (- milliseconds
);
219 /* WaitForSingleObject
220 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject> */
221 result
= WaitForSingleObject (mutex
->event
, timeout
);
222 if (result
== WAIT_FAILED
)
224 if (result
== WAIT_TIMEOUT
)
226 /* Another thread has just unlocked the mutex. We have good chances at
232 if (++(mutex
->depth
) == 0) /* wraparound? */
242 glwthread_timedrecmutex_unlock (glwthread_timedrecmutex_t
*mutex
)
244 if (mutex
->owner
!= GetCurrentThreadId ())
246 if (mutex
->depth
== 0)
248 if (--(mutex
->depth
) == 0)
251 LeaveCriticalSection (&mutex
->lock
);
252 /* Notify one of the threads that were waiting with a timeout. */
254 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-setevent> */
255 SetEvent (mutex
->event
);
261 glwthread_timedrecmutex_destroy (glwthread_timedrecmutex_t
*mutex
)
263 if (mutex
->owner
!= 0)
265 DeleteCriticalSection (&mutex
->lock
);
267 <https://docs.microsoft.com/en-us/windows/desktop/api/handleapi/nf-handleapi-closehandle> */
268 CloseHandle (mutex
->event
);
269 mutex
->guard
.done
= 0;