Neste trabalho iremos estender a biblioteca de threads em espaço do usuário implementada no TP1 para dar suporte a escalonamento com diferentes classes de prioridade bem como ajuste de prioridade automática.
Como nossa biblioteca de threads usa mapeamento muitos-para-um, uma thread não pode chamar sleep sem bloquear todas as threads do programa. Para solucionar esta limitação vamos criar uma função dccthread_sleep que retira a thread da CPU (como dccthread_yield) e da fila de prontos por uma quantidade de tempo pré-determinada.
Sua biblioteca the threads deverá implementar uma função:
void dccthread_sleep(struct timespec ts),
onde ts é uma variável do tipo struct timespec que contém o tempo pelo qual a thread deve ser mantida fora da fila de prontos.
Para implementar esta funcionalidade sua biblioteca deverá fazer uso de temporizadores adicionais (no TP1 já fizemos uso de temporizadores criados com a função setitimer). Para implementação de dccthread_sleep você pode usar o temporizador ITIMER_REAL da função setitimer ou o temporizador CLOCK_REALTIME da família de funções timer_create, timer_delete e timer_settime.
Dica. Sua biblioteca irá precisar de pelo menos dois temporizadores: um para controlar o quantum das threads e outro para controlar quais threads chamaram dccthread_sleep e estão esperando para voltar à fila de prontos.
Modifique sua biblioteca para que cada thread tenha uma classe de prioridade. Definimos classes de prioridades como números inteiros onde quanto maior o número maior a prioridade da thread. Sua função dccthread_create deve ser modificada para ter o seguinte cabeçalho:
dccthread_t * dccthread_create(const char *name, void(*func)(),
int param, int prio);
onde o parâmetro prio é a prioridade da nova thread. Implemente também a seguinte função:
void dccthread_setprio(int prio),
onde prio é a nova prioridade da thread, e deve ter efeito imediato.
Em nosso sistema, threads são escolhidas para executar ordenadas por prioridade. Se houver uma thread na fila de prontos com prioridade x, nenhuma thread com prioridade menor que x pode executar. No caso de existirem múltiplas threads com prioridade x, o sistema deve alternar entre elas. Note que após uma chamada a dccthread_setprio, se a nova prioridade for menor que a de outra thread na fila de prontos, a thread com prioridade maior deve começar a executar.
A parte 2 não especificou como deve ser o escalonamento de threads com a mesma prioridade. Nesta você deve implementar um algoritmo de escalonamento que faça divisão justa entre threads da mesma prioridade. Você deve:
Você deverá também avaliar seu algoritmo. Para isso, implemente casos de testes com várias threads com a mesma prioridade, onde cada thread tem padrão de comportamento diferente (considere pelo menos as três funções de teste abaixo). Sua avaliação deve analisar pelo menos o tempo de execução (uso da CPU) e tempo de espera na fila de prontos. Faça vários testes, apresente as medições usando gráficos e discuta os resultados.
void test_cpu_bound(int dummy) { while(1); }
void test_cpu_interactive(int dummy) { while(1) dccthread_yield(); }
void test_cpu_io(int dummy) {
struct timespec ts = {1, 0};
while(1) dccthread_sleep(ts);
}
Não use a biblioteca pthreads em sua implemencação.
A avaliação de desempenho das threads resultante do uso do seu escalonador é parte central deste trabalho. Você deverá fazer testes usando o método científico (várias execuções, controle de variabilidade dos resultados, garantia de reprodutibilidade), a avaliação deve ser quantitativa (use gráficos!) e a discussão deve explicar os resultados coletados.