Browse Source

[thread]: Document all functions in core:thread

flysand7 1 year ago
parent
commit
b84b4c47d7
1 changed files with 242 additions and 39 deletions
  1. 242 39
      core/thread/thread.odin

+ 242 - 39
core/thread/thread.odin

@@ -6,12 +6,26 @@ import "base:intrinsics"
 
 
 _ :: intrinsics
 _ :: intrinsics
 
 
+/*
+Value, specifying whether `core:thread` functionality is available on the
+current platform.
+*/
 IS_SUPPORTED :: _IS_SUPPORTED
 IS_SUPPORTED :: _IS_SUPPORTED
 
 
+/*
+Type for a procedure that will be run in a thread, after that thread has been
+started.
+*/
 Thread_Proc :: #type proc(^Thread)
 Thread_Proc :: #type proc(^Thread)
 
 
+/*
+Maximum number of user arguments for polymorphic thread procedures.
+*/
 MAX_USER_ARGUMENTS :: 8
 MAX_USER_ARGUMENTS :: 8
 
 
+/*
+Type representing the state/flags of the thread.
+*/
 Thread_State :: enum u8 {
 Thread_State :: enum u8 {
 	Started,
 	Started,
 	Joined,
 	Joined,
@@ -19,44 +33,48 @@ Thread_State :: enum u8 {
 	Self_Cleanup,
 	Self_Cleanup,
 }
 }
 
 
+/*
+Type representing a thread handle and the associated with that thread data.
+*/
 Thread :: struct {
 Thread :: struct {
 	using specific: Thread_Os_Specific,
 	using specific: Thread_Os_Specific,
 	flags: bit_set[Thread_State; u8],
 	flags: bit_set[Thread_State; u8],
-	id:             int,
-	procedure:      Thread_Proc,
-
-	/*
-		These are values that the user can set as they wish, after the thread has been created.
-		This data is easily available to the thread proc.
-
-		These fields can be assigned to directly.
-
-		Should be set after the thread is created, but before it is started.
-	*/
-	data:           rawptr,
-	user_index:     int,
-	user_args:      [MAX_USER_ARGUMENTS]rawptr,
-
-	/*
-		The context to be used as 'context' in the thread proc.
-
-		This field can be assigned to directly, after the thread has been created, but __before__ the thread has been started.
-		This field must not be changed after the thread has started.
-
-		NOTE: If you __don't__ set this, the temp allocator will be managed for you;
-		      If you __do__ set this, then you're expected to handle whatever allocators you set, yourself.
-
-		IMPORTANT:
-		By default, the thread proc will get the same context as `main()` gets.
-		In this situation, the thread will get a new temporary allocator which will be cleaned up when the thread dies.
-		***This does NOT happen when you set `init_context`.***
-		This means that if you set `init_context`, but still have the `temp_allocator` field set to the default temp allocator,
-		then you'll need to call `runtime.default_temp_allocator_destroy(auto_cast the_thread.init_context.temp_allocator.data)` manually,
-		in order to prevent any memory leaks.
-		This call ***must*** be done ***in the thread proc*** because the default temporary allocator uses thread local state!
-	*/
+	// Thread ID.
+	id: int,
+	// The thread procedure.
+	procedure: Thread_Proc,
+	// User-supplied pointer, that will be available to the thread once it is
+	// started. Should be set after the thread has been created, but before
+	// it is started.
+	data: rawptr,
+	// User-supplied integer, that will be available to the thread once it is
+	// started. Should be set after the thread has been created, but before
+	// it is started.
+	user_index: int,
+	// User-supplied array of arguments, that will be available to the thread,
+	// once it is started. Should be set after the thread has been created,
+	// but before it is started.
+	user_args: [MAX_USER_ARGUMENTS]rawptr,
+	// The thread context.
+	// This field can be assigned to directly, after the thread has been
+	// created, but __before__ the thread has been started. This field must
+	// not be changed after the thread has started.
+	//
+	// **Note**: If this field is **not** set, the temp allocator will be managed
+	// automatically. If it is set, the allocators must be handled manually.
+	//
+	// **IMPORTANT**:
+	// By default, the thread proc will get the same context as `main()` gets.
+	// In this situation, the thread will get a new temporary allocator which
+	// will be cleaned up when the thread dies. ***This does NOT happen when
+	// `init_context` field is initialized***.
+	//
+	// If `init_context` is initialized, and `temp_allocator` field is set to
+	// the default temp allocator, then `runtime.default_temp_allocator_destroy()`
+	// procedure needs to be called from the thread procedure, in order to prevent
+	// any memory leaks.
 	init_context: Maybe(runtime.Context),
 	init_context: Maybe(runtime.Context),
-
+	// The allocator used to allocate data for the thread.
 	creation_allocator: mem.Allocator,
 	creation_allocator: mem.Allocator,
 }
 }
 
 
@@ -64,6 +82,9 @@ when IS_SUPPORTED {
 	#assert(size_of(Thread{}.user_index) == size_of(uintptr))
 	#assert(size_of(Thread{}.user_index) == size_of(uintptr))
 }
 }
 
 
+/*
+Type representing priority of a thread.
+*/
 Thread_Priority :: enum {
 Thread_Priority :: enum {
 	Normal,
 	Normal,
 	Low,
 	Low,
@@ -71,74 +92,178 @@ Thread_Priority :: enum {
 }
 }
 
 
 /*
 /*
-	Creates a thread in a suspended state with the given priority.
-	To start the thread, call `thread.start()`.
+Create a thread in a suspended state with the given priority.
 
 
-	See `thread.create_and_start()`.
+This procedure creates a thread that will be set to run the procedure
+specified by `procedure` parameter with a specified priority. The returned
+thread will be in a suspended state, until `start()` procedure is called.
+
+To start the thread, call `start()`. Also the `create_and_start()`
+procedure can be called to create and start the thread immediately.
 */
 */
 create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
 create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
 	return _create(procedure, priority)
 	return _create(procedure, priority)
 }
 }
+
+/*
+Wait for the thread to finish and free all data associated with it.
+*/
 destroy :: proc(thread: ^Thread) {
 destroy :: proc(thread: ^Thread) {
 	_destroy(thread)
 	_destroy(thread)
 }
 }
 
 
+/*
+Start a suspended thread.
+*/
 start :: proc(thread: ^Thread) {
 start :: proc(thread: ^Thread) {
 	_start(thread)
 	_start(thread)
 }
 }
 
 
+/*
+Check if the thread has finished work.
+*/
 is_done :: proc(thread: ^Thread) -> bool {
 is_done :: proc(thread: ^Thread) -> bool {
 	return _is_done(thread)
 	return _is_done(thread)
 }
 }
 
 
-
+/*
+Wait for the thread to finish work.
+*/
 join :: proc(thread: ^Thread) {
 join :: proc(thread: ^Thread) {
 	_join(thread)
 	_join(thread)
 }
 }
 
 
-
+/*
+Wait for all threads to finish work.
+*/
 join_multiple :: proc(threads: ..^Thread) {
 join_multiple :: proc(threads: ..^Thread) {
 	_join_multiple(..threads)
 	_join_multiple(..threads)
 }
 }
 
 
+/*
+Forcibly terminate a running thread.
+*/
 terminate :: proc(thread: ^Thread, exit_code: int) {
 terminate :: proc(thread: ^Thread, exit_code: int) {
 	_terminate(thread, exit_code)
 	_terminate(thread, exit_code)
 }
 }
 
 
+/*
+Yield the execution of the current thread to another OS thread or process.
+*/
 yield :: proc() {
 yield :: proc() {
 	_yield()
 	_yield()
 }
 }
 
 
+/*
+Run a procedure on a different thread.
 
 
+This procedure runs the given procedure on another thread. The context
+specified by `init_context` will be used as the context in which `fn` is going
+to execute. The thread will have priority specified by the `priority` parameter.
 
 
+**IMPORTANT**: If `init_context` is specified and the default temporary allocator
+is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
+in order to free the resources associated with the temporary allocations.
+*/
 run :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
 run :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
 	create_and_start(fn, init_context, priority, true)
 	create_and_start(fn, init_context, priority, true)
 }
 }
 
 
+/*
+Run a procedure with one pointer parameter on a different thread.
+
+This procedure runs the given procedure on another thread. The context
+specified by `init_context` will be used as the context in which `fn` is going
+to execute. The thread will have priority specified by the `priority` parameter.
+
+**IMPORTANT**: If `init_context` is specified and the default temporary allocator
+is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
+in order to free the resources associated with the temporary allocations.
+*/
 run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
 run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
 	create_and_start_with_data(data, fn, init_context, priority, true)
 	create_and_start_with_data(data, fn, init_context, priority, true)
 }
 }
 
 
+/*
+Run a procedure with one polymorphic parameter on a different thread.
+
+This procedure runs the given procedure on another thread. The context
+specified by `init_context` will be used as the context in which `fn` is going
+to execute. The thread will have priority specified by the `priority` parameter.
+
+**IMPORTANT**: If `init_context` is specified and the default temporary allocator
+is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
+in order to free the resources associated with the temporary allocations.
+*/
 run_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
 run_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
 	where size_of(T) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	where size_of(T) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	create_and_start_with_poly_data(data, fn, init_context, priority, true)
 	create_and_start_with_poly_data(data, fn, init_context, priority, true)
 }
 }
 
 
+/*
+Run a procedure with two polymorphic parameters on a different thread.
+
+This procedure runs the given procedure on another thread. The context
+specified by `init_context` will be used as the context in which `fn` is going
+to execute. The thread will have priority specified by the `priority` parameter.
+
+**IMPORTANT**: If `init_context` is specified and the default temporary allocator
+is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
+in order to free the resources associated with the temporary allocations.
+*/
 run_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
 run_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
 	where size_of(T1) + size_of(T2) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	where size_of(T1) + size_of(T2) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	create_and_start_with_poly_data2(arg1, arg2, fn, init_context, priority, true)
 	create_and_start_with_poly_data2(arg1, arg2, fn, init_context, priority, true)
 }
 }
 
 
+/*
+Run a procedure with three polymorphic parameters on a different thread.
+
+This procedure runs the given procedure on another thread. The context
+specified by `init_context` will be used as the context in which `fn` is going
+to execute. The thread will have priority specified by the `priority` parameter.
+
+**IMPORTANT**: If `init_context` is specified and the default temporary allocator
+is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
+in order to free the resources associated with the temporary allocations.
+*/
 run_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
 run_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
 	where size_of(T1) + size_of(T2) + size_of(T3) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	where size_of(T1) + size_of(T2) + size_of(T3) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	create_and_start_with_poly_data3(arg1, arg2, arg3, fn, init_context, priority, true)
 	create_and_start_with_poly_data3(arg1, arg2, arg3, fn, init_context, priority, true)
 }
 }
+
+/*
+Run a procedure with four polymorphic parameters on a different thread.
+
+This procedure runs the given procedure on another thread. The context
+specified by `init_context` will be used as the context in which `fn` is going
+to execute. The thread will have priority specified by the `priority` parameter.
+
+**IMPORTANT**: If `init_context` is specified and the default temporary allocator
+is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
+in order to free the resources associated with the temporary allocations.
+*/
 run_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
 run_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
 	where size_of(T1) + size_of(T2) + size_of(T3) + size_of(T4) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	where size_of(T1) + size_of(T2) + size_of(T3) + size_of(T4) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	create_and_start_with_poly_data4(arg1, arg2, arg3, arg4, fn, init_context, priority, true)
 	create_and_start_with_poly_data4(arg1, arg2, arg3, arg4, fn, init_context, priority, true)
 }
 }
 
 
+/*
+Run a procedure on a different thread.
+
+This procedure runs the given procedure on another thread. The context
+specified by `init_context` will be used as the context in which `fn` is going
+to execute. The thread will have priority specified by the `priority` parameter.
+
+If `self_cleanup` is specified, after the thread finishes the execution of the
+`fn` procedure, the resources associated with the thread are going to be
+automatically freed. **Do not** dereference the `^Thread` pointer, if this
+flag is specified.
 
 
+**IMPORTANT**: If `init_context` is specified and the default temporary allocator
+is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
+in order to free the resources associated with the temporary allocations.
+*/
 create_and_start :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread {
 create_and_start :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread {
 	thread_proc :: proc(t: ^Thread) {
 	thread_proc :: proc(t: ^Thread) {
 		fn := cast(proc())t.data
 		fn := cast(proc())t.data
@@ -154,9 +279,22 @@ create_and_start :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil,
 	return t
 	return t
 }
 }
 
 
+/*
+Run a procedure with one pointer parameter on a different thread.
 
 
+This procedure runs the given procedure on another thread. The context
+specified by `init_context` will be used as the context in which `fn` is going
+to execute. The thread will have priority specified by the `priority` parameter.
 
 
+If `self_cleanup` is specified, after the thread finishes the execution of the
+`fn` procedure, the resources associated with the thread are going to be
+automatically freed. **Do not** dereference the `^Thread` pointer, if this
+flag is specified.
 
 
+**IMPORTANT**: If `init_context` is specified and the default temporary allocator
+is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
+in order to free the resources associated with the temporary allocations.
+*/
 create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread {
 create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread {
 	thread_proc :: proc(t: ^Thread) {
 	thread_proc :: proc(t: ^Thread) {
 		fn := cast(proc(rawptr))t.data
 		fn := cast(proc(rawptr))t.data
@@ -176,6 +314,22 @@ create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_co
 	return t
 	return t
 }
 }
 
 
+/*
+Run a procedure with one polymorphic parameter on a different thread.
+
+This procedure runs the given procedure on another thread. The context
+specified by `init_context` will be used as the context in which `fn` is going
+to execute. The thread will have priority specified by the `priority` parameter.
+
+If `self_cleanup` is specified, after the thread finishes the execution of the
+`fn` procedure, the resources associated with the thread are going to be
+automatically freed. **Do not** dereference the `^Thread` pointer, if this
+flag is specified.
+
+**IMPORTANT**: If `init_context` is specified and the default temporary allocator
+is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
+in order to free the resources associated with the temporary allocations.
+*/
 create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
 create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
 	where size_of(T) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	where size_of(T) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	thread_proc :: proc(t: ^Thread) {
 	thread_proc :: proc(t: ^Thread) {
@@ -201,6 +355,22 @@ create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_contex
 	return t
 	return t
 }
 }
 
 
+/*
+Run a procedure with two polymorphic parameters on a different thread.
+
+This procedure runs the given procedure on another thread. The context
+specified by `init_context` will be used as the context in which `fn` is going
+to execute. The thread will have priority specified by the `priority` parameter.
+
+If `self_cleanup` is specified, after the thread finishes the execution of the
+`fn` procedure, the resources associated with the thread are going to be
+automatically freed. **Do not** dereference the `^Thread` pointer, if this
+flag is specified.
+
+**IMPORTANT**: If `init_context` is specified and the default temporary allocator
+is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
+in order to free the resources associated with the temporary allocations.
+*/
 create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
 create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
 	where size_of(T1) + size_of(T2) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	where size_of(T1) + size_of(T2) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	thread_proc :: proc(t: ^Thread) {
 	thread_proc :: proc(t: ^Thread) {
@@ -232,6 +402,22 @@ create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2),
 	return t
 	return t
 }
 }
 
 
+/*
+Run a procedure with three polymorphic parameters on a different thread.
+
+This procedure runs the given procedure on another thread. The context
+specified by `init_context` will be used as the context in which `fn` is going
+to execute. The thread will have priority specified by the `priority` parameter.
+
+If `self_cleanup` is specified, after the thread finishes the execution of the
+`fn` procedure, the resources associated with the thread are going to be
+automatically freed. **Do not** dereference the `^Thread` pointer, if this
+flag is specified.
+
+**IMPORTANT**: If `init_context` is specified and the default temporary allocator
+is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
+in order to free the resources associated with the temporary allocations.
+*/
 create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
 create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
 	where size_of(T1) + size_of(T2) + size_of(T3) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	where size_of(T1) + size_of(T2) + size_of(T3) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	thread_proc :: proc(t: ^Thread) {
 	thread_proc :: proc(t: ^Thread) {
@@ -264,6 +450,23 @@ create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: pr
 	start(t)
 	start(t)
 	return t
 	return t
 }
 }
+
+/*
+Run a procedure with four polymorphic parameters on a different thread.
+
+This procedure runs the given procedure on another thread. The context
+specified by `init_context` will be used as the context in which `fn` is going
+to execute. The thread will have priority specified by the `priority` parameter.
+
+If `self_cleanup` is specified, after the thread finishes the execution of the
+`fn` procedure, the resources associated with the thread are going to be
+automatically freed. **Do not** dereference the `^Thread` pointer, if this
+flag is specified.
+
+**IMPORTANT**: If `init_context` is specified and the default temporary allocator
+is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
+in order to free the resources associated with the temporary allocations.
+*/
 create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
 create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
 	where size_of(T1) + size_of(T2) + size_of(T3) + size_of(T4) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	where size_of(T1) + size_of(T2) + size_of(T3) + size_of(T4) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
 	thread_proc :: proc(t: ^Thread) {
 	thread_proc :: proc(t: ^Thread) {