This module deals with Threads. The general items covered in this module include:
- Threads
- Mutexes
- Threading primitives
- Semaphores
- Thread Groups
Thread
Here is the struct definition for the Thread
primitive:
Thread :: struct {
index : Thread_Index;
proc : Thread_Proc;
data : *void;
workspace : Workspace;
starting_context: Context;
starting_temporary_storage: Temporary_Storage;
allocator_used_for_temporary_storage: Allocator;
worker_info: *Thread_Group.Worker_Info; // Used by Thread_Group; unused otherwise.
#if _STACK_TRACE stack_trace_sentinel: Stack_Trace_Node;
using specific : Thread_Os_Specific;
}
The Thread_Os_Specific
is information for specific OS’es. The index
starts at zero, and is incremented every time a new thread is spawned with thread_init
.
thread_create :: (proc: Thread_Proc, temporary_storage_size : s32 = 16384, starting_storage: *Temporary_Storage = null) -> *Thread;
This function creates a thread using heap allocation.
thread_init :: (thread: *Thread, proc: Thread_Proc, temporary_storage_size : s32 = 16384, starting_storage: *Temporary_Storage = null) -> bool {
This function initializes a thread. This function does not start a thread, but rather just initializes data.
thread_start :: (thread: *Thread);
This function starts the thread.
Thread Group
A Thread_Group
is a way of launching a bunch of threads to asynchronously respond to requests. You can initialize a thread group using the init :: ()
function. Call start :: ()
on the Thread_Group
to start running the threads. When you want the threads to stop running, call shutdown :: ()
. It is best practice to call shutdown before your program exits.
The Thread_Group
specializes in calling only one function. It does not compute any arbitrary amount of work passed to it, and only deals with one specific function.
init :: (group: *Thread_Group, num_threads: s32, group_proc: Thread_Group_Proc, enable_work_stealing := false);
This function initializes the Thread_Group
. Changing the number of threads increases the number of threads in the Thread_Group
.
The Thread_Group_Proc
function pointer is defined as follows:
Thread_Group_Proc :: #type (group: *Thread_Group, thread: *Thread, work: *void) -> Thread_Continue_Status;
The group
is the Thread_Group
, the thread
refers to the particular thread in question, and the work
is the work passed into the Thread_Group
through the add_work :: ()
function. The Thread_Continue_Status
is returned by procedure. Returning .STOP
causes the thread to terminate. .CONTINUE
causes the thread to continue to run. You usually want to return .CONTINUE
, and .STOP
is for a resource shortage of some kind.
start :: (group: *Thread_Group);
Starts up the threads in the thread group.
add_work :: (group: *Thread_Group, work: *void, logging_name := "");
Adds a unit of work, which will be given to one of the threads.
Basic Thread Group Example
main :: () {
thread_group: Thread_Group;
init(*thread_group, 4, thread_test, true);
thread_group.logging = false; // turns debugging off. set logging = true to turn on debugging
start(*thread_group);
for i: 0..10
add_work(*thread_group, null);
sleep_milliseconds(5000);
shutdown(*thread_group);
print("exit program\n");
}
thread_test :: (group: *Thread_Group, thread: *Thread, work: *void) -> Thread_Continue_Status {
print("thread_test :: () from thread.index = %\n", thread.index);
return .CONTINUE;
}
#import "Thread";
#import "Basic";
In this basic thread group example, we initialize a thread group, start it, add work to the group, and shutdown the thread group.
Thread Group Example with response to completed work
In the following example, we kick off a set of threads to do a set of tasks, then we use get_completed_work :: ()
to get the results back. In the main thread, we do something with those results achieved.
main :: () {
thread_group: Thread_Group;
init(*thread_group, 4, thread_test, true);
thread_group.logging = false;
start(*thread_group);
arr: [10] Work;
for i: 0..9 {
arr[i].count = 10000;
add_work(*thread_group, *arr[i]);
}
sleep_milliseconds(5000);
work_list := get_completed_work(*thread_group);
total := 0;
for work: work_list {
val := cast(*Work) work;
print("%\n", val.result);
total += val.result;
}
print("Total = %\n", total);
shutdown(*thread_group);
print("exit program\n");
}
thread_test :: (group: *Thread_Group, thread: *Thread, work: *void) -> Thread_Continue_Status {
w := cast(*Work)work;
print("thread_test :: () from thread.index = %, work.count = %\n", thread.index, w.count);
sum := 0;
for i: 0..w.count {
sum += i;
}
// return the result.
w.result = sum;
return .CONTINUE;
}
Work :: struct {
count: int;
result: int;
}
#import "Thread";
#import "Basic";