| ... | @@ -2347,18 +2347,30 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { | ... | @@ -2347,18 +2347,30 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { |
| 2347 | } | 2347 | } |
| 2348 | | 2348 | |
| 2349 | pub const Thread = struct { | 2349 | pub const Thread = struct { |
| 2350 | pid: pid_t, | 2350 | data: Data, |
| 2351 | allocator: ?&mem.Allocator, | | |
| 2352 | stack: []u8, | | |
| 2353 | pthread_handle: pthread_t, | | |
| 2354 | | 2351 | |
| 2355 | pub const use_pthreads = is_posix and builtin.link_libc; | 2352 | pub const use_pthreads = is_posix and builtin.link_libc; |
| 2356 | const pthread_t = if (use_pthreads) c.pthread_t else void; | 2353 | const Data = if (use_pthreads) struct { |
| 2357 | const pid_t = if (!use_pthreads) i32 else void; | 2354 | handle: c.pthread_t, |
| | 2355 | stack_addr: usize, |
| | 2356 | stack_len: usize, |
| | 2357 | } else switch (builtin.os) { |
| | 2358 | builtin.Os.linux => struct { |
| | 2359 | pid: i32, |
| | 2360 | stack_addr: usize, |
| | 2361 | stack_len: usize, |
| | 2362 | }, |
| | 2363 | builtin.Os.windows => struct { |
| | 2364 | handle: windows.HANDLE, |
| | 2365 | alloc_start: &c_void, |
| | 2366 | heap_handle: windows.HANDLE, |
| | 2367 | }, |
| | 2368 | else => @compileError("Unsupported OS"), |
| | 2369 | }; |
| 2358 | | 2370 | |
| 2359 | pub fn wait(self: &const Thread) void { | 2371 | pub fn wait(self: &const Thread) void { |
| 2360 | if (use_pthreads) { | 2372 | if (use_pthreads) { |
| 2361 | const err = c.pthread_join(self.pthread_handle, null); | 2373 | const err = c.pthread_join(self.data.handle, null); |
| 2362 | switch (err) { | 2374 | switch (err) { |
| 2363 | 0 => {}, | 2375 | 0 => {}, |
| 2364 | posix.EINVAL => unreachable, | 2376 | posix.EINVAL => unreachable, |
| ... | @@ -2366,23 +2378,27 @@ pub const Thread = struct { | ... | @@ -2366,23 +2378,27 @@ pub const Thread = struct { |
| 2366 | posix.EDEADLK => unreachable, | 2378 | posix.EDEADLK => unreachable, |
| 2367 | else => unreachable, | 2379 | else => unreachable, |
| 2368 | } | 2380 | } |
| 2369 | } else if (builtin.os == builtin.Os.linux) { | 2381 | assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); |
| 2370 | while (true) { | 2382 | } else switch (builtin.os) { |
| 2371 | const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst); | 2383 | builtin.Os.linux => { |
| 2372 | if (pid_value == 0) break; | 2384 | while (true) { |
| 2373 | const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null); | 2385 | const pid_value = @atomicLoad(i32, &self.data.pid, builtin.AtomicOrder.SeqCst); |
| 2374 | switch (linux.getErrno(rc)) { | 2386 | if (pid_value == 0) break; |
| 2375 | 0 => continue, | 2387 | const rc = linux.futex_wait(@ptrToInt(&self.data.pid), linux.FUTEX_WAIT, pid_value, null); |
| 2376 | posix.EINTR => continue, | 2388 | switch (linux.getErrno(rc)) { |
| 2377 | posix.EAGAIN => continue, | 2389 | 0 => continue, |
| 2378 | else => unreachable, | 2390 | posix.EINTR => continue, |
| | 2391 | posix.EAGAIN => continue, |
| | 2392 | else => unreachable, |
| | 2393 | } |
| 2379 | } | 2394 | } |
| 2380 | } | 2395 | assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); |
| 2381 | } else { | 2396 | }, |
| 2382 | @compileError("Unsupported OS"); | 2397 | builtin.Os.windows => { |
| 2383 | } | 2398 | assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); |
| 2384 | if (self.allocator) |a| { | 2399 | assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0); |
| 2385 | a.free(self.stack); | 2400 | }, |
| | 2401 | else => @compileError("Unsupported OS"), |
| 2386 | } | 2402 | } |
| 2387 | } | 2403 | } |
| 2388 | }; | 2404 | }; |
| ... | @@ -2407,52 +2423,60 @@ pub const SpawnThreadError = error { | ... | @@ -2407,52 +2423,60 @@ pub const SpawnThreadError = error { |
| 2407 | /// be copied. | 2423 | /// be copied. |
| 2408 | SystemResources, | 2424 | SystemResources, |
| 2409 | | 2425 | |
| 2410 | /// pthreads requires at least 16384 bytes of stack space | 2426 | /// Not enough userland memory to spawn the thread. |
| 2411 | StackTooSmall, | 2427 | OutOfMemory, |
| 2412 | | 2428 | |
| 2413 | Unexpected, | 2429 | Unexpected, |
| 2414 | }; | 2430 | }; |
| 2415 | | 2431 | |
| 2416 | pub const SpawnThreadAllocatorError = SpawnThreadError || error{OutOfMemory}; | | |
| 2417 | | | |
| 2418 | /// caller must call wait on the returned thread | 2432 | /// caller must call wait on the returned thread |
| 2419 | /// fn startFn(@typeOf(context)) T | 2433 | /// fn startFn(@typeOf(context)) T |
| 2420 | /// where T is u8, noreturn, void, or !void | 2434 | /// where T is u8, noreturn, void, or !void |
| 2421 | pub fn spawnThreadAllocator(allocator: &mem.Allocator, context: var, comptime startFn: var) SpawnThreadAllocatorError!&Thread { | 2435 | /// caller must call wait on the returned thread |
| | 2436 | pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread { |
| 2422 | // TODO compile-time call graph analysis to determine stack upper bound | 2437 | // TODO compile-time call graph analysis to determine stack upper bound |
| 2423 | // https://github.com/zig-lang/zig/issues/157 | 2438 | // https://github.com/zig-lang/zig/issues/157 |
| 2424 | const default_stack_size = 8 * 1024 * 1024; | 2439 | const default_stack_size = 8 * 1024 * 1024; |
| 2425 | const stack_bytes = try allocator.alignedAlloc(u8, os.page_size, default_stack_size); | | |
| 2426 | const thread = try spawnThread(stack_bytes, context, startFn); | | |
| 2427 | thread.allocator = allocator; | | |
| 2428 | return thread; | | |
| 2429 | } | | |
| 2430 | | 2440 | |
| 2431 | /// stack must be big enough to store one Thread and one @typeOf(context), each with default alignment, at the end | | |
| 2432 | /// fn startFn(@typeOf(context)) T | | |
| 2433 | /// where T is u8, noreturn, void, or !void | | |
| 2434 | /// caller must call wait on the returned thread | | |
| 2435 | pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime startFn: var) SpawnThreadError!&Thread { | | |
| 2436 | const Context = @typeOf(context); | 2441 | const Context = @typeOf(context); |
| 2437 | comptime assert(@ArgType(@typeOf(startFn), 0) == Context); | 2442 | comptime assert(@ArgType(@typeOf(startFn), 0) == Context); |
| 2438 | | 2443 | |
| 2439 | var stack_end: usize = @ptrToInt(stack.ptr) + stack.len; | 2444 | if (builtin.os == builtin.Os.windows) { |
| 2440 | var arg: usize = undefined; | 2445 | const WinThread = struct { |
| 2441 | if (@sizeOf(Context) != 0) { | 2446 | const OuterContext = struct { |
| 2442 | stack_end -= @sizeOf(Context); | 2447 | thread: Thread, |
| 2443 | stack_end -= stack_end % @alignOf(Context); | 2448 | inner: Context, |
| 2444 | assert(stack_end >= @ptrToInt(stack.ptr)); | 2449 | }; |
| 2445 | const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end)); | 2450 | extern fn threadMain(arg: windows.LPVOID) windows.DWORD { |
| 2446 | *context_ptr = context; | 2451 | if (@sizeOf(Context) == 0) { |
| 2447 | arg = stack_end; | 2452 | return startFn({}); |
| 2448 | } | 2453 | } else { |
| | 2454 | return startFn(*@ptrCast(&Context, @alignCast(@alignOf(Context), arg))); |
| | 2455 | } |
| | 2456 | } |
| | 2457 | }; |
| 2449 | | 2458 | |
| 2450 | stack_end -= @sizeOf(Thread); | 2459 | const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory; |
| 2451 | stack_end -= stack_end % @alignOf(Thread); | 2460 | const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); |
| 2452 | assert(stack_end >= @ptrToInt(stack.ptr)); | 2461 | const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory; |
| 2453 | const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end)); | 2462 | errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); |
| 2454 | thread_ptr.stack = stack; | 2463 | const bytes = @ptrCast(&u8, bytes_ptr)[0..byte_count]; |
| 2455 | thread_ptr.allocator = null; | 2464 | const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; |
| | 2465 | outer_context.inner = context; |
| | 2466 | outer_context.thread.data.heap_handle = heap_handle; |
| | 2467 | outer_context.thread.data.alloc_start = bytes_ptr; |
| | 2468 | |
| | 2469 | const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(&c_void, &outer_context.inner); |
| | 2470 | outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, |
| | 2471 | parameter, 0, null) ?? |
| | 2472 | { |
| | 2473 | const err = windows.GetLastError(); |
| | 2474 | return switch (err) { |
| | 2475 | else => os.unexpectedErrorWindows(err), |
| | 2476 | }; |
| | 2477 | }; |
| | 2478 | return &outer_context.thread; |
| | 2479 | } |
| 2456 | | 2480 | |
| 2457 | const MainFuncs = struct { | 2481 | const MainFuncs = struct { |
| 2458 | extern fn linuxThreadMain(ctx_addr: usize) u8 { | 2482 | extern fn linuxThreadMain(ctx_addr: usize) u8 { |
| ... | @@ -2473,6 +2497,29 @@ pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime start | ... | @@ -2473,6 +2497,29 @@ pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime start |
| 2473 | } | 2497 | } |
| 2474 | }; | 2498 | }; |
| 2475 | | 2499 | |
| | 2500 | const stack_len = default_stack_size; |
| | 2501 | const stack_addr = posix.mmap(null, stack_len, posix.PROT_READ|posix.PROT_WRITE, |
| | 2502 | posix.MAP_PRIVATE|posix.MAP_ANONYMOUS|posix.MAP_GROWSDOWN, -1, 0); |
| | 2503 | if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory; |
| | 2504 | errdefer _ = posix.munmap(stack_addr, stack_len); |
| | 2505 | |
| | 2506 | var stack_end: usize = stack_addr + stack_len; |
| | 2507 | var arg: usize = undefined; |
| | 2508 | if (@sizeOf(Context) != 0) { |
| | 2509 | stack_end -= @sizeOf(Context); |
| | 2510 | stack_end -= stack_end % @alignOf(Context); |
| | 2511 | assert(stack_end >= stack_addr); |
| | 2512 | const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end)); |
| | 2513 | *context_ptr = context; |
| | 2514 | arg = stack_end; |
| | 2515 | } |
| | 2516 | |
| | 2517 | stack_end -= @sizeOf(Thread); |
| | 2518 | stack_end -= stack_end % @alignOf(Thread); |
| | 2519 | assert(stack_end >= stack_addr); |
| | 2520 | const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end)); |
| | 2521 | |
| | 2522 | |
| 2476 | if (builtin.os == builtin.Os.windows) { | 2523 | if (builtin.os == builtin.Os.windows) { |
| 2477 | // use windows API directly | 2524 | // use windows API directly |
| 2478 | @compileError("TODO support spawnThread for Windows"); | 2525 | @compileError("TODO support spawnThread for Windows"); |
| ... | @@ -2484,14 +2531,12 @@ pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime start | ... | @@ -2484,14 +2531,12 @@ pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime start |
| 2484 | | 2531 | |
| 2485 | // align to page | 2532 | // align to page |
| 2486 | stack_end -= stack_end % os.page_size; | 2533 | stack_end -= stack_end % os.page_size; |
| | 2534 | assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_len) == 0); |
| 2487 | | 2535 | |
| 2488 | const stack_size = stack_end - @ptrToInt(stack.ptr); | 2536 | thread_ptr.data.stack_addr = stack_addr; |
| 2489 | const setstack_err = c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size); | 2537 | thread_ptr.data.stack_len = stack_len; |
| 2490 | if (setstack_err != 0) { | | |
| 2491 | return SpawnThreadError.StackTooSmall; // pthreads requires at least 16384 bytes | | |
| 2492 | } | | |
| 2493 | | 2538 | |
| 2494 | const err = c.pthread_create(&thread_ptr.pthread_handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); | 2539 | const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); |
| 2495 | switch (err) { | 2540 | switch (err) { |
| 2496 | 0 => return thread_ptr, | 2541 | 0 => return thread_ptr, |
| 2497 | posix.EAGAIN => return SpawnThreadError.SystemResources, | 2542 | posix.EAGAIN => return SpawnThreadError.SystemResources, |