Skip to documentation
SLOP

tiny.tldr.parallel

Reference tiny.tldr parallel

Defined in tiny.tldr.

API (13)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

No direct callersNo direct callstiny.tldrparallel
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callstest sourcelib.tldr.src.paralleltest: failure slots merge earliest re...parallelFailureSlots
Static calls · unresolved targets: 3 · external targets: 2.
Called byCallsNo direct callsprivate sourcelib.tldr.src.formats.elf.relocation.applicationapplyJobsParallelformats.elf.relocation.applicationmaterializeprivate sourcelib.tldr.src.parallel.WorkerArenaPoolAllocati...runtest sourcelib.tldr.src.paralleltest: worker arena pool routes worker...parallel.WorkerArenaPoolallocator
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsprivate sourcelib.tldr.src.formats.elf.relocation.applicationapplyJobsParallelformats.elf.relocation.applicationmaterializeprivate sourcelib.tldr.src.parallel.WorkerArenaPoolAllocati...runtest sourcelib.tldr.src.paralleltest: worker arena pool routes worker...parallel.WorkerArenaPooldeinit
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallsNo direct callsprivate sourcelib.tldr.src.formats.elf.relocation.applicationapplyJobsParallelformats.elf.relocation.applicationmaterializeprivate sourcelib.tldr.src.parallel.WorkerArenaPoolAllocati...runtest sourcelib.tldr.src.paralleltest: worker arena pool routes worker...parallel.WorkerArenaPoolinit
Static calls · unresolved targets: 1 · external targets: 5.
Called byCallsNo direct callsprivate sourcelib.tldr.src.formats.elf.archive.prefetchprepareSummariesprivate sourcelib.tldr.src.formats.elf.archive.selectparseSelectedObjectsformats.elf.ehframe.Scanprepareformats.elf.ifunc.Collectorinitformats.elf.image.symbolsappendExecutable+15 moreparallelchooseWorkers
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsprivate sourcelib.tldr.src.formats.elf.image.symbolsappendExecutableParallelprivate sourcelib.tldr.src.formats.elf.payloadcopyBytesformats.elf.relocation.decodeparseRelocationsBySectionparallelforItemstest sourcelib.tldr.src.paralleltest: background job runs alongside f...+4 moreprivate sourcelib.tldr.src.parallelChunkCallbackTypeparallelchooseWorkersparallelrangeStartparallelforChunks
Static calls · unresolved targets: 2 · external targets: 2.
Called byCallsprivate sourcelib.tldr.src.formats.elf.archive.prefetchprepareSummariesprivate sourcelib.tldr.src.formats.elf.archive.selectparseSelectedObjectsformats.elf.ehframe.Scanprepareformats.elf.ifunc.Collectorinitprivate sourcelib.tldr.src.formats.elf.layout.collectassignChains+14 moreprivate sourcelib.tldr.src.parallelItemCallbackTypeprivate sourcelib.tldr.src.parallelItemStateTypeparallelchooseWorkersparallelforChunksparallelforItems
Static calls · unresolved targets: 2 · external targets: 0.
Called byCallsNo direct callsprivate sourcelib.tldr.src.formats.elf.archive.prefetch.Pre...inittest sourcelib.tldr.src.formats.elf.archive.prefetchtest: prefetcher stashes the next arc...private sourcelib.tldr.src.incremental.formatvalidateRecordsparalleloverlapAvailable
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callsprivate sourcelib.tldr.src.parallel.WorkerPoolrunClaimparallelforChunkstest sourcelib.tldr.src.paralleltest: rangeStart partitions contiguou...parallelrangeStart
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.tldr.src.paralleltest: parallel sort matches serial so...private sourcelib.tldr.src.parallelSortCallbacksTypeprivate sourcelib.tldr.src.parallelSortChunkStateTypeprivate sourcelib.tldr.src.parallelSortMergeStateTypeparallelchooseWorkersparallelforItemsparallelsortItems
Static calls · unresolved targets: 1 · external targets: 0.

Source: lib/tldr/src/parallel.zig

zig
const std = @import("std");const allocators = @import("alloc");const alloc_arena = @import("alloc_arena");const sys = @import("sys");const Allocator = std.mem.Allocator;const Arena = alloc_arena.Arena;pub const max_workers = 64;pub const WorkerArenaPool = struct {    backing_owner: ?*allocators.LockedAllocator = null,    arenas: []Arena = &.{},    pub fn init(storage: Allocator, worker_count: usize) !WorkerArenaPool {        std.debug.assert(worker_count <= max_workers);        const count = @max(worker_count, 1);        std.debug.assert(count >= 1);        std.debug.assert(count <= max_workers);        const backing_owner = try storage.create(allocators.LockedAllocator);        errdefer storage.destroy(backing_owner);        backing_owner.* = allocators.LockedAllocator.init(storage);        const arenas = try storage.alloc(Arena, count);        for (arenas) |*arena| arena.* = Arena.init(backing_owner.allocator());        return .{ .backing_owner = backing_owner, .arenas = arenas };    }    pub fn deinit(self: *WorkerArenaPool) void {        std.debug.assert(self.backing_owner != null);        std.debug.assert(self.arenas.len >= 1);        const backing_owner = self.backing_owner.?;        const storage = backing_owner.child;        for (self.arenas) |*arena| arena.deinit();        storage.free(self.arenas);        storage.destroy(backing_owner);        self.* = .{};    }    pub fn allocator(self: *WorkerArenaPool, worker: usize) Allocator {        std.debug.assert(self.backing_owner != null);        std.debug.assert(worker < self.arenas.len);        return self.arenas[worker].allocator();    }};pub fn FailureSlots(comptime Failure: type) type {    return struct {        items: []Failure = &.{},        empty: Failure,        const Self = @This();        pub fn init(storage: Allocator, slot_count: usize, empty: Failure) !Self {            const count = @max(slot_count, 1);            const items = try storage.alloc(Failure, count);            for (items) |*item| item.* = empty;            return .{ .items = items, .empty = empty };        }        pub fn deinit(self: *Self, storage: Allocator) void {            storage.free(self.items);            self.* = .{ .empty = self.empty };        }        pub fn record(self: *Self, worker: usize, failure: Failure) void {            self.items[worker] = failure;        }        pub fn earliest(            self: *const Self,            comptime found: fn (Failure) bool,            comptime before: fn (Failure, Failure) bool,        ) ?Failure {            var best: ?Failure = null;            for (self.items) |item| {                if (!found(item)) continue;                if (best == null or before(item, best.?)) best = item;            }            return best;        }    };}const ChunkCallback = *const fn (*anyopaque, usize, usize, usize) void;const Job = struct {    total: usize = 0,    slots: usize = 0,    context: *anyopaque = undefined,    callback: ChunkCallback = undefined,    background: bool = false,    next_slot: usize = 0,    pending: usize = 0,    prev: ?*Job = null,    next: ?*Job = null,};const Claim = struct {    job: *Job,    slot: usize,};const JobQueue = struct {    head: ?*Job = null,    tail: ?*Job = null,    fn append(self: *JobQueue, job: *Job) void {        job.prev = self.tail;        job.next = null;        if (self.tail) |tail| {            tail.next = job;        } else {            self.head = job;        }        self.tail = job;    }    fn remove(self: *JobQueue, job: *Job) void {        if (job.prev) |prev| {            prev.next = job.next;        } else {            self.head = job.next;        }        if (job.next) |next| {            next.prev = job.prev;        } else {            self.tail = job.prev;        }        job.prev = null;        job.next = null;    }};const WorkerPool = struct {    mutex: sys.thread.Mutex = .{},    ready: sys.thread.Condition = .{},    done: sys.thread.Condition = .{},    worker_count: usize = 0,    spawn_failed: bool = false,    foreground: JobQueue = .{},    background: JobQueue = .{},    fn ensureStarted(self: *WorkerPool, desired_workers: usize) bool {        if (desired_workers == 0) return false;        self.mutex.lock();        defer self.mutex.unlock();        const desired = @min(desired_workers, max_workers - 1);        while (self.worker_count < desired and !self.spawn_failed) {            const handle = sys.thread.spawn(workerLoop, .{self}) catch {                self.spawn_failed = true;                break;            };            self.worker_count += 1;            handle.detach();        }        return self.worker_count != 0;    }    fn submit(self: *WorkerPool, job: *Job, desired_workers: usize, reserved_slots: usize) bool {        std.debug.assert(job.slots > reserved_slots);        if (!self.ensureStarted(desired_workers)) return false;        self.mutex.lock();        job.next_slot = reserved_slots;        job.pending = job.slots;        self.queueFor(job).append(job);        self.ready.broadcast();        self.mutex.unlock();        return true;    }    fn queueFor(self: *WorkerPool, job: *Job) *JobQueue {        return if (job.background) &self.background else &self.foreground;    }    fn claimAnyLocked(self: *WorkerPool) ?Claim {        const job = self.foreground.head orelse self.background.head orelse return null;        return self.claimFromLocked(job);    }    fn claimFromLocked(self: *WorkerPool, job: *Job) Claim {        const slot = job.next_slot;        job.next_slot += 1;        if (job.next_slot == job.slots) self.queueFor(job).remove(job);        return .{ .job = job, .slot = slot };    }    fn runClaim(claim: Claim) void {        const job = claim.job;        const start = rangeStart(job.total, job.slots, claim.slot);        const end = rangeStart(job.total, job.slots, claim.slot + 1);        job.callback(job.context, claim.slot, start, end);    }    fn finishLocked(self: *WorkerPool, job: *Job) void {        job.pending -= 1;        if (job.pending == 0) self.done.broadcast();    }    fn workerLoop(self: *WorkerPool) void {        self.mutex.lock();        while (true) {            if (self.claimAnyLocked()) |claim| {                self.mutex.unlock();                runClaim(claim);                self.mutex.lock();                self.finishLocked(claim.job);            } else {                self.ready.wait(&self.mutex);            }        }    }    fn wait(self: *WorkerPool, job: *Job) void {        self.mutex.lock();        while (job.pending != 0) {            if (job.next_slot < job.slots) {                const claim = self.claimFromLocked(job);                self.mutex.unlock();                runClaim(claim);                self.mutex.lock();                self.finishLocked(job);            } else {                self.done.wait(&self.mutex);            }        }        self.mutex.unlock();    }    fn completeReserved(self: *WorkerPool, job: *Job) void {        runClaim(.{ .job = job, .slot = 0 });        self.mutex.lock();        self.finishLocked(job);        self.mutex.unlock();        self.wait(job);    }};var global_worker_pool = WorkerPool{};pub fn overlapAvailable() bool {    return sys.thread.threadsSupported() and sys.thread.cpuCount() > 1;}pub fn chooseWorkers(total: usize, requested: usize) usize {    if (total <= 1 or !sys.thread.threadsSupported()) return 1;    const available = sys.thread.cpuCount();    const ceiling = if (requested == 0) available else @min(available, requested);    return @max(1, @min(@min(ceiling, total), max_workers));}pub fn rangeStart(total: usize, workers: usize, worker: usize) usize {    const base = total / workers;    const remainder = total % workers;    return worker * base + @min(worker, remainder);}fn ChunkCallbackType(    comptime Context: type,    comptime body: fn (Context, usize, usize, usize) void,) type {    return struct {        fn run_opaque(opaque_context: *anyopaque, worker: usize, start: usize, end: usize) void {            const typed_context: *Context = @ptrCast(@alignCast(opaque_context));            body(typed_context.*, worker, start, end);        }    };}pub fn forChunks(    total: usize,    requested_workers: usize,    context: anytype,    comptime body: fn (@TypeOf(context), usize, usize, usize) void,) void {    const workers = chooseWorkers(total, requested_workers);    if (workers <= 1) {        body(context, 0, 0, total);        return;    }    const Context: type = @TypeOf(context);    const Callback: type = ChunkCallbackType(Context, body);    var context_storage = context;    var job = Job{        .total = total,        .slots = workers,        .context = @ptrCast(&context_storage),        .callback = Callback.run_opaque,    };    if (global_worker_pool.submit(&job, workers - 1, 1)) {        global_worker_pool.completeReserved(&job);        return;    }    var worker: usize = 0;    while (worker < workers) : (worker += 1) {        body(            context,            worker,            rangeStart(total, workers, worker),            rangeStart(total, workers, worker + 1),        );    }}fn ItemStateType(comptime Context: type) type {    return struct {        user: Context,        next: std.atomic.Value(usize) = std.atomic.Value(usize).init(0),        total: usize,    };}fn ItemCallbackType(    comptime Context: type,    comptime State: type,    comptime body: fn (Context, usize, usize) void,) type {    return struct {        fn run(state: *State, worker: usize, start: usize, end: usize) void {            _ = start;            _ = end;            while (true) {                const item = state.next.fetchAdd(1, .monotonic);                if (item >= state.total) break;                body(state.user, worker, item);            }        }    };}pub fn forItems(    total: usize,    requested_workers: usize,    context: anytype,    comptime body: fn (@TypeOf(context), usize, usize) void,) void {    const workers = chooseWorkers(total, requested_workers);    if (workers <= 1) {        var item: usize = 0;        while (item < total) : (item += 1) body(context, 0, item);        return;    }    const Context: type = @TypeOf(context);    const State: type = ItemStateType(Context);    const Callback: type = ItemCallbackType(Context, State, body);    var state = State{ .user = context, .total = total };    forChunks(workers, workers, &state, Callback.run);}pub fn Background(    comptime Context: type,    comptime body: fn (Context, usize) void,) type {    return struct {        job: Job = .{ .background = true },        context: Context = undefined,        submitted: bool = false,        const Self = @This();        pub fn submit(self: *Self, total: usize, requested_workers: usize, context: Context) void {            std.debug.assert(!self.submitted);            if (total == 0) return;            self.context = context;            self.job.total = total;            self.job.slots = total;            self.job.context = @ptrCast(self);            self.job.callback = runOpaque;            if (sys.thread.threadsSupported()) {                const available = sys.thread.cpuCount();                const ceiling = if (requested_workers == 0)                    available                else                    @min(available, requested_workers);                const desired = @max(1, @min(@min(ceiling, total), max_workers));                if (global_worker_pool.submit(&self.job, desired, 0)) {                    self.submitted = true;                    return;                }            }            var item: usize = 0;            while (item < total) : (item += 1) body(context, item);        }        pub fn wait(self: *Self) void {            if (!self.submitted) return;            global_worker_pool.wait(&self.job);            self.submitted = false;        }        fn runOpaque(opaque_context: *anyopaque, slot: usize, start: usize, end: usize) void {            _ = start;            _ = end;            const self: *Self = @ptrCast(@alignCast(opaque_context));            body(self.context, slot);        }    };}fn SortChunkStateType(comptime T: type, comptime Context: type) type {    return struct {        items: []T,        chunk: usize,        user: Context,    };}fn SortMergeStateType(comptime T: type, comptime Context: type) type {    return struct {        source: []T,        target: []T,        width: usize,        user: Context,    };}fn SortCallbacksType(    comptime T: type,    comptime Context: type,    comptime SortState: type,    comptime MergeState: type,    comptime less: fn (Context, T, T) bool,) type {    return struct {        fn sort_chunk(state: *SortState, worker: usize, index: usize) void {            _ = worker;            const low = index * state.chunk;            if (low >= state.items.len) return;            const high = @min(low + state.chunk, state.items.len);            std.sort.pdq(T, state.items[low..high], state.user, less);        }        fn merge_segment(state: *MergeState, worker: usize, index: usize) void {            _ = worker;            const low = index * 2 * state.width;            if (low >= state.source.len) return;            const middle = @min(low + state.width, state.source.len);            const high = @min(low + 2 * state.width, state.source.len);            merge_runs(                state.user,                state.source[low..middle],                state.source[middle..high],                state.target[low..high],            );        }        fn merge_runs(user: Context, left: []const T, right: []const T, out: []T) void {            var left_index: usize = 0;            var right_index: usize = 0;            var out_index: usize = 0;            while (left_index < left.len and right_index < right.len) : (out_index += 1) {                if (less(user, right[right_index], left[left_index])) {                    out[out_index] = right[right_index];                    right_index += 1;                } else {                    out[out_index] = left[left_index];                    left_index += 1;                }            }            if (left_index < left.len) {                @memcpy(out[out_index..], left[left_index..]);            } else if (right_index < right.len) {                @memcpy(out[out_index..], right[right_index..]);            }        }    };}pub fn sortItems(    comptime T: type,    items: []T,    context: anytype,    comptime less: fn (@TypeOf(context), T, T) bool,    scratch: []T,    requested_workers: usize,) void {    const workers = chooseWorkers(items.len, requested_workers);    if (workers <= 1 or items.len < 2) {        std.sort.pdq(T, items, context, less);        return;    }    std.debug.assert(scratch.len >= items.len);    const Context: type = @TypeOf(context);    const SortState: type = SortChunkStateType(T, Context);    const MergeState: type = SortMergeStateType(T, Context);    const Callbacks: type = SortCallbacksType(T, Context, SortState, MergeState, less);    const total = items.len;    const chunk = (total + workers - 1) / workers;    var sort_state = SortState{ .items = items, .chunk = chunk, .user = context };    forItems(workers, workers, &sort_state, Callbacks.sort_chunk);    var source = items;    var target = scratch[0..total];    var width = chunk;    while (width < total) : (width *= 2) {        const segments = (total + 2 * width - 1) / (2 * width);        var merge_state = MergeState{            .source = source,            .target = target,            .width = width,            .user = context,        };        forItems(segments, workers, &merge_state, Callbacks.merge_segment);        const swapped = source;        source = target;        target = swapped;    }    if (source.ptr != items.ptr) @memcpy(items, source);}fn lessUsize(_: void, left: usize, right: usize) bool {    return left < right;}const U8HitsContext = struct {    hits: []u8,};const U16HitsContext = struct {    hits: []u16,};const SumContext = struct {    sum: *usize,};const TestFailure = struct {    found: bool = false,    index: usize = 0,};const WorkerArenaPoolAllocationFailures = struct {    fn run(storage: Allocator) !void {        var pool = try WorkerArenaPool.init(storage, 4);        defer pool.deinit();        _ = try pool.allocator(3).alloc(u8, 128);    }};fn count_u8_chunks(context: U8HitsContext, _: usize, start: usize, end: usize) void {    var index = start;    while (index < end) : (index += 1) context.hits[index] += 1;}fn count_u16_chunks(context: U16HitsContext, _: usize, start: usize, end: usize) void {    var index = start;    while (index < end) : (index += 1) context.hits[index] += 1;}fn count_u8_item(context: U8HitsContext, _: usize, item: usize) void {    context.hits[item] += 1;}fn count_u8_background_item(context: U8HitsContext, item: usize) void {    context.hits[item] += 1;}fn failure_found(failure: TestFailure) bool {    return failure.found;}fn failure_before(left: TestFailure, right: TestFailure) bool {    return left.index < right.index;}const TestBackground = Background(U8HitsContext, count_u8_background_item);const chained_background_total = 128;const nested_parallel_inner_total = 64;const ChainedBackgroundContext = struct {    hits: []u8,    second: *TestBackground,    second_hits: []u8,};fn start_chained_background(context: ChainedBackgroundContext, item: usize) void {    context.hits[item] += 1;    if (item == 0) {        context.second.submit(chained_background_total, 2, .{ .hits = context.second_hits });    }}const ChainedBackground = Background(ChainedBackgroundContext, start_chained_background);fn count_nested_chunks(context: U8HitsContext, _: usize, start: usize, end: usize) void {    var chunk = start;    while (chunk < end) : (chunk += 1) {        const offset = chunk * nested_parallel_inner_total;        forItems(nested_parallel_inner_total, 2, U8HitsContext{            .hits = context.hits[offset..][0..nested_parallel_inner_total],        }, count_u8_item);    }}fn sum_chunk(context: SumContext, _: usize, start: usize, end: usize) void {    context.sum.* += end - start;}test "parallel sort matches serial sort for total orders" {    const allocator = std.testing.allocator;    var prng = std.Random.DefaultPrng.init(0x74696e79736f7274);    const random = prng.random();    for ([_]usize{ 0, 1, 2, 63, 4096, 40_001 }) |count| {        const items = try allocator.alloc(usize, count);        defer allocator.free(items);        const expected = try allocator.alloc(usize, count);        defer allocator.free(expected);        const scratch = try allocator.alloc(usize, count);        defer allocator.free(scratch);        for (items, 0..) |*item, index| {            item.* = random.uintLessThan(usize, 1 << 40) * 100_000 + index;        }        @memcpy(expected, items);        std.sort.pdq(usize, expected, {}, lessUsize);        sortItems(usize, items, {}, lessUsize, scratch, 8);        try std.testing.expectEqualSlices(usize, expected, items);    }}test "worker arena pool routes worker allocations through bounded caller storage" {    var storage_bytes: [4096]u8 = undefined;    var storage = std.heap.FixedBufferAllocator.init(&storage_bytes);    var pool = try WorkerArenaPool.init(storage.allocator(), 4);    defer pool.deinit();    var worker: usize = 0;    while (worker < 4) : (worker += 1) {        const bytes = try pool.allocator(worker).alloc(u8, 32);        @memset(bytes, @intCast(worker + 1));        try std.testing.expect(storage.ownsSlice(bytes));        try std.testing.expectEqual(@as(u8, @intCast(worker + 1)), bytes[0]);    }    try std.testing.expectError(        error.OutOfMemory,        pool.allocator(0).alloc(u8, storage_bytes.len),    );}test "worker arena pool cleans every caller storage allocation failure" {    try std.testing.checkAllAllocationFailures(        std.testing.allocator,        WorkerArenaPoolAllocationFailures.run,        .{},    );}test "forChunks covers every index exactly once" {    const total = 10_000;    var hits = @as([total]u8, @splat(0));    forChunks(total, 8, U8HitsContext{ .hits = &hits }, count_u8_chunks);    for (hits) |hit| try std.testing.expectEqual(@as(u8, 1), hit);}test "forItems covers every item exactly once" {    const total = 10_000;    var hits = @as([total]u8, @splat(0));    forItems(total, 8, U8HitsContext{ .hits = &hits }, count_u8_item);    for (hits) |hit| try std.testing.expectEqual(@as(u8, 1), hit);}test "failure slots merge earliest recorded failure" {    var slots = try FailureSlots(TestFailure).init(std.testing.allocator, 4, .{});    defer slots.deinit(std.testing.allocator);    slots.record(2, .{ .found = true, .index = 40 });    slots.record(1, .{ .found = true, .index = 11 });    const earliest = slots.earliest(failure_found, failure_before) orelse        return error.MissingFailure;    try std.testing.expectEqual(@as(usize, 11), earliest.index);}test "forChunks handles repeated pooled calls" {    const total = 4096;    const rounds = 8;    var hits = @as([total]u16, @splat(0));    var round: usize = 0;    while (round < rounds) : (round += 1) {        forChunks(total, 8, U16HitsContext{ .hits = &hits }, count_u16_chunks);    }    for (hits) |hit| try std.testing.expectEqual(@as(u16, rounds), hit);}test "background job covers every item exactly once" {    const total = 512;    var hits = @as([total]u8, @splat(0));    var prefetch = TestBackground{};    prefetch.submit(total, 4, .{ .hits = &hits });    prefetch.wait();    for (hits) |hit| try std.testing.expectEqual(@as(u8, 1), hit);}test "background job runs alongside foreground chunk jobs" {    const background_total = 256;    const foreground_total = 4096;    var background_hits = @as([background_total]u8, @splat(0));    var foreground_hits = @as([foreground_total]u8, @splat(0));    var prefetch = TestBackground{};    prefetch.submit(background_total, 2, .{ .hits = &background_hits });    var round: usize = 0;    while (round < 4) : (round += 1) {        forChunks(foreground_total, 8, U8HitsContext{ .hits = &foreground_hits }, count_u8_chunks);    }    prefetch.wait();    for (background_hits) |hit| try std.testing.expectEqual(@as(u8, 1), hit);    for (foreground_hits) |hit| try std.testing.expectEqual(@as(u8, 4), hit);}test "background job may chain a following background job" {    var first_hits = @as([1]u8, @splat(0));    var second_hits = @as([chained_background_total]u8, @splat(0));    var second = TestBackground{};    var first = ChainedBackground{};    first.submit(1, 2, .{        .hits = first_hits[0..1],        .second = &second,        .second_hits = &second_hits,    });    first.wait();    second.wait();    try std.testing.expectEqual(@as(u8, 1), first_hits[0]);    for (second_hits) |hit| try std.testing.expectEqual(@as(u8, 1), hit);}test "background job with zero items completes without submission" {    var prefetch = TestBackground{};    prefetch.submit(0, 4, .{ .hits = &.{} });    prefetch.wait();    prefetch.wait();}test "nested parallel sections complete without stalling the pool" {    const outer_chunks = 4;    var hits = @as([(outer_chunks * nested_parallel_inner_total)]u8, @splat(0));    forChunks(outer_chunks, outer_chunks, U8HitsContext{ .hits = &hits }, count_nested_chunks);    for (hits) |hit| try std.testing.expectEqual(@as(u8, 1), hit);}test "forChunks single worker runs inline over full range" {    const total = 256;    var sum: usize = 0;    forChunks(total, 1, SumContext{ .sum = &sum }, sum_chunk);    try std.testing.expectEqual(@as(usize, total), sum);}test "rangeStart partitions contiguously and totals correctly" {    const total = 1003;    const workers = 7;    try std.testing.expectEqual(@as(usize, 0), rangeStart(total, workers, 0));    try std.testing.expectEqual(@as(usize, total), rangeStart(total, workers, workers));    var worker: usize = 0;    while (worker < workers) : (worker += 1) {        try std.testing.expect(            rangeStart(total, workers, worker) <= rangeStart(total, workers, worker + 1),        );    }}

Source: lib/tldr/src/root.zig:60

zig
pub const parallel = @import("parallel.zig");

Complete caller list for parallel.chooseWorkers

20 direct callers.

Complete caller list for parallel.forChunks

9 direct callers.

Complete caller list for parallel.forItems

19 direct callers.

Audit

Definitions14
Public names14
Members2
Version26.7.0
Revisiondaab053ee433