| ... | ... | @@ -2347,18 +2347,30 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { |
| 2347 | 2347 | } |
| 2348 | 2348 | |
| 2349 | 2349 | pub const Thread = struct { |
| 2350 | | pid: pid_t, |
| 2351 | | allocator: ?&mem.Allocator, |
| 2352 | | stack: []u8, |
| 2353 | | pthread_handle: pthread_t, |
| 2350 | data: Data, |
| 2354 | 2351 | |
| 2355 | 2352 | pub const use_pthreads = is_posix and builtin.link_libc; |
| 2356 | | const pthread_t = if (use_pthreads) c.pthread_t else void; |
| 2357 | | const pid_t = if (!use_pthreads) i32 else void; |
| 2353 | const Data = if (use_pthreads) struct { |
| 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 | 2371 | pub fn wait(self: &const Thread) void { |
| 2360 | 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 | 2374 | switch (err) { |
| 2363 | 2375 | 0 => {}, |
| 2364 | 2376 | posix.EINVAL => unreachable, |
| ... | ... | @@ -2366,23 +2378,27 @@ pub const Thread = struct { |
| 2366 | 2378 | posix.EDEADLK => unreachable, |
| 2367 | 2379 | else => unreachable, |
| 2368 | 2380 | } |
| 2369 | | } else if (builtin.os == builtin.Os.linux) { |
| 2370 | | while (true) { |
| 2371 | | const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst); |
| 2372 | | if (pid_value == 0) break; |
| 2373 | | const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null); |
| 2374 | | switch (linux.getErrno(rc)) { |
| 2375 | | 0 => continue, |
| 2376 | | posix.EINTR => continue, |
| 2377 | | posix.EAGAIN => continue, |
| 2378 | | else => unreachable, |
| 2381 | assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); |
| 2382 | } else switch (builtin.os) { |
| 2383 | builtin.Os.linux => { |
| 2384 | while (true) { |
| 2385 | const pid_value = @atomicLoad(i32, &self.data.pid, builtin.AtomicOrder.SeqCst); |
| 2386 | if (pid_value == 0) break; |
| 2387 | const rc = linux.futex_wait(@ptrToInt(&self.data.pid), linux.FUTEX_WAIT, pid_value, null); |
| 2388 | switch (linux.getErrno(rc)) { |
| 2389 | 0 => continue, |
| 2390 | posix.EINTR => continue, |
| 2391 | posix.EAGAIN => continue, |
| 2392 | else => unreachable, |
| 2393 | } |
| 2379 | 2394 | } |
| 2380 | | } |
| 2381 | | } else { |
| 2382 | | @compileError("Unsupported OS"); |
| 2383 | | } |
| 2384 | | if (self.allocator) |a| { |
| 2385 | | a.free(self.stack); |
| 2395 | assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); |
| 2396 | }, |
| 2397 | builtin.Os.windows => { |
| 2398 | assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); |
| 2399 | assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0); |
| 2400 | }, |
| 2401 | else => @compileError("Unsupported OS"), |
| 2386 | 2402 | } |
| 2387 | 2403 | } |
| 2388 | 2404 | }; |
| ... | ... | @@ -2407,52 +2423,60 @@ pub const SpawnThreadError = error { |
| 2407 | 2423 | /// be copied. |
| 2408 | 2424 | SystemResources, |
| 2409 | 2425 | |
| 2410 | | /// pthreads requires at least 16384 bytes of stack space |
| 2411 | | StackTooSmall, |
| 2426 | /// Not enough userland memory to spawn the thread. |
| 2427 | OutOfMemory, |
| 2412 | 2428 | |
| 2413 | 2429 | Unexpected, |
| 2414 | 2430 | }; |
| 2415 | 2431 | |
| 2416 | | pub const SpawnThreadAllocatorError = SpawnThreadError || error{OutOfMemory}; |
| 2417 | | |
| 2418 | 2432 | /// caller must call wait on the returned thread |
| 2419 | 2433 | /// fn startFn(@typeOf(context)) T |
| 2420 | 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 | 2437 | // TODO compile-time call graph analysis to determine stack upper bound |
| 2423 | 2438 | // https://github.com/zig-lang/zig/issues/157 |
| 2424 | 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 | 2441 | const Context = @typeOf(context); |
| 2437 | 2442 | comptime assert(@ArgType(@typeOf(startFn), 0) == Context); |
| 2438 | 2443 | |
| 2439 | | var stack_end: usize = @ptrToInt(stack.ptr) + stack.len; |
| 2440 | | var arg: usize = undefined; |
| 2441 | | if (@sizeOf(Context) != 0) { |
| 2442 | | stack_end -= @sizeOf(Context); |
| 2443 | | stack_end -= stack_end % @alignOf(Context); |
| 2444 | | assert(stack_end >= @ptrToInt(stack.ptr)); |
| 2445 | | const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end)); |
| 2446 | | *context_ptr = context; |
| 2447 | | arg = stack_end; |
| 2448 | | } |
| 2444 | if (builtin.os == builtin.Os.windows) { |
| 2445 | const WinThread = struct { |
| 2446 | const OuterContext = struct { |
| 2447 | thread: Thread, |
| 2448 | inner: Context, |
| 2449 | }; |
| 2450 | extern fn threadMain(arg: windows.LPVOID) windows.DWORD { |
| 2451 | if (@sizeOf(Context) == 0) { |
| 2452 | return startFn({}); |
| 2453 | } else { |
| 2454 | return startFn(*@ptrCast(&Context, @alignCast(@alignOf(Context), arg))); |
| 2455 | } |
| 2456 | } |
| 2457 | }; |
| 2449 | 2458 | |
| 2450 | | stack_end -= @sizeOf(Thread); |
| 2451 | | stack_end -= stack_end % @alignOf(Thread); |
| 2452 | | assert(stack_end >= @ptrToInt(stack.ptr)); |
| 2453 | | const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end)); |
| 2454 | | thread_ptr.stack = stack; |
| 2455 | | thread_ptr.allocator = null; |
| 2459 | const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory; |
| 2460 | const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); |
| 2461 | const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory; |
| 2462 | errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); |
| 2463 | const bytes = @ptrCast(&u8, bytes_ptr)[0..byte_count]; |
| 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 | 2481 | const MainFuncs = struct { |
| 2458 | 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 | 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 | 2523 | if (builtin.os == builtin.Os.windows) { |
| 2477 | 2524 | // use windows API directly |
| 2478 | 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 | 2531 | |
| 2485 | 2532 | // align to page |
| 2486 | 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); |
| 2489 | | const setstack_err = c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size); |
| 2490 | | if (setstack_err != 0) { |
| 2491 | | return SpawnThreadError.StackTooSmall; // pthreads requires at least 16384 bytes |
| 2492 | | } |
| 2536 | thread_ptr.data.stack_addr = stack_addr; |
| 2537 | thread_ptr.data.stack_len = stack_len; |
| 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 | 2540 | switch (err) { |
| 2496 | 2541 | 0 => return thread_ptr, |
| 2497 | 2542 | posix.EAGAIN => return SpawnThreadError.SystemResources, |