Ensure Automake does not drop ~~gnulib.m4.
[gnulib.git] / lib / windows-timedrecmutex.c
blob81fcdb66da3754138d7170327f6a68332740280b
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)
7 any later version.
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. */
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 int
30 glwthread_timedrecmutex_init (glwthread_timedrecmutex_t *mutex)
32 mutex->owner = 0;
33 mutex->depth = 0;
34 /* Attempt to allocate an auto-reset event object. */
35 /* CreateEvent
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)
39 return EAGAIN;
40 mutex->event = event;
41 InitializeCriticalSection (&mutex->lock);
42 mutex->guard.done = 1;
43 return 0;
46 int
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.
54 Initialize it. */
55 int err = glwthread_timedrecmutex_init (mutex);
56 if (err != 0)
58 /* Undo increment. */
59 InterlockedDecrement (&mutex->guard.started);
60 return err;
63 else
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)
70 Sleep (0);
74 DWORD self = GetCurrentThreadId ();
75 if (mutex->owner != self)
77 EnterCriticalSection (&mutex->lock);
78 mutex->owner = self;
80 if (++(mutex->depth) == 0) /* wraparound? */
82 mutex->depth--;
83 return EAGAIN;
86 return 0;
89 int
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.
97 Initialize it. */
98 int err = glwthread_timedrecmutex_init (mutex);
99 if (err != 0)
101 /* Undo increment. */
102 InterlockedDecrement (&mutex->guard.started);
103 return err;
106 else
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
111 lock this mutex. */
112 return EBUSY;
116 DWORD self = GetCurrentThreadId ();
117 if (mutex->owner != self)
119 if (!TryEnterCriticalSection (&mutex->lock))
120 return EBUSY;
121 mutex->owner = self;
123 if (++(mutex->depth) == 0) /* wraparound? */
125 mutex->depth--;
126 return EAGAIN;
129 return 0;
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.
141 Initialize it. */
142 int err = glwthread_timedrecmutex_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);
162 DWORD self = GetCurrentThreadId ();
163 if (mutex->owner != self)
165 /* POSIX says:
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
169 immediately."
170 Therefore start the loop with a TryEnterCriticalSection call. */
171 for (;;)
173 if (TryEnterCriticalSection (&mutex->lock))
174 break;
177 struct timeval currtime;
178 DWORD timeout;
179 DWORD result;
181 gettimeofday (&currtime, NULL);
183 /* Wait until another thread signals the event or until the
184 abstime passes. */
185 if (currtime.tv_sec > abstime->tv_sec)
186 timeout = 0;
187 else
189 unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
190 timeout = seconds * 1000;
191 if (timeout / 1000 != seconds) /* overflow? */
192 timeout = INFINITE;
193 else
195 long milliseconds =
196 abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
197 if (milliseconds >= 0)
199 timeout += milliseconds;
200 if (timeout < milliseconds) /* overflow? */
201 timeout = INFINITE;
203 else
205 if (timeout >= - milliseconds)
206 timeout -= (- milliseconds);
207 else
208 timeout = 0;
212 if (timeout == 0)
213 return ETIMEDOUT;
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)
219 abort ();
220 if (result == WAIT_TIMEOUT)
221 return ETIMEDOUT;
222 /* Another thread has just unlocked the mutex. We have good chances at
223 locking it now. */
226 mutex->owner = self;
228 if (++(mutex->depth) == 0) /* wraparound? */
230 mutex->depth--;
231 return EAGAIN;
234 return 0;
238 glwthread_timedrecmutex_unlock (glwthread_timedrecmutex_t *mutex)
240 if (mutex->owner != GetCurrentThreadId ())
241 return EPERM;
242 if (mutex->depth == 0)
243 return EINVAL;
244 if (--(mutex->depth) == 0)
246 mutex->owner = 0;
247 LeaveCriticalSection (&mutex->lock);
248 /* Notify one of the threads that were waiting with a timeout. */
249 /* SetEvent
250 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-setevent> */
251 SetEvent (mutex->event);
253 return 0;
257 glwthread_timedrecmutex_destroy (glwthread_timedrecmutex_t *mutex)
259 if (mutex->owner != 0)
260 return EBUSY;
261 DeleteCriticalSection (&mutex->lock);
262 /* CloseHandle
263 <https://docs.microsoft.com/en-us/windows/desktop/api/handleapi/nf-handleapi-closehandle> */
264 CloseHandle (mutex->event);
265 mutex->guard.done = 0;
266 return 0;