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 ".

questionAnswers(1)

yourAnswerToTheQuestion