Allow larger thread poly data

The poly data currently has the restriction of being less than a
pointer's size, but there is much more space in the `Thread.user_args`
array which can be utilized, this commit allows you to pass types that are
larger than pointer length as long as the total size of the poly data is
less than that of the `Thread.user_args`.
This commit is contained in:
Laytan Laats
2023-11-15 19:08:03 +01:00
parent 8028033513
commit 9078ddaf5a
4 changed files with 144 additions and 39 deletions
+55 -38
View File
@@ -116,26 +116,21 @@ run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(
} }
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) { 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_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(rawptr), where size_of(T1) + size_of(T2) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
size_of(T2) <= size_of(rawptr) {
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_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(rawptr), where size_of(T1) + size_of(T2) + size_of(T3) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
size_of(T2) <= size_of(rawptr),
size_of(T3) <= size_of(rawptr) {
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_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(rawptr), where size_of(T1) + size_of(T2) + size_of(T3) + size_of(T4) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
size_of(T2) <= size_of(rawptr),
size_of(T3) <= size_of(rawptr) {
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)
} }
@@ -178,7 +173,7 @@ create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_co
} }
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) { where size_of(T) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
thread_proc :: proc(t: ^Thread) { thread_proc :: proc(t: ^Thread) {
fn := cast(proc(T))t.data fn := cast(proc(T))t.data
assert(t.user_index >= 1) assert(t.user_index >= 1)
@@ -188,96 +183,118 @@ create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_contex
t := create(thread_proc, priority) t := create(thread_proc, priority)
t.data = rawptr(fn) t.data = rawptr(fn)
t.user_index = 1 t.user_index = 1
data := data data := data
mem.copy(&t.user_args[0], &data, size_of(data))
mem.copy(&t.user_args[0], &data, size_of(T))
if self_cleanup { if self_cleanup {
t.flags += {.Self_Cleanup} t.flags += {.Self_Cleanup}
} }
t.init_context = init_context t.init_context = init_context
start(t) start(t)
return t return t
} }
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(rawptr), where size_of(T1) + size_of(T2) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
size_of(T2) <= size_of(rawptr) {
thread_proc :: proc(t: ^Thread) { thread_proc :: proc(t: ^Thread) {
fn := cast(proc(T1, T2))t.data fn := cast(proc(T1, T2))t.data
assert(t.user_index >= 2) assert(t.user_index >= 2)
arg1 := (^T1)(&t.user_args[0])^
arg2 := (^T2)(&t.user_args[1])^ user_args := mem.slice_to_bytes(t.user_args[:])
arg1 := (^T1)(raw_data(user_args))^
arg2 := (^T2)(raw_data(user_args[size_of(T1):]))^
fn(arg1, arg2) fn(arg1, arg2)
} }
t := create(thread_proc, priority) t := create(thread_proc, priority)
t.data = rawptr(fn) t.data = rawptr(fn)
t.user_index = 2 t.user_index = 2
arg1, arg2 := arg1, arg2 arg1, arg2 := arg1, arg2
mem.copy(&t.user_args[0], &arg1, size_of(arg1)) user_args := mem.slice_to_bytes(t.user_args[:])
mem.copy(&t.user_args[1], &arg2, size_of(arg2))
n := copy(user_args, mem.ptr_to_bytes(&arg1))
_ = copy(user_args[n:], mem.ptr_to_bytes(&arg2))
if self_cleanup { if self_cleanup {
t.flags += {.Self_Cleanup} t.flags += {.Self_Cleanup}
} }
t.init_context = init_context t.init_context = init_context
start(t) start(t)
return t return t
} }
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(rawptr), where size_of(T1) + size_of(T2) + size_of(T3) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
size_of(T2) <= size_of(rawptr),
size_of(T3) <= size_of(rawptr) {
thread_proc :: proc(t: ^Thread) { thread_proc :: proc(t: ^Thread) {
fn := cast(proc(T1, T2, T3))t.data fn := cast(proc(T1, T2, T3))t.data
assert(t.user_index >= 3) assert(t.user_index >= 3)
arg1 := (^T1)(&t.user_args[0])^
arg2 := (^T2)(&t.user_args[1])^ user_args := mem.slice_to_bytes(t.user_args[:])
arg3 := (^T3)(&t.user_args[2])^ arg1 := (^T1)(raw_data(user_args))^
arg2 := (^T2)(raw_data(user_args[size_of(T1):]))^
arg3 := (^T3)(raw_data(user_args[size_of(T1) + size_of(T2):]))^
fn(arg1, arg2, arg3) fn(arg1, arg2, arg3)
} }
t := create(thread_proc, priority) t := create(thread_proc, priority)
t.data = rawptr(fn) t.data = rawptr(fn)
t.user_index = 3 t.user_index = 3
arg1, arg2, arg3 := arg1, arg2, arg3 arg1, arg2, arg3 := arg1, arg2, arg3
mem.copy(&t.user_args[0], &arg1, size_of(arg1)) user_args := mem.slice_to_bytes(t.user_args[:])
mem.copy(&t.user_args[1], &arg2, size_of(arg2))
mem.copy(&t.user_args[2], &arg3, size_of(arg3)) n := copy(user_args, mem.ptr_to_bytes(&arg1))
n += copy(user_args[n:], mem.ptr_to_bytes(&arg2))
_ = copy(user_args[n:], mem.ptr_to_bytes(&arg3))
if self_cleanup { if self_cleanup {
t.flags += {.Self_Cleanup} t.flags += {.Self_Cleanup}
} }
t.init_context = init_context t.init_context = init_context
start(t) start(t)
return t return t
} }
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(rawptr), where size_of(T1) + size_of(T2) + size_of(T3) + size_of(T4) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
size_of(T2) <= size_of(rawptr),
size_of(T3) <= size_of(rawptr) {
thread_proc :: proc(t: ^Thread) { thread_proc :: proc(t: ^Thread) {
fn := cast(proc(T1, T2, T3, T4))t.data fn := cast(proc(T1, T2, T3, T4))t.data
assert(t.user_index >= 4) assert(t.user_index >= 4)
arg1 := (^T1)(&t.user_args[0])^
arg2 := (^T2)(&t.user_args[1])^ user_args := mem.slice_to_bytes(t.user_args[:])
arg3 := (^T3)(&t.user_args[2])^ arg1 := (^T1)(raw_data(user_args))^
arg4 := (^T4)(&t.user_args[3])^ arg2 := (^T2)(raw_data(user_args[size_of(T1):]))^
arg3 := (^T3)(raw_data(user_args[size_of(T1) + size_of(T2):]))^
arg4 := (^T4)(raw_data(user_args[size_of(T1) + size_of(T2) + size_of(T3):]))^
fn(arg1, arg2, arg3, arg4) fn(arg1, arg2, arg3, arg4)
} }
t := create(thread_proc, priority) t := create(thread_proc, priority)
t.data = rawptr(fn) t.data = rawptr(fn)
t.user_index = 4 t.user_index = 4
arg1, arg2, arg3, arg4 := arg1, arg2, arg3, arg4 arg1, arg2, arg3, arg4 := arg1, arg2, arg3, arg4
mem.copy(&t.user_args[0], &arg1, size_of(arg1)) user_args := mem.slice_to_bytes(t.user_args[:])
mem.copy(&t.user_args[1], &arg2, size_of(arg2))
mem.copy(&t.user_args[2], &arg3, size_of(arg3)) n := copy(user_args, mem.ptr_to_bytes(&arg1))
mem.copy(&t.user_args[3], &arg4, size_of(arg4)) n += copy(user_args[n:], mem.ptr_to_bytes(&arg2))
n += copy(user_args[n:], mem.ptr_to_bytes(&arg3))
_ = copy(user_args[n:], mem.ptr_to_bytes(&arg4))
if self_cleanup { if self_cleanup {
t.flags += {.Self_Cleanup} t.flags += {.Self_Cleanup}
} }
t.init_context = init_context t.init_context = init_context
start(t) start(t)
return t return t
} }
_select_context_for_thread :: proc(init_context: Maybe(runtime.Context)) -> runtime.Context { _select_context_for_thread :: proc(init_context: Maybe(runtime.Context)) -> runtime.Context {
ctx, ok := init_context.? ctx, ok := init_context.?
if !ok { if !ok {
+4 -1
View File
@@ -3,7 +3,7 @@ PYTHON=$(shell which python3)
all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test \ all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test \
math_test linalg_glsl_math_test filepath_test reflect_test os_exit_test i18n_test match_test c_libc_test net_test \ math_test linalg_glsl_math_test filepath_test reflect_test os_exit_test i18n_test match_test c_libc_test net_test \
fmt_test fmt_test thread_test
download_test_assets: download_test_assets:
$(PYTHON) download_assets.py $(PYTHON) download_assets.py
@@ -61,3 +61,6 @@ net_test:
fmt_test: fmt_test:
$(ODIN) run fmt -out:test_core_fmt $(ODIN) run fmt -out:test_core_fmt
thread_test:
$(ODIN) run thread -out:test_core_thread
+5
View File
@@ -85,3 +85,8 @@ echo ---
echo Running core:container tests echo Running core:container tests
echo --- echo ---
%PATH_TO_ODIN% run container %COMMON% %COLLECTION% -out:test_core_container.exe || exit /b %PATH_TO_ODIN% run container %COMMON% %COLLECTION% -out:test_core_container.exe || exit /b
echo ---
echo Running core:thread tests
echo ---
%PATH_TO_ODIN% run thread %COMMON% %COLLECTION% -out:test_core_thread.exe || exit /b
+80
View File
@@ -0,0 +1,80 @@
package test_core_thread
import "core:testing"
import "core:thread"
import "core:fmt"
import "core:os"
TEST_count := 0
TEST_fail := 0
t := &testing.T{}
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
poly_data_test(t)
if TEST_fail > 0 {
os.exit(1)
}
}
@(test)
poly_data_test :: proc(_t: ^testing.T) {
MAX :: size_of(rawptr) * thread.MAX_USER_ARGUMENTS
@static t: ^testing.T
t = _t
b: [MAX]byte = 8
t1 := thread.create_and_start_with_poly_data(b, proc(b: [MAX]byte) {
b_expect: [MAX]byte = 8
expect(t, b == b_expect, "thread poly data not correct")
}, self_cleanup = true)
b1: [3]uintptr = 1
b2: [MAX / 2]byte = 3
t2 := thread.create_and_start_with_poly_data2(b1, b2, proc(b: [3]uintptr, b2: [MAX / 2]byte) {
b_expect: [3]uintptr = 1
b2_expect: [MAX / 2]byte = 3
expect(t, b == b_expect, "thread poly data not correct")
expect(t, b2 == b2_expect, "thread poly data not correct")
}, self_cleanup = true)
t3 := thread.create_and_start_with_poly_data3(b1, b2, uintptr(333), proc(b: [3]uintptr, b2: [MAX / 2]byte, b3: uintptr) {
b_expect: [3]uintptr = 1
b2_expect: [MAX / 2]byte = 3
expect(t, b == b_expect, "thread poly data not correct")
expect(t, b2 == b2_expect, "thread poly data not correct")
expect(t, b3 == 333, "thread poly data not correct")
}, self_cleanup = true)
t4 := thread.create_and_start_with_poly_data4(uintptr(111), b1, uintptr(333), u8(5), proc(n: uintptr, b: [3]uintptr, n2: uintptr, n4: u8) {
b_expect: [3]uintptr = 1
expect(t, n == 111, "thread poly data not correct")
expect(t, b == b_expect, "thread poly data not correct")
expect(t, n2 == 333, "thread poly data not correct")
expect(t, n4 == 5, "thread poly data not correct")
}, self_cleanup = true)
thread.join_multiple(t1, t2, t3, t4)
}