MCU & FPGA RTOS Mutex no FreeRTOS: Tipos, Herança de Prioridade e Boas Práticas em Sistemas de Tempo Real

Mutex no FreeRTOS: Tipos, Herança de Prioridade e Boas Práticas em Sistemas de Tempo Real

Mutex recursivo: quando ele é necessário e como usá-lo corretamente

O mutex recursivo existe para resolver um problema muito específico, que surge com frequência em arquiteturas de software modulares, em camadas ou orientadas a serviços. Ele não deve ser visto como um “mutex melhor” ou um substituto direto do mutex padrão, mas sim como uma ferramenta especializada para evitar deadlocks autoimpostos quando uma mesma tarefa precisa acessar repetidamente um recurso já protegido.

O problema que o mutex recursivo resolve

Considere uma aplicação estruturada em camadas:

  • Uma função de alto nível (Log_Write())
  • Que chama uma função intermediária (Log_Format())
  • Que, por sua vez, acessa a UART protegida por um mutex

Se Log_Write() já obteve o mutex da UART e, em algum ponto da execução, chama Log_Format(), que tenta obter o mesmo mutex, o resultado com um mutex padrão será um deadlock. A tarefa tentará bloquear a si mesma indefinidamente, pois o mutex não sabe que a tarefa atual já é a proprietária.

O mutex recursivo resolve esse cenário permitindo que a mesma tarefa adquira o mutex múltiplas vezes, mantendo um contador interno de travamentos. Cada chamada de “take” incrementa esse contador, e cada chamada de “give” o decrementa. O mutex só é realmente liberado quando o contador retorna a zero.

Criação do mutex recursivo

A criação é semelhante à do mutex padrão, mas usa uma API diferente:

#include "FreeRTOS.h"
#include "semphr.h"

static SemaphoreHandle_t xLogMutex;

void InitLogSystem(void)
{
    xLogMutex = xSemaphoreCreateRecursiveMutex();

    if (xLogMutex == NULL)
    {
        /* Erro crítico: falha na alocação */
        for (;;);
    }
}

Note que um mutex recursivo não pode ser usado com as funções normais de take/give. Ele possui APIs próprias.

Uso correto do mutex recursivo

void Log_Write(const char *msg)
{
    xSemaphoreTakeRecursive(xLogMutex, portMAX_DELAY);

    Log_Format(msg);

    xSemaphoreGiveRecursive(xLogMutex);
}

void Log_Format(const char *msg)
{
    xSemaphoreTakeRecursive(xLogMutex, portMAX_DELAY);

    UART_SendString("LOG: ");
    UART_SendString(msg);
    UART_SendString("\r\n");

    xSemaphoreGiveRecursive(xLogMutex);
}

Neste exemplo, a mesma tarefa entra duas vezes na região crítica protegida pelo mutex, sem causar bloqueio. O kernel reconhece que o mutex já pertence à tarefa atual e apenas incrementa o contador interno.

Armadilhas comuns no uso de mutex recursivo

Apesar de poderoso, o mutex recursivo deve ser usado com cautela. A principal armadilha é esquecer de liberar o mutex o mesmo número de vezes que ele foi adquirido. Um número desigual de chamadas xSemaphoreTakeRecursive() e xSemaphoreGiveRecursive() resulta em um mutex permanentemente bloqueado, levando a falhas difíceis de rastrear.

Outra armadilha é usar mutex recursivo como solução genérica para problemas de projeto. Em muitos casos, a necessidade de recursividade indica acoplamento excessivo ou falta de separação clara de responsabilidades. Sempre que possível, prefira reorganizar o código para que a posse do mutex seja clara e linear, utilizando mutex padrão.

Do ponto de vista de desempenho, o mutex recursivo possui um custo ligeiramente maior, devido ao controle adicional do contador e das verificações internas. Em sistemas muito restritos ou altamente determinísticos, esse custo pode ser relevante.

Na próxima seção, vamos consolidar o conteúdo apresentando boas práticas, erros comuns e critérios objetivos para escolher entre mutex padrão, mutex recursivo e outros mecanismos do FreeRTOS.

0 0 votos
Classificação do artigo
Inscrever-se
Notificar de
guest
0 Comentários
mais antigos
mais recentes Mais votado
Feedbacks embutidos
Ver todos os comentários

Related Post

0
Adoraria saber sua opinião, comente.x