exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / windows-timedrecmutex.c
blob5c9ca78d96dd33290454a1f0f9caffd78f2c7b4b
1 /* Timed recursive mutexes (native Windows implementation).
2 Copyright (C) 2005-2024 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. */
20 #include <config.h>
22 /* Specification. */
23 #include "windows-timedrecmutex.h"
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <sys/time.h>
29 /* Don't assume that UNICODE is not defined. */
30 #undef CreateEvent
31 #define CreateEvent CreateEventA
33 int
34 glwthread_timedrecmutex_init (glwthread_timedrecmutex_t *mutex)
36 mutex->owner = 0;
37 mutex->depth = 0;
38 /* Attempt to allocate an auto-reset event object. */
39 /* CreateEvent
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)
43 return EAGAIN;
44 mutex->event = event;
45 InitializeCriticalSection (&mutex->lock);
46 mutex->guard.done = 1;
47 return 0;
50 int
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.
58 Initialize it. */
59 int err = glwthread_timedrecmutex_init (mutex);
60 if (err != 0)
62 /* Undo increment. */
63 InterlockedDecrement (&mutex->guard.started);
64 return err;
67 else
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)
74 Sleep (0);
78 DWORD self = GetCurrentThreadId ();
79 if (mutex->owner != self)
81 EnterCriticalSection (&mutex->lock);
82 mutex->owner = self;
84 if (++(mutex->depth) == 0) /* wraparound? */
86 mutex->depth--;
87 return EAGAIN;
90 return 0;
93 int
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.
101 Initialize it. */
102 int err = glwthread_timedrecmutex_init (mutex);
103 if (err != 0)
105 /* Undo increment. */
106 InterlockedDecrement (&mutex->guard.started);
107 return err;
110 else
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
115 lock this mutex. */
116 return EBUSY;
120 DWORD self = GetCurrentThreadId ();
121 if (mutex->owner != self)
123 if (!TryEnterCriticalSection (&mutex->lock))
124 return EBUSY;
125 mutex->owner = self;
127 if (++(mutex->depth) == 0) /* wraparound? */
129 mutex->depth--;
130 return EAGAIN;
133 return 0;
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.
145 Initialize it. */
146 int err = glwthread_timedrecmutex_init (mutex);
147 if (err != 0)
149 /* Undo increment. */
150 InterlockedDecrement (&mutex->guard.started);
151 return err;
154 else
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)
161 Sleep (0);
166 DWORD self = GetCurrentThreadId ();
167 if (mutex->owner != self)
169 /* POSIX says:
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
173 immediately."
174 Therefore start the loop with a TryEnterCriticalSection call. */
175 for (;;)
177 if (TryEnterCriticalSection (&mutex->lock))
178 break;
181 struct timeval currtime;
182 DWORD timeout;
183 DWORD result;
185 gettimeofday (&currtime, NULL);
187 /* Wait until another thread signals the event or until the
188 abstime passes. */
189 if (currtime.tv_sec > abstime->tv_sec)
190 timeout = 0;
191 else
193 unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
194 timeout = seconds * 1000;
195 if (timeout / 1000 != seconds) /* overflow? */
196 timeout = INFINITE;
197 else
199 long milliseconds =
200 abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
201 if (milliseconds >= 0)
203 timeout += milliseconds;
204 if (timeout < milliseconds) /* overflow? */
205 timeout = INFINITE;
207 else
209 if (timeout >= - milliseconds)
210 timeout -= (- milliseconds);
211 else
212 timeout = 0;
216 if (timeout == 0)
217 return ETIMEDOUT;
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)
223 abort ();
224 if (result == WAIT_TIMEOUT)
225 return ETIMEDOUT;
226 /* Another thread has just unlocked the mutex. We have good chances at
227 locking it now. */
230 mutex->owner = self;
232 if (++(mutex->depth) == 0) /* wraparound? */
234 mutex->depth--;
235 return EAGAIN;
238 return 0;
242 glwthread_timedrecmutex_unlock (glwthread_timedrecmutex_t *mutex)
244 if (mutex->owner != GetCurrentThreadId ())
245 return EPERM;
246 if (mutex->depth == 0)
247 return EINVAL;
248 if (--(mutex->depth) == 0)
250 mutex->owner = 0;
251 LeaveCriticalSection (&mutex->lock);
252 /* Notify one of the threads that were waiting with a timeout. */
253 /* SetEvent
254 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-setevent> */
255 SetEvent (mutex->event);
257 return 0;
261 glwthread_timedrecmutex_destroy (glwthread_timedrecmutex_t *mutex)
263 if (mutex->owner != 0)
264 return EBUSY;
265 DeleteCriticalSection (&mutex->lock);
266 /* CloseHandle
267 <https://docs.microsoft.com/en-us/windows/desktop/api/handleapi/nf-handleapi-closehandle> */
268 CloseHandle (mutex->event);
269 mutex->guard.done = 0;
270 return 0;