Skip to content

Commit 6248191

Browse files
gzj-creatorGLfutureclaude
authored
feat(concurrentqueue): add compat.concurrentqueue 1.0.5 (#278)
moodycamel::ConcurrentQueue — the lock-free MPMC queue, plus its blocking sibling and a flat C API over both. Shape B (header-only): the three public headers sit at the tarball root, so `include_dirs = {"*"}` plus an anchor TU is the package; consumers write `#include <concurrentqueue.h>` exactly as upstream's README shows. The one compilable optional component — `c_api/`'s two extern "C" wrapper TUs — sits behind a default-off `c-api` feature (the compat.cjson `utils` precedent). Windows needs `MOODYCAMEL_STATIC` on BOTH sides: the header defaults to `__declspec(dllimport)`, which is wrong for the package's own TUs (they define the functions, and `cflags` never reaches a .cpp — hence `cxxflags`) and for a static-archive consumer (hence the feature's interface `defines`). The macro is only read under `#ifdef _WIN32`, so defining it everywhere is inert on linux/macos. Verified in the emitted compile_commands.json that all four TUs carry it. Tests: tests/examples/concurrentqueue asserts FIFO order, bulk round-trips, move-only elements, a blocking consumer that REALLY waits (empty-queue wait_dequeue_timed must burn its 50ms timeout), and a 4x4 MPMC race checked for exact-once delivery. tests/examples/concurrentqueue-c-api consumes the feature and round-trips integers through the void* C API. Both members register in the workspace manifest. Verified locally with mcpp 2026.8.27.1 (CI pins 2026.8.27.2; the release CDN times out from this host): both members green from cold; the gate is real — a TU referencing moodycamel_cq_create against the default build fails to LINK (undefined reference), and the default build's object dir holds the anchor alone. No CN mirror: no GITCODE_TOKEN on this host, so the descriptor uses the plain-string upstream url fallback and the descriptor catalog entry lands in docs/descriptor-examples.md (+ zh), per the post-#277 README layout. Co-authored-by: gong <gzj17607270093@163.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 27b5c28 commit 6248191

9 files changed

Lines changed: 520 additions & 2 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# 收录 concurrentqueue 1.0.5(compat.concurrentqueue)
2+
3+
日期:2026-08-29 · 分支:`feat/add-concurrentqueue` · 状态:本地验证通过
4+
5+
## 1. 来源与形态判定
6+
7+
concurrentqueue 属于来源 (a):第三方上游库,上游不提供 mcpp 支持。
8+
9+
- 上游:<https://github.com/cameron314/concurrentqueue>(Cameron Desrochers;
10+
`moodycamel::ConcurrentQueue`)。
11+
- 最新版本:`git ls-remote --tags` 排序后最大的是 **`v1.0.5`**(本地工作副本 checkout 在
12+
v1.0.5 之后 6 个 commit,未成 tag,不予采用;版本键用裸版本 `"1.0.5"`,下载 URL 保留
13+
上游的 `refs/tags/v1.0.5` 拼写)。
14+
- License:**双许可** —— 文件头声明 Simplified BSD(BSD-2-Clause)或 Boost 软件许可
15+
(BSL-1.0)二选一,故 `licenses = {"BSD-2-Clause", "BSL-1.0"}`(compat.libarchive 已有
16+
多值先例)。
17+
- 源码布局:`concurrentqueue-1.0.5/` 包一层。库本体是 tarball ****下三个头:
18+
`concurrentqueue.h`(核心)、`blockingconcurrentqueue.h`(阻塞变体,依赖
19+
`lightweightsemaphore.h`)、`internal/concurrentqueue_internal_debug.h`(调试头)。
20+
另有 `c_api/`(2 个 `.cpp` + 1 个 `extern "C"` 头)、`tests/``benchmarks/`
21+
22+
**形态 = B(header-only)**,叠加一个 source-gated 的 `c-api` feature。
23+
24+
## 2. 版本与下载源
25+
26+
`sha256 = 4d6368a27492d86011fde5ca0cf386dce7c49cd425aa3d9b063ca6ec373a6ef3`
27+
(1567167 字节,连算两次一致)。GitHub 的 tag 归档字节稳定,可直接作 GLOBAL。
28+
29+
## 3. CN 镜像
30+
31+
本机**没有** `GITCODE_TOKEN`(gitcode 公开读 API 可用,但建仓/release 需要写权限)。
32+
`docs/cn-mirror.md` 的回退方案:描述符采用**纯字符串 url**(只填上游 release),
33+
lint 的 `check_mirror_urls.lua` 对纯字符串不施加镜像约束,CN 用户回退上游源,
34+
镜像由维护者后续补充(`mcpp-res` 组织下尚无 `concurrentqueue` 仓)。
35+
36+
## 4. 实现决策
37+
38+
### 4.1 `include_dirs = {"*"}`:公开头就在 tarball 根
39+
40+
三个公开头与 LICENSE、CMakeLists 并列在归档顶层,没有 `include/` 一层可挑
41+
(compat.gtl 的「只取 `*/include`」在这里无处安放),`*` 即上游 README 让你放进
42+
include 路径的那个目录。anchor TU 照 compat.eigen / compat.plf-hive 先例,
43+
给 mcpp 一个可构建的 lib 目标。
44+
45+
一个需要点破的名字重叠:根下的 `concurrentqueue.h`(C++ 主头)与
46+
`c_api/concurrentqueue.h`(C API 头)**同名**。它不构成遮蔽危害,因为 include 路径上
47+
只有归档根一个目录 —— `<concurrentqueue.h>` 永远命中前者,
48+
`<c_api/concurrentqueue.h>` 是唯一能命中后者的拼法,两条路径互不混淆。
49+
50+
### 4.2 `c-api` feature:包里唯一可编译的可选组件
51+
52+
feature 准则(「该组件是否为额外的可编译源码」)在本包只有一个命中:`c_api/` 的两个
53+
`.cpp`(把两个队列封装成 `moodycamel_cq_*` / `moodycamel_bcq_*` 平面 C 接口)。
54+
默认关闭,请求后编进同一个 `concurrentqueue` lib 目标(compat.cjson 的 `utils` 先例)。
55+
56+
**其余一概不收**:`tests/``benchmarks/` 自带 `main()`,lib 目标的对象会急切进入消费者
57+
链接,包里不能带 main(compat.libaio 同一推理);`unsupported` 一类纯头内容不存在 ——
58+
这库没有纯头形式的可选项,因此没有「无从门控」的遗憾。
59+
60+
### 4.3 Windows 的 `MOODYCAMEL_STATIC`:`cxxflags` 与 feature `defines` 各管一半
61+
62+
`c_api/concurrentqueue.h` 自己挑 `MOODYCAMEL_EXPORT` 的拼法:未定义
63+
`MOODYCAMEL_STATIC`/`DLL_EXPORT` 时按 **DLL 客户端**处理,全部函数声明成
64+
`__declspec(dllimport)`。这在两边同时出错:
65+
66+
- 包自身的 `.cpp`**定义**这些函数 —— 定义一个 dllimport 函数是硬错误;
67+
- 静态库消费者本要链接符号,却被声明成导入。
68+
69+
于是包级 `cxxflags = { "-DMOODYCAMEL_STATIC" }` 覆盖包自身 TU(c_api 两个 TU 全是
70+
C++,`cflags` 到不了 —— compat.msdfgen 的教训),`c-api` feature 的
71+
`defines = { "MOODYCAMEL_STATIC" }` 覆盖消费者 TU(Feature System v2 的接口 define)。
72+
该宏只在 `#ifdef _WIN32` 内被读取,linux/macos 上处处定义等于无效操作,故不按平台拆分。
73+
已核对本地产出的 `compile_commands.json`:四个 TU(两个 c_api 源、anchor、消费者测试)
74+
都带上了 `-DMOODYCAMEL_STATIC`
75+
76+
### 4.4 Linux 侧无需任何 `-D`/`-l`
77+
78+
`lightweightsemaphore.h` 在 Linux 走 POSIX `sem_t`(`<semaphore.h>`),不碰 futex
79+
syscall,也就不触发 compat.libaio 踩过的 `__STRICT_ANSI__` 藏符号问题;
80+
`_GNU_SOURCE` 在该头里只控制一个 monotonic-clock 优化分支,不开只是退化不报错。
81+
`sem_*` 自 glibc 2.34 起在 libc 本体,本机验证未链 -lpthread 即通过。
82+
83+
## 5. 测试成员
84+
85+
- `tests/examples/concurrentqueue`(默认构建):单线程 FIFO、bulk 往返、ProducerToken、
86+
move-only 元素、阻塞队列(空队列 `wait_dequeue_timed`**真的等满** 50ms 才返回
87+
false —— 把「信号量提前 post」与「真等待」区分开的断言)、4 生产 × 4 消费的 MPMC
88+
竞争,逐值核验「恰好收到一次」(丢失/重复/ABA 都会表现为计数 ≠1)。
89+
- `tests/examples/concurrentqueue-c-api`(`features = ["c-api"]`):C API 的
90+
create/enqueue/try_dequeue/size_approx/destroy 与 bcq 的 wait_dequeue 顺序性;
91+
值以整数经 `void*` 往返,校验和必须分毫不差 —— 这正是「句柄类型装错队列」会暴露的地方。
92+
- 依赖一律**限定拼写** `[dependencies.compat]`(eigen 成员的推理:裸名会按请求的命名空间
93+
去公开远端索引解析,静默脱离本 checkout)。
94+
- 两个成员都已登记进根 `mcpp.toml``[workspace].members`
95+
96+
编写期自摆乌龙两处,恰好证明断言可失败:`wait_dequeue` 在 1.0.5 返回 `void`(只有
97+
`_timed` 拼写报告成败);`enqueue_bulk` 返回 `bool`(只有 `try_dequeue_bulk` 返回数量)。
98+
99+
## 6. 验证结论
100+
101+
mcpp 用**本地已有**的 2026.8.27.1(CI 锁 2026.8.27.2,差一个 patch;宿主到
102+
github.com 主站可达、release CDN 超时,CI 绿灯为最终裁决):
103+
104+
- `mcpp test -p concurrentqueue``test result ok`(冷启动,自干净 target/.mcpp);
105+
- `mcpp test -p concurrentqueue-c-api``test result ok`(独立重编包,feature 生效);
106+
- **负向验证**:引用 `moodycamel_cq_create` 的 TU 链接默认构建的包对象 →
107+
`undefined reference to moodycamel_cq_create`,证明门控真实;默认构建的包对象目录里
108+
也只有 `concurrentqueue_anchor.o`,无任何 `c_api/*.o`;
109+
- 定位/查找:`include_dirs = {"*"}``<concurrentqueue.h>` 命中根主头,
110+
`<c_api/concurrentqueue.h>` 命中 C API 头,同名不冲突。
111+
112+
## 7. 与 skill 流程的两处偏差
113+
114+
- **README 未改**:#277 之后描述符目录已从 README 移入 `docs/descriptor-examples.md`,
115+
README 只留每形态一行的示例表(该表无需新增行)。本 PR 按 #277 后的现状,把条目写进
116+
`docs/descriptor-examples.md``docs/zh/descriptor-examples.md`
117+
「header-only(with features)」行。
118+
- **mcpp 版本**:本地 2026.8.27.1 vs CI 2026.8.27.2,见第 6 节。

docs/descriptor-examples.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ in the [root README](../README.md#reference-examples).
2020
| C++-source compat, zero-dep client + optional components | [`compat.websocket`](../pkgs/c/compat.websocket.lua) (IXWebSocket 12.0.1 — a pure RFC 6455 client compiled from upstream's `IXWEBSOCKET_SOURCES` minus the four server TUs, so the **base build has zero external dependencies**: TLS off (the OpenSSL/MbedTLS/AppleSSL TUs aren't built) and `IXWEBSOCKET_USE_ZLIB` unset, so the gzip codec compiles to a no-op. Two optional features add on top: `server` (the four server TUs — `IXWebSocketServer`, `IXSocketServer`, `IXHttpServer`, `IXWebSocketProxyServer` — needing nothing external, and it **implies `zlib`** because upstream's server advertises permessage-deflate by default, which the transport negotiates regardless of the define) and `zlib` (deps `compat.zlib` and turns the codec into real per-message-deflate compression). The default-feature test brings its own minimal RFC 6455 echo server on loopback sockets (handshake, masking, fragmentation and close all exercised offline); a second member, `websocket-features`, runs a real `ix::WebSocketServer` and asserts the compression is observable on the wire — a 64 KiB repeated payload round-trips with `wireSize` = 80) |
2121
| Database client + the driver manager it needs, built from source | [`compat.nanodbc`](../pkgs/c/compat.nanodbc.lua) (nanodbc 2.14.0, frozen upstream — one TU over the platform ODBC driver manager. Two fixes make the four-year-old source compile and RUN here: a force-included `char_traits<unsigned char>` shim for libc++ (the standard's own customization point, guarded on `_LIBCPP_VERSION` so libstdc++/MSVC are untouched; and note `-include` reaches C++ TUs only through `cxxflags`, never `cflags`), and a per-platform answer to the manager itself — windows links the SDK's odbc32, macOS the OS's iODBC, while linux takes `compat.unixodbc` because mcpp's runtime closure rejects a NEEDED `libodbc.so.2` that only the host has. The test asserts the manager's own diagnostics surface through the wrapper — including nanodbc's frozen off-by-one that drops the last SQL-state character) · [`compat.unixodbc`](../pkgs/c/compat.unixodbc.lua) (unixODBC 2.3.14, Shape E over A — DM + odbcinst + ini/log/lst + libltdl compiled statically into one `odbc` target, exactly upstream's libodbc.a symbol set, so the consumer carries no `libodbc.so.2` NEEDED at all. The one non-obvious piece is libtool-free ltdl wiring: `-DLTDLOPEN=libltdlc` plus a generated `lt_libltdlc_LTX_preloaded_symbols` table (reconstructed from the libtool object's relocations) registers the dlopen loader. The frozen `config.h` merges ltdl's own configure output into the top-level one — ltdl sources never read the clashing identification macros, and the merge sidesteps a quoted `-DLT_CONFIG_H` that does not survive the pipeline. Verified against the libtool build of the same tarball: identical IM002 error path and identical `lt_dlopen` behaviour) |
2222
| C-source compat, an ISA tier turned off through the GENERATED config | [`compat.libwebp`](../pkgs/c/compat.libwebp.lua) (117 TUs as five directory globs rather than a transcribed file list, and one real decision. libwebp's SSE4.1 gate is `(__SSE4_1__ || WEBP_MSC_SSE41) && (!HAVE_CONFIG_H || WEBP_HAVE_SSE41)`, and `WEBP_MSC_SSE41` keys off `_MSC_VER` **alone** — which every MSVC-ABI compiler defines, including clang, while only cl.exe lets an intrinsic be used without a target flag. Under clang the SSE4.1 sources fail on `always_inline function '_mm_shuffle_epi8' requires target feature 'ssse3'`. Upstream answers with a PER-FILE `-msse4.1`, which no descriptor field expresses; adding it package-wide would let clang emit SSE4.1 in the BASELINE TUs too, past libwebp's own runtime dispatch — a SIGILL rather than a fallback. So the package uses the other half of upstream's own mechanism: `HAVE_CONFIG_H` plus a generated `src/webp/config.h` naming SSE2 and NEON and not SSE4.1, which turns `dec_sse41.c` and friends into upstream's `WEBP_DSP_INIT_STUB` and removes the matching `VP8DspInitSSE41()` call. `src/demux` and `src/mux` are separate upstream libraries with their own public headers and stay out until something asks for them) |
23-
| header-only (with `features`) | [`compat.eigen`](../pkgs/c/compat.eigen.lua) |
23+
| header-only (with `features`) | [`compat.eigen`](../pkgs/c/compat.eigen.lua) · [`compat.concurrentqueue`](../pkgs/c/compat.concurrentqueue.lua) (moodycamel's lock-free MPMC queue 1.0.5 — three public headers at the tarball root, so `*` plus an anchor TU is the package. The ONE compilable optional component, `c_api/`'s two `extern "C"` wrapper TUs, sits behind a `c-api` feature, and the Windows story is why the descriptor carries `cxxflags = {"-DMOODYCAMEL_STATIC"}` beside the feature's `defines`: the header defaults to `__declspec(dllimport)` unless told otherwise, which is wrong for both the package's own TUs (they DEFINE the functions — and `cflags` would never reach a `.cpp`) and a static-archive consumer; the macro is read only inside `#ifdef _WIN32`, so defining it everywhere is inert on linux/macos. Everything else upstream ships outside the library is either header-only (nothing to gate) or carries a `main()`/benchmarks, which a lib target cannot take — its objects enter the consumer's link eagerly) |
2424
| header-only, nothing to gate | [`compat.CLI11`](../pkgs/c/compat.CLI11.lua) (a command line parser whose every definition is `CLI11_INLINE`, so the package is `*/include` plus an anchor TU. Upstream's two extras stay out: `src/Precompile.cpp` only means anything when `CLI11_COMPILE` also reaches the CONSUMER's translation units — an interface define, not a sources-only gate — and `src/modules/CLI11.cppm` is a module layer, which is a package shape of its own rather than a feature of the compat package) · [`compat.gtl`](../pkgs/c/compat.gtl.lua) (Greg's Template Library — the Swiss-table `flat_hash_map` family plus btrees and a bit_vector. `*/include` exactly, not the tarball root: `tests/` and `examples/` carry headers of their own, and naming `include/` is what upstream's INTERFACE target exposes, so a consumer cannot accidentally resolve into test code) · [`compat.plf-hive`](../pkgs/c/compat.plf-hive.lua) (the reference implementation of the proposed `std::hive`; the whole library is one file at the tarball root, so `*` plus an anchor TU is the entire package. Untagged upstream, so the version is a DATE over a commit archive — the compat.khrplatform precedent) · [`compat.wil`](../pkgs/c/compat.wil.lua) (the Windows Implementation Library — RAII over Win32 handles, COM pointers and HRESULT. Windows-ONLY in an unusual sense: not a portable library with a Windows backend, but a library ABOUT Win32, so there are no other platform sections to declare and consumers gate the dependency with `[target.'cfg(windows)'.dependencies]` — compat.x11 and the gui-stack member in the other direction. Nothing is pre-configured: WIL's knobs (`WIL_ENABLE_EXCEPTIONS`, `RESULT_DIAGNOSTICS_LEVEL`, `WIL_USE_STL`) are macros the CONSUMER defines before including, and a header-only package has no compiled artifact for such a choice to be baked into anyway — pre-setting one would pick an error model on its consumers' behalf) |
2525
| single-header library + a GENERATED implementation TU | [`compat.nanosvg`](../pkgs/c/compat.nanosvg.lua) (two stb-style headers where the implementation hides behind `NANOSVG_IMPLEMENTATION` / `NANOSVGRAST_IMPLEMENTATION`. Upstream ships no `.c` — its examples define the macros inline — so the package generates one that instantiates BOTH halves once. That is what turns a header drop into something linkable, and it moves the duplicate-symbol hazard from every consumer to a single place: consumers must NOT define those macros again, and the test links `nsvgParse` and `nsvgRasterize` together precisely so a package that instantiated only one half fails here rather than downstream) · [`compat.vulkan-memory-allocator`](../pkgs/c/compat.vulkan-memory-allocator.lua) (VMA 3.4.0, same shape but the generated TU also has to make a POLICY choice. VMA defaults to `VMA_STATIC_VULKAN_FUNCTIONS 1`, which references `vkBindBufferMemory2` and seven siblings by name — eight undefined symbols against a headers-only dep. Pulling `compat.vulkan` to satisfy them would force a Vulkan loader on every consumer of a memory allocator and fight anyone dispatching through volk, so the generated TU selects the dynamic path instead and VMA resolves everything through `VmaVulkanFunctions`. Note the implementation is C++ despite the C-shaped API, so the generated file is `.cpp`) |
2626
| Runtime loader compat (pure sources, sidestepping upstream codegen/asm) | [`compat.vulkan`](../pkgs/c/compat.vulkan.lua) (the Khronos loader: `loader/generated/` is checked in, and the assembly path degrades to plain C through `UNKNOWN_FUNCTIONS_SUPPORTED`, so no CMake/Python/assembler is needed; windows deferred) · [`compat.vulkan-headers`](../pkgs/c/compat.vulkan-headers.lua) |

0 commit comments

Comments
 (0)