O exercício proposto foi eliminar o busy-waiting do semáforo.
Todas as vezes em que uma thread fica presa em um semáforo, o semáforo deve saber quem é a thread que está presa nele para poder liberá-la quando a outra thread que tiver obtido a tranca sair da região crítica (destrancar o semáforo). Para isso foi criada uma lista _waiting que armazena a informação de todas as threads que estão presas em cada semáforo.
As operações sleep, wakeup e wakeup all foram implementadas da seguinte maneira:
List<Thread> _waiting;
...
// Thread operations
void sleep() {
db<Semaphore>(TRC) << "Sleep...\n";
if(!busy_waiting) {
List<Thread>::Element * e = new List<Thread>::Element(Thread::running());
_waiting.insert(e);
Thread::running()->suspend();
}
}
void wakeup() {
db<Semaphore>(TRC) << "Wakeup...\n";
if(!busy_waiting) {
if (List<Thread>::Element * tmp = _waiting.remove()) {
tmp->object()->resume();
}
}
}
void wakeup_all() {
db<Semaphore>(TRC) << "Wakeup all...\n";
if(!busy_waiting) {
while (List<Thread>::Element * tmp = _waiting.remove()) {
tmp->object()->resume();
}
}
}
Havia um bug na implementação do semáforo que nos foi dada. Anteriormente, a saída do programa semaphore_test.cc era algo próximo de:
thinkingthinkingthinkingthinkingthinking eating eating eating eating eating thinkingthinkingthinkingthinkingthinking eating eating eating eating eating thinkingthinkingthinkingthinkingthinking eating eating eating eating eating thinkingthinkingthinkingthinkingthinking eating eating eating eating eating thinkingthinkingthinkingthinkingthinking eating eating eating eating eating thinkingthinkingthinkingthinkingthinking eating eating eating eating eating ...
Uma vez que cada hashi era compartilhado entre dois filósofos, e cada filósofo necessitava de dois hashis para poder comer, é impossível que os 5 filósofos estejam comendo ao mesmo tempo (como mostra a saída acima).
Percebemos que o que estava causando esse comportamento errôneo eram os métodos p() e v() do semáforo. A implementação correta é mostrada abaixo.
void p() {
db<Semaphore>(TRC) << "Semaphore::p(value=" << _value << ")\n";
if (dec(_value) < 1) // Antes era while(dec(_value) < 0)
sleep();
}
void v() {
db<Semaphore>(TRC) << "Semaphore::v(value=" << _value << ")\n";
if (inc(_value) < 0) // Antes era if(inc(_value) < 1)
wakeup();
}