sem_getvalue() & SEM_VALUE_MAX Reading Semaphore State

 

sem_getvalue() & SEM_VALUE_MAX
Chapter 53 – Part 4 of 6 | Reading Semaphore State

Reading a Semaphore’s Value

Sometimes you need to inspect the current value of a semaphore — for debugging, logging, or to make a conditional decision without modifying the semaphore. POSIX provides sem_getvalue() for this purpose.

Note: the value returned is a snapshot. By the time your code reads it and acts on it, another thread may have changed the semaphore. So never use sem_getvalue() as part of a synchronization decision — use it for informational purposes only.

sem_getvalue() – Function Signature

#include <semaphore.h>

int sem_getvalue(sem_t *sem, int *sval);
/* Returns: 0 on success, -1 on error */

After the call, the integer pointed to by sval holds the current value of the semaphore. If the value is 0 and one or more threads are blocked waiting on the semaphore, the behavior of sval depends on the implementation:

Implementation sval when value=0 and threads waiting Notes
Linux (glibc) Returns 0 Does NOT return negative values
Some BSD/older UNIX May return negative (number of waiters) Non-standard behavior
SUSv3 standard Unspecified (either 0 or negative) Do not rely on negative values for portability

Example: Using sem_getvalue()

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

void print_sem_value(sem_t *sem, const char *label)
{
    int val;
    if (sem_getvalue(sem, &val) == -1) {
        perror("sem_getvalue");
        return;
    }
    printf("[%s] semaphore value = %d\n", label, val);
}

int main(void)
{
    sem_t sem;

    sem_init(&sem, 0, 3);          /* initial value = 3 */
    print_sem_value(&sem, "init");

    sem_wait(&sem);
    print_sem_value(&sem, "after 1st wait");

    sem_wait(&sem);
    print_sem_value(&sem, "after 2nd wait");

    sem_post(&sem);
    print_sem_value(&sem, "after post");

    sem_destroy(&sem);
    return 0;
}
/* Output:
   [init] semaphore value = 3
   [after 1st wait] semaphore value = 2
   [after 2nd wait] semaphore value = 1
   [after post] semaphore value = 2
*/

Why sem_getvalue() Is Not for Synchronization

Race Condition Example (WRONG pattern)

/* WRONG – do NOT do this */
int val;
sem_getvalue(&sem, &val);
if (val > 0) {
    sem_wait(&sem);    /* Another thread could have decremented between
                          getvalue and wait! This is a race condition. */
}
The check-then-act gap is not atomic. Always use sem_trywait() if you want conditional non-blocking acquisition.
Correct pattern for conditional acquire

/* CORRECT – sem_trywait is atomic */
if (sem_trywait(&sem) == 0) {
    /* Successfully acquired */
} else {
    /* Not available (errno == EAGAIN) */
}

SEM_VALUE_MAX – Maximum Semaphore Value

#include <limits.h>    /* or <semaphore.h> */
/* SEM_VALUE_MAX is defined in <limits.h> */

SEM_VALUE_MAX is the maximum value a POSIX semaphore can hold. The POSIX/SUSv3 standard requires it to be at least 32,767. On Linux:

SEM_VALUE_MAX on Linux
SUSv3 Minimum Required
32,767
(2^15 – 1)
Linux/x86-32 Actual Value
2,147,483,647
INT_MAX (2^31 – 1)

On Linux, SEM_VALUE_MAX equals INT_MAX because the semaphore value is stored as a 32-bit signed integer. You can verify this at runtime:

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

int main(void)
{
    printf("SEM_VALUE_MAX = %d\n", SEM_VALUE_MAX);
    /* On Linux: SEM_VALUE_MAX = 2147483647 */

    /* Trying to init above SEM_VALUE_MAX fails with EINVAL */
    sem_t s;
    if (sem_init(&s, 0, SEM_VALUE_MAX) == 0) {
        printf("Max value semaphore created OK.\n");
        sem_destroy(&s);
    }
    return 0;
}

What Happens if sem_post() Exceeds SEM_VALUE_MAX?

If calling sem_post() would increment the value beyond SEM_VALUE_MAX, the call fails and returns -1 with errno = EOVERFLOW. The semaphore value is not changed.

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

int main(void)
{
    sem_t s;
    sem_init(&s, 0, SEM_VALUE_MAX);

    /* This post would exceed SEM_VALUE_MAX */
    if (sem_post(&s) == -1) {
        if (errno == EOVERFLOW)
            printf("sem_post failed: EOVERFLOW (value at maximum)\n");
        else
            perror("sem_post");
    }

    sem_destroy(&s);
    return 0;
}

Practical Use: Diagnostics and Debugging

The primary valid use for sem_getvalue() is logging and diagnostics — printing the semaphore state for debugging without modifying it.

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

/* Debug helper: print all semaphore states */
void dump_semaphore_state(sem_t *sems[], const char *names[], int count)
{
    printf("=== Semaphore State Dump ===\n");
    for (int i = 0; i < count; i++) {
        int val;
        sem_getvalue(sems[i], &val);
        printf("  %-20s : %d%s\n", names[i], val,
               val == 0 ? " [LOCKED/EMPTY]" : "");
    }
    printf("===========================\n");
}

int main(void)
{
    sem_t mutex, empty, full;
    sem_init(&mutex, 0, 1);
    sem_init(&empty, 0, 5);
    sem_init(&full,  0, 0);

    sem_t *sems[]     = { &mutex, &empty, &full };
    const char *names[] = { "mutex", "empty_slots", "full_slots" };

    dump_semaphore_state(sems, names, 3);

    /* Simulate acquiring mutex and adding item */
    sem_wait(&mutex);
    sem_wait(&empty);
    sem_post(&full);
    sem_post(&mutex);

    dump_semaphore_state(sems, names, 3);

    sem_destroy(&mutex);
    sem_destroy(&empty);
    sem_destroy(&full);
    return 0;
}

Interview Questions – sem_getvalue & SEM_VALUE_MAX

Q1. What does sem_getvalue() return when the semaphore value is 0 and threads are waiting?
On Linux it returns 0. On some BSD systems it may return a negative number representing the number of blocked threads. SUSv3 leaves this unspecified, so portable code should not rely on negative values.
Q2. Can you use sem_getvalue() to safely decide whether to call sem_wait()?
No. There is a race condition between reading the value and acting on it — another thread could change the semaphore between those two steps. Always use sem_trywait() for conditional non-blocking acquisition, which is atomic.
Q3. What is SEM_VALUE_MAX and what is its value on Linux?
SEM_VALUE_MAX is the maximum value a semaphore can hold. SUSv3 requires it to be at least 32,767. On Linux it equals INT_MAX (2,147,483,647) because the semaphore is stored as a 32-bit signed integer.
Q4. What happens when sem_post() is called on a semaphore already at SEM_VALUE_MAX?
sem_post() returns -1 with errno set to EOVERFLOW. The semaphore value is unchanged. This error is rare in practice because SEM_VALUE_MAX is very large, but it can occur in buggy code where post is called more times than wait.
Q5. Is sem_getvalue() thread-safe? Does it take a lock internally?
sem_getvalue() is thread-safe in the sense that it reads the value atomically. However, the value it returns is only a snapshot — it may be stale by the time the caller reads it. It does not hold any lock during or after the call.

Leave a Reply

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