| author | |
| committer | |
| log | eb6975f0889db4c2c29d47bb959bc39bd0a9b167 |
| tree | 688143d7828e2717ed2246c9a2482d8f22433976 |
| parent | 57cf9f7ea6d018714cf4afa711799c67ff730f12 |
| signature |
- deprecates `std.Thread.spinLoopHint` and moves it to `std.atomic.spinLoopHint`
- added an Atomic(T) generic wrapper type which replaces atomic.Bool and atomic.Int
- in Atomic(T), selectively expose member functions depending on T and include bitwise atomic methods when T is an Integer
- added fence() and compilerFence() to std.atomic14 files changed, 624 insertions(+), 193 deletions(-)
CMakeLists.txt+1-2| ... | @@ -337,8 +337,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -337,8 +337,7 @@ set(ZIG_STAGE2_SOURCES |
| 337 | "${CMAKE_SOURCE_DIR}/lib/std/array_list.zig" | 337 | "${CMAKE_SOURCE_DIR}/lib/std/array_list.zig" |
| 338 | "${CMAKE_SOURCE_DIR}/lib/std/ascii.zig" | 338 | "${CMAKE_SOURCE_DIR}/lib/std/ascii.zig" |
| 339 | "${CMAKE_SOURCE_DIR}/lib/std/atomic.zig" | 339 | "${CMAKE_SOURCE_DIR}/lib/std/atomic.zig" |
| 340 | "${CMAKE_SOURCE_DIR}/lib/std/atomic/bool.zig" | 340 | "${CMAKE_SOURCE_DIR}/lib/std/atomic/Atomic.zig" |
| 341 | "${CMAKE_SOURCE_DIR}/lib/std/atomic/int.zig" | ||
| 342 | "${CMAKE_SOURCE_DIR}/lib/std/atomic/queue.zig" | 341 | "${CMAKE_SOURCE_DIR}/lib/std/atomic/queue.zig" |
| 343 | "${CMAKE_SOURCE_DIR}/lib/std/atomic/stack.zig" | 342 | "${CMAKE_SOURCE_DIR}/lib/std/atomic/stack.zig" |
| 344 | "${CMAKE_SOURCE_DIR}/lib/std/base64.zig" | 343 | "${CMAKE_SOURCE_DIR}/lib/std/base64.zig" |
lib/std/Thread.zig+8-29| ... | @@ -67,33 +67,7 @@ else switch (std.Target.current.os.tag) { | ... | @@ -67,33 +67,7 @@ else switch (std.Target.current.os.tag) { |
| 67 | else => struct {}, | 67 | else => struct {}, |
| 68 | }; | 68 | }; |
| 69 | 69 | ||
| 70 | /// Signals the processor that it is inside a busy-wait spin-loop ("spin lock"). | 70 | pub const spinLoopHint = @compileError("deprecated: use std.atomic.spinLoopHint"); |
| 71 | pub inline fn spinLoopHint() void { | ||
| 72 | switch (std.Target.current.cpu.arch) { | ||
| 73 | .i386, .x86_64 => { | ||
| 74 | asm volatile ("pause" ::: "memory"); | ||
| 75 | }, | ||
| 76 | .arm, .armeb, .thumb, .thumbeb => { | ||
| 77 | // `yield` was introduced in v6k but are also available on v6m. | ||
| 78 | const can_yield = comptime std.Target.arm.featureSetHasAny(std.Target.current.cpu.features, .{ .has_v6k, .has_v6m }); | ||
| 79 | if (can_yield) asm volatile ("yield" ::: "memory") | ||
| 80 | // Fallback. | ||
| 81 | else asm volatile ("" ::: "memory"); | ||
| 82 | }, | ||
| 83 | .aarch64, .aarch64_be, .aarch64_32 => { | ||
| 84 | asm volatile ("isb" ::: "memory"); | ||
| 85 | }, | ||
| 86 | .powerpc64, .powerpc64le => { | ||
| 87 | // No-op that serves as `yield` hint. | ||
| 88 | asm volatile ("or 27, 27, 27" ::: "memory"); | ||
| 89 | }, | ||
| 90 | else => { | ||
| 91 | // Do nothing but prevent the compiler from optimizing away the | ||
| 92 | // spinning loop. | ||
| 93 | asm volatile ("" ::: "memory"); | ||
| 94 | }, | ||
| 95 | } | ||
| 96 | } | ||
| 97 | 71 | ||
| 98 | /// Returns the ID of the calling thread. | 72 | /// Returns the ID of the calling thread. |
| 99 | /// Makes a syscall every time the function is called. | 73 | /// Makes a syscall every time the function is called. |
| ... | @@ -597,8 +571,13 @@ pub fn getCurrentThreadId() u64 { | ... | @@ -597,8 +571,13 @@ pub fn getCurrentThreadId() u64 { |
| 597 | } | 571 | } |
| 598 | } | 572 | } |
| 599 | 573 | ||
| 600 | test { | 574 | test "std.Thread" { |
| 601 | if (!builtin.single_threaded) { | 575 | if (!builtin.single_threaded) { |
| 602 | std.testing.refAllDecls(@This()); | 576 | _ = AutoResetEvent; |
| 577 | _ = ResetEvent; | ||
| 578 | _ = StaticResetEvent; | ||
| 579 | _ = Mutex; | ||
| 580 | _ = Semaphore; | ||
| 581 | _ = Condition; | ||
| 603 | } | 582 | } |
| 604 | } | 583 | } |
lib/std/Thread/Condition.zig+1-1| ... | @@ -115,7 +115,7 @@ pub const AtomicCondition = struct { | ... | @@ -115,7 +115,7 @@ pub const AtomicCondition = struct { |
| 115 | else => unreachable, | 115 | else => unreachable, |
| 116 | } | 116 | } |
| 117 | }, | 117 | }, |
| 118 | else => spinLoopHint(), | 118 | else => std.atomic.spinLoopHint(), |
| 119 | } | 119 | } |
| 120 | } | 120 | } |
| 121 | } | 121 | } |
lib/std/Thread/Mutex.zig+2-2| ... | @@ -126,7 +126,7 @@ pub const AtomicMutex = struct { | ... | @@ -126,7 +126,7 @@ pub const AtomicMutex = struct { |
| 126 | 126 | ||
| 127 | var iter = std.math.min(32, spin + 1); | 127 | var iter = std.math.min(32, spin + 1); |
| 128 | while (iter > 0) : (iter -= 1) | 128 | while (iter > 0) : (iter -= 1) |
| 129 | std.Thread.spinLoopHint(); | 129 | std.atomic.spinLoopHint(); |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | new_state = .waiting; | 132 | new_state = .waiting; |
| ... | @@ -149,7 +149,7 @@ pub const AtomicMutex = struct { | ... | @@ -149,7 +149,7 @@ pub const AtomicMutex = struct { |
| 149 | else => unreachable, | 149 | else => unreachable, |
| 150 | } | 150 | } |
| 151 | }, | 151 | }, |
| 152 | else => std.Thread.spinLoopHint(), | 152 | else => std.atomic.spinLoopHint(), |
| 153 | } | 153 | } |
| 154 | } | 154 | } |
| 155 | } | 155 | } |
lib/std/Thread/StaticResetEvent.zig+2-2| ... | @@ -182,7 +182,7 @@ pub const AtomicEvent = struct { | ... | @@ -182,7 +182,7 @@ pub const AtomicEvent = struct { |
| 182 | timer = time.Timer.start() catch return error.TimedOut; | 182 | timer = time.Timer.start() catch return error.TimedOut; |
| 183 | 183 | ||
| 184 | while (@atomicLoad(u32, waiters, .Acquire) != WAKE) { | 184 | while (@atomicLoad(u32, waiters, .Acquire) != WAKE) { |
| 185 | std.os.sched_yield() catch std.Thread.spinLoopHint(); | 185 | std.os.sched_yield() catch std.atomic.spinLoopHint(); |
| 186 | if (timeout) |timeout_ns| { | 186 | if (timeout) |timeout_ns| { |
| 187 | if (timer.read() >= timeout_ns) | 187 | if (timer.read() >= timeout_ns) |
| 188 | return error.TimedOut; | 188 | return error.TimedOut; |
| ... | @@ -293,7 +293,7 @@ pub const AtomicEvent = struct { | ... | @@ -293,7 +293,7 @@ pub const AtomicEvent = struct { |
| 293 | return @intToPtr(?windows.HANDLE, handle); | 293 | return @intToPtr(?windows.HANDLE, handle); |
| 294 | }, | 294 | }, |
| 295 | LOADING => { | 295 | LOADING => { |
| 296 | std.os.sched_yield() catch std.Thread.spinLoopHint(); | 296 | std.os.sched_yield() catch std.atomic.spinLoopHint(); |
| 297 | handle = @atomicLoad(usize, &event_handle, .Monotonic); | 297 | handle = @atomicLoad(usize, &event_handle, .Monotonic); |
| 298 | }, | 298 | }, |
| 299 | else => { | 299 | else => { |
lib/std/atomic.zig+72-4| ... | @@ -3,14 +3,82 @@ | ... | @@ -3,14 +3,82 @@ |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | 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 | 4 | // The MIT license requires this copyright notice to be included in all copies |
| 5 | // and substantial portions of the software. | 5 | // and substantial portions of the software. |
| 6 | |||
| 7 | const std = @import("std.zig"); | ||
| 8 | const target = std.Target.current; | ||
| 9 | |||
| 10 | pub const Ordering = std.builtin.AtomicOrder; | ||
| 11 | |||
| 6 | pub const Stack = @import("atomic/stack.zig").Stack; | 12 | pub const Stack = @import("atomic/stack.zig").Stack; |
| 7 | pub const Queue = @import("atomic/queue.zig").Queue; | 13 | pub const Queue = @import("atomic/queue.zig").Queue; |
| 8 | pub const Bool = @import("atomic/bool.zig").Bool; | 14 | pub const Atomic = @import("atomic/Atomic.zig").Atomic; |
| 9 | pub const Int = @import("atomic/int.zig").Int; | ||
| 10 | 15 | ||
| 11 | test "std.atomic" { | 16 | test "std.atomic" { |
| 12 | _ = @import("atomic/stack.zig"); | 17 | _ = @import("atomic/stack.zig"); |
| 13 | _ = @import("atomic/queue.zig"); | 18 | _ = @import("atomic/queue.zig"); |
| 14 | _ = @import("atomic/bool.zig"); | 19 | _ = @import("atomic/Atomic.zig"); |
| 15 | _ = @import("atomic/int.zig"); | 20 | } |
| 21 | |||
| 22 | pub fn fence(comptime ordering: Ordering) callconv(.Inline) void { | ||
| 23 | switch (ordering) { | ||
| 24 | .Acquire, .Release, .AcqRel, .SeqCst => { | ||
| 25 | @fence(ordering); | ||
| 26 | }, | ||
| 27 | else => { | ||
| 28 | @compileLog(ordering, " only applies to a given memory location"); | ||
| 29 | }, | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | pub fn compilerFence(comptime ordering: Ordering) callconv(.Inline) void { | ||
| 34 | switch (ordering) { | ||
| 35 | .Acquire, .Release, .AcqRel, .SeqCst => asm volatile ("" ::: "memory"), | ||
| 36 | else => @compileLog(ordering, " only applies to a given memory location"), | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | test "fence/compilerFence" { | ||
| 41 | inline for (.{ .Acquire, .Release, .AcqRel, .SeqCst }) |ordering| { | ||
| 42 | compilerFence(ordering); | ||
| 43 | fence(ordering); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Signals to the processor that the caller is inside a busy-wait spin-loop. | ||
| 48 | pub fn spinLoopHint() callconv(.Inline) void { | ||
| 49 | const hint_instruction = switch (target.cpu.arch) { | ||
| 50 | // No-op instruction that can hint to save (or share with a hardware-thread) pipelining/power resources | ||
| 51 | // https://software.intel.com/content/www/us/en/develop/articles/benefitting-power-and-performance-sleep-loops.html | ||
| 52 | .i386, .x86_64 => "pause", | ||
| 53 | |||
| 54 | // No-op instruction that serves as a hardware-thread resource yield hint. | ||
| 55 | // https://stackoverflow.com/a/7588941 | ||
| 56 | .powerpc64, .powerpc64le => "or 27, 27, 27", | ||
| 57 | |||
| 58 | // `isb` appears more reliable for releasing execution resources than `yield` on common aarch64 CPUs. | ||
| 59 | // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8258604 | ||
| 60 | // https://bugs.mysql.com/bug.php?id=100664 | ||
| 61 | .aarch64, .aarch64_be, .aarch64_32 => "isb", | ||
| 62 | |||
| 63 | // `yield` was introduced in v6k but is also available on v6m. | ||
| 64 | // https://www.keil.com/support/man/docs/armasm/armasm_dom1361289926796.htm | ||
| 65 | .arm, .armeb, .thumb, .thumbeb => blk: { | ||
| 66 | const can_yield = comptime std.Target.arm.featureSetHasAny(target.cpu.features, .{ .has_v6k, .has_v6m }); | ||
| 67 | const instruction = if (can_yield) "yield" else ""; | ||
| 68 | break :blk instruction; | ||
| 69 | }, | ||
| 70 | |||
| 71 | else => "", | ||
| 72 | }; | ||
| 73 | |||
| 74 | // Memory barrier to prevent the compiler from optimizing away the spin-loop | ||
| 75 | // even if no hint_instruction was provided. | ||
| 76 | asm volatile (hint_instruction ::: "memory"); | ||
| 77 | } | ||
| 78 | |||
| 79 | test "spinLoopHint" { | ||
| 80 | var i: usize = 10; | ||
| 81 | while (i > 0) : (i -= 1) { | ||
| 82 | spinLoopHint(); | ||
| 83 | } | ||
| 16 | } | 84 | } |
lib/std/atomic/Atomic.zig created+522| ... | @@ -0,0 +1,522 @@ | ||
| 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 | |||
| 7 | const std = @import("../std.zig"); | ||
| 8 | |||
| 9 | const testing = std.testing; | ||
| 10 | const target = std.Target.current; | ||
| 11 | const Ordering = std.atomic.Ordering; | ||
| 12 | |||
| 13 | pub fn Atomic(comptime T: type) type { | ||
| 14 | return extern struct { | ||
| 15 | value: T, | ||
| 16 | |||
| 17 | const Self = @This(); | ||
| 18 | |||
| 19 | pub fn init(value: T) Self { | ||
| 20 | return .{ .value = value }; | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Non-atomically load from the atomic value without synchronization. | ||
| 24 | /// Care must be taken to avoid data-races when interacting with other atomic operations. | ||
| 25 | pub fn loadUnchecked(self: Self) T { | ||
| 26 | return self.value; | ||
| 27 | } | ||
| 28 | |||
| 29 | /// Non-atomically store to the atomic value without synchronization. | ||
| 30 | /// Care must be taken to avoid data-races when interacting with other atomic operations. | ||
| 31 | pub fn storeUnchecked(self: *Self, value: T) void { | ||
| 32 | self.value = value; | ||
| 33 | } | ||
| 34 | |||
| 35 | pub fn load(self: *const Self, comptime ordering: Ordering) T { | ||
| 36 | return switch (ordering) { | ||
| 37 | .AcqRel => @compileError(@tagName(ordering) ++ " implies " ++ @tagName(Ordering.Release) ++ " which is only allowed on atomic stores"), | ||
| 38 | .Release => @compileError(@tagName(ordering) ++ " is only allowed on atomic stores"), | ||
| 39 | else => @atomicLoad(T, &self.value, ordering), | ||
| 40 | }; | ||
| 41 | } | ||
| 42 | |||
| 43 | pub fn store(self: *Self, value: T, comptime ordering: Ordering) void { | ||
| 44 | return switch (ordering) { | ||
| 45 | .AcqRel => @compileError(@tagName(ordering) ++ " implies " ++ @tagName(Ordering.Acquire) ++ " which is only allowed on atomic loads"), | ||
| 46 | .Acquire => @compileError(@tagName(ordering) ++ " is only allowed on atomic loads"), | ||
| 47 | else => @atomicStore(T, &self.value, value, ordering), | ||
| 48 | }; | ||
| 49 | } | ||
| 50 | |||
| 51 | pub fn swap(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { | ||
| 52 | return self.rmw(.Xchg, value, ordering); | ||
| 53 | } | ||
| 54 | |||
| 55 | pub fn compareAndSwap( | ||
| 56 | self: *Self, | ||
| 57 | compare: T, | ||
| 58 | exchange: T, | ||
| 59 | comptime success: Ordering, | ||
| 60 | comptime failure: Ordering, | ||
| 61 | ) callconv(.Inline) ?T { | ||
| 62 | return self.cmpxchg(true, compare, exchange, success, failure); | ||
| 63 | } | ||
| 64 | |||
| 65 | pub fn tryCompareAndSwap( | ||
| 66 | self: *Self, | ||
| 67 | compare: T, | ||
| 68 | exchange: T, | ||
| 69 | comptime success: Ordering, | ||
| 70 | comptime failure: Ordering, | ||
| 71 | ) callconv(.Inline) ?T { | ||
| 72 | return self.cmpxchg(false, compare, exchange, success, failure); | ||
| 73 | } | ||
| 74 | |||
| 75 | fn cmpxchg( | ||
| 76 | self: *Self, | ||
| 77 | comptime is_strong: bool, | ||
| 78 | compare: T, | ||
| 79 | exchange: T, | ||
| 80 | comptime success: Ordering, | ||
| 81 | comptime failure: Ordering, | ||
| 82 | ) callconv(.Inline) ?T { | ||
| 83 | if (success == .Unordered or failure == .Unordered) { | ||
| 84 | @compileError(@tagName(Ordering.Unordered) ++ " is only allowed on atomic loads and stores"); | ||
| 85 | } | ||
| 86 | |||
| 87 | comptime var success_is_stronger = switch (failure) { | ||
| 88 | .SeqCst => success == .SeqCst, | ||
| 89 | .AcqRel => @compileError(@tagName(failure) ++ " implies " ++ @tagName(Ordering.Release) ++ " which is only allowed on success"), | ||
| 90 | .Acquire => success == .SeqCst or success == .AcqRel or success == .Acquire, | ||
| 91 | .Release => @compileError(@tagName(failure) ++ " is only allowed on success"), | ||
| 92 | .Monotonic => true, | ||
| 93 | .Unordered => unreachable, | ||
| 94 | }; | ||
| 95 | |||
| 96 | if (!success_is_stronger) { | ||
| 97 | @compileError(@tagName(success) ++ " must be stronger than " ++ @tagName(failure)); | ||
| 98 | } | ||
| 99 | |||
| 100 | return switch (is_strong) { | ||
| 101 | true => @cmpxchgStrong(T, &self.value, compare, exchange, success, failure), | ||
| 102 | false => @cmpxchgWeak(T, &self.value, compare, exchange, success, failure), | ||
| 103 | }; | ||
| 104 | } | ||
| 105 | |||
| 106 | fn rmw( | ||
| 107 | self: *Self, | ||
| 108 | comptime op: std.builtin.AtomicRmwOp, | ||
| 109 | value: T, | ||
| 110 | comptime ordering: Ordering, | ||
| 111 | ) callconv(.Inline) T { | ||
| 112 | return @atomicRmw(T, &self.value, op, value, ordering); | ||
| 113 | } | ||
| 114 | |||
| 115 | fn exportWhen(comptime condition: bool, comptime functions: type) type { | ||
| 116 | return if (condition) functions else struct {}; | ||
| 117 | } | ||
| 118 | |||
| 119 | pub usingnamespace exportWhen(std.meta.trait.isNumber(T), struct { | ||
| 120 | pub fn fetchAdd(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { | ||
| 121 | return self.rmw(.Add, value, ordering); | ||
| 122 | } | ||
| 123 | |||
| 124 | pub fn fetchSub(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { | ||
| 125 | return self.rmw(.Sub, value, ordering); | ||
| 126 | } | ||
| 127 | |||
| 128 | pub fn fetchMin(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { | ||
| 129 | return self.rmw(.Min, value, ordering); | ||
| 130 | } | ||
| 131 | |||
| 132 | pub fn fetchMax(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { | ||
| 133 | return self.rmw(.Max, value, ordering); | ||
| 134 | } | ||
| 135 | }); | ||
| 136 | |||
| 137 | pub usingnamespace exportWhen(std.meta.trait.isIntegral(T), struct { | ||
| 138 | pub fn fetchAnd(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { | ||
| 139 | return self.rmw(.And, value, ordering); | ||
| 140 | } | ||
| 141 | |||
| 142 | pub fn fetchNand(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { | ||
| 143 | return self.rmw(.Nand, value, ordering); | ||
| 144 | } | ||
| 145 | |||
| 146 | pub fn fetchOr(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { | ||
| 147 | return self.rmw(.Or, value, ordering); | ||
| 148 | } | ||
| 149 | |||
| 150 | pub fn fetchXor(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { | ||
| 151 | return self.rmw(.Xor, value, ordering); | ||
| 152 | } | ||
| 153 | |||
| 154 | const Bit = std.math.Log2Int(T); | ||
| 155 | const BitRmwOp = enum { | ||
| 156 | Set, | ||
| 157 | Reset, | ||
| 158 | Toggle, | ||
| 159 | }; | ||
| 160 | |||
| 161 | pub fn bitSet(self: *Self, bit: Bit, comptime ordering: Ordering) callconv(.Inline) u1 { | ||
| 162 | return bitRmw(self, .Set, bit, ordering); | ||
| 163 | } | ||
| 164 | |||
| 165 | pub fn bitReset(self: *Self, bit: Bit, comptime ordering: Ordering) callconv(.Inline) u1 { | ||
| 166 | return bitRmw(self, .Reset, bit, ordering); | ||
| 167 | } | ||
| 168 | |||
| 169 | pub fn bitToggle(self: *Self, bit: Bit, comptime ordering: Ordering) callconv(.Inline) u1 { | ||
| 170 | return bitRmw(self, .Toggle, bit, ordering); | ||
| 171 | } | ||
| 172 | |||
| 173 | fn bitRmw( | ||
| 174 | self: *Self, | ||
| 175 | comptime op: BitRmwOp, | ||
| 176 | bit: Bit, | ||
| 177 | comptime ordering: Ordering, | ||
| 178 | ) callconv(.Inline) u1 { | ||
| 179 | // x86 supports dedicated bitwise instructions | ||
| 180 | if (comptime target.cpu.arch.isX86() and @sizeOf(T) >= 2 and @sizeOf(T) <= 8) { | ||
| 181 | const instruction = switch (op) { | ||
| 182 | .Set => "lock bts", | ||
| 183 | .Reset => "lock btr", | ||
| 184 | .Toggle => "lock btc", | ||
| 185 | }; | ||
| 186 | |||
| 187 | const suffix = switch (@sizeOf(T)) { | ||
| 188 | 2 => "w", | ||
| 189 | 4 => "l", | ||
| 190 | 8 => "q", | ||
| 191 | else => @compileError("Invalid atomic type " ++ @typeName(T)), | ||
| 192 | }; | ||
| 193 | |||
| 194 | const old_bit = asm volatile (instruction ++ suffix ++ " %[bit], %[ptr]" | ||
| 195 | : [result] "={@ccc}" (-> u8) // LLVM doesn't support u1 flag register return values | ||
| 196 | : [ptr] "*p" (&self.value), | ||
| 197 | [bit] "X" (@as(T, bit)) | ||
| 198 | : "cc", "memory" | ||
| 199 | ); | ||
| 200 | |||
| 201 | return @intCast(u1, old_bit); | ||
| 202 | } | ||
| 203 | |||
| 204 | const mask = @as(T, 1) << bit; | ||
| 205 | const value = switch (op) { | ||
| 206 | .Set => self.fetchOr(mask, ordering), | ||
| 207 | .Reset => self.fetchAnd(~mask, ordering), | ||
| 208 | .Toggle => self.fetchXor(mask, ordering), | ||
| 209 | }; | ||
| 210 | |||
| 211 | return @boolToInt(value & mask != 0); | ||
| 212 | } | ||
| 213 | }); | ||
| 214 | }; | ||
| 215 | } | ||
| 216 | |||
| 217 | fn atomicIntTypes() []const type { | ||
| 218 | comptime var bytes = 1; | ||
| 219 | comptime var types: []const type = &[_]type{}; | ||
| 220 | inline while (bytes <= @sizeOf(usize)) : (bytes *= 2) { | ||
| 221 | types = types ++ &[_]type{std.meta.Int(.unsigned, bytes * 8)}; | ||
| 222 | } | ||
| 223 | return types; | ||
| 224 | } | ||
| 225 | |||
| 226 | test "Atomic.loadUnchecked" { | ||
| 227 | inline for (atomicIntTypes()) |Int| { | ||
| 228 | var x = Atomic(Int).init(5); | ||
| 229 | try testing.expectEqual(x.loadUnchecked(), 5); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | test "Atomic.storeUnchecked" { | ||
| 234 | inline for (atomicIntTypes()) |Int| { | ||
| 235 | var x = Atomic(usize).init(5); | ||
| 236 | x.storeUnchecked(10); | ||
| 237 | try testing.expectEqual(x.loadUnchecked(), 10); | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | test "Atomic.load" { | ||
| 242 | inline for (atomicIntTypes()) |Int| { | ||
| 243 | inline for (.{ .Unordered, .Monotonic, .Acquire, .SeqCst }) |ordering| { | ||
| 244 | var x = Atomic(Int).init(5); | ||
| 245 | try testing.expectEqual(x.load(ordering), 5); | ||
| 246 | } | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | test "Atomic.store" { | ||
| 251 | inline for (atomicIntTypes()) |Int| { | ||
| 252 | inline for (.{ .Unordered, .Monotonic, .Release, .SeqCst }) |ordering| { | ||
| 253 | var x = Atomic(usize).init(5); | ||
| 254 | x.store(10, ordering); | ||
| 255 | try testing.expectEqual(x.load(.SeqCst), 10); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | const atomic_rmw_orderings = [_]Ordering{ | ||
| 261 | .Monotonic, | ||
| 262 | .Acquire, | ||
| 263 | .Release, | ||
| 264 | .AcqRel, | ||
| 265 | .SeqCst, | ||
| 266 | }; | ||
| 267 | |||
| 268 | test "Atomic.swap" { | ||
| 269 | inline for (atomic_rmw_orderings) |ordering| { | ||
| 270 | var x = Atomic(usize).init(5); | ||
| 271 | try testing.expectEqual(x.swap(10, ordering), 5); | ||
| 272 | try testing.expectEqual(x.load(.SeqCst), 10); | ||
| 273 | |||
| 274 | var y = Atomic(enum(usize) { a, b, c }).init(.c); | ||
| 275 | try testing.expectEqual(y.swap(.a, ordering), .c); | ||
| 276 | try testing.expectEqual(y.load(.SeqCst), .a); | ||
| 277 | |||
| 278 | var z = Atomic(f32).init(5.0); | ||
| 279 | try testing.expectEqual(z.swap(10.0, ordering), 5.0); | ||
| 280 | try testing.expectEqual(z.load(.SeqCst), 10.0); | ||
| 281 | |||
| 282 | var a = Atomic(bool).init(false); | ||
| 283 | try testing.expectEqual(a.swap(true, ordering), false); | ||
| 284 | try testing.expectEqual(a.load(.SeqCst), true); | ||
| 285 | |||
| 286 | var b = Atomic(?*u8).init(null); | ||
| 287 | try testing.expectEqual(b.swap(@intToPtr(?*u8, @alignOf(u8)), ordering), null); | ||
| 288 | try testing.expectEqual(b.load(.SeqCst), @intToPtr(?*u8, @alignOf(u8))); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | const atomic_cmpxchg_orderings = [_][2]Ordering{ | ||
| 293 | .{ .Monotonic, .Monotonic }, | ||
| 294 | .{ .Acquire, .Monotonic }, | ||
| 295 | .{ .Acquire, .Acquire }, | ||
| 296 | .{ .Release, .Monotonic }, | ||
| 297 | // Although accepted by LLVM, acquire failure implies AcqRel success | ||
| 298 | // .{ .Release, .Acquire }, | ||
| 299 | .{ .AcqRel, .Monotonic }, | ||
| 300 | .{ .AcqRel, .Acquire }, | ||
| 301 | .{ .SeqCst, .Monotonic }, | ||
| 302 | .{ .SeqCst, .Acquire }, | ||
| 303 | .{ .SeqCst, .SeqCst }, | ||
| 304 | }; | ||
| 305 | |||
| 306 | test "Atomic.compareAndSwap" { | ||
| 307 | inline for (atomicIntTypes()) |Int| { | ||
| 308 | inline for (atomic_cmpxchg_orderings) |ordering| { | ||
| 309 | var x = Atomic(Int).init(0); | ||
| 310 | try testing.expectEqual(x.compareAndSwap(1, 0, ordering[0], ordering[1]), 0); | ||
| 311 | try testing.expectEqual(x.load(.SeqCst), 0); | ||
| 312 | try testing.expectEqual(x.compareAndSwap(0, 1, ordering[0], ordering[1]), null); | ||
| 313 | try testing.expectEqual(x.load(.SeqCst), 1); | ||
| 314 | try testing.expectEqual(x.compareAndSwap(1, 0, ordering[0], ordering[1]), null); | ||
| 315 | try testing.expectEqual(x.load(.SeqCst), 0); | ||
| 316 | } | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | test "Atomic.tryCompareAndSwap" { | ||
| 321 | inline for (atomicIntTypes()) |Int| { | ||
| 322 | inline for (atomic_cmpxchg_orderings) |ordering| { | ||
| 323 | var x = Atomic(Int).init(0); | ||
| 324 | |||
| 325 | try testing.expectEqual(x.tryCompareAndSwap(1, 0, ordering[0], ordering[1]), 0); | ||
| 326 | try testing.expectEqual(x.load(.SeqCst), 0); | ||
| 327 | |||
| 328 | while (x.tryCompareAndSwap(0, 1, ordering[0], ordering[1])) |_| {} | ||
| 329 | try testing.expectEqual(x.load(.SeqCst), 1); | ||
| 330 | |||
| 331 | while (x.tryCompareAndSwap(1, 0, ordering[0], ordering[1])) |_| {} | ||
| 332 | try testing.expectEqual(x.load(.SeqCst), 0); | ||
| 333 | } | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | test "Atomic.fetchAdd" { | ||
| 338 | inline for (atomicIntTypes()) |Int| { | ||
| 339 | inline for (atomic_rmw_orderings) |ordering| { | ||
| 340 | var x = Atomic(Int).init(5); | ||
| 341 | try testing.expectEqual(x.fetchAdd(5, ordering), 5); | ||
| 342 | try testing.expectEqual(x.load(.SeqCst), 10); | ||
| 343 | try testing.expectEqual(x.fetchAdd(std.math.maxInt(Int), ordering), 10); | ||
| 344 | try testing.expectEqual(x.load(.SeqCst), 9); | ||
| 345 | } | ||
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 349 | test "Atomic.fetchSub" { | ||
| 350 | inline for (atomicIntTypes()) |Int| { | ||
| 351 | inline for (atomic_rmw_orderings) |ordering| { | ||
| 352 | var x = Atomic(Int).init(5); | ||
| 353 | try testing.expectEqual(x.fetchSub(5, ordering), 5); | ||
| 354 | try testing.expectEqual(x.load(.SeqCst), 0); | ||
| 355 | try testing.expectEqual(x.fetchSub(1, ordering), 0); | ||
| 356 | try testing.expectEqual(x.load(.SeqCst), std.math.maxInt(Int)); | ||
| 357 | } | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | test "Atomic.fetchMin" { | ||
| 362 | inline for (atomicIntTypes()) |Int| { | ||
| 363 | inline for (atomic_rmw_orderings) |ordering| { | ||
| 364 | var x = Atomic(Int).init(5); | ||
| 365 | try testing.expectEqual(x.fetchMin(0, ordering), 5); | ||
| 366 | try testing.expectEqual(x.load(.SeqCst), 0); | ||
| 367 | try testing.expectEqual(x.fetchMin(10, ordering), 0); | ||
| 368 | try testing.expectEqual(x.load(.SeqCst), 0); | ||
| 369 | } | ||
| 370 | } | ||
| 371 | } | ||
| 372 | |||
| 373 | test "Atomic.fetchMax" { | ||
| 374 | inline for (atomicIntTypes()) |Int| { | ||
| 375 | inline for (atomic_rmw_orderings) |ordering| { | ||
| 376 | var x = Atomic(Int).init(5); | ||
| 377 | try testing.expectEqual(x.fetchMax(10, ordering), 5); | ||
| 378 | try testing.expectEqual(x.load(.SeqCst), 10); | ||
| 379 | try testing.expectEqual(x.fetchMax(5, ordering), 10); | ||
| 380 | try testing.expectEqual(x.load(.SeqCst), 10); | ||
| 381 | } | ||
| 382 | } | ||
| 383 | } | ||
| 384 | |||
| 385 | test "Atomic.fetchAnd" { | ||
| 386 | inline for (atomicIntTypes()) |Int| { | ||
| 387 | inline for (atomic_rmw_orderings) |ordering| { | ||
| 388 | var x = Atomic(Int).init(0b11); | ||
| 389 | try testing.expectEqual(x.fetchAnd(0b10, ordering), 0b11); | ||
| 390 | try testing.expectEqual(x.load(.SeqCst), 0b10); | ||
| 391 | try testing.expectEqual(x.fetchAnd(0b00, ordering), 0b10); | ||
| 392 | try testing.expectEqual(x.load(.SeqCst), 0b00); | ||
| 393 | } | ||
| 394 | } | ||
| 395 | } | ||
| 396 | |||
| 397 | test "Atomic.fetchNand" { | ||
| 398 | inline for (atomicIntTypes()) |Int| { | ||
| 399 | inline for (atomic_rmw_orderings) |ordering| { | ||
| 400 | var x = Atomic(Int).init(0b11); | ||
| 401 | try testing.expectEqual(x.fetchNand(0b10, ordering), 0b11); | ||
| 402 | try testing.expectEqual(x.load(.SeqCst), ~@as(Int, 0b10)); | ||
| 403 | try testing.expectEqual(x.fetchNand(0b00, ordering), ~@as(Int, 0b10)); | ||
| 404 | try testing.expectEqual(x.load(.SeqCst), ~@as(Int, 0b00)); | ||
| 405 | } | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | test "Atomic.fetchOr" { | ||
| 410 | inline for (atomicIntTypes()) |Int| { | ||
| 411 | inline for (atomic_rmw_orderings) |ordering| { | ||
| 412 | var x = Atomic(Int).init(0b11); | ||
| 413 | try testing.expectEqual(x.fetchOr(0b100, ordering), 0b11); | ||
| 414 | try testing.expectEqual(x.load(.SeqCst), 0b111); | ||
| 415 | try testing.expectEqual(x.fetchOr(0b010, ordering), 0b111); | ||
| 416 | try testing.expectEqual(x.load(.SeqCst), 0b111); | ||
| 417 | } | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 421 | test "Atomic.fetchXor" { | ||
| 422 | inline for (atomicIntTypes()) |Int| { | ||
| 423 | inline for (atomic_rmw_orderings) |ordering| { | ||
| 424 | var x = Atomic(Int).init(0b11); | ||
| 425 | try testing.expectEqual(x.fetchXor(0b10, ordering), 0b11); | ||
| 426 | try testing.expectEqual(x.load(.SeqCst), 0b01); | ||
| 427 | try testing.expectEqual(x.fetchXor(0b01, ordering), 0b01); | ||
| 428 | try testing.expectEqual(x.load(.SeqCst), 0b00); | ||
| 429 | } | ||
| 430 | } | ||
| 431 | } | ||
| 432 | |||
| 433 | test "Atomic.bitSet" { | ||
| 434 | inline for (atomicIntTypes()) |Int| { | ||
| 435 | inline for (atomic_rmw_orderings) |ordering| { | ||
| 436 | var x = Atomic(Int).init(0); | ||
| 437 | const bit_array = @as([std.meta.bitCount(Int)]void, undefined); | ||
| 438 | |||
| 439 | for (bit_array) |_, bit_index| { | ||
| 440 | const bit = @intCast(std.math.Log2Int(Int), bit_index); | ||
| 441 | const mask = @as(Int, 1) << bit; | ||
| 442 | |||
| 443 | // setting the bit should change the bit | ||
| 444 | try testing.expect(x.load(.SeqCst) & mask == 0); | ||
| 445 | try testing.expectEqual(x.bitSet(bit, ordering), 0); | ||
| 446 | try testing.expect(x.load(.SeqCst) & mask != 0); | ||
| 447 | |||
| 448 | // setting it again shouldn't change the bit | ||
| 449 | try testing.expectEqual(x.bitSet(bit, ordering), 1); | ||
| 450 | try testing.expect(x.load(.SeqCst) & mask != 0); | ||
| 451 | |||
| 452 | // all the previous bits should have not changed (still be set) | ||
| 453 | for (bit_array[0..bit_index]) |_, prev_bit_index| { | ||
| 454 | const prev_bit = @intCast(std.math.Log2Int(Int), prev_bit_index); | ||
| 455 | const prev_mask = @as(Int, 1) << prev_bit; | ||
| 456 | try testing.expect(x.load(.SeqCst) & prev_mask != 0); | ||
| 457 | } | ||
| 458 | } | ||
| 459 | } | ||
| 460 | } | ||
| 461 | } | ||
| 462 | |||
| 463 | test "Atomic.bitReset" { | ||
| 464 | inline for (atomicIntTypes()) |Int| { | ||
| 465 | inline for (atomic_rmw_orderings) |ordering| { | ||
| 466 | var x = Atomic(Int).init(0); | ||
| 467 | const bit_array = @as([std.meta.bitCount(Int)]void, undefined); | ||
| 468 | |||
| 469 | for (bit_array) |_, bit_index| { | ||
| 470 | const bit = @intCast(std.math.Log2Int(Int), bit_index); | ||
| 471 | const mask = @as(Int, 1) << bit; | ||
| 472 | x.storeUnchecked(x.loadUnchecked() | mask); | ||
| 473 | |||
| 474 | // unsetting the bit should change the bit | ||
| 475 | try testing.expect(x.load(.SeqCst) & mask != 0); | ||
| 476 | try testing.expectEqual(x.bitReset(bit, ordering), 1); | ||
| 477 | try testing.expect(x.load(.SeqCst) & mask == 0); | ||
| 478 | |||
| 479 | // unsetting it again shouldn't change the bit | ||
| 480 | try testing.expectEqual(x.bitReset(bit, ordering), 0); | ||
| 481 | try testing.expect(x.load(.SeqCst) & mask == 0); | ||
| 482 | |||
| 483 | // all the previous bits should have not changed (still be reset) | ||
| 484 | for (bit_array[0..bit_index]) |_, prev_bit_index| { | ||
| 485 | const prev_bit = @intCast(std.math.Log2Int(Int), prev_bit_index); | ||
| 486 | const prev_mask = @as(Int, 1) << prev_bit; | ||
| 487 | try testing.expect(x.load(.SeqCst) & prev_mask == 0); | ||
| 488 | } | ||
| 489 | } | ||
| 490 | } | ||
| 491 | } | ||
| 492 | } | ||
| 493 | |||
| 494 | test "Atomic.bitToggle" { | ||
| 495 | inline for (atomicIntTypes()) |Int| { | ||
| 496 | inline for (atomic_rmw_orderings) |ordering| { | ||
| 497 | var x = Atomic(Int).init(0); | ||
| 498 | const bit_array = @as([std.meta.bitCount(Int)]void, undefined); | ||
| 499 | |||
| 500 | for (bit_array) |_, bit_index| { | ||
| 501 | const bit = @intCast(std.math.Log2Int(Int), bit_index); | ||
| 502 | const mask = @as(Int, 1) << bit; | ||
| 503 | |||
| 504 | // toggling the bit should change the bit | ||
| 505 | try testing.expect(x.load(.SeqCst) & mask == 0); | ||
| 506 | try testing.expectEqual(x.bitToggle(bit, ordering), 0); | ||
| 507 | try testing.expect(x.load(.SeqCst) & mask != 0); | ||
| 508 | |||
| 509 | // toggling it again *should* change the bit | ||
| 510 | try testing.expectEqual(x.bitToggle(bit, ordering), 1); | ||
| 511 | try testing.expect(x.load(.SeqCst) & mask == 0); | ||
| 512 | |||
| 513 | // all the previous bits should have not changed (still be toggled back) | ||
| 514 | for (bit_array[0..bit_index]) |_, prev_bit_index| { | ||
| 515 | const prev_bit = @intCast(std.math.Log2Int(Int), prev_bit_index); | ||
| 516 | const prev_mask = @as(Int, 1) << prev_bit; | ||
| 517 | try testing.expect(x.load(.SeqCst) & prev_mask == 0); | ||
| 518 | } | ||
| 519 | } | ||
| 520 | } | ||
| 521 | } | ||
| 522 | } | ||
lib/std/atomic/bool.zig deleted-55| ... | @@ -1,55 +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 | |||
| 7 | const std = @import("std"); | ||
| 8 | const builtin = std.builtin; | ||
| 9 | const testing = std.testing; | ||
| 10 | |||
| 11 | /// Thread-safe, lock-free boolean | ||
| 12 | pub const Bool = extern struct { | ||
| 13 | unprotected_value: bool, | ||
| 14 | |||
| 15 | pub const Self = @This(); | ||
| 16 | |||
| 17 | pub fn init(init_val: bool) Self { | ||
| 18 | return Self{ .unprotected_value = init_val }; | ||
| 19 | } | ||
| 20 | |||
| 21 | // xchg is only valid rmw operation for a bool | ||
| 22 | /// Atomically modifies memory and then returns the previous value. | ||
| 23 | pub fn xchg(self: *Self, operand: bool, comptime ordering: std.builtin.AtomicOrder) bool { | ||
| 24 | switch (ordering) { | ||
| 25 | .Monotonic, .Acquire, .Release, .AcqRel, .SeqCst => {}, | ||
| 26 | else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a RMW operation"), | ||
| 27 | } | ||
| 28 | return @atomicRmw(bool, &self.unprotected_value, .Xchg, operand, ordering); | ||
| 29 | } | ||
| 30 | |||
| 31 | pub fn load(self: *const Self, comptime ordering: std.builtin.AtomicOrder) bool { | ||
| 32 | switch (ordering) { | ||
| 33 | .Unordered, .Monotonic, .Acquire, .SeqCst => {}, | ||
| 34 | else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a load operation"), | ||
| 35 | } | ||
| 36 | return @atomicLoad(bool, &self.unprotected_value, ordering); | ||
| 37 | } | ||
| 38 | |||
| 39 | pub fn store(self: *Self, value: bool, comptime ordering: std.builtin.AtomicOrder) void { | ||
| 40 | switch (ordering) { | ||
| 41 | .Unordered, .Monotonic, .Release, .SeqCst => {}, | ||
| 42 | else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a store operation"), | ||
| 43 | } | ||
| 44 | @atomicStore(bool, &self.unprotected_value, value, ordering); | ||
| 45 | } | ||
| 46 | }; | ||
| 47 | |||
| 48 | test "std.atomic.Bool" { | ||
| 49 | var a = Bool.init(false); | ||
| 50 | try testing.expectEqual(false, a.xchg(false, .SeqCst)); | ||
| 51 | try testing.expectEqual(false, a.load(.SeqCst)); | ||
| 52 | a.store(true, .SeqCst); | ||
| 53 | try testing.expectEqual(true, a.xchg(false, .SeqCst)); | ||
| 54 | try testing.expectEqual(false, a.load(.SeqCst)); | ||
| 55 | } | ||
lib/std/atomic/int.zig deleted-92| ... | @@ -1,92 +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 | |||
| 7 | const std = @import("std"); | ||
| 8 | const builtin = std.builtin; | ||
| 9 | const testing = std.testing; | ||
| 10 | |||
| 11 | /// Thread-safe, lock-free integer | ||
| 12 | pub fn Int(comptime T: type) type { | ||
| 13 | if (!std.meta.trait.isIntegral(T)) | ||
| 14 | @compileError("Expected integral type, got '" ++ @typeName(T) ++ "'"); | ||
| 15 | |||
| 16 | return extern struct { | ||
| 17 | unprotected_value: T, | ||
| 18 | |||
| 19 | pub const Self = @This(); | ||
| 20 | |||
| 21 | pub fn init(init_val: T) Self { | ||
| 22 | return Self{ .unprotected_value = init_val }; | ||
| 23 | } | ||
| 24 | |||
| 25 | /// Read, Modify, Write | ||
| 26 | pub fn rmw(self: *Self, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) T { | ||
| 27 | switch (ordering) { | ||
| 28 | .Monotonic, .Acquire, .Release, .AcqRel, .SeqCst => {}, | ||
| 29 | else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a RMW operation"), | ||
| 30 | } | ||
| 31 | return @atomicRmw(T, &self.unprotected_value, op, operand, ordering); | ||
| 32 | } | ||
| 33 | |||
| 34 | pub fn load(self: *const Self, comptime ordering: builtin.AtomicOrder) T { | ||
| 35 | switch (ordering) { | ||
| 36 | .Unordered, .Monotonic, .Acquire, .SeqCst => {}, | ||
| 37 | else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a load operation"), | ||
| 38 | } | ||
| 39 | return @atomicLoad(T, &self.unprotected_value, ordering); | ||
| 40 | } | ||
| 41 | |||
| 42 | pub fn store(self: *Self, value: T, comptime ordering: builtin.AtomicOrder) void { | ||
| 43 | switch (ordering) { | ||
| 44 | .Unordered, .Monotonic, .Release, .SeqCst => {}, | ||
| 45 | else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a store operation"), | ||
| 46 | } | ||
| 47 | @atomicStore(T, &self.unprotected_value, value, ordering); | ||
| 48 | } | ||
| 49 | |||
| 50 | /// Twos complement wraparound increment | ||
| 51 | /// Returns previous value | ||
| 52 | pub fn incr(self: *Self) T { | ||
| 53 | return self.rmw(.Add, 1, .SeqCst); | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Twos complement wraparound decrement | ||
| 57 | /// Returns previous value | ||
| 58 | pub fn decr(self: *Self) T { | ||
| 59 | return self.rmw(.Sub, 1, .SeqCst); | ||
| 60 | } | ||
| 61 | |||
| 62 | pub fn get(self: *const Self) T { | ||
| 63 | return self.load(.SeqCst); | ||
| 64 | } | ||
| 65 | |||
| 66 | pub fn set(self: *Self, new_value: T) void { | ||
| 67 | self.store(new_value, .SeqCst); | ||
| 68 | } | ||
| 69 | |||
| 70 | pub fn xchg(self: *Self, new_value: T) T { | ||
| 71 | return self.rmw(.Xchg, new_value, .SeqCst); | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Twos complement wraparound add | ||
| 75 | /// Returns previous value | ||
| 76 | pub fn fetchAdd(self: *Self, op: T) T { | ||
| 77 | return self.rmw(.Add, op, .SeqCst); | ||
| 78 | } | ||
| 79 | }; | ||
| 80 | } | ||
| 81 | |||
| 82 | test "std.atomic.Int" { | ||
| 83 | var a = Int(u8).init(0); | ||
| 84 | try testing.expectEqual(@as(u8, 0), a.incr()); | ||
| 85 | try testing.expectEqual(@as(u8, 1), a.load(.SeqCst)); | ||
| 86 | a.store(42, .SeqCst); | ||
| 87 | try testing.expectEqual(@as(u8, 42), a.decr()); | ||
| 88 | try testing.expectEqual(@as(u8, 41), a.xchg(100)); | ||
| 89 | try testing.expectEqual(@as(u8, 100), a.fetchAdd(5)); | ||
| 90 | try testing.expectEqual(@as(u8, 105), a.get()); | ||
| 91 | a.set(200); | ||
| 92 | } | ||
lib/std/json.zig+4-1| ... | @@ -2111,7 +2111,10 @@ test "parse into struct with duplicate field" { | ... | @@ -2111,7 +2111,10 @@ test "parse into struct with duplicate field" { |
| 2111 | const ballast = try testing.allocator.alloc(u64, 1); | 2111 | const ballast = try testing.allocator.alloc(u64, 1); |
| 2112 | defer testing.allocator.free(ballast); | 2112 | defer testing.allocator.free(ballast); |
| 2113 | 2113 | ||
| 2114 | const options_first = ParseOptions{ .allocator = testing.allocator, .duplicate_field_behavior = .UseFirst }; | 2114 | const options_first = ParseOptions{ |
| 2115 | .allocator = testing.allocator, | ||
| 2116 | .duplicate_field_behavior = .UseFirst, | ||
| 2117 | }; | ||
| 2115 | 2118 | ||
| 2116 | const options_last = ParseOptions{ | 2119 | const options_last = ParseOptions{ |
| 2117 | .allocator = testing.allocator, | 2120 | .allocator = testing.allocator, |
lib/std/os.zig+1-1| ... | @@ -5534,7 +5534,7 @@ pub const CopyFileRangeError = error{ | ... | @@ -5534,7 +5534,7 @@ pub const CopyFileRangeError = error{ |
| 5534 | 5534 | ||
| 5535 | var has_copy_file_range_syscall = init: { | 5535 | var has_copy_file_range_syscall = init: { |
| 5536 | const kernel_has_syscall = std.Target.current.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) orelse true; | 5536 | const kernel_has_syscall = std.Target.current.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) orelse true; |
| 5537 | break :init std.atomic.Bool.init(kernel_has_syscall); | 5537 | break :init std.atomic.Atomic(bool).init(kernel_has_syscall); |
| 5538 | }; | 5538 | }; |
| 5539 | 5539 | ||
| 5540 | /// Transfer data between file descriptors at specified offsets. | 5540 | /// Transfer data between file descriptors at specified offsets. |
lib/std/packed_int_array.zig+3-3| ... | @@ -379,9 +379,9 @@ test "PackedIntArray" { | ... | @@ -379,9 +379,9 @@ test "PackedIntArray" { |
| 379 | } | 379 | } |
| 380 | 380 | ||
| 381 | test "PackedIntIo" { | 381 | test "PackedIntIo" { |
| 382 | const bytes = [_]u8 { 0b01101_000, 0b01011_110, 0b00011_101 }; | 382 | const bytes = [_]u8{ 0b01101_000, 0b01011_110, 0b00011_101 }; |
| 383 | try testing.expectEqual(@as(u15, 0x2bcd), PackedIntIo(u15, .Little).get(&bytes, 0, 3)); | 383 | try testing.expectEqual(@as(u15, 0x2bcd), PackedIntIo(u15, .Little).get(&bytes, 0, 3)); |
| 384 | try testing.expectEqual(@as(u16, 0xabcd), PackedIntIo(u16, .Little).get(&bytes, 0, 3)); | 384 | try testing.expectEqual(@as(u16, 0xabcd), PackedIntIo(u16, .Little).get(&bytes, 0, 3)); |
| 385 | try testing.expectEqual(@as(u17, 0x1abcd), PackedIntIo(u17, .Little).get(&bytes, 0, 3)); | 385 | try testing.expectEqual(@as(u17, 0x1abcd), PackedIntIo(u17, .Little).get(&bytes, 0, 3)); |
| 386 | try testing.expectEqual(@as(u18, 0x3abcd), PackedIntIo(u18, .Little).get(&bytes, 0, 3)); | 386 | try testing.expectEqual(@as(u18, 0x3abcd), PackedIntIo(u18, .Little).get(&bytes, 0, 3)); |
| 387 | } | 387 | } |
lib/std/target.zig+7| ... | @@ -767,6 +767,13 @@ pub const Target = struct { | ... | @@ -767,6 +767,13 @@ pub const Target = struct { |
| 767 | spirv32, | 767 | spirv32, |
| 768 | spirv64, | 768 | spirv64, |
| 769 | 769 | ||
| 770 | pub fn isX86(arch: Arch) bool { | ||
| 771 | return switch (arch) { | ||
| 772 | .i386, .x86_64 => true, | ||
| 773 | else => false, | ||
| 774 | }; | ||
| 775 | } | ||
| 776 | |||
| 770 | pub fn isARM(arch: Arch) bool { | 777 | pub fn isARM(arch: Arch) bool { |
| 771 | return switch (arch) { | 778 | return switch (arch) { |
| 772 | .arm, .armeb => true, | 779 | .arm, .armeb => true, |
src/BuiltinFn.zig+1-1| ... | @@ -400,7 +400,7 @@ pub const list = list: { | ... | @@ -400,7 +400,7 @@ pub const list = list: { |
| 400 | "@fence", | 400 | "@fence", |
| 401 | .{ | 401 | .{ |
| 402 | .tag = .fence, | 402 | .tag = .fence, |
| 403 | .param_count = 0, | 403 | .param_count = 1, |
| 404 | }, | 404 | }, |
| 405 | }, | 405 | }, |
| 406 | .{ | 406 | .{ |