INF1018 - Software Básico - 2013.1

Aulas de Laboratório

Corotinas

Nesse laboratório vamos explorar o uso de corotinas.

  1. Para começar, pegue os arquivos: e coloque em um diretório novo chamado lab15.
  2. Observe a interface da biblioteca de corotinas (em corotinas.h). Veja que a função usada para criar uma corotina deve receber um inteiro como argumento e retornar um inteiro como resultado. Crie um programa C que utilize a biblioteca de corotinas (documentada em corotinas.h) para criar duas corotinas bem simples, chamadas "ping" e "pong". Cada uma delas, quando ativada, imprime "ping" ou "pong", respectivamente, e o valor recebido como argumento, e devolve o controle, retornando o valor de um contador (variável local). O programa principal deve ter um loop onde ativa alternadamente cada uma dessas corotinas (cada passada no loop ativa uma delas), passando sempre para a corotina o valor do contador do loop, e em seguida imprime o valor retornado por coro_resume. Uma saída exemplo desse programa seria:
    ping 0
    main: 0
    pong 1
    main: 0
    ping 2
    main: 1
    pong 3
    main: 1
    ...
    
    obs: Para compilar seu programa, use
     
      gcc -m32 -Wa,--execstack -o meuprog meuprog.c core.s corotinas.c
    
    .
  3. Um uso importante de corotinas é a provisão de multitasking cooperativo, onde diversas linhas de execução podem compartilhar o uso do processador, transferindo o controle explicitamente entre si. Em Lua, poderíamos escrever o código abaixo para criar uma estrutura chamada de escalonador de tarefas:
    function create_task(f)  -- cria uma tarefa
       local co = coroutine.create(f)
       table.insert(tasks, co)
    end
    function dispatcher()  -- escalonador de tarefas
      local i = 1
      while true do
        if tasks[i] == nil then
          if tasks[1] == nil then break end 
          i= 1
        end
        local status = coroutine.resume(tasks[i])
        if status == false then
          table.remove(tasks, i)
        else
          i= i + 1 
        end
      end
    end
    
    Vamos agora traduzir essa ideia para C. Várias mudanças terão que ser feitas: a primeira é que a estrutura de dados que armazena tarefas será uma lista encadeada. Pegue o arquivo com o esqueleto do programa. Esse esqueleto já contém as funções de manipulação da lista e a rotina main. A função main cria algumas corotinas para executarem a função boba, insere-as na lista encadeada, e em seguida ativa (chamar) o dispatcher.

    Escreva a função dispatcher, que executa as corotinas presentes na lista. Escreva uma função boba que receba um único parâmetro, um int, que irá representar o número da tarefa. boba deve conter um loop com chamadas à função mostra, que recebe dois inteiros, o número da tarefa e o valor de um contador (uma variável local, como em ping e pong.). (O número da tarefa está armazenado no nó da lista que descreve a tarefa e deve ser passado como parâmetro na chamada a coro_resume, juntamente com o descritor da corotina).

    Note que quem faz o yield nessas tarefas é a função mostra, que sempre devolve o valor 0, para indicar que a tarefa ainda está ativa. Sua tarefa (função boba), ao terminar, deve retornar o valor 1, para o dispatcher saber que ela acabou.

    Acabou? Tente fazer boba inserir novas tarefas!