pthread_create(3)

๐ŸŽ€ NAME ๐ŸŽ€

okay so this is pthread_create(3) from the Library Functions Manual, and babes, this is THE function. pthread_create โ€” create a new thread. that's the whole vibe. you want a thread? you're getting a thread ๐Ÿ’…


๐ŸŒˆ LIBRARY ๐ŸŒˆ

POSIX threads library (libpthread, -lpthread)

yes you have to link it. yes it's -lpthread. no we're not arguing about it.


โœจ SYNOPSIS โœจ

#include <pthread.h>

int pthread_create(pthread_t *restrict thread,
                   const pthread_attr_t *_Nullable restrict attr,
                   typeof(void *(void *_Nullable)) *start_routine,
                   void *_Nullable restrict arg);

look at that signature. gorgeous. iconic. don't look at it too long tho, it's giving "pointer to a function that returns a pointer to a function" energy and we simply do not have time ๐Ÿซถ


๐Ÿ’– DESCRIPTION ๐Ÿ’–

So pthread_create() starts a new thread inside the process that's calling it. The new thread begins its little life by invoking start_routine(), and arg gets handed to start_routine() as its one and only argument. one arg. that's all you get. pack wisely ๐ŸŽ€

The new thread can clock out in one of these ways:

  • It calls pthread_exit(3) and specifies an exit status value. That value is available to another thread in the same process that calls pthread_join(3). ๐Ÿ’ซ
  • It just returns from start_routine(). This is literally equivalent to calling pthread_exit(3) with whatever value was in the return statement.
  • It gets canceled โ€” see pthread_cancel(3) for that whole situation.
  • Any thread in the process calls exit(3), or the main thread does a return from main(). This terminates every single thread in the process. all of them. no survivors. ๐Ÿ’€

The attr argument points at a pthread_attr_t structure, and its contents get used at thread creation time to decide the attributes of the new thread. You initialize that structure with pthread_attr_init(3) and its related functions. If attr is NULL, then congrats, the thread gets created with default attributes โ€” no thoughts, head empty, defaults โœจ

Before it returns, a successful pthread_create() call stores the ID of the new thread in the buffer that thread points to. That identifier is how you refer to the thread in all your later calls to other pthreads functions. it's the thread's little name tag ๐Ÿท๏ธ

The new thread inherits a copy of the creating thread's signal mask โ€” that's pthread_sigmask(3). But the set of pending signals for the new thread is empty (sigpending(2)), and the new thread does not inherit the creating thread's alternate signal stack (sigaltstack(2)). so it copies the mask but not the vibes. noted.

The new thread also inherits the calling thread's floating-point environment (fenv(3)). ๐Ÿฆ‹

And the initial value of the new thread's CPU-time clock is 0 โ€” see pthread_getcpuclockid(3).

๐ŸŒธ Linux-specific details ๐ŸŒธ

The new thread inherits copies of the calling thread's capability sets (see capabilities(7)) and its CPU affinity mask (see sched_setaffinity(2)).

linux really said "you get the whole personality, bestie" ๐Ÿ’œ


๐Ÿ”ฅ RETURN VALUE ๐Ÿ”ฅ

On success, pthread_create() returns 0. On error, it returns an error number, and the contents of *thread are undefined โ€” so don't go reading that buffer like it means something, because it does not. ๐Ÿ™ˆ


๐Ÿ’€ ERRORS ๐Ÿ’€

  • EAGAIN โ€” Not enough resources to create another thread. the tank is empty.
  • EAGAIN โ€” A system-imposed limit on the number of threads was hit. There are a few limits that can trigger this: the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), which caps the number of processes and threads for a real user ID, was reached; the kernel's system-wide limit on processes and threads, /proc/sys/kernel/threads-max, was reached (see proc(5)); or the maximum number of PIDs, /proc/sys/kernel/pid_max, was reached (see proc(5)). three separate ceilings, all of them rude ๐Ÿ˜ญ
  • EINVAL โ€” Invalid settings in attr.
  • EPERM โ€” No permission to set the scheduling policy and parameters specified in attr. you simply cannot sit with us ๐Ÿšซ

๐Ÿ‘‘ ATTRIBUTES ๐Ÿ‘‘

For an explanation of the terms used in this section, see attributes(7).

  • Interface: pthread_create()
  • Attribute: Thread safety
  • Value: MT-Safe

MT-Safe!! she's safe, she's thread-safe, she's everything ๐Ÿซถ


โญ STANDARDS โญ

POSIX.1-2008.


๐Ÿฆ‹ HISTORY ๐Ÿฆ‹

POSIX.1-2001.

she's been here since 2001 and she's still serving. longevity queen.


๐Ÿ’… NOTES ๐Ÿ’…

Check pthread_self(3) if you want more info on the thread ID that pthread_create() drops into *thread. And unless real-time scheduling policies are in play, after a call to pthread_create() it is indeterminate which thread โ€” the caller or the new one โ€” runs next. so don't build your whole architecture around guessing who goes first, bestie, it's genuinely a coin flip ๐Ÿช™

A thread can be either joinable or detached. Here's the tea:

  • If a thread is joinable, another thread can call pthread_join(3) to wait for it to terminate and grab its exit status.
  • Only once a terminated joinable thread has been joined are the last of its resources released back to the system. so like, you have to actually collect your thread, it's not gonna recycle itself ๐Ÿงบ
  • When a detached thread terminates, its resources are released back to the system automatically. It is not possible to join with that thread to obtain its exit status. she's gone, she left no forwarding address.
  • Making a thread detached is useful for certain kinds of daemon threads whose exit status the application simply does not care about. no thoughts, just work, and then vanish โœจ
  • By default, a new thread is created in a joinable state โ€” unless attr was set to create it detached, using pthread_attr_setdetachstate(3).

Now, stack size lore, under the NPTL threading implementation: if the RLIMIT_STACK soft resource limit at the time the program started has any value other than "unlimited", then that value determines the default stack size of new threads. Using pthread_attr_setstacksize(3), you can explicitly set the stack size attribute in the attr argument you pass to create the thread, so you can get a stack size other than the default. And if RLIMIT_STACK is set to "unlimited", a per-architecture value is used instead: 2 MB on most architectures; 4 MB on POWER and Sparc-64. arch-dependent numbers, my beloathed ๐Ÿ˜ญ


๐Ÿ› BUGS ๐Ÿ›

In the obsolete LinuxThreads implementation, each of the threads in a process has a different process ID. This violates the POSIX threads specification and is the source of many other nonconformances to the standard โ€” see pthreads(7).

obsolete. we do not stan. we do not collect. we do not emulate. ๐Ÿ’€


...and that's not even the whole story bestie, there's more coming in part 2 ๐ŸŒธ

โœจ EXAMPLES โœจ

okay bestie, this next program is a whole demonstration โ€” it flexes pthread_create() and a bunch of other functions in the pthreads API at the same time. she's an overachiever, we love her ๐Ÿ’…

In this first run, on a system that's running the NPTL threading implementation, the stack size just defaults to whatever the "stack size" resource limit says. no cap, that's literally it:

$ ulimit -s
8192            # The stack size limit is 8 MB (0x800000 bytes)
$ ./a.out hola salut servus
Thread 1: top of stack near 0xb7dd03b8; argv_string=hola
Thread 2: top of stack near 0xb75cf3b8; argv_string=salut
Thread 3: top of stack near 0xb6dce3b8; argv_string=servus
Joined with thread 1; returned value was HOLA
Joined with thread 2; returned value was SALUT
Joined with thread 3; returned value was SERVUS

and then run number two: the program goes out of its way to explicitly set a stack size of 1 MB (using pthread_attr_setstacksize(3)) for the threads it creates. main character behavior tbh:

$ ./a.out -s 0x100000 hola salut servus
Thread 1: top of stack near 0xb7d723b8; argv_string=hola
Thread 2: top of stack near 0xb7c713b8; argv_string=salut
Thread 3: top of stack near 0xb7b703b8; argv_string=servus
Joined with thread 1; returned value was HOLA
Joined with thread 2; returned value was SALUT
Joined with thread 3; returned value was SERVUS

๐Ÿฆ‹ Program source ๐Ÿฆ‹

here she is, the full source code, no edits, no gatekeeping ๐Ÿซถ

#include <ctype.h>
#include <err.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

struct thread_info {    /* Used as argument to thread_start() */
    pthread_t thread_id;        /* ID returned by pthread_create() */
    int       thread_num;       /* Application-defined thread # */
    char     *argv_string;      /* From command-line argument */
};

/* Thread start function: display address near top of our stack,
   and return upper-cased copy of argv_string.  */

static void *
thread_start(void *arg)
{
    struct thread_info *tinfo = arg;
    char *uargv;

    printf("Thread %d: top of stack near %p; argv_string=%s\n",
           tinfo->thread_num, (void *) &tinfo, tinfo->argv_string);

    uargv = strdup(tinfo->argv_string);
    if (uargv == NULL)
        err(EXIT_FAILURE, "strdup");

    for (char *p = uargv; *p != '\0'; p++)
        *p = toupper(*p);

    return uargv;
}

int
main(int argc, char *argv[])
{
    int                 s, opt;
    void                *res;
    size_t              num_threads;
    ssize_t             stack_size;
    pthread_attr_t      attr;
    struct thread_info  *tinfo;

    /* The "-s" option specifies a stack size for our threads.  */

    stack_size = -1;
    while ((opt = getopt(argc, argv, "s:")) != -1) {
        switch (opt) {
        case 's':
            stack_size = strtoul(optarg, NULL, 0);
            break;

        default:
            fprintf(stderr, "Usage: %s [-s stack-size] arg...\n",
                    argv[0]);
            exit(EXIT_FAILURE);
        }
    }

    num_threads = argc - optind;

    /* Initialize thread creation attributes.  */

    s = pthread_attr_init(&attr);
    if (s != 0)
        errc(EXIT_FAILURE, s, "pthread_attr_init");

    if (stack_size > 0) {
        s = pthread_attr_setstacksize(&attr, stack_size);
        if (s != 0)
            errc(EXIT_FAILURE, s, "pthread_attr_setstacksize");
    }

    /* Allocate memory for pthread_create() arguments.  */

    tinfo = calloc(num_threads, sizeof(*tinfo));
    if (tinfo == NULL)
        err(EXIT_FAILURE, "calloc");

    /* Create one thread for each command-line argument.  */

    for (size_t tnum = 0; tnum < num_threads; tnum++) {
        tinfo[tnum].thread_num = tnum + 1;
        tinfo[tnum].argv_string = argv[optind + tnum];

        /* The pthread_create() call stores the thread ID into
           corresponding element of tinfo[].  */

        s = pthread_create(&tinfo[tnum].thread_id, &attr,
                           &thread_start, &tinfo[tnum]);
        if (s != 0)
            errc(EXIT_FAILURE, s, "pthread_create");
    }

    /* Destroy the thread attributes object, since it is no
       longer needed.  */

    s = pthread_attr_destroy(&attr);
    if (s != 0)
        errc(EXIT_FAILURE, s, "pthread_attr_destroy");

    /* Now join with each thread, and display its returned value.  */

    for (size_t tnum = 0; tnum < num_threads; tnum++) {
        s = pthread_join(tinfo[tnum].thread_id, &res);
        if (s != 0)
            errc(EXIT_FAILURE, s, "pthread_join");

        printf("Joined with thread %d; returned value was %s\n",
               tinfo[tnum].thread_num, (char *) res);
        free(res);      /* Free memory allocated by thread */
    }

    free(tinfo);
    exit(EXIT_SUCCESS);
}

๐Ÿ’œ SEE ALSO ๐Ÿ’œ

go read these next, they're the whole friend group ๐Ÿซถโœจ


and that's the whole page bestie, you ate it ๐Ÿ’…๐ŸŒธ this one's from Linux man-pages 6.20, dated 2026-02-10, and you just read pthread_create(3). go make some threads, you've got this ๐Ÿ‘‘๐Ÿ’–

๐Ÿ“ฆ where this page comes from
  • arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • ๐Ÿ“œ the page's license: Linux-man-pages-copyleft
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms