// only resume the thread internally // don't touch the timeout flag and the sleep queue static void _thread_resume(thread_t *t) { tdebug("t=%p\n",t); if (t->state != SUSPENDED) return; num_suspended_threads--; num_runnable_threads++; sanity_check_threadcounts(); assert(t->state == SUSPENDED); t->state = RUNNABLE; assert( t->sleep == -1 ); sched_add_thread(t); }
static thread_t* new_thread(char *name, void* (*func)(void *), void *arg, thread_attr_t attr) { static unsigned max_tid = 1; thread_t *t = malloc( sizeof(thread_t) ); int stack_size_kb_log2 = get_stack_size_kb_log2(func); void *stack = stack_get_chunk( stack_size_kb_log2 ); int stack_size = 1 << (stack_size_kb_log2 + 10); if( !t || !stack ) { if (t) free(t); if (stack) stack_return_chunk(stack_size_kb_log2, stack); return NULL; } bzero(t, sizeof(thread_t)); t->coro = co_create(new_thread_wrapper, stack - stack_size, stack_size); t->stack = stack; t->stack_size_kb_log2 = stack_size_kb_log2; t->stack_bottom = stack - stack_size; t->stack_fingerprint = 0; t->name = (name ? name : "noname"); t->initial_func = func; t->initial_arg = arg; t->joinable = 1; t->tid = max_tid++; t->sleep = -1; if( attr ) { t->joinable = attr->joinable; t->daemon = attr->daemon; if(t->daemon) num_daemon_threads++; } // FIXME: somehow track the parent thread, for stats creation? // make sure the thread has a valid node before we add it to the scheduling list bg_dummy_node->num_here++; t->curr_stats.node = bg_dummy_node; pl_add_tail(threadlist, t); num_runnable_threads++; sched_add_thread(t); sanity_check_threadcounts(); return t; }
/** * \brief init scheduler * \param[in] idle the initial idle thread * \return 0 on success, or a negative error code otherwise * * This initializes the scheduler. The passed thread is the idle * thread. It is added to the thread list automatically. */ int sched_init(struct tcb* idle) { assert(idle); for (size_t i = 0; i < ARRAY_NELEMS(g_current_thread); ++i) { g_current_thread[i] = idle; } for (size_t i = 0; i < ARRAY_NELEMS(g_thread); ++i) { list_init_head(g_thread + i); } alarm_init(&g_alarm, alarm_handler); int res = timer_add_alarm(&g_alarm, sched_timeout()); if (res < 0) { goto err_timer_add_alarm; } res = sched_add_thread(idle, 0); if (res < 0) { goto err_sched_add_thread; } return 0; err_sched_add_thread: timer_remove_alarm(&g_alarm); err_timer_add_alarm: for (size_t i = ARRAY_NELEMS(g_current_thread); i;) { --i; g_current_thread[i] = NULL; } return res; }
/** * 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; }