swoole/swoole-src: coroutines are not CPU parallelism, and the README says so
🚀 Coroutine-based concurrency library for PHP
At a glance
- What is it?
- Swoole is a PHP extension whose C and C++ core provides an event loop, a coroutine scheduler, servers and clients, and its documentation is unusually blunt about the three things it will not do for you. Coroutines do not give you CPU parallelism. Workers are long lived, so state survives requests unless you manage it. And runtime hooks do not reach arbitrary C library calls.
- Who is it for?
- Use Swoole for services that hold many connections and spend their time waiting on I/O, because that is the shape the Reactor and worker model is built for and where it wins against one process per request. Do not use it expecting a CPU-bound job to speed up, since the README says plainly that a CPU-intensive loop occupies its worker until it yields, and the answer is more workers, task workers, or optional threads.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.
Star history
- swoole/swoole-src18,938 ★ · +21
| Repository | 2026-09-21 | 2026-11-13 |
|---|---|---|
| swoole/swoole-src | 18.9k | 18.9k |
21 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The README says coroutines are not CPU parallelism
There is a paragraph in the How Swoole works section that will save you a day if you read it before you build. Coroutines are a concurrency tool, not automatic CPU parallelism. A CPU-intensive PHP loop still occupies its worker until it yields or finishes. Use worker or task processes, or optional thread support, when work needs parallel execution.
That is three sentences, and each one closes a door. A loop that does arithmetic does not become faster because it sits inside a coroutine; it holds the worker for exactly as long as before, and every other coroutine in that worker waits with it. The escape routes are all structural: more worker processes, the separate task worker path, or threads, which the README later notes are optional and need a ZTS build of PHP.
The mechanism above it explains why the model works when it does. The Reactor watches sockets and other registered file descriptors and dispatches events to callbacks, and servers can run multiple worker processes to use multiple CPU cores. Within one worker, coroutines let other tasks run while a task waits for supported I/O. So the win is over waiting, and only over waiting.
Long-lived workers mean your globals outlive the request
The second blunt paragraph is about state, and it is the one that causes production surprises. Swoole workers are long-lived. Global variables, static state, and open resources can survive beyond one request.
The consequences follow directly. Code written for a request lifecycle, where the process starts, does one thing and exits, is now running in a process that keeps going, so a static cache populated on request one is still there on request nine hundred, and a file handle or a database connection opened once stays open. The README's advice is to close or recycle connections, enforce timeouts, and keep per-request data out of process-wide state.
Then the other half of the paragraph, which is the part people miss: ordinary PHP variables are not shared between worker processes. Use IPC, Table, or external storage when data must be shared. So you have two symmetric problems. Within a worker, state persists whether you want it or not. Between workers, state does not exist at all unless you put it somewhere, and the three named places are interprocess communication, the Table structure, or external storage.
The failure mode is quiet in both directions: a stale value on one worker, an empty one on another, and results that depend on which worker handled the request.
Runtime hooks do not reach arbitrary C library calls
The hook mechanism is the part that makes ordinary PHP code coroutine friendly, and it has a documented boundary. `Swoole\Runtime::enableCoroutine()` hooks selected blocking PHP functions so they can yield inside a coroutine. Depending on the hook flags, the build options, and the loaded PHP extensions, this can cover streams, files, sleep functions, cURL, and supported database operations.
Three inputs decide what you get: flags, build options, and which extensions are loaded. Then the sentence that ends the discussion of automatic behaviour: hooks do not transform arbitrary third-party C library calls. Use the explicit coroutine client or Socket APIs when precise I/O behaviour matters.
The same conditional appears on the database side. For database access, combine the appropriate PHP driver with supported runtime hooks, or use the bundled library's PDO, MySQLi and Redis connection pools, and the text is explicit that a pool manages connection reuse while whether an operation yields depends on the driver and hook configuration. So a connection pool gives you reuse, not a concurrency guarantee, and two applications with the same pool can behave differently because one has a driver installed that the other does not.
What you can rely on is the explicit surface: `Swoole\Coroutine\Client`, `Swoole\Coroutine\Http\Client`, `Swoole\Coroutine\Socket` and `Swoole\Coroutine\System`, which are coroutine aware by construction rather than by hook.
master is the 6.3 development branch, and production is pointed at a release
The README carries a version note before anything else, and it is unusually specific. The master branch is the development branch for Swoole 6.3 and currently supports PHP 8.2 through 8.5. For production, choose a released version and check the supported versions document for its PHP compatibility and maintenance status.
So there is a supported-versions document, and it is a separate file at docs/SUPPORTED.md, which means the compatibility table lives outside both the README and the changelog. The recent releases are v6.2.4, v6.1.11 and v6.3.0-rc1, and the release candidate is consistent with master being 6.3.
The practical effect is three different answers to the question of what version you are running. A tag tells you the release, the branch tells you the development line, and the supported versions document tells you which PHP each one is compatible with and how it is maintained. A team that clones master by default is running the 6.3 development line, and the README tells that team to choose a released version for production.
The repository has an agents file at the root, and the last push was 2026-11-02, so the branch itself is current.
The Dockerfile exists to catch crashes, and it turns on the address sanitizer
The container definition says what it is for in its first comment: it is designed to create a debug version of the PHP and Swoole environment, enabling ASAN, the address sanitizer, to facilitate debugging and analysis of runtime crashes.
What it does is build PHP from source. The base is ubuntu:22.04, a build argument defaults the PHP version to 8.2.35, the source tarball is downloaded from php.net and extracted, the repository is copied into the extension directory inside that source tree, and then buildconf is run followed by a configure line with a long list of switches. The Swoole specific ones are the interesting part: separate flags for cURL, pgsql, sqlite, mysqlnd, c-ares, brotli, and ODBC with an explicit unixODBC path.
After the build it runs the version, the module list, and the reflection output for swoole, curl and openssl, and only then cleans the source tree and the apt lists.
Two lessons. First, the optional capabilities are configure-time switches, which is the same reason the feature text says compression and TLS depend on the relevant build options and environment: a capability that is not compiled in is not a runtime setting. Second, this image is a diagnostic tool, not a deployment image, and the fact that the project ships a sanitized build is a reasonable signal about how it expects memory bugs in the C core to be found.
Four server classes, and the choice is about lifecycle not protocol
There is a table that maps a requirement to a starting point, and it is the fastest way to understand the API surface. A custom TCP or UDP protocol, multiple ports, or task workers starts from `Swoole\Server`. An HTTP API with request callbacks and worker processes starts from `Swoole\Http\Server`. An HTTP service controlled from a coroutine starts from `Swoole\Coroutine\Http\Server`. WebSocket handshake and bidirectional frames start from `Swoole\WebSocket\Server`.
The pattern is that the same protocol can have two classes, and the difference is how the lifecycle is driven. The request callback model hands you a request and a response and runs it on a worker; the coroutine model lets you start the server inside a coroutine and drive it yourself. `Swoole\Server` provides the pieces a raw protocol needs: multiple listening ports, worker and task worker processes, connection management, heartbeats, and configurable packet boundaries such as length fields or EOF markers.
Within that, `task()` dispatches work to task workers and `sendMessage()` sends between workers, and `Swoole\Event` lets an application register its own file descriptors with the event loop. The examples directory mirrors the table with one directory per protocol concern, including dtls, eof, length, socket_coro and task, which tells you which of these are the fiddly ones.
A Table is hot in-memory state, and threads need a ZTS build
The shared data story has three mechanisms. `Swoole\Table` holds structured data shared between processes, and atomic values and messages support coordination. The guidance is that a table is useful for small, frequently accessed state, and that you should use a database for persistence or complex queries.
So the table is a fast shared dictionary, not storage. It holds what is hot and small, which is the right shape for counters, presence and routing state, and the wrong shape for anything you need to query or survive a restart. Paired with the earlier statement that ordinary PHP variables are not shared between workers, the division of labour is complete: per request inside one worker, in memory in a table across workers, on disk or over the network when it has to persist or be queried.
Threads are the last mechanism and the most conditional. The thread APIs are optional and require a ZTS build of PHP, and the instruction is to use Swoole's thread-safe containers for data shared by threads rather than assuming ordinary PHP variables behave that way. That is a compiler flag difference between two builds of the same extension, and it is easy to miss in a deployment checklist.
Two more optional integrations round it out: c-ares for DNS resolution and io_uring for additional asynchronous I/O paths on Linux, both opt-in, both of which change what the event loop can do without changing your code.
Editorial conclusion
Use Swoole for services that hold many connections and spend their time waiting on I/O, because that is the shape the Reactor and worker model is built for and where it wins against one process per request. Do not use it expecting a CPU-bound job to speed up, since the README says plainly that a CPU-intensive loop occupies its worker until it yields, and the answer is more workers, task workers, or optional threads. Check four things before you deploy: which branch you are on, because master is the 6.3 development branch and production is directed to a released version with its compatibility listed in docs/SUPPORTED.md; which PHP you run, since master supports 8.2 through 8.5; which build options you compiled in, because compression, TLS, c-ares DNS, io_uring, ODBC, brotli, pgsql, sqlite and cURL support are all configure-time switches; and what lives in process-wide state, because long-lived workers mean your globals and static values outlive the request that set them. The license is Apache-2.0 and the last push was 2026-11-02.
Frequently asked questions
Does a coroutine in swoole make my PHP code run in parallel?
No, and the README states it directly: coroutines are a concurrency tool, not automatic CPU parallelism. A CPU-intensive PHP loop still occupies its worker until it yields or finishes, and the guidance is to use worker or task processes, or optional thread support, when work needs parallel execution.
Is it safe to keep state in a swoole worker between requests?
Not without managing it. Swoole workers are long-lived, so global variables, static state and open resources can survive beyond one request. The README advises closing or recycling connections, enforcing timeouts, and keeping per-request data out of process-wide state, and notes that ordinary PHP variables are not shared between worker processes at all.
What PHP versions does swoole-src support?
The master branch is the development branch for Swoole 6.3 and currently supports PHP 8.2 through 8.5. For production, the README says to choose a released version and check the supported versions document, docs/SUPPORTED.md, for its PHP compatibility and maintenance status. The recent releases are v6.2.4, v6.1.11 and v6.3.0-rc1.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/swoole-swoole-src)Star history chart for your README
[](https://hysenlabs.com/projects/swoole-swoole-src)Was this useful?