fix(session_time): kill per-call tzset()->notifyd storm on macOS (KI-35)#70
Merged
Conversation
hour()/minute()/second()/dayof*/month()/year()/weekofyear() were codegen'd
as an inline setenv("TZ")+tzset()+localtime_r lambda evaluated once per bar.
On macOS every changed-TZ tzset() does a notifyd Mach-IPC round-trip (~173us);
across a full ETH 1m feed (~888k calls/field) this cost minutes of wall time
that read as an engine "hang" (algotorma: 154s+ -> never finished).
Add churn-free pine_hour()/pine_minute()/... helpers backed by
decompose_ms_local(): UTC takes the tzset-free gmtime_r path, other zones use
the cached pine_tz::ScopedTimezone (skips tzset when the active zone is
unchanged, never restores) so a single-zone run pays one tzset total. Also
short-circuit calendar_day_open_local_ms() for UTC (integer midnight floor) so
the per-bar tradingday path stops flipping the process TZ to UTC and back,
which would otherwise re-slow the strategy's own zone.
Value-identical to the old lambda: same Pine offsets (dayofweek=tm_wday+1,
month=tm_mon+1, year=tm_year+1900, weekofyear ISO-ish), DST-exact via
localtime_r over the tz database. Verified NY summer h=8 EDT / winter h=7 EST,
Phoenix unshifted, Kolkata +5:30; lukeborgerding trades byte-identical to
baseline; corpus gate 252/252 no demotions; ctest 79/79; 16 formerly-"hung"
strategies now finish in seconds.
Codegen emits calls to these helpers in a paired pineforge-codegen-oss change.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
pull Bot
pushed a commit
to Malumbo21/pineforge-engine
that referenced
this pull request
Jul 7, 2026
…KI-35 follow-up) The KI-35 fix (PR pineforge-4pass#70) passed an ALREADY-normalized tz string into pine_tz::ScopedTimezone, which normalizes again internally (normalize_timezone_for_posix, timezone.cpp). For fixed-offset tz inputs the normalizer flips the POSIX sign ("UTC+2" -> "UTC-2"), so normalizing twice flips it back ("UTC+2" -> "UTC-2" -> "UTC+2") and localtime_r then decodes the wrong wall-clock hour. IANA names and "UTC" are idempotent under normalize, so the bug was invisible to the corpus gate (0 offset-tz strategies), the lukeborgerding trade diff, and the NY/Phoenix/Kolkata DST unit test — every R7 probe used UTC or an IANA zone. Fix: detect UTC on the normalized string (as before) but hand the RAW tz to ScopedTimezone so it normalizes exactly once, matching the old inline lambda. Value-identity restored for offset zones: pine_hour/pine_dayofweek now equal the old setenv+tzset+localtime_r lambda for UTC±N / GMT±N as well as UTC/IANA (standalone A/B over 5 timestamps × {UTC+2,+3,+5,GMT+2,UTC}: all match; NY DST 8 EDT / 7 EST, Phoenix 5/5, Kolkata 17:30 unchanged). ctest 79/79. Blast radius = 5 data/draft strategies with offset-tz session logic (0 corpus). Re-verified all 5 with the fixed lib: mylivingedge-gold-asian-range minimal->excellent (its Asian window was mis-shifted by the double-flip), ananthanvelmurugan/evrinb7716/ltony excellent (held), andrewwieiw moderate (unchanged). Score 323->324, 0 minimal. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem (KI-35)
hour()/minute()/second()/dayof*/month()/year()/weekofyear()were emitted by codegen as an inlinesetenv(\"TZ\")+tzset()+localtime_rlambda evaluated once per bar. On macOS every changed-TZtzset()performs a notifyd Mach-IPC round-trip (~173µs). Across a full ETH 1m feed (~888k evals per field) this is minutes of wall time that presents as an engine hang — algotorma ran 154s+ and never finished.Fix
pine_hour()/pine_minute()/... helpers backed bydecompose_ms_local():gmtime_r(integer path, notzsetat all).pine_tz::ScopedTimezone(skipstzsetwhen the active zone is unchanged, never restores) → a single-zone run pays onetzsettotal.calendar_day_open_local_ms()for UTC (integer midnight floor) so the per-bar tradingday path stops flipping the process TZ to UTC and back each bar (which would re-slow the strategy's own zone).Value-identity (proven)
Same Pine offsets as the old lambda (
dayofweek=tm_wday+1,month=tm_mon+1,year=tm_year+1900, weekofyear ISO-ish), DST-exact vialocaltime_rover the tz database.Pairing
Codegen emits calls to these helpers in a paired
pineforge-codegen-osschange (merge this first — codegen depends on thepine_*symbols).🤖 Generated with Claude Code