| author | |
| committer | |
| log | 2f58efcc1f0a435846c0909fcde7b080ec4e5d3a |
| tree | d3d0ef761b3c8055ae69e78816f14d739122da86 |
| parent | d7d905696c3e3b0e2b8c691317cb696be940b9a3 |
structs which are intended to be directly initialized and support static
initialization should not have init/deinit methods.4 files changed, 88 insertions(+), 92 deletions(-)
CMakeLists.txt+1-1| ... | @@ -493,7 +493,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -493,7 +493,7 @@ set(ZIG_STAGE2_SOURCES |
| 493 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmodti4.zig" | 493 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmodti4.zig" |
| 494 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivti3.zig" | 494 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivti3.zig" |
| 495 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/umodti3.zig" | 495 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/umodti3.zig" |
| 496 | "${CMAKE_SOURCE_DIR}/lib/std/spinlock.zig" | 496 | "${CMAKE_SOURCE_DIR}/lib/std/SpinLock.zig" |
| 497 | "${CMAKE_SOURCE_DIR}/lib/std/start.zig" | 497 | "${CMAKE_SOURCE_DIR}/lib/std/start.zig" |
| 498 | "${CMAKE_SOURCE_DIR}/lib/std/std.zig" | 498 | "${CMAKE_SOURCE_DIR}/lib/std/std.zig" |
| 499 | "${CMAKE_SOURCE_DIR}/lib/std/target.zig" | 499 | "${CMAKE_SOURCE_DIR}/lib/std/target.zig" |
lib/std/SpinLock.zig created+86| ... | @@ -0,0 +1,86 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | //! A mutually exclusive lock that grinds the CPU rather than interacting with | ||
| 7 | //! the operating system. It does however yield to the OS scheduler while | ||
| 8 | //! spinning, when targeting an OS that supports it. | ||
| 9 | //! This struct can be initialized directly and statically initialized. The | ||
| 10 | //! default state is unlocked. | ||
| 11 | |||
| 12 | state: State = State.Unlocked, | ||
| 13 | |||
| 14 | const std = @import("std.zig"); | ||
| 15 | const builtin = @import("builtin"); | ||
| 16 | const SpinLock = @This(); | ||
| 17 | |||
| 18 | const State = enum(u8) { | ||
| 19 | Unlocked, | ||
| 20 | Locked, | ||
| 21 | }; | ||
| 22 | |||
| 23 | pub const Held = struct { | ||
| 24 | spinlock: *SpinLock, | ||
| 25 | |||
| 26 | pub fn release(self: Held) void { | ||
| 27 | @atomicStore(State, &self.spinlock.state, .Unlocked, .Release); | ||
| 28 | } | ||
| 29 | }; | ||
| 30 | |||
| 31 | pub fn tryAcquire(self: *SpinLock) ?Held { | ||
| 32 | return switch (@atomicRmw(State, &self.state, .Xchg, .Locked, .Acquire)) { | ||
| 33 | .Unlocked => Held{ .spinlock = self }, | ||
| 34 | .Locked => null, | ||
| 35 | }; | ||
| 36 | } | ||
| 37 | |||
| 38 | pub fn acquire(self: *SpinLock) Held { | ||
| 39 | while (true) { | ||
| 40 | return self.tryAcquire() orelse { | ||
| 41 | yield(); | ||
| 42 | continue; | ||
| 43 | }; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | pub fn yield() void { | ||
| 48 | // On native windows, SwitchToThread is too expensive, | ||
| 49 | // and yielding for 380-410 iterations was found to be | ||
| 50 | // a nice sweet spot. Posix systems on the other hand, | ||
| 51 | // especially linux, perform better by yielding the thread. | ||
| 52 | switch (builtin.os.tag) { | ||
| 53 | .windows => loopHint(400), | ||
| 54 | else => std.os.sched_yield() catch loopHint(1), | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Hint to the cpu that execution is spinning | ||
| 59 | /// for the given amount of iterations. | ||
| 60 | pub fn loopHint(iterations: usize) void { | ||
| 61 | var i = iterations; | ||
| 62 | while (i != 0) : (i -= 1) { | ||
| 63 | switch (builtin.arch) { | ||
| 64 | // these instructions use a memory clobber as they | ||
| 65 | // flush the pipeline of any speculated reads/writes. | ||
| 66 | .i386, .x86_64 => asm volatile ("pause" | ||
| 67 | : | ||
| 68 | : | ||
| 69 | : "memory" | ||
| 70 | ), | ||
| 71 | .arm, .aarch64 => asm volatile ("yield" | ||
| 72 | : | ||
| 73 | : | ||
| 74 | : "memory" | ||
| 75 | ), | ||
| 76 | else => std.os.sched_yield() catch {}, | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | test "basic usage" { | ||
| 82 | var lock: SpinLock = .{}; | ||
| 83 | |||
| 84 | const held = lock.acquire(); | ||
| 85 | defer held.release(); | ||
| 86 | } | ||
lib/std/spinlock.zig deleted-90| ... | @@ -1,90 +0,0 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("std.zig"); | ||
| 7 | const builtin = @import("builtin"); | ||
| 8 | |||
| 9 | pub const SpinLock = struct { | ||
| 10 | state: State = .Unlocked, | ||
| 11 | |||
| 12 | const State = enum(u8) { | ||
| 13 | Unlocked, | ||
| 14 | Locked, | ||
| 15 | }; | ||
| 16 | |||
| 17 | pub const Held = struct { | ||
| 18 | spinlock: *SpinLock, | ||
| 19 | |||
| 20 | pub fn release(self: Held) void { | ||
| 21 | @atomicStore(State, &self.spinlock.state, .Unlocked, .Release); | ||
| 22 | } | ||
| 23 | }; | ||
| 24 | |||
| 25 | pub fn init() SpinLock { | ||
| 26 | return SpinLock{ .state = .Unlocked }; | ||
| 27 | } | ||
| 28 | |||
| 29 | pub fn deinit(self: *SpinLock) void { | ||
| 30 | self.* = undefined; | ||
| 31 | } | ||
| 32 | |||
| 33 | pub fn tryAcquire(self: *SpinLock) ?Held { | ||
| 34 | return switch (@atomicRmw(State, &self.state, .Xchg, .Locked, .Acquire)) { | ||
| 35 | .Unlocked => Held{ .spinlock = self }, | ||
| 36 | .Locked => null, | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn acquire(self: *SpinLock) Held { | ||
| 41 | while (true) { | ||
| 42 | return self.tryAcquire() orelse { | ||
| 43 | yield(); | ||
| 44 | continue; | ||
| 45 | }; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | pub fn yield() void { | ||
| 50 | // On native windows, SwitchToThread is too expensive, | ||
| 51 | // and yielding for 380-410 iterations was found to be | ||
| 52 | // a nice sweet spot. Posix systems on the other hand, | ||
| 53 | // especially linux, perform better by yielding the thread. | ||
| 54 | switch (builtin.os.tag) { | ||
| 55 | .windows => loopHint(400), | ||
| 56 | else => std.os.sched_yield() catch loopHint(1), | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | /// Hint to the cpu that execution is spinning | ||
| 61 | /// for the given amount of iterations. | ||
| 62 | pub fn loopHint(iterations: usize) void { | ||
| 63 | var i = iterations; | ||
| 64 | while (i != 0) : (i -= 1) { | ||
| 65 | switch (builtin.arch) { | ||
| 66 | // these instructions use a memory clobber as they | ||
| 67 | // flush the pipeline of any speculated reads/writes. | ||
| 68 | .i386, .x86_64 => asm volatile ("pause" | ||
| 69 | : | ||
| 70 | : | ||
| 71 | : "memory" | ||
| 72 | ), | ||
| 73 | .arm, .aarch64 => asm volatile ("yield" | ||
| 74 | : | ||
| 75 | : | ||
| 76 | : "memory" | ||
| 77 | ), | ||
| 78 | else => std.os.sched_yield() catch {}, | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | }; | ||
| 83 | |||
| 84 | test "spinlock" { | ||
| 85 | var lock = SpinLock.init(); | ||
| 86 | defer lock.deinit(); | ||
| 87 | |||
| 88 | const held = lock.acquire(); | ||
| 89 | defer held.release(); | ||
| 90 | } | ||
lib/std/std.zig+1-1| ... | @@ -32,7 +32,7 @@ pub const Progress = @import("Progress.zig"); | ... | @@ -32,7 +32,7 @@ pub const Progress = @import("Progress.zig"); |
| 32 | pub const ResetEvent = @import("ResetEvent.zig"); | 32 | pub const ResetEvent = @import("ResetEvent.zig"); |
| 33 | pub const SemanticVersion = @import("SemanticVersion.zig"); | 33 | pub const SemanticVersion = @import("SemanticVersion.zig"); |
| 34 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; | 34 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; |
| 35 | pub const SpinLock = @import("spinlock.zig").SpinLock; | 35 | pub const SpinLock = @import("SpinLock.zig"); |
| 36 | pub const StaticResetEvent = @import("StaticResetEvent.zig"); | 36 | pub const StaticResetEvent = @import("StaticResetEvent.zig"); |
| 37 | pub const StringHashMap = hash_map.StringHashMap; | 37 | pub const StringHashMap = hash_map.StringHashMap; |
| 38 | pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged; | 38 | pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged; |