Threads and what they need to cooperate: Thread to run a procedure on its own thread, Mutex, Condition_Variable
and Semaphore to coordinate, and Thread_Group for a pool of workers.
Not available on wasm: it has no operating-system threads.
Queues work for one of the group's workers; group_proc receives the same pointer. The work must stay valid until it
comes back from get_completed_work. Send work to a group that has been set up and not shut down.
Stops the workers after they finish what is queued and frees the group. Returns false if they did not all stop within
timeout_milliseconds (-1 waits as long as it takes).
Waits until every piece of work added to group has been run by a worker, or until timeout_milliseconds pass (-1 waits as
long as it takes). Returns false on a timeout. The group has to be started, and has to have a live worker to run the work:
if every worker stopped with work still queued, only a timeout ends the wait.
A lock that lets one thread at a time into a piece of code. Set it up with init, take it with lock, give it back with
unlock. It can be locked again by the thread that already holds it.
Lets a thread sleep until another thread says the shared state changed. Use it with a Mutex: wait releases the mutex
while sleeping, and wake or wake_all end the sleep.
An operating-system thread. Set it up with thread_init, fill in data if the procedure needs it, run it with
thread_start, wait with thread_is_done and clean up with thread_deinit. The Thread must stay where it is until
thread_deinit. The procedure gets a pointer to it, with data, the thread's index and its own temporary storage.
A pool of worker threads that run one procedure over pieces of work. Set it up with init, queue work with add_work,
start the workers and collect results with get_completed_work. shutdown stops the workers.
Set before start to run something on each worker thread, once, before its first piece of work. worker_index is the
worker's place in the group, 0 up to the number of threads minus one. A good place to build state the worker keeps.
allocator: Allocator
worker_info: [] Worker_Info
worker_info_data_to_free: *void
next_worker_index: s32
outstanding: s64
Work added and not yet finished, and what wait_for_all sleeps on.