[WIP] perf: use rwlock for scope data access - #1877
Conversation
Instructions and example for changelogPlease add an entry to Example: ## Unreleased
### Features
- use rwlock for scope data access ([#1877](https://github.com/getsentry/sentry-native/pull/1877))If none of the above apply, you can opt out of this check by adding |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## jpnurmi/ref/attachments #1877 +/- ##
===========================================================
+ Coverage 74.36% 75.04% +0.68%
===========================================================
Files 104 104
Lines 25606 26453 +847
Branches 4567 4693 +126
===========================================================
+ Hits 19042 19852 +810
- Misses 5269 5297 +28
- Partials 1295 1304 +9 🚀 New features to boost your workflow:
|
495cd85 to
05fe336
Compare
| SET(Key, sentry_value_new_string(Source)); \ | ||
| } \ | ||
| } while (0) | ||
| #define PLACE_STRING_VALUE(Key, Source) \ | ||
| do { \ | ||
| if (IS_NULL(Key) && sentry_value_get_length(Source) > 0) { \ |
There was a problem hiding this comment.
Bug: The release and environment fields are now explicitly set to null in the Dynamic Sampling Context (DSC) when not provided, instead of being omitted, which is a behavioral regression.
Severity: MEDIUM
Suggested Fix
Modify the logic that updates the Dynamic Sampling Context (DSC), such as in data_set_release, to check for null values. If the value is null, the key should be omitted from the DSC object rather than being set with a null value, restoring the previous behavior.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: src/sentry_scope.c#L1750-L1755
Potential issue: When `options->release` or `options->environment` are `NULL`, the new
code in `sentry__scope_apply_options` sets the corresponding keys in the Dynamic
Sampling Context (DSC) to an explicit `null` value. Previously, these keys would have
been omitted from the DSC. This change in behavior can affect DSC serialization and any
downstream consumers that expect absent keys instead of `null` values. While event
application is protected by a check (`PLACE_STRING_VALUE`), the DSC is not, leading to
this regression.
|
|
||
| cleanup_data(data); | ||
| init_data(data); | ||
|
|
||
| sentry_value_decref(data->propagation_context); | ||
| sentry_value_decref(data->dynamic_sampling_context); | ||
| data->propagation_context = propagation_context; | ||
| data->dynamic_sampling_context = dynamic_sampling_context; | ||
| data->trace_managed = trace_managed; | ||
| } | ||
| } |
There was a problem hiding this comment.
Bug: If breadcrumb ringbuffer allocation fails in clear_data due to OOM, the scope is left in a silently degraded state where breadcrumbs no longer function.
Severity: LOW
Suggested Fix
Check the return value of sentry__ringbuffer_new within init_data. If the allocation fails, the function should handle the error, possibly by attempting to restore the old state or by logging the failure, rather than silently leaving data->breadcrumbs as NULL.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: src/sentry_scope.c#L253-L271
Potential issue: The `clear_data` function allocates a new breadcrumbs ringbuffer via
`sentry__ringbuffer_new` while holding a write lock. If this memory allocation fails
(e.g., under OOM conditions), `data->breadcrumbs` is set to `NULL` without any error
handling. Subsequent operations on breadcrumbs will silently fail as they are coded
defensively to handle a `NULL` ringbuffer, preventing a crash but leaving the scope in a
degraded state where breadcrumb functionality is lost without any notification.
3c7c966 to
4948141
Compare
Add a non-recursive sentry_rwlock_t abstraction backed by platform reader/writer locks. Cover static and dynamic initialization, reader sharing, writer exclusion, and mixed read/write stress in unit tests.
Convert the POSIX rwlock operations from statement macros to static inline functions so callers can use them from scoped lock macro expressions.
Add a per-scope rwlock and use scoped read/write locking for simple by-value fields. Keep borrowed scope properties unchanged for now.
Add ref-style scope accessors for user and fingerprint values so callers get a retained reference that was incremented under the scope read lock. Guard user and fingerprint replacement with the scope write lock and keep observer notifications outside the locked section.
Store release, environment, and transaction as sentry_value_t fields with retained scope accessors. Pass value references through observer notifications and add a generic consuming sentry__value_replace helper for scope value replacement.
Move scope payload access behind sentry_scope_data_t helpers so scope-level code no longer reaches through scope->data to individual fields.
Track active scope accesses and delay cleanup until they finish, while releasing the lifetime guard before backend scope flushing.
4948141 to
0b0e77c
Compare
| sentry__cond_wait(&g_scope_idle, &g_lock); | ||
| } | ||
| if (g_scope_initialized) { | ||
| g_scope_initialized = false; | ||
| cleanup_scope(&g_scope); | ||
| cleanup_global_data(g_scope.data); | ||
| g_scope.data = NULL; | ||
| cleanup_observers(&g_scope); | ||
| sentry__mutex_free(&g_scope.observers_lock); | ||
| } | ||
| sentry__mutex_unlock(&g_lock); | ||
| } |
There was a problem hiding this comment.
Bug: The condition variable g_scope_idle is initialized but never destroyed, causing a kernel handle leak on some Windows platforms.
Severity: LOW
Suggested Fix
Implement a cleanup path for the g_scope_idle condition variable. This can be done by adding a call to a new sentry__cond_free(&g_scope_idle) function within sentry__scope_cleanup(), ensuring it is only called when g_scope_idle_initialized is true. The sentry__cond_free function would need to be implemented to close the relevant handles on Windows.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: src/sentry_scope.c#L1219-L1234
Potential issue: The condition variable `g_scope_idle` is initialized on first use
within `sentry__scope_begin()` but is never destroyed. On pre-Vista Windows platforms,
the implementation of `sentry__cond_init` allocates kernel handles for a semaphore and
an event. Since there is no corresponding cleanup function called during shutdown (e.g.,
in `sentry__scope_cleanup`), these handles are leaked. This leads to a resource leak on
affected Windows versions.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0b0e77c. Configure here.
| data_set_release(sentry_scope_data_t *data, sentry_value_t value) | ||
| { | ||
| DATA_WRITE_LOCK (data) { | ||
| sentry__value_replace(&data->release, sentry_value_incref(value)); | ||
| sentry_value_set_by_key(data->dynamic_sampling_context, "release", | ||
| sentry_value_incref(value)); | ||
| } |
There was a problem hiding this comment.
Bug: Updates to release or environment via data_set_release() fail silently on the dynamic_sampling_context if it has been frozen, leading to inconsistent state.
Severity: MEDIUM
Suggested Fix
The functions data_set_release and data_set_environment should handle the case where the dynamic_sampling_context is frozen. Instead of ignoring the return value of sentry_value_set_by_key, the code could create a mutable copy of the DSC, apply the update, and then replace the frozen one. This would ensure that the DSC remains consistent with the top-level scope data.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: src/sentry_scope.c#L1038-L1044
Potential issue: When an application receives an upstream trace with Dynamic Sampling
Context (DSC) headers, `sentry__scope_freeze_dsc()` is called, freezing the
`data->dynamic_sampling_context`. Subsequent calls to `sentry_set_release()` or
`sentry_set_environment()` attempt to update this frozen DSC via
`sentry_value_set_by_key()`. This operation fails silently because the object is frozen,
but the top-level `data->release` or `data->environment` fields are updated
successfully. This creates an inconsistency where the DSC propagated to upstream
services will be missing the correct release and environment information, impacting
distributed tracing.
Also affects:
src/sentry_scope.c:1057~1064

Warning
WIP 🚧🔨⏳⛔
Mostly curious what the AI review bots have to say for now...
Allow concurrent scope data readers.
Before
After
Close: #1862