| 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 | 493 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmodti4.zig" |
| 494 | 494 | "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivti3.zig" |
| 495 | 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 | 497 | "${CMAKE_SOURCE_DIR}/lib/std/start.zig" |
| 498 | 498 | "${CMAKE_SOURCE_DIR}/lib/std/std.zig" |
| 499 | 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 | 32 | pub const ResetEvent = @import("ResetEvent.zig"); |
| 33 | 33 | pub const SemanticVersion = @import("SemanticVersion.zig"); |
| 34 | 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 | 36 | pub const StaticResetEvent = @import("StaticResetEvent.zig"); |
| 37 | 37 | pub const StringHashMap = hash_map.StringHashMap; |
| 38 | 38 | pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged; |