Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Lock-free inter-thread message queue primitive (#81): bounded MPSC/SPSC C-ABI
wrappers over moodycamel (`thread_queue`) plus a reactor-integrated MPSC mailbox
(`thread_mailbox`) that wakes the consumer's loop via a trigger event with
lost-wakeup-safe batch drain. Foundation for cross-worker HTTP/3 (#72) and
WebSocket (#2). Adds a C++ build dependency (libstdc++).

### Fixed

- **Windows: TCP listeners now bind.** The server failed to start on Windows
Expand Down
10 changes: 9 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
cmake_minimum_required(VERSION 3.22)
project(php_http_server C)
# C++ is needed for the moodycamel-backed inter-thread queue (issue #81,
# src/core/thread_queue.cc); everything else is C.
project(php_http_server C CXX)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# PHP source root (used for include dirs and, on Windows, the import lib).
set(PHP_INCLUDE_DIR "E:/php/php-src" CACHE PATH "PHP source directory")
Expand Down Expand Up @@ -65,6 +69,9 @@ set(CORE_SUBSYSTEM_SOURCES
src/core/http_protocol_strategy.c
src/core/http_known_strings.c
src/core/async_plain_event.c
# Inter-thread message queue (issue #81): C++ moodycamel wrapper + C reactor glue.
src/core/thread_queue.cc
src/core/thread_mailbox.c
)

# TLS sources — only compiled when OpenSSL is present. Both files
Expand Down Expand Up @@ -365,6 +372,7 @@ target_include_directories(php_true_async_server PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/core
${CMAKE_CURRENT_SOURCE_DIR}/deps/llhttp/include
${CMAKE_CURRENT_SOURCE_DIR}/deps/concurrentqueue
)

# PHP headers — added as SYSTEM so the compiler suppresses their warnings.
Expand Down
34 changes: 32 additions & 2 deletions config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,32 @@ if test "$PHP_HTTP_SERVER" != "no"; then
AC_DEFINE(HAVE_COVERAGE, 1, [Whether code coverage is enabled])
fi

dnl Inter-thread message queue (issue #81) wraps the moodycamel C++ headers
dnl (deps/concurrentqueue), so the extension needs a C++ toolchain and links
dnl libstdc++. The single C++ TU is src/core/thread_queue.cc.
PHP_REQUIRE_CXX()

dnl Pin a modern C++ standard. The PHP/autoconf build does not set one, so
dnl the compiler's default is used — fine for recent g++ (>= C++14) but
dnl AppleClang and older toolchains can default to C++98, which moodycamel
dnl rejects. CXXFLAGS only reaches the C++ TU (the C sources use CFLAGS), so
dnl this never touches the C build. Probed newest-first so an unusual
dnl compiler degrades to whatever it supports instead of failing outright.
_http_server_saved_cxxflags="$CXXFLAGS"
_http_server_cxxstd=""
AC_LANG_PUSH([C++])
for _http_server_std in -std=gnu++17 -std=c++17 -std=gnu++14 -std=c++14 -std=gnu++11 -std=c++11; do
CXXFLAGS="$_http_server_saved_cxxflags $_http_server_std"
AC_MSG_CHECKING([whether $CXX accepts $_http_server_std])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [])],
[AC_MSG_RESULT([yes])
_http_server_cxxstd="$_http_server_std"
break],
[AC_MSG_RESULT([no])])
done
AC_LANG_POP([C++])
CXXFLAGS="$_http_server_saved_cxxflags $_http_server_cxxstd"

dnl Define source files
dnl Phase 1: HTTP/1.1 Parser
dnl Phase 2: Server classes
Expand All @@ -464,6 +490,8 @@ if test "$PHP_HTTP_SERVER" != "no"; then
src/core/conn_arena.c
src/core/body_pool.c
src/core/async_plain_event.c
src/core/thread_queue.cc
src/core/thread_mailbox.c
src/http1/http_parser.c
src/http1/http1_stream.c
src/http1/http1_sendfile.c
Expand Down Expand Up @@ -585,14 +613,16 @@ if test "$PHP_HTTP_SERVER" != "no"; then
done
CFLAGS="$SAVE_CFLAGS"

dnl Create extension
PHP_NEW_EXTENSION(true_async_server, $http_server_sources, $ext_shared,, -Wall -Wextra -Wno-unused-parameter $HTTP_SERVER_HARDENING)
dnl Create extension. The trailing "cxx" arg makes the shared module link
dnl through $(CXX) so the C++ TU's runtime (libstdc++) is pulled in.
PHP_NEW_EXTENSION(true_async_server, $http_server_sources, $ext_shared,, -Wall -Wextra -Wno-unused-parameter $HTTP_SERVER_HARDENING, cxx)
PHP_SUBST(TRUE_ASYNC_SERVER_SHARED_LIBADD)

dnl Add include paths
PHP_ADD_INCLUDE([$ext_srcdir/include])
PHP_ADD_INCLUDE([$ext_srcdir/src])
PHP_ADD_INCLUDE([$ext_srcdir/src/core])
PHP_ADD_INCLUDE([$ext_srcdir/deps/concurrentqueue])
PHP_ADD_BUILD_DIR([$ext_builddir/src])
PHP_ADD_BUILD_DIR([$ext_builddir/src/core])
PHP_ADD_BUILD_DIR([$ext_builddir/src/http1])
Expand Down
8 changes: 8 additions & 0 deletions config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ if (PHP_TRUE_ASYNC_SERVER == "yes") {
"conn_arena.c " +
"body_pool.c " +
"async_plain_event.c " +
"thread_queue.cc " +
"thread_mailbox.c " +
"http_connection.c " +
"http_connection_tls.c " +
"http_protocol_handlers.c " +
Expand Down Expand Up @@ -99,6 +101,8 @@ if (PHP_TRUE_ASYNC_SERVER == "yes") {
ADD_FLAG("CFLAGS_TRUE_ASYNC_SERVER", "/I " + configure_module_dirname + "/src");
ADD_FLAG("CFLAGS_TRUE_ASYNC_SERVER", "/I " + configure_module_dirname + "/src/core");
ADD_FLAG("CFLAGS_TRUE_ASYNC_SERVER", "/I " + configure_module_dirname + "/deps/llhttp/include");
// moodycamel inter-thread queue headers (issue #81), consumed by thread_queue.cc.
ADD_FLAG("CFLAGS_TRUE_ASYNC_SERVER", "/I " + configure_module_dirname + "/deps/concurrentqueue");
// static/ files include each other by short name (e.g. "static/http_static_safety.h")
ADD_FLAG("CFLAGS_TRUE_ASYNC_SERVER", "/I " + configure_module_dirname + "/include/static");

Expand All @@ -119,6 +123,10 @@ if (PHP_TRUE_ASYNC_SERVER == "yes") {
// this is a tooling limitation, not a real bug.
ADD_FLAG("CFLAGS_TRUE_ASYNC_SERVER", "/W4 /GS /sdl /guard:cf /wd4701 /wd4703 /std:c11");

// /EHsc enables C++ exception unwinding for the one C++ TU (thread_queue.cc,
// issue #81); moodycamel and operator new can throw and the wrapper catches.
ADD_FLAG("CFLAGS_TRUE_ASYNC_SERVER", "/EHsc");

// OpenSSL (required for TLS layer inside the extension — matches config.m4
// default of --with-openssl=yes).
if (CHECK_HEADER_ADD_INCLUDE("openssl/ssl.h", "CFLAGS_TRUE_ASYNC_SERVER", PHP_PHP_BUILD + "\\include")
Expand Down
29 changes: 29 additions & 0 deletions deps/concurrentqueue/LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
This vendored code is from the moodycamel concurrent-queue family by
Cameron Desrochers. It is dual-licensed; TrueAsync Server uses it under the
Simplified BSD license below.

## Simplified BSD license

Copyright (c) 2013-2020, Cameron Desrochers.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

- Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The code is also dual-licensed under the Boost Software License v1.0.
36 changes: 36 additions & 0 deletions deps/concurrentqueue/UPSTREAM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# moodycamel queues — bundled dependency

Vendored header-only lock-free queues by Cameron Desrochers, selected by the
micro-benchmark in issue #81. Used to build the inter-thread message primitives
in `src/core/thread_queue.cc` (C-ABI) and `src/core/thread_mailbox.c` (reactor
wakeup).

| File | Upstream | Role |
|---|---|---|
| `concurrentqueue.h` | https://github.com/cameron314/concurrentqueue | MPSC (used as multi-producer, single-consumer) |
| `readerwriterqueue.h` | https://github.com/cameron314/readerwriterqueue | SPSC fast lane |
| `atomicops.h` | https://github.com/cameron314/readerwriterqueue | atomics + semaphore helpers required by `readerwriterqueue.h` |

| Field | Value |
|---|---|
| Retrieved | 2026-06-04 (latest `master` of each repo) |
| License | Simplified BSD / Boost (dual) — see `LICENSE.md` |

## Why these two

Per the #81 benchmark (`~/qbench/`, i7-11700K, payload = pointer, cap = 4096):

- **MPSC → ConcurrentQueue.** The only ready-made queue that holds throughput as
producers contend (56–68 M ops/s at P=1..8). Every bounded array + CAS-on-shared-index
design (rigtorp MPMCQueue, `ck_ring` MPMC) collapses ~30× under producer contention
(2.3 M ops/s at P=8). moodycamel sidesteps it with per-producer sub-queues.
- **SPSC → ReaderWriterQueue.** rigtorp SPSCQueue is marginally faster but its repo is
frozen (~2.5 years). RWQ is from the same author, actively maintained, and within range.

Both are used in bounded mode (`try_enqueue`, no growth) behind an explicit length cap
maintained in `thread_queue.cc` — see that file.

## Updating

Header-only: drop in the new `concurrentqueue.h` / `readerwriterqueue.h` / `atomicops.h`
from upstream, review the diff, and bump the "Retrieved" date above.
Loading
Loading