mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-28 18:30:06 +00:00
Begin migration from sys/win32 to sys/windows
This commit is contained in:
+90
-101
@@ -2,6 +2,7 @@ package sync
|
||||
|
||||
import "core:mem"
|
||||
import "core:time"
|
||||
import "core:fmt"
|
||||
import "core:math/rand"
|
||||
|
||||
_, _ :: time, rand;
|
||||
@@ -17,13 +18,16 @@ _Channel_Internal :: struct(T: typeid) {
|
||||
|
||||
unbuffered_msg: T, // Will be used as the backing to the queue if no `cap` is given
|
||||
|
||||
mutex: Mutex,
|
||||
r_cond: Condition,
|
||||
w_cond: Condition,
|
||||
mutex: Mutex,
|
||||
r_mutex: Mutex,
|
||||
w_mutex: Mutex,
|
||||
r_cond: Condition,
|
||||
w_cond: Condition,
|
||||
|
||||
closed: bool,
|
||||
r_waiting: int,
|
||||
w_waiting: int,
|
||||
is_buffered: bool,
|
||||
is_closed: bool,
|
||||
r_waiting: int,
|
||||
w_waiting: int,
|
||||
}
|
||||
|
||||
channel_init :: proc(c: ^$C/Channel($T), cap: int = 0, allocator := context.allocator) {
|
||||
@@ -38,16 +42,20 @@ channel_make :: proc($T: typeid, cap: int = 0, allocator := context.allocator) -
|
||||
ch.allocator = allocator;
|
||||
|
||||
mutex_init(&ch.mutex);
|
||||
mutex_init(&ch.r_mutex);
|
||||
mutex_init(&ch.w_mutex);
|
||||
condition_init(&ch.r_cond, &ch.mutex);
|
||||
condition_init(&ch.w_cond, &ch.mutex);
|
||||
ch.closed = false;
|
||||
ch.is_closed = false;
|
||||
ch.r_waiting = 0;
|
||||
ch.w_waiting = 0;
|
||||
ch.unbuffered_msg = T{};
|
||||
|
||||
if cap > 0 {
|
||||
ch.is_buffered = true;
|
||||
ch.queue = make([dynamic]T, 0, cap, ch.allocator);
|
||||
} else {
|
||||
ch.is_buffered = false;
|
||||
d := mem.Raw_Dynamic_Array{
|
||||
data = &ch.unbuffered_msg,
|
||||
len = 0,
|
||||
@@ -67,6 +75,8 @@ channel_destroy :: proc(ch: $C/Channel($T)) {
|
||||
}
|
||||
|
||||
mutex_destroy(&ch.mutex);
|
||||
mutex_destroy(&ch.r_mutex);
|
||||
mutex_destroy(&ch.w_mutex);
|
||||
condition_destroy(&ch.r_cond);
|
||||
condition_destroy(&ch.w_cond);
|
||||
free(ch.internal, ch.allocator);
|
||||
@@ -75,8 +85,8 @@ channel_destroy :: proc(ch: $C/Channel($T)) {
|
||||
channel_close :: proc(ch: $C/Channel($T)) -> (ok: bool) {
|
||||
mutex_lock(&ch.mutex);
|
||||
|
||||
if !ch.closed {
|
||||
ch.closed = true;
|
||||
if !ch.is_closed {
|
||||
ch.is_closed = true;
|
||||
condition_broadcast(&ch.r_cond);
|
||||
condition_broadcast(&ch.w_cond);
|
||||
ok = true;
|
||||
@@ -89,25 +99,45 @@ channel_close :: proc(ch: $C/Channel($T)) -> (ok: bool) {
|
||||
channel_write :: proc(ch: $C/Channel($T), msg: T) -> (ok: bool) {
|
||||
mutex_lock(&ch.mutex);
|
||||
defer mutex_unlock(&ch.mutex);
|
||||
// fmt.println("channel_write");
|
||||
// defer fmt.println("channel_write done");
|
||||
|
||||
if ch.closed {
|
||||
if ch.is_closed {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for len(ch.queue) == cap(ch.queue) {
|
||||
for !channel_can_write(ch) {
|
||||
ch.w_waiting += 1;
|
||||
condition_wait_for(&ch.w_cond);
|
||||
ch.w_waiting -= 1;
|
||||
}
|
||||
|
||||
if len(ch.queue) < cap(ch.queue) {
|
||||
if ch.is_buffered {
|
||||
if len(ch.queue) < cap(ch.queue) {
|
||||
append(&ch.queue, msg);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if ch.r_waiting > 0 {
|
||||
condition_signal(&ch.r_cond);
|
||||
}
|
||||
} else {
|
||||
for len(ch.queue) == cap(ch.queue) {
|
||||
ch.w_waiting += 1;
|
||||
condition_wait_for(&ch.w_cond);
|
||||
ch.w_waiting -= 1;
|
||||
}
|
||||
assert(len(ch.queue) < cap(ch.queue));
|
||||
append(&ch.queue, msg);
|
||||
ok = true;
|
||||
}
|
||||
assert(ch.w_waiting >= 0);
|
||||
ch.w_waiting += 1;
|
||||
|
||||
if ch.r_waiting > 0 {
|
||||
condition_signal(&ch.r_cond);
|
||||
if ch.r_waiting > 0 {
|
||||
condition_signal(&ch.r_cond);
|
||||
}
|
||||
|
||||
condition_wait_for(&ch.w_cond);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -116,27 +146,41 @@ channel_write :: proc(ch: $C/Channel($T), msg: T) -> (ok: bool) {
|
||||
channel_read :: proc(ch: $C/Channel($T)) -> (msg: T, ok: bool) #optional_ok {
|
||||
mutex_lock(&ch.mutex);
|
||||
defer mutex_unlock(&ch.mutex);
|
||||
// fmt.println("channel_read");
|
||||
// defer fmt.println("channel_read done");
|
||||
|
||||
for len(ch.queue) == 0 {
|
||||
if ch.closed {
|
||||
return;
|
||||
}
|
||||
|
||||
if ch.is_closed {
|
||||
return;
|
||||
}
|
||||
for !channel_can_read(ch) {
|
||||
ch.r_waiting += 1;
|
||||
condition_wait_for(&ch.r_cond);
|
||||
ch.r_waiting -= 1;
|
||||
}
|
||||
if ch.is_closed {
|
||||
return;
|
||||
}
|
||||
|
||||
msg, ok = pop_front(&ch.queue);
|
||||
if ch.is_buffered {
|
||||
assert(len(ch.queue) > 0);
|
||||
msg, ok = pop_front_safe(&ch.queue);
|
||||
|
||||
if ch.w_waiting > 0 {
|
||||
if ch.w_waiting > 0 {
|
||||
condition_signal(&ch.w_cond);
|
||||
}
|
||||
} else {
|
||||
assert(ch.w_waiting > 0);
|
||||
assert(len(ch.queue) > 0);
|
||||
msg, ok = pop_front_safe(&ch.queue);
|
||||
|
||||
ch.w_waiting -= 1;
|
||||
condition_signal(&ch.w_cond);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
channel_size :: proc(ch: $C/Channel($T)) -> (size: int) {
|
||||
channel_len :: proc(ch: $C/Channel($T)) -> (size: int) {
|
||||
if channel_is_buffered(ch) {
|
||||
mutex_lock(&ch.mutex);
|
||||
size = len(ch.queue);
|
||||
@@ -147,111 +191,56 @@ channel_size :: proc(ch: $C/Channel($T)) -> (size: int) {
|
||||
|
||||
channel_is_closed :: proc(ch: $C/Channel($T)) -> bool {
|
||||
mutex_lock(&ch.mutex);
|
||||
closed := ch.closed;
|
||||
closed := ch.is_closed;
|
||||
mutex_unlock(&ch.mutex);
|
||||
return closed;
|
||||
}
|
||||
|
||||
channel_is_buffered :: proc(ch: $C/Channel($T)) -> bool {
|
||||
q := transmute(mem.Raw_Dynamic_Array)ch.queue;
|
||||
return q.cap != 0 && (q.data != &ch.unbuffered_msg);
|
||||
return ch.is_buffered;
|
||||
}
|
||||
|
||||
channel_can_write :: proc(ch: $C/Channel($T)) -> bool {
|
||||
mutex_lock(&ch.mutex);
|
||||
defer mutex_unlock(&ch.mutex);
|
||||
return len(ch.queue) < cap(ch.queue);
|
||||
if ch.is_closed {
|
||||
return false;
|
||||
}
|
||||
if ch.is_buffered {
|
||||
return len(ch.queue) < cap(ch.queue);
|
||||
}
|
||||
return ch.r_waiting > 0;
|
||||
}
|
||||
|
||||
channel_can_read :: proc(ch: $C/Channel($T)) -> bool {
|
||||
mutex_lock(&ch.mutex);
|
||||
defer mutex_unlock(&ch.mutex);
|
||||
return len(ch.queue) > 0;
|
||||
if ch.is_buffered {
|
||||
return len(ch.queue) > 0;
|
||||
}
|
||||
return ch.w_waiting > 0;
|
||||
}
|
||||
|
||||
channel_can_read_write :: proc(ch: $C/Channel($T)) -> bool {
|
||||
mutex_lock(&ch.mutex);
|
||||
defer mutex_unlock(&ch.mutex);
|
||||
return 0 < len(ch.queue) && len(ch.queue) < cap(ch.queue);
|
||||
if ch.is_buffered {
|
||||
return 0 < len(ch.queue) && len(ch.queue) < cap(ch.queue);
|
||||
}
|
||||
return ch.r_waiting > 0 && ch.w_waiting > 0;
|
||||
}
|
||||
|
||||
channel_iterator :: proc(ch: $C/Channel($T)) -> (elem: T, ok: bool) {
|
||||
mutex_lock(&ch.mutex);
|
||||
defer mutex_unlock(&ch.mutex);
|
||||
|
||||
if len(ch.queue) > 0 {
|
||||
if ch.is_buffered {
|
||||
if len(ch.queue) > 0 {
|
||||
return channel_read(ch);
|
||||
}
|
||||
} else if ch.w_waiting > 0 {
|
||||
return channel_read(ch);
|
||||
}
|
||||
|
||||
return T{}, false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
channel_select :: proc(readers, writers: []$C/Channel($T), write_msgs: []T) -> (read_msg: T, index: int) {
|
||||
Candidate :: struct {
|
||||
ch: C,
|
||||
msg: T,
|
||||
index: int,
|
||||
read: bool,
|
||||
};
|
||||
|
||||
count := 0;
|
||||
candidates := make([]Candidate, len(readers) + len(writers));
|
||||
defer delete(candidates);
|
||||
|
||||
for c, i in readers {
|
||||
if channel_can_read(c) {
|
||||
candidates[count] = {
|
||||
ch = c,
|
||||
index = i,
|
||||
read = true,
|
||||
};
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for c, i in writers {
|
||||
if channel_can_write(c) {
|
||||
candidates[count] = {
|
||||
ch = c,
|
||||
index = count,
|
||||
read = false,
|
||||
msg = write_msgs[i],
|
||||
};
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return T{}, -1;
|
||||
}
|
||||
|
||||
// Randomize the input
|
||||
r := rand.create(time.read_cycle_counter());
|
||||
s := candidates[rand.int_max(count, &r)];
|
||||
if s.read {
|
||||
ok: bool;
|
||||
if read_msg, ok = channel_read(s.ch); !ok {
|
||||
index = -1;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if !channel_write(s.ch, s.msg) {
|
||||
index = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
index = s.index;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
channel_select_write :: proc(writers: []$C/Channel($T), write_msgs: []T) -> (read_msg: T, index: int) {
|
||||
return channel_select([]C{}, writers, msg);
|
||||
}
|
||||
channel_select_read :: proc(readers: []$C/Channel($T)) -> (index: int) {
|
||||
_, index = channel_select(readers, []C{}, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
+47
-33
@@ -1,20 +1,23 @@
|
||||
// +build windows
|
||||
package sync
|
||||
|
||||
import "core:sys/win32"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
foreign import kernel32 "system:kernel32.lib"
|
||||
|
||||
// A lock that can only be held by one thread at once.
|
||||
Mutex :: struct {
|
||||
_critical_section: win32.Critical_Section,
|
||||
_handle: win32.SRWLOCK,
|
||||
|
||||
}
|
||||
|
||||
Recursive_Mutex :: struct {
|
||||
_handle: win32.CRITICAL_SECTION,
|
||||
}
|
||||
|
||||
|
||||
// Blocks until signalled.
|
||||
// When signalled, awakens exactly one waiting thread.
|
||||
Condition :: struct {
|
||||
_handle: WIN32_CONDITION_VARIABLE,
|
||||
_handle: win32.CONDITION_VARIABLE,
|
||||
|
||||
mutex: ^Mutex,
|
||||
}
|
||||
@@ -22,87 +25,98 @@ Condition :: struct {
|
||||
// When waited upon, blocks until the internal count is greater than zero, then subtracts one.
|
||||
// Posting to the semaphore increases the count by one, or the provided amount.
|
||||
Semaphore :: struct {
|
||||
_handle: win32.Handle,
|
||||
_handle: win32.HANDLE,
|
||||
}
|
||||
|
||||
|
||||
semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
|
||||
s._handle = win32.create_semaphore_w(nil, i32(initial_count), 1<<31-1, nil);
|
||||
s._handle = win32.CreateSemaphoreW(nil, win32.LONG(initial_count), 1<<31-1, nil);
|
||||
}
|
||||
|
||||
semaphore_destroy :: proc(s: ^Semaphore) {
|
||||
win32.close_handle(s._handle);
|
||||
win32.CloseHandle(s._handle);
|
||||
}
|
||||
|
||||
semaphore_post :: proc(s: ^Semaphore, count := 1) {
|
||||
win32.release_semaphore(s._handle, i32(count), nil);
|
||||
win32.ReleaseSemaphore(s._handle, win32.LONG(count), nil);
|
||||
}
|
||||
|
||||
semaphore_wait_for :: proc(s: ^Semaphore) {
|
||||
// NOTE(tetra, 2019-10-30): wait_for_single_object decrements the count before it returns.
|
||||
result := win32.wait_for_single_object(s._handle, win32.INFINITE);
|
||||
// NOTE(tetra, 2019-10-30): WaitForSingleObject decrements the count before it returns.
|
||||
result := win32.WaitForSingleObject(s._handle, win32.INFINITE);
|
||||
assert(result != win32.WAIT_FAILED);
|
||||
}
|
||||
|
||||
|
||||
mutex_init :: proc(m: ^Mutex, spin_count := 0) {
|
||||
win32.initialize_critical_section_and_spin_count(&m._critical_section, u32(spin_count));
|
||||
win32.InitializeSRWLock(&m._handle);
|
||||
}
|
||||
|
||||
mutex_destroy :: proc(m: ^Mutex) {
|
||||
win32.delete_critical_section(&m._critical_section);
|
||||
win32.ReleaseSRWLockExclusive(&m._handle);
|
||||
}
|
||||
|
||||
mutex_lock :: proc(m: ^Mutex) {
|
||||
win32.enter_critical_section(&m._critical_section);
|
||||
win32.AcquireSRWLockExclusive(&m._handle);
|
||||
}
|
||||
|
||||
mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
return bool(win32.try_enter_critical_section(&m._critical_section));
|
||||
return bool(win32.TryAcquireSRWLockExclusive(&m._handle));
|
||||
}
|
||||
|
||||
mutex_unlock :: proc(m: ^Mutex) {
|
||||
win32.leave_critical_section(&m._critical_section);
|
||||
win32.ReleaseSRWLockExclusive(&m._handle);
|
||||
}
|
||||
|
||||
@private WIN32_CONDITION_VARIABLE :: distinct rawptr;
|
||||
@private
|
||||
foreign kernel32 {
|
||||
InitializeConditionVariable :: proc(ConditionVariable: ^WIN32_CONDITION_VARIABLE) ---
|
||||
WakeConditionVariable :: proc(ConditionVariable: ^WIN32_CONDITION_VARIABLE) ---
|
||||
WakeAllConditionVariable :: proc(ConditionVariable: ^WIN32_CONDITION_VARIABLE) ---
|
||||
SleepConditionVariableCS :: proc(ConditionVariable: ^WIN32_CONDITION_VARIABLE, CriticalSection: ^win32.Critical_Section, dwMilliseconds: u32) -> b32 ---
|
||||
|
||||
recursive_mutex_init :: proc(m: ^Recursive_Mutex, spin_count := 0) {
|
||||
win32.InitializeCriticalSectionAndSpinCount(&m._handle, u32(spin_count));
|
||||
}
|
||||
|
||||
recursive_mutex_destroy :: proc(m: ^Recursive_Mutex) {
|
||||
win32.DeleteCriticalSection(&m._handle);
|
||||
}
|
||||
|
||||
recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
|
||||
win32.EnterCriticalSection(&m._handle);
|
||||
}
|
||||
|
||||
recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
|
||||
return bool(win32.TryEnterCriticalSection(&m._handle));
|
||||
}
|
||||
|
||||
recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
|
||||
win32.LeaveCriticalSection(&m._handle);
|
||||
}
|
||||
|
||||
condition_init :: proc(c: ^Condition, mutex: ^Mutex) -> bool {
|
||||
assert(mutex != nil);
|
||||
InitializeConditionVariable(&c._handle);
|
||||
win32.InitializeConditionVariable(&c._handle);
|
||||
c.mutex = mutex;
|
||||
return c._handle != nil;
|
||||
return true;
|
||||
}
|
||||
|
||||
condition_destroy :: proc(c: ^Condition) {
|
||||
if c._handle != nil {
|
||||
WakeAllConditionVariable(&c._handle);
|
||||
}
|
||||
// Does nothing
|
||||
}
|
||||
|
||||
condition_signal :: proc(c: ^Condition) -> bool {
|
||||
if c._handle == nil {
|
||||
if c._handle.ptr == nil {
|
||||
return false;
|
||||
}
|
||||
WakeConditionVariable(&c._handle);
|
||||
win32.WakeConditionVariable(&c._handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
condition_broadcast :: proc(c: ^Condition) -> bool {
|
||||
if c._handle == nil {
|
||||
if c._handle.ptr == nil {
|
||||
return false;
|
||||
}
|
||||
WakeAllConditionVariable(&c._handle);
|
||||
win32.WakeAllConditionVariable(&c._handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
condition_wait_for :: proc(c: ^Condition) -> bool {
|
||||
return cast(bool)SleepConditionVariableCS(&c._handle, &c.mutex._critical_section, win32.INFINITE);
|
||||
res := win32.SleepConditionVariableSRW(&c._handle, &c.mutex._handle, win32.INFINITE, 0);
|
||||
return bool(res);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user