Início do programa:
O programa inicia com um buffer de 8 espaços na memória, já disparando as duas threads, uma para o produtor e outra para o consumidor.
O produtor:
O produtor é iniciado com uma lista de 23 (0-22) elementos (produto), letras de a-z para adicionar no buffer. Sua primeira ação é testar o acesso às variáveis que controlam a quantidade de espaço livre e de espaço ocupado, para ver se estão liberadas para uso, só aí então verificar se deve prosseguir produzindo ou se deve entrar em modo de espera:
/* Semáforo para definir status do produtor - Dormindo ou acordado */
pthread_mutex_lock(&mutex_status_buff);
if (buff_empty == 0 || buff_full == 8){ /* Se buffer vazio for 0, dorme */
printf("\n[Produtor diz:] Vou dormir enquanto não tem trabalho!!\n");
status_produz = DORMINDO;
/* Enquanto não estiver onde escrever, processo dorme.. Só libera quando receber um sinal de que pode começar de novo*/
pthread_cond_wait(&status_produtor, &mutex_status_buff); /* Em modo de espera, aguardando sinal */
status_produz = ACORDADO; /* Depois de receber sinal, volta a produzir*/
printf("\n[Produtor diz:] Tocou a campainha, vou trabalhar!!\n");
}
Se buffer estiver cheio, entra em modo de espera, até que o consumidor mande um sinal dizendo que há espaço livre no buffer. Repare que, quando o programa chega em pthread_cond_wait(&status_produtor, &mutex_status_buff), ele entra em modo de espera, pois essa é a função que coloca o processo neste estado, e só vai prosseguir quando receber um sinal, e quando isso acontecer já muda seu status para ACORDADO e libera as variáveis.
Se buffer estiver com posições livres, já libera as variáveis e segue para o próximo passo, que é preencher o buffer propriamente dito:
/* Semáforo para exclusão mutua que controla posições do buffer */
pthread_mutex_lock(&mutex);
buff_empty--; /* Decrementa espaço livre */
buff_full++; /* Incrementa espaço ocupado */
/*Recebe posição da fila, se retorno for -1 quer dizer que a fila não inicializou.. então ele deve pegar do controle do buffer*/
if ((posicao_buffer = getFila(&fila_escrita_inicio, &fila_escrita_fim)) == -1) /*pega posição livre do buffer*/
posicao_buffer = buff_full;/* Se for inicio do programa a fila estará vazia, mas o buffer cheio. A fila pode ser inicializada no inicio do programa também */
/*Manda posição escrita para fila*/
if (setFila(posicao_buffer, &fila_leitura_inicio, &fila_leitura_fim) == -1) /*Adiciona na fila*/
sprintf(stderr,"\n\nErro ao inserir na fila: %d!!\n\n", errno);
/*Preenche buffer*/
bufferLimitado[posicao_buffer] = abc[i];
printf("\n\n[Produtor diz:] Pid [%d] | Tid [%u] | Posição [%d] | %s [%c] \n",getpid(),(unsigned int)pthread_self(), posicao_buffer, (char*) texto, bufferLimitado[posicao_buffer]);
Nessa parte o sistema bloqueia as variáveis de controle de buffer, Empty e Full, busca a próxima posição livre da fila de escrita. Repare que se a fila estiver vazia, e há espaço livre no buffer, quer dizer que é o início do programa, a fila poderia ser iniciada antes das threads serem disparadas, mas para ficar de um melhor entendimento, a meu ver, optei por essa forma. Em seguida adiciona nova posição escrita (Ocupada) para a fila de leitura, e por fim, escreve o "produzido" na posição ocupada, mandando uma mensagem logo depois para avisar que há produção na fila.
pthread_mutex_lock(&mutex_status_buff);
if (status_consome == DORMINDO){ /* Se consumidor estiver dormindo, o acorda.. pois há produção no buffer*/
printf("\n[Produtor diz:] O consumidor está dormindo, vou avisar que tem produto no buffer!!\n");
sleep(rand() % 2);
pthread_cond_signal(&status_consumidor); /* Envia um sinal para consumidor, e o acorda */
}
Antes de finalizar o processo o produtor verifica se o consumidor não está em período de espera, se estiver, é por que não havia nada no buffer quando ele executou, e agora o produtor deve enviar um sinal dizendo que há produção no buffer, para que o consumidor possa consumir. Se o consumidor não estiver em espera ele passa direto, liberando as variáveis bloqueadas. Pronto! acabou as verificações do produtor na primeira execução, agora ele reinicia o processo, até encerrar a produção. Depois de encerrar a produção:
status_processamento = ACABADO;
printf("\n[Produtor diz:] Meu trabalho acaba por aqui, vou produzir o ultimo e ir embora. Até..!!\n");
A última ação do produtor, antes de "ir embora", é avisar que acabou, setando o status de processamento para ACABADO. Assim quando consumidor chegar ao último item produzido poderá encerrar seu trabalho.