C SDK: Make library thread-safe with mutex-protected allocator#51
Merged
Conversation
- Add mutex protection for g_allocator using pthread on Unix and CRITICAL_SECTION on Windows - Protect js_set_allocator(), js_get_allocator(), js_malloc(), js_realloc(), js_free() - Add js_init_allocator_mutex() and js_destroy_allocator_mutex() for lifecycle management - Update js_init() to initialize mutex - Update js_cleanup() to call js_regex_cache_clear() and destroy mutex - Add comprehensive thread safety tests with concurrent validation - Update documentation in headers and README with thread-safety guidelines - Link pthread on Unix systems in CMakeLists.txt Co-authored-by: clemensv <542030+clemensv@users.noreply.github.com>
- Replace manual initialization flag with pthread_once on Unix - Use InitOnceExecuteOnce on Windows for thread-safe initialization - This ensures no race condition even when multiple threads call allocator functions before js_init() - Add comment about one-time initialization semantics in js_destroy_allocator_mutex() - All tests still pass Co-authored-by: clemensv <542030+clemensv@users.noreply.github.com>
- Clarify that js_cleanup() can be followed by js_init() for reinitialization - Document that pthread_once/InitOnceExecuteOnce persist for process lifetime - This addresses code review feedback about cleanup/reinit behavior Co-authored-by: clemensv <542030+clemensv@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix thread safety issues in C SDK
C SDK: Make library thread-safe with mutex-protected allocator
Dec 9, 2025
…ry allocation The default malloc/free and realloc functions are already thread-safe on all modern platforms. We only need mutex protection when changing the allocator itself via js_set_allocator(). This removes the per-allocation locking overhead and fixes a potential issue with CRITICAL_SECTION initialization on Windows.
c696139 to
cd34c81
Compare
added 2 commits
December 9, 2025 12:06
SRWLOCK has significant advantages over CRITICAL_SECTION:
- Zero-initialized by default (SRWLOCK_INIT = {0})
- No initialization function call needed
- No destruction needed
- Very lightweight and fast
- Available on Windows Vista and later
This simplifies the code and eliminates potential issues with
uninitialized or improperly destroyed CRITICAL_SECTION objects.
_beginthreadex returns handle (non-zero) on success and 0 on failure, whereas pthread_create returns 0 on success and non-zero on failure. Fixed by using a helper function that returns consistent error codes: 0 for success, non-zero for failure.
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.
The C SDK had race conditions in global allocator access and was missing cleanup of the regex cache. Multiple threads calling validation functions concurrently could corrupt allocator state.
Changes
Thread-safe allocator access:
g_allocatorreads/writes withpthread_mutex_t(Unix) /CRITICAL_SECTION(Windows)pthread_once/InitOnceExecuteOncefor race-free mutex initializationjs_malloc(),js_realloc(),js_free(),js_set_allocator(),js_get_allocator()Cleanup lifecycle:
js_cleanup()now callsjs_regex_cache_clear()to free compiled patternsjs_init()explicitly initializes allocator mutex (optional but recommended)Documentation:
types.handjson_structure.hjs_set_allocator()andjs_cleanup()must not be called during validationTesting:
Usage
Notes
std::mutex) now properly cleaned uppthread_oncepersists for process lifetimejs_cleanup()butpthread_oncestate remainsOriginal prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.