Skip to content

Commit d5bc117

Browse files
authored
The task interface needs the ABI switch, and a task program runs with it (0.1.1) (#1)
* The task interface needs the ABI switch, and a task program runs with it (0.1.1) The threads feature declares requires_abi = { threads = true } instead of putting -pthread on this package's own units: the switch belongs to the artefact, and mcpp 2026.9.12.2 carries it as [target.'cfg(os = "emscripten")'.abi] threads = true in the consumer's root manifest. The README section that recorded a missing whole-graph channel is corrected, and CI asserts no task symbol without the feature, a refusal naming the feature without the switch, and with it eight symbols and a task program exiting 0 under node. * ci: test against mcpp 2026.9.12.2, the engine that carries the ABI table
1 parent 9ab7153 commit d5bc117

3 files changed

Lines changed: 115 additions & 56 deletions

File tree

.github/workflows/ci.yml

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ env:
4040
# `em++`'s C compiler is `emcc` and not `em` (a C translation unit in the
4141
# conformance suite does not compile without that), and the wasm target rows
4242
# resolve the emsdk payload.
43-
MCPP_VERSION: 2026.9.11.4
43+
MCPP_VERSION: 2026.9.12.2
4444
XLINGS_VERSION: v2026.8.17.2
4545
XLINGS_NON_INTERACTIVE: '1'
4646

@@ -167,8 +167,9 @@ jobs:
167167
# The set is named rather than `full`: clause 6.1 says an implementation
168168
# provides an interface in whole or not at all, and a group this platform
169169
# does not have is an absence rather than a deviation. `task` is not in
170-
# the set -- see the task-gate step below for why it needs a link this
171-
# suite cannot currently be given.
170+
# the set: the suite's consumer manifest is generated by the
171+
# specification's runner, which does not state the ABI switch threads
172+
# need. The task-gate step below builds and runs a task program with it.
172173
- name: Every interface this platform has, examined
173174
run: |
174175
OPENKAL_CONFORMANCE_RUNNER="$NODE_BIN" \
@@ -288,20 +289,24 @@ jobs:
288289
done
289290
test "$fail" -eq 0
290291
291-
# THE TASK GATE, IN BOTH DIRECTIONS.
292+
# THE TASK GATE, IN THREE DIRECTIONS.
292293
#
293294
# `openkal.task` needs `-pthread`, which selects a different C library
294-
# build and a different memory model -- a property of the whole link. So
295-
# the interface is carried by a feature: without it the translation unit
296-
# is empty and the eight symbols do not exist, which is the same
297-
# treatment the three absent interfaces get and for the same reason.
295+
# build and a different memory model -- a property of the whole link, and
296+
# therefore of the consumer's root manifest (`[target.<selector>.abi]
297+
# threads = true`, mcpp 2026.9.12.2). The `threads` feature states that it
298+
# needs the switch. Asserted:
298299
#
299-
# Both directions are asserted, because a gate is only a gate if the
300-
# other side of it differs. The conformance suite cannot yet be built
301-
# with this switch -- it would have to reach the specification package's
302-
# modules too, and mcpp has no whole-graph flag channel -- so what is
303-
# asserted here is the gating, and the README records the rest.
304-
- name: The task interface exists in a -pthread link and in no other
300+
# 1. without the feature and the switch, no task symbol exists;
301+
# 2. the feature without the switch is refused before anything compiles,
302+
# naming the feature;
303+
# 3. with both, the eight symbols exist and a program that starts a task
304+
# and joins it exits 0 under node.
305+
#
306+
# The consumer names `openkal` by path, as the implementation's manifest
307+
# now does (the step above): mcpp refuses one package reached both by path
308+
# and by version.
309+
- name: The task interface needs the ABI switch, is refused without it, and runs with it
305310
run: |
306311
set -euo pipefail
307312
rm -rf target
@@ -311,14 +316,55 @@ jobs:
311316
test "$n" -eq 0 || { echo "::error::$n kal_task symbols without the feature"; exit 1; }
312317
echo " ok no task symbol without the feature"
313318
314-
rm -rf target
315-
mcpp build --target wasm32-emscripten --features threads
316-
objs=$(find target -path '*/obj/*' -name '*.o' ! -name '*.m.o' | tr '\n' ' ')
319+
work="$RUNNER_TEMP/tasks"
320+
impl="$(pwd)"
321+
rm -rf "$work"; mkdir -p "$work/src"
322+
cat > "$work/src/main.cpp" << 'CPP'
323+
import openkal.task;
324+
static void entry(void* arg) { *static_cast<int*>(arg) = 1; }
325+
int main() {
326+
// KAL_TASK_PROP_PARALLEL is bit 1 (openkal/task.h); a macro does not
327+
// cross a module.
328+
if ((kal_task_props() & 2u) == 0) return 2;
329+
int ran = 0;
330+
kal_task t{};
331+
if (kal_task_start(&entry, &ran, &t) != 0) return 3;
332+
if (kal_task_join(t) != 0) return 4;
333+
return ran == 1 ? 0 : 5;
334+
}
335+
CPP
336+
manifest() {
337+
cat > "$work/mcpp.toml" << TOML
338+
[package]
339+
name = "tasks"
340+
version = "0.1.0"
341+
342+
[dependencies]
343+
openkal = { path = "$impl/.spec" }
344+
openkal-emscripten = { path = "$impl", features = ["threads"] }
345+
$1
346+
TOML
347+
}
348+
349+
manifest ""
350+
if ( cd "$work" && mcpp build --target wasm32-emscripten ) > refused.log 2>&1; then
351+
cat refused.log; echo "::error::the threads feature built without the ABI switch"; exit 1
352+
fi
353+
grep -q "requires the artefact's ABI to have threads (feature \`threads\`)" refused.log \
354+
|| { cat refused.log; echo "::error::the refusal does not name the feature"; exit 1; }
355+
echo " ok the feature without the switch is refused, naming the feature"
356+
357+
manifest "$(printf '\n[target.%s.abi]\nthreads = true\n' "'cfg(os = \"emscripten\")'")"
358+
( cd "$work" && mcpp build --target wasm32-emscripten )
359+
objs=$(find "$work/target" -path '*/obj/*' -name '*.o' ! -name '*.m.o' | tr '\n' ' ')
317360
n=$("$NM" --defined-only $objs 2>/dev/null \
318361
| awk '$2=="T"||$2=="W"{print $3}' | sed 's/^_//' \
319362
| grep -c '^kal_task' || true)
320-
test "$n" -eq 8 || { echo "::error::expected 8 kal_task symbols with the feature, found $n"; exit 1; }
321-
echo " ok eight task symbols with the feature"
363+
test "$n" -eq 8 || { echo "::error::expected 8 kal_task symbols with the switch, found $n"; exit 1; }
364+
echo " ok eight task symbols with the switch"
365+
( cd "$work" && timeout 300 mcpp run --target wasm32-emscripten ) \
366+
|| { echo "::error::the task program did not exit 0 under node"; exit 1; }
367+
echo " ok a task starts, runs and joins under node"
322368
323369
- name: Restore the manifest
324370
if: always()

README.md

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,30 +61,48 @@ So the interface is carried by a feature:
6161

6262
```toml
6363
[dependencies]
64-
openkal-emscripten = { version = "0.1.0", features = ["threads"] }
64+
openkal-emscripten = { version = "0.1.1", features = ["threads"] }
6565
```
6666

6767
Without it, `src/threads/task.cpp` compiles to nothing, the eight `kal_task_*`
6868
symbols do not exist, and `kal_interfaces()` does not claim the interface --
6969
the same treatment the three absent interfaces get, for the same reason.
7070

71-
**This feature is not yet usable end to end, and the limitation is the build
72-
tool's rather than this package's.** `-pthread` changes the module
73-
configuration of *every* translation unit in the link, including the
74-
specification package's, and mcpp has no channel for a flag that applies to a
75-
whole dependency graph: a feature contributes sources, defines and per-glob
76-
compile flags, and `[build] ldflags` reaches the root package only. Measured
77-
2026-09-11 with the feature active and `-pthread` on the consumer:
71+
**The switch belongs to the artefact, and the root manifest states it.**
72+
`-pthread` changes the module configuration of *every* translation unit in the
73+
link, including the specification package's, so it cannot be a flag of this
74+
package's own units. With mcpp 2026.9.12.2 or later, the consumer writes
75+
76+
```toml
77+
[target.'cfg(os = "emscripten")'.abi]
78+
threads = true
79+
```
80+
81+
and the switch reaches the standard library module, every translation unit of
82+
every package, and the link. The `threads` feature states that it needs the
83+
switch (`requires_abi = { threads = true }`), so a consumer that activates the
84+
feature without the table is refused before anything compiles, naming the
85+
feature and the table. Measured 2026-09-12 with emsdk 6.0.9 and a development
86+
build of mcpp 2026.9.12.2: with the table, a program that starts a task and
87+
joins it exits 0 under node, and without it the build stops at
88+
89+
```
90+
error: `openkal-emscripten` requires the artefact's ABI to have threads (feature `threads`), and this build does not state it.
91+
```
92+
93+
An earlier version of this section, measured 2026-09-11, concluded that mcpp had
94+
no channel for a flag that applies to a whole dependency graph. What had been
95+
measured was `-pthread` in the consumer's per-package `cxxflags`:
7896

7997
```
8098
error: POSIX thread support was disabled in precompiled file
8199
'.../pcm.cache/openkal.types.pcm' but is currently enabled
82100
```
83101

84-
which is the specification package's module, compiled without the switch. The
85-
interface word is already correct for a link that manages it; closing the gap
86-
is an engine change of the same shape as the existing whole-graph runtime
87-
flags, and it is recorded rather than worked around.
102+
That channel does not reach the specification package's module. A graph-wide
103+
channel did exist (`[build] dialect_cxxflags`); what was missing was a way to
104+
scope it to one target and to let this feature state its requirement, which the
105+
typed table provides.
88106

89107
## Conformance
90108

@@ -128,7 +146,7 @@ implementation in its source:
128146
openkal = "0.12.0"
129147

130148
[target.'cfg(os = "emscripten")'.dependencies]
131-
openkal-emscripten = "0.1.0"
149+
openkal-emscripten = "0.1.1"
132150
```
133151

134152
## Licence

mcpp.toml

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
namespace = "mcpplibs"
33
name = "openkal-emscripten"
4-
version = "0.1.0"
4+
version = "0.1.1"
55
description = "An implementation of openkal for Emscripten, written ABOVE a C library rather than beneath one, because on this platform there is no kernel to issue calls to."
66
license = "Apache-2.0"
77

@@ -59,29 +59,24 @@ default = []
5959
# capability is not a property of the machine this runs on, and it is not
6060
# discoverable at run time by anything this package can ask.
6161
#
62-
# Declaring it as a feature puts the decision where the rest of the link is
63-
# decided, and `kal_task_props` reports the consequence: with this feature the
64-
# word carries KAL_TASK_PROP_PARALLEL, and without it the interface is still
65-
# provided and `kal_task_start` reports kal_err_not_supported -- which is
66-
# clause 6.2's third time, a capability word read before use, rather than the
67-
# shape the specification forbids.
62+
# THE SWITCH IS THE ARTEFACT'S, SO THE ROOT MANIFEST STATES IT. `-pthread`
63+
# changes the module configuration of every translation unit in the link, the
64+
# specification package's included, so it cannot be a flag of this package's
65+
# own units. mcpp 2026.9.12.2 carries it as a typed member of the root's target
66+
# table,
6867
#
69-
# `-pthread` ON THIS PACKAGE'S OWN UNITS, AND NOT A DEFINE OF ITS OWN. Passing
70-
# the switch is what makes Emscripten define `__EMSCRIPTEN_PTHREADS__`, so the
71-
# code keys on that rather than on a name this manifest invented: one
72-
# mechanism, and it is the compiler's own answer to "was this compiled with
73-
# threads" instead of a second word that could disagree with it.
68+
# [target.'cfg(os = "emscripten")'.abi]
69+
# threads = true
7470
#
75-
# THE LINK HALF IS THE CONSUMER'S, AND THAT IS A LIMITATION WORTH NAMING. A
76-
# feature contributes build INPUTS -- sources, defines, flags -- and mcpp's
77-
# feature axis has no `ldflags`, so this cannot put `-pthread` on the link
78-
# line. A project that activates this feature must also write
71+
# which reaches the standard library module, every translation unit and the
72+
# link. This feature states that it needs the switch: a build that activates it
73+
# without the table is refused before anything compiles, naming the feature and
74+
# the table.
7975
#
80-
# [build]
81-
# ldflags = ["-pthread"]
82-
#
83-
# and a project that forgets fails at LINK naming the pthread symbols, which
84-
# is a loud failure rather than a silent single-threaded program.
85-
threads = { flags = [
86-
{ glob = "src/**", cxxflags = ["-pthread"] },
87-
] }
76+
# `kal_task_props` reports the consequence: with the switch the word carries
77+
# KAL_TASK_PROP_PARALLEL, and without it `kal_task_start` reports
78+
# kal_err_not_supported -- clause 6.2's third time, a capability word read
79+
# before use. The code keys on `__EMSCRIPTEN_PTHREADS__`, which the compiler
80+
# defines when `-pthread` is passed: one mechanism, and it is the compiler's own
81+
# answer to "was this compiled with threads".
82+
threads = { requires_abi = { threads = true } }

0 commit comments

Comments
 (0)