authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-21 15:17:12-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-23 13:36:21-08:00
log028af97df46f1b856047cc146739cc5066e65bab
treee736bf194aa735c9c94379ef57f8ade9da2cdfa7
parent829c00a77fd2d6b7576c6d2b724f69ba9cfe10f2

std.ResetEvent: use sem_t when linking against pthreads


3 files changed, 100 insertions(+), 89 deletions(-)

lib/std/c.zig+6
...@@ -270,6 +270,12 @@ pub extern "c" fn pthread_atfork(...@@ -270,6 +270,12 @@ pub extern "c" fn pthread_atfork(
270 parent: ?fn () callconv(.C) void,270 parent: ?fn () callconv(.C) void,
271 child: ?fn () callconv(.C) void,271 child: ?fn () callconv(.C) void,
272) c_int;272) c_int;
273pub extern "c" fn sem_init(sem: *sem_t, pshared: c_int, value: c_uint) c_int;
274pub extern "c" fn sem_destroy(sem: *sem_t) c_int;
275pub extern "c" fn sem_post(sem: *sem_t) c_int;
276pub extern "c" fn sem_wait(sem: *sem_t) c_int;
277pub extern "c" fn sem_trywait(sem: *sem_t) c_int;
278pub extern "c" fn sem_timedwait(sem: *sem_t, abs_timeout: *const timespec) c_int;
273279
274pub extern "c" fn kqueue() c_int;280pub extern "c" fn kqueue() c_int;
275pub extern "c" fn kevent(281pub extern "c" fn kevent(
lib/std/c/linux.zig+32
...@@ -135,6 +135,38 @@ const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os.tag == .fuchsia) 40 else switch...@@ -135,6 +135,38 @@ const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os.tag == .fuchsia) 40 else switch
135 else => unreachable,135 else => unreachable,
136};136};
137137
138pub const sem_t = switch (builtin.abi) {
139 .musl, .musleabi, .musleabihf => extern struct {
140 __val: [4 * @sizeOf(c_long) / @sizeOf(c_int)]c_int,
141
142 pub fn init(pshared: c_int, value: c_uint) @This() {
143 var result: @This() = undefined;
144 result.__val[0] = @bitCast(c_int, value);
145 result.__val[1] = 0;
146 result.__val[2] = if (pshared != 0) 0 else 128;
147 return result;
148 }
149 },
150 .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => extern struct {
151 __lock: c_int,
152 __queue: ?*pthread_t,
153 __pshared: c_int,
154 __value: c_int,
155 __data: ?*c_void,
156
157 pub fn init(pshared: c_int, value: c_uint) @This() {
158 return .{
159 .__lock = 0,
160 .__queue = null,
161 .__pshared = pshared,
162 .__value = @bitCast(c_int, value),
163 .__data = null,
164 };
165 }
166 },
167 else => unreachable,
168};
169
138pub const RTLD_LAZY = 1;170pub const RTLD_LAZY = 1;
139pub const RTLD_NOW = 2;171pub const RTLD_NOW = 2;
140pub const RTLD_NOLOAD = 4;172pub const RTLD_NOLOAD = 4;
lib/std/reset_event.zig+62-89
...@@ -21,7 +21,7 @@ pub const ResetEvent = struct {...@@ -21,7 +21,7 @@ pub const ResetEvent = struct {
2121
22 pub const OsEvent = if (builtin.single_threaded)22 pub const OsEvent = if (builtin.single_threaded)
23 DebugEvent23 DebugEvent
24 else if (builtin.link_libc and builtin.os.tag != .windows and builtin.os.tag != .linux)24 else if (std.Thread.use_pthreads)
25 PosixEvent25 PosixEvent
26 else26 else
27 AtomicEvent;27 AtomicEvent;
...@@ -34,11 +34,6 @@ pub const ResetEvent = struct {...@@ -34,11 +34,6 @@ pub const ResetEvent = struct {
34 self.os_event.deinit();34 self.os_event.deinit();
35 }35 }
3636
37 /// Returns whether or not the event is currenetly set
38 pub fn isSet(self: *ResetEvent) bool {
39 return self.os_event.isSet();
40 }
41
42 /// Sets the event if not already set and37 /// Sets the event if not already set and
43 /// wakes up all the threads waiting on the event.38 /// wakes up all the threads waiting on the event.
44 pub fn set(self: *ResetEvent) void {39 pub fn set(self: *ResetEvent) void {
...@@ -46,20 +41,28 @@ pub const ResetEvent = struct {...@@ -46,20 +41,28 @@ pub const ResetEvent = struct {
46 }41 }
4742
48 /// Resets the event to its original, unset state.43 /// Resets the event to its original, unset state.
44 /// TODO improve these docs:
45 /// * under what circumstances does it make sense to call this function?
49 pub fn reset(self: *ResetEvent) void {46 pub fn reset(self: *ResetEvent) void {
50 return self.os_event.reset();47 return self.os_event.reset();
51 }48 }
5249
53 /// Wait for the event to be set by blocking the current thread.50 /// Wait for the event to be set by blocking the current thread.
51 /// TODO improve these docs:
52 /// * is the function thread-safe?
53 /// * does it have suprious wakeups?
54 pub fn wait(self: *ResetEvent) void {54 pub fn wait(self: *ResetEvent) void {
55 return self.os_event.wait(null) catch unreachable;55 return self.os_event.wait();
56 }56 }
5757
58 /// Wait for the event to be set by blocking the current thread.58 /// Wait for the event to be set by blocking the current thread.
59 /// A timeout in nanoseconds can be provided as a hint for how59 /// A timeout in nanoseconds can be provided as a hint for how
60 /// long the thread should block on the unset event before throwing error.TimedOut.60 /// long the thread should block on the unset event before throwing error.TimedOut.
61 /// TODO improve these docs:
62 /// * is the function thread-safe?
63 /// * does it have suprious wakeups?
61 pub fn timedWait(self: *ResetEvent, timeout_ns: u64) !void {64 pub fn timedWait(self: *ResetEvent, timeout_ns: u64) !void {
62 return self.os_event.wait(timeout_ns);65 return self.os_event.timedWait(timeout_ns);
63 }66 }
64};67};
6568
...@@ -74,10 +77,6 @@ const DebugEvent = struct {...@@ -74,10 +77,6 @@ const DebugEvent = struct {
74 self.* = undefined;77 self.* = undefined;
75 }78 }
7679
77 fn isSet(self: *DebugEvent) bool {
78 return self.is_set;
79 }
80
81 fn reset(self: *DebugEvent) void {80 fn reset(self: *DebugEvent) void {
82 self.is_set = false;81 self.is_set = false;
83 }82 }
...@@ -86,101 +85,75 @@ const DebugEvent = struct {...@@ -86,101 +85,75 @@ const DebugEvent = struct {
86 self.is_set = true;85 self.is_set = true;
87 }86 }
8887
89 fn wait(self: *DebugEvent, timeout: ?u64) !void {88 fn wait(self: *DebugEvent) void {
90 if (self.is_set)89 if (self.is_set)
91 return;90 return;
92 if (timeout != null)91
93 return error.TimedOut;
94 @panic("deadlock detected");92 @panic("deadlock detected");
95 }93 }
94
95 fn timedWait(self: *DebugEvent, timeout: u64) !void {
96 if (self.is_set)
97 return;
98
99 return error.TimedOut;
100 }
96};101};
97102
98const PosixEvent = struct {103const PosixEvent = struct {
99 is_set: bool,104 sem: c.sem_t,
100 cond: c.pthread_cond_t,
101 mutex: c.pthread_mutex_t,
102105
103 fn init() PosixEvent {106 fn init() PosixEvent {
104 return PosixEvent{107 return PosixEvent{
105 .is_set = false,108 .sem = c.sem_t.init(0, 0),
106 .cond = c.PTHREAD_COND_INITIALIZER,
107 .mutex = c.PTHREAD_MUTEX_INITIALIZER,
108 };109 };
109 }110 }
110111
111 fn deinit(self: *PosixEvent) void {112 fn deinit(self: *PosixEvent) void {
112 // on dragonfly or openbsd, *destroy() functions can return EINVAL113 assert(c.sem_destroy(&self.sem) == 0);
113 // for statically initialized pthread structures
114 const err = if (builtin.os.tag == .dragonfly or builtin.os.tag == .openbsd)
115 os.EINVAL
116 else
117 0;
118
119 const retm = c.pthread_mutex_destroy(&self.mutex);
120 assert(retm == 0 or retm == err);
121 const retc = c.pthread_cond_destroy(&self.cond);
122 assert(retc == 0 or retc == err);
123 }
124
125 fn isSet(self: *PosixEvent) bool {
126 assert(c.pthread_mutex_lock(&self.mutex) == 0);
127 defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
128
129 return self.is_set;
130 }114 }
131115
132 fn reset(self: *PosixEvent) void {116 fn reset(self: *PosixEvent) void {
133 assert(c.pthread_mutex_lock(&self.mutex) == 0);117 self.deinit();
134 defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);118 assert(c.sem_init(&self.sem, 0, 0) == 0);
135
136 self.is_set = false;
137 }119 }
138120
139 fn set(self: *PosixEvent) void {121 fn set(self: *PosixEvent) void {
140 assert(c.pthread_mutex_lock(&self.mutex) == 0);122 assert(c.sem_post(&self.sem) == 0);
141 defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);123 }
142124
143 if (!self.is_set) {125 fn wait(self: *PosixEvent) void {
144 self.is_set = true;126 while (true) {
145 assert(c.pthread_cond_broadcast(&self.cond) == 0);127 switch (c.getErrno(c.sem_wait(&self.sem))) {
128 0 => return,
129 c.EINTR => continue,
130 c.EINVAL => unreachable,
131 else => unreachable,
132 }
146 }133 }
147 }134 }
148135
149 fn wait(self: *PosixEvent, timeout: ?u64) !void {136 fn timedWait(self: *PosixEvent, timeout_ns: u64) !void {
150 assert(c.pthread_mutex_lock(&self.mutex) == 0);
151 defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
152
153 // quick guard before possibly calling time syscalls below
154 if (self.is_set)
155 return;
156
157 var ts: os.timespec = undefined;137 var ts: os.timespec = undefined;
158 if (timeout) |timeout_ns| {138 var timeout_abs = timeout_ns;
159 var timeout_abs = timeout_ns;139 if (comptime std.Target.current.isDarwin()) {
160 if (comptime std.Target.current.isDarwin()) {140 var tv: os.darwin.timeval = undefined;
161 var tv: os.darwin.timeval = undefined;141 assert(os.darwin.gettimeofday(&tv, null) == 0);
162 assert(os.darwin.gettimeofday(&tv, null) == 0);142 timeout_abs += @intCast(u64, tv.tv_sec) * time.ns_per_s;
163 timeout_abs += @intCast(u64, tv.tv_sec) * time.ns_per_s;143 timeout_abs += @intCast(u64, tv.tv_usec) * time.ns_per_us;
164 timeout_abs += @intCast(u64, tv.tv_usec) * time.ns_per_us;144 } else {
165 } else {145 os.clock_gettime(os.CLOCK_REALTIME, &ts) catch return error.TimedOut;
166 os.clock_gettime(os.CLOCK_REALTIME, &ts) catch unreachable;146 timeout_abs += @intCast(u64, ts.tv_sec) * time.ns_per_s;
167 timeout_abs += @intCast(u64, ts.tv_sec) * time.ns_per_s;147 timeout_abs += @intCast(u64, ts.tv_nsec);
168 timeout_abs += @intCast(u64, ts.tv_nsec);
169 }
170 ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), @divFloor(timeout_abs, time.ns_per_s));
171 ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.ns_per_s));
172 }148 }
173149 ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), @divFloor(timeout_abs, time.ns_per_s));
174 while (!self.is_set) {150 ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.ns_per_s));
175 const rc = switch (timeout == null) {151 while (true) {
176 true => c.pthread_cond_wait(&self.cond, &self.mutex),152 switch (c.getErrno(c.sem_timedwait(&self.sem, &ts))) {
177 else => c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts),153 0 => return,
178 };154 c.EINTR => continue,
179 switch (rc) {155 c.EINVAL => unreachable,
180 0 => {},156 c.ETIMEDOUT => return error.TimedOut,
181 os.ETIMEDOUT => return error.TimedOut,
182 os.EINVAL => unreachable,
183 os.EPERM => unreachable,
184 else => unreachable,157 else => unreachable,
185 }158 }
186 }159 }
...@@ -201,10 +174,6 @@ const AtomicEvent = struct {...@@ -201,10 +174,6 @@ const AtomicEvent = struct {
201 self.* = undefined;174 self.* = undefined;
202 }175 }
203176
204 fn isSet(self: *const AtomicEvent) bool {
205 return @atomicLoad(u32, &self.waiters, .Acquire) == WAKE;
206 }
207
208 fn reset(self: *AtomicEvent) void {177 fn reset(self: *AtomicEvent) void {
209 @atomicStore(u32, &self.waiters, 0, .Monotonic);178 @atomicStore(u32, &self.waiters, 0, .Monotonic);
210 }179 }
...@@ -216,7 +185,11 @@ const AtomicEvent = struct {...@@ -216,7 +185,11 @@ const AtomicEvent = struct {
216 }185 }
217 }186 }
218187
219 fn wait(self: *AtomicEvent, timeout: ?u64) !void {188 fn wait(self: *AtomicEvent) void {
189 return self.timedWait(null) catch unreachable;
190 }
191
192 fn timedWait(self: *AtomicEvent, timeout: ?u64) !void {
220 var waiters = @atomicLoad(u32, &self.waiters, .Acquire);193 var waiters = @atomicLoad(u32, &self.waiters, .Acquire);
221 while (waiters != WAKE) {194 while (waiters != WAKE) {
222 waiters = @cmpxchgWeak(u32, &self.waiters, waiters, waiters + WAIT, .Acquire, .Acquire) orelse return Futex.wait(&self.waiters, timeout);195 waiters = @cmpxchgWeak(u32, &self.waiters, waiters, waiters + WAIT, .Acquire, .Acquire) orelse return Futex.wait(&self.waiters, timeout);
...@@ -367,17 +340,17 @@ test "ResetEvent" {...@@ -367,17 +340,17 @@ test "ResetEvent" {
367 defer event.deinit();340 defer event.deinit();
368341
369 // test event setting342 // test event setting
370 testing.expect(event.isSet() == false);
371 event.set();343 event.set();
372 testing.expect(event.isSet() == true);
373344
374 // test event resetting345 // test event resetting
375 event.reset();346 event.reset();
376 testing.expect(event.isSet() == false);
377347
378 // test event waiting (non-blocking)348 // test event waiting (non-blocking)
379 event.set();349 event.set();
380 event.wait();350 event.wait();
351 event.reset();
352
353 event.set();
381 try event.timedWait(1);354 try event.timedWait(1);
382355
383 // test cross-thread signaling356 // test cross-thread signaling