/*** * The thread pool has the property that all the threads will keep running until signalled to quit. * Each thread will call pthread_exit() once it has completed execution of it's current 'task_t'. */ void thread_pool_join_all( thread_pool_t *pool ) { dna_log(DEBUG, "Joining thread pool:"); while ( !fifo_is_empty(pool->thread_queue) ) { dna_mutex_lock( pool->mutex ); int threadsAreStillRunning = 0; while ( (threadsAreStillRunning = fifo_any( pool->thread_queue, &thread_context_is_running)) ) { dna_log(DEBUG, "Some threads are still running...%i", threadsAreStillRunning); dna_cond_wait( pool->wait, pool->mutex ); } dna_thread_context_t *context = (dna_thread_context_t*) fifo_pop( pool->thread_queue ); if (context->runstate == RUNNING) { dna_log(WARN, "context is still running, placing back in the queue."); fifo_push( pool->thread_queue, context ); } else { // if the context is != RUNNING, it's either SHOULD_QUIT or HAS_QUIT // so we have to rely on the null work we pushed earlier to clear // any blocks dna_thread_context_join( context ); dna_thread_context_destroy(context); } dna_mutex_unlock( pool->mutex ); } dna_log(DEBUG, "Thread pool joined."); }
// test-helper: empty the fifo and see what we have in it void fifo_check() { dna_log(DEBUG, "emptying result fifo..."); int ctr = 0; while( !fifo_is_empty(fifo) ) { void *val = fifo_pop( fifo ); if (val) { message_t *msg = (message_t *) val; if (msg) { free(msg); } ctr++; } } dna_log(INFO, "Counted %i elements popped from value queue", ctr); }
void test_empty_thread_pool() { dna_log(INFO, "<-------------------- test_empty_thread_pool ---------------------"); int i = 0; for (i = 0; i < 10 ; i++ ) { dna_log(INFO, ">> --- cycle %i --- <<", i+1); // global here, for all threads to share fifo = fifo_create("values", 0); dna_log(DEBUG, "starting thread pool"); thread_pool_t *pool = thread_pool_create("main pool", 1); dna_log(DEBUG, "destroying thread pool"); thread_pool_exit_all(pool); thread_pool_destroy(pool); fifo_check(); fifo_destroy(fifo); } }
void test_fifo() { dna_log(INFO, "<-------------------- test_fifo ---------------------"); int j = 0; for (j = 0; j < 10; j++) { fifo_t *fifo = fifo_create("<test fifo>", 0); fifo_destroy(fifo); } }
void sleep_for( long seconds ) { dna_log(DEBUG, "Sleeping for %lu", seconds); struct timespec interval = { .tv_sec = (time_t) seconds, .tv_nsec = 0 }; nanosleep(&interval, NULL); } void test_empty_fifo() { dna_log(INFO, "<-------------------- test_empty_fifo ---------------------"); int j = 0; for (j = 0; j < 10; j++) { fifo_t *fifo = fifo_create(" <test fifo>", 0); fifo_destroy(fifo); } }
void test_busy_thread_pool() { dna_log(INFO, "<-------------------- test_busy_thread_pool ---------------------"); fifo = fifo_create("<(busy_thread_pool) value fifo>", 0); thread_pool_t *pool = thread_pool_create("<busy thread pool>", 8); // should auto-determine thread count maybe? dna_log(DEBUG, "adding %i tasks to the queue...", ELEMS); int i = 0; for( i = 0; i < ELEMS; i++ ) { thread_pool_enqueue(pool, &fifo_fill, NULL); } dna_log(DEBUG, "waiting for threads to complete on their own..."); thread_pool_exit_all(pool); thread_pool_join_all( pool ); dna_log(DEBUG, "destroying thread pool..."); thread_pool_destroy( pool ); fifo_check(); dna_log(DEBUG, "destroying global value fifo."); fifo_destroy( fifo ); }
status_t semaphore_alarm (void * data) /* * ARGUMENTS * * data : a thread_t element. * * RETURN * * DNA_BAD_ARGUMENT: the thread parameter is not valid * * DNA_ERROR: the thread is not a valid thread * * DNA_OK: the operation succeeded * * SOURCE */ { thread_t thread = data; status_t status = DNA_OK; watch (status_t) { ensure (thread != NULL, DNA_BAD_ARGUMENT); ensure (thread -> resource_queue != NULL, DNA_ERROR); ensure (thread -> info . status == DNA_THREAD_WAITING, DNA_ERROR); ensure (thread -> info . resource == DNA_RESOURCE_SEMAPHORE, DNA_ERROR); /* * Lock the thread's resource queue. * Extract the thread from the waiting list. */ lock_acquire (& thread -> resource_queue -> lock); status = queue_extract (thread -> resource_queue, thread); lock_release (& thread -> resource_queue -> lock); /* * If the thread was waiting, we can dispatch it */ if (status == DNA_OK) { lock_acquire (& thread -> lock); dna_log(VERBOSE_LEVEL, " %s DNA_NO_RESOURCE ", thread -> info . name); thread -> resource_queue = NULL; thread -> info . sem_tokens = 0; thread -> info . resource = DNA_NO_RESOURCE; thread -> info . resource_id = -1; thread -> info . status = DNA_THREAD_READY; status = scheduler_dispatch (thread); } return status; } }
void *fifo_fill( void *nothing ) { int i = 0; for (i = 0; i < ELEMS; i++) { fifo_push( fifo, NULL ); #ifdef VALUE_LOG dna_log(DEBUG, "<< added %i to value fifo", i); #endif } return NULL; }
/** * Until pthread_exit, pull a task_t out of the task queue and run it on our thread */ void *execute_task_thread_internal( void *args ) { execution_args_t *yargs = (execution_args_t*) args; fifo_t *tasks = yargs->task_list; dna_thread_context_t *context = yargs->thread_context; free( yargs ); dna_log(DEBUG, "started execution of thread %lu", context->id); task_t *task = NULL; while ( !dna_thread_context_should_exit(context) && (task = (task_t*) fifo_pop( tasks ) ) ) { if ( task->func ) { // thread_pool_destroy sends tasks which have NULL members in, don't bother executing it task_execute(task); } task_destroy( task ); if (dna_thread_context_should_exit(context)) { break; } } dna_log(DEBUG, "Execution of thread %lu has finished.", context->id ); return NULL; }
/* Actor system should be the only one repsonsible for creating, storing, retreiving or freeing messages */ message_t *message_create(void *data, int type, actor_t *from) { message_t *message = (message_t*) malloc( sizeof(message_t) ); message->data = data; message->type = type; message->promise = NULL; message->from = from; message->id = ++messageId; if (message->id % 1000 == 0) { dna_log(DEBUG, "New message created. Reached new message id : %lu", message->id ); } return message; }
void thread_pool_destroy( thread_pool_t *pool ) { dna_log(DEBUG, "Inside thread_pool_destroy()"); if ( pool ) { dna_log(DEBUG, "Telling threads to exit..."); thread_pool_join_all(pool); dna_log(DEBUG, "Destroying execution context fifo..."); fifo_destroy( pool->thread_queue ); pool->thread_queue = NULL; dna_log(DEBUG, "Destroying tasks in fifo..."); while ( !fifo_is_empty( pool->tasks ) ) { task_t *task = (task_t*) fifo_pop( pool->tasks ); task_destroy( task ); } fifo_destroy( pool->tasks ); pool->tasks = NULL; dna_log(DEBUG, "Freeing thread context pool \"%s\".", pool->name); dna_mutex_destroy( pool->mutex ); dna_cond_destroy( pool->wait ); free(pool->mutex); free(pool->wait); free( pool ); } }
status_t block_device_control (void * handler, int32_t function, va_list arguments, int32_t * p_ret) { interrupt_status_t it_status; block_device_control_t * block_device = (block_device_control_t *)handler; watch (status_t) { ensure (block_device != NULL, DNA_ERROR); it_status = cpu_trap_mask_and_backup (); switch (function) { case DNA_GET_DEVICE_SIZE: { int64_t * p_size = va_arg(arguments, int64_t *); // p_size = (int64_t *)arguments; ensure (p_size != NULL, DNA_ERROR); *p_size = block_device->block_count * block_device->block_size; *p_ret = 0; break; } case DNA_GET_INFO: { device_info_t * info = va_arg (arguments, device_info_t *); ensure (info != NULL, DNA_ERROR); dna_memset (info, 0, sizeof (device_info_t)); info->type = DNA_DISK_DEVICE; *p_ret = 0; break; } default: { dna_log(INFO_LEVEL, "Unsupported control code 0x%x.", function); *p_ret = -1; break; } } cpu_trap_restore (it_status); return DNA_OK; } }
status_t semaphore_get_info (int32_t id, semaphore_info_t * info) /* * ARGUMENTS * * id : the ID of the semaphore to get_info. * * sem_info : a pointer to a sem_info structure. * * RESULT * * DNA_BAD_SEM_ID: the id parameter is invalid * * DNA_BAD_ARGUMENT: the sem_info pointer is invalid * * DNA_OK: the operation succeeded * * SOURCE */ { semaphore_t sem = NULL; semaphore_id_t sid = { .raw = id }; interrupt_status_t it_status = 0; status_t status = DNA_OK; watch (status_t) { ensure (info != NULL, DNA_BAD_ARGUMENT); ensure (sid . s . index < DNA_MAX_SEM, DNA_BAD_SEM_ID); it_status = cpu_trap_mask_and_backup(); lock_acquire (& semaphore_pool . lock); /* * Look for the semaphore with ID id */ sem = & semaphore_pool . data[sid . s . index]; check (invalid_semaphore, sem != NULL, DNA_BAD_SEM_ID); DCACHE_INVAL(&sem->id.raw, sizeof(int32_t)); check (invalid_semaphore, sem -> id . raw == sid . raw, DNA_BAD_SEM_ID); lock_acquire (& sem -> lock); lock_release (& semaphore_pool . lock); /* * Copy the semaphore information */ // cpu_dcache_inv_addr(&sem -> info.tokens); dna_log(VERBOSE_LEVEL, "ID(%d:%d) TOKEN(%d)", sem -> id . s . value, sem -> id . s . index, sem -> info . tokens); DCACHE_INVAL(&sem->info, sizeof(port_info_t)); *info = sem -> info; DCACHE_FLUSH(info, sizeof(port_info_t)); lock_release (& sem -> lock); cpu_trap_restore(it_status); return status; } rescue (invalid_semaphore) { lock_release (& semaphore_pool . lock); cpu_trap_restore(it_status); leave; } }
status_t file_put (int16_t fd) /* * ARGUMENTS * * fd : the file descriptor. * * FUNCTION * Check if the fd entry exist in the current group pool. If it is the case, * decrements its usage counter, and delete the file when the counter reaches 0. * * RESULT * * DNA_INVALID_FD if fd is not a valid file * * DNA_OK if the operation succeed * * SOURCE */ { file_t file; int32_t tid; thread_info_t info; status_t status = DNA_OK; interrupt_status_t it_status = 0; vnode_t vnode = NULL; watch (status_t) { status = thread_find (NULL, & tid); ensure (status == DNA_OK, status); status = thread_get_info (tid, & info); ensure (status == DNA_OK, status); ensure (info . group >= 0, DNA_BAD_ARGUMENT); ensure (info . group < DNA_MAX_GROUP, DNA_BAD_ARGUMENT); /* * Look for the file in the pool. */ it_status = cpu_trap_mask_and_backup(); lock_acquire (& file_pool . lock); file = file_pool . file[info . group][fd]; check (error, file != NULL, DNA_INVALID_FD); check (error, file -> usage_counter > 0, DNA_ERROR); atomic_add (& file -> usage_counter, -1); if (file -> usage_counter == 0 && file -> destroy) { file_pool . file[info . group][fd] = NULL; lock_release (& file_pool . lock); cpu_trap_restore(it_status); status = file -> vnode -> volume -> cmd -> free (file -> vnode -> volume -> data, file -> vnode -> data, file -> data); panic (status != DNA_OK); vnode = file -> vnode; kernel_free (file); status = vnode_put (vnode -> volume -> id, vnode -> id); panic (status != DNA_OK); } else { lock_release (& file_pool . lock); cpu_trap_restore(it_status); } dna_log(VERBOSE_LEVEL, "Put FD %d.", fd); return DNA_OK; } rescue (error) { lock_release (& file_pool . lock); cpu_trap_restore(it_status); leave; } }