Linux System Programming · intermediate · ~10 min

Writing thread-safe code

Recognize what makes a function thread-safe, and what does not.

Lesson

What "thread-safe" means

A thread-safe function can be called from several threads at the same time and still produce the documented result.

In multithreaded code, concurrently means two or more threads may run the same function at overlapping moments. A thread-safe function behaves correctly even then.

Three categories

Functions fall into three rough groups.

1. Reentrant

A reentrant function uses only its arguments and local variables. It shares no state at all.

  • Example: strlen(const char *s).
  • These are automatically thread-safe.

2. Thread-safe with internal locking

These functions do have shared state, but they take a lock internally to protect it. The caller does not need to do anything extra.

  • Example: malloc on glibc.

3. Not thread-safe

These functions use static state (data that persists between calls and is shared by all callers). The caller must serialize access to them.

  • Classic examples: strtok, gmtime, asctime, rand.
  • Each has a reentrant _r variant you can use instead: strtok_r, gmtime_r, rand_r.

Writing your own helpers

When you write your own functions, follow these rules.

  • Prefer pure functions. Avoid globals and static buffers.
  • Let the caller own any state. If you need state, put it in a struct the caller passes in, and document that the caller must serialize access to it.
  • Protect any globals you must use. Wrap them in a mutex, or make them _Atomic.

Code examples

/* Not thread-safe — internal static buffer */
char *to_hex(unsigned x) {
    static char buf[16];
    snprintf(buf, sizeof buf, "%x", x);
    return buf;       /* every caller shares this buffer */
}

/* Thread-safe — caller provides the buffer */
char *to_hex_r(unsigned x, char *buf, size_t cap) {
    snprintf(buf, cap, "%x", x);
    return buf;
}

Common mistakes

  • Using strtok, gmtime, asctime, or ctime in multithreaded code. Use the _r variants instead.
  • Believing that "global counters are fine if they're only an int." A read-modify-write (such as count++) is never atomic on a plain int. Use _Atomic to make it safe.

Summary

  • Thread-safe means no unprotected shared state.
  • Avoid strtok, gmtime, asctime, and rand; use their _r variants.
  • Prefer pure functions; if you need state, let the caller own it.
  • Protect any required globals with a mutex or _Atomic.

Practice with these exercises