authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-02 13:47:40-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-12-02 13:47:40-08:00
logf84232714791137ab0e3f457b0eed773e476f293
tree714e7088e694e65e24a5e0496723f38256ce8f7b
parent4c8632e244fbf1213fb78f21417a2a5c6347cab3
parent471e26425ab1faef944bf37c29e39d2241220f00
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7277 from LemonBoy/thread-posix

std: make the use of pthread_join POSIX-compliant

3 files changed, 98 insertions(+), 98 deletions(-)

ci/azure/linux_script+18-2
...@@ -8,11 +8,27 @@ BUILDDIR="$(pwd)"...@@ -8,11 +8,27 @@ BUILDDIR="$(pwd)"
8sudo sh -c 'echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main" >> /etc/apt/sources.list'8sudo sh -c 'echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main" >> /etc/apt/sources.list'
9wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -9wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
10sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test10sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
11sudo apt-get update -q
1211
13sudo apt-get remove -y llvm-*12sudo apt-get remove -y llvm-*
14sudo rm -rf /usr/local/*13sudo rm -rf /usr/local/*
15sudo apt-get install -y libxml2-dev libclang-11-dev llvm-11 llvm-11-dev liblld-11-dev cmake s3cmd gcc-7 g++-7 ninja-build tidy14
15# Some APT mirrors can be flaky, retry the download instead of failing right
16# away.
17APT_MAX_RETRY=3
18
19for i in $(seq 1 "$APT_MAX_RETRY"); do
20 sudo apt-get update -q
21 sudo apt-get install -y \
22 libxml2-dev libclang-11-dev llvm-11 llvm-11-dev liblld-11-dev cmake s3cmd \
23 gcc-7 g++-7 ninja-build tidy \
24 && break
25 if [ "$i" -eq "$APT_MAX_RETRY" ]; then
26 echo 'apt-get failed, giving up...'
27 exit 1
28 fi
29 echo 'apt-get failed, retrying...'
30 sleep 5s
31done
1632
17QEMUBASE="qemu-linux-x86_64-5.1.0"33QEMUBASE="qemu-linux-x86_64-5.1.0"
18wget https://ziglang.org/deps/$QEMUBASE.tar.xz34wget https://ziglang.org/deps/$QEMUBASE.tar.xz
lib/std/c.zig+1
...@@ -259,6 +259,7 @@ pub extern "c" fn futimens(fd: fd_t, times: *const [2]timespec) c_int;...@@ -259,6 +259,7 @@ pub extern "c" fn futimens(fd: fd_t, times: *const [2]timespec) c_int;
259pub extern "c" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: fn (?*c_void) callconv(.C) ?*c_void, noalias arg: ?*c_void) c_int;259pub extern "c" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: fn (?*c_void) callconv(.C) ?*c_void, noalias arg: ?*c_void) c_int;
260pub extern "c" fn pthread_attr_init(attr: *pthread_attr_t) c_int;260pub extern "c" fn pthread_attr_init(attr: *pthread_attr_t) c_int;
261pub extern "c" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int;261pub extern "c" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int;
262pub extern "c" fn pthread_attr_setstacksize(attr: *pthread_attr_t, stacksize: usize) c_int;
262pub extern "c" fn pthread_attr_setguardsize(attr: *pthread_attr_t, guardsize: usize) c_int;263pub extern "c" fn pthread_attr_setguardsize(attr: *pthread_attr_t, guardsize: usize) c_int;
263pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;264pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;
264pub extern "c" fn pthread_self() pthread_t;265pub extern "c" fn pthread_self() pthread_t;
lib/std/thread.zig+79-96
...@@ -40,7 +40,7 @@ pub const Thread = struct {...@@ -40,7 +40,7 @@ pub const Thread = struct {
40 pub const Data = if (use_pthreads)40 pub const Data = if (use_pthreads)
41 struct {41 struct {
42 handle: Thread.Handle,42 handle: Thread.Handle,
43 memory: []align(mem.page_size) u8,43 memory: []u8,
44 }44 }
45 else switch (std.Target.current.os.tag) {45 else switch (std.Target.current.os.tag) {
46 .linux => struct {46 .linux => struct {
...@@ -63,10 +63,10 @@ pub const Thread = struct {...@@ -63,10 +63,10 @@ pub const Thread = struct {
63 return c.pthread_self();63 return c.pthread_self();
64 } else64 } else
65 return switch (std.Target.current.os.tag) {65 return switch (std.Target.current.os.tag) {
66 .linux => os.linux.gettid(),66 .linux => os.linux.gettid(),
67 .windows => windows.kernel32.GetCurrentThreadId(),67 .windows => windows.kernel32.GetCurrentThreadId(),
68 else => @compileError("Unsupported OS"),68 else => @compileError("Unsupported OS"),
69 };69 };
70 }70 }
7171
72 /// Returns the handle of this thread.72 /// Returns the handle of this thread.
...@@ -79,7 +79,7 @@ pub const Thread = struct {...@@ -79,7 +79,7 @@ pub const Thread = struct {
79 return self.data.handle;79 return self.data.handle;
80 }80 }
8181
82 pub fn wait(self: *const Thread) void {82 pub fn wait(self: *Thread) void {
83 if (use_pthreads) {83 if (use_pthreads) {
84 const err = c.pthread_join(self.data.handle, null);84 const err = c.pthread_join(self.data.handle, null);
85 switch (err) {85 switch (err) {
...@@ -89,7 +89,8 @@ pub const Thread = struct {...@@ -89,7 +89,8 @@ pub const Thread = struct {
89 os.EDEADLK => unreachable,89 os.EDEADLK => unreachable,
90 else => unreachable,90 else => unreachable,
91 }91 }
92 os.munmap(self.data.memory);92 std.heap.c_allocator.free(self.data.memory);
93 std.heap.c_allocator.destroy(self);
93 } else switch (std.Target.current.os.tag) {94 } else switch (std.Target.current.os.tag) {
94 .linux => {95 .linux => {
95 while (true) {96 while (true) {
...@@ -292,6 +293,47 @@ pub const Thread = struct {...@@ -292,6 +293,47 @@ pub const Thread = struct {
292 }293 }
293 };294 };
294295
296 if (Thread.use_pthreads) {
297 var attr: c.pthread_attr_t = undefined;
298 if (c.pthread_attr_init(&attr) != 0) return error.SystemResources;
299 defer assert(c.pthread_attr_destroy(&attr) == 0);
300
301 const thread_obj = try std.heap.c_allocator.create(Thread);
302 errdefer std.heap.c_allocator.destroy(thread_obj);
303 if (@sizeOf(Context) > 0) {
304 thread_obj.data.memory = try std.heap.c_allocator.allocAdvanced(
305 u8,
306 @alignOf(Context),
307 @sizeOf(Context),
308 .at_least,
309 );
310 errdefer std.heap.c_allocator.free(thread_obj.data.memory);
311 mem.copy(u8, thread_obj.data.memory, mem.asBytes(&context));
312 } else {
313 thread_obj.data.memory = @as([*]u8, undefined)[0..0];
314 }
315
316 // Use the same set of parameters used by the libc-less impl.
317 assert(c.pthread_attr_setstacksize(&attr, default_stack_size) == 0);
318 assert(c.pthread_attr_setguardsize(&attr, mem.page_size) == 0);
319
320 const err = c.pthread_create(
321 &thread_obj.data.handle,
322 &attr,
323 MainFuncs.posixThreadMain,
324 thread_obj.data.memory.ptr,
325 );
326 switch (err) {
327 0 => return thread_obj,
328 os.EAGAIN => return error.SystemResources,
329 os.EPERM => unreachable,
330 os.EINVAL => unreachable,
331 else => return os.unexpectedErrno(@intCast(usize, err)),
332 }
333
334 return thread_obj;
335 }
336
295 var guard_end_offset: usize = undefined;337 var guard_end_offset: usize = undefined;
296 var stack_end_offset: usize = undefined;338 var stack_end_offset: usize = undefined;
297 var thread_start_offset: usize = undefined;339 var thread_start_offset: usize = undefined;
...@@ -315,74 +357,41 @@ pub const Thread = struct {...@@ -315,74 +357,41 @@ pub const Thread = struct {
315 l += @sizeOf(Context);357 l += @sizeOf(Context);
316 }358 }
317 // Finally, the Thread Local Storage, if any.359 // Finally, the Thread Local Storage, if any.
318 if (!Thread.use_pthreads) {360 l = mem.alignForward(l, os.linux.tls.tls_image.alloc_align);
319 l = mem.alignForward(l, os.linux.tls.tls_image.alloc_align);361 tls_start_offset = l;
320 tls_start_offset = l;362 l += os.linux.tls.tls_image.alloc_size;
321 l += os.linux.tls.tls_image.alloc_size;
322 }
323 // Round the size to the page size.363 // Round the size to the page size.
324 break :blk mem.alignForward(l, mem.page_size);364 break :blk mem.alignForward(l, mem.page_size);
325 };365 };
326366
327 const mmap_slice = mem: {367 const mmap_slice = mem: {
328 if (std.Target.current.os.tag != .netbsd) {368 // Map the whole stack with no rw permissions to avoid
329 // Map the whole stack with no rw permissions to avoid369 // committing the whole region right away
330 // committing the whole region right away370 const mmap_slice = os.mmap(
331 const mmap_slice = os.mmap(371 null,
332 null,372 mmap_len,
333 mmap_len,373 os.PROT_NONE,
334 os.PROT_NONE,374 os.MAP_PRIVATE | os.MAP_ANONYMOUS,
335 os.MAP_PRIVATE | os.MAP_ANONYMOUS,375 -1,
336 -1,376 0,
337 0,377 ) catch |err| switch (err) {
338 ) catch |err| switch (err) {378 error.MemoryMappingNotSupported => unreachable,
339 error.MemoryMappingNotSupported => unreachable,379 error.AccessDenied => unreachable,
340 error.AccessDenied => unreachable,380 error.PermissionDenied => unreachable,
341 error.PermissionDenied => unreachable,381 else => |e| return e,
342 else => |e| return e,382 };
343 };383 errdefer os.munmap(mmap_slice);
344 errdefer os.munmap(mmap_slice);384
345385 // Map everything but the guard page as rw
346 // Map everything but the guard page as rw386 os.mprotect(
347 os.mprotect(387 mmap_slice[guard_end_offset..],
348 mmap_slice[guard_end_offset..],388 os.PROT_READ | os.PROT_WRITE,
349 os.PROT_READ | os.PROT_WRITE,389 ) catch |err| switch (err) {
350 ) catch |err| switch (err) {390 error.AccessDenied => unreachable,
351 error.AccessDenied => unreachable,391 else => |e| return e,
352 else => |e| return e,392 };
353 };
354
355 break :mem mmap_slice;
356 } else {
357 // NetBSD mprotect is very strict and doesn't allow to "upgrade"
358 // a PROT_NONE mapping to a RW one so let's allocate everything
359 // right away
360 const mmap_slice = os.mmap(
361 null,
362 mmap_len,
363 os.PROT_READ | os.PROT_WRITE,
364 os.MAP_PRIVATE | os.MAP_ANONYMOUS,
365 -1,
366 0,
367 ) catch |err| switch (err) {
368 error.MemoryMappingNotSupported => unreachable,
369 error.AccessDenied => unreachable,
370 error.PermissionDenied => unreachable,
371 else => |e| return e,
372 };
373 errdefer os.munmap(mmap_slice);
374
375 // Remap the guard page with no permissions
376 os.mprotect(
377 mmap_slice[0..guard_end_offset],
378 os.PROT_NONE,
379 ) catch |err| switch (err) {
380 error.AccessDenied => unreachable,
381 else => |e| return e,
382 };
383393
384 break :mem mmap_slice;394 break :mem mmap_slice;
385 }
386 };395 };
387396
388 const mmap_addr = @ptrToInt(mmap_slice.ptr);397 const mmap_addr = @ptrToInt(mmap_slice.ptr);
...@@ -397,33 +406,7 @@ pub const Thread = struct {...@@ -397,33 +406,7 @@ pub const Thread = struct {
397 context_ptr.* = context;406 context_ptr.* = context;
398 }407 }
399408
400 if (Thread.use_pthreads) {409 if (std.Target.current.os.tag == .linux) {
401 // use pthreads
402 var attr: c.pthread_attr_t = undefined;
403 if (c.pthread_attr_init(&attr) != 0) return error.SystemResources;
404 defer assert(c.pthread_attr_destroy(&attr) == 0);
405
406 // Tell pthread where the effective stack start is and its size
407 assert(c.pthread_attr_setstack(
408 &attr,
409 mmap_slice.ptr + guard_end_offset,
410 stack_end_offset - guard_end_offset,
411 ) == 0);
412 // Even though pthread's man pages state that the guard size is
413 // ignored when the stack address is explicitly given, on some
414 // plaforms such as NetBSD we still have to zero it to prevent
415 // random crashes in pthread_join calls
416 assert(c.pthread_attr_setguardsize(&attr, 0) == 0);
417
418 const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
419 switch (err) {
420 0 => return thread_ptr,
421 os.EAGAIN => return error.SystemResources,
422 os.EPERM => unreachable,
423 os.EINVAL => unreachable,
424 else => return os.unexpectedErrno(@intCast(usize, err)),
425 }
426 } else if (std.Target.current.os.tag == .linux) {
427 const flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES |410 const flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES |
428 os.CLONE_SIGHAND | os.CLONE_THREAD | os.CLONE_SYSVSEM |411 os.CLONE_SIGHAND | os.CLONE_THREAD | os.CLONE_SYSVSEM |
429 os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID |412 os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID |