v / vlib / sync
Raw file | 217 loc (185 sloc) | 4.6 KB | Latest commit hash de136f6ba
1// Copyright (c) 2019-2023 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) int
15
16// TODO: The suggestion of using CriticalSection instead of mutex
17// was discussed. Needs consideration.
18
19// Mutex HANDLE
20type MHANDLE = voidptr
21
22// Semaphore HANDLE
23type SHANDLE = voidptr
24
25//[init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
26
27// `SRWLOCK` is much more performant that `Mutex` on Windows, so use that in both cases since we don't want to share with other processes
28[heap]
29pub struct Mutex {
30mut:
31 mx C.SRWLOCK // mutex handle
32}
33
34[heap]
35pub struct RwMutex {
36mut:
37 mx C.SRWLOCK // mutex handle
38}
39
40[heap]
41pub struct Semaphore {
42 mtx C.SRWLOCK
43 cond C.CONDITION_VARIABLE
44mut:
45 count u32
46}
47
48pub fn new_mutex() &Mutex {
49 mut m := &Mutex{}
50 m.init()
51 return m
52}
53
54pub fn new_rwmutex() &RwMutex {
55 mut m := &RwMutex{}
56 m.init()
57 return m
58}
59
60pub fn (mut m Mutex) init() {
61 C.InitializeSRWLock(&m.mx)
62}
63
64pub fn (mut m RwMutex) init() {
65 C.InitializeSRWLock(&m.mx)
66}
67
68pub fn (mut m Mutex) @lock() {
69 C.AcquireSRWLockExclusive(&m.mx)
70}
71
72pub fn (mut m Mutex) unlock() {
73 C.ReleaseSRWLockExclusive(&m.mx)
74}
75
76// RwMutex has separate read- and write locks
77pub fn (mut m RwMutex) @rlock() {
78 C.AcquireSRWLockShared(&m.mx)
79}
80
81pub fn (mut m RwMutex) @lock() {
82 C.AcquireSRWLockExclusive(&m.mx)
83}
84
85// Windows SRWLocks have different function to unlock
86// So provide two functions here, too, to have a common interface
87pub fn (mut m RwMutex) runlock() {
88 C.ReleaseSRWLockShared(&m.mx)
89}
90
91pub fn (mut m RwMutex) unlock() {
92 C.ReleaseSRWLockExclusive(&m.mx)
93}
94
95[inline]
96pub fn new_semaphore() &Semaphore {
97 return new_semaphore_init(0)
98}
99
100pub fn new_semaphore_init(n u32) &Semaphore {
101 mut sem := &Semaphore{}
102 sem.init(n)
103 return sem
104}
105
106pub fn (mut sem Semaphore) init(n u32) {
107 C.atomic_store_u32(&sem.count, n)
108 C.InitializeSRWLock(&sem.mtx)
109 C.InitializeConditionVariable(&sem.cond)
110}
111
112pub fn (mut sem Semaphore) post() {
113 mut c := C.atomic_load_u32(&sem.count)
114 for c > 1 {
115 if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c + 1) {
116 return
117 }
118 }
119 C.AcquireSRWLockExclusive(&sem.mtx)
120 c = C.atomic_fetch_add_u32(&sem.count, 1)
121 if c == 0 {
122 C.WakeConditionVariable(&sem.cond)
123 }
124 C.ReleaseSRWLockExclusive(&sem.mtx)
125}
126
127pub fn (mut sem Semaphore) wait() {
128 mut c := C.atomic_load_u32(&sem.count)
129 for c > 0 {
130 if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) {
131 return
132 }
133 }
134 C.AcquireSRWLockExclusive(&sem.mtx)
135 c = C.atomic_load_u32(&sem.count)
136
137 outer: for {
138 if c == 0 {
139 C.SleepConditionVariableSRW(&sem.cond, &sem.mtx, C.INFINITE, 0)
140 c = C.atomic_load_u32(&sem.count)
141 }
142 for c > 0 {
143 if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) {
144 if c > 1 {
145 C.WakeConditionVariable(&sem.cond)
146 }
147 break outer
148 }
149 }
150 }
151 C.ReleaseSRWLockExclusive(&sem.mtx)
152}
153
154pub fn (mut sem Semaphore) try_wait() bool {
155 mut c := C.atomic_load_u32(&sem.count)
156 for c > 0 {
157 if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) {
158 return true
159 }
160 }
161 return false
162}
163
164pub fn (mut sem Semaphore) timed_wait(timeout time.Duration) bool {
165 mut c := C.atomic_load_u32(&sem.count)
166 for c > 0 {
167 if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) {
168 return true
169 }
170 }
171 mut ft_start := C._FILETIME{}
172 C.GetSystemTimeAsFileTime(&ft_start)
173 time_end := ((u64(ft_start.dwHighDateTime) << 32) | ft_start.dwLowDateTime) +
174 u64(timeout / (100 * time.nanosecond))
175 mut t_ms := u32(timeout.sys_milliseconds())
176 C.AcquireSRWLockExclusive(&sem.mtx)
177 mut res := 0
178 c = C.atomic_load_u32(&sem.count)
179
180 outer: for {
181 if c == 0 {
182 res = C.SleepConditionVariableSRW(&sem.cond, &sem.mtx, t_ms, 0)
183 if res == 0 {
184 break outer
185 }
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 C.GetSystemTimeAsFileTime(&ft_start)
197 time_now := ((u64(ft_start.dwHighDateTime) << 32) | ft_start.dwLowDateTime) // in 100ns
198 if time_now > time_end {
199 break outer // timeout exceeded
200 }
201 t_ms = u32((time_end - time_now) / 10000)
202 }
203 C.ReleaseSRWLockExclusive(&sem.mtx)
204 return res != 0
205}
206
207pub fn (mut m RwMutex) destroy() {
208 // nothing to do
209}
210
211pub fn (mut m Mutex) destroy() {
212 // nothing to do
213}
214
215pub fn (s Semaphore) destroy() {
216 // nothing to do
217}