Thread-Safety Considerations When Mixing Libfiber Fibers with pthreads

Libfiber fibers are cooperative and bound to a single OS thread, requiring independent schedulers per pthread and exclusive use of library synchronization primitives like fiber_mutex and go_wait_thread to avoid race conditions.

When building high-concurrency applications with iqiyi/libfiber, developers often need to combine lightweight cooperative fibers with traditional POSIX threads (pthreads). Understanding the thread-safety considerations when mixing fibers with pthreads is critical to prevent deadlocks, data races, and undefined behavior, as libfiber’s user-level scheduling model imposes strict binding between fibers and their host OS threads.

Understanding the Fiber-to-Thread Binding Model

Libfiber implements user-level, cooperative fibers scheduled by the library itself. Each fiber runs inside exactly one OS thread, and the relationship is thread-local. In cpp/src/fiber.cpp (lines 53-56), the self() method retrieves the current fiber ID only for the calling thread:

unsigned long long fiber::self(void) {
    return acl_fiber_self();  // Returns ID of fiber running on this thread only
}

Attempting to call fiber::self() from a thread that never entered the libfiber scheduler results in undefined behavior. Always initialize the scheduler first.

Initializing Schedulers on Every OS Thread

The event loop in libfiber is per-thread. In cpp/src/fiber.cpp (lines 82-89), schedule() initializes a thread-local scheduler queue:

void fiber::schedule(void) {
    acl_fiber_schedule();  // Starts the event loop for this thread only
}

To use fibers across multiple pthreads, each thread must call acl::fiber::schedule() independently. Fibers created within one pthread never migrate to another pthread’s ready-queue, preserving isolation.

Safe Communication Between Fibers and pthreads

Using Thread-Safe Synchronization Primitives

When sharing data between fibers and pthreads, use libfiber’s thread-safe primitives instead of standard mutexes. These wrap pthread_mutex and pthread_cond internally. In c/src/sync/fiber_mutex.c, the core implementation ensures cross-thread safety:

// C++ wrapper in cpp/src/fiber_mutex.cpp (lines 7-38)
class fiber_mutex {
    // Wraps acl_fiber_mutex_* which uses pthread_mutex internally
};

Safe primitives include:

  • fiber_mutex – mutual exclusion between fibers and threads
  • fiber_cond – condition variables
  • fiber_sem – semaphores
  • fiber_event – event signaling
  • fiber_tbox – typed message boxes
  • wait_group – synchronization barriers

Spawning pthreads from Fibers with go_wait_thread

The go_wait_thread macro (defined in cpp/include/fiber/go_fiber.hpp, lines 64-73) safely creates a std::thread from within a fiber and blocks the fiber until the thread completes:

go_wait_thread[&] {
    // This lambda runs in a new std::thread
    usleep(50000);
    shared_data = 42;
    box.push(nullptr);  // Signal completion
};

The fiber blocks safely via fiber_tbox, which uses fiber_mutex internally, preventing race conditions between the fiber and the spawned pthread.

Shared Resources and External Locking

The fiber_pool class manages fiber lifecycles but does not provide internal locking for cross-thread access. In cpp/src/fiber_pool.cpp (lines 46-51 and 84-86), the fibers_ set is modified without synchronization:

// fiber_pool.cpp - unsynchronised modifications
void fiber_pool::fiber_create(...) {
    fibers_.insert(...);  // No internal lock
}

When sharing a fiber_pool across multiple pthreads, protect public API calls (fiber_create, stop, schedule) with an external std::mutex, or maintain separate pools per thread.

Summary

  • Bind fibers to threads: Each pthread must call acl::fiber::schedule() to initialize its own scheduler; fibers never migrate between OS threads.
  • Use library primitives: Synchronize between fibers and pthreads exclusively through fiber_mutex, fiber_cond, fiber_tbox, and related thread-safe types.
  • Spawn safely: Use go_wait_thread to launch pthreads from fibers and wait for completion without manual locking.
  • Protect shared pools: When using fiber_pool across threads, apply external locking or use thread-local pool instances.
  • Avoid raw access: Never manipulate fiber object internals (e.g., fb->i_) from a different thread without synchronization.

Frequently Asked Questions

Can a fiber migrate between different pthreads during execution?

No. Libfiber fibers are strictly bound to the OS thread that created them. The scheduler in cpp/src/fiber.cpp maintains thread-local state, and a fiber’s context remains on the same pthread until it exits. Migration would require explicit stack copying and scheduler coordination, which the library does not support.

Is it safe to call fiber::self() from a plain pthread that never initialized libfiber?

No. Calling acl::fiber::self() from a thread that never entered the scheduler returns undefined values or crashes. In cpp/src/fiber.cpp (lines 53-56), the function assumes the calling thread has valid thread-local storage initialized by acl::fiber::schedule() or acl::fiber::init().

What happens if I use a standard std::mutex instead of fiber_mutex between a fiber and a pthread?

Using std::mutex can cause deadlocks or priority inversion. If a fiber locks a std::mutex and then yields (e.g., via I/O), the pthread waiting on that mutex may block indefinitely because the fiber scheduler cannot resume the fiber until the mutex owner releases it. Libfiber’s fiber_mutex yields control safely and integrates with the scheduler.

Can I share a single fiber_pool instance across multiple pthreads without external locking?

No. The fiber_pool implementation in cpp/src/fiber_pool.cpp (lines 46-51, 84-86) modifies internal containers like fibers_ without synchronization primitives. Concurrent access from multiple pthreads causes data races. You must protect fiber_pool method calls with an external std::mutex or create separate pool instances per thread.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →