threading: Support rwlock try_write_lock() on Windows
[strongswan.git] / src / libstrongswan / threading / windows / rwlock.c
1 /*
2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "thread.h"
17
18 #include <utils/debug.h>
19 #include <threading/rwlock.h>
20 #include <threading/rwlock_condvar.h>
21 #include <threading/thread_value.h>
22
23 typedef struct private_rwlock_t private_rwlock_t;
24 typedef struct private_rwlock_condvar_t private_rwlock_condvar_t;
25
26 /**
27 * private data of rwlock
28 */
29 struct private_rwlock_t {
30
31 /**
32 * public functions
33 */
34 rwlock_t public;
35
36 /**
37 * wrapped rwlock
38 */
39 SRWLOCK srw;
40
41 /**
42 * Thread specific shared lock count
43 */
44 thread_value_t *shared;
45 };
46
47 /**
48 * private data of condvar
49 */
50 struct private_rwlock_condvar_t {
51
52 /**
53 * public interface
54 */
55 rwlock_condvar_t public;
56
57 /**
58 * condition variable
59 */
60 CONDITION_VARIABLE cv;
61 };
62
63 METHOD(rwlock_t, read_lock, void,
64 private_rwlock_t *this)
65 {
66 uintptr_t count;
67
68 /* Recursive read locks are not supported. Use a thread specific
69 * recursiveness counter. */
70
71 count = (uintptr_t)this->shared->get(this->shared);
72 if (count == 0)
73 {
74 AcquireSRWLockShared(&this->srw);
75 }
76 this->shared->set(this->shared, (void*)(count + 1));
77 }
78
79 METHOD(rwlock_t, write_lock, void,
80 private_rwlock_t *this)
81 {
82 AcquireSRWLockExclusive(&this->srw);
83 }
84
85 METHOD(rwlock_t, try_write_lock, bool,
86 private_rwlock_t *this)
87 {
88 return TryAcquireSRWLockExclusive(&this->srw);
89 }
90
91 METHOD(rwlock_t, unlock, void,
92 private_rwlock_t *this)
93 {
94 uintptr_t count;
95
96 count = (uintptr_t)this->shared->get(this->shared);
97 switch (count)
98 {
99 case 0:
100 ReleaseSRWLockExclusive(&this->srw);
101 break;
102 case 1:
103 ReleaseSRWLockShared(&this->srw);
104 /* fall */
105 default:
106 this->shared->set(this->shared, (void*)(count - 1));
107 break;
108 }
109 }
110
111 METHOD(rwlock_t, destroy, void,
112 private_rwlock_t *this)
113 {
114 this->shared->destroy(this->shared);
115 free(this);
116 }
117
118 /*
119 * see header file
120 */
121 rwlock_t *rwlock_create(rwlock_type_t type)
122 {
123 private_rwlock_t *this;
124
125 INIT(this,
126 .public = {
127 .read_lock = _read_lock,
128 .write_lock = _write_lock,
129 .try_write_lock = _try_write_lock,
130 .unlock = _unlock,
131 .destroy = _destroy,
132 },
133 .shared = thread_value_create(NULL),
134 );
135
136 InitializeSRWLock(&this->srw);
137
138 return &this->public;
139 }
140
141 METHOD(rwlock_condvar_t, timed_wait, bool,
142 private_rwlock_condvar_t *this, rwlock_t *pubrwlock, u_int timeout)
143 {
144 private_rwlock_t *rwlock = (private_rwlock_t*)pubrwlock;
145 bool ret;
146
147 thread_set_active_condvar(&this->cv);
148
149 ret = SleepConditionVariableSRW(&this->cv, &rwlock->srw, timeout, 0);
150
151 thread_set_active_condvar(NULL);
152
153 return ret == 0;
154 }
155
156 METHOD(rwlock_condvar_t, wait_, void,
157 private_rwlock_condvar_t *this, rwlock_t *lock)
158 {
159 timed_wait(this, lock, INFINITE);
160 }
161
162 METHOD(rwlock_condvar_t, timed_wait_abs, bool,
163 private_rwlock_condvar_t *this, rwlock_t *lock, timeval_t tv)
164 {
165 DWORD timeout;
166 timeval_t now, diff;
167
168 time_monotonic(&now);
169 if (timercmp(&now, &tv, >))
170 {
171 return TRUE;
172 }
173 timersub(&tv, &now, &diff);
174 timeout = diff.tv_sec * 1000 + diff.tv_usec / 1000;
175
176 return timed_wait(this, lock, timeout);
177 }
178
179 METHOD(rwlock_condvar_t, signal_, void,
180 private_rwlock_condvar_t *this)
181 {
182 WakeConditionVariable(&this->cv);
183 }
184
185 METHOD(rwlock_condvar_t, broadcast, void,
186 private_rwlock_condvar_t *this)
187 {
188 WakeAllConditionVariable(&this->cv);
189 }
190
191 METHOD(rwlock_condvar_t, condvar_destroy, void,
192 private_rwlock_condvar_t *this)
193 {
194 free(this);
195 }
196
197 /*
198 * see header file
199 */
200 rwlock_condvar_t *rwlock_condvar_create()
201 {
202 private_rwlock_condvar_t *this;
203
204 INIT(this,
205 .public = {
206 .wait = _wait_,
207 .timed_wait = _timed_wait,
208 .timed_wait_abs = _timed_wait_abs,
209 .signal = _signal_,
210 .broadcast = _broadcast,
211 .destroy = _condvar_destroy,
212 },
213 );
214
215 InitializeConditionVariable(&this->cv);
216
217 return &this->public;
218 }