2019-03-02 13:46:04 -08:00
|
|
|
const std = @import("std.zig");
|
2018-10-03 10:19:10 -07:00
|
|
|
const builtin = @import("builtin");
|
|
|
|
const assert = std.debug.assert;
|
2019-11-05 06:16:08 -08:00
|
|
|
const time = std.time;
|
|
|
|
const linux = std.os.linux;
|
|
|
|
const windows = std.os.windows;
|
2018-10-03 10:19:10 -07:00
|
|
|
|
2018-11-13 05:08:37 -08:00
|
|
|
pub const SpinLock = struct {
|
2018-10-03 10:19:10 -07:00
|
|
|
lock: u8, // TODO use a bool or enum
|
|
|
|
|
2018-11-13 05:08:37 -08:00
|
|
|
pub const Held = struct {
|
2018-10-03 10:19:10 -07:00
|
|
|
spinlock: *SpinLock,
|
|
|
|
|
|
|
|
pub fn release(self: Held) void {
|
2019-11-05 06:16:08 -08:00
|
|
|
// TODO: @atomicStore() https://github.com/ziglang/zig/issues/2995
|
|
|
|
assert(@atomicRmw(u8, &self.spinlock.lock, .Xchg, 0, .Release) == 1);
|
2018-10-03 10:19:10 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn init() SpinLock {
|
2018-11-13 05:08:37 -08:00
|
|
|
return SpinLock{ .lock = 0 };
|
2018-10-03 10:19:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn acquire(self: *SpinLock) Held {
|
2019-11-05 07:31:29 -08:00
|
|
|
var backoff = Backoff.init();
|
|
|
|
while (@atomicRmw(u8, &self.lock, .Xchg, 1, .Acquire) != 0)
|
|
|
|
backoff.yield();
|
2018-11-13 05:08:37 -08:00
|
|
|
return Held{ .spinlock = self };
|
2018-10-03 10:19:10 -07:00
|
|
|
}
|
2019-11-05 06:16:08 -08:00
|
|
|
|
2019-11-07 13:32:20 -08:00
|
|
|
pub fn yield(iterations: usize) void {
|
|
|
|
var i = iterations;
|
|
|
|
while (i != 0) : (i -= 1) {
|
|
|
|
switch (builtin.arch) {
|
|
|
|
.i386, .x86_64 => asm volatile("pause" ::: "memory"),
|
|
|
|
// .arm, .aarch64 => asm volatile("yield"),
|
|
|
|
//
|
|
|
|
// Causes CI to fail
|
|
|
|
// See: https://github.com/ziglang/zig/pull/3585#issuecomment-549962765
|
|
|
|
else => time.sleep(0),
|
|
|
|
}
|
2019-11-05 06:16:08 -08:00
|
|
|
}
|
|
|
|
}
|
2019-11-05 07:31:29 -08:00
|
|
|
|
2019-11-05 11:43:17 -08:00
|
|
|
/// Provides a method to incrementally yield longer each time its called.
|
|
|
|
pub const Backoff = struct {
|
2019-11-05 07:31:29 -08:00
|
|
|
iteration: usize,
|
|
|
|
|
2019-11-05 11:43:17 -08:00
|
|
|
pub fn init() @This() {
|
2019-11-05 07:31:29 -08:00
|
|
|
return @This(){ .iteration = 0 };
|
|
|
|
}
|
|
|
|
|
2019-11-07 13:32:20 -08:00
|
|
|
/// Modified hybrid yielding from
|
2019-11-05 11:43:17 -08:00
|
|
|
/// http://www.1024cores.net/home/lock-free-algorithms/tricks/spinning
|
|
|
|
pub fn yield(self: *@This()) void {
|
2019-11-05 07:31:29 -08:00
|
|
|
defer self.iteration +%= 1;
|
2019-11-07 13:32:20 -08:00
|
|
|
if (self.iteration < 20) {
|
|
|
|
SpinLock.yield(self.iteration);
|
2019-11-05 07:31:29 -08:00
|
|
|
} else if (self.iteration < 24) {
|
2019-11-07 13:32:20 -08:00
|
|
|
os.yield();
|
2019-11-05 07:31:29 -08:00
|
|
|
} else if (self.iteration < 26) {
|
|
|
|
time.sleep(1 * time.millisecond);
|
|
|
|
} else {
|
|
|
|
time.sleep(10 * time.millisecond);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2018-10-03 10:19:10 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
test "spinlock" {
|
|
|
|
var lock = SpinLock.init();
|
|
|
|
const held = lock.acquire();
|
|
|
|
defer held.release();
|
|
|
|
}
|