DECLARE_TEST( error, thread ) { //Launch 32 threads object_t thread[32]; int i; for( i = 0; i < 32; ++i ) { thread[i] = thread_create( error_thread, "error", THREAD_PRIORITY_NORMAL, 0 ); thread_start( thread[i], 0 ); } test_wait_for_threads_startup( thread, 32 ); test_wait_for_threads_finish( thread, 32 ); for( i = 0; i < 32; ++i ) { EXPECT_EQ( thread_result( thread[i] ), 0 ); thread_destroy( thread[i] ); } test_wait_for_threads_exit( thread, 32 ); return 0; }
DECLARE_TEST(objectmap, thread) { objectmap_t* map; thread_t thread[32]; size_t ith; size_t num_threads = math_clamp(system_hardware_threads() * 4, 4, 32); map = objectmap_allocate((num_threads + 1) * 512); for (ith = 0; ith < num_threads; ++ith) thread_initialize(&thread[ith], objectmap_thread, map, STRING_CONST("objectmap_thread"), THREAD_PRIORITY_NORMAL, 0); for (ith = 0; ith < num_threads; ++ith) thread_start(&thread[ith]); test_wait_for_threads_startup(thread, num_threads); test_wait_for_threads_finish(thread, num_threads); for (ith = 0; ith < num_threads; ++ith) EXPECT_EQ(thread[ith].result, 0); for (ith = 0; ith < num_threads; ++ith) thread_finalize(&thread[ith]); objectmap_deallocate(map); return 0; }
DECLARE_TEST(mutex, sync) { mutex_t* mutex; thread_t thread[32]; size_t ith; mutex = mutex_allocate(STRING_CONST("test")); mutex_lock(mutex); for (ith = 0; ith < 32; ++ith) thread_initialize(&thread[ith], mutex_thread, mutex, STRING_CONST("mutex_thread"), THREAD_PRIORITY_NORMAL, 0); for (ith = 0; ith < 32; ++ith) thread_start(&thread[ith]); test_wait_for_threads_startup(thread, 32); mutex_unlock(mutex); test_wait_for_threads_finish(thread, 32); for (ith = 0; ith < 32; ++ith) thread_finalize(&thread[ith]); mutex_deallocate(mutex); EXPECT_EQ(thread_counter, 32 * 128); return 0; }
DECLARE_TEST(atomic, cas) { size_t num_threads = math_clamp(system_hardware_threads() * 4, 4, 32); size_t ithread; thread_t threads[32]; cas_value_t cas_values[32]; for (ithread = 0; ithread < num_threads; ++ithread) { cas_values[ithread].val_32 = (int32_t)ithread; cas_values[ithread].val_64 = (int64_t)ithread; cas_values[ithread].val_ptr = (void*)(uintptr_t)ithread; thread_initialize(&threads[ithread], cas_thread, &cas_values[ithread], STRING_CONST("cas"), THREAD_PRIORITY_NORMAL, 0); } for (ithread = 0; ithread < num_threads; ++ithread) thread_start(&threads[ithread]); test_wait_for_threads_startup(threads, num_threads); test_wait_for_threads_finish(threads, num_threads); for (ithread = 0; ithread < num_threads; ++ithread) thread_finalize(&threads[ithread]); EXPECT_EQ(atomic_load32(&val_32), 0); EXPECT_EQ(atomic_load64(&val_64), 0); EXPECT_EQ(atomic_loadptr(&val_ptr), 0); return 0; }
DECLARE_TEST(profile, thread) { thread_t thread[32]; int ith; uint64_t frame; error_t err = error(); _test_profile_offset = 0; atomic_store32(&_test_profile_output_counter, 0); profile_initialize(STRING_CONST("test_profile"), _test_profile_buffer, 30000); profile_enable(true); profile_set_output_wait(1); log_enable_stdout(false); for (ith = 0; ith < 32; ++ith) thread_initialize(&thread[ith], _profile_fail_thread, 0, STRING_CONST("profile_thread"), THREAD_PRIORITY_NORMAL, 0); for (ith = 0; ith < 32; ++ith) thread_start(&thread[ith]); test_wait_for_threads_startup(thread, 32); for (frame = 0; frame < 1000; ++frame) { thread_sleep(16); profile_end_frame(frame); } for (ith = 0; ith < 32; ++ith) thread_signal(&thread[ith]); test_wait_for_threads_finish(thread, 32); for (ith = 0; ith < 32; ++ith) thread_finalize(&thread[ith]); log_enable_stdout(true); err = error(); thread_sleep(1000); profile_enable(false); profile_finalize(); #if BUILD_ENABLE_PROFILE EXPECT_INTGT(atomic_load32(&_test_profile_output_counter), 0); //TODO: Implement parsing output results #else EXPECT_INTEQ(atomic_load32(&_test_profile_output_counter), 0); #endif EXPECT_INTEQ(err, ERROR_NONE); return 0; }
DECLARE_TEST(error, thread) { //Launch 32 threads thread_t thread[32]; int i; for (i = 0; i < 32; ++i) thread_initialize(&thread[i], error_thread, 0, STRING_CONST("error"), THREAD_PRIORITY_NORMAL, 0); for (i = 0; i < 32; ++i) thread_start(&thread[i]); test_wait_for_threads_startup(thread, 32); test_wait_for_threads_finish(thread, 32); for (i = 0; i < 32; ++i) { EXPECT_EQ(thread[i].result, 0); thread_finalize(&thread[i]); } return 0; }
DECLARE_TEST(atomic, add) { size_t num_threads = math_clamp(system_hardware_threads() * 4, 4, 32); size_t ithread; thread_t threads[32]; for (ithread = 0; ithread < num_threads; ++ithread) thread_initialize(&threads[ithread], add_thread, 0, STRING_CONST("add"), THREAD_PRIORITY_NORMAL, 0); for (ithread = 0; ithread < num_threads; ++ithread) thread_start(&threads[ithread]); test_wait_for_threads_startup(threads, num_threads); test_wait_for_threads_finish(threads, num_threads); for (ithread = 0; ithread < num_threads; ++ithread) thread_finalize(&threads[ithread]); EXPECT_EQ(atomic_load32(&val_32), 0); EXPECT_EQ(atomic_load64(&val_64), 0); return 0; }
DECLARE_TEST(mutex, signal) { mutex_t* mutex; thread_t thread[32]; size_t ith; mutex = mutex_allocate(STRING_CONST("test")); mutex_lock(mutex); for (ith = 0; ith < 32; ++ith) thread_initialize(&thread[ith], thread_waiter, mutex, STRING_CONST("thread_wait"), THREAD_PRIORITY_NORMAL, 0); for (ith = 0; ith < 32; ++ith) thread_start(&thread[ith]); mutex_unlock(mutex); test_wait_for_threads_startup(thread, 32); while (atomic_load32(&thread_waiting) < 32) thread_yield(); thread_sleep(1000); //Hack wait to give threads time to progress from atomic_incr to mutex_wait mutex_signal(mutex); test_wait_for_threads_finish(thread, 32); for (ith = 0; ith < 32; ++ith) thread_finalize(&thread[ith]); EXPECT_EQ(atomic_load32(&thread_waited), 32); EXPECT_FALSE(mutex_try_wait(mutex, 500)); mutex_deallocate(mutex); return 0; }
DECLARE_TEST(profile, stream) { thread_t thread[32]; int ith; uint64_t frame; string_t filename; error(); //Clear error filename = path_allocate_concat(STRING_ARGS(environment_temporary_directory()), STRING_CONST("test.profile")); //log_infof(HASH_TEST, STRING_CONST("Output to profile file: %.*s"), STRING_FORMAT(filename)); fs_make_directory(STRING_ARGS(environment_temporary_directory())); _profile_stream = fs_open_file(STRING_ARGS(filename), STREAM_OUT | STREAM_BINARY); string_deallocate(filename.str); profile_initialize(STRING_CONST("test_profile"), _test_profile_buffer, TEST_PROFILE_BUFFER_SIZE); profile_set_output(_profile_file_writer); profile_set_output_wait(10); profile_enable(true); for (ith = 0; ith < 32; ++ith) thread_initialize(&thread[ith], _profile_stream_thread, 0, STRING_CONST("profile_thread"), THREAD_PRIORITY_NORMAL, 0); for (ith = 0; ith < 32; ++ith) thread_start(&thread[ith]); test_wait_for_threads_startup(thread, 32); for (frame = 0; frame < 1000; ++frame) { thread_sleep(16); profile_log( STRING_CONST("This is a really long profile log line that should break into multiple profile blocks automatically without causing any issues whatsoever if everything works as expected which it should or the code needs to be fixed")); profile_end_frame(frame++); if ((frame % 30) == 0) { profile_enable(false); thread_sleep(10); profile_enable(true); } } for (ith = 0; ith < 32; ++ith) thread_signal(&thread[ith]); test_wait_for_threads_finish(thread, 32); for (ith = 0; ith < 32; ++ith) thread_finalize(&thread[ith]); profile_end_frame(frame++); profile_set_output_wait(10000); thread_sleep(1000); profile_begin_block(STRING_CONST("Should be cleaned up")); profile_end_block(); profile_enable(false); profile_finalize(); error(); stream_deallocate(_profile_stream); //TODO: Validate that output is sane log_debugf(HASH_TEST, STRING_CONST("Generated %" PRId64 " blocks"), atomic_load64(&_profile_generated_blocks)); return 0; }