Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(session_time): kill per-call tzset()->notifyd storm on macOS (KI-35)#70

Merged
luisleo526 merged 1 commit into
mainfrom
fix/ki35-tzset-notifyd-storm
Jul 7, 2026
Merged

fix(session_time): kill per-call tzset()->notifyd storm on macOS (KI-35)#70
luisleo526 merged 1 commit into
mainfrom
fix/ki35-tzset-notifyd-storm

Conversation

@luisleo526

Copy link
Copy Markdown
Collaborator

Problem (KI-35)

hour()/minute()/second()/dayof*/month()/year()/weekofyear() were emitted by codegen as an inline setenv(\"TZ\")+tzset()+localtime_r lambda evaluated once per bar. On macOS every changed-TZ tzset() 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

  • Add churn-free pine_hour()/pine_minute()/... helpers backed by decompose_ms_local():
    • UTC → tzset-free gmtime_r (integer path, no tzset at all).
    • other zones → cached pine_tz::ScopedTimezone (skips tzset when the active zone is unchanged, never restores) → a single-zone run pays one tzset total.
  • 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 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 via localtime_r over the tz database.

  • DST unit test: NY summer h=8 (EDT-4), NY winter h=7 (EST-5), Phoenix unshifted, Kolkata +5:30, UTC/empty→12.
  • Trade diff: lukeborgerding (uses hour/minute/second) byte-for-byte identical to baseline.
  • Corpus gate: 252/252 ran, 0 fail, excellent=239 strong=12 — byte-exactly the baseline, no demotions.
  • ctest: 79/79.
  • Formerly-"hung": 16 strategies now finish in seconds (algotorma 154s+ → 1.5s, 282/282 100%).

Pairing

Codegen emits calls to these helpers in a paired pineforge-codegen-oss change (merge this first — codegen depends on the pine_* symbols).

🤖 Generated with Claude Code

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]>
@luisleo526 luisleo526 merged commit 6b43e73 into main Jul 7, 2026
5 checks passed
@luisleo526 luisleo526 deleted the fix/ki35-tzset-notifyd-storm branch July 7, 2026 04:50
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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant