1 /* Timed mutexes (native Windows implementation).
2 Copyright (C) 2005-2025 Free Software Foundation, Inc.
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
9 This file 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 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser 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-timedmutex.h"
29 /* Don't assume that UNICODE is not defined. */
31 #define CreateEvent CreateEventA
34 glwthread_timedmutex_init (glwthread_timedmutex_t
*mutex
)
36 /* Attempt to allocate an auto-reset event object. */
38 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createeventa> */
39 HANDLE event
= CreateEvent (NULL
, FALSE
, FALSE
, NULL
);
40 if (event
== INVALID_HANDLE_VALUE
)
44 InitializeCriticalSection (&mutex
->lock
);
45 mutex
->guard
.done
= 1;
50 glwthread_timedmutex_lock (glwthread_timedmutex_t
*mutex
)
52 if (!mutex
->guard
.done
)
54 if (InterlockedIncrement (&mutex
->guard
.started
) == 0)
56 /* This thread is the first one to need this mutex.
58 int err
= glwthread_timedmutex_init (mutex
);
62 InterlockedDecrement (&mutex
->guard
.started
);
68 /* Don't let mutex->guard.started grow and wrap around. */
69 InterlockedDecrement (&mutex
->guard
.started
);
70 /* Yield the CPU while waiting for another thread to finish
71 initializing this mutex. */
72 while (!mutex
->guard
.done
)
76 /* If this thread already owns the mutex, POSIX pthread_mutex_lock() is
77 required to deadlock here. But let's not do that on purpose. */
78 EnterCriticalSection (&mutex
->lock
);
80 DWORD self
= GetCurrentThreadId ();
87 glwthread_timedmutex_trylock (glwthread_timedmutex_t
*mutex
)
89 if (!mutex
->guard
.done
)
91 if (InterlockedIncrement (&mutex
->guard
.started
) == 0)
93 /* This thread is the first one to need this mutex.
95 int err
= glwthread_timedmutex_init (mutex
);
99 InterlockedDecrement (&mutex
->guard
.started
);
105 /* Don't let mutex->guard.started grow and wrap around. */
106 InterlockedDecrement (&mutex
->guard
.started
);
107 /* Let another thread finish initializing this mutex, and let it also
112 if (!TryEnterCriticalSection (&mutex
->lock
))
115 DWORD self
= GetCurrentThreadId ();
116 /* TryEnterCriticalSection succeeded. This means that the mutex was either
117 previously unlocked (and thus mutex->owner == 0) or previously locked by
118 this thread (and thus mutex->owner == self). Since the mutex is meant to
119 be plain, we need to fail in the latter case. */
120 if (mutex
->owner
== self
)
122 LeaveCriticalSection (&mutex
->lock
);
125 if (mutex
->owner
!= 0)
133 glwthread_timedmutex_timedlock (glwthread_timedmutex_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_timedmutex_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 "Under no circumstance shall the function fail with a timeout if
163 the mutex can be locked immediately. The validity of the abstime
164 parameter need not be checked if the mutex can be locked
166 Therefore start the loop with a TryEnterCriticalSection call. */
169 if (TryEnterCriticalSection (&mutex
->lock
))
173 struct timeval currtime
;
177 gettimeofday (&currtime
, NULL
);
179 /* Wait until another thread signals the event or until the
181 if (currtime
.tv_sec
> abstime
->tv_sec
)
185 unsigned long seconds
= abstime
->tv_sec
- currtime
.tv_sec
;
186 timeout
= seconds
* 1000;
187 if (timeout
/ 1000 != seconds
) /* overflow? */
192 abstime
->tv_nsec
/ 1000000 - currtime
.tv_usec
/ 1000;
193 if (milliseconds
>= 0)
195 timeout
+= milliseconds
;
196 if (timeout
< milliseconds
) /* overflow? */
201 if (timeout
>= - milliseconds
)
202 timeout
-= (- milliseconds
);
211 /* WaitForSingleObject
212 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject> */
213 result
= WaitForSingleObject (mutex
->event
, timeout
);
214 if (result
== WAIT_FAILED
)
216 if (result
== WAIT_TIMEOUT
)
218 /* Another thread has just unlocked the mutex. We have good chances at
223 DWORD self
= GetCurrentThreadId ();
224 /* TryEnterCriticalSection succeeded. This means that the mutex was either
225 previously unlocked (and thus mutex->owner == 0) or previously locked by
226 this thread (and thus mutex->owner == self). Since the mutex is meant to
227 be plain, it is useful to fail in the latter case. */
228 if (mutex
->owner
== self
)
230 LeaveCriticalSection (&mutex
->lock
);
233 if (mutex
->owner
!= 0)
241 glwthread_timedmutex_unlock (glwthread_timedmutex_t
*mutex
)
243 if (!mutex
->guard
.done
)
246 LeaveCriticalSection (&mutex
->lock
);
247 /* Notify one of the threads that were waiting with a timeout. */
249 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-setevent> */
250 SetEvent (mutex
->event
);
255 glwthread_timedmutex_destroy (glwthread_timedmutex_t
*mutex
)
257 if (!mutex
->guard
.done
)
259 DeleteCriticalSection (&mutex
->lock
);
261 <https://docs.microsoft.com/en-us/windows/desktop/api/handleapi/nf-handleapi-closehandle> */
262 CloseHandle (mutex
->event
);
263 mutex
->guard
.done
= 0;