/* Thread0 scheduler ops (for processes that haven't linked in a full 2LS) */ static void thread0_sched_entry(void) { if (current_uthread) run_current_uthread(); else run_uthread(thread0_uth); }
static void __pth_switch_cb(struct uthread *uthread, void *target) { /* by not returning, this bypasses vcore entry and event checks, though when * we pop back out of the 2LS, we'll check notif pending. think about this * if you put this into your 2LS. */ current_uthread = NULL; run_uthread((struct uthread*)target); assert(0); }
/* Called from vcore entry. Options usually include restarting whoever was * running there before or running a new thread. Events are handled out of * event.c (table of function pointers, stuff like that). */ static void __attribute__((noreturn)) pth_sched_entry(void) { uint32_t vcoreid = vcore_id(); if (current_uthread) { /* Prep the pthread to run any pending posix signal handlers registered * via pthread_kill once it is restored. */ __pthread_prep_for_pending_posix_signals((pthread_t)current_uthread); /* Run the thread itself */ run_current_uthread(); assert(0); } /* no one currently running, so lets get someone from the ready queue */ struct pthread_tcb *new_thread = NULL; /* Try to get a thread. If we get one, we'll break out and run it. If not, * we'll try to yield. vcore_yield() might return, if we lost a race and * had a new event come in, one that may make us able to get a new_thread */ do { handle_events(vcoreid); __check_preempt_pending(vcoreid); mcs_pdr_lock(&queue_lock); new_thread = TAILQ_FIRST(&ready_queue); if (new_thread) { TAILQ_REMOVE(&ready_queue, new_thread, tq_next); TAILQ_INSERT_TAIL(&active_queue, new_thread, tq_next); threads_active++; threads_ready--; mcs_pdr_unlock(&queue_lock); /* If you see what looks like the same uthread running in multiple * places, your list might be jacked up. Turn this on. */ printd("[P] got uthread %08p on vc %d state %08p flags %08p\n", new_thread, vcoreid, ((struct uthread*)new_thread)->state, ((struct uthread*)new_thread)->flags); break; } mcs_pdr_unlock(&queue_lock); /* no new thread, try to yield */ printd("[P] No threads, vcore %d is yielding\n", vcore_id()); /* TODO: you can imagine having something smarter here, like spin for a * bit before yielding (or not at all if you want to be greedy). */ if (can_adjust_vcores) vcore_yield(FALSE); if (!parlib_wants_to_be_mcp) sys_yield(FALSE); } while (1); assert(new_thread->state == PTH_RUNNABLE); /* Prep the pthread to run any pending posix signal handlers registered * via pthread_kill once it is restored. */ __pthread_prep_for_pending_posix_signals(new_thread); /* Run the thread itself */ run_uthread((struct uthread*)new_thread); assert(0); }
/* Thread0 scheduler ops (for processes that haven't linked in a full 2LS) */ static void thread0_sched_entry(void) { /* TODO: support signal handling whenever we run a uthread */ if (current_uthread) { uthread_prep_pending_signals(current_uthread); run_current_uthread(); assert(0); } while (1) { if (!thread0_info.is_blocked) { uthread_prep_pending_signals(thread0_uth); run_uthread(thread0_uth); assert(0); } sys_yield(FALSE); handle_events(0); } }
/* Called from vcore entry. Options usually include restarting whoever was * running there before or running a new thread. Events are handled out of * event.c (table of function pointers, stuff like that). */ void __attribute__((noreturn)) pth_sched_entry(void) { uint32_t vcoreid = vcore_id(); if (current_uthread) { run_current_uthread(); assert(0); } /* no one currently running, so lets get someone from the ready queue */ struct pthread_tcb *new_thread = NULL; struct mcs_lock_qnode local_qn = {0}; /* For now, let's spin and handle events til we get a thread to run. This * will help catch races, instead of only having one core ever run a thread * (if there is just one, etc). Also, we don't need the EVENT_IPIs for this * to work (since we poll handle_events() */ while (!new_thread) { handle_events(vcoreid); mcs_lock_notifsafe(&queue_lock, &local_qn); new_thread = TAILQ_FIRST(&ready_queue); if (new_thread) { TAILQ_REMOVE(&ready_queue, new_thread, next); TAILQ_INSERT_TAIL(&active_queue, new_thread, next); threads_active++; threads_ready--; } mcs_unlock_notifsafe(&queue_lock, &local_qn); } /* Instead of yielding, you could spin, turn off the core, set an alarm, * whatever. You want some logic to decide this. Uthread code wil have * helpers for this (like how we provide run_uthread()) */ if (!new_thread) { /* Note, we currently don't get here (due to the while loop) */ printd("[P] No threads, vcore %d is yielding\n", vcore_id()); /* Not actually yielding - just spin for now, so we can get syscall * unblocking events */ vcore_idle(); //sys_yield(0); assert(0); } assert(((struct uthread*)new_thread)->state != UT_RUNNING); run_uthread((struct uthread*)new_thread); assert(0); }