1 /* Plain recursive mutexes (native Windows implementation).
2    Copyright (C) 2005-2020 Free Software Foundation, Inc.
3 
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 3, or (at your option)
7    any later version.
8 
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.
13 
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/>.  */
16 
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005.
18    Based on GCC's gthr-win32.h.  */
19 
20 #include <config.h>
21 
22 /* Specification.  */
23 #include "windows-recmutex.h"
24 
25 #include <errno.h>
26 
27 void
glwthread_recmutex_init(glwthread_recmutex_t * mutex)28 glwthread_recmutex_init (glwthread_recmutex_t *mutex)
29 {
30   mutex->owner = 0;
31   mutex->depth = 0;
32   InitializeCriticalSection (&mutex->lock);
33   mutex->guard.done = 1;
34 }
35 
36 int
glwthread_recmutex_lock(glwthread_recmutex_t * mutex)37 glwthread_recmutex_lock (glwthread_recmutex_t *mutex)
38 {
39   if (!mutex->guard.done)
40     {
41       if (InterlockedIncrement (&mutex->guard.started) == 0)
42         /* This thread is the first one to need this mutex.  Initialize it.  */
43         glwthread_recmutex_init (mutex);
44       else
45         {
46           /* Don't let mutex->guard.started grow and wrap around.  */
47           InterlockedDecrement (&mutex->guard.started);
48           /* Yield the CPU while waiting for another thread to finish
49              initializing this mutex.  */
50           while (!mutex->guard.done)
51             Sleep (0);
52         }
53     }
54   {
55     DWORD self = GetCurrentThreadId ();
56     if (mutex->owner != self)
57       {
58         EnterCriticalSection (&mutex->lock);
59         mutex->owner = self;
60       }
61     if (++(mutex->depth) == 0) /* wraparound? */
62       {
63         mutex->depth--;
64         return EAGAIN;
65       }
66   }
67   return 0;
68 }
69 
70 int
glwthread_recmutex_trylock(glwthread_recmutex_t * mutex)71 glwthread_recmutex_trylock (glwthread_recmutex_t *mutex)
72 {
73   if (!mutex->guard.done)
74     {
75       if (InterlockedIncrement (&mutex->guard.started) == 0)
76         /* This thread is the first one to need this mutex.  Initialize it.  */
77         glwthread_recmutex_init (mutex);
78       else
79         {
80           /* Don't let mutex->guard.started grow and wrap around.  */
81           InterlockedDecrement (&mutex->guard.started);
82           /* Let another thread finish initializing this mutex, and let it also
83              lock this mutex.  */
84           return EBUSY;
85         }
86     }
87   {
88     DWORD self = GetCurrentThreadId ();
89     if (mutex->owner != self)
90       {
91         if (!TryEnterCriticalSection (&mutex->lock))
92           return EBUSY;
93         mutex->owner = self;
94       }
95     if (++(mutex->depth) == 0) /* wraparound? */
96       {
97         mutex->depth--;
98         return EAGAIN;
99       }
100   }
101   return 0;
102 }
103 
104 int
glwthread_recmutex_unlock(glwthread_recmutex_t * mutex)105 glwthread_recmutex_unlock (glwthread_recmutex_t *mutex)
106 {
107   if (mutex->owner != GetCurrentThreadId ())
108     return EPERM;
109   if (mutex->depth == 0)
110     return EINVAL;
111   if (--(mutex->depth) == 0)
112     {
113       mutex->owner = 0;
114       LeaveCriticalSection (&mutex->lock);
115     }
116   return 0;
117 }
118 
119 int
glwthread_recmutex_destroy(glwthread_recmutex_t * mutex)120 glwthread_recmutex_destroy (glwthread_recmutex_t *mutex)
121 {
122   if (mutex->owner != 0)
123     return EBUSY;
124   DeleteCriticalSection (&mutex->lock);
125   mutex->guard.done = 0;
126   return 0;
127 }
128