Backend Development 23 min read

Understanding Swoole Coroutines: Architecture, Zend VM Integration, and Implementation Details

This article explains the concept and history of coroutines, surveys language support, and provides an in‑depth analysis of Swoole’s coroutine implementations from version 1.x to 4.x, including PHP Zend VM integration, C‑stack management with assembly, and practical code examples.

Xueersi Online School Tech Team
Xueersi Online School Tech Team
Xueersi Online School Tech Team
Understanding Swoole Coroutines: Architecture, Zend VM Integration, and Implementation Details

What Is a Coroutine?

Coroutines, coined by Melvin Conway in 1958, are a form of sub‑routine that can transfer control via yield without OS‑level preemption, making them lightweight user‑space threads managed cooperatively by the programmer.

Modern languages such as Golang , Lua , Python , C# , JavaScript , and even C/C++ provide coroutine‑like constructs; PHP implements them through generators.

Swoole Coroutine Evolution

Swoole 1.x

Swoole started as a high‑performance network engine using asynchronous callbacks. As projects grew, callback nesting became hard to maintain, prompting Swoole to explore coroutine support.

Swoole 2.x

Version 2.x introduced kernel‑native coroutines based on setjmp / longjmp . While this removed the need for explicit yield , it suffered from stack‑frame loss and undefined behavior when the original frame had already exited.

Swoole 3.x

Version 3.x borrowed ideas from the fiber‑ext project and leveraged PHP 7 VM interrupt mechanisms to switch stacks safely during certain bytecode executions.

Swoole 4.x

Version 4.x implements a dual‑stack coroutine kernel, separating the C stack and the PHP VM stack, and introduces a Runtime Hook that transparently converts blocking PHP code into coroutine‑friendly code.

Simple Coroutine Example

<?php
go(function(){
    echo "coro 1 start\n";
    co::sleep(1);
    echo "coro 1 exit";
});

echo "main flag\n";

go(function(){
    echo "coro 2 start\n";
    co::sleep(1);
    echo "coro 2 exit\n";
});

echo "main end\n";

The output demonstrates how Swoole jumps between coroutine stacks while preserving the main execution flow.

Zend VM Internals Relevant to Coroutines

The PHP interpreter compiles scripts into an opcode array ( zend_op_array ). Each opcode is represented by the zend_op struct, which contains handler pointers, operands, and metadata.

struct _zend_op {
    const void *handler;   // C handler for the opcode
    znode_op op1;
    znode_op op2;
    znode_op result;
    uint32_t extended_value;
    uint32_t lineno;
    zend_uchar opcode;
    zend_uchar op1_type;
    zend_uchar op2_type;
    zend_uchar result_type;
};

Each function (including the main script) has its own zend_op_array , which stores the opcode list, literals, and variable tables.

struct _zend_op_array {
    uint32_t last;          // number of opcodes
    zend_op *opcodes;       // pointer to opcode array
    int last_var;           // number of CVs
    uint32_t T;             // number of temporary vars
    zend_string **vars;     // variable names
    int last_literal;       // number of literals
    zval *literals;        // literal values
};

Execution state is kept in zend_execute_data , which links stack frames together via prev_execute_data and holds the current opcode pointer.

struct _zend_execute_data {
    const zend_op *opline;          // current opcode
    zend_execute_data *call;        // call frame
    zval *return_value;
    zend_function *func;
    zval This;
    zend_class_entry *called_scope;
    zend_execute_data *prev_execute_data;
    zend_array *symbol_table;
    void **run_time_cache;
    zval *literals;
};

When a coroutine yields (e.g., during co::sleep() ), Swoole saves the current zend_execute_data and the VM stack pointers, then restores them when the coroutine is resumed.

PHP Coroutine Task Structure

struct php_coro_task {
    zval *vm_stack_top;   // top of PHP VM stack
    zval *vm_stack_end;   // bottom of PHP VM stack
    zend_vm_stack vm_stack; // pointer to the stack memory
    zend_execute_data *execute_data; // current PHP stack frame
    php_coro_task *origin_task; // previous coroutine (like prev_execute_data)
};

Key operations are:

create : allocate a new PHP stack frame and start execution.

yield : save execute_data and VM stack, then switch to another coroutine.

resume : restore the saved state and continue execution.

close : clean up the stack and associated resources.

C‑Stack Management (Assembly)

Swoole uses a custom make_fcontext / jump_fcontext implementation (derived from Boost) to save and restore the C stack.

// make_fcontext creates a new context on the given stack
fcontext_t make_fcontext(void *sp, size_t size, void (*fn)(intptr_t));

// jump_fcontext switches from *ofc to nfc, optionally preserving FPU state
intptr_t jump_fcontext(fcontext_t *ofc, fcontext_t nfc, intptr_t vp, bool preserve_fpu = false);

The assembly saves registers ( %rbp , %rbx , %r12‑%r15 ), the FPU state, and the return address, then restores them on the target context.

Coroutine API (C++)

// Create a coroutine with a C‑stack and entry function
long Coroutine::create(coroutine_func_t fn, void *args = nullptr);

// Switch out of the current coroutine
void Coroutine::yield();

// Switch into a coroutine
void Coroutine::resume();

// Destroy a coroutine
void Coroutine::close();

Integration with Zend VM (swoole_coroutine.cc)

The file provides glue functions that expose PHP‑level coroutine creation, yielding, and resuming to the underlying C‑stack implementation, handling VM stack initialization, saving/restoring, and output buffering.

Dual‑Stack Architecture

Swoole now maintains both a C stack (managed by Context ) and a PHP VM stack (managed by php_coro_task ). This design eliminates the previous limitations where only the PHP stack could be switched, enabling true native coroutine behavior in PHP.

Overall, the article gives a comprehensive view of how Swoole implements high‑performance, low‑overhead coroutines by bridging the PHP Zend VM with a custom C‑stack scheduler.

PHPCoroutineC++SwooleZendVM
Xueersi Online School Tech Team
Written by

Xueersi Online School Tech Team

The Xueersi Online School Tech Team, dedicated to innovating and promoting internet education technology.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.