Repository: SerenityOS/serenity
File: Userland/Libraries/LibC/pthread.cpp (C++)

Code before:
        *thread = rc;
    else
        MUST(__free_tls_region(bit_cast<FlatPtr>(thread_params->tls_pointer)));

    __RETURN_PTHREAD_ERROR(rc);
}

// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_create.html
int pthread_create(pthread_t* thread, pthread_attr_t const* attributes, void* (*start_routine)(void*), void* argument_to_start_routine)
{
    if (!thread)
        return -EINVAL;

    PthreadAttrImpl default_attributes {};
    PthreadAttrImpl* const* arg_attributes = reinterpret_cast<PthreadAttrImpl* const*>(attributes);

    PthreadAttrImpl* used_attributes = arg_attributes ? *arg_attributes : &default_attributes;

    if (!used_attributes->stack_location) {
        // adjust stack size, user might have called setstacksize, which has no restrictions on size/alignment
        if (0 != (used_attributes->stack_size % required_stack_alignment))
            used_attributes->stack_size += required_stack_alignment - (used_attributes->stack_size % required_stack_alignment);

        used_attributes->stack_location = mmap_with_name(nullptr, used_attributes->stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, 0, 0, "Thread stack");
        if (!used_attributes->stack_location)
            return -1;
    }

    dbgln_if(PTHREAD_DEBUG, "pthread_create: Creating thread with attributes at {}, detach state {}, priority {}, guard page size {}, stack size {}, stack location {}",
        used_attributes,
        (PTHREAD_CREATE_JOINABLE == used_attributes->detach_state) ? "joinable" : "detached",
        used_attributes->schedule_priority,
        used_attributes->guard_page_size,
        used_attributes->stack_size,
        used_attributes->stack_location);

    return create_thread(thread, start_routine, argument_to_start_routine, used_attributes);
}

// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_exit.html
void pthread_exit(void* value_ptr)
{
    while (!cleanup_handlers.is_empty()) {
        auto handler = cleanup_handlers.take_first();
        handler.routine(handler.argument);
    }

    pthread_exit_without_cleanup_handlers(value_ptr);
}

#ifndef _DYNAMIC_LOADER

Review comment (suggestion):
I assume the respective part of the specification is "2.9.8 Use of Application-Managed Thread Stacks"?

From there I can see no immediate relation between the stack address attribute and the guard size. It appears that if the stack address attribute has been set then it's the applications responsibility to manage the stack as a whole (including initial allocation). This is already correctly represented here, so I don't see a point in having this as a comment additionally.

Code after:
        *thread = rc;
    else
        MUST(__free_tls_region(bit_cast<FlatPtr>(thread_params->tls_pointer)));

    __RETURN_PTHREAD_ERROR(rc);
}

// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_create.html
int pthread_create(pthread_t* thread, pthread_attr_t const* attributes, void* (*start_routine)(void*), void* argument_to_start_routine)
{
    if (!thread)
        return -EINVAL;

    PthreadAttrImpl default_attributes {};
    PthreadAttrImpl* const* arg_attributes = reinterpret_cast<PthreadAttrImpl* const*>(attributes);

    PthreadAttrImpl* used_attributes = arg_attributes ? *arg_attributes : &default_attributes;

    if (!used_attributes->stack_location) {
        // adjust stack size, user might have called setstacksize, which has no restrictions on size/alignment
        if (0 != (used_attributes->stack_size % required_stack_alignment))
            used_attributes->stack_size += required_stack_alignment - (used_attributes->stack_size % required_stack_alignment);

        // According to POSIX, if the stack address attribute has been set in attr,
        // then the guard size attribute is ignored: it is the application's responsibility
        // to handle stack overflow.
        size_t total_size = used_attributes->stack_size + used_attributes->guard_page_size;
        void* memory_location = mmap_with_name(nullptr, total_size, used_attributes->guard_page_size == 0 ? PROT_READ | PROT_WRITE : PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, 0, 0, "Thread stack");
        if (!memory_location)
            return -1;
        if (used_attributes->guard_page_size > 0) {
            if (mprotect((void*)((FlatPtr)memory_location + used_attributes->guard_page_size), used_attributes->stack_size, PROT_READ | PROT_WRITE) < 0) {
                munmap(memory_location, total_size);
                return -1;
            }
        }
        used_attributes->stack_location = (void*)((FlatPtr)memory_location + used_attributes->guard_page_size);
    }

    dbgln_if(PTHREAD_DEBUG, "pthread_create: Creating thread with attributes at {}, detach state {}, priority {}, guard page size {}, stack size {}, stack location {}",
        used_attributes,
        (PTHREAD_CREATE_JOINABLE == used_attributes->detach_state) ? "joinable" : "detached",
        used_attributes->schedule_priority,
        used_attributes->guard_page_size,
        used_attributes->stack_size,
        used_attributes->stack_location);

    return create_thread(thread, start_routine, argument_to_start_routine, used_attributes);
}

// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_exit.html