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. |
4 | module sync |
5 | |
6 | import time |
7 | |
8 | #include <synchapi.h> |
9 | #include <time.h> |
10 | |
11 | fn C.GetSystemTimeAsFileTime(lpSystemTimeAsFileTime &C._FILETIME) |
12 | fn C.InitializeConditionVariable(voidptr) |
13 | fn C.WakeConditionVariable(voidptr) |
14 | fn 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 |
20 | type MHANDLE = voidptr |
21 | |
22 | // Semaphore HANDLE |
23 | type 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] |
29 | pub struct Mutex { |
30 | mut: |
31 | mx C.SRWLOCK // mutex handle |
32 | } |
33 | |
34 | [heap] |
35 | pub struct RwMutex { |
36 | mut: |
37 | mx C.SRWLOCK // mutex handle |
38 | } |
39 | |
40 | [heap] |
41 | pub struct Semaphore { |
42 | mtx C.SRWLOCK |
43 | cond C.CONDITION_VARIABLE |
44 | mut: |
45 | count u32 |
46 | } |
47 | |
48 | pub fn new_mutex() &Mutex { |
49 | mut m := &Mutex{} |
50 | m.init() |
51 | return m |
52 | } |
53 | |
54 | pub fn new_rwmutex() &RwMutex { |
55 | mut m := &RwMutex{} |
56 | m.init() |
57 | return m |
58 | } |
59 | |
60 | pub fn (mut m Mutex) init() { |
61 | C.InitializeSRWLock(&m.mx) |
62 | } |
63 | |
64 | pub fn (mut m RwMutex) init() { |
65 | C.InitializeSRWLock(&m.mx) |
66 | } |
67 | |
68 | pub fn (mut m Mutex) @lock() { |
69 | C.AcquireSRWLockExclusive(&m.mx) |
70 | } |
71 | |
72 | pub fn (mut m Mutex) unlock() { |
73 | C.ReleaseSRWLockExclusive(&m.mx) |
74 | } |
75 | |
76 | // RwMutex has separate read- and write locks |
77 | pub fn (mut m RwMutex) @rlock() { |
78 | C.AcquireSRWLockShared(&m.mx) |
79 | } |
80 | |
81 | pub 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 |
87 | pub fn (mut m RwMutex) runlock() { |
88 | C.ReleaseSRWLockShared(&m.mx) |
89 | } |
90 | |
91 | pub fn (mut m RwMutex) unlock() { |
92 | C.ReleaseSRWLockExclusive(&m.mx) |
93 | } |
94 | |
95 | [inline] |
96 | pub fn new_semaphore() &Semaphore { |
97 | return new_semaphore_init(0) |
98 | } |
99 | |
100 | pub fn new_semaphore_init(n u32) &Semaphore { |
101 | mut sem := &Semaphore{} |
102 | sem.init(n) |
103 | return sem |
104 | } |
105 | |
106 | pub 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 | |
112 | pub 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 | |
127 | pub 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 | |
154 | pub 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 | |
164 | pub 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 | |
207 | pub fn (mut m RwMutex) destroy() { |
208 | // nothing to do |
209 | } |
210 | |
211 | pub fn (mut m Mutex) destroy() { |
212 | // nothing to do |
213 | } |
214 | |
215 | pub fn (s Semaphore) destroy() { |
216 | // nothing to do |
217 | } |