| ... | @@ -24,17 +24,6 @@ pub const Condition = @import("Thread/Condition.zig"); | ... | @@ -24,17 +24,6 @@ pub const Condition = @import("Thread/Condition.zig"); |
| 24 | | 24 | |
| 25 | pub const spinLoopHint = @compileError("deprecated: use std.atomic.spinLoopHint"); | 25 | pub const spinLoopHint = @compileError("deprecated: use std.atomic.spinLoopHint"); |
| 26 | | 26 | |
| 27 | test "std.Thread" { | | |
| 28 | // Doesn't use testing.refAllDecls() since that would pull in the compileError spinLoopHint. | | |
| 29 | _ = AutoResetEvent; | | |
| 30 | _ = Futex; | | |
| 31 | _ = ResetEvent; | | |
| 32 | _ = StaticResetEvent; | | |
| 33 | _ = Mutex; | | |
| 34 | _ = Semaphore; | | |
| 35 | _ = Condition; | | |
| 36 | } | | |
| 37 | | | |
| 38 | pub const use_pthreads = target.os.tag != .windows and std.builtin.link_libc; | 27 | pub const use_pthreads = target.os.tag != .windows and std.builtin.link_libc; |
| 39 | | 28 | |
| 40 | const Thread = @This(); | 29 | const Thread = @This(); |
| ... | @@ -50,7 +39,6 @@ else | ... | @@ -50,7 +39,6 @@ else |
| 50 | impl: Impl, | 39 | impl: Impl, |
| 51 | | 40 | |
| 52 | /// Represents a unique ID per thread. | 41 | /// Represents a unique ID per thread. |
| 53 | /// May be an integer or pointer depending on the platform. | | |
| 54 | pub const Id = u64; | 42 | pub const Id = u64; |
| 55 | | 43 | |
| 56 | /// Returns the platform ID of the callers thread. | 44 | /// Returns the platform ID of the callers thread. |
| ... | @@ -79,7 +67,7 @@ pub const SpawnConfig = struct { | ... | @@ -79,7 +67,7 @@ pub const SpawnConfig = struct { |
| 79 | stack_size: usize = 16 * 1024 * 1024, | 67 | stack_size: usize = 16 * 1024 * 1024, |
| 80 | }; | 68 | }; |
| 81 | | 69 | |
| 82 | pub const SpawnError = error { | 70 | pub const SpawnError = error{ |
| 83 | /// A system-imposed limit on the number of threads was encountered. | 71 | /// A system-imposed limit on the number of threads was encountered. |
| 84 | /// There are a number of limits that may trigger this error: | 72 | /// There are a number of limits that may trigger this error: |
| 85 | /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), | 73 | /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), |
| ... | @@ -115,7 +103,7 @@ pub const SpawnError = error { | ... | @@ -115,7 +103,7 @@ pub const SpawnError = error { |
| 115 | /// or call `detach()` to excuse the caller from calling `join()` and have the thread clean up its resources on completion`. | 103 | /// or call `detach()` to excuse the caller from calling `join()` and have the thread clean up its resources on completion`. |
| 116 | pub fn spawn(config: SpawnConfig, comptime function: anytype, args: anytype) SpawnError!Thread { | 104 | pub fn spawn(config: SpawnConfig, comptime function: anytype, args: anytype) SpawnError!Thread { |
| 117 | if (std.builtin.single_threaded) { | 105 | if (std.builtin.single_threaded) { |
| 118 | @compileError("cannot spawn thread when building in single-threaded mode"); | 106 | @compileError("Cannot spawn thread when building in single-threaded mode"); |
| 119 | } | 107 | } |
| 120 | | 108 | |
| 121 | const impl = try Impl.spawn(config, function, args); | 109 | const impl = try Impl.spawn(config, function, args); |
| ... | @@ -132,11 +120,13 @@ pub fn getHandle(self: Thread) Handle { | ... | @@ -132,11 +120,13 @@ pub fn getHandle(self: Thread) Handle { |
| 132 | } | 120 | } |
| 133 | | 121 | |
| 134 | /// Release the obligation of the caller to call `join()` and have the thread clean up its own resources on completion. | 122 | /// Release the obligation of the caller to call `join()` and have the thread clean up its own resources on completion. |
| | 123 | /// Once called, this consumes the Thread object and invoking any other functions on it is considered undefined behavior. |
| 135 | pub fn detach(self: Thread) void { | 124 | pub fn detach(self: Thread) void { |
| 136 | return self.impl.detach(); | 125 | return self.impl.detach(); |
| 137 | } | 126 | } |
| 138 | | 127 | |
| 139 | /// Waits for the thread to complete, then deallocates any resources created on `spawn()`. | 128 | /// Waits for the thread to complete, then deallocates any resources created on `spawn()`. |
| | 129 | /// Once called, this consumes the Thread object and invoking any other functions on it is considered undefined behavior. |
| 140 | pub fn join(self: Thread) void { | 130 | pub fn join(self: Thread) void { |
| 141 | return self.impl.join(); | 131 | return self.impl.join(); |
| 142 | } | 132 | } |
| ... | @@ -200,6 +190,8 @@ fn callFn(comptime f: anytype, args: anytype) switch (Impl) { | ... | @@ -200,6 +190,8 @@ fn callFn(comptime f: anytype, args: anytype) switch (Impl) { |
| 200 | } | 190 | } |
| 201 | } | 191 | } |
| 202 | | 192 | |
| | 193 | /// We can't compile error in the `Impl` switch statement as its eagerly evaluated. |
| | 194 | /// So instead, we compile-error on the methods themselves for platforms which don't support threads. |
| 203 | const UnsupportedImpl = struct { | 195 | const UnsupportedImpl = struct { |
| 204 | pub const ThreadHandle = void; | 196 | pub const ThreadHandle = void; |
| 205 | | 197 | |
| ... | @@ -212,7 +204,7 @@ const UnsupportedImpl = struct { | ... | @@ -212,7 +204,7 @@ const UnsupportedImpl = struct { |
| 212 | } | 204 | } |
| 213 | | 205 | |
| 214 | fn spawn(config: SpawnConfig, comptime f: anytype, args: anytype) !Impl { | 206 | fn spawn(config: SpawnConfig, comptime f: anytype, args: anytype) !Impl { |
| 215 | return unsupported(.{config, f, args}); | 207 | return unsupported(.{ config, f, args }); |
| 216 | } | 208 | } |
| 217 | | 209 | |
| 218 | fn getHandle(self: Impl) ThreadHandle { | 210 | fn getHandle(self: Impl) ThreadHandle { |
| ... | @@ -225,7 +217,7 @@ const UnsupportedImpl = struct { | ... | @@ -225,7 +217,7 @@ const UnsupportedImpl = struct { |
| 225 | | 217 | |
| 226 | fn join(self: Impl) void { | 218 | fn join(self: Impl) void { |
| 227 | return unsupported(self); | 219 | return unsupported(self); |
| 228 | } | 220 | } |
| 229 | | 221 | |
| 230 | fn unsupported(unusued: anytype) noreturn { | 222 | fn unsupported(unusued: anytype) noreturn { |
| 231 | @compileLog("Unsupported operating system", target.os.tag); | 223 | @compileLog("Unsupported operating system", target.os.tag); |
| ... | @@ -244,6 +236,7 @@ const WindowsThreadImpl = struct { | ... | @@ -244,6 +236,7 @@ const WindowsThreadImpl = struct { |
| 244 | } | 236 | } |
| 245 | | 237 | |
| 246 | fn getCpuCount() !usize { | 238 | fn getCpuCount() !usize { |
| | 239 | // Faster than calling into GetSystemInfo(), even if amortized. |
| 247 | return windows.peb().NumberOfProcessors; | 240 | return windows.peb().NumberOfProcessors; |
| 248 | } | 241 | } |
| 249 | | 242 | |
| ... | @@ -299,16 +292,17 @@ const WindowsThreadImpl = struct { | ... | @@ -299,16 +292,17 @@ const WindowsThreadImpl = struct { |
| 299 | // Its also fine if the limit here is incorrect as stack size is only a hint. | 292 | // Its also fine if the limit here is incorrect as stack size is only a hint. |
| 300 | var stack_size = std.math.cast(u32, config.stack_size) catch std.math.maxInt(u32); | 293 | var stack_size = std.math.cast(u32, config.stack_size) catch std.math.maxInt(u32); |
| 301 | stack_size = std.math.max(64 * 1024, stack_size); | 294 | stack_size = std.math.max(64 * 1024, stack_size); |
| 302 | | 295 | |
| 303 | instance.thread.thread_handle = windows.kernel32.CreateThread( | 296 | instance.thread.thread_handle = windows.kernel32.CreateThread( |
| 304 | null, | 297 | null, |
| 305 | stack_size, | 298 | stack_size, |
| 306 | Instance.entryFn, | 299 | Instance.entryFn, |
| 307 | @ptrCast(*c_void, instance), | 300 | @ptrCast(*c_void, instance), |
| 308 | 0, | 301 | 0, |
| 309 | null, | 302 | null, |
| 310 | ) orelse { | 303 | ) orelse { |
| 311 | return windows.unexpectedError(windows.kernel32.GetLastError()); | 304 | const errno = windows.kernel32.GetLastError(); |
| | 305 | return windows.unexpectedError(errno); |
| 312 | }; | 306 | }; |
| 313 | | 307 | |
| 314 | return Impl{ .thread = &instance.thread }; | 308 | return Impl{ .thread = &instance.thread }; |
| ... | @@ -332,7 +326,7 @@ const WindowsThreadImpl = struct { | ... | @@ -332,7 +326,7 @@ const WindowsThreadImpl = struct { |
| 332 | windows.CloseHandle(self.thread.thread_handle); | 326 | windows.CloseHandle(self.thread.thread_handle); |
| 333 | assert(self.thread.completion.load(.SeqCst) == .completed); | 327 | assert(self.thread.completion.load(.SeqCst) == .completed); |
| 334 | self.thread.free(); | 328 | self.thread.free(); |
| 335 | } | 329 | } |
| 336 | }; | 330 | }; |
| 337 | | 331 | |
| 338 | const PosixThreadImpl = struct { | 332 | const PosixThreadImpl = struct { |
| ... | @@ -374,7 +368,9 @@ const PosixThreadImpl = struct { | ... | @@ -374,7 +368,9 @@ const PosixThreadImpl = struct { |
| 374 | | 368 | |
| 375 | fn getCpuCount() !usize { | 369 | fn getCpuCount() !usize { |
| 376 | switch (target.os.tag) { | 370 | switch (target.os.tag) { |
| 377 | .linux => return LinuxThreadImpl.getCpuCount(), | 371 | .linux => { |
| | 372 | return LinuxThreadImpl.getCpuCount(); |
| | 373 | }, |
| 378 | .openbsd => { | 374 | .openbsd => { |
| 379 | var count: c_int = undefined; | 375 | var count: c_int = undefined; |
| 380 | var count_size: usize = @sizeOf(c_int); | 376 | var count_size: usize = @sizeOf(c_int); |
| ... | @@ -413,6 +409,7 @@ const PosixThreadImpl = struct { | ... | @@ -413,6 +409,7 @@ const PosixThreadImpl = struct { |
| 413 | | 409 | |
| 414 | const Instance = struct { | 410 | const Instance = struct { |
| 415 | fn entryFn(raw_arg: ?*c_void) callconv(.C) ?*c_void { | 411 | fn entryFn(raw_arg: ?*c_void) callconv(.C) ?*c_void { |
| | 412 | // @alignCast() below doesn't support zero-sized-types (ZST) |
| 416 | if (@sizeOf(Args) < 1) { | 413 | if (@sizeOf(Args) < 1) { |
| 417 | return callFn(f, @as(Args, undefined)); | 414 | return callFn(f, @as(Args, undefined)); |
| 418 | } | 415 | } |
| ... | @@ -457,8 +454,9 @@ const PosixThreadImpl = struct { | ... | @@ -457,8 +454,9 @@ const PosixThreadImpl = struct { |
| 457 | | 454 | |
| 458 | fn detach(self: Impl) void { | 455 | fn detach(self: Impl) void { |
| 459 | switch (c.pthread_detach(self.handle)) { | 456 | switch (c.pthread_detach(self.handle)) { |
| 460 | os.EINVAL => unreachable, | 457 | 0 => {}, |
| 461 | os.ESRCH => unreachable, | 458 | os.EINVAL => unreachable, // thread handle is not joinable |
| | 459 | os.ESRCH => unreachable, // thread handle is invalid |
| 462 | else => unreachable, | 460 | else => unreachable, |
| 463 | } | 461 | } |
| 464 | } | 462 | } |
| ... | @@ -466,9 +464,9 @@ const PosixThreadImpl = struct { | ... | @@ -466,9 +464,9 @@ const PosixThreadImpl = struct { |
| 466 | fn join(self: Impl) void { | 464 | fn join(self: Impl) void { |
| 467 | switch (c.pthread_join(self.handle, null)) { | 465 | switch (c.pthread_join(self.handle, null)) { |
| 468 | 0 => {}, | 466 | 0 => {}, |
| 469 | os.EINVAL => unreachable, | 467 | os.EINVAL => unreachable, // thread handle is not joinable (or another thread is already joining in) |
| 470 | os.ESRCH => unreachable, | 468 | os.ESRCH => unreachable, // thread handle is invalid |
| 471 | os.EDEADLK => unreachable, | 469 | os.EDEADLK => unreachable, // two threads tried to join each other |
| 472 | else => unreachable, | 470 | else => unreachable, |
| 473 | } | 471 | } |
| 474 | } | 472 | } |
| ... | @@ -476,7 +474,7 @@ const PosixThreadImpl = struct { | ... | @@ -476,7 +474,7 @@ const PosixThreadImpl = struct { |
| 476 | | 474 | |
| 477 | const LinuxThreadImpl = struct { | 475 | const LinuxThreadImpl = struct { |
| 478 | const linux = os.linux; | 476 | const linux = os.linux; |
| 479 | | 477 | |
| 480 | pub const ThreadHandle = i32; | 478 | pub const ThreadHandle = i32; |
| 481 | | 479 | |
| 482 | threadlocal var tls_thread_id: ?Id = null; | 480 | threadlocal var tls_thread_id: ?Id = null; |
| ... | @@ -491,7 +489,8 @@ const LinuxThreadImpl = struct { | ... | @@ -491,7 +489,8 @@ const LinuxThreadImpl = struct { |
| 491 | | 489 | |
| 492 | fn getCpuCount() !usize { | 490 | fn getCpuCount() !usize { |
| 493 | const cpu_set = try os.sched_getaffinity(0); | 491 | const cpu_set = try os.sched_getaffinity(0); |
| 494 | return @as(usize, os.CPU_COUNT(cpu_set)); // TODO should not need this usize cast | 492 | // TODO: should not need this usize cast |
| | 493 | return @as(usize, os.CPU_COUNT(cpu_set)); |
| 495 | } | 494 | } |
| 496 | | 495 | |
| 497 | thread: *ThreadCompletion, | 496 | thread: *ThreadCompletion, |
| ... | @@ -547,7 +546,7 @@ const LinuxThreadImpl = struct { | ... | @@ -547,7 +546,7 @@ const LinuxThreadImpl = struct { |
| 547 | bytes = std.mem.alignForward(bytes, std.mem.page_size); | 546 | bytes = std.mem.alignForward(bytes, std.mem.page_size); |
| 548 | break :blk bytes; | 547 | break :blk bytes; |
| 549 | }; | 548 | }; |
| 550 | | 549 | |
| 551 | // map all memory needed without read/write permissions | 550 | // map all memory needed without read/write permissions |
| 552 | // to avoid committing the whole region right away | 551 | // to avoid committing the whole region right away |
| 553 | const mapped = os.mmap( | 552 | const mapped = os.mmap( |
| ... | @@ -654,7 +653,7 @@ const LinuxThreadImpl = struct { | ... | @@ -654,7 +653,7 @@ const LinuxThreadImpl = struct { |
| 654 | | 653 | |
| 655 | switch (linux.getErrno(linux.futex_wait( | 654 | switch (linux.getErrno(linux.futex_wait( |
| 656 | &self.thread.child_tid.value, | 655 | &self.thread.child_tid.value, |
| 657 | linux.FUTEX_WAIT, | 656 | linux.FUTEX_WAIT, |
| 658 | tid, | 657 | tid, |
| 659 | null, | 658 | null, |
| 660 | ))) { | 659 | ))) { |
| ... | @@ -671,98 +670,145 @@ const LinuxThreadImpl = struct { | ... | @@ -671,98 +670,145 @@ const LinuxThreadImpl = struct { |
| 671 | extern fn __unmap_and_exit(ptr: usize, len: usize) callconv(.C) noreturn; | 670 | extern fn __unmap_and_exit(ptr: usize, len: usize) callconv(.C) noreturn; |
| 672 | comptime { | 671 | comptime { |
| 673 | if (target.os.tag == .linux) { | 672 | if (target.os.tag == .linux) { |
| 674 | asm(switch (target.cpu.arch) { | 673 | asm (switch (target.cpu.arch) { |
| 675 | .i386 => ( | 674 | .i386 => ( |
| 676 | \\.text | 675 | \\.text |
| 677 | \\.global __unmap_and_exit | 676 | \\.global __unmap_and_exit |
| 678 | \\.type __unmap_and_exit, @function | 677 | \\.type __unmap_and_exit, @function |
| 679 | \\__unmap_and_exit: | 678 | \\__unmap_and_exit: |
| 680 | \\ movl $91, %eax | 679 | \\ movl $91, %eax |
| 681 | \\ movl 4(%esp), %ebx | 680 | \\ movl 4(%esp), %ebx |
| 682 | \\ movl 8(%esp), %ecx | 681 | \\ movl 8(%esp), %ecx |
| 683 | \\ int $128 | 682 | \\ int $128 |
| 684 | \\ xorl %ebx, %ebx | 683 | \\ xorl %ebx, %ebx |
| 685 | \\ movl $1, %eax | 684 | \\ movl $1, %eax |
| 686 | \\ int $128 | 685 | \\ int $128 |
| 687 | ), | 686 | ), |
| 688 | .x86_64 => ( | 687 | .x86_64 => ( |
| 689 | \\.text | 688 | \\.text |
| 690 | \\.global __unmap_and_exit | 689 | \\.global __unmap_and_exit |
| 691 | \\.type __unmap_and_exit, @function | 690 | \\.type __unmap_and_exit, @function |
| 692 | \\__unmap_and_exit: | 691 | \\__unmap_and_exit: |
| 693 | \\ movl $11, %eax | 692 | \\ movl $11, %eax |
| 694 | \\ syscall | 693 | \\ syscall |
| 695 | \\ xor %rdi, %rdi | 694 | \\ xor %rdi, %rdi |
| 696 | \\ movl $60, %eax | 695 | \\ movl $60, %eax |
| 697 | \\ syscall | 696 | \\ syscall |
| 698 | ), | 697 | ), |
| 699 | .arm, .armeb, .thumb, .thumbeb => ( | 698 | .arm, .armeb, .thumb, .thumbeb => ( |
| 700 | \\.syntax unified | 699 | \\.syntax unified |
| 701 | \\.text | 700 | \\.text |
| 702 | \\.global __unmap_and_exit | 701 | \\.global __unmap_and_exit |
| 703 | \\.type __unmap_and_exit, %function | 702 | \\.type __unmap_and_exit, %function |
| 704 | \\__unmap_and_exit: | 703 | \\__unmap_and_exit: |
| 705 | \\ mov r7, #91 | 704 | \\ mov r7, #91 |
| 706 | \\ svc 0 | 705 | \\ svc 0 |
| 707 | \\ mov r7, #1 | 706 | \\ mov r7, #1 |
| 708 | \\ svc 0 | 707 | \\ svc 0 |
| 709 | ), | 708 | ), |
| 710 | .aarch64, .aarch64_be, .aarch64_32 => ( | 709 | .aarch64, .aarch64_be, .aarch64_32 => ( |
| 711 | \\.global __unmap_and_exit | 710 | \\.global __unmap_and_exit |
| 712 | \\.type __unmap_and_exit, %function | 711 | \\.type __unmap_and_exit, %function |
| 713 | \\__unmap_and_exit: | 712 | \\__unmap_and_exit: |
| 714 | \\ mov x8, #215 | 713 | \\ mov x8, #215 |
| 715 | \\ svc 0 | 714 | \\ svc 0 |
| 716 | \\ mov x8, #93 | 715 | \\ mov x8, #93 |
| 717 | \\ svc 0 | 716 | \\ svc 0 |
| 718 | ), | 717 | ), |
| 719 | .mips, .mipsel, => ( | 718 | .mips, |
| 720 | \\.set noreorder | 719 | .mipsel, |
| 721 | \\.global __unmap_and_exit | 720 | => ( |
| 722 | \\.type __unmap_and_exit,@function | 721 | \\.set noreorder |
| 723 | \\__unmap_and_exit: | 722 | \\.global __unmap_and_exit |
| 724 | \\ move $sp, $25 | 723 | \\.type __unmap_and_exit,@function |
| 725 | \\ li $2, 4091 | 724 | \\__unmap_and_exit: |
| 726 | \\ syscall | 725 | \\ move $sp, $25 |
| 727 | \\ li $4, 0 | 726 | \\ li $2, 4091 |
| 728 | \\ li $2, 4001 | 727 | \\ syscall |
| 729 | \\ syscall | 728 | \\ li $4, 0 |
| 730 | ), | 729 | \\ li $2, 4001 |
| 731 | .mips64, .mips64el => ( | 730 | \\ syscall |
| 732 | \\.set noreorder | 731 | ), |
| 733 | \\.global __unmap_and_exit | 732 | .mips64, .mips64el => ( |
| 734 | \\.type __unmap_and_exit, @function | 733 | \\.set noreorder |
| 735 | \\__unmap_and_exit: | 734 | \\.global __unmap_and_exit |
| 736 | \\ li $2, 4091 | 735 | \\.type __unmap_and_exit, @function |
| 737 | \\ syscall | 736 | \\__unmap_and_exit: |
| 738 | \\ li $4, 0 | 737 | \\ li $2, 4091 |
| 739 | \\ li $2, 4001 | 738 | \\ syscall |
| 740 | \\ syscall | 739 | \\ li $4, 0 |
| 741 | ), | 740 | \\ li $2, 4001 |
| 742 | .powerpc, .powerpc64, .powerpc64le => ( | 741 | \\ syscall |
| 743 | \\.text | 742 | ), |
| 744 | \\.global __unmap_and_exit | 743 | .powerpc, .powerpc64, .powerpc64le => ( |
| 745 | \\.type __unmap_and_exit, %function | 744 | \\.text |
| 746 | \\__unmap_and_exit: | 745 | \\.global __unmap_and_exit |
| 747 | \\ li 0, 91 | 746 | \\.type __unmap_and_exit, %function |
| 748 | \\ sc | 747 | \\__unmap_and_exit: |
| 749 | \\ li 0, 1 | 748 | \\ li 0, 91 |
| 750 | \\ sc | 749 | \\ sc |
| 751 | \\ blr | 750 | \\ li 0, 1 |
| 752 | ), | 751 | \\ sc |
| 753 | .riscv64 => ( | 752 | \\ blr |
| 754 | \\.global __unmap_and_exit | 753 | ), |
| 755 | \\.type __unmap_and_exit, %function | 754 | .riscv64 => ( |
| 756 | \\__unmap_and_exit: | 755 | \\.global __unmap_and_exit |
| 757 | \\ li a7, 215 | 756 | \\.type __unmap_and_exit, %function |
| 758 | \\ ecall | 757 | \\__unmap_and_exit: |
| 759 | \\ li a7, 93 | 758 | \\ li a7, 215 |
| 760 | \\ ecall | 759 | \\ ecall |
| 761 | ), | 760 | \\ li a7, 93 |
| 762 | else => |cpu_arch| { | 761 | \\ ecall |
| 763 | @compileLog("linux arch", cpu_arch, "is not supported"); | 762 | ), |
| 764 | }, | 763 | else => |cpu_arch| { |
| 765 | }); | 764 | @compileLog("linux arch", cpu_arch, "is not supported"); |
| | 765 | }, |
| | 766 | }); |
| 766 | } | 767 | } |
| 767 | } | 768 | } |
| 768 | }; | | |
| \ No newline at end of file |
| | 769 | }; |
| | 770 | |
| | 771 | test "std.Thread" { |
| | 772 | // Doesn't use testing.refAllDecls() since that would pull in the compileError spinLoopHint. |
| | 773 | _ = AutoResetEvent; |
| | 774 | _ = Futex; |
| | 775 | _ = ResetEvent; |
| | 776 | _ = StaticResetEvent; |
| | 777 | _ = Mutex; |
| | 778 | _ = Semaphore; |
| | 779 | _ = Condition; |
| | 780 | } |
| | 781 | |
| | 782 | fn testIncrementNotify(value: *usize, event: *ResetEvent) void { |
| | 783 | value.* += 1; |
| | 784 | event.set(); |
| | 785 | } |
| | 786 | |
| | 787 | test "Thread.join" { |
| | 788 | if (std.builtin.single_threaded) return error.SkipZigTest; |
| | 789 | |
| | 790 | var value: usize = 0; |
| | 791 | var event: ResetEvent = undefined; |
| | 792 | try event.init(); |
| | 793 | defer event.deinit(); |
| | 794 | |
| | 795 | const thread = try Thread.spawn(.{}, testIncrementNotify, .{&value, &event}); |
| | 796 | thread.join(); |
| | 797 | |
| | 798 | try std.testing.expectEqual(value, 1); |
| | 799 | } |
| | 800 | |
| | 801 | test "Thread.detach" { |
| | 802 | if (std.builtin.single_threaded) return error.SkipZigTest; |
| | 803 | |
| | 804 | var value: usize = 0; |
| | 805 | var event: ResetEvent = undefined; |
| | 806 | try event.init(); |
| | 807 | defer event.deinit(); |
| | 808 | |
| | 809 | const thread = try Thread.spawn(.{}, testIncrementNotify, .{&value, &event}); |
| | 810 | thread.detach(); |
| | 811 | |
| | 812 | event.wait(); |
| | 813 | try std.testing.expectEqual(value, 1); |
| | 814 | } |
| \ No newline at end of file |