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(
270270 parent: ?fn () callconv(.C) void,
271271 child: ?fn () callconv(.C) void,
272272) 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
274280pub extern "c" fn kqueue() c_int;
275281pub 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
135135 else => unreachable,
136136};
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
138170pub const RTLD_LAZY = 1;
139171pub const RTLD_NOW = 2;
140172pub const RTLD_NOLOAD = 4;
lib/std/reset_event.zig+62-89
......@@ -21,7 +21,7 @@ pub const ResetEvent = struct {
2121
2222 pub const OsEvent = if (builtin.single_threaded)
2323 DebugEvent
24 else if (builtin.link_libc and builtin.os.tag != .windows and builtin.os.tag != .linux)
24 else if (std.Thread.use_pthreads)
2525 PosixEvent
2626 else
2727 AtomicEvent;
......@@ -34,11 +34,6 @@ pub const ResetEvent = struct {
3434 self.os_event.deinit();
3535 }
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
4237 /// Sets the event if not already set and
4338 /// wakes up all the threads waiting on the event.
4439 pub fn set(self: *ResetEvent) void {
......@@ -46,20 +41,28 @@ pub const ResetEvent = struct {
4641 }
4742
4843 /// 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?
4946 pub fn reset(self: *ResetEvent) void {
5047 return self.os_event.reset();
5148 }
5249
5350 /// 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?
5454 pub fn wait(self: *ResetEvent) void {
55 return self.os_event.wait(null) catch unreachable;
55 return self.os_event.wait();
5656 }
5757
5858 /// Wait for the event to be set by blocking the current thread.
5959 /// A timeout in nanoseconds can be provided as a hint for how
6060 /// 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?
6164 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);
6366 }
6467};
6568
......@@ -74,10 +77,6 @@ const DebugEvent = struct {
7477 self.* = undefined;
7578 }
7679
77 fn isSet(self: *DebugEvent) bool {
78 return self.is_set;
79 }
80
8180 fn reset(self: *DebugEvent) void {
8281 self.is_set = false;
8382 }
......@@ -86,101 +85,75 @@ const DebugEvent = struct {
8685 self.is_set = true;
8786 }
8887
89 fn wait(self: *DebugEvent, timeout: ?u64) !void {
88 fn wait(self: *DebugEvent) void {
9089 if (self.is_set)
9190 return;
92 if (timeout != null)
93 return error.TimedOut;
91
9492 @panic("deadlock detected");
9593 }
94
95 fn timedWait(self: *DebugEvent, timeout: u64) !void {
96 if (self.is_set)
97 return;
98
99 return error.TimedOut;
100 }
96101};
97102
98103const PosixEvent = struct {
99 is_set: bool,
100 cond: c.pthread_cond_t,
101 mutex: c.pthread_mutex_t,
104 sem: c.sem_t,
102105
103106 fn init() PosixEvent {
104107 return PosixEvent{
105 .is_set = false,
106 .cond = c.PTHREAD_COND_INITIALIZER,
107 .mutex = c.PTHREAD_MUTEX_INITIALIZER,
108 .sem = c.sem_t.init(0, 0),
108109 };
109110 }
110111
111112 fn deinit(self: *PosixEvent) void {
112 // on dragonfly or openbsd, *destroy() functions can return EINVAL
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;
113 assert(c.sem_destroy(&self.sem) == 0);
130114 }
131115
132116 fn reset(self: *PosixEvent) void {
133 assert(c.pthread_mutex_lock(&self.mutex) == 0);
134 defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
135
136 self.is_set = false;
117 self.deinit();
118 assert(c.sem_init(&self.sem, 0, 0) == 0);
137119 }
138120
139121 fn set(self: *PosixEvent) void {
140 assert(c.pthread_mutex_lock(&self.mutex) == 0);
141 defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
122 assert(c.sem_post(&self.sem) == 0);
123 }
142124
143 if (!self.is_set) {
144 self.is_set = true;
145 assert(c.pthread_cond_broadcast(&self.cond) == 0);
125 fn wait(self: *PosixEvent) void {
126 while (true) {
127 switch (c.getErrno(c.sem_wait(&self.sem))) {
128 0 => return,
129 c.EINTR => continue,
130 c.EINVAL => unreachable,
131 else => unreachable,
132 }
146133 }
147134 }
148135
149 fn wait(self: *PosixEvent, timeout: ?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
136 fn timedWait(self: *PosixEvent, timeout_ns: u64) !void {
157137 var ts: os.timespec = undefined;
158 if (timeout) |timeout_ns| {
159 var timeout_abs = timeout_ns;
160 if (comptime std.Target.current.isDarwin()) {
161 var tv: os.darwin.timeval = undefined;
162 assert(os.darwin.gettimeofday(&tv, null) == 0);
163 timeout_abs += @intCast(u64, tv.tv_sec) * time.ns_per_s;
164 timeout_abs += @intCast(u64, tv.tv_usec) * time.ns_per_us;
165 } else {
166 os.clock_gettime(os.CLOCK_REALTIME, &ts) catch unreachable;
167 timeout_abs += @intCast(u64, ts.tv_sec) * time.ns_per_s;
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));
138 var timeout_abs = timeout_ns;
139 if (comptime std.Target.current.isDarwin()) {
140 var tv: os.darwin.timeval = undefined;
141 assert(os.darwin.gettimeofday(&tv, null) == 0);
142 timeout_abs += @intCast(u64, tv.tv_sec) * time.ns_per_s;
143 timeout_abs += @intCast(u64, tv.tv_usec) * time.ns_per_us;
144 } else {
145 os.clock_gettime(os.CLOCK_REALTIME, &ts) catch return error.TimedOut;
146 timeout_abs += @intCast(u64, ts.tv_sec) * time.ns_per_s;
147 timeout_abs += @intCast(u64, ts.tv_nsec);
172148 }
173
174 while (!self.is_set) {
175 const rc = switch (timeout == null) {
176 true => c.pthread_cond_wait(&self.cond, &self.mutex),
177 else => c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts),
178 };
179 switch (rc) {
180 0 => {},
181 os.ETIMEDOUT => return error.TimedOut,
182 os.EINVAL => unreachable,
183 os.EPERM => unreachable,
149 ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), @divFloor(timeout_abs, time.ns_per_s));
150 ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.ns_per_s));
151 while (true) {
152 switch (c.getErrno(c.sem_timedwait(&self.sem, &ts))) {
153 0 => return,
154 c.EINTR => continue,
155 c.EINVAL => unreachable,
156 c.ETIMEDOUT => return error.TimedOut,
184157 else => unreachable,
185158 }
186159 }
......@@ -201,10 +174,6 @@ const AtomicEvent = struct {
201174 self.* = undefined;
202175 }
203176
204 fn isSet(self: *const AtomicEvent) bool {
205 return @atomicLoad(u32, &self.waiters, .Acquire) == WAKE;
206 }
207
208177 fn reset(self: *AtomicEvent) void {
209178 @atomicStore(u32, &self.waiters, 0, .Monotonic);
210179 }
......@@ -216,7 +185,11 @@ const AtomicEvent = struct {
216185 }
217186 }
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 {
220193 var waiters = @atomicLoad(u32, &self.waiters, .Acquire);
221194 while (waiters != WAKE) {
222195 waiters = @cmpxchgWeak(u32, &self.waiters, waiters, waiters + WAIT, .Acquire, .Acquire) orelse return Futex.wait(&self.waiters, timeout);
......@@ -367,17 +340,17 @@ test "ResetEvent" {
367340 defer event.deinit();
368341
369342 // test event setting
370 testing.expect(event.isSet() == false);
371343 event.set();
372 testing.expect(event.isSet() == true);
373344
374345 // test event resetting
375346 event.reset();
376 testing.expect(event.isSet() == false);
377347
378348 // test event waiting (non-blocking)
379349 event.set();
380350 event.wait();
351 event.reset();
352
353 event.set();
381354 try event.timedWait(1);
382355
383356 // test cross-thread signaling