Skip to content

Module-level state races: _handle, install/uninstall, init-vs-dispose #4

@AndresL230

Description

@AndresL230

Summary

Multiple module globals are read/written without locks:

1. _handle race

_init.py:78, 225 reads/writes _handle without a lock. Two threads racing init() can both pass the if _handle is not None guard, both call install(), and orphan the first thread's transport + aggregator (the background timer thread leaks).

2. install/uninstall vs. init/dispose desynchronization

Independent of (1), the patches and the callback registration can desynchronize. Thread A in dispose() is mid-_unpatch_* while Thread B in init() calls install(on_event) and sees _installed=True (set by A but not yet cleared). B short-circuits — the new init has no patches.

Fix

Guard init(), dispose(), install(), and uninstall() with a single module-level threading.RLock. Add tests that race both pairs.

Files

  • recost/_init.py
  • recost/_interceptor.py
  • tests/test_init.py

Priority

P1 — produces a silently-broken SDK ("ingest is no-op") in production restart scenarios.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Should fix before next releasebugSomething isn't workingruntimeRuntime / behavioral bug

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions