| ... | @@ -2,6 +2,10 @@ const std = @import("../index.zig"); | ... | @@ -2,6 +2,10 @@ const std = @import("../index.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Os = builtin.Os; | 3 | const Os = builtin.Os; |
| 4 | const is_windows = builtin.os == Os.windows; | 4 | const is_windows = builtin.os == Os.windows; |
| | 5 | const is_posix = switch (builtin.os) { |
| | 6 | builtin.Os.linux, builtin.Os.macosx => true, |
| | 7 | else => false, |
| | 8 | }; |
| 5 | const os = this; | 9 | const os = this; |
| 6 | | 10 | |
| 7 | test "std.os" { | 11 | test "std.os" { |
| ... | @@ -2343,21 +2347,39 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { | ... | @@ -2343,21 +2347,39 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { |
| 2343 | } | 2347 | } |
| 2344 | | 2348 | |
| 2345 | pub const Thread = struct { | 2349 | pub const Thread = struct { |
| 2346 | pid: i32, | 2350 | pid: pid_t, |
| 2347 | allocator: ?&mem.Allocator, | 2351 | allocator: ?&mem.Allocator, |
| 2348 | stack: []u8, | 2352 | stack: []u8, |
| | 2353 | pthread_handle: pthread_t, |
| | 2354 | |
| | 2355 | 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; |
| 2349 | | 2358 | |
| 2350 | pub fn wait(self: &const Thread) void { | 2359 | pub fn wait(self: &const Thread) void { |
| 2351 | while (true) { | 2360 | if (use_pthreads) { |
| 2352 | const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst); | 2361 | const err = c.pthread_join(self.pthread_handle, null); |
| 2353 | if (pid_value == 0) break; | 2362 | switch (err) { |
| 2354 | const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null); | 2363 | 0 => {}, |
| 2355 | switch (linux.getErrno(rc)) { | 2364 | posix.EINVAL => unreachable, |
| 2356 | 0 => continue, | 2365 | posix.ESRCH => unreachable, |
| 2357 | posix.EINTR => continue, | 2366 | posix.EDEADLK => unreachable, |
| 2358 | posix.EAGAIN => continue, | | |
| 2359 | else => unreachable, | 2367 | else => unreachable, |
| 2360 | } | 2368 | } |
| | 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, |
| | 2379 | } |
| | 2380 | } |
| | 2381 | } else { |
| | 2382 | @compileError("Unsupported OS"); |
| 2361 | } | 2383 | } |
| 2362 | if (self.allocator) |a| { | 2384 | if (self.allocator) |a| { |
| 2363 | a.free(self.stack); | 2385 | a.free(self.stack); |
| ... | @@ -2429,31 +2451,67 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread | ... | @@ -2429,31 +2451,67 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread |
| 2429 | thread_ptr.stack = stack; | 2451 | thread_ptr.stack = stack; |
| 2430 | thread_ptr.allocator = null; | 2452 | thread_ptr.allocator = null; |
| 2431 | | 2453 | |
| 2432 | const threadMain = struct { | 2454 | const MainFuncs = struct { |
| 2433 | extern fn threadMain(ctx_addr: usize) u8 { | 2455 | extern fn linuxThreadMain(ctx_addr: usize) u8 { |
| 2434 | if (@sizeOf(Context) == 0) { | 2456 | if (@sizeOf(Context) == 0) { |
| 2435 | return startFn({}); | 2457 | return startFn({}); |
| 2436 | } else { | 2458 | } else { |
| 2437 | return startFn(*@intToPtr(&const Context, ctx_addr)); | 2459 | return startFn(*@intToPtr(&const Context, ctx_addr)); |
| 2438 | } | 2460 | } |
| 2439 | } | 2461 | } |
| 2440 | }.threadMain; | 2462 | extern fn posixThreadMain(ctx: ?&c_void) ?&c_void { |
| | 2463 | if (@sizeOf(Context) == 0) { |
| | 2464 | _ = startFn({}); |
| | 2465 | return null; |
| | 2466 | } else { |
| | 2467 | _ = startFn(*@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx))); |
| | 2468 | return null; |
| | 2469 | } |
| | 2470 | } |
| | 2471 | }; |
| | 2472 | |
| | 2473 | if (builtin.os == builtin.Os.windows) { |
| | 2474 | // use windows API directly |
| | 2475 | @compileError("TODO support spawnThread for Windows"); |
| | 2476 | } else if (Thread.use_pthreads) { |
| | 2477 | // use pthreads |
| | 2478 | var attr: c.pthread_attr_t = undefined; |
| | 2479 | if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; |
| | 2480 | defer assert(c.pthread_attr_destroy(&attr) == 0); |
| | 2481 | |
| | 2482 | const stack_size = stack_end - @ptrToInt(stack.ptr); |
| | 2483 | if (c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size) != 0) { |
| | 2484 | return SpawnThreadError.SystemResources; |
| | 2485 | } |
| 2441 | | 2486 | |
| 2442 | const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | 2487 | const err = c.pthread_create(&thread_ptr.pthread_handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); |
| 2443 | | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS | 2488 | switch (err) { |
| 2444 | | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; | 2489 | 0 => return thread_ptr, |
| 2445 | const newtls: usize = 0; | 2490 | posix.EAGAIN => return SpawnThreadError.SystemResources, |
| 2446 | const rc = posix.clone(threadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid); | 2491 | posix.EPERM => unreachable, |
| 2447 | const err = posix.getErrno(rc); | 2492 | posix.EINVAL => unreachable, |
| 2448 | switch (err) { | 2493 | else => return unexpectedErrorPosix(usize(err)), |
| 2449 | 0 => return thread_ptr, | 2494 | } |
| 2450 | posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, | 2495 | } else if (builtin.os == builtin.Os.linux) { |
| 2451 | posix.EINVAL => unreachable, | 2496 | // use linux API directly |
| 2452 | posix.ENOMEM => return SpawnThreadError.SystemResources, | 2497 | const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND |
| 2453 | posix.ENOSPC => unreachable, | 2498 | | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS |
| 2454 | posix.EPERM => unreachable, | 2499 | | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; |
| 2455 | posix.EUSERS => unreachable, | 2500 | const newtls: usize = 0; |
| 2456 | else => return unexpectedErrorPosix(err), | 2501 | const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid); |
| | 2502 | const err = posix.getErrno(rc); |
| | 2503 | switch (err) { |
| | 2504 | 0 => return thread_ptr, |
| | 2505 | posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, |
| | 2506 | posix.EINVAL => unreachable, |
| | 2507 | posix.ENOMEM => return SpawnThreadError.SystemResources, |
| | 2508 | posix.ENOSPC => unreachable, |
| | 2509 | posix.EPERM => unreachable, |
| | 2510 | posix.EUSERS => unreachable, |
| | 2511 | else => return unexpectedErrorPosix(err), |
| | 2512 | } |
| | 2513 | } else { |
| | 2514 | @compileError("Unsupported OS"); |
| 2457 | } | 2515 | } |
| 2458 | } | 2516 | } |
| 2459 | | 2517 | |