| ... | @@ -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 | } else | 64 | } 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 | } |
| 71 | | 71 | |
| 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 | } |
| 81 | | 81 | |
| 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 | }; |
| 294 | | 295 | |
| | 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 | }; |
| 326 | | 366 | |
| 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 avoid | 369 | // committing the whole region right away |
| 330 | // committing the whole region right away | 370 | 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 | |
| 345 | | 385 | // Map everything but the guard page as rw |
| 346 | // Map everything but the guard page as rw | 386 | 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 | }; | | |
| 383 | | 393 | |
| 384 | break :mem mmap_slice; | 394 | break :mem mmap_slice; |
| 385 | } | | |
| 386 | }; | 395 | }; |
| 387 | | 396 | |
| 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 | } |
| 399 | | 408 | |
| 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 | |