v / vlib / sync / sync_windows.c.v
263 lines · 226 sloc · 6.53 KB · a87a4d73b9ab25cfff0822f4e94cf2a2d9e64323
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module sync
5
6import time
7
8#include <synchapi.h>
9#include <time.h>
10
11fn C.GetSystemTimeAsFileTime(lpSystemTimeAsFileTime &C._FILETIME)
12fn C.InitializeConditionVariable(voidptr)
13fn C.WakeConditionVariable(voidptr)
14fn C.SleepConditionVariableSRW(voidptr, voidptr, u32, u32) i32
15
16fn C.TryAcquireSRWLockExclusive(h voidptr) i32
17fn C.TryAcquireSRWLockShared(h voidptr) i32
18
19// TODO: The suggestion of using CriticalSection instead of mutex
20// was discussed. Needs consideration.
21
22// Mutex HANDLE
23type MHANDLE = voidptr
24
25// Semaphore HANDLE
26type SHANDLE = voidptr
27
28@[typedef]
29pub struct C.CONDITION_VARIABLE {}
30
31//[init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
32
33// `SRWLOCK` is much more performant that `Mutex` on Windows, so use that in both cases since we don't
34// want to share with other processes
35@[heap]
36pub struct Mutex {
37mut:
38 mx C.SRWLOCK // mutex handle
39}
40
41@[heap]
42pub struct RwMutex {
43mut:
44 mx C.SRWLOCK // mutex handle
45}
46
47@[heap]
48pub struct Semaphore {
49 mtx C.SRWLOCK
50 cond C.CONDITION_VARIABLE
51mut:
52 count u32
53}
54
55pub fn new_mutex() &Mutex {
56 mut m := &Mutex{}
57 m.init()
58 return m
59}
60
61pub fn new_rwmutex() &RwMutex {
62 mut m := &RwMutex{}
63 m.init()
64 return m
65}
66
67pub fn (mut m Mutex) init() {
68 C.InitializeSRWLock(&m.mx)
69}
70
71pub fn (mut m RwMutex) init() {
72 C.InitializeSRWLock(&m.mx)
73}
74
75pub fn (mut m Mutex) lock() {
76 C.AcquireSRWLockExclusive(&m.mx)
77}
78
79// try_lock try to lock the mutex instance and return immediately.
80// If the mutex was already locked, it will return false.
81// NOTE: try_lock require Windows 7 or later. Before Windows 7, it will always return false.
82// NOTE: To enable try_lock , you should compile your project with `-d windows_7`, like `v . -d windows_7`
83// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-tryacquiresrwlockexclusive
84pub fn (mut m Mutex) try_lock() bool {
85 $if windows_7 ? {
86 return C.TryAcquireSRWLockExclusive(&m.mx) != 0
87 } $else {
88 return false
89 }
90}
91
92pub fn (mut m Mutex) unlock() {
93 C.ReleaseSRWLockExclusive(&m.mx)
94}
95
96// RwMutex has separate read- and write locks
97pub fn (mut m RwMutex) rlock() {
98 C.AcquireSRWLockShared(&m.mx)
99}
100
101pub fn (mut m RwMutex) lock() {
102 C.AcquireSRWLockExclusive(&m.mx)
103}
104
105// try_rlock try to lock the given RwMutex instance for reading and return immediately.
106// If the mutex was already locked, it will return false.
107// NOTE: try_rlock require Windows 7 or later. Before Windows 7, it will always return false.
108// NOTE: To enable try_rlock , you should compile your project with `-d windows_7`, like `v . -d windows_7`
109// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-tryacquiresrwlockshared
110pub fn (mut m RwMutex) try_rlock() bool {
111 $if windows_7 ? {
112 return C.TryAcquireSRWLockShared(&m.mx) != 0
113 } $else {
114 return false
115 }
116}
117
118// try_wlock try to lock the given RwMutex instance for writing and return immediately.
119// If the mutex was already locked, it will return false.
120// NOTE: try_wlock require Windows 7 or later. Before Windows 7, it will always return false.
121// NOTE: To enable try_wlock , you should compile your project with `-d windows_7`, like `v . -d windows_7`
122// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-tryacquiresrwlockexclusive
123pub fn (mut m RwMutex) try_wlock() bool {
124 $if windows_7 ? {
125 return C.TryAcquireSRWLockExclusive(&m.mx) != 0
126 } $else {
127 return false
128 }
129}
130
131// Windows SRWLocks have different function to unlock
132// So provide two functions here, too, to have a common interface
133pub fn (mut m RwMutex) runlock() {
134 C.ReleaseSRWLockShared(&m.mx)
135}
136
137pub fn (mut m RwMutex) unlock() {
138 C.ReleaseSRWLockExclusive(&m.mx)
139}
140
141@[inline]
142pub fn new_semaphore() &Semaphore {
143 return new_semaphore_init(0)
144}
145
146pub fn new_semaphore_init(n u32) &Semaphore {
147 mut sem := &Semaphore{}
148 sem.init(n)
149 return sem
150}
151
152pub fn (mut sem Semaphore) init(n u32) {
153 C.atomic_store_u32(&sem.count, n)
154 C.InitializeSRWLock(&sem.mtx)
155 C.InitializeConditionVariable(&sem.cond)
156}
157
158pub fn (mut sem Semaphore) post() {
159 mut c := C.atomic_load_u32(&sem.count)
160 for c > 1 {
161 if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c + 1) {
162 return
163 }
164 }
165 C.AcquireSRWLockExclusive(&sem.mtx)
166 c = C.atomic_fetch_add_u32(voidptr(&sem.count), 1)
167 if c == 0 {
168 C.WakeConditionVariable(&sem.cond)
169 }
170 C.ReleaseSRWLockExclusive(&sem.mtx)
171}
172
173pub fn (mut sem Semaphore) wait() {
174 mut c := C.atomic_load_u32(&sem.count)
175 for c > 0 {
176 if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) {
177 return
178 }
179 }
180 C.AcquireSRWLockExclusive(&sem.mtx)
181 c = C.atomic_load_u32(&sem.count)
182
183 outer: for {
184 if c == 0 {
185 C.SleepConditionVariableSRW(&sem.cond, &sem.mtx, C.INFINITE, 0)
186 c = C.atomic_load_u32(&sem.count)
187 }
188 for c > 0 {
189 if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) {
190 if c > 1 {
191 C.WakeConditionVariable(&sem.cond)
192 }
193 break outer
194 }
195 }
196 }
197 C.ReleaseSRWLockExclusive(&sem.mtx)
198}
199
200pub fn (mut sem Semaphore) try_wait() bool {
201 mut c := C.atomic_load_u32(&sem.count)
202 for c > 0 {
203 if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) {
204 return true
205 }
206 }
207 return false
208}
209
210pub fn (mut sem Semaphore) timed_wait(timeout time.Duration) bool {
211 mut c := C.atomic_load_u32(&sem.count)
212 for c > 0 {
213 if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) {
214 return true
215 }
216 }
217 mut ft_start := C._FILETIME{}
218 C.GetSystemTimeAsFileTime(&ft_start)
219 time_end := ((u64(ft_start.dwHighDateTime) << 32) | ft_start.dwLowDateTime) +
220 u64(timeout / (100 * time.nanosecond))
221 mut t_ms := u32(timeout.sys_milliseconds())
222 C.AcquireSRWLockExclusive(&sem.mtx)
223 mut res := 0
224 c = C.atomic_load_u32(&sem.count)
225
226 outer: for {
227 if c == 0 {
228 res = C.SleepConditionVariableSRW(&sem.cond, &sem.mtx, t_ms, 0)
229 if res == 0 {
230 break outer
231 }
232 c = C.atomic_load_u32(&sem.count)
233 }
234 for c > 0 {
235 if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) {
236 if c > 1 {
237 C.WakeConditionVariable(&sem.cond)
238 }
239 break outer
240 }
241 }
242 C.GetSystemTimeAsFileTime(&ft_start)
243 time_now := ((u64(ft_start.dwHighDateTime) << 32) | ft_start.dwLowDateTime) // in 100ns
244 if time_now > time_end {
245 break outer // timeout exceeded
246 }
247 t_ms = u32((time_end - time_now) / 10000)
248 }
249 C.ReleaseSRWLockExclusive(&sem.mtx)
250 return res != 0
251}
252
253pub fn (mut m RwMutex) destroy() {
254 // nothing to do
255}
256
257pub fn (mut m Mutex) destroy() {
258 // nothing to do
259}
260
261pub fn (s Semaphore) destroy() {
262 // nothing to do
263}
264