lockfree/smr/nebr/thread_id
Platform-abstracted thread identifier for thread neutralization.
DEBRA+ needs a way to (a) identify the calling thread and (b) interrupt a stalled remote thread so the reclaimer can advance. The mechanism is platform-specific:
- POSIX (Linux, macOS, BSD): the identifier is a Pthread. Remote neutralization is done by pthread_kill(tid, SIGUSR1) which delivers an asynchronous signal; the SIGUSR1 handler in signal.nim flips the target slot's pinned/neutralized flags. The sendSignal proc is the primary entry point on POSIX.
- Windows: the identifier is the raw OS thread ID (uint32, the DWORD returned by GetCurrentThreadId). It is OS-managed — no kernel handle is allocated, duplicated, or stored. Remote neutralization uses SuspendThread / ResumeThread: the scanner opens a temporary handle via OpenThread(THREAD_SUSPEND_RESUME), suspends the target, directly flips its slot via the same pointer-arithmetic walk that the POSIX signal handler uses, resumes, then CloseHandles in a finally block. Handle lifetime is the duration of a single neutralizeRemoteSlot call. This avoids the async-signal-safety constraints of the POSIX path; signal.nim's handler-install machinery is a no-op on Windows. An earlier v0.10.0-dev design stored a duplicated handle in the slot; that design carried a bounded handle leak on slot churn and a use-after-close race between scanner and unregister. The on-demand OpenThread/CloseHandle design eliminates both by construction.
Deadlock constraint (both platforms): do NOT call DEBRA reclamation while holding a lock that other DEBRA-using threads might be blocked on. On POSIX the SIGUSR1 handler runs in the target's context and may need to acquire shared state; on Windows a SuspendThread'd target that holds a lock will deadlock the scanner if the scanner waits on that lock. Same hazard, different mechanism.
Cross-thread state mutation (the slot flip) lives in neutralizeRemoteSlot so the call site in neutralize.scanAndSignal stays platform-neutral.
ThreadId
Section titled “ThreadId”ThreadId = objectPlatform-abstracted thread identifier (POSIX arm).
Procedures & Functions
Section titled “Procedures & Functions”currentThreadId
Section titled “currentThreadId”proc currentThreadId(): ThreadId {.raises: [], tags: [], forbids: [].}Get the ThreadId of the current thread.
isCurrent
Section titled “isCurrent”proc isCurrent(tid: ThreadId): bool {.inline, raises: [], tags: [], forbids: [].}Non-allocating identity check: returns true iff tid refers to the calling thread. Mirrors the Windows arm — on POSIX pthread_self() is cheap (no kernel call), so the no-alloc guarantee is structural rather than a meaningful perf win, but the API parity matters for the cross-platform call site in neutralize.scanAndSignal.
==
proc `==`(a, b: ThreadId): bool {.raises: [], tags: [], forbids: [].}Compare two ThreadIds for equality.
proc `$`(tid: ThreadId): string {.raises: [], tags: [], forbids: [].}Render a ThreadId for diagnostics. The auto-generated stringifier tries to format the underlying Pthread (an opaque struct pointer on some POSIX platforms) as an integer, which fails to compile; we render the pointer bits as hex instead. Used by unittest2.check when an equality assertion involving a ThreadId fails.
isValid
Section titled “isValid”proc isValid(tid: ThreadId): bool {.raises: [], tags: [], forbids: [].}Check if this ThreadId represents a valid thread.
sendSignal
Section titled “sendSignal”proc sendSignal(tid: ThreadId; sig: cint): cint {.raises: [], tags: [], forbids: [].}Send a signal to the thread identified by tid. POSIX only.
Returns 0 on success. Returns ESRCH immediately when tid equals InvalidThreadId; otherwise returns the error code from pthread_kill.
Note: POSIX explicitly leaves pthread_kill with an invalid pthread_t as undefined behavior. Apple's libpthread happens to validate and return ESRCH, but glibc dereferences the handle and may segfault. Callers must only pass either a live thread's id or InvalidThreadId; passing a fabricated non-zero handle is unsupported.
unsafeThreadId
Section titled “unsafeThreadId”proc unsafeThreadId(handle: Pthread): ThreadId {.raises: [], tags: [], forbids: [].}Create a ThreadId from a raw Pthread handle. For testing purposes only.
unsafeThreadIdFromInt
Section titled “unsafeThreadIdFromInt”proc unsafeThreadIdFromInt(value: int): ThreadId {.raises: [], tags: [], forbids: [].}Create a ThreadId from an integer value. For testing purposes only - creates a fake thread ID.
Templates
Section titled “Templates”InvalidThreadId
Section titled “InvalidThreadId”template InvalidThreadId(): ThreadId