#define ENGINE_THREADS_INTERNAL #include "engine/threads.h" #include #include #include #include void threads_init(struct threads *handler, size_t capacity) { // maybe this will be a speed up? // why am i micro optimizing here?????? // idk i just watched the eskil steenberg video about UB memset(handler, 0, sizeof(*handler)); handler->threads = malloc(sizeof(*handler->threads) * capacity); handler->args = malloc(sizeof(*handler->args) * capacity); handler->capacity = capacity; handler->count = 0; handler->running_count = 0; handler->engine_messages = NULL; handler->go_args = NULL; handler->position = NULL; } void threads_deinit(struct threads *handler) { assert(handler->running_count == 0); free(handler->threads); free(handler->args); } void threads_reserve(struct threads *handler, size_t capacity) { if (handler->capacity >= capacity) return; handler->threads = realloc( handler->threads, sizeof(*handler->threads) * capacity ); handler->args = realloc( handler->args, sizeof(*handler->args) * capacity ); handler->capacity = capacity; } bool threads_go_loop(struct threads *handler) { // idk maybe do something while waiting for calculations to flow in return false; } bool threads_all_done(struct threads *handler) { int running_count = handler->running_count; assert(running_count > 0); for (int i = 0; i < running_count; i++) { // `finished` is part of the common initial sequence of both union // members (stop, finished), so reading it via either member is well-defined. if (atomic_load(&handler->args[i].root_thread_args.finished) == 1) continue; return false; } return true; } void threads_cleanup(struct threads *handler) { int running_count = handler->running_count; for (int i = 0; i < running_count; i++) { __threads_args_init(handler->args + i, handler, i); pthread_cancel(handler->threads[i]); } handler->running_count = 0; } void __threads_args_init( thread_args_t* _args, struct threads *handler, int i ) { if (i > 0) { struct child_thread_args *args = (struct child_thread_args*)_args; atomic_init(&args->stop, 0); atomic_init(&args->finished, 0); args->id = i; args->position = handler->position; args->go_args = handler->go_args; args->ttable = &handler->ttable; args->sp_stack = 0; args->message = handler->engine_messages->data[i]; return; } struct root_thread_args *args = (struct root_thread_args*)_args; atomic_init(&args->stop, 0); atomic_init(&args->finished, 0); args->id = i; args->position = handler->position; args->go_args = handler->go_args; args->message = handler->engine_messages->data[i]; args->ttable = &handler->ttable; } #ifdef TEST_MOD bool test_threads_args_init_root_propagates() { engine_message_handler_t dummy[2]; engine_message_handler_t *dummy_ptrs[2] = { &dummy[0], &dummy[1] }; engine_messages_t messages = { .data = dummy_ptrs, .count = 2, .capacity = 2, }; go_args_t go_args = {0}; position_t pos = {0}; struct threads handler = {0}; handler.engine_messages = &messages; handler.go_args = &go_args; handler.position = &pos; ttable_init(&handler.ttable, 2); thread_args_t args; __threads_args_init(&args, &handler, 0); bool ok = true; ok &= args.root_thread_args.id == 0; ok &= atomic_load(&args.root_thread_args.stop) == 0; ok &= atomic_load(&args.root_thread_args.finished) == 0; ok &= args.root_thread_args.ttable == &handler.ttable; ok &= args.root_thread_args.go_args == &go_args; ok &= args.root_thread_args.position == &pos; ok &= args.root_thread_args.message == &dummy[0]; ttable_deinit(handler.ttable); return ok; } bool test_threads_args_init_child_propagates() { engine_message_handler_t dummy[2]; engine_message_handler_t *dummy_ptrs[2] = { &dummy[0], &dummy[1] }; engine_messages_t messages = { .data = dummy_ptrs, .count = 2, .capacity = 2, }; go_args_t go_args = {0}; position_t pos = {0}; struct threads handler = {0}; handler.engine_messages = &messages; handler.go_args = &go_args; handler.position = &pos; ttable_init(&handler.ttable, 2); thread_args_t args; __threads_args_init(&args, &handler, 1); bool ok = true; ok &= args.child_thread_args.id == 1; ok &= args.child_thread_args.ttable == &handler.ttable; ok &= args.child_thread_args.go_args == &go_args; ok &= args.child_thread_args.position == &pos; ok &= args.child_thread_args.message == &dummy[1]; ok &= args.child_thread_args.sp_stack == 0; ttable_deinit(handler.ttable); return ok; } #endif