/** * Run a scheduler thread until termination. */ static void run(scheduler_t* sched) { pony_actor_t* actor = pop_global(sched); while(true) { if(actor == NULL) { // We had an empty queue and no rescheduled actor. actor = steal(sched, NULL); if(actor == NULL) { // Termination. assert(pop(sched) == NULL); return; } } // Run the current actor and get the next actor. bool reschedule = ponyint_actor_run(&sched->ctx, actor, SCHED_BATCH); pony_actor_t* next = pop_global(sched); if(reschedule) { if(next != NULL) { // If we have a next actor, we go on the back of the queue. Otherwise, // we continue to run this actor. push(sched, actor); actor = next; } else if(ponyint_is_cycle(actor)) { // If all we have is the cycle detector, try to steal something else to // run as well. next = steal(sched, actor); if(next == NULL) { // Termination. return; } // Push the cycle detector and run the actor we stole. if(actor != next) { push(sched, actor); actor = next; } } } else { // We aren't rescheduling, so run the next actor. This may be NULL if our // queue was empty. actor = next; } } }
/** * Use mpmcqs to allow stealing directly from a victim, without waiting for a * response. */ static pony_actor_t* steal(scheduler_t* sched, pony_actor_t* prev) { send_msg(0, SCHED_BLOCK, 0); uint64_t tsc = ponyint_cpu_tick(); pony_actor_t* actor; while(true) { scheduler_t* victim = choose_victim(sched); if(victim == NULL) actor = (pony_actor_t*)ponyint_mpmcq_pop(&inject); else actor = pop_global(victim); if(actor != NULL) { DTRACE3(WORK_STEAL_SUCCESSFUL, (uintptr_t)sched, (uintptr_t)victim, (uintptr_t)actor); break; } uint64_t tsc2 = ponyint_cpu_tick(); if(quiescent(sched, tsc, tsc2)) { DTRACE2(WORK_STEAL_FAILURE, (uintptr_t)sched, (uintptr_t)victim); return NULL; } // If we have been passed an actor (implicitly, the cycle detector), and // enough time has elapsed without stealing or quiescing, return the actor // we were passed (allowing the cycle detector to run). if((prev != NULL) && ((tsc2 - tsc) > 10000000000)) { actor = prev; break; } } send_msg(0, SCHED_UNBLOCK, 0); return actor; }
/** * Use mpmcqs to allow stealing directly from a victim, without waiting for a * response. */ static pony_actor_t* steal(scheduler_t* sched, pony_actor_t* prev) { send_msg(0, SCHED_BLOCK, 0); uint64_t tsc = ponyint_cpu_tick(); pony_actor_t* actor; while(true) { scheduler_t* victim = choose_victim(sched); if(victim == NULL) victim = sched; actor = pop_global(victim); if(actor != NULL) break; uint64_t tsc2 = ponyint_cpu_tick(); if(quiescent(sched, tsc, tsc2)) return NULL; // If we have been passed an actor (implicitly, the cycle detector), and // enough time has elapsed without stealing or quiescing, return the actor // we were passed (allowing the cycle detector to run). if((prev != NULL) && ((tsc2 - tsc) > 10000000000)) { actor = prev; break; } } send_msg(0, SCHED_UNBLOCK, 0); return actor; }