Implementação de semáforo de baixo nível C
Eu estava pensando em como implementar semáforos (não binários) usando menos código ASM possível.
Não consegui pensar e escrever sem usar um mutex, então aqui está o melhor que pude fazer até agora:
Global:
#include <stdlib.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdbool.h>
typedef struct
{
atomic_ullong value;
pthread_mutex_t *lock_op;
bool ready;
} semaphore_t;
typedef struct
{
atomic_ullong value;
pthread_mutex_t lock_op;
bool ready;
} static_semaphore_t;
/* use with static_semaphore_t */
#define SEMAPHORE_INITIALIZER(value) = {value, PTHREAD_MUTEX_INITIALIZER, true}
Funções:
bool semaphore_init(semaphore_t *semaphore, unsigned long long init_value)
{
if(semaphore->ready) if(!(semaphore->lock_op = \
calloc(1, sizeof(pthread_mutex_t)))) return false;
else pthread_mutex_destroy(semaphore->lock_op);
if(pthread_mutex_init(semaphore->lock_op, NULL))
return false;
semaphore->value = init_value;
semaphore->ready = true;
return true;
}
bool semaphore_wait(semaphore_t *semaphore)
{
if(!semaphore->ready) return false;
pthread_mutex_lock(&(semaphore->lock_op));
while(!semaphore->value) __asm__ __volatile__("nop");
(semaphore->value)--;
pthread_mutex_unlock(&(semaphore->lock_op));
return true;
}
bool semaphore_post(semaphore_t *semaphore)
{
if(!semaphore->ready) return false;
atomic_fetch_add(&(semaphore->value), (unsigned long long) 1);
return true;
}
É possível implementar um semáforo usando apenas algumas linhas, com os componentes atômicos ou diretamente na montagem (ex.lock cmpxchg
)?
Observando a estrutura sem_t de<bits/sempahore.h>
incluído por<semaphore.h>
parece-me que foi escolhido um caminho muito diferente ...
typedef union
{
char __size[__SIZEOF_SEM_T];
long int __align;
} sem_t;
ATUALIZAR:
O @PeterCordes propôs uma solução definitivamente muito melhor, usando os átomos, sem um mutex, fazendo as verificações diretamente no valor do semáforo.
Ainda quero entender melhor as chances de melhorar o código em termos de desempenho, aproveitando as funções internas de pausa ou chamadas de kernel que evitam o desperdício de CPU, aguardando a disponibilidade dos recursos críticos.
Também seria bom ter uma implementação padrão de mutexes e semáforos não binários para comparação.
Defutex (7) Eu li:"O kernel do Linux fornece futexes (" Mutexes rápidos no espaço do usuário ") como um bloco de construção para bloqueio e semáforos rápidos no espaço do usuário. Os futexes são muito básicos e se prestam bem na criação de abstrações de bloqueio de nível superior, como mutexes, variáveis de condição, bloqueios de leitura e gravação, barreiras e semáforos ".