O exercício proposto foi manter uma thread rodando quando não houver nenhuma outra thread a ser executada pela CPU. Oque essa thread deve fazer é confirmar a ociosidade da CPU (verificar se não existe nenhuma thread suspensa) e suspender as operações da mesma, habilitando apenas as interrupções.
Nossa solução cria, antes da criação da primeira thread da aplicação no método Thread::init(System_Info * si), uma thread que deverá ser executada somente quando nenhuma outra estiver disponível.
int Thread::init(System_Info * si)
{
db<Init>(TRC) << "Thread::init(entry="
<< (void *)si->lmm.app_entry << ")\n";
db<(INF) << "Creating dummy thread ... \n";
new Thread(&Thread::check_idleness, READY, LOW);
if(Traits::active_scheduler)
Alarm::master(Traits::quantum, &reschedule);
_running = new(malloc(sizeof(Thread))) Thread(reinterpret_cast<int (*)()>(si->lmm.app_entry), RUNNING);
_running->_context->load();
return 0;
}
Quando ativa, a thread idle verifica se não existe nenhuma thread na fila de prontas e nem na fila de suspensas, e então efetua um halt na CPU.
static int check_idleness() {
for (;;) {
db<Thread>(TRC) << "Checking CPU idleness...\n";
if (_ready.empty() && _suspended.empty()) {
Thread::idle();
}
Thread::yield();
}
return 0;
}
Da forma como estavam implementadas, as filas de threads (_ready e _suspended) não privilegiavam threads com maior prioridade. Nós alteramos as filas para filas ordenadas e definimos a prioridade da thread como o critério de ordenação.
typedef Ordered_Queue<Thread> Queue;
...
void body() {
if(Traits::active_scheduler)
CPU::int_disable();
// Setting the priority as the rank of the list element
_link.rank(_priority);
switch(_state) {
case RUNNING: break;
case SUSPENDED: _suspended.insert(&_link); break;
default: _ready.insert(&_link);
}
if(Traits::active_scheduler)
CPU::int_enable();
}
Tivemos que alterar o método yield também, pois estava dando erro ao "trocar o contexto" de uma thread para ela mesma.
void Thread::yield() {
db<Thread>(TRC) << "Thread::yield()\n";
if(Traits::active_scheduler)
CPU::int_disable();
if(!_ready.empty()) {
Thread * old = _running;
old->_state = READY;
_ready.insert(&old->_link);
_running = _ready.remove()->object();
_running->_state = RUNNING;
// old->_context->save(); // can be used to force an update
db<Thread>(INF) << "old={" << old << ","
<< *old->_context << "}\n";
db<Thread>(INF) << "new={" << _running << ","
<< *_running->_context << "}\n";
if (old != _running)
CPU::switch_context(&old->_context, _running->_context);
}
if(Traits::active_scheduler)
CPU::int_enable();
}