authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-06 17:36:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-06 17:36:06-07:00
log2f58efcc1f0a435846c0909fcde7b080ec4e5d3a
treed3d0ef761b3c8055ae69e78816f14d739122da86
parentd7d905696c3e3b0e2b8c691317cb696be940b9a3

std.SpinLock: flatten and remove init/deinit

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
493493 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmodti4.zig"
494494 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivti3.zig"
495495 "${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"
497497 "${CMAKE_SOURCE_DIR}/lib/std/start.zig"
498498 "${CMAKE_SOURCE_DIR}/lib/std/std.zig"
499499 "${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
12state: State = State.Unlocked,
13
14const std = @import("std.zig");
15const builtin = @import("builtin");
16const SpinLock = @This();
17
18const State = enum(u8) {
19 Unlocked,
20 Locked,
21};
22
23pub 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
31pub 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
38pub fn acquire(self: *SpinLock) Held {
39 while (true) {
40 return self.tryAcquire() orelse {
41 yield();
42 continue;
43 };
44 }
45}
46
47pub 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.
60pub 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
81test "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.
6const std = @import("std.zig");
7const builtin = @import("builtin");
8
9pub 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
84test "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");
3232pub const ResetEvent = @import("ResetEvent.zig");
3333pub const SemanticVersion = @import("SemanticVersion.zig");
3434pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;
35pub const SpinLock = @import("spinlock.zig").SpinLock;
35pub const SpinLock = @import("SpinLock.zig");
3636pub const StaticResetEvent = @import("StaticResetEvent.zig");
3737pub const StringHashMap = hash_map.StringHashMap;
3838pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged;