Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

WindowsThreadImpl

Thread.WindowsThreadImpl
const WindowsThreadImpl = struct

File

lib/std/Thread.zig:497

Code

const WindowsThreadImpl = struct {
    pub const ThreadHandle = windows.HANDLE;

    fn getCurrentId() windows.DWORD {
        return windows.GetCurrentThreadId();
    }

    fn getCpuCount() !usize {
        // Faster than calling into GetSystemInfo(), even if amortized.
        return windows.peb().NumberOfProcessors;
    }

    thread: *ThreadCompletion,

    const ThreadCompletion = struct {
        completion: Completion,
        heap_ptr: windows.PVOID,
        heap_handle: *windows.HEAP,
        thread_handle: windows.HANDLE = undefined,

        fn free(self: ThreadCompletion) void {
            const status = windows.ntdll.RtlFreeHeap(self.heap_handle, .{}, self.heap_ptr);
            assert(status != 0);
        }
    };

    fn spawn(config: SpawnConfig, comptime f: anytype, args: anytype) !Impl {
        const Args = @TypeOf(args);
        const Instance = struct {
            fn_args: Args,
            thread: ThreadCompletion,

            fn entryFn(raw_ptr: windows.PVOID) callconv(.winapi) windows.NTSTATUS {
                const self: *@This() = @ptrCast(@alignCast(raw_ptr));
                defer switch (self.thread.completion.swap(.completed, .seq_cst)) {
                    .running => {},
                    .completed => unreachable,
                    .detached => self.thread.free(),
                };
                return callFn(f, self.fn_args);
            }
        };

        const heap_handle = windows.GetProcessHeap() orelse return error.OutOfMemory;
        const alloc_bytes = @alignOf(Instance) + @sizeOf(Instance);
        const alloc_ptr = windows.ntdll.RtlAllocateHeap(heap_handle, .{}, alloc_bytes) orelse return error.OutOfMemory;
        errdefer assert(windows.ntdll.RtlFreeHeap(heap_handle, .{}, alloc_ptr) != 0);

        const instance_bytes = @as([*]u8, @ptrCast(alloc_ptr))[0..alloc_bytes];
        var fba = std.heap.FixedBufferAllocator.init(instance_bytes);
        const instance = fba.allocator().create(Instance) catch unreachable;
        instance.* = .{
            .fn_args = args,
            .thread = .{
                .completion = Completion.init(.running),
                .heap_ptr = alloc_ptr,
                .heap_handle = heap_handle,
            },
        };

        // Windows appears to only support SYSTEM.BASIC_INFORMATION.AllocationGranularity
        // minimum stack size. Going lower makes it default to that specified in the executable
        // (~1mb). Its also fine if the limit here is incorrect as stack size is only a hint.
        const stack_size = @max(64 * 1024, std.math.lossyCast(u32, config.stack_size));

        // Intended to be equivalent to a kernel32.CreateThread call with no flags set.
        // However, CreateThread is just a wrapper around CreateRemoteThreadEx,
        // so that's the more relevant function in this context.
        //
        // https://github.com/wine-mirror/wine/blob/3d128be6400b3869119d293d0c8fa9e7702978f8/dlls/kernelbase/thread.c#L85
        instance.thread.thread_handle = blk: {
            var active_ctx: ?windows.HANDLE = undefined;
            // Note: Can return null on SUCCESS
            switch (windows.ntdll.RtlGetActiveActivationContext(&active_ctx)) {
                .SUCCESS => {},
                else => |status| return windows.unexpectedStatus(status),
            }
            defer if (active_ctx) |ctx| windows.ntdll.RtlReleaseActivationContext(ctx);

            var teb: *windows.TEB = undefined;
            var attr_list = windows.PS.ATTRIBUTE.LIST{
                .TotalLength = @sizeOf(windows.PS.ATTRIBUTE.LIST),
                .Attributes = .{
                    .{
                        .Attribute = .TEB_ADDRESS,
                        .Size = @sizeOf(*windows.TEB),
                        .u = .{
                            .ValuePtr = @ptrCast(&teb),
                        },
                        .ReturnLength = null,
                    },
                },
            };

            var thread_handle: windows.HANDLE = undefined;
            switch (windows.ntdll.NtCreateThreadEx(
                &thread_handle,
                .{ .MAXIMUM_ALLOWED = true },
                &.{},
                windows.GetCurrentProcess(),
                Instance.entryFn,
                instance,
                .{ .CREATE_SUSPENDED = true },
                0,
                @fromBackingInt(@intCast(stack_size)),
                .default,
                &attr_list,
            )) {
                .SUCCESS => {},
                else => |status| return windows.unexpectedStatus(status),
            }

            if (active_ctx) |ctx| {
                var cookie: windows.ULONG = 0;
                switch (windows.ntdll.RtlActivateActivationContextEx(0, teb, ctx, &cookie)) {
                    .SUCCESS => {},
                    else => |status| return windows.unexpectedStatus(status),
                }
            }

            switch (windows.ntdll.NtResumeThread(thread_handle, null)) {
                .SUCCESS => {},
                else => |status| return windows.unexpectedStatus(status),
            }

            break :blk thread_handle;
        };

        return Impl{ .thread = &instance.thread };
    }

    fn getHandle(self: Impl) ThreadHandle {
        return self.thread.thread_handle;
    }

    fn detach(self: Impl) void {
        windows.CloseHandle(self.thread.thread_handle);
        switch (self.thread.completion.swap(.detached, .seq_cst)) {
            .running => {},
            .completed => self.thread.free(),
            .detached => unreachable,
        }
    }

    fn join(self: Impl) void {
        const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER);
        switch (windows.ntdll.NtWaitForSingleObject(self.thread.thread_handle, .FALSE, &infinite_timeout)) {
            windows.NTSTATUS.WAIT_0 => {},
            else => |status| windows.unexpectedStatus(status) catch unreachable,
        }
        windows.CloseHandle(self.thread.thread_handle);
        assert(self.thread.completion.load(.seq_cst) == .completed);
        self.thread.free();
    }
}