From bfc1772d8ead9e8fcde0a315411ddcd040cc3d28 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Feb 2019 12:22:21 -0500 Subject: [PATCH] fixups --- CMakeLists.txt | 31 ++++---- std/fmt/index.zig | 2 - std/index.zig | 2 + std/mutex.zig | 61 +++------------- std/os/windows/kernel32.zig | 14 ++-- std/statically_initialized_mutex.zig | 105 +++++++++++++++++++++++++++ 6 files changed, 142 insertions(+), 73 deletions(-) create mode 100644 std/statically_initialized_mutex.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 3841519f29..4dd6a1dcfa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -454,10 +454,10 @@ set(ZIG_STD_FILES "crypto/hmac.zig" "crypto/index.zig" "crypto/md5.zig" + "crypto/poly1305.zig" "crypto/sha1.zig" "crypto/sha2.zig" "crypto/sha3.zig" - "crypto/poly1305.zig" "crypto/x25519.zig" "cstr.zig" "debug/failing_allocator.zig" @@ -566,9 +566,9 @@ set(ZIG_STD_FILES "math/tan.zig" "math/tanh.zig" "math/trunc.zig" + "mem.zig" "meta/index.zig" "meta/trait.zig" - "mem.zig" "mutex.zig" "net.zig" "os/child_process.zig" @@ -576,16 +576,16 @@ set(ZIG_STD_FILES "os/darwin/errno.zig" "os/epoch.zig" "os/file.zig" + "os/freebsd/errno.zig" + "os/freebsd/index.zig" "os/get_app_data_dir.zig" "os/get_user_id.zig" "os/index.zig" + "os/linux/arm64.zig" "os/linux/errno.zig" "os/linux/index.zig" "os/linux/vdso.zig" "os/linux/x86_64.zig" - "os/linux/arm64.zig" - "os/freebsd/errno.zig" - "os/freebsd/index.zig" "os/path.zig" "os/time.zig" "os/uefi.zig" @@ -612,6 +612,16 @@ set(ZIG_STD_FILES "special/compiler_rt/comparetf2.zig" "special/compiler_rt/divti3.zig" "special/compiler_rt/extendXfYf2.zig" + "special/compiler_rt/fixdfdi.zig" + "special/compiler_rt/fixdfsi.zig" + "special/compiler_rt/fixdfti.zig" + "special/compiler_rt/fixint.zig" + "special/compiler_rt/fixsfdi.zig" + "special/compiler_rt/fixsfsi.zig" + "special/compiler_rt/fixsfti.zig" + "special/compiler_rt/fixtfdi.zig" + "special/compiler_rt/fixtfsi.zig" + "special/compiler_rt/fixtfti.zig" "special/compiler_rt/fixuint.zig" "special/compiler_rt/fixunsdfdi.zig" "special/compiler_rt/fixunsdfsi.zig" @@ -622,16 +632,6 @@ set(ZIG_STD_FILES "special/compiler_rt/fixunstfdi.zig" "special/compiler_rt/fixunstfsi.zig" "special/compiler_rt/fixunstfti.zig" - "special/compiler_rt/fixint.zig" - "special/compiler_rt/fixdfdi.zig" - "special/compiler_rt/fixdfsi.zig" - "special/compiler_rt/fixdfti.zig" - "special/compiler_rt/fixsfdi.zig" - "special/compiler_rt/fixsfsi.zig" - "special/compiler_rt/fixsfti.zig" - "special/compiler_rt/fixtfdi.zig" - "special/compiler_rt/fixtfsi.zig" - "special/compiler_rt/fixtfti.zig" "special/compiler_rt/floattidf.zig" "special/compiler_rt/floattisf.zig" "special/compiler_rt/floattitf.zig" @@ -656,6 +656,7 @@ set(ZIG_STD_FILES "special/panic.zig" "special/test_runner.zig" "spinlock.zig" + "statically_initialized_mutex.zig" "unicode.zig" "zig/ast.zig" "zig/index.zig" diff --git a/std/fmt/index.zig b/std/fmt/index.zig index b010072273..6097a12c23 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -982,13 +982,11 @@ test "fmt.format" { context = BufPrintContext{ .remaining = buf1[0..] }; try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite); res = buf1[0 .. buf1.len - context.remaining.len]; - debug.warn("{}\n", res); assert(mem.eql(u8, res, "a")); context = BufPrintContext{ .remaining = buf1[0..] }; try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite); res = buf1[0 .. buf1.len - context.remaining.len]; - debug.warn("{}\n", res); assert(mem.eql(u8, res, "1100")); } { diff --git a/std/index.zig b/std/index.zig index 33eec14b0e..80d1e46bb6 100644 --- a/std/index.zig +++ b/std/index.zig @@ -9,6 +9,7 @@ pub const DynLib = @import("dynamic_library.zig").DynLib; pub const HashMap = @import("hash_map.zig").HashMap; pub const LinkedList = @import("linked_list.zig").LinkedList; pub const Mutex = @import("mutex.zig").Mutex; +pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex; pub const SegmentedList = @import("segmented_list.zig").SegmentedList; pub const SpinLock = @import("spinlock.zig").SpinLock; @@ -55,6 +56,7 @@ test "std" { _ = @import("hash_map.zig"); _ = @import("linked_list.zig"); _ = @import("mutex.zig"); + _ = @import("statically_initialized_mutex.zig"); _ = @import("segmented_list.zig"); _ = @import("spinlock.zig"); diff --git a/std/mutex.zig b/std/mutex.zig index 2885a99ed7..723581cbef 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -9,6 +9,9 @@ const windows = std.os.windows; /// Lock may be held only once. If the same thread /// tries to acquire the same mutex twice, it deadlocks. +/// This type must be initialized at runtime, and then deinitialized when no +/// longer needed, to free resources. +/// If you need static initialization, use std.StaticallyInitializedMutex. /// The Linux implementation is based on mutex3 from /// https://www.akkadia.org/drepper/futex.pdf pub const Mutex = switch(builtin.os) { @@ -60,57 +63,15 @@ pub const Mutex = switch(builtin.os) { return Held { .mutex = self }; } }, - builtin.Os.windows => struct { - - lock: windows.CRITICAL_SECTION, - init_once: windows.RTL_RUN_ONCE, - - pub const Held = struct { - mutex: *Mutex, - - pub fn release(self: Held) void { - windows.LeaveCriticalSection(&self.mutex.lock); - } - }; - - pub fn init() Mutex { - return Mutex { - .lock = undefined, - .init_once = windows.INIT_ONCE_STATIC_INIT, - }; - } - - extern fn initCriticalSection( - InitOnce: *windows.RTL_RUN_ONCE, - Parameter: ?windows.PVOID, - Context: ?windows.PVOID - ) windows.BOOL { - var lock = @ptrCast( - *windows.CRITICAL_SECTION, - @alignCast(@alignOf(*windows.CRITICAL_SECTION), Context.?) - ); - windows.InitializeCriticalSection(lock); - return windows.TRUE; - } - - pub fn deinit(self: *Mutex) void { - windows.DeleteCriticalSection(&self.lock); - } - - pub fn acquire(self: *Mutex) Held { - if (windows.InitOnceExecuteOnce( - &self.init_once, - initCriticalSection, - null, @ptrCast(?windows.PVOID, self) - ) == windows.FALSE) { - unreachable; - } - windows.EnterCriticalSection(&self.lock); - return Held { .mutex = self }; - } - }, + // TODO once https://github.com/ziglang/zig/issues/287 (copy elision) is solved, we can make a + // better implementation of this. The problem is we need the init() function to have access to + // the address of the CRITICAL_SECTION, and then have it not move. + builtin.Os.windows => std.StaticallyInitializedMutex, else => struct { - /// TODO better implementation than spin lock + /// TODO better implementation than spin lock. + /// When changing this, one must also change the corresponding + /// std.StaticallyInitializedMutex code, since it aliases this type, + /// under the assumption that it works both statically and at runtime. lock: SpinLock, pub const Held = struct { diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 7a6126133a..66b9552c5f 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -253,15 +253,17 @@ pub const RTL_CRITICAL_SECTION = extern struct { }; pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; +pub const INIT_ONCE = RTL_RUN_ONCE; +pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT; -pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *RTL_RUN_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?LPVOID) BOOL; +pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL; -pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *RTL_RUN_ONCE, Parameter: ?PVOID, Context: ?PVOID) BOOL; +pub const INIT_ONCE_FN = extern fn(InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL; pub const RTL_RUN_ONCE = extern struct { - Ptr: ?PVOID, + Ptr: ?*c_void, }; -pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE { - .Ptr = null, -}; \ No newline at end of file +pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE { + .Ptr = null, +}; diff --git a/std/statically_initialized_mutex.zig b/std/statically_initialized_mutex.zig new file mode 100644 index 0000000000..dd875eeaf9 --- /dev/null +++ b/std/statically_initialized_mutex.zig @@ -0,0 +1,105 @@ +const std = @import("index.zig"); +const builtin = @import("builtin"); +const AtomicOrder = builtin.AtomicOrder; +const AtomicRmwOp = builtin.AtomicRmwOp; +const assert = std.debug.assert; +const windows = std.os.windows; + +/// Lock may be held only once. If the same thread +/// tries to acquire the same mutex twice, it deadlocks. +/// This type is intended to be initialized statically. If you don't +/// require static initialization, use std.Mutex. +/// On Windows, this mutex allocates resources when it is +/// first used, and the resources cannot be freed. +/// On Linux, this is an alias of std.Mutex. +pub const StaticallyInitializedMutex = switch(builtin.os) { + builtin.Os.linux => std.Mutex, + builtin.Os.windows => struct { + lock: windows.CRITICAL_SECTION, + init_once: windows.RTL_RUN_ONCE, + + pub const Held = struct { + mutex: *StaticallyInitializedMutex, + + pub fn release(self: Held) void { + windows.LeaveCriticalSection(&self.mutex.lock); + } + }; + + pub fn init() StaticallyInitializedMutex { + return StaticallyInitializedMutex { + .lock = undefined, + .init_once = windows.INIT_ONCE_STATIC_INIT, + }; + } + + extern fn initCriticalSection( + InitOnce: *windows.RTL_RUN_ONCE, + Parameter: ?*c_void, + Context: ?*c_void, + ) windows.BOOL { + const lock = @ptrCast(*windows.CRITICAL_SECTION, @alignCast(@alignOf(windows.CRITICAL_SECTION), Parameter)); + windows.InitializeCriticalSection(lock); + return windows.TRUE; + } + + /// TODO: once https://github.com/ziglang/zig/issues/287 is solved and std.Mutex has a better + /// implementation of a runtime initialized mutex, remove this function. + pub fn deinit(self: *StaticallyInitializedMutex) void { + assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); + windows.DeleteCriticalSection(&self.lock); + } + + pub fn acquire(self: *StaticallyInitializedMutex) Held { + assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); + windows.EnterCriticalSection(&self.lock); + return Held { .mutex = self }; + } + }, + else => std.Mutex, +}; + +test "std.StaticallyInitializedMutex" { + const TestContext = struct { + data: i128, + + const TestContext = @This(); + const incr_count = 10000; + + var mutex = StaticallyInitializedMutex.init(); + + fn worker(ctx: *TestContext) void { + var i: usize = 0; + while (i != TestContext.incr_count) : (i += 1) { + const held = mutex.acquire(); + defer held.release(); + + ctx.data += 1; + } + } + }; + + var direct_allocator = std.heap.DirectAllocator.init(); + defer direct_allocator.deinit(); + + var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024); + defer direct_allocator.allocator.free(plenty_of_memory); + + var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); + var a = &fixed_buffer_allocator.allocator; + + + var context = TestContext{ + .data = 0, + }; + + const thread_count = 10; + var threads: [thread_count]*std.os.Thread = undefined; + for (threads) |*t| { + t.* = try std.os.spawnThread(&context, TestContext.worker); + } + for (threads) |t| + t.wait(); + + std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count); +}