unistr/u{8,16,32}-uctomb: Avoid possible trouble with huge strings.
[gnulib.git] / lib / windows-timedmutex.c
blob788fe40fc31606df366c826d0c54d4c1b3ab9a50
1 /* Timed 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-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 InitializeCriticalSection (&mutex->lock);
44 mutex->guard.done = 1;
45 return 0;
48 int
49 glwthread_timedmutex_lock (glwthread_timedmutex_t *mutex)
51 if (!mutex->guard.done)
53 if (InterlockedIncrement (&mutex->guard.started) == 0)
55 /* This thread is the first one to need this mutex.
56 Initialize it. */
57 int err = glwthread_timedmutex_init (mutex);
58 if (err != 0)
60 /* Undo increment. */
61 InterlockedDecrement (&mutex->guard.started);
62 return err;
65 else
67 /* Don't let mutex->guard.started grow and wrap around. */
68 InterlockedDecrement (&mutex->guard.started);
69 /* Yield the CPU while waiting for another thread to finish
70 initializing this mutex. */
71 while (!mutex->guard.done)
72 Sleep (0);
75 EnterCriticalSection (&mutex->lock);
76 return 0;
79 int
80 glwthread_timedmutex_trylock (glwthread_timedmutex_t *mutex)
82 if (!mutex->guard.done)
84 if (InterlockedIncrement (&mutex->guard.started) == 0)
86 /* This thread is the first one to need this mutex.
87 Initialize it. */
88 int err = glwthread_timedmutex_init (mutex);
89 if (err != 0)
91 /* Undo increment. */
92 InterlockedDecrement (&mutex->guard.started);
93 return err;
96 else
98 /* Don't let mutex->guard.started grow and wrap around. */
99 InterlockedDecrement (&mutex->guard.started);
100 /* Let another thread finish initializing this mutex, and let it also
101 lock this mutex. */
102 return EBUSY;
105 if (!TryEnterCriticalSection (&mutex->lock))
106 return EBUSY;
107 return 0;
111 glwthread_timedmutex_timedlock (glwthread_timedmutex_t *mutex,
112 const struct timespec *abstime)
114 if (!mutex->guard.done)
116 if (InterlockedIncrement (&mutex->guard.started) == 0)
118 /* This thread is the first one to need this mutex.
119 Initialize it. */
120 int err = glwthread_timedmutex_init (mutex);
121 if (err != 0)
123 /* Undo increment. */
124 InterlockedDecrement (&mutex->guard.started);
125 return err;
128 else
130 /* Don't let mutex->guard.started grow and wrap around. */
131 InterlockedDecrement (&mutex->guard.started);
132 /* Yield the CPU while waiting for another thread to finish
133 initializing this mutex. */
134 while (!mutex->guard.done)
135 Sleep (0);
139 /* POSIX says:
140 "Under no circumstance shall the function fail with a timeout if
141 the mutex can be locked immediately. The validity of the abstime
142 parameter need not be checked if the mutex can be locked
143 immediately."
144 Therefore start the loop with a TryEnterCriticalSection call. */
145 for (;;)
147 if (TryEnterCriticalSection (&mutex->lock))
148 break;
151 struct timeval currtime;
152 DWORD timeout;
153 DWORD result;
155 gettimeofday (&currtime, NULL);
157 /* Wait until another thread signals the event or until the
158 abstime passes. */
159 if (currtime.tv_sec > abstime->tv_sec)
160 timeout = 0;
161 else
163 unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
164 timeout = seconds * 1000;
165 if (timeout / 1000 != seconds) /* overflow? */
166 timeout = INFINITE;
167 else
169 long milliseconds =
170 abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
171 if (milliseconds >= 0)
173 timeout += milliseconds;
174 if (timeout < milliseconds) /* overflow? */
175 timeout = INFINITE;
177 else
179 if (timeout >= - milliseconds)
180 timeout -= (- milliseconds);
181 else
182 timeout = 0;
186 if (timeout == 0)
187 return ETIMEDOUT;
189 /* WaitForSingleObject
190 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject> */
191 result = WaitForSingleObject (mutex->event, timeout);
192 if (result == WAIT_FAILED)
193 abort ();
194 if (result == WAIT_TIMEOUT)
195 return ETIMEDOUT;
196 /* Another thread has just unlocked the mutex. We have good chances at
197 locking it now. */
200 return 0;
204 glwthread_timedmutex_unlock (glwthread_timedmutex_t *mutex)
206 if (!mutex->guard.done)
207 return EINVAL;
208 LeaveCriticalSection (&mutex->lock);
209 /* Notify one of the threads that were waiting with a timeout. */
210 /* SetEvent
211 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-setevent> */
212 SetEvent (mutex->event);
213 return 0;
217 glwthread_timedmutex_destroy (glwthread_timedmutex_t *mutex)
219 if (!mutex->guard.done)
220 return EINVAL;
221 DeleteCriticalSection (&mutex->lock);
222 /* CloseHandle
223 <https://docs.microsoft.com/en-us/windows/desktop/api/handleapi/nf-handleapi-closehandle> */
224 CloseHandle (mutex->event);
225 mutex->guard.done = 0;
226 return 0;