Tags: codenameone/CodenameOne
Tags
Drain the GC's grace pass as it walks, ending the overflow spiral (is… …sue #5537) (#5573) * Drain the GC's grace pass as it walks, ending the overflow spiral (issue #5537) A deep game-tree search that #5563 saved from an EXC_RESOURCE kill came back frozen instead: GC pauses growing longer and more frequent until they were effectively continuous, with the simulator's footprint climbing to gigabytes while the app retained nothing. Both readings are the same defect, and it sits under the one #5563 fixed rather than beside it. Every cycle the grace pass walks the BiBOP page registry and marks every object allocated since the last cycle -- a fresh object may already be linked into the live graph, so it and its subtree have to survive. How many that is depends on the mutator's ALLOCATION RATE, not on the live set, and the pass pushed all of them onto a fixed 65536-entry worklist before draining any of it. A worker churning small short-lived objects produces several times that per cycle, so the worklist overflowed as a matter of course. Overflow is survivable -- the dropped entries are already marked and the belt re-discovers their children -- but the belt is a full O(heap) rescan. It makes the cycle several times longer, the mutator leaves proportionally more fresh objects for the next one, and that one overflows for certain. The collector never returns to its fast path. Every symptom on the issue follows from that single loop: the original kill by the iOS per-process ceiling, the frozen app once #5563's pacing held the process under that ceiling and had to park the mutator on nearly every allocation instead, and the simulator's climbing footprint where no ceiling exists at all. Both grace passes -- the page registry and the legacy table -- now drain when the worklist reaches half capacity. That costs nothing the end-of-pass drain would not have cost anyway, since the same objects are scanned, only sooner; what it buys is a cursor that cannot run away. The drain runs outside the trusted window (CN1_GC_TRUSTED_SUSPEND/RESUME, added because BEGIN/END save and restore a block-scoped local and so cannot express a hole inside a walk): a drain follows child words out of arbitrary mark functions, which is precisely what the resolve guard exists for. A _Static_assert pins the remaining assumption -- that a whole page of slots fits above the drain threshold -- so raising CN1_BIBOP_PAGE_SIZE fails the build rather than quietly restoring the spiral. Measured on a repro of the reporter's shape (worker thread, tree search, live set of one path). Realistic version, no ceiling: peak footprint 6.2GB -> 231MB, cycle time 6ms->750ms -> a flat 6ms, and 30% more nodes searched. Heavier version under a 512MB simulated ceiling, which is the device case: 77 of 150 cycles overflowed and the mutator parked 72 times -> 0 of 440 and no parks, 10.2s -> 6.8s, with 174MB of headroom left instead of 64MB. GcOverflowSpiralIntegrationTest guards it, asserting zero overflow cycles under a simulated ceiling and that the pass actually reached its drain threshold (else the first assertion would pass on a run that never allocated). Ablating the drain and leaving everything else in place fails it with 77 overflows. Overflow cycles are counted through a new env-gated [GC-OVERFLOW] tracer, and the count is taken with an exchange on the existing flag so it reads once per cycle rather than once per dropped push. Not addressed here, and separate: off a per-process ceiling the pacing cap is still a fraction of the HOST's free RAM, so on a RAM-rich Mac a sufficiently extreme allocator can build gigabytes of garbage before anything stalls it. A live-set-relative cap and an absolute cap were both measured and rejected -- each cost 2-4x throughput, because a volume-cap park waits out a whole collection while the footprint-based admission used under a real ceiling is both bounded and free. Extending that admission to hosts with a footprint probe but no ceiling is the right fix and needs its own benchmarking. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Keep the grace pass's periodic drain off the heap-rescan path The first cut hung the Mac Catalyst screenshot suite: the collector never finished a cycle, the EDT stacked up in the pacing park behind it, and the DeviceRunner never reached its completion marker. The hang sample is unambiguous -- the GC thread sits in gcMarkDrain called from the interleaved drain this branch added, while the EDT sits in cn1PacingPark under cn1BibopAlloc. gcMarkDrain is not "drain the worklist". Every call to it also walks allObjectsInHeap from index 0 and re-pushes every object already marked this cycle, so that anything left marked-but-unscanned by an overflow gets its mark function run. That is the right shape for the handful of calls a cycle makes, and quadratic for a caller that drains PERIODICALLY: the grace pass drains once per half-worklist, which turned one O(heap) rescan per cycle into hundreds. Split the worklist loop out as gcMarkDrainWorklist and point the two interleaved drains at it. The passes still end with a full gcMarkDrain, which is what closes the fixpoint; nothing a periodic drain leaves behind escapes it. The local guard could not see this and now can. A translated micro-benchmark holds almost nothing in allObjectsInHeap, so an O(table) drain and a cheap one measure the same -- which is exactly why this passed here and failed on a real app. GcOverflowSpiralApp now retains a reference-carrying legacy population (Object[] blocks; the rescan skips objects with no mark function, so an earlier byte[] version of this fixture was free and proved nothing), and the VM reports graceFullDrains: full drains taken while a grace pass is running. Two per cycle is all a correct implementation makes, one to end each pass. Ablating the fix by pointing the interleaved drain back at gcMarkDrain takes that from 262 across 133 cycles to 1277, and the new assertion fails. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Gate against relying on ClassCastException, which ParparVM does not t… …hrow (#5531) (#5532) * Gate against relying on ClassCastException (issue #5531) ParparVM's CHECKCAST is unchecked: BC_CHECKCAST expands to nothing and BytecodeMethod.optimize() drops the instruction, so a failed cast hands the wrong pointer to the next instruction rather than throwing. Code written to catch the failure therefore behaves differently on iOS than on the simulator and Android -- in the reported case StringToReal.parseDouble ran with a Double as its String argument and died at cn1_intrinsics.h:90 reading java_lang_String_value out of it, uncatchable from Java. Making CHECKCAST throw would put a class check on every cast in every app, so the rule is instead that our own code must not depend on the exception. Adds CastSemanticsVerifier, which reports a CHECKCAST inside a try whose handler catches ClassCastException or a supertype, and wires it into the PR CI Java 8 leg over JavaAPI, core, android and ios. The rule is about the cast, not the handler: a catch(ClassCastException) with no cast under it is fine, because an explicitly thrown ClassCastException still propagates normally (java.util.AbstractSet.equals is that case). Casts already guarded by an instanceof are recognised and never reported, so the remedy the gate asks for satisfies it. Findings are held against scripts/cast-semantics-baseline.txt -- a ratchet of 187 pre-existing sites, most of them casts that merely sit inside a broad defensive guard. New code cannot add entries. Fixes two genuine cases found this way: - Purchase.getReceipts read a storage entry of any type as a List<Receipt> - PropertyIndex.newInstance returned any newInstance() as a business object Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Regenerate the cast-semantics baseline from a fresh build The baseline shipped in the previous commit was generated against a stale maven/android/target/classes, so three AndroidImplementation methods that exist in the source were missing from it and PR CI failed on code that was not new. Rebuilds android + ios with -Pcompile-android and regenerates: nothing is removed, the four Android findings are added. Adds --require-all, used by CI, so a module that is not built fails the gate instead of silently shrinking its coverage -- which is what let the stale build through. Also records why maven/java-runtime (Ports/CLDC11) is deliberately out of scope: it runs on a real JVM, where a failed cast does throw. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Start the Maven Central → Cloudflare R2 migration, and cut the releas… …e payload by 66% (#5497) * Start the Maven Central to Cloudflare R2 migration, and cut the release payload by 66% Sonatype rate-limits Maven Central consumption for commercial open-source projects, which makes Central a product 8000 availability risk rather than just a CI one. This is phase 0 (shrink what we publish) plus phase 1 (dual-publish to R2 alongside Central). Measured against the real published 7.0.258 (44 artifacts, 127 jars), a release was 229.5 MB. It is now 76.9 MB. At ~65 published versions a year that is the difference between 6 months and ~1.6 years of history inside R2's free tier, so retention becomes a safety net rather than something the product depends on. Extract the CSS compiler CLI from the Resource Editor CSS compilation ran by shelling out to the 43.5 MB codenameone-designer jar-with-dependencies -- the whole Swing editor, on every headless build. The CSS engine (CSSTheme, ResourcesMutator, WebviewSnapshotter, PollingFileWatcher) was already in css-compiler, but its NoCefCSSCLI entry point sets strictNoCef=true and has no -merge/-l/-watch, so it could not replace the real driver. CN1CSSCLI referenced no other Designer class, so it moves verbatim into a new 28 KB codenameone-css-cli module, launched with `java -cp` from a resolved classpath. The simulator gets that classpath via simulator.properties, the same channel SourceChangeWatcher already uses, so no archetype or generated-pom changes are needed and CSSWatcher still falls back for older projects. capture.js moves to css-compiler as well: ResourcesMutator loads it via getResourceAsStream but it only ever shipped inside the designer jar. Freeze the deprecated Resource Editor, javase-svg and sqlite-jdbc Excluded from publication and resolved on demand at a pinned version. Note that <maven.deploy.skip> does not achieve this on its own: central-publishing-maven- plugin runs with extensions=true, clears maven-deploy-plugin's executions, and filters solely on excludeArtifacts.contains(getArtifactId()). Both are set -- excludeArtifacts for today, the module properties for the plain deploy path. sqlite-jdbc also needed its dependency version pinned, because codenameone-javase depends on it and is published; otherwise every released javase pom would point at an artifact that was never uploaded. Stop attaching five unconsumed jar-with-dependencies classifiers javase, ios, windows, linux and parparvm published a shaded fat jar nothing consumes -- verified across this repo plus BuildDaemon, BuildCloud, BuildClient, OfflineBuilder and Deploy. The builders use the -bundle classifier, which is unchanged. The fat jars are still built on disk because the bundle steps read them; they are just no longer attached. Publish to R2 maven/scripts/r2/ uploads the tree central-publishing already stages (standard layout, four checksums, .asc) before it uploads to Central, so R2 succeeds even when Central false-negatives. maven-metadata.xml is regenerated from the bucket listing rather than the build, because a staging tree holds only the version just built and would clobber the accumulated history. Cloudflare caches 404s on the R2 custom domain, so CI polls are cache-busted; a Cache Rule sets Status Code TTL 400-599 to no-store as the primary fix. Also fix the Central confirmation poll, which looked for "platform-feature-catalog" while the module has always been "codenameone-platform-feature-catalog". On the false-negative path it would have spun for 30 minutes and failed a release that actually succeeded. Three CSS regression tests were orphaned in an Ant target CI never ran. They now run under surefire -- and two were broken. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Address review feedback and fix CI Fix the sqlite-jdbc pin, which did not actually pin (P1) maven/javase/pom.xml declared <version>${project.version}</version> on the dependency, which overrides dependencyManagement. Since the same change excludes sqlite-jdbc from publication, every released codenameone-javase pom would have requested a sqlite-jdbc that was never uploaded -- exactly the failure the pin was meant to prevent. Dropping the child version lets the parent's ${cn1.sqlite.jdbc.version} apply; the effective pom now resolves 7.0.263. Publish editor staging trees even when Central fails (P1) The editor R2 steps had no status function, so they inherited the implicit success() check and were skipped whenever the editor's Central deploy failed -- the exact outage R2 exists to survive. The three deploys now run with continue-on-error so their staging trees reach R2 first, the uploads are always(), and a final gate fails the job if Central rejected any of them. Metadata is regenerated once at the end instead of after each editor. Compare .sha1 sidecars, not artifact ETags (P2) An object's ETag equals its MD5 only for single-part uploads, and aws s3 cp goes multipart above 8MB. Comparing jar ETags would report every large artifact as a conflict when a tag is retried. The sidecars are 40 bytes, always single-part, and each uniquely identifies its artifact's content. Fix the malformed java -jar in the CSSWatcher fallback JVM options must precede -jar and the jar path must follow it immediately. The pre-existing ordering put -Dcli=true where the jar belongs, so the JVM treated that as the jar name. Only reachable on the legacy path now, but it never worked. Python nits from the code-quality bot: @functools.total_ordering on ComparableVersion, and an explicit namespace string instead of implicit literal concatenation inside a list. Fix CI - CompileCSSMojoTest stubbed getDesignerJar(), which CompileCSSMojo no longer calls; it now stubs getCssCliClasspath(). All 308 plugin tests pass. This slipped through because the earlier local run used -DskipTests. - Add the Codename One GPLv2 + Classpath Exception header to the relocated test sources, HeadlessTestSupport and capture.js. Their previous location was never checked, because the gate only inspects changed files. - Make check-copyright-headers.sh rename-aware. PropertiesUtil kept its Oracle header across a pure move, but the Oracle-allowance check looked the file up at its new path in the base revision and failed. Moving a file must not force it to be relicensed, so renames are now resolved to their base path. The remaining build-test (8) failure is an unrelated pre-existing flake: MCPReleaseBuildGateTest and MCPLoopbackTransportOpenTest contend over process-wide MCP state on fixed port 47899. Nothing under core-unittests or com/codename1/mcp is touched here, and jobs 17 and 21 pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Do not let one publication destination's failure skip the others Abort when the R2 head check itself fails (P1) `|| true` turned every head-object failure into empty output, which the next line read as "object not published yet". Throttling, an expired token or a transient endpoint error would therefore silently disable the immutability guard and let the copy overwrite an already-released artifact. Only a confirmed 404 now counts as absent; anything else aborts with the AWS error. Keep later editor deploys running after an R2 failure (P1) The previous commit stopped a Central failure from skipping the R2 upload, but introduced the mirror of that bug: the R2 steps had no continue-on-error, so a Game Builder R2 outage failed the job and GitHub's implicit success() guard skipped the Signing Wizard and Settings *Central* deploys -- turning one destination's outage into a partial release on the other. Every publish step is now non-fatal with an id, every conditional step carries an explicit status function, and a single gate at the end reports each destination and decides the job's verdict. One destination failing can no longer skip the other, and continue-on-error cannot mask a failure because nothing but that gate determines the result. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Keep R2 a complete release when Central fails, and never advertise a partial one Build the editors when Central failed but R2 succeeded (P1) The editor deploys were gated solely on the core Central deploy, and they are the steps that produce the editors' R2 staging trees. A genuine Central failure therefore left R2 with the new plugin but no matching-version codenameone-gamebuilder / -certificatewizard / -settings, which is exactly what cn1:gamebuilder and friends resolve per version. The JDK 17 setup and all three editor deploys now also proceed when r2_core succeeded. Check the artifact when its .sha1 sidecar is absent (P1) An interrupted recursive copy can leave a jar or pom behind without its sidecar. The conflict check looks only at sidecars, so it read that as "not published" and would have re-copied over the artifact. It now heads the artifact itself in that case and says so. The upload still proceeds, deliberately: a completed release always has both files, so artifact-without-sidecar means the earlier upload never finished and the version was never advertised in maven-metadata -- finishing it is the correct action, and now a visible one rather than a silent one. Do not regenerate editor metadata after a partial upload (P2) regen-maven-metadata.py discovers a version from any key under <artifact>/<version>/, so running it after a partially-failed editor copy would publish an incomplete release as <latest>/<release>. That step is now skipped when any editor upload failed; the final gate still fails the job. Verified against the live bucket: identical bytes re-upload idempotently, differing bytes for a published version abort with exit 1, and the sidecar-absent path reports the partial upload it is completing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Decide the release verdict in exactly one place The core Central failure was already caught -- "Fail when release did not complete" has no continue-on-error, its condition covers it, and every preceding step is continue-on-error so it is always reachable. But having a second verdict-deciding step in the middle of a workflow whose stated model is "one gate at the end decides" is a maintenance hazard, and it reads like a hole to anyone auditing the gate. It was in fact read that way in review. Fold the core Central check into the final gate and delete the mid-workflow step, so the invariant is now true rather than merely intended: nothing but that gate determines whether the job is red. Simulated across the outcome combinations: clean run passes; a false-negative Central deploy rescued by the confirm poll passes; deploy and confirm both failing fails; an R2 failure with Central fine fails; a single editor R2 failure fails. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Keep the legacy style editor reachable, and publish metadata only once a version is whole Restore Component Inspector's Edit Style on a frozen designer (P2) Freezing the Resource Editor broke a path I had not considered. editStyle() falls back through codename1.designer.jar (which PrepareSimulatorClasspathMojo no longer sets), then MavenUtils.findDesignerJarInM2 -- which looked for a designer matching the *current core version*. With the editor pinned at a version that no longer tracks core, that lookup could never hit, so on a CSS-less project the action just told the user to open the designer first. findDesignerJarInM2 now falls back to the newest designer actually present in m2, ordered numerically so 7.0.263 beats 7.0.9. That keeps the action working wherever a designer has been fetched, without making every cn1:run download 43MB to serve a feature most projects never touch. CSS projects are unaffected either way: that path opens the stylesheet in the IDE and returns before any of this. Publish R2 metadata only after every artifact is in place (P2) Core metadata was regenerated immediately after the core upload, which advertised the new plugin version as <latest>/<release> before the editors existed. Since the plugin resolves gamebuilder / certificatewizard / settings at its own version, a consumer could upgrade to a version whose matching tools could not resolve, and a later gate cannot retract metadata already published. There is now a single regeneration at the end, gated on the core and all three editor uploads succeeding, and the maven-metadata assertion moved there with it. Metadata is what makes a version discoverable, so it is written last -- a failed release leaves its artifacts orphaned but invisible, which is the recoverable direction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Add copyright headers to the two files this branch modified MavenUtils carried the NetBeans "To change this license header" placeholder and MavenUtilsTest had none. Both predate this branch, but the gate only inspects changed files, so touching them is what surfaced it. Also refresh the findDesignerJarInM2 javadoc, which still described the designer as a plugin dependency fetched on every build. That stopped being true when the editor was frozen, and the comment was the reason the version-matching assumption looked correct. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Record that the legacy Ant Designer build does not compile Review flagged that moving CN1CSSCLI and PropertiesUtil out of CodenameOneDesigner/src breaks the Ant build, whose javac.classpath never referenced the new Maven modules. The premise is right; the conclusion that this branch broke it is not. That build already could not compile on master. CN1CSSCLI there imports com.codename1.designer.css.CSSTheme, and CSSTheme lives only in maven/css-compiler/src -- not in CodenameOne/src, Ports/JavaSE/src or Ports/JavaSEWithSVGSupport/src, and css-compiler appears nowhere in nbproject/project.properties. So the classpath gap predates this branch by however long ago the CSS engine was extracted; these moves add files to a list that was already unsatisfiable. Nothing exercises the path either: ant.yml runs Maven despite its name, no ant check exists on PRs, and the BuildDaemon's three invocations of CodenameOne/build.xml's `release` target -- the only caller of this file -- are all commented out. Rather than make an unverifiable change to a build that is already broken, unused and untestable here, document the state so the next person does not have to rediscover it, and say what repairing it would actually take. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Fix invalid XML: double hyphens inside the comment I just added XML forbids "--" inside a comment. I pushed before validating, so the previous commit left CodenameOneDesigner/build.xml unparseable. Second time this trap has bitten in this branch; validate XML before pushing, not after. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Never advertise a version whose upload did not finish Gate discovery on a completion marker (P2) Skipping metadata regeneration on the run that failed only protected that run. Every later run rebuilds metadata from the same bucket listing, and discovery treated any key under <artifact>/<version>/ as a whole version -- so the next successful release would find the abandoned partial upload and publish it, possibly as <latest>. publish-staging-to-r2.sh now writes _cn1-upload-complete into each version directory only after that directory's copy succeeds, and regen-maven-metadata.py refuses to advertise a version without it, naming what it skipped. The marker is deliberately not a Maven artifact pattern, so no resolver will ever request it. Accept retries of non-latest tags (P2) The confirmation asserted that the tag being built is <release>. Re-running an older tag to repair it is legitimate, and metadata correctly keeps the newer version as <release> -- so a successful repair was reported as a failure. It now asserts discoverability (the tag appears in <versions>) and only reports on <release> when this tag really is the newest. Verified against the live bucket end to end: a complete upload is marked and advertised; a version with files but no marker is skipped by name and stays out of <versions>; re-running it completes the upload, writes the marker, and it then appears with <release> moving to it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Do not publish editors to Central when the core release did not get there Allowing the editors to build on the R2-only path (so R2 gets matching-version editors) had a consequence I missed: the deploy command still carries its *-central profile, and the editor resolves the just-built core from the local repository, so its Central upload can succeed even when that core/plugin version never reached Central. Central releases are immutable, so that leaves a permanently unresolvable editor artifact there. The editors now pass -DskipPublishing unless the core release actually reached Central and dual publish is on. skipPublishing still stages the artifacts, so the R2 upload is unaffected -- which is the whole point of building them on that path. The Central-only confirmation polls already use the same predicate, so they stay consistent with it. Simulated the branch: dual publish on with Central healthy publishes; a false negative rescued by the confirm poll publishes; Central genuinely failing stages for R2 only; and with dual publish off it never publishes to Central. Also record the invariants this review round established in the R2 README: a version is only real once marked complete, metadata is written last on purpose, and an editor is never published to a Central that lacks its core. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Make release completeness per release, not per directory The per-directory completion marker did not survive contact with the case it was meant to cover. Everything a tag publishes shares one version, and the plugin resolves its editors at its own version, so the coupling that matters is across artifacts: if the core uploads and marks itself complete but the Game Builder upload then fails, the core directory is complete in exactly the situation where the release is not. Skipping metadata on that run hid it only until the next tag, whose regeneration rebuilds from the same bucket listing and would have advertised the abandoned version. Replace it with a single release-level marker, written by mark-release-complete.sh only once the core reactor and all three editors are up. Discovery advertises no artifact at a version lacking it. Two mechanisms for one invariant is how the gap appeared, so the per-directory marker is removed rather than layered. Verified against the live bucket with the reported scenario: tag 1.0 complete, tag 2.0 uploads core only and is never marked, tag 3.0 complete. Regeneration after 3.0 reports "skipping 2.0" and serves probe-core listing 1.0 and 3.0 only -- previously 2.0 would have been advertised here. Re-running 2.0 with its missing editor and marking it then lists all three, with <release> correctly remaining 3.0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Document mark-release-complete.sh in the R2 README run order The local-run recipe still ended at regen, which would silently produce metadata listing nothing: an unmarked version is ignored rather than reported as an error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Close the last gaps in the publish chain Fail when a requested staging tree is missing or empty (P1) The script skipped it and exited 0. So an editor build that died before staging reported a successful R2 upload, the workflow's object check could pass against a POM left by an earlier partial upload, and the release was then marked complete without that editor -- defeating the completeness marker added a commit ago. A caller names a specific tree, so its absence means the build that produces it failed, and that is now an error. Honour CN1_DUAL_PUBLISH in the core deploy (P2) The three editors were changed to stage-only when Central is not a valid target, but the core deploy was not, so with dual publish off it kept creating immutable Central releases -- exactly what turning the flag off is meant to stop. All four deploys now derive skipPublishing the same way. Mitigate metadata checksum consistency (P2) Cannot be solved: a metadata set is a body plus four checksum objects and object storage has no multi-object atomic write. Made unlikely and self-healing instead -- uploads retry with backoff, checksums are written before the body so an interrupted run still serves the previous parseable metadata, the script is idempotent so a re-run repairs, and the 60s TTL keeps the window short. Both Maven's default checksumPolicy and the one in our generated poms are `warn`, so a consumer in that window is warned rather than broken. Documented as a limitation rather than a fix. Verified: missing and empty staging trees both exit 1; a published maven-metadata.xml and its .sha1 agree when compared as bytes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Fix two long-standing CN1CSSCLI bugs, and require the R2 confirmation before marking Copilot flagged these in its review summaries rather than as inline threads, so they were never surfaced by a reviewThreads query and I missed them for several rounds. Both are real, and both predate this branch: CN1CSSCLI moved with a zero-line content diff. getMergedFile ignored the cn1.cssMergeFile override if (System.getProperty("cn1.cssMergeFile") != null) { System.getProperty("cn1.cssMergeFile"); // value discarded } The property was read into a bare expression and thrown away, so anyone setting it silently got the path derived from the input file instead. contains() only ever detected a direct parent It recursed as contains(directory2, parent2), which drops directory1 from the comparison after one level, so contains(/a, /a/b/c) answered false. The copy-into-itself guard built on it could be walked straight past. Now recurses with directory1 held fixed. Both covered by CN1CSSCLILogicTest. Require the R2 confirmation before marking a release complete Review argued a partially staged reactor tree could be published. Tested it: with a mid-reactor compile failure the workflow's path, maven/target/central-staging, is never created, and the module-level directory that does appear is empty -- and this round already made both missing and empty an error. So that path is not reachable. But checking it surfaced a real gap next door: mark-release-complete did not depend on r2_core_confirm, which is the step that verifies the expected artifacts are actually on R2 rather than that an upload exited 0. Marking without it would let a truncated publish become discoverable. Marking now requires it, and metadata regeneration follows the marker rather than repeating its conditions. Also make aws_with_retry's unreachable terminal path an explicit raise instead of an implicit None fall-through. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Treat a cancelled step as a failure, and vary cache busting per run attempt Require success, not "not failure", before marking a release (P1) A cancelled or timed-out step reports outcome 'cancelled'. Combined with always() on the marker, all three `!= 'failure'` editor checks passed, so a job cancelled mid-upload could still mark the release complete and make a partial publish discoverable. Every check is now == 'success'. The final gate had the same flaw in mirror image: it flagged 'failure', so a cancelled step was reported as fine. It now classifies anything that is neither success nor a legitimate skip as a failure. Re-simulated across seven outcome combinations, including a cancelled editor upload and a cancelled marker step; both now fail the job, and a legitimately skipped metadata step still passes. Vary the cache-busting key per attempt (P2) GITHUB_RUN_ID is stable when a failed job is rerun, so a 404 cached during the first attempt was re-requested under the same key on the retry -- defeating the fallback in exactly the situation it exists for, if the Cloudflare no-store rule is missing. All five polls now append GITHUB_RUN_ATTEMPT as well. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Order qualifiers per Maven, and let cn1:update discover R2-only releases Sort prereleases below their final version The comparison padded the shorter version, so 7.0.259 sorted below 7.0.259-rc1 and an RC would have been advertised as <latest>/<release> after the final shipped. 1.0-alpha also sorted above 1.0.1. Comparison now follows Maven's run-out rule: a qualifier is below the release it qualifies, a further numeric segment is above it. The claim that this repo already uses rc-style tags is not true -- there are zero qualifier tags -- so this was latent rather than active. Fixed anyway because ordering drives both <release> and which versions the retention job trims. Point cn1:update at R2 first, with Central as fallback findLatestVersionOnMavenCentral read repo1 exclusively. With CN1_DUAL_PUBLISH off, new versions exist only on R2, so cn1:update would have kept reporting the last Central release and could never discover a newer one -- a footgun this PR creates by introducing the flag, even though the consumer-side migration is phase 2. Now reads the R2 metadata first and falls back to Central, so it works during dual publish, after the cut, and for versions released before the move. Overridable with -Dcn1.metadataUrl. Document the concurrency limitation rather than paper over it A single concurrency group serialises releases so metadata regeneration cannot interleave, but GitHub keeps only one pending member per group: a third tag pushed during a release replaces the pending one, and the middle tag is silently never published. Recorded in the R2 README with the operational mitigation (push tags singly; the release marker is the proof a tag published) and the real fixes (external FIFO dispatch, or a reconciliation job) -- none of which belong in this change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Add the copyright header to UpdateCodenameOneMojo It carried the NetBeans placeholder rather than a real header, and the gate only inspects changed files, so modifying it is what surfaced that. Third time this has come up in this branch; verified locally before pushing this time rather than after. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Do not offer a version the project cannot resolve Repointing cn1:update discovery at the Codename One repository moved discovery ahead of resolution. Generated projects still ship empty <repositories> and <pluginRepositories> -- that is phase 2 -- so cn1:update could select a version that exists only on R2, write it into cn1.version and cn1.plugin.version, and leave the next build unable to fetch it. Closing one footgun opened another. Discovery is now a function of what the project can actually reach: the Codename One repository is consulted only when the pom declares it for both dependencies and plugins, since the framework resolves from <repositories> and the plugin itself from <pluginRepositories>, and half the configuration is unusable. A project without it is offered only what Central can serve, and told why. This corrects itself rather than needing to be revisited: when phase 2 adds the repository to generated projects, they start discovering from it automatically. The predicate is extracted as a pure static so it is testable without a half-initialised MavenProject, and covered four ways: neither repository kind declares it, both do, and each one alone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Seed the frozen artifacts into R2, and rescue false-negative editor deploys Seed frozen artifacts (P1) Excluding sqlite-jdbc from publication while pinning consumers to 7.0.263 left that coordinate reachable only from Maven Central. That is not history that can be left behind: codenameone-javase declares it, and codenameone-javase is itself a runtime dependency of codenameone-maven-plugin, so ordinary plugin resolution reaches it. An R2 consumer would fail to resolve a release exactly when Central is throttled, which is the failure this migration exists to survive. The same applies to the frozen designer and javase-svg for cn1:designer. seed-frozen-artifacts.sh copies them from Central once, verifying every file against its .sha1 before upload so a truncated copy cannot be published under an immutability guard that would then refuse to repair it. Verified end to end against the live bucket: 24 files seeded for sqlite-jdbc:7.0.263 and both the pom and jar resolve over repo.codenameone.com. They deliberately carry no release-completion marker, because they are resolved by exact pinned version rather than discovered, and a release marker would claim a whole release is present when only one artifact is. regen-maven-metadata.py knows them as frozen: it advertises them from what is present, and no longer reports them as incomplete releases on every run. Rescue false-negative editor deployments (P2) The core deploy has a confirmation poll to survive central-publishing reporting failure for a bundle it accepted; the editors did not, so that false negative left the release permanently red, and a rerun could not repair it because Central rejects the already-published immutable version. The editor confirmations now have ids, run only when their deploy reported failure, and fail when they do not observe the artifact rather than merely logging. The gate accepts a clean deploy or a confirmation that saw it. Simulated: a false negative rescued by the poll passes, both failing fails, and a cancelled R2 upload still fails. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Derive the frozen pins instead of repeating them maven/pom.xml claims the designer pin "lives in ONE place", and until the seeding script that claim was true. seed-frozen-artifacts.sh then hardcoded 7.0.263 three more times, so bumping either pin would have moved the version consumers resolve while leaving the seeding on the old one -- silently, with no build failure, and discovered only when someone could not resolve an artifact after cutover. The script now reads the sqlite pin from <cn1.sqlite.jdbc.version> in maven/pom.xml and the designer pin from the cn1.designer.version @parameter default in AbstractCN1Mojo, and exits non-zero naming the file if either cannot be found, so a rename surfaces immediately rather than seeding the wrong version. Verified both derive 7.0.263 today, and that renaming the property produces the error rather than an empty version. The comment in maven/pom.xml now says the script reads from there, so the claim and the code agree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Give the editor confirmations the full propagation window they now gate on Making the editor confirmation polls gating left them at 15 x 20s = 5 minutes, while the workflow's own comment two lines above said propagation to repo1 can take 30+ minutes and the core poll already used 90 x 20s. While they were informational that mismatch was harmless; once their verdict decided the release, an accepted-but-slow bundle was reported as a failed Central publication -- and a rerun could not repair it, because Central rejects the already-published immutable version. That is the same unrecoverable state the rescue was added to prevent. All three editor loops now cover 30 minutes, matching the core, with the progress counters and the timeout message updated to match. The comment above them still described the step as "informational only" and a "best-effort sanity poll, not a gate", which stopped being true when I made it one. Replaced with what the step actually does and why the window has to be this wide. Bounded the job at 240 minutes. Each poll fires only when its own deploy failed, so the worst case is ~2h of polling on top of the build; that is well inside the 6h default, but a wedged release should fail within a shift rather than overnight. Co-Authored-By: 8000 Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
JavaScript port: emit a bundle that is ready to deploy as-is (#5466) * Keep the port's demo fixtures out of every app's JavaScript bundle JavaScriptPort.jar carries webapp/ so off-repo builds can find port.js, and the translator copies webapp/assets into every app it builds. That meant ~3.6MB of the port test app's own fixtures -- leather.res (2.1MB), chrome.res (778KB), video.mp4 (666KB), Handlee-Regular.ttf, Page.html, test.json -- landed in every JavaScript app's public web root. None of them is referenced by any class in the port or in CodenameOne core. Exclude them from the jar. They stay in the source tree, so in-repo builds and the javascript-screenshots suite still see them. The system themes HTML5Implementation.getNativeTheme() resolves (iOS7Theme.res, iPhoneTheme.res, iOSModernTheme.res, android_holo_light.res, tzone_theme.res) and CN1Resource.res are kept. Also delete the jar before rebuilding it: Ant's jar task treats an existing archive as up to date when it is newer than every input file, so on an incremental build a change to this include/exclude list is silently ignored and a stale jar ships. That cost a full debugging cycle here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Also exclude the port's own theme.res and the unused tzone theme assets/theme.res is the port test app's theme (1.4MB). Real apps ship their own theme.res at the bundle root and getResourceAsStream() deliberately prefers the root copy for theme.res / CN1Resource.res, so this one is never read -- confirmed against a running app, whose request log shows the root theme.res fetched and this one never touched. Shipping it also kept a shadowing hazard alive for no benefit. assets/tzone_theme.res is returned by no code path: getNativeTheme() only ever yields iOS7Theme / iPhoneTheme / iOSModernTheme / android_holo_light / AndroidMaterialTheme / androidTheme. The single mention of tzone_theme in the port is inside a comment, which is exactly why it survived the first pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Correct the theme rationale in the exclusion comment I had described iOS7Theme/iPhoneTheme/android_holo_light as "the themes getNativeTheme() resolves" and listed iOSModernTheme among them. That misread installNativeTheme(): the JS port defaults to the LEGACY pair (android_holo_light on an Android user agent, iOS7Theme elsewhere) so existing screenshot baselines stay comparable, and the modern themes are opt-in via ios.themeMode / and.themeMode / nativeTheme / javascript.native.theme. The modern .res files live in Themes/ and are staged per target, not from webapp/assets. No functional change: the same files are excluded and the same ones kept. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Ship the native themes from Themes/, so opting into a theme mode works HTML5Implementation.resolveNativeThemeResource() can return six themes -- iOS7Theme, iPhoneTheme, iOSModernTheme, androidTheme, android_holo_light, AndroidMaterialTheme -- but the bundle only carried three of them, so setting ios.themeMode / and.themeMode / nativeTheme / javascript.native.theme to anything modern (or to legacy on Android) hit the catch in installNativeTheme() and silently fell back to the legacy theme. The modern themes postdate the old TeaVM port; the ParparVM port never grew the wiring the other targets have. The build cannot pick one of the six: resolveNativeThemeResource() decides at RUNTIME from the browser user agent, so "modern" means iOSModernTheme in an iOS-like browser and AndroidMaterialTheme everywhere else. Ship the whole set instead. Take them from Themes/, the single source of truth kept in sync from the CSS sources by native-themes-sync.yml, the same way maven/ios folds iOSModernTheme.res into nativeios.jar and maven/javase copies the set onto its classpath -- rather than from the port's own copies under webapp/assets, which drift: the webapp's iPhoneTheme.res was already a stale version of the Themes/ one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Emit a JavaScript bundle that is ready to deploy as-is The bundle is meant to be unpacked straight into a web root, but two things made every consumer post-process it first. Zip the dist FLAT. zipDirectory() was passed the dist directory's own name as the zip root, so everything landed under "<MainClass>-js/" and had to be flattened before it could be served. The old cloud/TeaVM bundle was flat, so this also lets one deployment script handle both. Stop copying build descriptors into the bundle. ByteCodeTranslator copies every non-class input file into the output by basename, so a Maven-built app leaks META-INF/maven/**/pom.xml -- dependency list and all -- plus pom.properties and MANIFEST.MF. Harmless for the iOS target, where the output is a source tree; for the JavaScript target that output IS a public document root. None of them is an application resource. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Stop bundling the media stack and the developer diagnostics Three artifacts shipped in every JavaScript app that should not have. video.js + RecordRTC (~944KB) were already meant to be opt-in. VideoJS is only ever reached from HTML5Implementation.captureVideo(), which calls VideoJS.init() behind a try/catch and, when it fails, logs "VideoJS is not loaded, using default captureVideo behaviour. Add the javascript.includeVideoJS build hint..." and falls back. The libraries are lazy-loaded at runtime by ScriptTool. So the hint documented an opt-in that nothing on the build side implemented: every app paid for the library whether or not it captured video. Honour the hint. samplerate.min.js (~485KB) is unconditional dead weight. Nothing loads it by path; js/fontmetrics.js only probes for an already-defined Samplerate global ("use libsamplerate if it is available") and nothing ever defines one. vm_protocol.md and jso-bridge-dispatch-ids.txt are developer artifacts, and the translator's output directory becomes the app's PUBLIC web root. vm_protocol.md documents the worker boundary and is checked in under vm/ByteCodeTranslator/src/javascript/, so copying it into every deployed app published documentation nobody reads from there. jso-bridge-dispatch-ids.txt is a sidecar for the dispatch-id mangler, which is opt-in and off by default (-Dparparvm.js.manglesigs=1); the class walk that builds it is what the mangler needs, and nothing reads the emitted file at all. Both now require -Dparparvm.js.diagnostics. Note reachability cannot decide the media case: HTML5Implementation references the media classes itself, so they survive culling even in an app that plays and records nothing (HTML5MediaRecorder still had 129 references in a console bundle). The build hint is the right gate. The integration test opts into diagnostics so it still covers vm_protocol.md; the core-slice test drops that assertion, since translated_app.js already proves the translator ran. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Address review feedback on the bundle hygiene changes - isBuildMetadata() is path-aware: it only skips pom.xml / pom.properties / MANIFEST.MF that sit under a META-INF directory, so an app resource that happens to carry one of those names is still copied into the bundle. - Move the helper above copy()'s Javadoc; inserting it in between had detached the @PARAM i / @PARAM o lines from the method. - emitDiagnostics() parses parparvm.js.diagnostics for 1/true rather than testing for presence, matching the documented contract. - Report a failed prune instead of silently shipping the file: both js/videojs and js/samplerate.min.js verify the deletion happened and log a warning if it did not. - Add the missing licence headers to JavascriptBundleWriter and JavascriptCn1CoreCompletenessTest (check-copyright-headers). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fix GC storm on retained large-array loads (issue 5425, final Dtest s… …hape) (#5446) * Fix GC storm on retained large-array loads (issue 5425 final Dtest shape) Arrays above CN1_BIBOP_MAX_OBJECT always take the legacy allocation path, whose only byte-based GC signal was the pre-BiBOP 1MB isHighFrequencyGC re-arm -- 24x more aggressive than the BiBOP trigger. Retaining a few hundred 10K byte[] blocks (no garbage at all) kept full collection cycles starting every 200ms wait interval over the whole survivor set: "allocating the large arrays is triggering a global multiple times". Fix, all gated on the BiBOP build so -DCN1_DISABLE_BIBOP keeps the old shape verbatim: - cn1LegacyBytesSinceGc: per-cycle legacy allocation byte counter, reset in cn1BibopBeginGcCycle alongside bibopBytesSinceGc. - Event-driven trigger in codenameOneGcMalloc's legacy tail mirroring cn1BibopMaybeGc: async System.gc() when legacy volume crosses CN1_LEGACY_GC_TRIGGER_BYTES (24MB, -D overridable), so legacy-churn workloads still collect promptly. - isHighFrequencyGC re-arms at max(1MB, bibopGcTriggerBytes): with both paths event-driven on volume, the 200ms loop only needs to re-arm when the volume budget would have been crossed anyway. - CN1_GC_LOG_CYCLES env-gated cycle tracer in codenameOneGCMark (one stderr line per cycle) for CI assertions and on-device diagnosis. Guard: com.bench.LargeArrayLoad models the final Dtest (persistent small survivor set; retained large-array pass producing no garbage; wall-stretched phases so re-arm windows are machine-independent), and LargeArrayGcIntegrationTest gates on cycle count via the tracer -- measured 15 cycles unfixed vs 6 fixed, stable across runs, budget 10 -- plus RESULT parity with the host JVM. Cycle count is load-independent, unlike phase wall times which only inflate under mutator contention. Validated: integration test passes fixed / fails unfixed; gauntlet green (tortures byte-identical to host JVM, GcStress/MtStress in both cooperative and CN1_GC_SIGNAL_STOP modes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Add copyright headers to large-array GC guard test files check-copyright-headers flagged the two new test files; use the standard Codename One GPLv2 + Classpath Exception header. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Address review: exchange-reset for legacy byte counter, bracket gate Use the same atomic_exchange idiom as the BiBOP reset for cn1LegacyBytesSinceGc (racing adds land before the swap or charge the next cycle), and mirror cn1BibopMaybeGc's native-allocation-bracket gate on the legacy trigger: without conservative roots a bracketed thread must not trigger a cycle; with them, gating would starve the trigger for native-bracket allocators. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Address review: static legacy byte counter, document tracer gating cn1LegacyBytesSinceGc is file-local -- make it static. The CN1_GC_LOG_CYCLES tracer stays outside the CN1_DISABLE_BIBOP guard on purpose (A/B builds need the same cycle-count observable); document that instead of gating it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Surface app-side CN1SS diagnostics when the Linux screenshot gate fails A missing screenshot means a test never emitted its capture; the only evidence is the app's own CN1SS:WARN/ERR lines in app-output.log, which the gate job downloads but never printed. Dump the relevant lines on gate failure so a miss like the current ToastBarTopPosition one is triageable from the job log. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Address review: edge-trigger legacy GC schedule, stable segfault-retry marker Level-triggering called System.gc() (lock+notify) on every legacy allocation between the 24MB crossing and the counter reset; trigger on the crossing only, and drop the gcCurrentlyRunning suppression so a crossing during a running cycle schedules the follow-up pass instead of losing the edge. The integration test's segfault retry now keys on a controlled VM_RUN_EXIT marker instead of JUnit's assertion-message format. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Add poll-state tracing to ToastBarTopPosition for the Linux CI miss The Linux GTK suite times this test out with the screenshot never emitted (stage=capture-requested, both arches, reproducible). The per-second probes and reset-event prints show which wait the choreography dies in; the CN1SS:WARN prefix routes them into the gate-failure diagnostic dump automatically. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Add copyright header to ToastBarTopPositionScreenshotTest Touching the file pulled it into check-copyright-headers scope. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Fix Thread.sleep truncation by signals (Linux ToastBar CI failure root cause) usleep()/nanosleep() return EINTR when any signal lands and POSIX never restarts them (SA_RESTART explicitly excludes them). The conservative- roots collector signal-stops SLEEPING threads every cycle to scan their native stacks, so ParparVM's single-usleep Thread.sleep slept only until the next collection or any other process signal: measured Thread.sleep(3000) returning after ~20ms on the Linux port under allocation churn. java.util.Timer schedules through Thread.sleep, so every TimerTask fired almost immediately -- ToastBar's 10-minute "practically unexpiring" status dismissed itself mid-test, which is exactly the ToastBarTopPosition miss on both Linux arches (the toast kept vanishing between the test's polls, so its re-show loop never reached a stable capture). Sleep on a monotonic deadline (wall clock on the MSVC target, matching nanoTime) and resume across early wakeups, chunked below 1s because POSIX allows usleep(>=1e6) to fail with EINVAL (musl). The loop honors the interrupt flag, so Thread.interrupt() now shortens a sleep deliberately instead of relying on an accidental EINTR. Validated on the translated Linux port under Xvfb: Thread.sleep(3000) now sleeps 3000ms exactly (was ~20ms) and the ToastBar show/poll choreography runs to completion; full gauntlet green (10 tortures byte-identical to host JVM, GcStress x10 + MtStress x6 in both stop modes, LargeArrayGc cycle gate unchanged at 6). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Address review: reserved identifier, atomic tracer gate, temp cleanup, probe gating Rename __prevLegacyBytes (double underscore is implementation-reserved), make the CN1_GC_LOG_CYCLES lazy gate an atomic, delete the integration test's temp trees in a finally (the translated CMake build is large), and emit ToastBar diagnostics only during extended waits so healthy runs stay quiet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Add TimerLatency sleep/timer fidelity probe Guards the Thread.sleep signal-truncation defect: signed timer-fire deviations (the broken behavior is EARLY fire, which a lateness-only metric clamps away) plus a direct sleep-duration check; prints TIMER_LATENCY_OK / TIMER_LATENCY_DEGRADED. Verified OK on the fixed VM and the host JVM. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Fix MSVC build: useconds_t is absent on the clang-cl target The Windows clean-target job failed compiling the new Thread.sleep loop: cn1_win_compat's usleep shim has no useconds_t typedef. Cast to JAVA_INT as the pre-existing code did. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Saturate the Thread.sleep deadline instead of overflowing Thread.sleep(Long.MAX_VALUE) is a real park-this-thread idiom; the new deadline computation would wrap negative and return immediately. Clamp delta and deadline to LLONG_MAX. SleepEdge guards the edge cases: tiny sleeps stay accurate, a Long.MAX_VALUE sleep parks, and interrupt() wakes it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Preserve historical interrupt semantics in the Thread.sleep resume loop This VM has never delivered InterruptedException from sleep -- interrupt() only sets the flag -- so the resume loop must not exit early on it either: waking without the exception would be a third behavior, neither historical nor Java's. Proper interrupt delivery is a separate change. SleepEdge now asserts only duration fidelity and the Long.MAX_VALUE park. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Restore review fixes clobbered by a stale local snapshot restore A local A/B build step restored cn1_globals.m from an outdated scratchpad snapshot, silently reverting four already-reviewed changes that then shipped in 811bce7. This re-applies them: - cn1LegacyBytesSinceGc is static again (single-TU internal linkage) - cn1BibopBeginGcCycle resets it with the same atomic_exchange idiom as the BiBOP counter (a racing fetch_add is never dropped) - the legacy trigger is edge-triggered on the 24MB crossing instead of level-triggered (at most one System.gc() schedule per cycle window, no !gcCurrentlyRunning suppression that would lose the edge) - the native-allocation-mode gate mirrors cn1BibopMaybeGc exactly, including the CN1_CONSERVATIVE_GC_ROOTS carve-out Also: README wording fix for the TimerLatency gate and a design note in TimerLatency.java documenting the deliberate one-Timer-per-task shape (ParparVM's Timer spawns a thread per schedule()). Revalidated locally: LargeArrayLoad 6 GC cycles (x2 runs), RESULT=1099 host parity, TIMER_LATENCY_OK, SLEEP_EDGE_OK, MapTorture MATCH, GcStress/MtStress clean in both stop modes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Harden legacy GC counter width and sleep parking for the Windows target Two review findings on the Windows (LLP64 / no-signal-stop) target: - cn1LegacyBytesSinceGc was _Atomic long, which is 32-bit under LLP64; the counter accumulates raw bytes between cycle resets, so it now uses long long (with the fetch_add operand and prevLegacyBytes widened to match) so the edge-trigger comparison can never see a wrapped value. - Thread.sleep published threadActive=FALSE without a cooperative park capture (a pre-existing pattern this PR's rewrite kept). Under CN1_CONSERVATIVE_GC_ROOTS the collector scans a parked thread's native stack either via the capture or a signal stop, and Windows has no signal-stop fallback -- an uncaptured sleeper's native-stack roots went unscanned for the cycle. sleep now runs CN1_GC_PARK_CAPTURE before parking, per the CN1_YIELD_THREAD contract, and drops the capture on resume exactly like CN1_RESUME_THREAD. Revalidated locally: LargeArrayLoad 6 GC cycles (x2), RESULT=1099 host parity, SLEEP_EDGE_OK, TIMER_LATENCY_OK (max_early_ms=0, min_sleep1500_ms=1500), MapTorture byte-identical to the host JVM, GcStress x4 + MtStress x3 in cooperative and CN1_GC_SIGNAL_STOP=1 modes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Make the Windows sleep clock monotonic; add cleanup and drift breadcrumbs - The sleep deadline on the MSVC/clang-cl target was computed from gettimeofday, a wall clock: a system time step mid-sleep stretched or cut the remaining duration. cn1_win_compat gains cn1_monotonic_micros (QueryPerformanceCounter, overflow-safe split divide) and cn1SleepNowMicros now uses it, keeping windows.h out of translated compilation units per the compat layer's rule. - LargeArrayGcIntegrationTest's best-effort temp cleanup no longer fails silently: one stderr line per root reports the first failed deletion so leaked translated build trees leave a breadcrumb on self-hosted runners. - LargeArrayLoad and its CI twin LargeArrayGcApp now carry explicit KEEP IN SYNC notes in both directions, documenting that the shared host-JVM-asserted RESULT= value turns one-sided edits into a visible parity break rather than silent drift. Revalidated locally: LargeArrayLoad 6 GC cycles (x2), RESULT=1099, SLEEP_EDGE_OK, TIMER_LATENCY_OK; vm/tests module test-compiles clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Add the Codename One GPLv2+Classpath header to cn1_win_compat The copyright gate checks every file a PR touches; these two predate the gate and entered the diff via the monotonic-clock shim. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Drop the lazily-written freq cache in cn1_monotonic_micros The unsynchronized static was a formal C data race across concurrent sleepers; QueryPerformanceFrequency is a cheap userspace read of a boot constant, so just call it per invocation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Latch the legacy GC trigger per cycle; throw on negative Thread.sleep Two review findings: - The edge-triggered legacy volume trigger could lose its edge for good: a crossing that landed while the allocating thread was inside a native-allocation bracket (gated when conservative roots are off) was skipped, and every later allocation saw prev already above the threshold, deferring collection to the GC thread's 30s idle wake. The trigger is now LEVEL-triggered on the counter and LATCHED (cn1LegacyGcScheduled, cleared at cycle begin after the counter reset) so a suppressed crossing is retried by the next out-of-bracket legacy allocation on any thread while System.gc() is still scheduled at most once per cycle window. - Thread.sleep(long) now throws IllegalArgumentException for negative millis per the JDK contract (the (millis, nanos) overload already did). The guard lives in Java -- sleep(long) delegates to a private native sleepImpl -- so the exception class is retained by the translator's reachability pass wherever sleep is used. SleepEdge now asserts the negative case. Revalidated locally: LargeArrayLoad 6 GC cycles (x2), RESULT=1099, SLEEP_EDGE_OK (negativeThrew=true), TIMER_LATENCY_OK, MapTorture parity, GcStress x3 + MtStress x2 in both stop modes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Bind sleepImpl in the JavaScript target runtime and registry Thread.sleep(long) became a Java wrapper delegating to the native sleepImpl(long), so the JS translator now emits calls to cn1_java_lang_Thread_sleepImpl_*; the runtime binds those (keeping the old sleep aliases for previously-translated bundles), the native registry lists the new symbol, and the opcode coverage test asserts it. Also: the Linux gate's diagnostic grep uses -E with standard alternation instead of GNU basic-regex escapes. JavascriptOpcodeCoverageTest passes (3/3); LargeArrayGcIntegrationTest re-verified green at both e2a6c74 and 0a13eca on this machine (the one local failure was CPU-starvation flake from a concurrent full module build, not a code regression). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 * Add the GPLv2+Classpath header to JavascriptOpcodeCoverageTest The copyright gate checks every PR-touched file; this pre-existing test entered the diff via the sleepImpl binding assertion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29 --------- Co-authored-by: Claude <noreply@anthropic.com>
Fix release CI: bump certificatewizard poms to release version (#5355) The release-on-maven-central workflow's "Deploy Signing Wizard editor" step failed with 403 Forbidden because update-version.sh never bumped the scripts/certificatewizard poms off 8.0-SNAPSHOT. mvn deploy therefore targeted the Central *snapshots* repository, which the release job has no rights to publish to. The Game Builder editor (scripts/gamebuilder) — same out-of-reactor, own-coordinates shape — was already handled; certificatewizard was added later without the matching wiring. Mirror the gamebuilder block: rewrite the three module versions plus the cn1.version/cn1.plugin.version the editor builds against, and git add the tree before committing. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PreviousNext