Thread initialization

Arquivo

Solução

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.

Inicialização da thread

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;
}

Corpo da thread

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;
}

Escalonamento

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();
}