How to Handle Graceful Fiber Cancellation and Shutdown in libfiber

Graceful fiber cancellation in libfiber relies on the FIBER_F_CANCELED flag checked via acl_fiber_canceled(), with shutdown triggered through acl_fiber_kill() or acl_fiber_signal() APIs that wake suspended fibers and propagate ECANCELED errors.

When building high-concurrency applications with iqiyi/libfiber, managing the lifecycle of fibers—including their orderly termination—is critical for resource cleanup and system stability. The library provides a deterministic cancellation mechanism that allows external threads or fibers to request shutdown while giving target fibers the opportunity to release resources and exit cleanly. This guide examines the source-level implementation of cancellation flags, the public APIs for triggering shutdown, and the patterns required to write robust, cancellation-aware fiber code.

Understanding the Cancellation Architecture

At the core of libfiber’s shutdown semantics is a bitmask-based state machine defined in c/include/fiber/fiber_base.h. The architecture separates the act of canceling a fiber from the fiber’s cooperative response to that cancellation.

The FIBER_F_CANCELED Flag

The cancellation flag aggregates three terminal states into a single testable mask. As defined at line 55 of c/include/fiber/fiber_base.h, the macro FIBER_F_CANCELED represents the bitwise OR of:

  • FIBER_F_KILLED — set by acl_fiber_kill()
  • FIBER_F_CLOSED — set when a fiber’s context is closed
  • FIBER_F_SIGNALED — set by acl_fiber_signal()

When any of these individual flags are set on a fiber’s flag field, acl_fiber_canceled() (implemented in c/src/fiber.c at lines 429–435) returns non-zero, indicating the fiber should abort its current operation.

Integration with Synchronization and I/O

All blocking primitives in libfiber check the cancellation state before suspending. For example, in c/src/sync/fiber_sem.c (lines 94–100), acl_fiber_sem_timed_wait begins with:

if (acl_fiber_canceled(curr))
    return FIBER_EAGAIN;

Similarly, I/O operations in c/src/fiber_io.c (lines 478–519) invoke file_cancel() to abort underlying OS requests when acl_fiber_canceled() is true, ensuring that pending read/write calls return immediately with an error rather than hanging indefinitely.

Triggering Fiber Cancellation

libfiber provides three main APIs for requesting fiber termination, ranging from non-blocking signals to synchronous, blocking waits.

Signaling with acl_fiber_signal

The asynchronous signal API allows you to notify a fiber using POSIX signal conventions:

acl_fiber_signal(target_fiber, SIGTERM);

According to the implementation in c/src/fiber.c (lines 445–470), this function sets the FIBER_F_SIGNALED bit on the target fiber’s flags. If the fiber is currently suspended (status FIBER_STATUS_SUSPEND), the scheduler immediately queues it for re-scheduling via the FIBER_READY macro, allowing the fiber to wake and check its cancellation state.

Killing with acl_fiber_kill

For graceful shutdown without waiting, use acl_fiber_kill:

acl_fiber_kill(target_fiber);

This function (lines 498–502) sets fiber->errnum to ECANCELED and invokes the internal fiber_signal() with SIGTERM. The target fiber will detect the cancellation on its next check, clean up resources, and exit its entry function.

Synchronous Shutdown with acl_fiber_kill_wait

When the caller must ensure the target fiber has fully exited before proceeding, use acl_fiber_kill_wait:

acl_fiber_kill_wait(target_fiber);

As implemented in lines 503–507 of c/src/fiber.c, this behaves identically to acl_fiber_kill() but sets the synchronized flag, forcing the calling fiber to yield (acl_fiber_yield()) until the target fiber completes its execution and is reclaimed by the scheduler.

Resetting State

In rare scenarios (typically testing), you can clear a fiber’s cancellation state using:

acl_fiber_clear(target_fiber);

Located at lines 37–43 of c/src/fiber.c, this clears both the errnum and the FIBER_F_CANCELED mask, allowing the fiber structure to be reused.

Writing Cancellation-Aware Fiber Code

Long-running fibers must cooperatively check for cancellation to avoid resource leaks. Follow this pattern in your fiber entry functions:

void my_fiber(ACL_FIBER *self, void *arg)
{
    ACL_FIBER_SEM *sem = (ACL_FIBER_SEM *)arg;
    
    while (!acl_fiber_canceled(self)) {
        /* Perform work */
        do_work();
        
        /* Check cancellation before blocking */
        if (acl_fiber_canceled(self)) {
            cleanup_resources();
            return;
        }
        
        /* Wait with timeout (handles cancellation internally) */
        int rc = acl_fiber_sem_timed_wait(sem, 1000); /* 1 second */
        if (rc < 0) {
            if (acl_fiber_canceled(self)) {
                printf("Cancelled during wait, errno = %d\n", fiber_errno(self));
                cleanup_resources();
                return;
            }
            /* Handle timeout or other errors */
        }
    }
}

Key implementation details:

  • Always check acl_fiber_canceled() before entering blocking sections
  • Synchronization primitives like acl_fiber_sem_timed_wait return errors when cancelled, setting ECANCELED
  • I/O cancellations propagate automatically through file_cancel() in c/src/fiber_io.c

Complete Example: Graceful Worker Shutdown

The following program demonstrates creating a worker fiber, allowing it to run, then requesting graceful shutdown:

#include "fiber/libfiber.h"
#include <stdio.h>
#include <unistd.h>

static void worker(ACL_FIBER *self, void *arg)
{
    int counter = 0;
    ACL_FIBER_SEM *sem = (ACL_FIBER_SEM *)arg;
    
    while (!acl_fiber_canceled(self)) {
        printf("Worker iteration: %d\n", counter++);
        
        /* Simulate periodic work with cancellation-aware wait */
        if (acl_fiber_sem_timed_wait(sem, 1000) < 0) {
            if (acl_fiber_canceled(self)) {
                printf("Worker detected cancellation, exiting cleanly.\n");
                return;
            }
        }
    }
}

int main(void)
{
    ACL_FIBER_SEM *sem = acl_fiber_sem_create(1);
    ACL_FIBER *worker_fiber = acl_fiber_create(worker, sem, 327680);
    
    /* Let worker run for 3 seconds */
    sleep(3);
    
    /* Request graceful shutdown and wait for completion */
    printf("Main: requesting shutdown...\n");
    acl_fiber_kill_wait(worker_fiber);
    
    acl_fiber_sem_free(sem);
    printf("Main: worker terminated, resources freed.\n");
    return 0;
}

In this example:

  • acl_fiber_create allocates a 320KB stack for the worker
  • acl_fiber_sem_timed_wait provides a cancellation point every second
  • acl_fiber_kill_wait ensures the main thread blocks until the worker exits

Summary

Graceful fiber cancellation in libfiber is built around cooperative polling of the FIBER_F_CANCELED flag:

  • Cancellation state is tracked via bitwise flags defined in c/include/fiber/fiber_base.h
  • External shutdown is triggered by acl_fiber_kill() (async) or acl_fiber_kill_wait() (sync)
  • Blocking primitives automatically check acl_fiber_canceled() and return ECANCELED when terminated
  • I/O operations utilize file_cancel() in c/src/fiber_io.c to abort pending system calls
  • Cooperative cleanup requires fibers to poll acl_fiber_canceled() and exit their entry functions cleanly

Frequently Asked Questions

How does libfiber distinguish between a timeout and a cancellation in blocking calls?

Both conditions cause waiting primitives to return an error, but you distinguish them by checking acl_fiber_canceled() and examining fiber_errno(). After a cancellation, errno is set to ECANCELED (as set in acl_fiber_kill() at lines 498–502 of c/src/fiber.c), whereas timeouts typically return ETIME or FIBER_EAGAIN depending on the specific primitive.

Can I cancel a fiber that is actively running on another CPU core?

Yes. acl_fiber_signal() and acl_fiber_kill() are thread-safe operations that set the cancellation flag atomically. If the target fiber is currently executing, it will detect the cancellation on its next call to acl_fiber_canceled() or when it enters a blocking primitive. If suspended, the scheduler wakes it immediately via the FIBER_READY macro.

What happens if I ignore the cancellation flag in my fiber code?

Ignoring acl_fiber_canceled() prevents graceful shutdown. The fiber will continue running until completion or until the process terminates. For fibers blocked in I/O, the underlying operation can be aborted via file_cancel() (as seen in c/src/fiber_io.c), but the fiber code must still check the return value and exit; otherwise, it may loop indefinitely or leak resources.

Is it safe to reuse a fiber structure after cancellation?

Only if you explicitly clear the state. The acl_fiber_clear() function (lines 37–43 of c/src/fiber.c) resets the errnum and clears the FIBER_F_CANCELED mask. However, this is generally discouraged for production code; the intended pattern is to create a new fiber via acl_fiber_create() rather than recycling old structures, as the fiber’s stack may contain stale data from the previous execution.

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 →