Thread type

The fossil_threads library provides a cross-platform, lightweight abstraction for thread creation and management. It supports creating joinable or detached threads, joining threads, detaching them, yielding the processor, sleeping, and querying thread IDs. The C++ wrapper offers RAII management, move semantics, and convenient static utilities for sleeping, yielding, and comparing threads.

HEADER REFERENCE #

#ifndef FOSSIL_THREADS_THREAD_H
#define FOSSIL_THREADS_THREAD_H

#include <stddef.h>

#ifdef __cplusplus
extern "C"
{
#endif

/* ---------- Configuration / visibility ---------- */
#if defined(_WIN32) && defined(FOSSIL_THREADS_BUILD_DLL)
#  define FOSSIL_THREADS_API __declspec(dllexport)
#elif defined(_WIN32) && defined(FOSSIL_THREADS_USE_DLL)
#  define FOSSIL_THREADS_API __declspec(dllimport)
#else
#  define FOSSIL_THREADS_API
#endif

/* ---------- Types ---------- */

/* User thread entry point: return value is optionally retrieved by join(). */
typedef void* (*fossil_threads_thread_func)(void *arg);

/* Opaque-ish handle; fields are public for POD-ness but do not touch them. */
typedef struct fossil_threads_thread {
    void *handle;          /* OS handle: HANDLE on Win, pthread_t* heap ptr on POSIX */
    unsigned long id;      /* OS thread id */
    void *retval;          /* join() result (set by wrapper) */
    int   joinable;        /* 1 if joinable, 0 if detached */
    int   started;         /* 1 after successful create */
    int   finished;        /* 1 after thread function returns */
} fossil_threads_thread_t;

/* ---------- Lifecycle ---------- */

// *****************************************************************************
// Function prototypes
// *****************************************************************************

/* 
 * Create a joinable thread that runs 'func(arg)' in a new OS thread.
 * 
 * @param thread Pointer to a fossil_threads_thread_t structure to initialize.
 * @param func   Function pointer to the thread entry point (must match fossil_threads_thread_func signature).
 * @param arg    Argument to pass to the thread function (may be NULL).
 * @return       0 on success, or error code on failure (see error codes below).
 * 
 * On success, 'thread' is initialized and represents a joinable thread.
 * The thread will execute 'func(arg)' and may be joined or detached.
 */
FOSSIL_THREADS_API int fossil_threads_thread_create(
    fossil_threads_thread_t *thread, 
    fossil_threads_thread_func func, 
    void *arg
);

/* 
 * Join a joinable thread, blocking until it finishes execution.
 * 
 * @param thread Pointer to a fossil_threads_thread_t representing a joinable thread.
 * @param retval If not NULL, receives the return value from the thread function.
 * @return       0 on success, or error code on failure.
 * 
 * After a successful join, the thread handle is cleaned up and cannot be joined or detached again.
 * If the thread was already detached or not joinable, returns an error.
 */
FOSSIL_THREADS_API int fossil_threads_thread_join(
    fossil_threads_thread_t *thread, 
    void **retval
);

/* 
 * Detach a thread, allowing its resources to be released automatically when it finishes.
 * 
 * @param thread Pointer to a fossil_threads_thread_t representing a joinable thread.
 * @return       0 on success, or error code on failure.
 * 
 * After detaching, join() is invalid for this thread.
 * Detaching a thread that is already detached or not joinable returns an error.
 */
FOSSIL_THREADS_API int fossil_threads_thread_detach(
    fossil_threads_thread_t *thread
);

/* 
 * Initialize a fossil_threads_thread_t structure to a safe, zeroed state.
 * 
 * @param thread Pointer to the thread structure to initialize.
 * 
 * This is a no-op if the structure is already zeroed.
 * It is safe to call before create(), or to reinitialize a disposed structure.
 */
FOSSIL_THREADS_API void fossil_threads_thread_init(
    fossil_threads_thread_t *thread
);

/* 
 * Dispose of a thread handle, releasing any resources if necessary.
 * 
 * @param thread Pointer to the thread structure to dispose.
 * 
 * Safe to call on zeroed, unused, or already-joined/detached structures.
 * Does not affect running threads; only cleans up handles.
 */
FOSSIL_THREADS_API void fossil_threads_thread_dispose(
    fossil_threads_thread_t *thread
);

/* ---------- Management / utilities ---------- */

/* 
 * Yield the processor, hinting the OS scheduler to switch to another thread.
 * 
 * @return 0 on success, or error code on failure.
 * 
 * This is a cooperative scheduling hint; actual behavior is OS-dependent.
 */
FOSSIL_THREADS_API int fossil_threads_thread_yield(void);

/* 
 * Sleep the calling thread for the specified number of milliseconds.
 * 
 * @param ms Number of milliseconds to sleep.
 * @return   0 on success, or error code on failure.
 * 
 * The thread is suspended for at least 'ms' milliseconds.
 * Actual sleep duration may be longer due to OS scheduling.
 */
FOSSIL_THREADS_API int fossil_threads_thread_sleep_ms(
    unsigned int ms
);

/* 
 * Get the current thread's OS-specific thread identifier.
 * 
 * @return The thread ID as an unsigned long.
 * 
 * This value is suitable for comparison and logging, but may not be unique across processes.
 */
FOSSIL_THREADS_API unsigned long fossil_threads_thread_id(void);

/* 
 * Compare two thread objects to determine if they refer to the same underlying thread.
 * 
 * @param t1 Pointer to the first thread structure.
 * @param t2 Pointer to the second thread structure.
 * @return   Nonzero if both refer to the same thread, 0 otherwise.
 * 
 * Useful for checking thread identity in portable code.
 */
FOSSIL_THREADS_API int fossil_threads_thread_equal(
    const fossil_threads_thread_t *t1, 
    const fossil_threads_thread_t *t2
);

/* Error codes (subset mirrors common errno/GetLastError patterns) */
enum {
    FOSSIL_THREADS_OK            = 0,
    FOSSIL_THREADS_EINVAL        = 22,   /* invalid argument */
    FOSSIL_THREADS_EBUSY         = 16,   /* resource busy / wrong state */
    FOSSIL_THREADS_ENOMEM        = 12,   /* allocation failed */
    FOSSIL_THREADS_EPERM         = 1,    /* not permitted / wrong usage */
    FOSSIL_THREADS_EINTERNAL     = 199   /* generic internal failure */
};

#ifdef __cplusplus
}
#include <stdexcept>
#include <utility>

namespace fossil {

namespace threads {

    class Thread {
    public:
        using Func = void*(*)(void*);

        /**
         * Default constructor.
         * Initializes the native thread structure to a safe, zeroed state.
         * Does not start a thread.
         */
        Thread() {
            fossil_threads_thread_init(&native_);
        }

        /**
         * Parameterized constructor.
         * Initializes the native thread structure and starts a new thread
         * running the given function with the provided argument.
         * Throws std::runtime_error if thread creation fails.
         *
         * @param func  The thread entry point function.
         * @param arg   Argument to pass to the thread function (default: nullptr).
         */
        explicit Thread(Func func, void* arg = nullptr) {
            fossil_threads_thread_init(&native_);
            if (fossil_threads_thread_create(&native_, func, arg) != 0) {
            throw std::runtime_error("Failed to create thread");
            }
        }

        /**
         * Destructor.
         * Cleans up any resources associated with the thread.
         * If the thread is still running, this does not join or detach it.
         */
        ~Thread() {
            fossil_threads_thread_dispose(&native_);
        }

        // Disable copy construction and copy assignment to prevent
        // accidental copying of thread handles.
        Thread(const Thread&) = delete;
        Thread& operator=(const Thread&) = delete;

        /**
         * Move constructor.
         * Transfers ownership of the thread handle from another Thread object.
         * The source object is reset to a safe, zeroed state.
         *
         * @param other  The Thread object to move from.
         */
        Thread(Thread&& other) noexcept {
            native_ = other.native_;
            other.native_ = {};
        }

        /**
         * Move assignment operator.
         * Disposes of any existing thread handle, then transfers ownership
         * from another Thread object. The source object is reset.
         *
         * @param other  The Thread object to move from.
         * @return       Reference to this object.
         */
        Thread& operator=(Thread&& other) noexcept {
            if (this != &other) {
            fossil_threads_thread_dispose(&native_);
            native_ = other.native_;
            other.native_ = {};
            }
            return *this;
        }

        /**
         * Join the thread.
         * Waits for the thread to finish execution. Optionally retrieves the
         * return value from the thread function.
         *
         * @param retval  Pointer to receive the thread's return value (default: nullptr).
         * @return        0 on success, error code otherwise.
         */
        int join(void** retval = nullptr) {
            return fossil_threads_thread_join(&native_, retval);
        }

        /**
         * Detach the thread.
         * Releases resources when the thread finishes. After detaching,
         * join() is invalid.
         *
         * @return  0 on success, error code otherwise.
         */
        int detach() {
            return fossil_threads_thread_detach(&native_);
        }

        /**
         * Get the thread ID.
         * Returns the OS-specific thread identifier for this thread.
         *
         * @return  The thread ID as an unsigned long.
         */
        unsigned long id() const {
            return native_.id;
        }

        /**
         * Check if the thread is joinable.
         * Returns true if the thread can be joined, false if detached or not started.
         *
         * @return  True if joinable, false otherwise.
         */
        bool joinable() const {
            return native_.joinable != 0;
        }

        /**
         * Yield the processor to another thread.
         * Static utility to hint the OS scheduler to switch threads.
         */
        static void yield() {
            fossil_threads_thread_yield();
        }

        /**
         * Sleep for a specified duration (in milliseconds).
         * Static utility to pause the current thread.
         *
         * @param ms  Number of milliseconds to sleep.
         */
        static void sleep_ms(unsigned int ms) {
            fossil_threads_thread_sleep_ms(ms);
        }

        /**
         * Get the current thread ID.
         * Static utility to retrieve the calling thread's OS-specific ID.
         *
         * @return  The current thread ID as an unsigned long.
         */
        static unsigned long current_id() {
            return fossil_threads_thread_id();
        }

        /**
         * Compare if two thread objects refer to the same underlying thread.
         * Static utility to check thread identity.
         *
         * @param t1  First thread object.
         * @param t2  Second thread object.
         * @return    True if both refer to the same thread, false otherwise.
         */
        static bool equal(const Thread& t1, const Thread& t2) {
            return fossil_threads_thread_equal(&t1.native_, &t2.native_) != 0;
        }

        /**
         * Get the native thread handle.
         * Returns a pointer to the underlying C thread structure.
         *
         * @return  Pointer to fossil_threads_thread_t.
         */
        fossil_threads_thread_t* native_handle() { return &native_; }

        /**
         * Get the native thread handle (const version).
         * Returns a const pointer to the underlying C thread structure.
         *
         * @return  Const pointer to fossil_threads_thread_t.
         */
        const fossil_threads_thread_t* native_handle() const { return &native_; }

    private:
        fossil_threads_thread_t native_{};
    };

} // namespace threads

} // namespace fossil

#endif

#endif /* FOSSIL_THREADS_THREAD_H */

SAMPLE CODE C #

#include "fossil/threads/thread.h"
#include <stdio.h>

void* my_thread_func(void* arg) {
    printf("Hello from thread! Arg = %s\n", (char*)arg);
    return arg;
}

int main() {
    fossil_threads_thread_t thread;
    fossil_threads_thread_init(&thread);

    if (fossil_threads_thread_create(&thread, my_thread_func, "C thread") != 0) {
        printf("Failed to create thread\n");
        return 1;
    }

    void* retval;
    fossil_threads_thread_join(&thread, &retval);
    printf("Thread returned: %s\n", (char*)retval);

    fossil_threads_thread_dispose(&thread);
    return 0;
}

SAMPLE CODE C++ #

#include "fossil/threads/thread.h"
#include <iostream>

void* thread_func(void* arg) {
    std::cout << "Hello from thread! Arg = " << static_cast<char*>(arg) << "\n";
    return arg;
}

int main() {
    try {
        fossil::threads::Thread t(thread_func, (void*)"C++ thread");

        std::cout << "Thread ID: " << t.id() << "\n";

        void* retval = nullptr;
        t.join(&retval);
        std::cout << "Thread returned: " << static_cast<char*>(retval) << "\n";

        fossil::threads::Thread::sleep_ms(500);
        fossil::threads::Thread::yield();

    } catch (const std::runtime_error& e) {
        std::cerr << "Thread error: " << e.what() << "\n";
    }
}

What are your feelings

Updated on August 21, 2025