intprops: new macro INT_PROMOTE
[gnulib.git] / lib / windows-timedmutex.c
blob6ee6edbf325ae8139e3db5f0c29b462a4291d659
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. */
20 #include <config.h>
22 /* Specification. */
23 #include "windows-timedmutex.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_timedmutex_init (glwthread_timedmutex_t *mutex)
36 /* Attempt to allocate an auto-reset event object. */
37 /* CreateEvent
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)
41 return EAGAIN;
42 mutex->event = event;
43 mutex->owner = 0;
44 InitializeCriticalSection (&mutex->lock);
45 mutex->guard.done = 1;
46 return 0;
49 int
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.
57 Initialize it. */
58 int err = glwthread_timedmutex_init (mutex);
59 if (err != 0)
61 /* Undo increment. */
62 InterlockedDecrement (&mutex->guard.started);
63 return err;
66 else
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)
73 Sleep (0);
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 ();
81 mutex->owner = self;
83 return 0;
86 int
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.
94 Initialize it. */
95 int err = glwthread_timedmutex_init (mutex);
96 if (err != 0)
98 /* Undo increment. */
99 InterlockedDecrement (&mutex->guard.started);
100 return err;
103 else
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
108 lock this mutex. */
109 return EBUSY;
112 if (!TryEnterCriticalSection (&mutex->lock))
113 return EBUSY;
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);
123 return EBUSY;
125 if (mutex->owner != 0)
126 abort ();
127 mutex->owner = self;
129 return 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.
141 Initialize it. */
142 int err = glwthread_timedmutex_init (mutex);
143 if (err != 0)
145 /* Undo increment. */
146 InterlockedDecrement (&mutex->guard.started);
147 return err;
150 else
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)
157 Sleep (0);
161 /* POSIX says:
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
165 immediately."
166 Therefore start the loop with a TryEnterCriticalSection call. */
167 for (;;)
169 if (TryEnterCriticalSection (&mutex->lock))
170 break;
173 struct timeval currtime;
174 DWORD timeout;
175 DWORD result;
177 gettimeofday (&currtime, NULL);
179 /* Wait until another thread signals the event or until the
180 abstime passes. */
181 if (currtime.tv_sec > abstime->tv_sec)
182 timeout = 0;
183 else
185 unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
186 timeout = seconds * 1000;
187 if (timeout / 1000 != seconds) /* overflow? */
188 timeout = INFINITE;
189 else
191 long milliseconds =
192 abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
193 if (milliseconds >= 0)
195 timeout += milliseconds;
196 if (timeout < milliseconds) /* overflow? */
197 timeout = INFINITE;
199 else
201 if (timeout >= - milliseconds)
202 timeout -= (- milliseconds);
203 else
204 timeout = 0;
208 if (timeout == 0)
209 return ETIMEDOUT;
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)
215 abort ();
216 if (result == WAIT_TIMEOUT)
217 return ETIMEDOUT;
218 /* Another thread has just unlocked the mutex. We have good chances at
219 locking it now. */
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);
231 return EDEADLK;
233 if (mutex->owner != 0)
234 abort ();
235 mutex->owner = self;
237 return 0;
241 glwthread_timedmutex_unlock (glwthread_timedmutex_t *mutex)
243 if (!mutex->guard.done)
244 return EINVAL;
245 mutex->owner = 0;
246 LeaveCriticalSection (&mutex->lock);
247 /* Notify one of the threads that were waiting with a timeout. */
248 /* SetEvent
249 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-setevent> */
250 SetEvent (mutex->event);
251 return 0;
255 glwthread_timedmutex_destroy (glwthread_timedmutex_t *mutex)
257 if (!mutex->guard.done)
258 return EINVAL;
259 DeleteCriticalSection (&mutex->lock);
260 /* CloseHandle
261 <https://docs.microsoft.com/en-us/windows/desktop/api/handleapi/nf-handleapi-closehandle> */
262 CloseHandle (mutex->event);
263 mutex->guard.done = 0;
264 return 0;