linux-sysprog · intermediate · ~12 min

Build a tiny thread-safe counter type

Wrap a mutex + state in a tiny opaque struct.

Challenge

Package a mutex-protected long into a small opaque counter type with create / increment / read / free operations — a reusable thread-safe building block.

Task

Implement these four functions for a thread-safe counter:

typedef struct ts_counter ts_counter_t;
ts_counter_t *tsc_new(void);
void tsc_inc(ts_counter_t *c);
long tsc_value(ts_counter_t *c);
void tsc_free(ts_counter_t *c);

Input

  • tsc_new: no parameters.
  • tsc_inc, tsc_value, tsc_free: a counter handle c.

Output

  • tsc_new allocates a counter starting at 0 (with its own pthread_mutex_t) and returns it, or NULL on allocation failure.
  • tsc_inc increments the counter under the lock.
  • tsc_value returns the current value, read under the lock.
  • tsc_free destroys the mutex and frees the counter (safe on NULL).

Example

c = tsc_new()              ->   non-NULL
tsc_value(c)               ->   0
tsc_inc(c); tsc_inc(c); tsc_inc(c)
tsc_value(c)               ->   3
4 threads each call tsc_inc 1000 times
tsc_value(c)               ->   3 + 4000 = 4003   (no lost updates)
tsc_free(c)

Edge cases

  • A fresh counter reads 0.
  • Concurrent increments must not lose updates.
  • tsc_free(NULL) is a safe no-op.

Rules

  • Protect both the increment and the read with the same per-counter mutex.

Why this matters

Encapsulate a primitive into a reusable thread-safe API — the building block of every server's stats counter.

Input format

tsc_new takes nothing; tsc_inc/tsc_value/tsc_free take a ts_counter_t* handle.

Output format

tsc_new returns a new zeroed counter (or NULL); tsc_inc increments; tsc_value returns the current value under the lock; tsc_free releases it.

Constraints

Each counter owns a pthread_mutex_t guarding its long. Read and increment both under the lock; tsc_free(NULL) is safe.

Starter code

#include <pthread.h>
#include <stddef.h>
struct ts_counter;
typedef struct ts_counter ts_counter_t;
ts_counter_t *tsc_new(void) { return NULL; }
void tsc_inc(ts_counter_t *c) { (void)c; }
long tsc_value(ts_counter_t *c) { (void)c; return 0; }
void tsc_free(ts_counter_t *c) { (void)c; }

Background lessons

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.