mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-31 19:28:55 +00:00
Merge pull request #5464 from peperronii/master
core:thread get_name/set_name
This commit is contained in:
@@ -5,7 +5,7 @@ import "core:c"
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
foreign import lib "system:System"
|
||||
} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .Linux {
|
||||
} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .Linux || ODIN_OS == .OpenBSD {
|
||||
foreign import lib "system:pthread"
|
||||
} else {
|
||||
foreign import lib "system:c"
|
||||
|
||||
@@ -180,7 +180,7 @@ foreign kernel32 {
|
||||
ResumeThread :: proc(thread: HANDLE) -> DWORD ---
|
||||
GetThreadPriority :: proc(thread: HANDLE) -> c_int ---
|
||||
SetThreadPriority :: proc(thread: HANDLE, priority: c_int) -> BOOL ---
|
||||
GetThreadDescription :: proc(hThread: HANDLE, ppszThreadDescription: ^PCWSTR) -> HRESULT ---
|
||||
GetThreadDescription :: proc(hThread: HANDLE, ppszThreadDescription: ^PWSTR) -> HRESULT ---
|
||||
SetThreadDescription :: proc(hThread: HANDLE, lpThreadDescription: PCWSTR) -> HRESULT ---
|
||||
GetExitCodeThread :: proc(thread: HANDLE, exit_code: ^DWORD) -> BOOL ---
|
||||
TerminateThread :: proc(thread: HANDLE, exit_code: DWORD) -> BOOL ---
|
||||
|
||||
@@ -52,6 +52,10 @@ Thread :: struct {
|
||||
// started. Should be set after the thread has been created, but before
|
||||
// it is started.
|
||||
data: rawptr,
|
||||
// Thread's Name/Description that will get set during thread creation
|
||||
// it will be set using init_context's allocator to allocate and free a cstring buffer
|
||||
// for thread's creation only : do not refer to it, use thread.get_name instead
|
||||
name: Maybe(string),
|
||||
// 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.
|
||||
@@ -105,9 +109,13 @@ 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.
|
||||
|
||||
Optionally specify the thread's name/description.
|
||||
the name/description will be truncated to fit the OS's limit.
|
||||
*/
|
||||
create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
|
||||
return _create(procedure, priority)
|
||||
|
||||
create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal, name: Maybe(string) = nil) -> ^Thread {
|
||||
return _create(procedure, priority, name)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -159,6 +167,20 @@ yield :: proc() {
|
||||
_yield()
|
||||
}
|
||||
|
||||
/*
|
||||
Get thread's name/description.
|
||||
|
||||
This procedure returns the name of the given thread. If `thread` is `nil`, this procedure returns the name of the calling thread.
|
||||
OS level errors are silently ignored.
|
||||
|
||||
**Note(linux, bsd)**: Because the thread name is stored in as the `cmdline`, if the thread name was not set, the command that has been used to create the process will be used as the name of the thread.
|
||||
|
||||
allocates memory for the returned string using provided allocator.
|
||||
*/
|
||||
get_name :: proc(thread: ^Thread = nil, allocator := context.temp_allocator, loc := #caller_location) -> (string, runtime.Allocator_Error) {
|
||||
return _get_name(thread, allocator, loc)
|
||||
}
|
||||
|
||||
/*
|
||||
Run a procedure on a different thread.
|
||||
|
||||
@@ -166,12 +188,15 @@ 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.
|
||||
|
||||
Optionally specify the thread's name/description.
|
||||
the name/description will be truncated to fit the OS's limit.
|
||||
|
||||
**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) {
|
||||
create_and_start(fn, init_context, priority, true)
|
||||
run :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, name: Maybe(string) = nil) {
|
||||
create_and_start(fn, init_context, priority, true, name)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -181,12 +206,15 @@ 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.
|
||||
|
||||
Optionally specify the thread's name/description.
|
||||
the name/description will be truncated to fit the OS's limit.
|
||||
|
||||
**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) {
|
||||
create_and_start_with_data(data, fn, init_context, priority, true)
|
||||
run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, name: Maybe(string) = nil) {
|
||||
create_and_start_with_data(data, fn, init_context, priority, true, name)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -196,13 +224,16 @@ 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.
|
||||
|
||||
Optionally specify the thread's name/description.
|
||||
the name/description will be truncated to fit the OS's limit.
|
||||
|
||||
**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, name: Maybe(string) = nil)
|
||||
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, name)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -212,13 +243,16 @@ 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.
|
||||
|
||||
Optionally specify the thread's name/description.
|
||||
the name/description will be truncated to fit the OS's limit.
|
||||
|
||||
**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, name: Maybe(string) = nil)
|
||||
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, name)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -228,13 +262,16 @@ 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.
|
||||
|
||||
Optionally specify the thread's name/description.
|
||||
the name/description will be truncated to fit the OS's limit.
|
||||
|
||||
**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, name: Maybe(string) = nil)
|
||||
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, name)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -244,13 +281,16 @@ 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.
|
||||
|
||||
Optionally specify the thread's name/description.
|
||||
the name/description will be truncated to fit the OS's limit.
|
||||
|
||||
**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, name: Maybe(string) = nil)
|
||||
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, name)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -264,6 +304,9 @@ 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.
|
||||
|
||||
Optionally specify the thread's name/description.
|
||||
the name/description will be truncated to fit the OS's limit.
|
||||
|
||||
**Do not** dereference the `^Thread` pointer, if this flag is specified.
|
||||
That includes calling `join`, which needs to dereference ^Thread`.
|
||||
|
||||
@@ -271,12 +314,12 @@ That includes calling `join`, which needs to dereference ^Thread`.
|
||||
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) -> (t: ^Thread) {
|
||||
create_and_start :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false, name: Maybe(string) = nil) -> (t: ^Thread) {
|
||||
thread_proc :: proc(t: ^Thread) {
|
||||
fn := cast(proc())t.data
|
||||
fn()
|
||||
}
|
||||
if t = create(thread_proc, priority); t == nil {
|
||||
if t = create(thread_proc, priority, name); t == nil {
|
||||
return
|
||||
}
|
||||
t.data = rawptr(fn)
|
||||
@@ -299,6 +342,9 @@ 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.
|
||||
|
||||
Optionally specify the thread's name/description.
|
||||
the name/description will be truncated to fit the OS's limit.
|
||||
|
||||
**Do not** dereference the `^Thread` pointer, if this flag is specified.
|
||||
That includes calling `join`, which needs to dereference ^Thread`.
|
||||
|
||||
@@ -306,14 +352,14 @@ That includes calling `join`, which needs to dereference ^Thread`.
|
||||
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) -> (t: ^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, name: Maybe(string) = nil) -> (t: ^Thread) {
|
||||
thread_proc :: proc(t: ^Thread) {
|
||||
fn := cast(proc(rawptr))t.data
|
||||
assert(t.user_index >= 1)
|
||||
data := t.user_args[0]
|
||||
fn(data)
|
||||
}
|
||||
if t = create(thread_proc, priority); t == nil {
|
||||
if t = create(thread_proc, priority, name); t == nil {
|
||||
return
|
||||
}
|
||||
t.data = rawptr(fn)
|
||||
@@ -338,6 +384,9 @@ 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.
|
||||
|
||||
Optionally specify the thread's name/description.
|
||||
the name/description will be truncated to fit the OS's limit.
|
||||
|
||||
**Do not** dereference the `^Thread` pointer, if this flag is specified.
|
||||
That includes calling `join`, which needs to dereference ^Thread`.
|
||||
|
||||
@@ -345,7 +394,7 @@ That includes calling `join`, which needs to dereference ^Thread`.
|
||||
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) -> (t: ^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, name: Maybe(string) = nil) -> (t: ^Thread)
|
||||
where size_of(T) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
|
||||
thread_proc :: proc(t: ^Thread) {
|
||||
fn := cast(proc(T))t.data
|
||||
@@ -355,7 +404,7 @@ create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_contex
|
||||
|
||||
fn(data)
|
||||
}
|
||||
if t = create(thread_proc, priority); t == nil {
|
||||
if t = create(thread_proc, priority, name); t == nil {
|
||||
return
|
||||
}
|
||||
t.data = rawptr(fn)
|
||||
@@ -383,6 +432,9 @@ 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.
|
||||
|
||||
Optionally specify the thread's name/description.
|
||||
the name/description will be truncated to fit the OS's limit.
|
||||
|
||||
**Do not** dereference the `^Thread` pointer, if this flag is specified.
|
||||
That includes calling `join`, which needs to dereference ^Thread`.
|
||||
|
||||
@@ -390,7 +442,7 @@ That includes calling `join`, which needs to dereference ^Thread`.
|
||||
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) -> (t: ^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, name: Maybe(string) = nil) -> (t: ^Thread)
|
||||
where size_of(T1) + size_of(T2) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
|
||||
thread_proc :: proc(t: ^Thread) {
|
||||
fn := cast(proc(T1, T2))t.data
|
||||
@@ -403,7 +455,7 @@ create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2),
|
||||
|
||||
fn(arg1, arg2)
|
||||
}
|
||||
if t = create(thread_proc, priority); t == nil {
|
||||
if t = create(thread_proc, priority, name); t == nil {
|
||||
return
|
||||
}
|
||||
t.data = rawptr(fn)
|
||||
@@ -434,6 +486,9 @@ 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.
|
||||
|
||||
Optionally specify the thread's name/description.
|
||||
the name/description will be truncated to fit the OS's limit.
|
||||
|
||||
**Do not** dereference the `^Thread` pointer, if this flag is specified.
|
||||
That includes calling `join`, which needs to dereference ^Thread`.
|
||||
|
||||
@@ -441,7 +496,7 @@ That includes calling `join`, which needs to dereference ^Thread`.
|
||||
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) -> (t: ^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, name: Maybe(string) = nil) -> (t: ^Thread)
|
||||
where size_of(T1) + size_of(T2) + size_of(T3) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
|
||||
thread_proc :: proc(t: ^Thread) {
|
||||
fn := cast(proc(T1, T2, T3))t.data
|
||||
@@ -455,7 +510,7 @@ create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: pr
|
||||
|
||||
fn(arg1, arg2, arg3)
|
||||
}
|
||||
if t = create(thread_proc, priority); t == nil {
|
||||
if t = create(thread_proc, priority, name); t == nil {
|
||||
return
|
||||
}
|
||||
t.data = rawptr(fn)
|
||||
@@ -487,6 +542,9 @@ 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.
|
||||
|
||||
Optionally specify the thread's name/description.
|
||||
the name/description will be truncated to fit the OS's limit.
|
||||
|
||||
**Do not** dereference the `^Thread` pointer, if this flag is specified.
|
||||
That includes calling `join`, which needs to dereference ^Thread`.
|
||||
|
||||
@@ -494,7 +552,7 @@ That includes calling `join`, which needs to dereference ^Thread`.
|
||||
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) -> (t: ^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, name: Maybe(string) = nil) -> (t: ^Thread)
|
||||
where size_of(T1) + size_of(T2) + size_of(T3) + size_of(T4) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
|
||||
thread_proc :: proc(t: ^Thread) {
|
||||
fn := cast(proc(T1, T2, T3, T4))t.data
|
||||
@@ -508,7 +566,7 @@ create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4:
|
||||
|
||||
fn(arg1, arg2, arg3, arg4)
|
||||
}
|
||||
if t = create(thread_proc, priority); t == nil {
|
||||
if t = create(thread_proc, priority, name); t == nil {
|
||||
return
|
||||
}
|
||||
t.data = rawptr(fn)
|
||||
@@ -560,4 +618,4 @@ _maybe_destroy_default_temp_allocator :: proc(init_context: Maybe(runtime.Contex
|
||||
if context.temp_allocator.procedure == runtime.default_temp_allocator_proc {
|
||||
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
core/thread/thread_name_darwin.odin
Normal file
15
core/thread/thread_name_darwin.odin
Normal file
@@ -0,0 +1,15 @@
|
||||
#+build darwin
|
||||
#+private
|
||||
package thread
|
||||
|
||||
import "core:sys/posix"
|
||||
import "core:c"
|
||||
|
||||
_MAX_PTHREAD_NAME_LENGTH :: 64
|
||||
|
||||
foreign import pthread "system:System.framework"
|
||||
|
||||
foreign pthread {
|
||||
pthread_getname_np :: proc(thread: posix.pthread_t, name: [^]u8, len: c.size_t) -> posix.Errno ---
|
||||
pthread_setname_np :: proc(name: [^]u8) -> posix.Errno ---
|
||||
}
|
||||
15
core/thread/thread_name_freebsd.odin
Normal file
15
core/thread/thread_name_freebsd.odin
Normal file
@@ -0,0 +1,15 @@
|
||||
#+build freebsd
|
||||
#+private
|
||||
package thread
|
||||
|
||||
import "core:sys/posix"
|
||||
import "core:c"
|
||||
|
||||
_MAX_PTHREAD_NAME_LENGTH :: 20
|
||||
|
||||
foreign import pthread "system:pthread"
|
||||
|
||||
foreign pthread {
|
||||
pthread_getname_np :: proc(thread: posix.pthread_t, name: [^]u8, len: c.size_t) -> posix.Errno ---
|
||||
pthread_setname_np :: proc(thread: posix.pthread_t, name: [^]u8) -> posix.Errno ---
|
||||
}
|
||||
15
core/thread/thread_name_linux.odin
Normal file
15
core/thread/thread_name_linux.odin
Normal file
@@ -0,0 +1,15 @@
|
||||
#+build linux
|
||||
#+private
|
||||
package thread
|
||||
|
||||
import "core:sys/posix"
|
||||
import "core:c"
|
||||
|
||||
_MAX_PTHREAD_NAME_LENGTH :: 16
|
||||
|
||||
foreign import pthread "system:pthread"
|
||||
|
||||
foreign pthread {
|
||||
pthread_getname_np :: proc(thread: posix.pthread_t, name: [^]u8, len: c.size_t) -> posix.Errno ---
|
||||
pthread_setname_np :: proc(thread: posix.pthread_t, name: [^]u8) -> posix.Errno ---
|
||||
}
|
||||
15
core/thread/thread_name_netbsd.odin
Normal file
15
core/thread/thread_name_netbsd.odin
Normal file
@@ -0,0 +1,15 @@
|
||||
#+build netbsd
|
||||
#+private
|
||||
package thread
|
||||
|
||||
import "core:sys/posix"
|
||||
import "core:c"
|
||||
|
||||
_MAX_PTHREAD_NAME_LENGTH :: 32
|
||||
|
||||
foreign import pthread "system:pthread"
|
||||
|
||||
foreign pthread {
|
||||
pthread_getname_np :: proc(thread: posix.pthread_t, name: [^]u8, len: c.size_t) -> posix.Errno ---
|
||||
pthread_setname_np :: proc(thread: posix.pthread_t, name: cstring, arg: rawptr) -> posix.Errno ---
|
||||
}
|
||||
15
core/thread/thread_name_openbsd.odin
Normal file
15
core/thread/thread_name_openbsd.odin
Normal file
@@ -0,0 +1,15 @@
|
||||
#+build openbsd
|
||||
#+private
|
||||
package thread
|
||||
|
||||
import "core:sys/posix"
|
||||
import "core:c"
|
||||
|
||||
foreign import pthread "system:pthread"
|
||||
|
||||
_MAX_PTHREAD_NAME_LENGTH :: 20
|
||||
|
||||
foreign pthread {
|
||||
pthread_get_name_np :: proc(thread: posix.pthread_t, name: [^]u8, len: c.size_t) ---
|
||||
pthread_set_name_np :: proc(thread: posix.pthread_t, name: [^]u8) ---
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
package thread
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
|
||||
_IS_SUPPORTED :: false
|
||||
|
||||
@@ -13,7 +14,7 @@ _thread_priority_map := [Thread_Priority]i32{
|
||||
.High = +2,
|
||||
}
|
||||
|
||||
_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
|
||||
_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal, name: Maybe(string) = nil) -> ^Thread {
|
||||
unimplemented("core:thread procedure not supported on target")
|
||||
}
|
||||
|
||||
@@ -45,3 +46,10 @@ _yield :: proc() {
|
||||
unimplemented("core:thread procedure not supported on target")
|
||||
}
|
||||
|
||||
_get_name :: proc(thread: ^Thread, allocator : runtime.Allocator, loc : runtime.Source_Code_Location) -> (string, runtime.Allocator_Error) {
|
||||
unimplemented("core:thread procedure not supported on this target")
|
||||
}
|
||||
|
||||
_set_name :: proc(thread: ^Thread, name:string) {
|
||||
unimplemented("core:thread procedure not supported on this target")
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ package thread
|
||||
import "base:runtime"
|
||||
import "core:sync"
|
||||
import "core:sys/posix"
|
||||
import "core:strings"
|
||||
|
||||
_IS_SUPPORTED :: true
|
||||
|
||||
// NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t.
|
||||
// Also see core/sys/darwin/mach_darwin.odin/semaphore_t.
|
||||
Thread_Os_Specific :: struct #align(16) {
|
||||
@@ -18,7 +18,7 @@ Thread_Os_Specific :: struct #align(16) {
|
||||
// Creates a thread which will run the given procedure.
|
||||
// It then waits for `start` to be called.
|
||||
//
|
||||
_create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
_create :: proc(procedure: Thread_Proc, priority: Thread_Priority, name: Maybe(string)) -> ^Thread {
|
||||
__unix_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
|
||||
t := (^Thread)(t)
|
||||
|
||||
@@ -50,6 +50,8 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
runtime.run_thread_local_cleaners()
|
||||
}
|
||||
|
||||
_set_name(t)
|
||||
|
||||
t.procedure(t)
|
||||
}
|
||||
|
||||
@@ -122,6 +124,9 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
free(thread, thread.creation_allocator)
|
||||
return nil
|
||||
}
|
||||
|
||||
thread.name = name
|
||||
|
||||
return thread
|
||||
}
|
||||
|
||||
@@ -184,3 +189,50 @@ _terminate :: proc(t: ^Thread, exit_code: int) {
|
||||
_yield :: proc() {
|
||||
posix.sched_yield()
|
||||
}
|
||||
|
||||
_get_name :: proc(thread: ^Thread, allocator: runtime.Allocator, loc: runtime.Source_Code_Location) -> (name: string, err: runtime.Allocator_Error) {
|
||||
tid: posix.pthread_t
|
||||
if thread == nil {
|
||||
tid = posix.pthread_self()
|
||||
} else {
|
||||
tid = thread.unix_thread
|
||||
}
|
||||
|
||||
buf : [_MAX_PTHREAD_NAME_LENGTH]u8
|
||||
|
||||
when ODIN_OS == .OpenBSD {
|
||||
pthread_get_name_np(tid, raw_data(buf[:]), len(buf))
|
||||
} else {
|
||||
pthread_getname_np(tid, raw_data(buf[:]), len(buf))
|
||||
}
|
||||
|
||||
name, err = strings.clone_from_cstring(cstring(raw_data(buf[:])), allocator, loc)
|
||||
return
|
||||
}
|
||||
|
||||
_set_name :: proc(thread: ^Thread) {
|
||||
name, ok := thread.name.?
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
when ODIN_OS != .Darwin {
|
||||
tid := thread.unix_thread
|
||||
}
|
||||
|
||||
// _MAX_PTHREAD_NAME_LENGTH includes terminating null
|
||||
buflen := len(name) + 1 < _MAX_PTHREAD_NAME_LENGTH ? len(name) + 1 : _MAX_PTHREAD_NAME_LENGTH
|
||||
buf := make([]u8, buflen)
|
||||
defer delete(buf)
|
||||
copy(buf[:len(buf) - 1], name)
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
pthread_setname_np(raw_data(buf[:]))
|
||||
} else when ODIN_OS == .OpenBSD {
|
||||
pthread_set_name_np(tid, raw_data(buf[:]))
|
||||
} else when ODIN_OS == .NetBSD {
|
||||
pthread_setname_np(tid, "%s", raw_data(buf[:]))
|
||||
} else {
|
||||
pthread_setname_np(tid, raw_data(buf[:]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,13 @@ import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:sync"
|
||||
import win32 "core:sys/windows"
|
||||
import "core:unicode/utf16"
|
||||
|
||||
_IS_SUPPORTED :: true
|
||||
|
||||
//NOTE(peperronii): this is the system limit for windows api call, not specific to thread description
|
||||
_THREAD_DESCRIPTION_LENGTH :: 32_767
|
||||
|
||||
Thread_Os_Specific :: struct {
|
||||
win32_thread: win32.HANDLE,
|
||||
win32_thread_id: win32.DWORD,
|
||||
@@ -22,7 +26,7 @@ _thread_priority_map := [Thread_Priority]i32{
|
||||
.High = +2,
|
||||
}
|
||||
|
||||
_create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
_create :: proc(procedure: Thread_Proc, priority: Thread_Priority, name: Maybe(string)) -> ^Thread {
|
||||
win32_thread_id: win32.DWORD
|
||||
|
||||
__windows_thread_entry_proc :: proc "system" (t_: rawptr) -> win32.DWORD {
|
||||
@@ -44,6 +48,8 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
runtime.run_thread_local_cleaners()
|
||||
}
|
||||
|
||||
_set_name(t)
|
||||
|
||||
t.procedure(t)
|
||||
}
|
||||
|
||||
@@ -76,6 +82,7 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
thread.win32_thread = win32_thread
|
||||
thread.win32_thread_id = win32_thread_id
|
||||
thread.id = int(win32_thread_id)
|
||||
thread.name = name
|
||||
|
||||
ok := win32.SetThreadPriority(win32_thread, _thread_priority_map[priority])
|
||||
assert(ok == true)
|
||||
@@ -152,3 +159,38 @@ _yield :: proc() {
|
||||
win32.SwitchToThread()
|
||||
}
|
||||
|
||||
_get_name :: proc(thread: ^Thread, allocator: runtime.Allocator, loc: runtime.Source_Code_Location) -> (name: string, err: runtime.Allocator_Error) {
|
||||
t_handle: win32.HANDLE
|
||||
if thread == nil {
|
||||
t_handle = win32.GetCurrentThread()
|
||||
} else {
|
||||
t_handle = thread.win32_thread
|
||||
}
|
||||
|
||||
buf: win32.PWSTR
|
||||
|
||||
hr := win32.GetThreadDescription(t_handle, &buf)
|
||||
|
||||
if win32.SUCCEEDED(hr) {
|
||||
defer win32.LocalFree(rawptr(buf))
|
||||
name, err = win32.wstring_to_utf8(buf, -1, allocator)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_set_name :: proc(thread: ^Thread) {
|
||||
name, ok := thread.name.?
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
t_handle := thread.win32_thread
|
||||
|
||||
// _THREAD_DESCRIPTION_LENGTH includes terminating null
|
||||
buflen := len(name) + 1 < _THREAD_DESCRIPTION_LENGTH ? len(name) + 1 : _THREAD_DESCRIPTION_LENGTH
|
||||
buf := make([]u16, buflen)
|
||||
defer delete(buf)
|
||||
utf16.encode_string(buf[:len(buf) - 1], name)
|
||||
win32.SetThreadDescription(t_handle, cstring16(raw_data(buf[:])))
|
||||
}
|
||||
|
||||
|
||||
@@ -49,4 +49,40 @@ poly_data_test :: proc(_t: ^testing.T) {
|
||||
defer free(t4)
|
||||
|
||||
thread.join_multiple(t1, t2, t3, t4)
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
name_test :: proc(_t: ^testing.T) {
|
||||
@static name_test_t: ^testing.T
|
||||
@static main_wait := true
|
||||
@static child_wait := true
|
||||
name_test_t = _t
|
||||
|
||||
t := thread.create_and_start(name = "test_name", fn = proc() {
|
||||
main_wait = false
|
||||
|
||||
for (child_wait) {}
|
||||
|
||||
n, err := thread.get_name()
|
||||
defer if err != nil {
|
||||
delete(n)
|
||||
}
|
||||
testing.expect(name_test_t, err == nil, "thread name allocation failed")
|
||||
testing.expectf(name_test_t, n == "test_name","thread name on self did not match : got %v", n)
|
||||
|
||||
})
|
||||
defer free(t)
|
||||
|
||||
for (main_wait) {}
|
||||
|
||||
n, err := thread.get_name(t)
|
||||
defer if err != nil {
|
||||
delete(n)
|
||||
}
|
||||
testing.expect(name_test_t, err == nil, "thread name allocation failed")
|
||||
testing.expectf(name_test_t, n == "test_name","thread name on main did not match : got %v", n)
|
||||
|
||||
child_wait = false
|
||||
|
||||
thread.join(t)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user