Skip to content

Register the SIP queue thread before pjsua_create() - #36

Closed
PetrShtuka wants to merge 1 commit into
petrshtuka/fix-push-before-loginfrom
petrshtuka/fix-pjlib-thread-registration
Closed

PetrShtuka wants to merge 1 commit into
petrshtuka/fix-push-before-loginfrom
petrshtuka/fix-pjlib-thread-registration

Conversation

@PetrShtuka

Copy link
Copy Markdown
Owner

Stacked on #35 — review that one first.

Problem

Starting the engine aborts the process when something else has already initialized PJLIB:

Assertion failed: (!"Calling pjlib from unknown/external thread. You must register external threads with pj_thread_register() before calling any pjlib functions."), function pj_thread_this, file os_core_unix.c, line 977.

pjsua_create() opens with pj_init(), which registers its caller only when it is the call that brings PJLIB up. If the host — or a second PJSIP-based stack in the same process — already initialized PJLIB, pj_init() only bumps its reference count and the thread running the start stays unknown to PJLIB. The first per-thread call inside pjsua_create() then aborts. A push-driven start runs on a transient sipQueue worker (-wakeRegistration → -startWithError:), so this is reachable from an incoming call.

ensurePJThreadRegistered() could not cover it: it returns early while the library's own gPJInitialized flag is still false, and it has to, because pj_thread_is_registered() is not safe before PJLIB is up.

Changes

  • -startEngineLocked initializes PJLIB itself, registers the calling thread, and only then calls pjsua_create(). Both failure paths release that reference.
  • The teardown releases it after pjsua_destroy() has released the one pjsua_create() took (confirmed against the vendored binary: pjsua_destroy2 ends with pj_log_pop_indent and pj_shutdown), so PJLIB stays up for whoever else initialized it.
  • Two regression tests: a push-driven start with credentials, and a start with PJLIB already initialized by a second consumer. The second aborts the test process without this change.

Testing

./Scripts/run-package-tests.sh — 84 registry tests and 65 package tests, 0 failures. Reverting only the library hunks makes testEngineStartsWhenSomethingElseAlreadyInitializedPJLIB abort, which is how the fix was verified.

🤖 Generated with Claude Code

pjsua_create() opens with pj_init(), which registers its caller only when
it is the call that brings PJLIB up. When something else in the process
has already initialized PJLIB -- the host, or a second PJSIP-based stack
-- pj_init() only bumps its reference count, and the thread running the
start stays unknown to PJLIB. The first per-thread call inside
pjsua_create() then aborts with "Calling pjlib from unknown/external
thread". A push-driven start runs on a transient sipQueue worker, so this
is reachable from -wakeRegistration.

PJLIB is now initialized first, which makes the existing thread
registration legal, and the teardown releases that reference after
pjsua_destroy() has released its own. PJLIB stays up for whoever else
initialized it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@PetrShtuka
PetrShtuka deleted the branch petrshtuka/fix-push-before-login September 13, 2026 06:16
@PetrShtuka PetrShtuka closed this Sep 13, 2026
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