Skip to content

Commit 6189122

Browse files
authored
Merge pull request #967 from fglock/docs/thread-release-gates
docs: deliver permanent Perl thread release gates
2 parents 23507ac + acebf8b commit 6189122

20 files changed

Lines changed: 453 additions & 71 deletions

File tree

.github/workflows/gradle.yml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,25 @@ jobs:
4141
if: runner.os == 'Linux'
4242
run: make ci
4343

44+
- name: Checkout pinned Perl thread compatibility corpus
45+
if: runner.os == 'Linux'
46+
uses: actions/checkout@v4
47+
with:
48+
repository: Perl/perl5
49+
ref: de80c8ecd40c6d5b677847699e5482b44bc748c6
50+
path: perl5
51+
sparse-checkout: |
52+
t
53+
dist/threads
54+
dist/threads-shared
55+
dist/Thread-Queue
56+
dist/Thread-Semaphore
57+
58+
- name: Run Perl thread compatibility gate
59+
if: runner.os == 'Linux'
60+
timeout-minutes: 20
61+
run: make test-threads
62+
4463
- name: Generate SBOM (Linux only)
4564
if: runner.os == 'Linux'
4665
run: make sbom
@@ -59,4 +78,6 @@ jobs:
5978
uses: actions/upload-artifact@v4
6079
with:
6180
name: test-results-${{ matrix.os }}
62-
path: build/reports/tests/test/
81+
path: |
82+
build/reports/tests/test/
83+
build/reports/threads/

Makefile

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,26 @@
1-
.PHONY: all clean test test-unit test-interpreter test-bundled-modules test-cpan-distroprefs test-exiftool test-all test-gradle test-gradle-unit test-gradle-all test-gradle-parallel test-maven-parallel build run wrapper check-java-gradle dev ci sbom sbom-java sbom-perl sbom-clean check-links
1+
.PHONY: all clean test test-unit test-interpreter check-thread-test-sources test-threads test-threads-release test-bundled-modules test-cpan-distroprefs test-exiftool test-all test-gradle test-gradle-unit test-gradle-all test-gradle-parallel test-maven-parallel build run wrapper check-java-gradle dev ci sbom sbom-java sbom-perl sbom-clean check-links
2+
3+
THREAD_DIST_DIRS := perl5/dist/threads/t perl5/dist/threads-shared/t perl5/dist/Thread-Queue/t perl5/dist/Thread-Semaphore/t
4+
THREAD_PLATFORM_TESTS := \
5+
perl5/dist/threads/t/end.t \
6+
perl5/dist/threads/t/exit.t \
7+
perl5/dist/threads/t/free.t \
8+
perl5/dist/threads/t/free2.t \
9+
perl5/dist/threads/t/join.t \
10+
perl5/dist/threads/t/kill.t \
11+
perl5/dist/threads/t/kill2.t \
12+
perl5/dist/threads/t/kill3.t \
13+
perl5/dist/threads/t/stack.t \
14+
perl5/dist/threads/t/stack_env.t \
15+
perl5/dist/threads/t/state.t \
16+
perl5/dist/threads/t/zz_deadlock.t \
17+
perl5/dist/threads-shared/t/cond.t \
18+
perl5/dist/threads-shared/t/wait.t \
19+
perl5/dist/threads-shared/t/waithires.t \
20+
perl5/dist/Thread-Queue/t/09_ended.t \
21+
perl5/dist/Thread-Queue/t/10_timed.t \
22+
perl5/dist/Thread-Semaphore/t/04_nonblocking.t \
23+
perl5/dist/Thread-Semaphore/t/06_timed.t
224

325
all: build
426

@@ -87,6 +109,35 @@ test-interpreter:
87109
@echo "Running unit tests with bytecode interpreter..."
88110
JPERL_INTERPRETER=1 perl dev/tools/perl_test_runner.pl --jobs 8 --timeout 60 --output test_interpreter_results.json src/test/resources/unit
89111

112+
# Verify the unchanged upstream test distributions are available. GitHub CI
113+
# sparse-checks them out at the compatibility-corpus commit recorded below;
114+
# local developers normally use their adjacent/gitignored perl5 source tree.
115+
check-thread-test-sources:
116+
@for dir in $(THREAD_DIST_DIRS); do \
117+
if [ ! -d "$$dir" ]; then \
118+
echo "Error: $$dir is missing."; \
119+
echo "Clone Perl commit de80c8ecd40c6d5b677847699e5482b44bc748c6 into ./perl5 before running the thread gates."; \
120+
exit 1; \
121+
fi; \
122+
done
123+
124+
# Permanent Perl ithread compatibility gate used by Ubuntu pull-request CI.
125+
# Full upstream distributions run on both backends with the default virtual
126+
# carrier; lifecycle, stack, signal, wait, timeout, and deadlock coverage also
127+
# runs on the platform carrier. Reports are retained under build/reports/threads.
128+
test-threads: check-java-gradle check-thread-test-sources
129+
@mkdir -p build/reports/threads
130+
JPERL_THREAD_MODE=virtual perl dev/tools/perl_test_runner.pl --strict-exit --jobs 8 --timeout 300 --output build/reports/threads/jvm-virtual.json $(THREAD_DIST_DIRS)
131+
JPERL_INTERPRETER=1 JPERL_THREAD_MODE=virtual perl dev/tools/perl_test_runner.pl --strict-exit --jobs 8 --timeout 300 --output build/reports/threads/interpreter-virtual.json $(THREAD_DIST_DIRS)
132+
JPERL_THREAD_MODE=platform perl dev/tools/perl_test_runner.pl --strict-exit --jobs 8 --timeout 300 --output build/reports/threads/platform-focused.json $(THREAD_PLATFORM_TESTS)
133+
134+
# Thread release gate: extend the PR gate to the complete platform-carrier
135+
# distribution matrix. Together with test-threads this covers both backends on
136+
# both carrier policies without making every pull request repeat all four runs.
137+
test-threads-release: test-threads
138+
JPERL_THREAD_MODE=platform perl dev/tools/perl_test_runner.pl --strict-exit --jobs 8 --timeout 300 --output build/reports/threads/jvm-platform.json $(THREAD_DIST_DIRS)
139+
JPERL_INTERPRETER=1 JPERL_THREAD_MODE=platform perl dev/tools/perl_test_runner.pl --strict-exit --jobs 8 --timeout 300 --output build/reports/threads/interpreter-platform.json $(THREAD_DIST_DIRS)
140+
90141
# Bundled CPAN module tests (XML::Parser, etc.)
91142
# Tests live under src/test/resources/module/{ModuleName}/t/
92143
test-bundled-modules: check-java-gradle

dev/design/concurrency.md

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Perl Threads Implementation Plan
22

3-
**Status:** Active implementation plan
4-
**Version:** 2.2
3+
**Status:** Delivered; compatibility maintenance
4+
**Version:** 3.0
55
**Date:** 2026-08-15
66

77
## 1. Goal and Non-Negotiable Delivery Rule
@@ -222,13 +222,13 @@ direct companions.
222222
## 6. Supporting Design Contracts
223223

224224
- `dev/design/attributes.md` defines the supported `shared` attribute surface.
225-
- `dev/design/runtime-pooling-reset-contract.md` defines the proof required
226-
before runtime pooling can be enabled.
225+
- `dev/design/runtime-pooling-reset-contract.md` defines the implemented
226+
bounded pool reset and fresh-runtime replacement contract.
227227
- `dev/design/phase36-regex-parity.md` owns direct regex-language and Joni work.
228228

229229
## 7. Progress Tracking
230230

231-
### Current Status: release validation in progress; Joni/regex parity is separate
231+
### Current Status: delivered; Joni/regex parity is separate
232232

233233
`PerlRuntime` owns interpreter state, ithreads clone one isolated runtime graph,
234234
and `threads::shared` supplies explicit cross-runtime storage and synchronization.
@@ -246,19 +246,21 @@ requests.
246246

247247
### Next Steps
248248

249-
1. Finish the local release gates: `make`, documentation links, all four
250-
upstream distribution configurations, the thread preservation matrix,
251-
and `timeout 3600 ./jcpan --jobs 8 -t DBIx::Class`.
252-
2. Review the final diff for temporary diagnostics, generated files, accidental
253-
upstream-test edits, and any mutable runtime state lacking an ownership
254-
classification.
255-
3. Open the pull request with the exact gate results in its commit messages and
256-
description.
257-
4. Require green Ubuntu and Windows CI. Investigate and fix any CI failure on
258-
the branch; do not merge on a rerun-only explanation.
259-
5. After merge, treat the four unchanged upstream distributions as permanent
260-
regression gates. Continue direct regex-language work, including Joni, only
261-
in the separate Phase 36 project.
249+
1. Keep `make test-threads` as a permanent Ubuntu pull-request gate. It runs
250+
both execution backends with virtual carriers and focused platform lifecycle,
251+
signal, stack, wait, timeout, and deadlock coverage.
252+
2. Run `make test-threads-release` before any thread/runtime release. It extends
253+
the pull-request gate to the complete four-module matrix on platform carriers.
254+
3. Continue CPAN ecosystem hardening: opt-in Test2 stress tests, remaining Moose
255+
thread-suite classification, Net::SSLeay callback suites 61/62, and permanent
256+
DBI ownership plus DBIx::Class release coverage.
257+
4. Require every future native resource, I/O handle, and callback adapter to
258+
declare its runtime ownership, snapshot, aliasing, and close policy.
259+
5. Monitor snapshot retention and startup cost. Runtime pooling remains bounded,
260+
opt-in, and disabled by default; returned application runtimes are replaced,
261+
not partially reused.
262+
6. Keep direct regex-language and Joni work in the separate Phase 36 project.
263+
Threaded regex wrappers must preserve their same-commit direct behavior.
262264

263265
Direct regex-language work, including Joni integration, is not part of this
264266
release.
@@ -278,6 +280,7 @@ relative to their same-commit direct companions.
278280
## Related Documents
279281

280282
- `dev/design/attributes.md` — current attribute behavior
281-
- `dev/design/runtime-pooling-reset-contract.md` — required proof before pooling
283+
- `dev/design/runtime-pooling-reset-contract.md` — bounded pooling and
284+
fresh-runtime replacement contract
282285
- `dev/design/phase36-regex-parity.md` — independent regex parity project
283286
- `dev/design/fork_open_emulation.md` — process/fork-related alternatives

dev/tools/perl_test_runner.pl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818
my $jobs = 5; # Default to 5 parallel jobs
1919
my $output_file;
2020
my $help;
21+
my $strict_exit = 0;
2122

2223
GetOptions(
2324
'jperl=s' => \$jperl_path,
2425
'timeout=f' => \$timeout,
2526
'jobs|j=i' => \$jobs,
2627
'output=s' => \$output_file,
28+
'strict-exit!' => \$strict_exit,
2729
'help' => \$help,
2830
) or die "Error in command line arguments\n";
2931

@@ -113,6 +115,12 @@
113115
save_results($output_file);
114116
}
115117

118+
if ($strict_exit
119+
&& ($summary{fail} || $summary{error} || $summary{timeout}
120+
|| $summary{incomplete})) {
121+
exit 1;
122+
}
123+
116124
# Subroutines
117125

118126
sub find_test_files {
@@ -239,6 +247,12 @@ sub process_test_result {
239247
print " " x (50 - length($rel_path)) if length($rel_path) < 50;
240248
printf " ... %s %d/%d ok (%.2fs)\n",
241249
$char, $result->{ok_count}, $result->{total_tests}, $duration;
250+
if ($result->{status} ne 'pass' && defined $result->{failure_output}) {
251+
print "----- captured output: $rel_path -----\n";
252+
print $result->{failure_output};
253+
print "\n" unless $result->{failure_output} =~ /\n\z/;
254+
print "----- end captured output -----\n";
255+
}
242256
}
243257

244258
# Single, clean run_single_test function
@@ -475,6 +489,11 @@ sub run_single_test {
475489

476490
my $result = parse_tap_output($output, $exit_code);
477491
$result->{raw_output_path} = $raw_output_path;
492+
# Preserve a bounded diagnostic tail in the JSON report and CI log. Raw
493+
# output normally lives in /tmp on the worker and disappears before an
494+
# uploaded report can be inspected.
495+
$result->{failure_output} = substr($output, -32768)
496+
if $result->{status} ne 'pass';
478497
return $result;
479498
}
480499

@@ -504,6 +523,8 @@ sub timeout_for_test {
504523
sub requires_exclusive_slot {
505524
my ($test_file) = @_;
506525
return $test_file =~ m{
526+
(?:^|/)perl5/dist/threads/t/join\.t$
527+
|
507528
(?:^|/)perl5_t/t/op/gv\.t$
508529
| (?:^|/)perl5_t/t/re/pat(?:_thr)?\.t$
509530
| (?:^|/)perl5_t/t/re/pat_psycho(?:_thr)?\.t$
@@ -820,6 +841,7 @@ sub print_usage {
820841
subprocess-heavy tests have a documented minimum)
821842
--jobs|-j NUM Number of parallel jobs (default: 5)
822843
--output FILE Save detailed results to JSON file
844+
--strict-exit Exit nonzero if any file fails, errors, times out, or is incomplete
823845
--help Show this help message
824846
825847
Examples:

docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Technical reference documentation:
3030
- **[Bundled Modules](reference/bundled-modules.md)** - Complete list of included modules
3131
- **[XS Compatibility](reference/xs-compatibility.md)** - XS modules and Java implementations
3232
- **[CLI Options](reference/cli-options.md)** - Command-line reference
33-
- **[Concurrency](reference/feature-matrix.md#concurrency-and-perl-threads)** - Multiplicity, ithreads, shared storage, and limitations
33+
- **[Perl Threads](reference/threads.md)** - Ithreads, shared storage, carrier policy, resources, and testing
3434
- **[Runtime Configuration](reference/configure.md#runtime-thread-configuration)** - Thread capability flags and execution mode
3535
- **[Memory Management](reference/memory-management.md)** - JVM GC, deterministic destruction, weak references, and threads
3636
- **[Testing](reference/testing.md)** - Test suite information

docs/about/changelog.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Release history of PerlOnJava. See [Roadmap](roadmap.md) for future plans.
88
manifest, provide a JAXP-backed `XML::LibXSLT`, and preserve descriptors for
99
anonymous handles stored in container lvalues. This unblocks
1010
`Catmandu::CrossRef` and `AnyEvent::SMTP` without distribution preferences.
11-
- Add Perl interpreter multiplicity and the supported ithread tranche across
11+
- Add Perl interpreter multiplicity and full ithread support across
1212
the JVM and interpreter backends. Mutable execution state is owned by
1313
independent `PerlRuntime` instances; child threads receive identity-aware
1414
snapshots, while `threads::shared` preserves explicitly shared
@@ -22,9 +22,11 @@ Release history of PerlOnJava. See [Roadmap](roadmap.md) for future plans.
2222
registrations retain their owning runtime, internal pipes have an explicit
2323
inherited-handle policy, and nested plain shared graphs are validated before
2424
publication. General resource inheritance, DBI ownership, lexical regex
25-
diagnostics, blessed roots, and tied shared-value conversion are implemented;
26-
exact nested shared-reference proxies remain limited;
27-
see the [feature matrix](../reference/feature-matrix.md#concurrency-and-perl-threads).
25+
diagnostics, blessed roots, tied shared-value conversion, nested shared
26+
proxy views, and global final destruction are implemented. The unchanged
27+
upstream `threads`, `threads::shared`, `Thread::Queue`, and
28+
`Thread::Semaphore` distributions pass on both backends and both Java carrier
29+
policies; see the [Perl threads reference](../reference/threads.md).
2830
- CPAN/tooling: expose tested dependency scripts through `PATH`, deduplicate
2931
repeated `PERL5LIB` setup, and resolve test prerequisites against tested
3032
`blib` trees before launching tests. Add `JSON::DWIW`, `Taint::Runtime`, and

docs/about/roadmap.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ These capabilities are implemented and available in the current release:
4646
- **I/O Subsystem** — Sockets, I/O layers (`:raw`, `:utf8`, `:crlf`, `:encoding()`), in-memory files, pipes, file descriptor duplication, `flock`, tied handles.
4747
- **Multiplicity and Perl ithreads** — Mutable interpreter state is owned by
4848
`PerlRuntime`; `Config` advertises `useithreads`, `usethreads`, and
49-
`usemultiplicity`. The supported `threads` and `threads::shared` tranche
50-
includes isolated create/join, shared scalar/array/hash storage, recursive
51-
locks, and condition variables on both backends. See the
52-
[feature matrix](../reference/feature-matrix.md#concurrency-and-perl-threads).
49+
`usemultiplicity`. The bundled `threads`, `threads::shared`, `Thread::Queue`,
50+
and `Thread::Semaphore` distributions pass unchanged on both backends and
51+
both Java carrier policies. See the
52+
[Perl threads reference](../reference/threads.md).
5353
- **Pack/Unpack** — Full template support for binary data manipulation. See `dev/design/pack_unpack_architecture.md`.
5454
- **Subroutine Prototypes and Signatures** — All prototype characters supported; formal parameter signatures implemented.
5555
- **`format`/`write`** — Report generation with `formline` and `$^A` accumulator.
@@ -259,17 +259,17 @@ Introduce a normalization pass between parsing and code generation to eliminate
259259

260260
## Objective 6: Concurrency & Runtime Isolation
261261

262-
*Priority: Long-term — Major architectural work required.*
262+
*Priority: Maintenance and ecosystem hardening.*
263263

264264
See `dev/design/concurrency.md` for the comprehensive design covering multiplicity, fork emulation, and threads.
265265

266266
### Multiplicity
267267

268-
Multiple independent `PerlRuntime` instances and snapshot cloning are now
269-
implemented. Remaining work is compatibility hardening: close the applicable
270-
core/CPAN thread-suite gaps, prove native callback isolation, and define a
271-
reset contract before considering runtime pooling. Multiplicity alone does not
272-
make one JSR-223 engine or one captured PSGI app concurrently callable.
268+
Multiple independent `PerlRuntime` instances and snapshot cloning are
269+
implemented. The bounded PSGI runtime pool is opt-in and replaces returned app
270+
snapshots from an authoritative template; it does not partially reset and reuse
271+
an entered runtime. Multiplicity alone does not make one JSR-223 engine or one
272+
captured PSGI app concurrently callable.
273273

274274
### Fork Emulation
275275

@@ -281,18 +281,18 @@ Implement `fork()` via runtime cloning + thread. Currently returns `undef`.
281281

282282
### Threads (ithreads)
283283

284-
The supported ithread tranche is shipped: snapshot-based variable isolation,
285-
create/join/detach and lifecycle inspection, nested threads and child exit,
286-
`threads::shared` storage, recursive locks, and condition variables. Java 24
287-
virtual threads are the default; platform carriers remain selectable.
284+
Perl ithreads are shipped: snapshot-based variable isolation, lifecycle and
285+
signals, nested threads and child exit, `threads::shared` graphs and proxies,
286+
locks and conditions, `Thread::Queue`, and `Thread::Semaphore`. Java 24 virtual
287+
threads are the default; platform carriers remain selectable.
288288

289289
Remaining work:
290290

291-
- Complete the currently partial applicable core and regex suites.
292-
- Finish exact nested-reference proxy identity and global `DESTROY` ownership
293-
for blessed values fetched through `threads::shared` aggregates. Root
294-
blessed storage and the system-Perl tied conversion rules are implemented.
295-
- Keep runtime pooling disabled until the reset-equivalence contract is proven.
291+
- Keep the permanent PR and release matrices green.
292+
- Broaden CPAN/native ecosystem coverage, including opt-in Test2 and Moose
293+
thread suites and Net::SSLeay callback stress.
294+
- Keep direct regex-language/Joni work in the separate Phase 36 project while
295+
thread wrappers preserve the behavior of their direct companions.
296296

297297
---
298298

docs/reference/cli-options.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ JPERL_OPTS='-Djperl.thread.mode=platform' ./jperl threaded.pl
227227
The carrier selection does not change Perl snapshot or shared-storage
228228
semantics. A nonzero per-thread stack request automatically selects a platform
229229
carrier because virtual-thread stacks are JVM-managed. See the
230+
[Perl threads reference](threads.md) and
230231
[concurrency feature matrix](feature-matrix.md#concurrency-and-perl-threads).
231232

232233
### Runtime pooling

docs/reference/configure.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ module reports the shipped runtime capabilities directly:
2929
Java 24 virtual threads are the launcher default. The platform executor remains
3030
available process-wide with `JPERL_THREAD_MODE=platform` or the JVM property
3131
`-Djperl.thread.mode=platform`. Unknown values are rejected. See
32-
[CLI Options](cli-options.md#thread-execution-mode) and the
32+
[CLI Options](cli-options.md#thread-execution-mode), the
33+
[Perl threads reference](threads.md), and the
3334
[feature matrix](feature-matrix.md#concurrency-and-perl-threads).
3435

3536
Runtime pooling is independently opt-in. `JPERL_RUNTIME_POOL_SIZE=N` (or

0 commit comments

Comments
 (0)