DECLARE_TEST( atomic, cas ) { int num_threads = 32; int ithread; object_t threads[32]; cas_value_t cas_values[32]; for( ithread = 0; ithread < num_threads; ++ithread ) { threads[ithread] = thread_create( cas_thread, "cas", THREAD_PRIORITY_NORMAL, 0 ); cas_values[ithread].val_32 = ithread; cas_values[ithread].val_64 = ithread; cas_values[ithread].val_ptr = (void*)(uintptr_t)ithread; } for( ithread = 0; ithread < num_threads; ++ithread ) thread_start( threads[ithread], &cas_values[ithread] ); test_wait_for_threads_startup( threads, num_threads ); for( ithread = 0; ithread < num_threads; ++ithread ) thread_destroy( threads[ithread] ); test_wait_for_threads_exit( threads, num_threads ); EXPECT_EQ( val_32, 0 ); EXPECT_EQ( val_64, 0 ); EXPECT_EQ( val_ptr, 0 ); return 0; }
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( mutex, sync ) { mutex_t* mutex; object_t thread[32]; int ith; mutex = mutex_allocate( "test" ); mutex_lock( mutex ); for( ith = 0; ith < 32; ++ith ) { thread[ith] = thread_create( mutex_thread, "mutex_thread", THREAD_PRIORITY_NORMAL, 0 ); thread_start( thread[ith], mutex ); } test_wait_for_threads_startup( thread, 32 ); for( ith = 0; ith < 32; ++ith ) { thread_terminate( thread[ith] ); thread_destroy( thread[ith] ); } mutex_unlock( mutex ); test_wait_for_threads_exit( thread, 32 ); mutex_deallocate( mutex ); EXPECT_EQ( thread_counter, 32 * 128 ); return 0; }
DECLARE_TEST( profile, thread ) { object_t thread[32]; int ith; int frame; error_t err = error(); _test_profile_offset = 0; atomic_store32( &_test_profile_output_counter, 0 ); profile_initialize( "test_profile", _test_profile_buffer, 30000/*_test_profile_buffer_size*/ ); profile_enable( true ); profile_set_output_wait( 1 ); log_info( HASH_TEST, "This test will intentionally run out of memory in profiling system" ); for( ith = 0; ith < 32; ++ith ) { thread[ith] = thread_create( _profile_fail_thread, "profile_thread", THREAD_PRIORITY_NORMAL, 0 ); thread_start( thread[ith], 0 ); } 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_terminate( thread[ith] ); thread_destroy( thread[ith] ); thread_yield(); } test_wait_for_threads_exit( thread, 32 ); thread_sleep( 1000 ); profile_enable( false ); profile_shutdown(); err = error(); #if BUILD_ENABLE_PROFILE EXPECT_GT( atomic_load32( &_test_profile_output_counter ), 0 ); //TODO: Implement parsing output results #else EXPECT_EQ( atomic_load32( &_test_profile_output_counter ), 0 ); #endif EXPECT_EQ( err, ERROR_NONE ); return 0; }
DECLARE_TEST( semaphore, threaded ) { object_t thread[32]; int ith; int failed_waits; semaphore_test_t test; semaphore_initialize( &test.read, 0 ); semaphore_initialize( &test.write, 0 ); test.loopcount = 128; test.counter = 0; for( ith = 0; ith < 32; ++ith ) { thread[ith] = thread_create( semaphore_waiter, "semaphore_waiter", THREAD_PRIORITY_NORMAL, 0 ); thread_start( thread[ith], &test ); } test_wait_for_threads_startup( thread, 32 ); failed_waits = 0; for( ith = 0; ith < test.loopcount * 32; ++ith ) { semaphore_post( &test.read ); thread_yield(); if( !semaphore_try_wait( &test.write, 200 ) ) { failed_waits++; EXPECT_TRUE( semaphore_wait( &test.write ) ); } } for( ith = 0; ith < 32; ++ith ) { thread_terminate( thread[ith] ); thread_destroy( thread[ith] ); thread_yield(); } test_wait_for_threads_exit( thread, 32 ); EXPECT_EQ( test.counter, test.loopcount * 32 ); EXPECT_EQ( failed_waits, 0 ); semaphore_destroy( &test.read ); semaphore_destroy( &test.write ); return 0; }
DECLARE_TEST( uuid, threaded ) { object_t thread[32]; int ith, i, jth, j; int num_threads = math_clamp( system_hardware_threads() + 1, 3, 32 ); for( ith = 0; ith < num_threads; ++ith ) { thread[ith] = thread_create( uuid_thread_time, "uuid_thread", THREAD_PRIORITY_NORMAL, 0 ); thread_start( thread[ith], (void*)(uintptr_t)ith ); } test_wait_for_threads_startup( thread, num_threads ); for( ith = 0; ith < num_threads; ++ith ) { thread_terminate( thread[ith] ); thread_destroy( thread[ith] ); } test_wait_for_threads_exit( thread, num_threads ); for( ith = 0; ith < num_threads; ++ith ) { for( i = 0; i < 8192; ++i ) { for( jth = ith + 1; jth < num_threads; ++jth ) { for( j = 0; j < 8192; ++j ) { EXPECT_FALSE( uuid_equal( uuid_thread_store[ith][i], uuid_thread_store[jth][j] ) ); } } for( j = i + 1; j < 8192; ++j ) { EXPECT_FALSE( uuid_equal( uuid_thread_store[ith][i], uuid_thread_store[ith][j] ) ); } } } return 0; }
DECLARE_TEST( mutex, signal ) { mutex_t* mutex; object_t thread[32]; int ith; mutex = mutex_allocate( "test" ); mutex_lock( mutex ); for( ith = 0; ith < 32; ++ith ) { thread[ith] = thread_create( thread_wait, "thread_wait", THREAD_PRIORITY_NORMAL, 0 ); thread_start( thread[ith], mutex ); } 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 ); for( ith = 0; ith < 32; ++ith ) { thread_terminate( thread[ith] ); thread_destroy( thread[ith] ); } test_wait_for_threads_exit( thread, 32 ); EXPECT_EQ( atomic_load32( &thread_waited ), 32 ); EXPECT_FALSE( mutex_wait( mutex, 500 ) ); mutex_deallocate( mutex ); return 0; }
DECLARE_TEST( atomic, add ) { int num_threads = 32; int ithread; object_t threads[32]; for( ithread = 0; ithread < num_threads; ++ithread ) threads[ithread] = thread_create( add_thread, "add", THREAD_PRIORITY_NORMAL, 0 ); for( ithread = 0; ithread < num_threads; ++ithread ) thread_start( threads[ithread], 0 ); test_wait_for_threads_startup( threads, num_threads ); for( ithread = 0; ithread < num_threads; ++ithread ) thread_destroy( threads[ithread] ); test_wait_for_threads_exit( threads, num_threads ); EXPECT_EQ( val_32, 0 ); EXPECT_EQ( val_64, 0 ); return 0; }
DECLARE_TEST( profile, stream ) { object_t thread[32]; int ith; int frame; char* filename; error(); filename = path_merge( environment_temporary_directory(), "test.profile" ); log_infof( HASH_TEST, "Output to profile file: %s", filename ); fs_make_directory( environment_temporary_directory() ); _profile_stream = fs_open_file( filename, STREAM_OUT | STREAM_BINARY ); string_deallocate( filename ); profile_initialize( "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[ith] = thread_create( _profile_stream_thread, "profile_thread", THREAD_PRIORITY_NORMAL, 0 ); thread_start( thread[ith], 0 ); } test_wait_for_threads_startup( thread, 32 ); for( frame = 0; frame < 1000; ++frame ) { thread_sleep( 16 ); profile_log( "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_terminate( thread[ith] ); thread_destroy( thread[ith] ); thread_yield(); } test_wait_for_threads_exit( thread, 32 ); profile_end_frame( frame++ ); profile_set_output_wait( 100 ); thread_sleep( 1000 ); profile_enable( false ); profile_shutdown(); error(); stream_deallocate( _profile_stream ); //TODO: Validate that output is sane log_debugf( HASH_TEST, "Generated %lld blocks", atomic_load64( &_profile_generated_blocks ) ); return 0; }
DECLARE_TEST( event, delay_threaded ) { object_t thread[32]; producer_thread_arg_t args[32] = {0}; event_stream_t* stream; event_block_t* block; event_t* event; tick_t endtime, curtime, payloadtime, begintime, prevtime; unsigned int read[32]; int i; bool running = true; int num_threads = math_clamp( system_hardware_threads() * 4, 4, 32 ); stream = event_stream_allocate( 0 ); begintime = time_current(); for( i = 0; i < num_threads; ++i ) { args[i].stream = stream; args[i].end_time = time_current() + ( time_ticks_per_second() * 5 ); args[i].max_delay = time_ticks_per_second() * 5; args[i].sleep_time = 50; args[i].id = i; read[i] = 0; thread[i] = thread_create( producer_thread, "event_producer", THREAD_PRIORITY_NORMAL, 0 ); thread_start( thread[i], &args[i] ); } test_wait_for_threads_startup( thread, num_threads ); while( running ) { running = false; for( i = 0; i < num_threads; ++i ) { if( thread_is_running( thread[i] ) ) { running = true; break; } } thread_yield(); prevtime = begintime; begintime = time_current(); block = event_stream_process( stream ); event = event_next( block, 0 ); curtime = time_current(); while( event ) { running = true; ++read[ event->object ]; memcpy( &payloadtime, event->payload, sizeof( tick_t ) ); EXPECT_GE( event_payload_size( event ), 8 ); EXPECT_LE( event_payload_size( event ), 256 ); EXPECT_GE( payloadtime, prevtime ); EXPECT_GE( curtime, payloadtime ); event = event_next( block, event ); curtime = time_current(); } } endtime = time_current() + ( time_ticks_per_second() * 6 ); do { prevtime = begintime; begintime = time_current(); block = event_stream_process( stream ); event = event_next( block, 0 ); curtime = time_current(); while( event ) { ++read[ event->object ]; memcpy( &payloadtime, event->payload, sizeof( tick_t ) ); EXPECT_GE( event_payload_size( event ), 8 ); EXPECT_LE( event_payload_size( event ), 256 ); EXPECT_GE( payloadtime, prevtime ); EXPECT_GE( curtime, payloadtime ); event = event_next( block, event ); curtime = time_current(); } thread_sleep( 10 ); } while( time_current() < endtime ); for( i = 0; i < num_threads; ++i ) { unsigned int should_have_read = (unsigned int)((uintptr_t)thread_result( thread[i] )); EXPECT_EQ( read[i], should_have_read ); thread_terminate( thread[i] ); thread_destroy( thread[i] ); } test_wait_for_threads_exit( thread, num_threads ); event_stream_deallocate( stream ); return 0; }