diff --git a/core/thread/thread.odin b/core/thread/thread.odin index 9ba03203f..9fcc5b84f 100644 --- a/core/thread/thread.odin +++ b/core/thread/thread.odin @@ -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) - 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) } 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), - size_of(T2) <= size_of(rawptr) { + 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) } 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), - size_of(T2) <= size_of(rawptr), - size_of(T3) <= size_of(rawptr) { + 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) } 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), - size_of(T2) <= size_of(rawptr), - size_of(T3) <= size_of(rawptr) { + 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) } @@ -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 - where size_of(T) <= size_of(rawptr) { + where size_of(T) <= size_of(rawptr) * MAX_USER_ARGUMENTS { thread_proc :: proc(t: ^Thread) { fn := cast(proc(T))t.data 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.data = rawptr(fn) t.user_index = 1 + 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 { t.flags += {.Self_Cleanup} } + t.init_context = init_context start(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 - where size_of(T1) <= size_of(rawptr), - size_of(T2) <= size_of(rawptr) { + 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 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) } t := create(thread_proc, priority) t.data = rawptr(fn) t.user_index = 2 + arg1, arg2 := arg1, arg2 - mem.copy(&t.user_args[0], &arg1, size_of(arg1)) - mem.copy(&t.user_args[1], &arg2, size_of(arg2)) + user_args := mem.slice_to_bytes(t.user_args[:]) + + n := copy(user_args, mem.ptr_to_bytes(&arg1)) + _ = copy(user_args[n:], mem.ptr_to_bytes(&arg2)) + if self_cleanup { t.flags += {.Self_Cleanup} } + t.init_context = init_context start(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 - where size_of(T1) <= size_of(rawptr), - size_of(T2) <= size_of(rawptr), - size_of(T3) <= size_of(rawptr) { + 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 assert(t.user_index >= 3) - arg1 := (^T1)(&t.user_args[0])^ - arg2 := (^T2)(&t.user_args[1])^ - arg3 := (^T3)(&t.user_args[2])^ + + user_args := mem.slice_to_bytes(t.user_args[:]) + 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) } t := create(thread_proc, priority) t.data = rawptr(fn) t.user_index = 3 + arg1, arg2, arg3 := arg1, arg2, arg3 - mem.copy(&t.user_args[0], &arg1, size_of(arg1)) - mem.copy(&t.user_args[1], &arg2, size_of(arg2)) - mem.copy(&t.user_args[2], &arg3, size_of(arg3)) + user_args := mem.slice_to_bytes(t.user_args[:]) + + 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 { t.flags += {.Self_Cleanup} } + t.init_context = init_context start(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 - where size_of(T1) <= size_of(rawptr), - size_of(T2) <= size_of(rawptr), - size_of(T3) <= size_of(rawptr) { + 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 assert(t.user_index >= 4) - arg1 := (^T1)(&t.user_args[0])^ - arg2 := (^T2)(&t.user_args[1])^ - arg3 := (^T3)(&t.user_args[2])^ - arg4 := (^T4)(&t.user_args[3])^ + + user_args := mem.slice_to_bytes(t.user_args[:]) + 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):]))^ + arg4 := (^T4)(raw_data(user_args[size_of(T1) + size_of(T2) + size_of(T3):]))^ + fn(arg1, arg2, arg3, arg4) } t := create(thread_proc, priority) t.data = rawptr(fn) t.user_index = 4 + arg1, arg2, arg3, arg4 := arg1, arg2, arg3, arg4 - mem.copy(&t.user_args[0], &arg1, size_of(arg1)) - mem.copy(&t.user_args[1], &arg2, size_of(arg2)) - mem.copy(&t.user_args[2], &arg3, size_of(arg3)) - mem.copy(&t.user_args[3], &arg4, size_of(arg4)) + user_args := mem.slice_to_bytes(t.user_args[:]) + + n := copy(user_args, mem.ptr_to_bytes(&arg1)) + 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 { t.flags += {.Self_Cleanup} } + t.init_context = init_context start(t) return t } - _select_context_for_thread :: proc(init_context: Maybe(runtime.Context)) -> runtime.Context { ctx, ok := init_context.? if !ok { diff --git a/tests/core/Makefile b/tests/core/Makefile index 919262f85..3fdadc246 100644 --- a/tests/core/Makefile +++ b/tests/core/Makefile @@ -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 \ 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: $(PYTHON) download_assets.py @@ -61,3 +61,6 @@ net_test: fmt_test: $(ODIN) run fmt -out:test_core_fmt + +thread_test: + $(ODIN) run thread -out:test_core_thread diff --git a/tests/core/build.bat b/tests/core/build.bat index 1d146c8a4..311c9c78e 100644 --- a/tests/core/build.bat +++ b/tests/core/build.bat @@ -85,3 +85,8 @@ echo --- echo Running core:container tests echo --- %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 diff --git a/tests/core/thread/test_core_thread.odin b/tests/core/thread/test_core_thread.odin new file mode 100644 index 000000000..c0c7396a7 --- /dev/null +++ b/tests/core/thread/test_core_thread.odin @@ -0,0 +1,84 @@ +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 poly_data_test_t: ^testing.T + poly_data_test_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(poly_data_test_t, b == b_expect, "thread poly data not correct") + }) + defer free(t1) + + 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(poly_data_test_t, b == b_expect, "thread poly data not correct") + expect(poly_data_test_t, b2 == b2_expect, "thread poly data not correct") + }) + defer free(t2) + + 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(poly_data_test_t, b == b_expect, "thread poly data not correct") + expect(poly_data_test_t, b2 == b2_expect, "thread poly data not correct") + expect(poly_data_test_t, b3 == 333, "thread poly data not correct") + }) + defer free(t3) + + 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(poly_data_test_t, n == 111, "thread poly data not correct") + expect(poly_data_test_t, b == b_expect, "thread poly data not correct") + expect(poly_data_test_t, n2 == 333, "thread poly data not correct") + expect(poly_data_test_t, n4 == 5, "thread poly data not correct") + }) + defer free(t4) + + thread.join_multiple(t1, t2, t3, t4) +}