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
76 changes: 76 additions & 0 deletions .agents/docs/2026-10-28-add-concurrentqueue-mwrap-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Add mcpplibs.mwrap.concurrentqueue (concurrentqueue-m 1.0.5)

## Form

- Third-party upstream [cameron314/concurrentqueue](https://github.com/cameron314/concurrentqueue), tag `v1.0.5`. It
is available under the Simplified BSD license or the Boost Software License; its semaphore is under zlib. The
tarball (`4d6368a2…6ef3`) is the one compat.concurrentqueue 1.0.5 already pins.
- Form A external mwrap module repository: [mcpplibs/concurrentqueue-m](https://github.com/mcpplibs/concurrentqueue-m),
package `mcpplibs.mwrap.concurrentqueue`. The version is the upstream one (first release of the identity, no
revision suffix); tag series `mwrap-${version}`.
- Implementation ownership: the headers, and the C API sources with the `c-api` feature, are compat.concurrentqueue's.
The wrapper vendors no upstream code. It retains upstream's README and license under `upstream/concurrentqueue/`,
with checksums in `upstream/documents.json`.

## Modules

- `mcpplibs.mwrap.concurrentqueue` exports the upstream entities of `concurrentqueue.h`, `blockingconcurrentqueue.h`
and `lightweightsemaphore.h` by `using` in `export namespace moodycamel`: `ConcurrentQueue`,
`BlockingConcurrentQueue`, `ConcurrentQueueDefaultTraits`, `ProducerToken`, `ConsumerToken`, `LightweightSemaphore`,
every `swap` overload, and `ConcurrentQueueTests`.
- `moodycamel::details` is the implementation and is not exported.
- Its TU-local helpers that are not templates over arbitrary types are adapted in
`mcpplibs::mwrap::concurrentqueue::details`, each calling upstream's body.
- `mcpplibs.mwrap.concurrentqueue.macros` holds the value macros and the configuration inputs as built, all as
`constexpr bool`. The syntax macros stay in `<mwrap/concurrentqueue/macros.hpp>`.
- `mcpplibs.mwrap.concurrentqueue.c_api` exports upstream's C API (`moodycamel_cq_*`, `moodycamel_bcq_*`), with the
`c-api` feature, which forwards to `compat.concurrentqueue/c-api`.
- The `no-thread-local` feature sets upstream's `MOODYCAMEL_NO_THREAD_LOCAL`.
- GCC 16.1 module handling:
- `MOODYCAMEL_NOEXCEPT_CTOR` / `_ASSIGN` are spelled with the equivalent type traits (upstream's configuration
point). Upstream's expression names a `static` function inside a template argument, which GCC rejects as an
exposure.
- The module instantiates the queues' members once, so that the non-dependent specializations of `static` helpers
have bodies in its interface.
- A unit that includes `<cxxabi.h>` cannot instantiate the thread-exit path; `no-thread-local` is the documented
remedy.

`tools/check_exports.py` audits every declaration of `moodycamel`, every name of `moodycamel::details`, the C API, and
every macro and configuration input against the pinned headers. It was negative-checked by removing an export and a
mapping.

## Verification

Wrapper CI runs on four rows, all required: Linux GCC 16.1, Linux LLVM 23.1.3, macOS LLVM and Windows LLVM. It
covers:

- the build, with and without `c-api`;
- the tests: multi-producer multi-consumer traffic with tokens and bulk operations, timed waits, user types and
traits, the semaphore, the adapted helpers, the constants and header mixing;
- the `c-api` / `no-thread-local` test project;
- the export audit;
- upstream's `tests/unittests`, all 45 registered tests, built against the module instead of the headers. The
conformance tool documents its mechanical edits and applies them to the pinned tarball's files.

The templates job scaffolds the default `pipeline` template as an installed package, builds it with `--strict` and runs
it.

Local checks, with the CI-pinned mcpp 2026.10.26.1 and `MCPP_INDEX_MIRROR=GLOBAL`:

- `bash tests/run_members.sh concurrentqueue-mpmc` installs the real release archive through the candidate index. The
consumer runs four producers enqueueing in bulk through tokens and four consumers waiting with timeouts; every item
must arrive once and in per-producer order. It then exercises a timed-out wait and the semaphore. Result:
`test result ok`.
- `python3 tests/mwrap/check_installed_template.py --package mcpplibs.mwrap.concurrentqueue@1.0.5` reports "Verified
real indexed archive installation and unmodified template consumer".

## Mirror and updates

This change has no `mcpp-res` access. The descriptor therefore uses the GitHub release URL as a plain string, and CN
users fall back to it. A maintainer can add the GitCode mirror with identical bytes. `extensions.autoupdate` follows
the repository's own `mwrap-${version}` tags.

## Motivation

openmui.core's UI queue is a `moodycamel::ConcurrentQueue`. Through this module its implementation unit imports
`mcpplibs.mwrap.concurrentqueue` instead of including concurrentqueue.h.
5 changes: 5 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1623,6 +1623,11 @@ jobs:
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.libpng@1.6.43

- name: Installed concurrentqueue wrapper archive and actual default pipeline template
if: contains(format(' {0} ', env.MEMBERS), ' concurrentqueue-mpmc ') && matrix.toolchain == 'default'
shell: bash
run: python3 tests/mwrap/check_installed_template.py --mcpp "$MCPP" --package mcpplibs.mwrap.concurrentqueue@1.0.5

- name: Installed FriBidi wrapper archive, actual default bidi template and its relocated LGPL notices
if: contains(format(' {0} ', env.MEMBERS), ' fribidi-bidi ') && matrix.toolchain == 'default'
shell: bash
Expand Down
1 change: 1 addition & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ members = [
"tests/mwrap/lua-embed",
"tests/mwrap/quickjs-embed",
"tests/mwrap/libpng-image",
"tests/mwrap/concurrentqueue-mpmc",
"tests/mwrap/fribidi-bidi",
"tests/mwrap/freetype-render",
"tests/mwrap/harfbuzz-shape",
Expand Down
38 changes: 38 additions & 0 deletions pkgs/mwrap/concurrentqueue.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
-- Immutable upstream-aligned mwrap source release (concurrentqueue-m mwrap-1.0.5).
-- The CN mirror is added by a maintainer with mcpp-res access; until then CN
-- users fall back to the GitHub release (docs/cn-mirror.md).
package = {
spec = "1",
namespace = "mcpplibs.mwrap",
name = "concurrentqueue",
description = "concurrentqueue-m: moodycamel::ConcurrentQueue (lock-free MPMC queue) as C++23 modules",
licenses = { "Apache-2.0" },
repo = "https://github.com/mcpplibs/concurrentqueue-m",
type = "package",
extensions = {
autoupdate = {
tag = "mwrap-${version}",
url = "https://github.com/mcpplibs/concurrentqueue-m/releases/download/${tag}/concurrentqueue-m-${version}.tar.gz",
},
},
xpm = {
linux = {
["1.0.5"] = {
url = "https://github.com/mcpplibs/concurrentqueue-m/releases/download/mwrap-1.0.5/concurrentqueue-m-1.0.5.tar.gz",
sha256 = "896e0adb535d74633bed4cd10cfe161bc6b45ff9b386faf141ab808f196050d1",
},
},
macosx = {
["1.0.5"] = {
url = "https://github.com/mcpplibs/concurrentqueue-m/releases/download/mwrap-1.0.5/concurrentqueue-m-1.0.5.tar.gz",
sha256 = "896e0adb535d74633bed4cd10cfe161bc6b45ff9b386faf141ab808f196050d1",
},
},
windows = {
["1.0.5"] = {
url = "https://github.com/mcpplibs/concurrentqueue-m/releases/download/mwrap-1.0.5/concurrentqueue-m-1.0.5.tar.gz",
sha256 = "896e0adb535d74633bed4cd10cfe161bc6b45ff9b386faf141ab808f196050d1",
},
},
},
}
11 changes: 11 additions & 0 deletions tests/mwrap/concurrentqueue-mpmc/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "concurrentqueue-mpmc"
version = "1.0.5"
description = "Consume the immutable concurrentqueue wrapper: multi-producer multi-consumer traffic with tokens and bulk operations, the blocking queue's timed waits, the semaphore, the mapped macros"

[indices]
# Use the complete candidate built-in index prepared by tests/run_members.sh.
# An explicit empty table overrides the legacy workspace redirect.

[dependencies]
"mcpplibs.mwrap.concurrentqueue" = "1.0.5"
57 changes: 57 additions & 0 deletions tests/mwrap/concurrentqueue-mpmc/tests/behavior.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import std;
import mcpplibs.mwrap.concurrentqueue;

namespace macros = mcpplibs::mwrap::concurrentqueue::macros;

int main() {
static_assert(macros::MOODYCAMEL_EXCEPTIONS_ENABLED);
constexpr int producers = 4, consumers = 4, per_producer = 10000, total = producers * per_producer;
// Producers enqueue in bulk through their tokens; consumers wait with a
// timeout and stop once everything is taken.
moodycamel::BlockingConcurrentQueue<std::pair<int, int>> queue;
std::vector<std::vector<char>> seen(producers, std::vector<char>(per_producer, 0));
std::mutex seen_lock;
std::atomic<int> taken{0};
bool ordered = true;
{
std::vector<std::jthread> threads;
for (int p = 0; p < producers; ++p)
threads.emplace_back([&, p] {
moodycamel::ProducerToken token(queue);
std::array<std::pair<int, int>, 100> batch;
for (int i = 0; i < per_producer; i += 100) {
for (int k = 0; k < 100; ++k) batch[k] = {p, i + k};
queue.enqueue_bulk(token, batch.begin(), batch.size());
}
});
for (int c = 0; c < consumers; ++c)
threads.emplace_back([&] {
moodycamel::ConsumerToken token(queue);
std::array<std::pair<int, int>, 64> batch;
std::array<int, producers> last{-1, -1, -1, -1};
while (taken.load() < total) {
const auto n = queue.wait_dequeue_bulk_timed(token, batch.begin(), batch.size(), std::chrono::milliseconds(1));
std::lock_guard lock(seen_lock);
for (std::size_t k = 0; k < n; ++k) {
auto [p, i] = batch[k];
if (i <= last[p]) ordered = false; // per-producer FIFO
last[p] = i;
++seen[p][i];
}
taken.fetch_add(static_cast<int>(n));
}
});
}
for (const auto& p : seen)
for (char c : p)
if (c != 1) return 1;
if (!ordered) return 2;
// A timed wait on an empty queue times out; the semaphore counts.
std::pair<int, int> none;
if (queue.wait_dequeue_timed(none, std::chrono::milliseconds(2))) return 3;
moodycamel::LightweightSemaphore sema;
sema.signal(2);
if (sema.waitMany(5) != 2 || sema.tryWait()) return 4;
std::println("concurrentqueue-mpmc: ok ({} items, {} producers, {} consumers)", total, producers, consumers);
return 0;
}
Loading