/** * gda_worker_new: (skip) * * Creates a new #GdaWorker object. * * Returns: (transfer full): a new #GdaWorker, or %NULL if an error occurred * * Since: 6.0 */ GdaWorker * gda_worker_new (void) { GdaWorker *worker; worker = bg_get_spare_gda_worker (); if (worker) return worker; worker = g_slice_new0 (GdaWorker); worker->submit_its = itsignaler_new (); if (!worker->submit_its) { g_slice_free (GdaWorker, worker); return NULL; } worker->ref_count = 1; worker->callbacks_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) declared_callback_free); worker->jobs_hash = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, (GDestroyNotify) worker_job_free); worker->worker_must_quit = 0; worker->location = NULL; g_rec_mutex_init (& worker->rmutex); gchar *str; static guint counter = 0; str = g_strdup_printf ("gdaWrkrTh%u", counter); counter++; worker->worker_thread = g_thread_try_new (str, (GThreadFunc) worker_thread_main, worker, NULL); g_free (str); if (!worker->worker_thread) { itsignaler_unref (worker->submit_its); g_hash_table_destroy (worker->callbacks_hash); g_hash_table_destroy (worker->jobs_hash); g_rec_mutex_clear (& worker->rmutex); g_slice_free (GdaWorker, worker); return NULL; } else bg_update_stats (BG_STARTED_THREADS); #ifdef DEBUG_NOTIFICATION g_print ("[W] created GdaWorker %p\n", worker); #endif bg_update_stats (BG_CREATED_WORKER); return worker; }
void thread_exit(void *ret) { thread_t *t = current_thread; sanity_check_threadcounts(); tdebug("current=%s\n", current_thread?current_thread->name : "NULL"); if (current_thread == main_thread && main_exited == 0) { // the case when the user calls thread_exit() in main thread is complicated // we cannot simply terminate the main thread, because we need that stack to terminate the // whole program normally. so we call exit() to make the c runtime help us get the stack // context where we can just return to terminate the whole program // this will call exit_func() and in turn call thread_exit() again main_exited = 1; exit (0); } // note the thread exit in the blocking graph t->curr_stats.node = bg_exit_node; current_thread->prev_stats.node->num_here--; current_thread->curr_stats.node->num_here++; if( bg_save_stats ) { bg_update_stats(); } // update thread counts num_runnable_threads--; if( t->daemon ) num_daemon_threads--; t->state = ZOMBIE; num_zombie_threads++; // deallocate the TCB // keep the thread, if the thread is Joinable, and we want the return value for something if ( !( t->joinable ) ) { // tell the scheduler thread to delete the current one current_thread_exited = 1; } else { t->ret = ret; if (t->join_thread) thread_resume(t->join_thread); } sanity_check_threadcounts(); // squirrel away the stack limit--not that we'll need it again current_thread->stack_bottom = stack_bottom; current_thread->stack_fingerprint = stack_fingerprint; // give control back to the scheduler #ifdef NO_SCHEDULER_THREAD do_scheduler(NULL); #else co_call(scheduler_thread->coro, NULL); #endif }
/* * main function of the worker thread */ static gpointer worker_thread_main (GdaWorker *worker) { #define TIMER 150 #ifdef DEBUG_NOTIFICATION g_print ("[W] GdaWorker %p, worker thread %p started!\n", worker, g_thread_self()); #endif itsignaler_ref (worker->submit_its); while (1) { WorkerJob *job; job = itsignaler_pop_notification (worker->submit_its, TIMER); if (job) { g_rec_mutex_lock (&worker->rmutex); if (! (job->status & JOB_CANCELLED)) { /* handle job */ job->status |= JOB_BEING_PROCESSED; g_rec_mutex_unlock (&worker->rmutex); job->result = job->func (job->data, & job->error); g_rec_mutex_lock (&worker->rmutex); job->status |= JOB_PROCESSED; if (job->reply_its) itsignaler_push_notification (job->reply_its, job, NULL); } if ((job->status & JOB_CANCELLED) && worker->jobs_hash) g_hash_table_remove (worker->jobs_hash, &job->id); g_rec_mutex_unlock (&worker->rmutex); } if (worker->worker_must_quit) { #ifdef DEBUG_NOTIFICATION g_print ("[W] GdaWorker %p, worker thread %p finished!\n", worker, g_thread_self()); #endif itsignaler_unref (worker->submit_its); g_rec_mutex_clear (& worker->rmutex); g_slice_free (GdaWorker, worker); bg_update_stats (BG_DESTROYED_WORKER); bg_join_thread (); return NULL; } } return NULL; }
/** * perform necessary management to yield the current thread * if suspended == TRUE && timeout != 0 -> the thread is added * to the sleep queue and later waken up when the clock times out * returns FALSE if time-out actually happens, TRUE if waken up * by other threads, INTERRUPTED if interrupted by a signal **/ static int thread_yield_internal(int suspended, unsigned long long timeout) { // now we use a per-thread errno stored in thread_t int savederrno; int rv = OK; tdebug("current_thread=%p\n",current_thread); savederrno = errno; // decide what to do with the thread if( !suspended ) // just add it to the runlist sched_add_thread( current_thread ); else if( timeout ) // add to the sleep list sleepq_add_thread( current_thread, timeout); { #ifdef SHOW_EDGE_TIMES cpu_tick_t start, end, rstart, rend; GET_CPU_TICKS(start); GET_REAL_CPU_TICKS(rstart); #endif // figure out the current node in the graph if( !conf_no_stacktrace ) bg_backtrace_set_node(); // FIXME: fake out what cil would do... current_thread->curr_stats.node = bg_dummy_node; // we should already have been told the node by CIL or directly by the programmer assert( current_thread->curr_stats.node != NULL ); // update node counts current_thread->prev_stats.node->num_here--; current_thread->curr_stats.node->num_here++; // update the blocking graph info if( bg_save_stats ) bg_update_stats(); #ifdef SHOW_EDGE_TIMES GET_CPU_TICKS(end); GET_REAL_CPU_TICKS(rend); { thread_stats_t *curr = ¤t_thread->curr_stats; thread_stats_t *prev = ¤t_thread->prev_stats; output(" %3d -> %-3d %7lld ticks (%lld ms) %7lld rticks (%lld ms) ", prev->node->node_num, curr->node->node_num, curr->cpu_ticks - prev->cpu_ticks, (curr->cpu_ticks - prev->cpu_ticks) / ticks_per_millisecond, # ifdef USE_PERFCTR curr->real_ticks - prev->real_ticks, (curr->real_ticks - prev->real_ticks) / ticks_per_millisecond # else curr->cpu_ticks - prev->cpu_ticks, (curr->cpu_ticks - prev->cpu_ticks) / ticks_per_millisecond # endif ); output("update bg node %d: %lld (%lld ms) real: %lld (%lld ms)\n", current_thread->curr_stats.node->node_num, (end-start), (end-start)/ticks_per_millisecond, (rend-rstart), (rend-rstart)/ticks_per_millisecond); } #endif } // squirrel away the stack limit for next time current_thread->stack_bottom = stack_bottom; current_thread->stack_fingerprint = stack_fingerprint; // switch to the scheduler thread #ifdef NO_SCHEDULER_THREAD do_scheduler(NULL); #else co_call(scheduler_thread->coro, NULL); #endif // set up stack limit for new thread stack_bottom = current_thread->stack_bottom; stack_fingerprint = current_thread->stack_fingerprint; // rotate the stats if( bg_save_stats ) { current_thread->prev_stats = current_thread->curr_stats; // update thread time, to skip time asleep GET_CPU_TICKS( current_thread->prev_stats.cpu_ticks ); current_thread->prev_stats.cpu_ticks -= ticks_diff; // FIXME: subtract out time to do debug output #ifdef USE_PERFCTR GET_REAL_CPU_TICKS( current_thread->prev_stats.real_ticks ); current_thread->prev_stats.real_ticks -= ticks_rdiff; // FIXME: subtract out time to do debug output #endif } else { current_thread->prev_stats.node = current_thread->curr_stats.node; } // check whether time-out happens if (suspended && timeout && current_thread->timeout) { rv = TIMEDOUT; current_thread->timeout = 0; } // check for and process pending signals if ( likely(!current_thread->sig_waiting) ) { if (sig_process_pending()) rv = INTERRUPTED; } else { // if sig_waiting is 1, sigwait() itself will handle the remaining rv = INTERRUPTED; } errno = savederrno; return rv; }