| ... | ... | @@ -40,7 +40,7 @@ pub const Thread = struct { |
| 40 | 40 | pub const Data = if (use_pthreads) |
| 41 | 41 | struct { |
| 42 | 42 | handle: Thread.Handle, |
| 43 | | memory: []align(mem.page_size) u8, |
| 43 | memory: []u8, |
| 44 | 44 | } |
| 45 | 45 | else switch (std.Target.current.os.tag) { |
| 46 | 46 | .linux => struct { |
| ... | ... | @@ -63,10 +63,10 @@ pub const Thread = struct { |
| 63 | 63 | return c.pthread_self(); |
| 64 | 64 | } else |
| 65 | 65 | return switch (std.Target.current.os.tag) { |
| 66 | | .linux => os.linux.gettid(), |
| 67 | | .windows => windows.kernel32.GetCurrentThreadId(), |
| 68 | | else => @compileError("Unsupported OS"), |
| 69 | | }; |
| 66 | .linux => os.linux.gettid(), |
| 67 | .windows => windows.kernel32.GetCurrentThreadId(), |
| 68 | else => @compileError("Unsupported OS"), |
| 69 | }; |
| 70 | 70 | } |
| 71 | 71 | |
| 72 | 72 | /// Returns the handle of this thread. |
| ... | ... | @@ -79,7 +79,7 @@ pub const Thread = struct { |
| 79 | 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 | 83 | if (use_pthreads) { |
| 84 | 84 | const err = c.pthread_join(self.data.handle, null); |
| 85 | 85 | switch (err) { |
| ... | ... | @@ -89,7 +89,8 @@ pub const Thread = struct { |
| 89 | 89 | os.EDEADLK => unreachable, |
| 90 | 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 | 94 | } else switch (std.Target.current.os.tag) { |
| 94 | 95 | .linux => { |
| 95 | 96 | while (true) { |
| ... | ... | @@ -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 | 337 | var guard_end_offset: usize = undefined; |
| 296 | 338 | var stack_end_offset: usize = undefined; |
| 297 | 339 | var thread_start_offset: usize = undefined; |
| ... | ... | @@ -315,74 +357,41 @@ pub const Thread = struct { |
| 315 | 357 | l += @sizeOf(Context); |
| 316 | 358 | } |
| 317 | 359 | // Finally, the Thread Local Storage, if any. |
| 318 | | if (!Thread.use_pthreads) { |
| 319 | | l = mem.alignForward(l, os.linux.tls.tls_image.alloc_align); |
| 320 | | tls_start_offset = l; |
| 321 | | l += os.linux.tls.tls_image.alloc_size; |
| 322 | | } |
| 360 | l = mem.alignForward(l, os.linux.tls.tls_image.alloc_align); |
| 361 | tls_start_offset = l; |
| 362 | l += os.linux.tls.tls_image.alloc_size; |
| 323 | 363 | // Round the size to the page size. |
| 324 | 364 | break :blk mem.alignForward(l, mem.page_size); |
| 325 | 365 | }; |
| 326 | 366 | |
| 327 | 367 | const mmap_slice = mem: { |
| 328 | | if (std.Target.current.os.tag != .netbsd) { |
| 329 | | // Map the whole stack with no rw permissions to avoid |
| 330 | | // committing the whole region right away |
| 331 | | const mmap_slice = os.mmap( |
| 332 | | null, |
| 333 | | mmap_len, |
| 334 | | os.PROT_NONE, |
| 335 | | os.MAP_PRIVATE | os.MAP_ANONYMOUS, |
| 336 | | -1, |
| 337 | | 0, |
| 338 | | ) catch |err| switch (err) { |
| 339 | | error.MemoryMappingNotSupported => unreachable, |
| 340 | | error.AccessDenied => unreachable, |
| 341 | | error.PermissionDenied => unreachable, |
| 342 | | else => |e| return e, |
| 343 | | }; |
| 344 | | errdefer os.munmap(mmap_slice); |
| 345 | | |
| 346 | | // Map everything but the guard page as rw |
| 347 | | os.mprotect( |
| 348 | | mmap_slice[guard_end_offset..], |
| 349 | | os.PROT_READ | os.PROT_WRITE, |
| 350 | | ) catch |err| switch (err) { |
| 351 | | error.AccessDenied => unreachable, |
| 352 | | else => |e| return e, |
| 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 | | }; |
| 368 | // Map the whole stack with no rw permissions to avoid |
| 369 | // committing the whole region right away |
| 370 | const mmap_slice = os.mmap( |
| 371 | null, |
| 372 | mmap_len, |
| 373 | os.PROT_NONE, |
| 374 | os.MAP_PRIVATE | os.MAP_ANONYMOUS, |
| 375 | -1, |
| 376 | 0, |
| 377 | ) catch |err| switch (err) { |
| 378 | error.MemoryMappingNotSupported => unreachable, |
| 379 | error.AccessDenied => unreachable, |
| 380 | error.PermissionDenied => unreachable, |
| 381 | else => |e| return e, |
| 382 | }; |
| 383 | errdefer os.munmap(mmap_slice); |
| 384 | |
| 385 | // Map everything but the guard page as rw |
| 386 | os.mprotect( |
| 387 | mmap_slice[guard_end_offset..], |
| 388 | os.PROT_READ | os.PROT_WRITE, |
| 389 | ) catch |err| switch (err) { |
| 390 | error.AccessDenied => unreachable, |
| 391 | else => |e| return e, |
| 392 | }; |
| 383 | 393 | |
| 384 | | break :mem mmap_slice; |
| 385 | | } |
| 394 | break :mem mmap_slice; |
| 386 | 395 | }; |
| 387 | 396 | |
| 388 | 397 | const mmap_addr = @ptrToInt(mmap_slice.ptr); |
| ... | ... | @@ -397,33 +406,7 @@ pub const Thread = struct { |
| 397 | 406 | context_ptr.* = context; |
| 398 | 407 | } |
| 399 | 408 | |
| 400 | | if (Thread.use_pthreads) { |
| 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) { |
| 409 | if (std.Target.current.os.tag == .linux) { |
| 427 | 410 | const flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES | |
| 428 | 411 | os.CLONE_SIGHAND | os.CLONE_THREAD | os.CLONE_SYSVSEM | |
| 429 | 412 | os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID | |