sem_getvalue() Reading a Semaphore’s Value

 

sem_getvalue()
Reading a Semaphore’s Value โ€” Chapter 53, File 4 of 5

What does sem_getvalue() do?

sem_getvalue() retrieves the current value of a semaphore and stores it in an integer variable. This is primarily useful for debugging and monitoring โ€” for example, checking how many resources are currently available, or how many threads are waiting.

Important: The value you read is a snapshot. By the time you examine it, another thread might have already changed the semaphore. So you should never use sem_getvalue() for control-flow decisions (like “if value > 0, then sem_wait()” โ€” that is a race condition).

Function Signature

#include <semaphore.h>

int sem_getvalue(sem_t *sem, int *sval);

/* Returns: 0 on success, -1 on error */
/* On success: *sval is set to the current value of the semaphore */
Parameter Type Description
sem sem_t * Pointer to the semaphore (from sem_open or &variable)
sval int * Output parameter: receives the current semaphore value

Basic Example: Reading Semaphore Value

#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    sem_t *sem;
    int val;

    /* Create semaphore with initial value 3 */
    sem = sem_open("/pool", O_CREAT | O_EXCL, 0660, 3);
    if (sem == SEM_FAILED) { perror("sem_open"); exit(1); }

    /* Read initial value */
    if (sem_getvalue(sem, &val) == -1) {
        perror("sem_getvalue");
        exit(1);
    }
    printf("Initial value: %d\n", val);   /* prints 3 */

    /* Decrement once */
    sem_wait(sem);
    sem_getvalue(sem, &val);
    printf("After sem_wait: %d\n", val);  /* prints 2 */

    /* Increment once */
    sem_post(sem);
    sem_getvalue(sem, &val);
    printf("After sem_post: %d\n", val);  /* prints 3 */

    /* Decrement to 0 */
    sem_wait(sem); sem_wait(sem); sem_wait(sem);
    sem_getvalue(sem, &val);
    printf("After 3 waits: %d\n", val);   /* prints 0 */

    sem_close(sem);
    sem_unlink("/pool");
    return 0;
}

POSIX Specification: Negative Values

โš  The Negative Value Controversy

The SUSv3/POSIX standard says: if a semaphore’s value is 0 and some processes are blocked waiting, sem_getvalue() may return either 0 or a negative number whose absolute value represents the number of waiting processes.

However, Linux always returns 0 (never negative) when the semaphore is 0 and threads are waiting. Do not write portable code that depends on negative values from sem_getvalue().

Platform sem_getvalue() when value=0 and waiters exist
Linux Returns 0 (never negative)
Some UNIX systems May return negative number = -N where N = number of waiters
POSIX spec Allows either 0 or negative โ€” implementation defined

The Snapshot Problem

The value returned by sem_getvalue() is a snapshot at a point in time. It may already be stale by the time your code reads it. Here is a dangerous anti-pattern:

/* DANGEROUS: race condition - DO NOT do this */
int val;
sem_getvalue(sem, &val);
if (val > 0) {
    /* WRONG: another thread might have decremented it
       between sem_getvalue() and sem_wait() */
    sem_wait(sem);
}

/* CORRECT: just call sem_wait() directly.
   It is atomic - no race condition */
sem_wait(sem);   /* blocks if 0, decrements if > 0 */

Valid Use Cases for sem_getvalue()

โœ… Good Uses
  • Debugging / logging semaphore state
  • Monitoring dashboards
  • Health checks / diagnostics
  • Unit testing assertions
  • Detecting when a semaphore is stuck at 0
โŒ Bad Uses (Races!)
  • “if (val > 0) sem_wait()” patterns
  • “if (val == 0) skip” patterns
  • Using as condition for branching
  • Checking before post/wait decisions

Practical Example: Resource Pool Monitor

Here is a realistic use of sem_getvalue() for monitoring how many connections from a pool are in use:

#include <semaphore.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define MAX_CONNECTIONS  5
#define SEM_NAME "/db_pool"

static sem_t *pool_sem;

void print_pool_status(void)
{
    int available;
    if (sem_getvalue(pool_sem, &available) == -1) {
        perror("sem_getvalue");
        return;
    }
    printf("[Monitor] Connections available: %d / %d (in-use: %d)\n",
           available, MAX_CONNECTIONS, MAX_CONNECTIONS - available);
}

void *use_connection(void *arg)
{
    int thread_id = *(int *)arg;

    printf("Thread %d: waiting for connection...\n", thread_id);
    sem_wait(pool_sem);   /* acquire a connection */
    printf("Thread %d: got connection\n", thread_id);
    print_pool_status();

    sleep(2);   /* simulate database operation */

    sem_post(pool_sem);   /* release connection back to pool */
    printf("Thread %d: released connection\n", thread_id);
    print_pool_status();

    return NULL;
}

int main(void)
{
    pthread_t threads[8];
    int ids[8];

    sem_unlink(SEM_NAME);   /* clean up any leftover */
    pool_sem = sem_open(SEM_NAME, O_CREAT | O_EXCL, 0660, MAX_CONNECTIONS);
    if (pool_sem == SEM_FAILED) { perror("sem_open"); exit(1); }

    printf("Pool initialized with %d connections\n", MAX_CONNECTIONS);
    print_pool_status();

    /* Launch 8 threads competing for 5 connections */
    for (int i = 0; i < 8; i++) {
        ids[i] = i + 1;
        pthread_create(&threads[i], NULL, use_connection, &ids[i]);
    }

    for (int i = 0; i < 8; i++)
        pthread_join(threads[i], NULL);

    printf("\nAll done.\n");
    print_pool_status();

    sem_close(pool_sem);
    sem_unlink(SEM_NAME);
    return 0;
}
/* Compile */
gcc pool_monitor.c -o pool_monitor -pthread
./pool_monitor

Debug Helper: Dump Semaphore Info

#include <semaphore.h>
#include <stdio.h>

/*
 * Utility function: print semaphore name and value.
 * Safe to call at any time - only for debugging.
 */
void debug_sem(const char *label, sem_t *sem)
{
    int val;
    if (sem_getvalue(sem, &val) == -1) {
        fprintf(stderr, "[%s] sem_getvalue failed\n", label);
        return;
    }
    printf("[DEBUG] %s: value = %d%s\n",
           label, val,
           val == 0 ? " (LOCKED/EMPTY)" : "");
}

/* Usage */
// debug_sem("before wait", my_sem);
// sem_wait(my_sem);
// debug_sem("after wait", my_sem);

๐Ÿ…พ Interview Questions & Answers

Q1. What does sem_getvalue() return on Linux when the semaphore value is 0 and threads are blocked?

On Linux, it always stores 0 in *sval, even if there are threads blocked in sem_wait(). Some other UNIX systems may store a negative number (-N where N is the waiter count), but this is not guaranteed. Write portable code that treats any value โ‰ค 0 as “unavailable”.

Q2. Is it safe to use sem_getvalue() to decide whether to call sem_wait()?

No. The value is a snapshot and may change immediately after you read it. The correct approach is to call sem_wait() directly (it’s atomic). If you don’t want to block, use sem_trywait() instead. Never do if (sem_getvalue() > 0) sem_wait().

Q3. What are valid use cases for sem_getvalue()?

Debugging and monitoring only: logging available resources, health checks, unit test assertions to verify semaphore state after operations. Never use it for synchronization decisions, as the value can change between the read and any subsequent action.

Q4. Why does SUSv3 allow a negative value from sem_getvalue()?

It’s for informational purposes: a negative value of -N would indicate N processes are currently blocked waiting. This gives a way to know not just that the semaphore is locked (value=0), but also the backlog of waiters. However, since Linux doesn’t implement this, code depending on it is not portable.

Q5. Can sem_getvalue() be called from a signal handler?

No. sem_getvalue() is NOT listed as async-signal-safe in POSIX. Only sem_post() is async-signal-safe. Do not call sem_getvalue(), sem_wait(), or sem_open() from signal handlers.

Leave a Reply

Your email address will not be published. Required fields are marked *