Tipos de Mutex implementados no FreeRTOS
O FreeRTOS implementa dois tipos principais de mutex, ambos construídos sobre a infraestrutura interna de semáforos do kernel, mas com comportamento estendido para atender às exigências de sistemas de tempo real. Esses tipos são: o mutex padrão (não recursivo) e o mutex recursivo. Embora à primeira vista pareçam variações simples de uma mesma ideia, cada um resolve problemas específicos e possui implicações diretas no projeto da aplicação.
O mutex padrão, criado com a função xSemaphoreCreateMutex(), é o mais utilizado e deve ser considerado a opção padrão para proteger recursos compartilhados entre tarefas. Ele permite que apenas uma tarefa por vez entre na seção crítica associada ao recurso protegido. Caso outra tarefa tente obter o mutex enquanto ele já está em posse de alguém, ela será bloqueada até que o mutex seja liberado ou até que expire o tempo de espera especificado. O ponto fundamental é que esse tipo de mutex implementa herança de prioridade automaticamente, elevando temporariamente a prioridade da tarefa detentora do mutex caso uma tarefa de prioridade mais alta fique bloqueada esperando por ele.
O mutex recursivo, criado com xSemaphoreCreateRecursiveMutex(), é uma extensão do mutex padrão destinada a cenários mais complexos, onde uma mesma tarefa pode precisar entrar repetidas vezes na mesma região crítica, direta ou indiretamente. Isso ocorre com frequência em arquiteturas em camadas, onde uma função de alto nível chama outra função intermediária que, por sua vez, acessa o mesmo recurso protegido. O mutex recursivo mantém um contador interno de travamentos, permitindo que a tarefa que já possui o mutex o adquira novamente sem causar deadlock. Somente quando o número correspondente de liberações for realizado o mutex será efetivamente disponibilizado para outras tarefas.
É importante ressaltar que o FreeRTOS não oferece mutexes “leves” sem herança de prioridade. Essa decisão de projeto é deliberada: em sistemas de tempo real, a ausência de herança de prioridade pode introduzir inversão de prioridade silenciosa, difícil de detectar e potencialmente catastrófica. Se o objetivo for apenas sinalização simples ou sincronização sem posse de recurso, o kernel fornece semáforos binários e contadores, que possuem menor custo computacional.
Outro aspecto relevante é que, internamente, mutexes no FreeRTOS são implementados como semáforos com contador máximo igual a 1, mas com metadados adicionais que armazenam o handle da tarefa proprietária e o nível original de prioridade. Esses dados permitem ao escalonador realizar os ajustes necessários durante a herança de prioridade e restaurar corretamente a prioridade original quando o mutex é liberado.
Por fim, vale destacar que mutexes no FreeRTOS são objetos do kernel, alocados dinamicamente ou estaticamente, dependendo da API utilizada. Isso significa que seu uso deve ser planejado com cuidado em sistemas com restrições severas de memória, especialmente quando múltiplos mutexes são empregados para proteger diferentes recursos.
Na próxima seção, vamos explorar em profundidade o problema da inversão de prioridade, entender por que ele é tão crítico em sistemas de tempo real e como o FreeRTOS utiliza mutexes para mitigá-lo de forma automática e determinística.