From f1978809b17e25a27eeb7e2a903010c49ae2f687 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 22 Jul 2026 13:32:17 +0200 Subject: [PATCH 01/28] refactor(scope): hide direct data access --- src/backends/sentry_backend_crashpad.cpp | 3 +- src/sentry_core.c | 173 ++--------- src/sentry_envelope.c | 7 +- src/sentry_scope.c | 364 +++++++++++++++++++++++ src/sentry_scope.h | 67 +++++ src/sentry_session.c | 8 +- src/sentry_tracing.c | 38 +-- tests/unit/test_logs.c | 4 +- tests/unit/test_scope.c | 146 +++++---- tests/unit/test_tracing.c | 19 +- 10 files changed, 576 insertions(+), 253 deletions(-) diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index bbfa1689a1..1d576387db 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -516,7 +516,8 @@ crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) // written above and stays breadcrumb-free SENTRY_WITH_SCOPE (scope) { sentry_value_set_by_key(crash_event, "breadcrumbs", - sentry__ringbuffer_to_list(scope->breadcrumbs)); + sentry__ringbuffer_to_list( + sentry__scope_get_breadcrumbs(scope))); } sentry__session_replay_flush_pending( diff --git a/src/sentry_core.c b/src/sentry_core.c index 770d85dca6..5ce4310d6d 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -78,23 +78,6 @@ sentry__should_skip_upload(void) return skip; } -static void -generate_propagation_context(sentry_value_t propagation_context) -{ - sentry_value_set_by_key( - propagation_context, "trace", sentry_value_new_object()); - sentry_uuid_t trace_id = sentry_uuid_new_v4(); - sentry_uuid_t span_id = sentry_uuid_new_v4(); - sentry_value_set_by_key( - sentry_value_get_by_key(propagation_context, "trace"), "trace_id", - sentry__value_new_internal_uuid(&trace_id)); - sentry_value_set_by_key( - sentry_value_get_by_key(propagation_context, "trace"), "span_id", - sentry__value_new_span_uuid(&span_id)); - sentry__generate_sample_rand( - sentry_value_get_by_key(propagation_context, "trace")); -} - static void register_integrations(sentry_scope_t *scope, const sentry_options_t *options) { @@ -245,38 +228,7 @@ sentry_init(sentry_options_t *options) // `client_sdk` in the `scope` because some downstream SDKs want to override // it at runtime via the options interface. SENTRY_WITH_SCOPE_MUT (scope) { - if (options->sdk_name) { - sentry_value_t sdk_name - = sentry_value_new_string(options->sdk_name); - sentry_value_set_by_key(scope->client_sdk, "name", sdk_name); - } - sentry_value_t integrations - = sentry_value_get_by_key(scope->client_sdk, "integrations"); - for (size_t i = 0; i < options->num_integrations; i++) { - const char *name = options->integrations[i]->name; - if (!name) { - continue; - } - if (sentry_value_is_null(integrations)) { - integrations = sentry_value_new_list(); - sentry_value_set_by_key( - scope->client_sdk, "integrations", integrations); - } - sentry_value_append(integrations, sentry_value_new_string(name)); - } - sentry_value_freeze(scope->client_sdk); - generate_propagation_context(scope->propagation_context); - scope->release = sentry__string_clone(options->release); - scope->environment = sentry__string_clone(options->environment); - sentry_value_decref(scope->attachments); - scope->attachments = options->attachments; - options->attachments = sentry_value_new_null(); - - sentry__ringbuffer_set_max_size( - scope->breadcrumbs, options->max_breadcrumbs); - - sentry__scope_update_dsc(scope, options); - + sentry__scope_apply_options(scope, options); register_integrations(scope, options); } if (backend && backend->user_consent_changed_func) { @@ -758,9 +710,10 @@ sentry__prepare_event(const sentry_options_t *options, sentry_value_t event, if (local_scope && sentry_value_get_length(local_scope->attachments) > 0) { // all attachments merged from multiple scopes + sentry__attachments_extend(&all_attachments, + sentry__scope_get_attachments(local_scope)); sentry__attachments_extend( - &all_attachments, local_scope->attachments); - sentry__attachments_extend(&all_attachments, scope->attachments); + &all_attachments, sentry__scope_get_attachments(scope)); attachments = all_attachments; } // otherwise only global scope has attachments @@ -986,11 +939,7 @@ void sentry_set_release_n(const char *release, size_t release_len) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_free(scope->release); - scope->release = sentry__string_clone_n(release, release_len); - sentry_value_set_by_key(scope->dynamic_sampling_context, "release", - sentry_value_new_string(scope->release)); - SENTRY_SCOPE_NOTIFY(scope, set_release, scope->release); + sentry__scope_set_release_n(scope, release, release_len); } } @@ -1004,12 +953,7 @@ void sentry_set_environment_n(const char *environment, size_t environment_len) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_free(scope->environment); - scope->environment - = sentry__string_clone_n(environment, environment_len); - sentry_value_set_by_key(scope->dynamic_sampling_context, "environment", - sentry_value_new_string(scope->environment)); - SENTRY_SCOPE_NOTIFY(scope, set_environment, scope->environment); + sentry__scope_set_environment_n(scope, environment, environment_len); } } @@ -1089,9 +1033,7 @@ void sentry_remove_tag(const char *key) { SENTRY_WITH_SCOPE_MUT (scope) { - if (sentry_value_remove_by_key(scope->tags, key) == 0) { - SENTRY_SCOPE_NOTIFY(scope, remove_tag, key); - } + sentry__scope_remove_tag(scope, key); } } @@ -1099,12 +1041,7 @@ void sentry_remove_tag_n(const char *key, size_t key_len) { SENTRY_WITH_SCOPE_MUT (scope) { - char *k - = sentry__value_remove_and_take_key_n(scope->tags, key, key_len); - if (k) { - SENTRY_SCOPE_NOTIFY(scope, remove_tag, k); - } - sentry_free(k); + sentry__scope_remove_tag_n(scope, key, key_len); } } @@ -1128,9 +1065,7 @@ void sentry_remove_extra(const char *key) { SENTRY_WITH_SCOPE_MUT (scope) { - if (sentry_value_remove_by_key(scope->extra, key) == 0) { - SENTRY_SCOPE_NOTIFY(scope, remove_extra, key); - } + sentry__scope_remove_extra(scope, key); } } @@ -1138,12 +1073,7 @@ void sentry_remove_extra_n(const char *key, size_t key_len) { SENTRY_WITH_SCOPE_MUT (scope) { - char *k - = sentry__value_remove_and_take_key_n(scope->extra, key, key_len); - if (k) { - SENTRY_SCOPE_NOTIFY(scope, remove_extra, k); - } - sentry_free(k); + sentry__scope_remove_extra_n(scope, key, key_len); } } @@ -1216,7 +1146,7 @@ void sentry__set_propagation_context(const char *key, sentry_value_t value) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_value_set_by_key(scope->propagation_context, key, value); + sentry__scope_set_propagation_context(scope, key, value); } } @@ -1244,9 +1174,7 @@ void sentry_remove_context(const char *key) { SENTRY_WITH_SCOPE_MUT (scope) { - if (sentry_value_remove_by_key(scope->contexts, key) == 0) { - SENTRY_SCOPE_NOTIFY(scope, remove_context, key); - } + sentry__scope_remove_context(scope, key); } } @@ -1254,12 +1182,7 @@ void sentry_remove_context_n(const char *key, size_t key_len) { SENTRY_WITH_SCOPE_MUT (scope) { - char *k = sentry__value_remove_and_take_key_n( - scope->contexts, key, key_len); - if (k) { - SENTRY_SCOPE_NOTIFY(scope, remove_context, k); - } - sentry_free(k); + sentry__scope_remove_context_n(scope, key, key_len); } } @@ -1325,7 +1248,7 @@ sentry_set_trace_n(const char *trace_id, size_t trace_id_len, sentry_uuid_t span_id = sentry_uuid_new_v4(); sentry_value_set_by_key( context, "span_id", sentry__value_new_span_uuid(&span_id)); - scope->trace_managed = false; + sentry__scope_set_trace_managed(scope, false); } if (!sentry_value_is_null(context)) { @@ -1346,8 +1269,8 @@ sentry_regenerate_trace(void) { SENTRY_WITH_OPTIONS (options) { SENTRY_WITH_SCOPE_MUT (scope) { - generate_propagation_context(scope->propagation_context); - scope->trace_managed = false; + sentry__scope_regenerate_propagation_context(scope); + sentry__scope_set_trace_managed(scope, false); sentry__scope_update_dsc(scope, options); } } @@ -1363,15 +1286,7 @@ void sentry_set_transaction_n(const char *transaction, size_t transaction_len) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_free(scope->transaction); - scope->transaction - = sentry__string_clone_n(transaction, transaction_len); - - if (scope->transaction_object) { - sentry_transaction_set_name_n( - scope->transaction_object, transaction, transaction_len); - } - SENTRY_SCOPE_NOTIFY(scope, set_transaction, scope->transaction); + sentry__scope_set_transaction_n(scope, transaction, transaction_len); } } @@ -1434,11 +1349,9 @@ sentry_transaction_start_ts(sentry_transaction_context_t *opaque_tx_ctx, // Regenerate the scope's propagation context so events // captured outside this transaction also carry the new // trace_id, and align the tx's trace_id with it. - generate_propagation_context(scope->propagation_context); + sentry__scope_regenerate_propagation_context(scope); sentry_value_t scope_trace_id = sentry_value_get_by_key( - sentry_value_get_by_key( - scope->propagation_context, "trace"), - "trace_id"); + sentry__scope_get_trace_context(scope), "trace_id"); sentry_value_incref(scope_trace_id); sentry_value_set_by_key(tx, "trace_id", scope_trace_id); sentry_value_remove_by_key(tx, "parent_span_id"); @@ -1453,8 +1366,7 @@ sentry_transaction_start_ts(sentry_transaction_context_t *opaque_tx_ctx, double sample_rand = 1.0; SENTRY_WITH_SCOPE (scope) { sample_rand = sentry_value_as_double(sentry_value_get_by_key( - sentry_value_get_by_key(scope->propagation_context, "trace"), - "sample_rand")); + sentry__scope_get_trace_context(scope), "sample_rand")); } sentry_sampling_context_t sampling_ctx = { opaque_tx_ctx, custom_sampling_ctx, NULL, sample_rand }; @@ -1500,10 +1412,7 @@ sentry_transaction_discard(sentry_transaction_t *opaque_tx) } SENTRY_WITH_SCOPE_MUT (scope) { - if (scope->transaction_object == opaque_tx) { - sentry__transaction_decref(scope->transaction_object); - scope->transaction_object = NULL; - } + sentry__scope_remove_transaction_object(scope, opaque_tx); } sentry__transaction_decref(opaque_tx); @@ -1521,27 +1430,15 @@ sentry__transaction_finish_value( sentry_value_t tx = sentry__value_clone(opaque_tx->inner); SENTRY_WITH_SCOPE_MUT (scope) { - if (scope->transaction_object) { - sentry_value_t scope_tx = scope->transaction_object->inner; - - const char *tx_id = sentry_value_as_string( - sentry_value_get_by_key(tx, "span_id")); - const char *scope_tx_id = sentry_value_as_string( - sentry_value_get_by_key(scope_tx, "span_id")); - if (sentry__string_eq(tx_id, scope_tx_id)) { - sentry__transaction_decref(scope->transaction_object); - scope->transaction_object = NULL; - } - } + sentry__scope_remove_transaction_value(scope, tx); // if the SDK manages the trace (rather than the user or a downstream // SDK) we break propagation context traces at transaction boundaries. - if (scope->trace_managed) { + if (sentry__scope_is_trace_managed(scope)) { sentry_value_t txn_trace_id = sentry_value_get_by_key(tx, "trace_id"); sentry_value_incref(txn_trace_id); - sentry_value_set_by_key( - sentry_value_get_by_key(scope->propagation_context, "trace"), + sentry_value_set_by_key(sentry__scope_get_trace_context(scope), "trace_id", txn_trace_id); } } @@ -1602,7 +1499,7 @@ void sentry_set_transaction_object(sentry_transaction_t *tx) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_scope_set_transaction_object(scope, tx); + sentry__scope_set_transaction_object(scope, tx); } } @@ -1610,7 +1507,7 @@ void sentry_set_span(sentry_span_t *span) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_scope_set_span(scope, span); + sentry__scope_set_span(scope, span); } } @@ -1762,18 +1659,7 @@ sentry_span_finish_ts(sentry_span_t *opaque_span, uint64_t timestamp) sentry_value_t span = sentry__value_clone(opaque_span->inner); SENTRY_WITH_SCOPE_MUT (scope) { - if (scope->span) { - sentry_value_t scope_span = scope->span->inner; - - const char *span_id = sentry_value_as_string( - sentry_value_get_by_key(span, "span_id")); - const char *scope_span_id = sentry_value_as_string( - sentry_value_get_by_key(scope_span, "span_id")); - if (sentry__string_eq(span_id, scope_span_id)) { - sentry__span_decref(scope->span); - scope->span = NULL; - } - } + sentry__scope_remove_span_value(scope, span); } // Note that the current API makes it impossible to set a sampled value @@ -1832,10 +1718,7 @@ sentry_span_discard(sentry_span_t *opaque_span) sentry__transaction_remove_child(opaque_span->transaction, opaque_span); SENTRY_WITH_SCOPE_MUT (scope) { - if (scope->span == opaque_span) { - sentry__span_decref(scope->span); - scope->span = NULL; - } + sentry__scope_remove_span(scope, opaque_span); } sentry__span_decref(opaque_span); diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index f94cba4639..672e38deeb 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -415,10 +415,9 @@ sentry__envelope_add_event(sentry_envelope_t *envelope, sentry_value_t event) sentry_value_t dsc = sentry_value_new_null(); double sample_rand = (double)NAN; SENTRY_WITH_SCOPE (scope) { - dsc = sentry__value_clone(scope->dynamic_sampling_context); + dsc = sentry__value_clone(sentry__scope_get_dsc(scope)); sample_rand = sentry_value_as_double(sentry_value_get_by_key( - sentry_value_get_by_key(scope->propagation_context, "trace"), - "sample_rand")); + sentry__scope_get_trace_context(scope), "sample_rand")); } if (!sentry_value_is_null(dsc)) { sentry_value_t trace_id = sentry_value_get_by_key( @@ -496,7 +495,7 @@ sentry__envelope_add_transaction( sentry_value_t dsc = sentry_value_new_null(); SENTRY_WITH_SCOPE (scope) { - dsc = sentry__value_clone(scope->dynamic_sampling_context); + dsc = sentry__value_clone(sentry__scope_get_dsc(scope)); } if (!sentry_value_is_null(dsc)) { diff --git a/src/sentry_scope.c b/src/sentry_scope.c index fe83f05955..b27e098f40 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -13,6 +13,7 @@ #include "sentry_sync.h" #include "sentry_tracing.h" #include "sentry_transport.h" +#include "sentry_uuid.h" #include "sentry_value.h" #include @@ -69,6 +70,23 @@ get_client_sdk(void) return client_sdk; } +static void +generate_propagation_context(sentry_value_t propagation_context) +{ + sentry_value_set_by_key( + propagation_context, "trace", sentry_value_new_object()); + sentry_uuid_t trace_id = sentry_uuid_new_v4(); + sentry_uuid_t span_id = sentry_uuid_new_v4(); + sentry_value_set_by_key( + sentry_value_get_by_key(propagation_context, "trace"), "trace_id", + sentry__value_new_internal_uuid(&trace_id)); + sentry_value_set_by_key( + sentry_value_get_by_key(propagation_context, "trace"), "span_id", + sentry__value_new_span_uuid(&span_id)); + sentry__generate_sample_rand( + sentry_value_get_by_key(propagation_context, "trace")); +} + static void init_scope(sentry_scope_t *scope) { @@ -339,6 +357,28 @@ sentry_local_scope_new(void) return scope; } +void +sentry__scope_apply_options(sentry_scope_t *scope, sentry_options_t *options) +{ + if (options->sdk_name) { + sentry_value_t sdk_name = sentry_value_new_string(options->sdk_name); + sentry_value_set_by_key(scope->client_sdk, "name", sdk_name); + } + sentry_value_freeze(scope->client_sdk); + generate_propagation_context(scope->propagation_context); + sentry__scope_set_release_n( + scope, options->release, sentry__guarded_strlen(options->release)); + sentry__scope_set_environment_n(scope, options->environment, + sentry__guarded_strlen(options->environment)); + scope->attachments = options->attachments; + options->attachments = NULL; + + sentry__ringbuffer_set_max_size( + scope->breadcrumbs, options->max_breadcrumbs); + + sentry__scope_update_dsc(scope, options); +} + void sentry_scope_clear(sentry_scope_t *scope) { @@ -430,6 +470,49 @@ sentry_scope_clone(const sentry_scope_t *scope) return clone; } +sentry_value_t +sentry__scope_get_propagation_context(const sentry_scope_t *scope) +{ + return scope->propagation_context; +} + +sentry_value_t +sentry__scope_get_trace_context(const sentry_scope_t *scope) +{ + return sentry_value_get_by_key(scope->propagation_context, "trace"); +} + +void +sentry__scope_set_propagation_context( + sentry_scope_t *scope, const char *key, sentry_value_t value) +{ + sentry_value_set_by_key(scope->propagation_context, key, value); +} + +void +sentry__scope_regenerate_propagation_context(sentry_scope_t *scope) +{ + generate_propagation_context(scope->propagation_context); +} + +bool +sentry__scope_is_trace_managed(const sentry_scope_t *scope) +{ + return scope->trace_managed; +} + +void +sentry__scope_set_trace_managed(sentry_scope_t *scope, bool managed) +{ + scope->trace_managed = managed; +} + +sentry_value_t +sentry__scope_get_dsc(const sentry_scope_t *scope) +{ + return scope->dynamic_sampling_context; +} + void sentry__scope_freeze_dsc(sentry_scope_t *scope, sentry_value_t incoming) { @@ -760,6 +843,18 @@ sentry_scope_add_breadcrumb(sentry_scope_t *scope, sentry_value_t breadcrumb) } } +const sentry_ringbuffer_t * +sentry__scope_get_breadcrumbs(const sentry_scope_t *scope) +{ + return scope->breadcrumbs; +} + +sentry_value_t +sentry__scope_get_user(const sentry_scope_t *scope) +{ + return scope->user; +} + void sentry_scope_set_user(sentry_scope_t *scope, sentry_value_t user) { @@ -768,6 +863,12 @@ sentry_scope_set_user(sentry_scope_t *scope, sentry_value_t user) SENTRY_SCOPE_NOTIFY(scope, set_user, user); } +sentry_value_t +sentry__scope_get_tags(const sentry_scope_t *scope) +{ + return scope->tags; +} + void sentry_scope_set_tag(sentry_scope_t *scope, const char *key, const char *value) { @@ -807,6 +908,31 @@ sentry_scope_set_tags(sentry_scope_t *scope, sentry_value_t tags) sentry_value_decref(tags); } +void +sentry__scope_remove_tag(sentry_scope_t *scope, const char *key) +{ + if (sentry_value_remove_by_key(scope->tags, key) == 0) { + SENTRY_SCOPE_NOTIFY(scope, remove_tag, key); + } +} + +void +sentry__scope_remove_tag_n( + sentry_scope_t *scope, const char *key, size_t key_len) +{ + char *k = sentry__value_remove_and_take_key_n(scope->tags, key, key_len); + if (k) { + SENTRY_SCOPE_NOTIFY(scope, remove_tag, k); + } + sentry_free(k); +} + +sentry_value_t +sentry__scope_get_extra(const sentry_scope_t *scope) +{ + return scope->extra; +} + void sentry_scope_set_extra( sentry_scope_t *scope, const char *key, sentry_value_t value) @@ -826,6 +952,25 @@ sentry_scope_set_extra_n(sentry_scope_t *scope, const char *key, size_t key_len, } } +void +sentry__scope_remove_extra(sentry_scope_t *scope, const char *key) +{ + if (sentry_value_remove_by_key(scope->extra, key) == 0) { + SENTRY_SCOPE_NOTIFY(scope, remove_extra, key); + } +} + +void +sentry__scope_remove_extra_n( + sentry_scope_t *scope, const char *key, size_t key_len) +{ + char *k = sentry__value_remove_and_take_key_n(scope->extra, key, key_len); + if (k) { + SENTRY_SCOPE_NOTIFY(scope, remove_extra, k); + } + sentry_free(k); +} + void sentry_scope_set_attribute( sentry_scope_t *scope, const char *key, sentry_value_t attribute) @@ -847,6 +992,12 @@ sentry_scope_set_attribute_n(sentry_scope_t *scope, const char *key, sentry_value_set_by_key_n(scope->attributes, key, key_len, attribute); } +sentry_value_t +sentry__scope_get_attributes(const sentry_scope_t *scope) +{ + return scope->attributes; +} + void sentry_scope_remove_attribute(sentry_scope_t *scope, const char *key) { @@ -860,6 +1011,12 @@ sentry_scope_remove_attribute_n( sentry_value_remove_by_key_n(scope->attributes, key, key_len); } +sentry_value_t +sentry__scope_get_contexts(const sentry_scope_t *scope) +{ + return scope->contexts; +} + void sentry_scope_set_context( sentry_scope_t *scope, const char *key, sentry_value_t value) @@ -910,6 +1067,32 @@ sentry_scope_update_context_n(sentry_scope_t *scope, const char *key, SENTRY_SCOPE_NOTIFY(scope, set_context, k, value); } +void +sentry__scope_remove_context(sentry_scope_t *scope, const char *key) +{ + if (sentry_value_remove_by_key(scope->contexts, key) == 0) { + SENTRY_SCOPE_NOTIFY(scope, remove_context, key); + } +} + +void +sentry__scope_remove_context_n( + sentry_scope_t *scope, const char *key, size_t key_len) +{ + char *k + = sentry__value_remove_and_take_key_n(scope->contexts, key, key_len); + if (k) { + SENTRY_SCOPE_NOTIFY(scope, remove_context, k); + } + sentry_free(k); +} + +sentry_value_t +sentry__scope_get_fingerprint(const sentry_scope_t *scope) +{ + return scope->fingerprint; +} + void sentry__scope_set_fingerprint_va( sentry_scope_t *scope, const char *fingerprint, va_list va) @@ -988,6 +1171,12 @@ sentry_scope_remove_fingerprint(sentry_scope_t *scope) SENTRY_SCOPE_NOTIFY(scope, set_fingerprint, scope->fingerprint); } +sentry_level_t +sentry__scope_get_level(const sentry_scope_t *scope) +{ + return scope->level; +} + void sentry_scope_set_level(sentry_scope_t *scope, sentry_level_t level) { @@ -1045,6 +1234,181 @@ sentry_scope_add_attachment(sentry_scope_t *scope, sentry_value_t attachment) return attachment_id; } +sentry_transaction_t * +sentry__scope_get_transaction_object(const sentry_scope_t *scope) +{ + return scope->transaction_object; +} + +static bool +value_has_span_id(sentry_value_t value, const char *span_id) +{ + const char *value_span_id + = sentry_value_as_string(sentry_value_get_by_key(value, "span_id")); + return sentry__string_eq(value_span_id, span_id); +} + +void +sentry__scope_set_transaction_object( + sentry_scope_t *scope, sentry_transaction_t *transaction) +{ + sentry__transaction_incref(transaction); + sentry__span_decref(scope->span); + scope->span = NULL; + sentry__transaction_decref(scope->transaction_object); + scope->transaction_object = transaction; +} + +bool +sentry__scope_remove_transaction_object( + sentry_scope_t *scope, sentry_transaction_t *transaction) +{ + if (!transaction || scope->transaction_object != transaction) { + return false; + } + + scope->transaction_object = NULL; + sentry__transaction_decref(transaction); + return true; +} + +bool +sentry__scope_remove_transaction_value( + sentry_scope_t *scope, sentry_value_t transaction) +{ + const char *span_id = sentry_value_as_string( + sentry_value_get_by_key(transaction, "span_id")); + if (!scope->transaction_object + || !value_has_span_id(scope->transaction_object->inner, span_id)) { + return false; + } + + sentry_transaction_t *transaction_object = scope->transaction_object; + scope->transaction_object = NULL; + sentry__transaction_decref(transaction_object); + return true; +} + +bool +sentry__scope_restore_transaction_object( + sentry_scope_t *scope, sentry_transaction_t *transaction) +{ + if (scope->transaction_object || !transaction) { + return false; + } + + scope->transaction_object = transaction; + return true; +} + +sentry_span_t * +sentry__scope_get_span(const sentry_scope_t *scope) +{ + return scope->span; +} + +void +sentry__scope_set_span(sentry_scope_t *scope, sentry_span_t *span) +{ + sentry__span_incref(span); + sentry__transaction_decref(scope->transaction_object); + scope->transaction_object = NULL; + sentry__span_decref(scope->span); + scope->span = span; +} + +bool +sentry__scope_remove_span(sentry_scope_t *scope, sentry_span_t *span) +{ + if (!span || scope->span != span) { + return false; + } + + scope->span = NULL; + sentry__span_decref(span); + return true; +} + +bool +sentry__scope_remove_span_value(sentry_scope_t *scope, sentry_value_t span) +{ + const char *span_id + = sentry_value_as_string(sentry_value_get_by_key(span, "span_id")); + if (!scope->span || !value_has_span_id(scope->span->inner, span_id)) { + return false; + } + + sentry_span_t *scope_span = scope->span; + scope->span = NULL; + sentry__span_decref(scope_span); + return true; +} + +bool +sentry__scope_restore_span(sentry_scope_t *scope, sentry_span_t *span) +{ + if (scope->span || !span) { + return false; + } + + scope->span = span; + return true; +} + +const char * +sentry__scope_get_release(const sentry_scope_t *scope) +{ + return scope->release; +} + +void +sentry__scope_set_release_n( + sentry_scope_t *scope, const char *release, size_t release_len) +{ + sentry_free(scope->release); + scope->release = sentry__string_clone_n(release, release_len); + sentry_value_set_by_key(scope->dynamic_sampling_context, "release", + sentry_value_new_string(scope->release)); + SENTRY_SCOPE_NOTIFY(scope, set_release, scope->release); +} + +const char * +sentry__scope_get_environment(const sentry_scope_t *scope) +{ + return scope->environment; +} + +void +sentry__scope_set_environment_n( + sentry_scope_t *scope, const char *environment, size_t environment_len) +{ + sentry_free(scope->environment); + scope->environment = sentry__string_clone_n(environment, environment_len); + sentry_value_set_by_key(scope->dynamic_sampling_context, "environment", + sentry_value_new_string(scope->environment)); + SENTRY_SCOPE_NOTIFY(scope, set_environment, scope->environment); +} + +const char * +sentry__scope_get_transaction(const sentry_scope_t *scope) +{ + return scope->transaction; +} + +void +sentry__scope_set_transaction_n( + sentry_scope_t *scope, const char *transaction, size_t transaction_len) +{ + sentry_free(scope->transaction); + scope->transaction = sentry__string_clone_n(transaction, transaction_len); + + if (scope->transaction_object) { + sentry_transaction_set_name_n( + scope->transaction_object, transaction, transaction_len); + } + SENTRY_SCOPE_NOTIFY(scope, set_transaction, scope->transaction); +} + sentry_uuid_t sentry_scope_attach_file(sentry_scope_t *scope, const char *path) { diff --git a/src/sentry_scope.h b/src/sentry_scope.h index aa758a824b..4da36822cf 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -123,6 +123,9 @@ void sentry__scope_unlock(void); */ void sentry__scope_cleanup(void); +void sentry__scope_apply_options( + sentry_scope_t *scope, sentry_options_t *options); + /** * Frees the scope if it is a one-shot local scope. */ @@ -145,13 +148,75 @@ void sentry__scope_apply_to_event(const sentry_scope_t *scope, const sentry_options_t *options, sentry_value_t event, sentry_scope_mode_t mode); +const char *sentry__scope_get_release(const sentry_scope_t *scope); +void sentry__scope_set_release_n( + sentry_scope_t *scope, const char *release, size_t release_len); + +const char *sentry__scope_get_environment(const sentry_scope_t *scope); +void sentry__scope_set_environment_n( + sentry_scope_t *scope, const char *environment, size_t environment_len); + +const char *sentry__scope_get_transaction(const sentry_scope_t *scope); +void sentry__scope_set_transaction_n( + sentry_scope_t *scope, const char *transaction, size_t transaction_len); + +sentry_value_t sentry__scope_get_fingerprint(const sentry_scope_t *scope); void sentry__scope_set_fingerprint_va( sentry_scope_t *scope, const char *fingerprint, va_list va); void sentry__scope_set_fingerprint_nva(sentry_scope_t *scope, const char *fingerprint, size_t fingerprint_len, va_list va); +sentry_value_t sentry__scope_get_user(const sentry_scope_t *scope); +sentry_level_t sentry__scope_get_level(const sentry_scope_t *scope); sentry_value_t sentry__scope_add_attachment( sentry_scope_t *scope, sentry_value_t attachment); +sentry_value_t sentry__scope_get_tags(const sentry_scope_t *scope); +void sentry__scope_remove_tag(sentry_scope_t *scope, const char *key); +void sentry__scope_remove_tag_n( + sentry_scope_t *scope, const char *key, size_t key_len); + +sentry_value_t sentry__scope_get_extra(const sentry_scope_t *scope); +void sentry__scope_remove_extra(sentry_scope_t *scope, const char *key); +void sentry__scope_remove_extra_n( + sentry_scope_t *scope, const char *key, size_t key_len); + +sentry_value_t sentry__scope_get_attributes(const sentry_scope_t *scope); + +sentry_value_t sentry__scope_get_contexts(const sentry_scope_t *scope); +void sentry__scope_remove_context(sentry_scope_t *scope, const char *key); +void sentry__scope_remove_context_n( + sentry_scope_t *scope, const char *key, size_t key_len); + +sentry_value_t sentry__scope_get_propagation_context( + const sentry_scope_t *scope); +sentry_value_t sentry__scope_get_trace_context(const sentry_scope_t *scope); +void sentry__scope_set_propagation_context( + sentry_scope_t *scope, const char *key, sentry_value_t value); +void sentry__scope_regenerate_propagation_context(sentry_scope_t *scope); + +const sentry_ringbuffer_t *sentry__scope_get_breadcrumbs( + const sentry_scope_t *scope); + +sentry_transaction_t *sentry__scope_get_transaction_object( + const sentry_scope_t *scope); +void sentry__scope_set_transaction_object( + sentry_scope_t *scope, sentry_transaction_t *transaction); +bool sentry__scope_remove_transaction_object( + sentry_scope_t *scope, sentry_transaction_t *transaction); +bool sentry__scope_remove_transaction_value( + sentry_scope_t *scope, sentry_value_t transaction); +bool sentry__scope_restore_transaction_object( + sentry_scope_t *scope, sentry_transaction_t *transaction); + +sentry_span_t *sentry__scope_get_span(const sentry_scope_t *scope); +void sentry__scope_set_span(sentry_scope_t *scope, sentry_span_t *span); +bool sentry__scope_remove_span(sentry_scope_t *scope, sentry_span_t *span); +bool sentry__scope_remove_span_value( + sentry_scope_t *scope, sentry_value_t span); +bool sentry__scope_restore_span(sentry_scope_t *scope, sentry_span_t *span); + +bool sentry__scope_is_trace_managed(const sentry_scope_t *scope); +void sentry__scope_set_trace_managed(sentry_scope_t *scope, bool managed); /** * These are convenience macros to automatically lock/unlock the global scope @@ -211,6 +276,8 @@ void sentry__scope_end_notify(sentry_scope_t *scope); sentry__scope_end_notify(scope); \ } while (0) +sentry_value_t sentry__scope_get_dsc(const sentry_scope_t *scope); + /** * Rebuilds the scope's dynamic sampling context (DSC) from the SDK options * and the current propagation context. The previous DSC is discarded. diff --git a/src/sentry_session.c b/src/sentry_session.c index 182dfa92c5..49b90415cc 100644 --- a/src/sentry_session.c +++ b/src/sentry_session.c @@ -50,8 +50,9 @@ status_from_string(const char *status) sentry_session_t * sentry__session_new(const sentry_scope_t *scope) { - char *release = sentry__string_clone(scope->release); - char *environment = sentry__string_clone(scope->environment); + char *release = sentry__string_clone(sentry__scope_get_release(scope)); + char *environment + = sentry__string_clone(sentry__scope_get_environment(scope)); if (!release) { sentry_free(environment); @@ -219,7 +220,8 @@ sentry_start_session(void) if (options) { options->session = sentry__session_new(scope); if (options->session) { - sentry__session_sync_user(options->session, scope->user, + sentry__session_sync_user(options->session, + sentry__scope_get_user(scope), options->run ? options->run->installation_id : NULL); sentry__run_write_session(options->run, options->session); } diff --git a/src/sentry_tracing.c b/src/sentry_tracing.c index 8b94d36e9f..a812942208 100644 --- a/src/sentry_tracing.c +++ b/src/sentry_tracing.c @@ -96,22 +96,18 @@ transaction_context_new_n(sentry_slice_t name, sentry_slice_t operation) sentry_value_new_string_n(name.ptr, name.len)); SENTRY_WITH_SCOPE_MUT (scope) { - if (!scope->trace_managed - && !sentry_value_is_null( - sentry_value_get_by_key(scope->propagation_context, "trace"))) { + sentry_value_t trace_context = sentry__scope_get_trace_context(scope); + if (!sentry__scope_is_trace_managed(scope) + && !sentry_value_is_null(trace_context)) { // The trace is managed from outside, so we use the propagation // context as the trace source for this transaction. This means that // either a downstream SDK or the user manages trace life-cycles. sentry_value_set_by_key(transaction_context, "trace_id", - sentry__value_clone(sentry_value_get_by_key( - sentry_value_get_by_key( - scope->propagation_context, "trace"), - "trace_id"))); + sentry__value_clone( + sentry_value_get_by_key(trace_context, "trace_id"))); sentry_value_set_by_key(transaction_context, "parent_span_id", - sentry__value_clone(sentry_value_get_by_key( - sentry_value_get_by_key( - scope->propagation_context, "trace"), - "parent_span_id"))); + sentry__value_clone( + sentry_value_get_by_key(trace_context, "parent_span_id"))); } } @@ -965,7 +961,7 @@ sentry__span_iter_headers(sentry_value_t span, SENTRY_WITH_SCOPE (scope) { sentry__value_foreach_key_value( - scope->dynamic_sampling_context, append_baggage_member, &sb); + sentry__scope_get_dsc(scope), append_baggage_member, &sb); } char *baggage = sentry__stringbuilder_into_string(&sb); @@ -1019,13 +1015,13 @@ save_active_trace(void) { saved_trace_t s = { 0 }; SENTRY_WITH_SCOPE (scope) { - if (scope->span) { - sentry__span_incref(scope->span); - s.saved_span = scope->span; + s.saved_span = sentry__scope_get_span(scope); + if (s.saved_span) { + sentry__span_incref(s.saved_span); } - if (scope->transaction_object) { - sentry__transaction_incref(scope->transaction_object); - s.saved_tx_obj = scope->transaction_object; + s.saved_tx_obj = sentry__scope_get_transaction_object(scope); + if (s.saved_tx_obj) { + sentry__transaction_incref(s.saved_tx_obj); } } s.active_tx = s.saved_span && s.saved_span->transaction @@ -1041,12 +1037,10 @@ static void restore_active_trace(saved_trace_t *s) { SENTRY_WITH_SCOPE_MUT (scope) { - if (!scope->span && s->saved_span) { - scope->span = s->saved_span; + if (sentry__scope_restore_span(scope, s->saved_span)) { s->saved_span = NULL; } - if (!scope->transaction_object && s->saved_tx_obj) { - scope->transaction_object = s->saved_tx_obj; + if (sentry__scope_restore_transaction_object(scope, s->saved_tx_obj)) { s->saved_tx_obj = NULL; } } diff --git a/tests/unit/test_logs.c b/tests/unit/test_logs.c index 1e7cfcb945..13182ff536 100644 --- a/tests/unit/test_logs.c +++ b/tests/unit/test_logs.c @@ -768,9 +768,7 @@ SENTRY_TEST(scope_capture_log_trace_id) TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( captured_log, "trace_id")), sentry_value_as_string(sentry_value_get_by_key( - sentry_value_get_by_key( - global_scope->propagation_context, "trace"), - "trace_id"))); + sentry__scope_get_trace_context(global_scope), "trace_id"))); } sentry_scope_free(scope); diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index 64d87d5f53..28e595c291 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -123,8 +123,8 @@ SENTRY_TEST(scope_update_context) sentry_update_context("device", device); SENTRY_WITH_SCOPE (scope) { - sentry_value_t ctx - = sentry_value_get_by_key(scope->contexts, "device"); + sentry_value_t ctx = sentry_value_get_by_key( + sentry__scope_get_contexts(scope), "device"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "model")), "Xbox Series X"); @@ -143,8 +143,8 @@ SENTRY_TEST(scope_update_context) sentry_update_context("device", extra); SENTRY_WITH_SCOPE (scope) { - sentry_value_t ctx - = sentry_value_get_by_key(scope->contexts, "device"); + sentry_value_t ctx = sentry_value_get_by_key( + sentry__scope_get_contexts(scope), "device"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "model")), "PC"); @@ -166,8 +166,8 @@ SENTRY_TEST(scope_update_context) sentry_value_set_by_key(os, "name", sentry_value_new_string("SteamOS")); sentry_scope_update_context(local_scope, "os", os); - sentry_value_t ctx - = sentry_value_get_by_key(local_scope->contexts, "os"); + sentry_value_t ctx = sentry_value_get_by_key( + sentry__scope_get_contexts(local_scope), "os"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "name")), "SteamOS"); @@ -178,7 +178,8 @@ SENTRY_TEST(scope_update_context) sentry_value_set_by_key(os2, "version", sentry_value_new_string("6.1")); sentry_scope_update_context(local_scope, "os", os2); - ctx = sentry_value_get_by_key(local_scope->contexts, "os"); + ctx = sentry_value_get_by_key( + sentry__scope_get_contexts(local_scope), "os"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "name")), "Linux"); @@ -1095,7 +1096,7 @@ SENTRY_TEST(scope_clone) // scope values must not be corrupted by before_send modifications SENTRY_WITH_SCOPE (scope) { sentry_value_t scope_gpu - = sentry_value_get_by_key(scope->contexts, "gpu"); + = sentry_value_get_by_key(sentry__scope_get_contexts(scope), "gpu"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(scope_gpu, "name")), "original"); @@ -1103,7 +1104,7 @@ SENTRY_TEST(scope_clone) sentry_value_get_by_key(scope_gpu, "injected"))); sentry_value_t scope_data - = sentry_value_get_by_key(scope->extra, "data"); + = sentry_value_get_by_key(sentry__scope_get_extra(scope), "data"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(scope_data, "key")), "original"); @@ -1111,12 +1112,13 @@ SENTRY_TEST(scope_clone) sentry_value_get_by_key(scope_data, "injected"))); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - scope->user, "username")), + sentry__scope_get_user(scope), "username")), "original"); - TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(scope->user, "injected"))); + TEST_CHECK(sentry_value_is_null(sentry_value_get_by_key( + sentry__scope_get_user(scope), "injected"))); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope->fingerprint), 2); + TEST_CHECK_INT_EQUAL( + sentry_value_get_length(sentry__scope_get_fingerprint(scope)), 2); } sentry_close(); @@ -1133,7 +1135,7 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute("valid_key", valid_attr); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = scope->attributes; + sentry_value_t attributes = sentry__scope_get_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "valid_key"); @@ -1155,7 +1157,7 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute("invalid_no_value", invalid_attr_no_value); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = scope->attributes; + sentry_value_t attributes = sentry__scope_get_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "invalid_no_value"); @@ -1171,7 +1173,7 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute("invalid_no_type", invalid_attr_no_type); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = scope->attributes; + sentry_value_t attributes = sentry__scope_get_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "invalid_no_type"); @@ -1183,7 +1185,7 @@ SENTRY_TEST(scope_global_attributes) sentry_remove_attribute("valid_key"); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = scope->attributes; + sentry_value_t attributes = sentry__scope_get_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "valid_key"); @@ -1197,7 +1199,7 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute_n("key_n", 5, attr_n); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = scope->attributes; + sentry_value_t attributes = sentry__scope_get_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "key_n"); @@ -1232,7 +1234,7 @@ SENTRY_TEST(scope_local_attributes) sentry_value_new_attribute(sentry_value_new_string("global"), NULL)); SENTRY_WITH_SCOPE (global_scope) { - sentry_value_t attributes = global_scope->attributes; + sentry_value_t attributes = sentry__scope_get_attributes(global_scope); // Verify global attributes are set TEST_CHECK_STRING_EQUAL( @@ -1260,7 +1262,8 @@ SENTRY_TEST(scope_local_attributes) sentry_scope_set_attribute(local_scope, "scope", sentry_value_new_attribute(sentry_value_new_string("local"), NULL)); - sentry_value_t local_attributes = local_scope->attributes; + sentry_value_t local_attributes + = sentry__scope_get_attributes(local_scope); // Verify local attributes are set TEST_CHECK_STRING_EQUAL( @@ -1277,7 +1280,8 @@ SENTRY_TEST(scope_local_attributes) "local"); // Verify global scope still has its own attributes - sentry_value_t global_attributes = global_scope->attributes; + sentry_value_t global_attributes + = sentry__scope_get_attributes(global_scope); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key( sentry_value_get_by_key(global_attributes, "all"), "value")), @@ -1294,7 +1298,7 @@ SENTRY_TEST(scope_local_attributes) sentry_remove_attribute("all"); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = scope->attributes; + sentry_value_t attributes = sentry__scope_get_attributes(scope); TEST_CHECK( sentry_value_is_null(sentry_value_get_by_key(attributes, "all"))); // Other attributes should still exist @@ -1310,7 +1314,8 @@ SENTRY_TEST(scope_local_attributes) sentry_scope_set_attribute_n(local_scope, "test_key", 8, sentry_value_new_attribute(sentry_value_new_int32(100), "percent")); - sentry_value_t local_attributes = local_scope->attributes; + sentry_value_t local_attributes + = sentry__scope_get_attributes(local_scope); sentry_value_t attr = sentry_value_get_by_key(local_attributes, "test_key"); @@ -1342,7 +1347,8 @@ SENTRY_TEST(scope_local_attributes) invalid_attr, "type", sentry_value_new_string("string")); sentry_scope_set_attribute(local_scope, "invalid", invalid_attr); - sentry_value_t local_attributes = local_scope->attributes; + sentry_value_t local_attributes + = sentry__scope_get_attributes(local_scope); TEST_CHECK(sentry_value_is_null( sentry_value_get_by_key(local_attributes, "invalid"))); @@ -1647,7 +1653,7 @@ SENTRY_TEST(scope_observer_clear) sentry_scope_clear(scope); TEST_CHECK(d.was_cleared); TEST_CHECK_INT_EQUAL(scope->num_observers, 1); - TEST_CHECK(sentry_value_get_length(scope->tags) == 0); + TEST_CHECK(sentry_value_get_length(sentry__scope_get_tags(scope)) == 0); d.was_called = false; sentry_scope_set_tag(scope, "after", "clear"); @@ -1675,7 +1681,7 @@ SENTRY_TEST(scope_observer_clear) TEST_CHECK(observer_data.was_cleared); TEST_CHECK_INT_EQUAL(scope->is_notifying, 0); TEST_CHECK_INT_EQUAL(scope->num_observers, 1); - TEST_CHECK(sentry_value_get_length(scope->tags) == 0); + TEST_CHECK(sentry_value_get_length(sentry__scope_get_tags(scope)) == 0); sentry_value_decref(observer_data.tags); sentry_scope_free(scope); @@ -2398,7 +2404,8 @@ SENTRY_TEST(scope_set_attribute_invalid_decref_value) TEST_CHECK_INT_EQUAL(sentry_value_refcount(no_type), 1); sentry_value_decref(no_type); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope->attributes), 0); + TEST_CHECK_INT_EQUAL( + sentry_value_get_length(sentry__scope_get_attributes(scope)), 0); sentry_scope_free(scope); } @@ -2415,7 +2422,8 @@ SENTRY_TEST(scope_set_attribute_null_key_decref_value) TEST_CHECK_INT_EQUAL(sentry_value_refcount(v), 1); sentry_value_decref(v); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope->attributes), 0); + TEST_CHECK_INT_EQUAL( + sentry_value_get_length(sentry__scope_get_attributes(scope)), 0); sentry_scope_free(scope); } @@ -2436,7 +2444,8 @@ SENTRY_TEST(scope_ownership) static size_t scope_breadcrumb_count(const sentry_scope_t *scope) { - sentry_value_t breadcrumbs = sentry__ringbuffer_to_list(scope->breadcrumbs); + sentry_value_t breadcrumbs + = sentry__ringbuffer_to_list(sentry__scope_get_breadcrumbs(scope)); size_t count = sentry_value_get_length(breadcrumbs); sentry_value_decref(breadcrumbs); return count; @@ -2458,10 +2467,10 @@ SENTRY_TEST(scope_clone_independence) TEST_CHECK(!clone->one_shot); // The clone carries over the source's data. - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key(clone->tags, "shared")), + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + sentry__scope_get_tags(clone), "shared")), "original"); - TEST_CHECK(clone->level == SENTRY_LEVEL_WARNING); + TEST_CHECK(sentry__scope_get_level(clone) == SENTRY_LEVEL_WARNING); TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(clone), 1); // Mutating the clone does not affect the source, and vice versa. @@ -2470,11 +2479,11 @@ SENTRY_TEST(scope_clone_independence) sentry_scope_add_breadcrumb( clone, sentry_value_new_breadcrumb(NULL, "second")); - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key(scope->tags, "shared")), + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + sentry__scope_get_tags(scope), "shared")), "original"); TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(scope->tags, "clone_only"))); + sentry_value_get_by_key(sentry__scope_get_tags(scope), "clone_only"))); TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(scope), 1); TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(clone), 2); @@ -2509,23 +2518,26 @@ SENTRY_TEST(scope_clone_preserves_data) sentry_value_decref( sentry__attachments_remove(scope->attachments, &attachment_id)); - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key(clone->tags, "tag_key")), + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + sentry__scope_get_tags(clone), "tag_key")), "tag_value"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - clone->contexts, "device")), + sentry__scope_get_contexts(clone), "device")), "Xbox"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - clone->user, "username")), + sentry__scope_get_user(clone), "username")), "alice"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - clone->extra, "extra_key")), + sentry__scope_get_extra(clone), "extra_key")), "extra_value"); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(clone->fingerprint), 2); - TEST_CHECK(clone->level == SENTRY_LEVEL_WARNING); + TEST_CHECK_INT_EQUAL( + sentry_value_get_length(sentry__scope_get_fingerprint(clone)), 2); + TEST_CHECK(sentry__scope_get_level(clone) == SENTRY_LEVEL_WARNING); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key( - sentry_value_get_by_key(clone->attributes, "attr_key"), "value")), + sentry_value_get_by_key( + sentry__scope_get_attributes(clone), "attr_key"), + "value")), "attr_value"); TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(clone), 1); @@ -2559,13 +2571,13 @@ SENTRY_TEST(scope_clone_shares_span) sentry_scope_t *clone = NULL; sentry_transaction_t *scope_txn = NULL; SENTRY_WITH_SCOPE (scope) { - scope_txn = scope->transaction_object; + scope_txn = sentry__scope_get_transaction_object(scope); clone = sentry_scope_clone(scope); } // The active transaction is shared by reference, not dropped or duplicated. TEST_CHECK(scope_txn != NULL); - TEST_CHECK(clone->transaction_object == scope_txn); + TEST_CHECK(sentry__scope_get_transaction_object(clone) == scope_txn); // The shared reference keeps the transaction alive for the original: the // clone can be freed and the transaction still finished safely. @@ -2584,9 +2596,9 @@ SENTRY_TEST(scope_clear) // Clearing a scope must keep trace propagation data intact, including the // dynamic sampling context. - sentry_value_set_by_key( - scope->propagation_context, "marker", sentry_value_new_string("keep")); - sentry_value_set_by_key(scope->dynamic_sampling_context, "marker", + sentry__scope_set_propagation_context( + scope, "marker", sentry_value_new_string("keep")); + sentry_value_set_by_key(sentry__scope_get_dsc(scope), "marker", sentry_value_new_string("keep")); sentry_scope_set_tag(scope, "tag", "value"); @@ -2604,35 +2616,39 @@ SENTRY_TEST(scope_clear) sentry_scope_add_breadcrumb( scope, sentry_value_new_breadcrumb(NULL, "crumb")); - TEST_CHECK(sentry_value_get_length(scope->tags) == 1); - TEST_CHECK(sentry_value_get_length(scope->attributes) == 1); - TEST_CHECK(!sentry_value_is_null(scope->user)); + TEST_CHECK(sentry_value_get_length(sentry__scope_get_tags(scope)) == 1); + TEST_CHECK( + sentry_value_get_length(sentry__scope_get_attributes(scope)) == 1); + TEST_CHECK(!sentry_value_is_null(sentry__scope_get_user(scope))); sentry_scope_clear(scope); // Everything is reset to the state of a fresh scope. - TEST_CHECK(sentry_value_get_length(scope->tags) == 0); - TEST_CHECK(sentry_value_get_length(scope->extra) == 0); - TEST_CHECK(sentry_value_get_length(scope->contexts) == 0); - TEST_CHECK(sentry_value_get_length(scope->attributes) == 0); - TEST_CHECK(sentry_value_is_null(scope->user)); - TEST_CHECK(sentry_value_is_null(scope->fingerprint)); - TEST_CHECK_INT_EQUAL(scope->level, SENTRY_LEVEL_ERROR); - sentry_value_t crumbs = sentry__ringbuffer_to_list(scope->breadcrumbs); + TEST_CHECK(sentry_value_get_length(sentry__scope_get_tags(scope)) == 0); + TEST_CHECK(sentry_value_get_length(sentry__scope_get_extra(scope)) == 0); + TEST_CHECK(sentry_value_get_length(sentry__scope_get_contexts(scope)) == 0); + TEST_CHECK( + sentry_value_get_length(sentry__scope_get_attributes(scope)) == 0); + TEST_CHECK(sentry_value_is_null(sentry__scope_get_user(scope))); + TEST_CHECK(sentry_value_is_null(sentry__scope_get_fingerprint(scope))); + TEST_CHECK_INT_EQUAL(sentry__scope_get_level(scope), SENTRY_LEVEL_ERROR); + sentry_value_t crumbs + = sentry__ringbuffer_to_list(sentry__scope_get_breadcrumbs(scope)); TEST_CHECK(sentry_value_get_length(crumbs) == 0); sentry_value_decref(crumbs); // ... except the trace, which is preserved. - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - scope->propagation_context, "marker")), + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key( + sentry__scope_get_propagation_context(scope), "marker")), "keep"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - scope->dynamic_sampling_context, "marker")), + sentry__scope_get_dsc(scope), "marker")), "keep"); // The cleared scope is still usable. sentry_scope_set_tag(scope, "after", "clear"); - TEST_CHECK(sentry_value_get_length(scope->tags) == 1); + TEST_CHECK(sentry_value_get_length(sentry__scope_get_tags(scope)) == 1); sentry_scope_free(scope); sentry_close(); @@ -2825,8 +2841,8 @@ SENTRY_TEST(scope_capture_user_owned) // The scope was applied but not freed, so reading and reusing it is safe // (a use-after-free here would trip the sanitizers). - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key(scope->tags, "run")), + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + sentry__scope_get_tags(scope), "run")), "first"); sentry_scope_set_tag(scope, "run", "second"); diff --git a/tests/unit/test_tracing.c b/tests/unit/test_tracing.c index ae1cb9588a..31fc613c28 100644 --- a/tests/unit/test_tracing.c +++ b/tests/unit/test_tracing.c @@ -852,7 +852,7 @@ SENTRY_TEST(trace_finish) // Scope still points at the (finished) span so a subsequent crash event // inherits its trace context. SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(scope->span != NULL); + TEST_CHECK(sentry__scope_get_span(scope) != NULL); } sentry__span_decref(grand); @@ -934,13 +934,13 @@ SENTRY_TEST(discard_transaction) sentry_set_transaction_object(tx); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(scope->transaction_object == tx); + TEST_CHECK(sentry__scope_get_transaction_object(scope) == tx); } sentry_transaction_discard(tx); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(scope->transaction_object == NULL); + TEST_CHECK(sentry__scope_get_transaction_object(scope) == NULL); } sentry_close(); @@ -970,13 +970,13 @@ SENTRY_TEST(discard_span) sentry_set_span(span); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(scope->span == span); + TEST_CHECK(sentry__scope_get_span(scope) == span); } sentry_span_discard(span); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(scope->span == NULL); + TEST_CHECK(sentry__scope_get_span(scope) == NULL); } TEST_CHECK_INT_EQUAL( sentry_value_get_length(sentry_value_get_by_key(tx->inner, "spans")), @@ -1566,7 +1566,7 @@ SENTRY_TEST(set_trace) SENTRY_WITH_SCOPE (scope) { sentry_value_t propagation_trace_context - = sentry_value_get_by_key(scope->propagation_context, "trace"); + = sentry__scope_get_trace_context(scope); TEST_CHECK(!sentry_value_is_null(propagation_trace_context)); CHECK_STRING_PROPERTY(propagation_trace_context, "type", "trace"); @@ -2428,8 +2428,7 @@ SENTRY_TEST(strict_continuation_no_baggage_forks) SENTRY_WITH_SCOPE (scope) { const char *scope_trace_id = sentry_value_as_string(sentry_value_get_by_key( - sentry_value_get_by_key(scope->propagation_context, "trace"), - "trace_id")); + sentry__scope_get_trace_context(scope), "trace_id")); TEST_CHECK(strcmp(scope_trace_id, UPSTREAM_TRACE_ID) != 0); TEST_CHECK_STRING_EQUAL(scope_trace_id, trace_id); } @@ -2477,7 +2476,7 @@ SENTRY_TEST(set_trace_rebuilds_dsc_sample_rand) double init_sample_rand = 0.0; SENTRY_WITH_SCOPE (scope) { init_sample_rand = sentry_value_as_double(sentry_value_get_by_key( - scope->dynamic_sampling_context, "sample_rand")); + sentry__scope_get_dsc(scope), "sample_rand")); } sentry_set_trace("11112222333344445555666677778888", "1234567812345678"); @@ -2485,7 +2484,7 @@ SENTRY_TEST(set_trace_rebuilds_dsc_sample_rand) double new_sample_rand = -1.0; SENTRY_WITH_SCOPE (scope) { new_sample_rand = sentry_value_as_double(sentry_value_get_by_key( - scope->dynamic_sampling_context, "sample_rand")); + sentry__scope_get_dsc(scope), "sample_rand")); } // sample_rand is regenerated for the new trace, so the DSC must reflect // the fresh value, not the init-time one. From ffa73f75f59ea7051f101ddab1dd0ae744376fce Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 27 Jul 2026 15:54:04 +0200 Subject: [PATCH 02/28] WIP: observers --- src/sentry_scope.c | 12 ++++++++++++ src/sentry_scope.h | 2 ++ tests/unit/test_scope.c | 26 ++++++++++---------------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index b27e098f40..008b4a10ba 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -681,6 +681,18 @@ sentry__scope_get_span_or_transaction(void) } return result; } + +bool +sentry__scope_has_observers(const sentry_scope_t *scope) +{ + return scope->num_observers > 0; +} + +bool +sentry__scope_is_one_shot(const sentry_scope_t *scope) +{ + return scope->one_shot; +} #endif void diff --git a/src/sentry_scope.h b/src/sentry_scope.h index 4da36822cf..4bc100ffc9 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -313,4 +313,6 @@ void sentry__scope_capture_envelope(sentry_scope_t *scope, // this is only used in unit tests #ifdef SENTRY_UNITTEST sentry_value_t sentry__scope_get_span_or_transaction(void); +bool sentry__scope_has_observers(const sentry_scope_t *scope); +bool sentry__scope_is_one_shot(const sentry_scope_t *scope); #endif diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index 28e595c291..1723cfa0e9 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -1652,7 +1652,7 @@ SENTRY_TEST(scope_observer_clear) sentry_scope_clear(scope); TEST_CHECK(d.was_cleared); - TEST_CHECK_INT_EQUAL(scope->num_observers, 1); + TEST_CHECK(sentry__scope_has_observers(scope)); TEST_CHECK(sentry_value_get_length(sentry__scope_get_tags(scope)) == 0); d.was_called = false; @@ -1679,8 +1679,7 @@ SENTRY_TEST(scope_observer_clear) sentry_scope_set_tag(scope, "during", "notify"); TEST_CHECK(observer_data.was_called); TEST_CHECK(observer_data.was_cleared); - TEST_CHECK_INT_EQUAL(scope->is_notifying, 0); - TEST_CHECK_INT_EQUAL(scope->num_observers, 1); + TEST_CHECK(sentry__scope_has_observers(scope)); TEST_CHECK(sentry_value_get_length(sentry__scope_get_tags(scope)) == 0); sentry_value_decref(observer_data.tags); @@ -1694,12 +1693,10 @@ SENTRY_TEST(scope_observer_null) SENTRY_WITH_SCOPE_MUT (scope) { TEST_CHECK(!sentry__scope_add_observer(scope, NULL)); - TEST_CHECK_INT_EQUAL(scope->num_observers, 0); - TEST_CHECK(scope->observers == NULL); + TEST_CHECK(!sentry__scope_has_observers(scope)); sentry__scope_remove_observer(scope, NULL); - TEST_CHECK_INT_EQUAL(scope->num_observers, 0); - TEST_CHECK(scope->observers == NULL); + TEST_CHECK(!sentry__scope_has_observers(scope)); } test_observer_data_t d = { .tags = sentry_value_new_null() }; @@ -1757,8 +1754,7 @@ SENTRY_TEST(scope_observer_multiple) d2.was_called = false; SENTRY_WITH_SCOPE_MUT (scope) { sentry__scope_remove_observer(scope, observer2); - TEST_CHECK_INT_EQUAL(scope->num_observers, 1); - TEST_CHECK(scope->observers != NULL); + TEST_CHECK(sentry__scope_has_observers(scope)); } sentry_set_tag("multi", "again"); @@ -1770,8 +1766,7 @@ SENTRY_TEST(scope_observer_multiple) SENTRY_WITH_SCOPE_MUT (scope) { sentry__scope_remove_observer(scope, observer1); - TEST_CHECK_INT_EQUAL(scope->num_observers, 0); - TEST_CHECK(scope->observers == NULL); + TEST_CHECK(!sentry__scope_has_observers(scope)); } sentry_value_decref(d1.tags); @@ -1836,14 +1831,13 @@ SENTRY_TEST(scope_observer_mutate) SENTRY_WITH_SCOPE_MUT (scope) { TEST_CHECK(sentry__scope_add_observer(scope, observer4)); - TEST_CHECK_INT_EQUAL(scope->num_observers, 1); + TEST_CHECK(sentry__scope_has_observers(scope)); } sentry_set_tag("self", "remove"); TEST_CHECK(d4.was_called); SENTRY_WITH_SCOPE_MUT (scope) { - TEST_CHECK_INT_EQUAL(scope->num_observers, 0); - TEST_CHECK(scope->observers == NULL); + TEST_CHECK(!sentry__scope_has_observers(scope)); } sentry_value_decref(d1.tags); @@ -2433,11 +2427,11 @@ SENTRY_TEST(scope_ownership) // `sentry_local_scope_new` makes a one-shot scope, `sentry_scope_new` does // not. sentry_scope_t *local_scope = sentry_local_scope_new(); - TEST_CHECK(local_scope->one_shot); + TEST_CHECK(sentry__scope_is_one_shot(local_scope)); sentry_scope_free(local_scope); sentry_scope_t *user_scope = sentry_scope_new(); - TEST_CHECK(!user_scope->one_shot); + TEST_CHECK(!sentry__scope_is_one_shot(user_scope)); sentry_scope_free(user_scope); } From 56f7f071a1355ed6ef154c54d089396648e19320 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 5 Aug 2026 16:15:54 +0200 Subject: [PATCH 03/28] feat(sync): add platform rwlock primitive 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. --- src/sentry_sync.h | 184 ++++++++++++++++++++++++++++ tests/unit/test_sync.c | 270 +++++++++++++++++++++++++++++++++++++++++ tests/unit/tests.inc | 7 ++ 3 files changed, 461 insertions(+) diff --git a/src/sentry_sync.h b/src/sentry_sync.h index 3e28a28360..39cfbe81f5 100644 --- a/src/sentry_sync.h +++ b/src/sentry_sync.h @@ -9,6 +9,7 @@ // This is a NOP for platforms that support static mutex initialization. #define SENTRY__MUTEX_INIT_DYN_ONCE(Mutex) ((void)0) +#define SENTRY__RWLOCK_INIT_DYN_ONCE(Rwlock) ((void)0) #ifdef _MSC_VER # define THREAD_FUNCTION_API __stdcall @@ -191,6 +192,137 @@ typedef struct sentry__winmutex_s sentry_mutex_t; # define sentry__mutex_free(Lock) \ DeleteCriticalSection(&(Lock)->critical_section) +# if _WIN32_WINNT >= 0x0600 +typedef SRWLOCK sentry_rwlock_t; +# define SENTRY__RWLOCK_INIT SRWLOCK_INIT +# define sentry__rwlock_init(Lock) InitializeSRWLock(Lock) +# define sentry__rwlock_read_lock(Lock) AcquireSRWLockShared(Lock) +# define sentry__rwlock_read_unlock(Lock) ReleaseSRWLockShared(Lock) +# define sentry__rwlock_write_lock(Lock) AcquireSRWLockExclusive(Lock) +# define sentry__rwlock_write_unlock(Lock) ReleaseSRWLockExclusive(Lock) +# define sentry__rwlock_free(Lock) ((void)0) +# else +typedef struct sentry__winrwlock_s { + INIT_ONCE init_once; + CRITICAL_SECTION lock; + CONDITION_VARIABLE_PREVISTA readers_cond; + CONDITION_VARIABLE_PREVISTA writers_cond; + unsigned readers; + unsigned writers_waiting; + bool writer; +} sentry_rwlock_t; + +# define SENTRY__RWLOCK_INIT \ + { INIT_ONCE_STATIC_INIT, { 0 }, { 0 }, { 0 }, 0, 0, false } + +static inline BOOL CALLBACK +sentry__winrwlock_initonce( + PINIT_ONCE UNUSED(InitOnce), PVOID data, PVOID *UNUSED(lpContext)) +{ + sentry_rwlock_t *rwlock = (sentry_rwlock_t *)data; + InitializeCriticalSection(&rwlock->lock); + InitializeConditionVariable_PREVISTA(&rwlock->readers_cond); + InitializeConditionVariable_PREVISTA(&rwlock->writers_cond); + rwlock->readers = 0; + rwlock->writers_waiting = 0; + rwlock->writer = false; + return TRUE; +} + +static inline void +sentry__winrwlock_ensure_init(sentry_rwlock_t *rwlock) +{ + InitOnceExecuteOnce( + &rwlock->init_once, sentry__winrwlock_initonce, rwlock, NULL); +} + +static inline void +sentry__winrwlock_init(sentry_rwlock_t *rwlock) +{ + sentry_rwlock_t tmp = SENTRY__RWLOCK_INIT; + *rwlock = tmp; + sentry__winrwlock_ensure_init(rwlock); +} + +static inline void +sentry__winrwlock_read_lock(sentry_rwlock_t *rwlock) +{ + sentry__winrwlock_ensure_init(rwlock); + EnterCriticalSection(&rwlock->lock); + while (rwlock->writer || rwlock->writers_waiting > 0) { + SleepConditionVariableCS_PREVISTA( + &rwlock->readers_cond, &rwlock->lock, INFINITE); + } + rwlock->readers++; + if (rwlock->writers_waiting == 0) { + WakeConditionVariable_PREVISTA(&rwlock->readers_cond); + } + LeaveCriticalSection(&rwlock->lock); +} + +static inline void +sentry__winrwlock_read_unlock(sentry_rwlock_t *rwlock) +{ + EnterCriticalSection(&rwlock->lock); + assert(rwlock->readers > 0); + rwlock->readers--; + if (rwlock->readers == 0 && rwlock->writers_waiting > 0) { + WakeConditionVariable_PREVISTA(&rwlock->writers_cond); + } + LeaveCriticalSection(&rwlock->lock); +} + +static inline void +sentry__winrwlock_write_lock(sentry_rwlock_t *rwlock) +{ + sentry__winrwlock_ensure_init(rwlock); + EnterCriticalSection(&rwlock->lock); + rwlock->writers_waiting++; + while (rwlock->writer || rwlock->readers > 0) { + SleepConditionVariableCS_PREVISTA( + &rwlock->writers_cond, &rwlock->lock, INFINITE); + } + rwlock->writers_waiting--; + rwlock->writer = true; + LeaveCriticalSection(&rwlock->lock); +} + +static inline void +sentry__winrwlock_write_unlock(sentry_rwlock_t *rwlock) +{ + EnterCriticalSection(&rwlock->lock); + assert(rwlock->writer); + rwlock->writer = false; + if (rwlock->writers_waiting > 0) { + WakeConditionVariable_PREVISTA(&rwlock->writers_cond); + } else { + WakeConditionVariable_PREVISTA(&rwlock->readers_cond); + } + LeaveCriticalSection(&rwlock->lock); +} + +static inline void +sentry__winrwlock_free(sentry_rwlock_t *rwlock) +{ + sentry__winrwlock_ensure_init(rwlock); + DeleteCriticalSection(&rwlock->lock); + CloseHandle(rwlock->readers_cond.Semaphore); + CloseHandle(rwlock->readers_cond.ContinueEvent); + CloseHandle(rwlock->writers_cond.Semaphore); + CloseHandle(rwlock->writers_cond.ContinueEvent); +} + +# define sentry__rwlock_init(Lock) sentry__winrwlock_init(Lock) +# define sentry__rwlock_read_lock(Lock) sentry__winrwlock_read_lock(Lock) +# define sentry__rwlock_read_unlock(Lock) \ + sentry__winrwlock_read_unlock(Lock) +# define sentry__rwlock_write_lock(Lock) \ + sentry__winrwlock_write_lock(Lock) +# define sentry__rwlock_write_unlock(Lock) \ + sentry__winrwlock_write_unlock(Lock) +# define sentry__rwlock_free(Lock) sentry__winrwlock_free(Lock) +# endif + # define sentry__thread_init(ThreadId) *ThreadId = INVALID_HANDLE_VALUE # define sentry__thread_spawn(ThreadId, Func, Data) \ (*ThreadId = CreateThread(NULL, 0, Func, Data, 0, NULL), \ @@ -268,6 +400,7 @@ void sentry__leave_signal_handler(void); typedef pthread_t sentry_threadid_t; typedef pthread_mutex_t sentry_mutex_t; +typedef pthread_rwlock_t sentry_rwlock_t; typedef pthread_cond_t sentry_cond_t; # ifdef SENTRY_PLATFORM_LINUX @@ -349,6 +482,57 @@ typedef pthread_cond_t sentry_cond_t; } \ } while (0) # define sentry__mutex_free(Lock) pthread_mutex_destroy(Lock) +# ifdef PTHREAD_RWLOCK_INITIALIZER +# define SENTRY__RWLOCK_INIT PTHREAD_RWLOCK_INITIALIZER +# define sentry__rwlock_init(Rwlock) \ + do { \ + sentry_rwlock_t tmp = SENTRY__RWLOCK_INIT; \ + *(Rwlock) = tmp; \ + } while (0) +# else +# define SENTRY__RWLOCK_INIT_DYN(Rwlock) \ + static sentry_rwlock_t Rwlock; \ + static pthread_once_t Rwlock##_init_once = PTHREAD_ONCE_INIT; \ + static void init_##Rwlock(void) { sentry__rwlock_init(&Rwlock); } +# undef SENTRY__RWLOCK_INIT_DYN_ONCE +# define SENTRY__RWLOCK_INIT_DYN_ONCE(Rwlock) \ + pthread_once(&Rwlock##_init_once, init_##Rwlock) +# define sentry__rwlock_init(Rwlock) \ + do { \ + int rv = pthread_rwlock_init(Rwlock, NULL); \ + (void)rv; \ + assert(rv == 0); \ + } while (0) +# endif +# define sentry__rwlock_read_lock(Rwlock) \ + do { \ + if (sentry__block_for_signal_handler()) { \ + int rv = pthread_rwlock_rdlock(Rwlock); \ + (void)rv; \ + assert(rv == 0); \ + } \ + } while (0) +# define sentry__rwlock_read_unlock(Rwlock) \ + do { \ + if (sentry__block_for_signal_handler()) { \ + pthread_rwlock_unlock(Rwlock); \ + } \ + } while (0) +# define sentry__rwlock_write_lock(Rwlock) \ + do { \ + if (sentry__block_for_signal_handler()) { \ + int rv = pthread_rwlock_wrlock(Rwlock); \ + (void)rv; \ + assert(rv == 0); \ + } \ + } while (0) +# define sentry__rwlock_write_unlock(Rwlock) \ + do { \ + if (sentry__block_for_signal_handler()) { \ + pthread_rwlock_unlock(Rwlock); \ + } \ + } while (0) +# define sentry__rwlock_free(Rwlock) pthread_rwlock_destroy(Rwlock) # define sentry__cond_init(CondVar) \ do { \ diff --git a/tests/unit/test_sync.c b/tests/unit/test_sync.c index d73df51064..38ec25a9e1 100644 --- a/tests/unit/test_sync.c +++ b/tests/unit/test_sync.c @@ -5,6 +5,276 @@ #include "sentry_utils.h" #include +#define RWLOCK_WAIT_TIMEOUT_MS 2000 + +static bool +wait_for_atomic_value(volatile long *value, long expected) +{ + uint64_t deadline = sentry__monotonic_time() + RWLOCK_WAIT_TIMEOUT_MS; + while (sentry__atomic_fetch(value) != expected) { + if (sentry__monotonic_time() >= deadline) { + return false; + } + sentry__thread_yield(); + sleep_ms(1); + } + return true; +} + +#ifdef SENTRY__RWLOCK_INIT_DYN +SENTRY__RWLOCK_INIT_DYN(static_rwlock) +#else +static sentry_rwlock_t static_rwlock = SENTRY__RWLOCK_INIT; +#endif + +SENTRY_TEST(rwlock_static_init) +{ + SENTRY__RWLOCK_INIT_DYN_ONCE(static_rwlock); + + sentry__rwlock_read_lock(&static_rwlock); + sentry__rwlock_read_unlock(&static_rwlock); + + sentry__rwlock_write_lock(&static_rwlock); + sentry__rwlock_write_unlock(&static_rwlock); +} + +SENTRY_TEST(rwlock_dynamic_init) +{ + sentry_rwlock_t rwlock; + sentry__rwlock_init(&rwlock); + + sentry__rwlock_read_lock(&rwlock); + sentry__rwlock_read_unlock(&rwlock); + + sentry__rwlock_write_lock(&rwlock); + sentry__rwlock_write_unlock(&rwlock); + + sentry__rwlock_free(&rwlock); +} + +typedef struct { + sentry_rwlock_t *rwlock; + volatile long start; + volatile long release; + volatile long entered; + volatile long active_readers; +} rwlock_readers_state_t; + +SENTRY_THREAD_FN +rwlock_reader_thread(void *data) +{ + rwlock_readers_state_t *state = (rwlock_readers_state_t *)data; + while (!sentry__atomic_fetch(&state->start)) { + sentry__thread_yield(); + } + + sentry__rwlock_read_lock(state->rwlock); + sentry__atomic_fetch_and_add(&state->active_readers, 1); + sentry__atomic_fetch_and_add(&state->entered, 1); + while (!sentry__atomic_fetch(&state->release)) { + sentry__thread_yield(); + } + sentry__atomic_fetch_and_add(&state->active_readers, -1); + sentry__rwlock_read_unlock(state->rwlock); + return 0; +} + +SENTRY_TEST(rwlock_shared_readers) +{ + enum { NUM_READERS = 8 }; + + sentry_rwlock_t rwlock; + sentry__rwlock_init(&rwlock); + + rwlock_readers_state_t state = { &rwlock, 0, 0, 0, 0 }; + sentry_threadid_t threads[NUM_READERS]; + for (int i = 0; i < NUM_READERS; i++) { + sentry__thread_init(&threads[i]); + TEST_CHECK_INT_EQUAL( + sentry__thread_spawn(&threads[i], rwlock_reader_thread, &state), 0); + } + + sentry__atomic_store(&state.start, 1); + TEST_CHECK(wait_for_atomic_value(&state.entered, NUM_READERS)); + TEST_CHECK_INT_EQUAL( + sentry__atomic_fetch(&state.active_readers), NUM_READERS); + + sentry__atomic_store(&state.release, 1); + for (int i = 0; i < NUM_READERS; i++) { + sentry__thread_join(threads[i]); + } + + sentry__rwlock_free(&rwlock); +} + +typedef struct { + sentry_rwlock_t *rwlock; + bool write; + volatile long attempting; + volatile long entered; + volatile long release; +} rwlock_waiter_state_t; + +SENTRY_THREAD_FN +rwlock_waiter_thread(void *data) +{ + rwlock_waiter_state_t *state = (rwlock_waiter_state_t *)data; + sentry__atomic_store(&state->attempting, 1); + if (state->write) { + sentry__rwlock_write_lock(state->rwlock); + } else { + sentry__rwlock_read_lock(state->rwlock); + } + sentry__atomic_store(&state->entered, 1); + while (!sentry__atomic_fetch(&state->release)) { + sentry__thread_yield(); + } + if (state->write) { + sentry__rwlock_write_unlock(state->rwlock); + } else { + sentry__rwlock_read_unlock(state->rwlock); + } + return 0; +} + +static void +check_rwlock_blocks_waiter(bool holder_write, bool waiter_write) +{ + sentry_rwlock_t rwlock; + sentry__rwlock_init(&rwlock); + + if (holder_write) { + sentry__rwlock_write_lock(&rwlock); + } else { + sentry__rwlock_read_lock(&rwlock); + } + + rwlock_waiter_state_t state = { &rwlock, waiter_write, 0, 0, 0 }; + sentry_threadid_t thread; + sentry__thread_init(&thread); + TEST_CHECK_INT_EQUAL( + sentry__thread_spawn(&thread, rwlock_waiter_thread, &state), 0); + + TEST_CHECK(wait_for_atomic_value(&state.attempting, 1)); + sleep_ms(100); + TEST_CHECK_INT_EQUAL(sentry__atomic_fetch(&state.entered), 0); + + if (holder_write) { + sentry__rwlock_write_unlock(&rwlock); + } else { + sentry__rwlock_read_unlock(&rwlock); + } + + TEST_CHECK(wait_for_atomic_value(&state.entered, 1)); + sentry__atomic_store(&state.release, 1); + sentry__thread_join(thread); + + sentry__rwlock_free(&rwlock); +} + +SENTRY_TEST(rwlock_read_blocks_writer) +{ + check_rwlock_blocks_waiter(false, true); +} + +SENTRY_TEST(rwlock_write_blocks_reader) +{ + check_rwlock_blocks_waiter(true, false); +} + +SENTRY_TEST(rwlock_write_blocks_writer) +{ + check_rwlock_blocks_waiter(true, true); +} + +typedef struct { + sentry_rwlock_t *rwlock; + volatile long start; + volatile long writers_done; + volatile long failed; + long writers_total; + long counter; +} rwlock_stress_state_t; + +SENTRY_THREAD_FN +rwlock_writer_thread(void *data) +{ + rwlock_stress_state_t *state = (rwlock_stress_state_t *)data; + while (!sentry__atomic_fetch(&state->start)) { + sentry__thread_yield(); + } + + for (int i = 0; i < 1000; i++) { + sentry__rwlock_write_lock(state->rwlock); + state->counter++; + sentry__rwlock_write_unlock(state->rwlock); + if (i % 16 == 0) { + sentry__thread_yield(); + } + } + sentry__atomic_fetch_and_add(&state->writers_done, 1); + return 0; +} + +SENTRY_THREAD_FN +rwlock_stress_reader_thread(void *data) +{ + rwlock_stress_state_t *state = (rwlock_stress_state_t *)data; + long previous = 0; + while (!sentry__atomic_fetch(&state->start)) { + sentry__thread_yield(); + } + + while (sentry__atomic_fetch(&state->writers_done) < state->writers_total) { + sentry__rwlock_read_lock(state->rwlock); + long current = state->counter; + sentry__rwlock_read_unlock(state->rwlock); + if (current < previous) { + sentry__atomic_store(&state->failed, 1); + } + previous = current; + sentry__thread_yield(); + } + return 0; +} + +SENTRY_TEST(rwlock_stress) +{ + enum { NUM_WRITERS = 4, NUM_READERS = 4 }; + + sentry_rwlock_t rwlock; + sentry__rwlock_init(&rwlock); + + rwlock_stress_state_t state = { &rwlock, 0, 0, 0, NUM_WRITERS, 0 }; + sentry_threadid_t writers[NUM_WRITERS]; + sentry_threadid_t readers[NUM_READERS]; + + for (int i = 0; i < NUM_WRITERS; i++) { + sentry__thread_init(&writers[i]); + TEST_CHECK_INT_EQUAL( + sentry__thread_spawn(&writers[i], rwlock_writer_thread, &state), 0); + } + for (int i = 0; i < NUM_READERS; i++) { + sentry__thread_init(&readers[i]); + TEST_CHECK_INT_EQUAL(sentry__thread_spawn(&readers[i], + rwlock_stress_reader_thread, &state), + 0); + } + + sentry__atomic_store(&state.start, 1); + for (int i = 0; i < NUM_WRITERS; i++) { + sentry__thread_join(writers[i]); + } + for (int i = 0; i < NUM_READERS; i++) { + sentry__thread_join(readers[i]); + } + + TEST_CHECK_INT_EQUAL(state.counter, NUM_WRITERS * 1000); + TEST_CHECK_INT_EQUAL(sentry__atomic_fetch(&state.failed), 0); + + sentry__rwlock_free(&rwlock); +} + struct task_state { int executed; bool running; diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 201c435043..391063f970 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -306,6 +306,13 @@ XX(ringbuffer_max_size_null_noop) XX(ringbuffer_max_size_post_init) XX(ringbuffer_to_list_null_value_null) XX(ringbuffer_zero_noop) +XX(rwlock_dynamic_init) +XX(rwlock_read_blocks_writer) +XX(rwlock_shared_readers) +XX(rwlock_static_init) +XX(rwlock_stress) +XX(rwlock_write_blocks_reader) +XX(rwlock_write_blocks_writer) XX(sampling_before_send) XX(sampling_decision) XX(sampling_transaction) From 47bba2678fcbfe2522a5805039dec34b053d69ba Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 5 Aug 2026 17:58:03 +0200 Subject: [PATCH 04/28] refactor(sync): make rwlock operations inline Convert the POSIX rwlock operations from statement macros to static inline functions so callers can use them from scoped lock macro expressions. --- src/sentry_sync.h | 97 ++++++++++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 40 deletions(-) diff --git a/src/sentry_sync.h b/src/sentry_sync.h index 39cfbe81f5..61a72edfb2 100644 --- a/src/sentry_sync.h +++ b/src/sentry_sync.h @@ -484,12 +484,22 @@ typedef pthread_cond_t sentry_cond_t; # define sentry__mutex_free(Lock) pthread_mutex_destroy(Lock) # ifdef PTHREAD_RWLOCK_INITIALIZER # define SENTRY__RWLOCK_INIT PTHREAD_RWLOCK_INITIALIZER -# define sentry__rwlock_init(Rwlock) \ - do { \ - sentry_rwlock_t tmp = SENTRY__RWLOCK_INIT; \ - *(Rwlock) = tmp; \ - } while (0) +# endif + +static inline void +sentry__rwlock_init(sentry_rwlock_t *rwlock) +{ +# ifdef PTHREAD_RWLOCK_INITIALIZER + sentry_rwlock_t tmp = SENTRY__RWLOCK_INIT; + *rwlock = tmp; # else + int rv = pthread_rwlock_init(rwlock, NULL); + (void)rv; + assert(rv == 0); +# endif +} + +# ifndef PTHREAD_RWLOCK_INITIALIZER # define SENTRY__RWLOCK_INIT_DYN(Rwlock) \ static sentry_rwlock_t Rwlock; \ static pthread_once_t Rwlock##_init_once = PTHREAD_ONCE_INIT; \ @@ -497,42 +507,49 @@ typedef pthread_cond_t sentry_cond_t; # undef SENTRY__RWLOCK_INIT_DYN_ONCE # define SENTRY__RWLOCK_INIT_DYN_ONCE(Rwlock) \ pthread_once(&Rwlock##_init_once, init_##Rwlock) -# define sentry__rwlock_init(Rwlock) \ - do { \ - int rv = pthread_rwlock_init(Rwlock, NULL); \ - (void)rv; \ - assert(rv == 0); \ - } while (0) # endif -# define sentry__rwlock_read_lock(Rwlock) \ - do { \ - if (sentry__block_for_signal_handler()) { \ - int rv = pthread_rwlock_rdlock(Rwlock); \ - (void)rv; \ - assert(rv == 0); \ - } \ - } while (0) -# define sentry__rwlock_read_unlock(Rwlock) \ - do { \ - if (sentry__block_for_signal_handler()) { \ - pthread_rwlock_unlock(Rwlock); \ - } \ - } while (0) -# define sentry__rwlock_write_lock(Rwlock) \ - do { \ - if (sentry__block_for_signal_handler()) { \ - int rv = pthread_rwlock_wrlock(Rwlock); \ - (void)rv; \ - assert(rv == 0); \ - } \ - } while (0) -# define sentry__rwlock_write_unlock(Rwlock) \ - do { \ - if (sentry__block_for_signal_handler()) { \ - pthread_rwlock_unlock(Rwlock); \ - } \ - } while (0) -# define sentry__rwlock_free(Rwlock) pthread_rwlock_destroy(Rwlock) + +static inline void +sentry__rwlock_read_lock(sentry_rwlock_t *rwlock) +{ + if (sentry__block_for_signal_handler()) { + int rv = pthread_rwlock_rdlock(rwlock); + (void)rv; + assert(rv == 0); + } +} + +static inline void +sentry__rwlock_read_unlock(sentry_rwlock_t *rwlock) +{ + if (sentry__block_for_signal_handler()) { + pthread_rwlock_unlock(rwlock); + } +} + +static inline void +sentry__rwlock_write_lock(sentry_rwlock_t *rwlock) +{ + if (sentry__block_for_signal_handler()) { + int rv = pthread_rwlock_wrlock(rwlock); + (void)rv; + assert(rv == 0); + } +} + +static inline void +sentry__rwlock_write_unlock(sentry_rwlock_t *rwlock) +{ + if (sentry__block_for_signal_handler()) { + pthread_rwlock_unlock(rwlock); + } +} + +static inline void +sentry__rwlock_free(sentry_rwlock_t *rwlock) +{ + pthread_rwlock_destroy(rwlock); +} # define sentry__cond_init(CondVar) \ do { \ From aa0be8fd532bc474ab09027a1bb6f2e3fe874bfe Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 5 Aug 2026 18:05:48 +0200 Subject: [PATCH 05/28] refactor(scope): guard simple fields with rwlock Add a per-scope rwlock and use scoped read/write locking for simple by-value fields. Keep borrowed scope properties unchanged for now. --- src/sentry_core.c | 2 +- src/sentry_scope.c | 89 +++++++++++++++++++++++++++++++---------- src/sentry_scope.h | 10 ++++- tests/unit/test_scope.c | 2 +- 4 files changed, 78 insertions(+), 25 deletions(-) diff --git a/src/sentry_core.c b/src/sentry_core.c index 5ce4310d6d..5dd5822547 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -471,7 +471,7 @@ sentry__capture_envelope(sentry_transport_t *transport, sentry_uuid_t event_id = sentry__envelope_get_event_id(envelope); if (!sentry_uuid_is_nil(&event_id)) { SENTRY_WITH_SCOPE_MUT_NO_FLUSH (scope) { - scope->last_event_id = event_id; + sentry__scope_set_last_event_id(scope, event_id); } } diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 008b4a10ba..f956461959 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -36,6 +36,24 @@ SENTRY__MUTEX_INIT_DYN(g_lock) static sentry_mutex_t g_lock = SENTRY__MUTEX_INIT; #endif +#define SCOPE_READ_LOCK(Scope) \ + for (const sentry_scope_t *_locked_scope = (Scope); _locked_scope; \ + sentry__rwlock_read_unlock((sentry_rwlock_t *)&_locked_scope->rwlock), \ + _locked_scope = NULL) \ + for (bool _locked_once \ + = (sentry__rwlock_read_lock( \ + (sentry_rwlock_t *)&_locked_scope->rwlock), \ + true); \ + _locked_once; _locked_once = false) + +#define SCOPE_WRITE_LOCK(Scope) \ + for (sentry_scope_t *_locked_scope = (Scope); _locked_scope; \ + sentry__rwlock_write_unlock(&_locked_scope->rwlock), \ + _locked_scope = NULL) \ + for (bool _locked_once \ + = (sentry__rwlock_write_lock(&_locked_scope->rwlock), true); \ + _locked_once; _locked_once = false) + static sentry_value_t get_client_sdk(void) { @@ -124,6 +142,7 @@ get_scope(void) } memset(&g_scope, 0, sizeof(sentry_scope_t)); + sentry__rwlock_init(&g_scope.rwlock); init_scope(&g_scope); g_scope.user = sentry_value_new_object(); sentry_value_set_by_key(g_scope.contexts, "os", sentry__get_os_context()); @@ -170,6 +189,7 @@ sentry__scope_cleanup(void) if (g_scope_initialized) { g_scope_initialized = false; cleanup_scope(&g_scope); + sentry__rwlock_free(&g_scope.rwlock); } sentry__mutex_unlock(&g_lock); } @@ -324,6 +344,7 @@ sentry_scope_new(void) return NULL; } + sentry__rwlock_init(&scope->rwlock); init_scope(scope); return scope; } @@ -336,13 +357,28 @@ sentry_scope_free(sentry_scope_t *scope) } cleanup_scope(scope); + sentry__rwlock_free(&scope->rwlock); sentry_free(scope); } +bool +sentry__scope_is_one_shot(const sentry_scope_t *scope) +{ + bool one_shot = false; + SCOPE_READ_LOCK(scope) { one_shot = scope->one_shot; } + return one_shot; +} + +void +sentry__scope_set_one_shot(sentry_scope_t *scope, bool one_shot) +{ + SCOPE_WRITE_LOCK(scope) { scope->one_shot = one_shot; } +} + void sentry__scope_free_one_shot(sentry_scope_t *scope) { - if (scope && scope->one_shot) { + if (sentry__scope_is_one_shot(scope)) { sentry_scope_free(scope); } } @@ -352,7 +388,7 @@ sentry_local_scope_new(void) { sentry_scope_t *scope = sentry_scope_new(); if (scope) { - scope->one_shot = true; + sentry__scope_set_one_shot(scope, true); } return scope; } @@ -404,12 +440,12 @@ sentry_scope_clear(sentry_scope_t *scope) // Keep the propagation and dynamic sampling contexts across clears so // telemetry captured afterwards continues on the same trace. - bool trace_managed = scope->trace_managed; + bool trace_managed = sentry__scope_is_trace_managed(scope); sentry_value_t propagation_context = scope->propagation_context; sentry_value_t dynamic_sampling_context = scope->dynamic_sampling_context; sentry_value_incref(propagation_context); sentry_value_incref(dynamic_sampling_context); - bool one_shot = scope->one_shot; + bool one_shot = sentry__scope_is_one_shot(scope); cleanup_scope(scope); init_scope(scope); @@ -418,8 +454,8 @@ sentry_scope_clear(sentry_scope_t *scope) sentry_value_decref(scope->dynamic_sampling_context); scope->propagation_context = propagation_context; scope->dynamic_sampling_context = dynamic_sampling_context; - scope->trace_managed = trace_managed; - scope->one_shot = one_shot; + sentry__scope_set_trace_managed(scope, trace_managed); + sentry__scope_set_one_shot(scope, one_shot); scope->observers = observers; scope->num_observers = num_observers; scope->is_notifying = is_notifying; @@ -438,6 +474,8 @@ sentry_scope_clone(const sentry_scope_t *scope) return NULL; } + sentry__rwlock_init(&clone->rwlock); + clone->release = sentry__string_clone(scope->release); clone->environment = sentry__string_clone(scope->environment); clone->transaction = sentry__string_clone(scope->transaction); @@ -455,8 +493,8 @@ sentry_scope_clone(const sentry_scope_t *scope) if (sentry_value_is_frozen(scope->dynamic_sampling_context)) { sentry_value_freeze(clone->dynamic_sampling_context); } - clone->level = scope->level; - clone->last_event_id = scope->last_event_id; + clone->level = sentry__scope_get_level(scope); + clone->last_event_id = sentry_scope_get_last_event_id(scope); clone->client_sdk = sentry__value_clone(scope->client_sdk); clone->attachments = sentry__attachments_new(); sentry__attachments_extend(&clone->attachments, scope->attachments); @@ -465,7 +503,7 @@ sentry_scope_clone(const sentry_scope_t *scope) sentry__transaction_incref(clone->transaction_object); clone->span = scope->span; sentry__span_incref(clone->span); - clone->trace_managed = scope->trace_managed; + clone->trace_managed = sentry__scope_is_trace_managed(scope); return clone; } @@ -498,13 +536,15 @@ sentry__scope_regenerate_propagation_context(sentry_scope_t *scope) bool sentry__scope_is_trace_managed(const sentry_scope_t *scope) { - return scope->trace_managed; + bool managed = false; + SCOPE_READ_LOCK(scope) { managed = scope->trace_managed; } + return managed; } void sentry__scope_set_trace_managed(sentry_scope_t *scope, bool managed) { - scope->trace_managed = managed; + SCOPE_WRITE_LOCK(scope) { scope->trace_managed = managed; } } sentry_value_t @@ -687,12 +727,6 @@ sentry__scope_has_observers(const sentry_scope_t *scope) { return scope->num_observers > 0; } - -bool -sentry__scope_is_one_shot(const sentry_scope_t *scope) -{ - return scope->one_shot; -} #endif void @@ -730,7 +764,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, // is not transaction and has no level if (IS_NULL("type") && IS_NULL("level")) { - SET("level", sentry__value_new_level(scope->level)); + SET("level", sentry__value_new_level(sentry__scope_get_level(scope))); } if (sentry_value_get_type(scope->user) == SENTRY_VALUE_TYPE_OBJECT) { @@ -1186,13 +1220,15 @@ sentry_scope_remove_fingerprint(sentry_scope_t *scope) sentry_level_t sentry__scope_get_level(const sentry_scope_t *scope) { - return scope->level; + sentry_level_t level = SENTRY_LEVEL_ERROR; + SCOPE_READ_LOCK(scope) { level = scope->level; } + return level; } void sentry_scope_set_level(sentry_scope_t *scope, sentry_level_t level) { - scope->level = level; + SCOPE_WRITE_LOCK(scope) { scope->level = level; } SENTRY_SCOPE_NOTIFY(scope, set_level, level); } @@ -1584,7 +1620,16 @@ sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, sentry_uuid_t sentry_scope_get_last_event_id(const sentry_scope_t *scope) { - return scope ? scope->last_event_id : sentry_uuid_nil(); + sentry_uuid_t event_id = sentry_uuid_nil(); + SCOPE_READ_LOCK(scope) { event_id = scope->last_event_id; } + return event_id; +} + +void +sentry__scope_set_last_event_id( + sentry_scope_t *scope, sentry_uuid_t event_id) +{ + SCOPE_WRITE_LOCK(scope) { scope->last_event_id = event_id; } } void @@ -1594,7 +1639,7 @@ sentry__scope_capture_envelope(sentry_scope_t *scope, { sentry_uuid_t event_id = sentry__envelope_get_event_id(envelope); if (!sentry_uuid_is_nil(&event_id)) { - scope->last_event_id = event_id; + sentry__scope_set_last_event_id(scope, event_id); } sentry__submit_envelope(transport, envelope, options); diff --git a/src/sentry_scope.h b/src/sentry_scope.h index 4bc100ffc9..ef8e4aa95b 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -6,6 +6,7 @@ #include "sentry_attachment.h" #include "sentry_ringbuffer.h" #include "sentry_session.h" +#include "sentry_sync.h" #include "sentry_value.h" /** @@ -53,6 +54,8 @@ typedef struct sentry_scope_observer_s { * This represents the current scope. */ struct sentry_scope_s { + sentry_rwlock_t rwlock; + char *release; char *environment; char *transaction; @@ -126,6 +129,9 @@ void sentry__scope_cleanup(void); void sentry__scope_apply_options( sentry_scope_t *scope, sentry_options_t *options); +bool sentry__scope_is_one_shot(const sentry_scope_t *scope); +void sentry__scope_set_one_shot(sentry_scope_t *scope, bool one_shot); + /** * Frees the scope if it is a one-shot local scope. */ @@ -301,6 +307,9 @@ void sentry__scope_freeze_dsc(sentry_scope_t *scope, sentry_value_t incoming); void sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, sentry_value_t telemetry, sentry_value_t attributes); +void sentry__scope_set_last_event_id( + sentry_scope_t *scope, sentry_uuid_t event_id); + /** * Captures the `envelope` on `scope`, recording the last sent event ID. */ @@ -314,5 +323,4 @@ void sentry__scope_capture_envelope(sentry_scope_t *scope, #ifdef SENTRY_UNITTEST sentry_value_t sentry__scope_get_span_or_transaction(void); bool sentry__scope_has_observers(const sentry_scope_t *scope); -bool sentry__scope_is_one_shot(const sentry_scope_t *scope); #endif diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index 1723cfa0e9..f84e4f253d 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -2458,7 +2458,7 @@ SENTRY_TEST(scope_clone_independence) sentry_scope_t *clone = sentry_scope_clone(scope); // A clone is reusable, never one-shot. - TEST_CHECK(!clone->one_shot); + TEST_CHECK(!sentry__scope_is_one_shot(clone)); // The clone carries over the source's data. TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( From ba2a876197d5d3a8252f66de1f474e7ef8ec9f08 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 5 Aug 2026 18:39:32 +0200 Subject: [PATCH 06/28] refactor(scope): retain user and fingerprint values safely 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. --- src/sentry_scope.c | 108 ++++++++++++++++++++++++++++------------ src/sentry_scope.h | 4 +- src/sentry_session.c | 5 +- tests/unit/test_scope.c | 58 ++++++++++++++++----- 4 files changed, 127 insertions(+), 48 deletions(-) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index f956461959..7acf6b1530 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -479,8 +479,13 @@ sentry_scope_clone(const sentry_scope_t *scope) clone->release = sentry__string_clone(scope->release); clone->environment = sentry__string_clone(scope->environment); clone->transaction = sentry__string_clone(scope->transaction); - clone->fingerprint = sentry__value_clone(scope->fingerprint); - clone->user = sentry__value_clone(scope->user); + sentry_value_t fingerprint = sentry__scope_ref_fingerprint(scope); + clone->fingerprint = sentry__value_clone(fingerprint); + sentry_value_decref(fingerprint); + + sentry_value_t user = sentry__scope_ref_user(scope); + clone->user = sentry__value_clone(user); + sentry_value_decref(user); clone->tags = sentry__value_clone(scope->tags); clone->extra = sentry__value_clone(scope->extra); clone->attributes = sentry__value_clone(scope->attributes); @@ -767,24 +772,31 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, SET("level", sentry__value_new_level(sentry__scope_get_level(scope))); } - if (sentry_value_get_type(scope->user) == SENTRY_VALUE_TYPE_OBJECT) { + sentry_value_t user = sentry__scope_ref_user(scope); + if (sentry_value_get_type(user) == SENTRY_VALUE_TYPE_OBJECT) { if (options->run && options->run->installation_id) { // ensure event has a user object if (IS_NULL("user")) { - SET("user", sentry__value_clone(scope->user)); + SET("user", sentry__value_clone(user)); } // patch missing user ID with installation ID - sentry_value_t user = sentry_value_get_by_key(event, "user"); - if (sentry_value_get_type(user) == SENTRY_VALUE_TYPE_OBJECT - && sentry_value_is_null(sentry_value_get_by_key(user, "id"))) { - sentry_value_set_by_key(user, "id", + sentry_value_t event_user = sentry_value_get_by_key(event, "user"); + if (sentry_value_get_type(event_user) == SENTRY_VALUE_TYPE_OBJECT + && sentry_value_is_null( + sentry_value_get_by_key(event_user, "id"))) { + sentry_value_set_by_key(event_user, "id", sentry_value_new_string(options->run->installation_id)); } - } else if (sentry_value_get_length(scope->user) > 0) { - PLACE_CLONED_VALUE("user", scope->user); + } else if (sentry_value_get_length(user) > 0) { + PLACE_CLONED_VALUE("user", user); } } - PLACE_CLONED_VALUE("fingerprint", scope->fingerprint); + sentry_value_decref(user); + + sentry_value_t fingerprint = sentry__scope_ref_fingerprint(scope); + PLACE_CLONED_VALUE("fingerprint", fingerprint); + sentry_value_decref(fingerprint); + PLACE_STRING("transaction", scope->transaction); PLACE_VALUE("sdk", scope->client_sdk); @@ -896,17 +908,27 @@ sentry__scope_get_breadcrumbs(const sentry_scope_t *scope) } sentry_value_t -sentry__scope_get_user(const sentry_scope_t *scope) +sentry__scope_ref_user(const sentry_scope_t *scope) { - return scope->user; + sentry_value_t user = sentry_value_new_null(); + SCOPE_READ_LOCK(scope) { user = sentry_value_incref(scope->user); } + return user; } void sentry_scope_set_user(sentry_scope_t *scope, sentry_value_t user) { - sentry_value_decref(scope->user); - scope->user = user; - SENTRY_SCOPE_NOTIFY(scope, set_user, user); + sentry_value_t old_user = sentry_value_new_null(); + sentry_value_t notify_user = sentry_value_new_null(); + SCOPE_WRITE_LOCK(scope) + { + old_user = scope->user; + scope->user = user; + notify_user = sentry_value_incref(user); + } + sentry_value_decref(old_user); + SENTRY_SCOPE_NOTIFY(scope, set_user, notify_user); + sentry_value_decref(notify_user); } sentry_value_t @@ -1134,9 +1156,14 @@ sentry__scope_remove_context_n( } sentry_value_t -sentry__scope_get_fingerprint(const sentry_scope_t *scope) +sentry__scope_ref_fingerprint(const sentry_scope_t *scope) { - return scope->fingerprint; + sentry_value_t fingerprint = sentry_value_new_null(); + SCOPE_READ_LOCK(scope) + { + fingerprint = sentry_value_incref(scope->fingerprint); + } + return fingerprint; } void @@ -1149,9 +1176,7 @@ sentry__scope_set_fingerprint_va( fingerprint_value, sentry_value_new_string(fingerprint)); } - sentry_value_decref(scope->fingerprint); - scope->fingerprint = fingerprint_value; - SENTRY_SCOPE_NOTIFY(scope, set_fingerprint, fingerprint_value); + sentry_scope_set_fingerprints(scope, fingerprint_value); } void @@ -1204,17 +1229,34 @@ sentry_scope_set_fingerprints( return; } - sentry_value_decref(scope->fingerprint); - scope->fingerprint = fingerprints; - SENTRY_SCOPE_NOTIFY(scope, set_fingerprint, fingerprints); + sentry_value_t old_fingerprint = sentry_value_new_null(); + sentry_value_t notify_fingerprint = sentry_value_new_null(); + SCOPE_WRITE_LOCK(scope) + { + old_fingerprint = scope->fingerprint; + scope->fingerprint = fingerprints; + notify_fingerprint = sentry_value_incref(fingerprints); + } + sentry_value_decref(old_fingerprint); + SENTRY_SCOPE_NOTIFY(scope, set_fingerprint, notify_fingerprint); + sentry_value_decref(notify_fingerprint); } void sentry_scope_remove_fingerprint(sentry_scope_t *scope) { - sentry_value_decref(scope->fingerprint); - scope->fingerprint = sentry_value_new_null(); - SENTRY_SCOPE_NOTIFY(scope, set_fingerprint, scope->fingerprint); + sentry_value_t fingerprint = sentry_value_new_null(); + sentry_value_t old_fingerprint = sentry_value_new_null(); + sentry_value_t notify_fingerprint = sentry_value_new_null(); + SCOPE_WRITE_LOCK(scope) + { + old_fingerprint = scope->fingerprint; + scope->fingerprint = fingerprint; + notify_fingerprint = sentry_value_incref(fingerprint); + } + sentry_value_decref(old_fingerprint); + SENTRY_SCOPE_NOTIFY(scope, set_fingerprint, notify_fingerprint); + sentry_value_decref(notify_fingerprint); } sentry_level_t @@ -1565,8 +1607,9 @@ sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, sentry_value_set_by_key(telemetry, "trace_id", trace_id); } - if (!sentry_value_is_null(scope->user)) { - sentry_value_t user_id = sentry_value_get_by_key(scope->user, "id"); + sentry_value_t user = sentry__scope_ref_user(scope); + if (!sentry_value_is_null(user)) { + sentry_value_t user_id = sentry_value_get_by_key(user, "id"); if (!sentry_value_is_null(user_id)) { sentry_value_incref(user_id); sentry__value_add_attribute( @@ -1574,21 +1617,22 @@ sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, } sentry_value_t user_username - = sentry_value_get_by_key(scope->user, "username"); + = sentry_value_get_by_key(user, "username"); if (!sentry_value_is_null(user_username)) { sentry_value_incref(user_username); sentry__value_add_attribute( attributes, user_username, "string", "user.name"); } - sentry_value_t user_email - = sentry_value_get_by_key(scope->user, "email"); + sentry_value_t user_email = sentry_value_get_by_key(user, "email"); if (!sentry_value_is_null(user_email)) { sentry_value_incref(user_email); sentry__value_add_attribute( attributes, user_email, "string", "user.email"); } } + sentry_value_decref(user); + sentry_value_t os_context = sentry_value_get_by_key(scope->contexts, "os"); if (!sentry_value_is_null(os_context)) { sentry_value_t os_name = sentry_value_get_by_key(os_context, "name"); diff --git a/src/sentry_scope.h b/src/sentry_scope.h index ef8e4aa95b..79dce2b6c7 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -166,12 +166,12 @@ const char *sentry__scope_get_transaction(const sentry_scope_t *scope); void sentry__scope_set_transaction_n( sentry_scope_t *scope, const char *transaction, size_t transaction_len); -sentry_value_t sentry__scope_get_fingerprint(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_fingerprint(const sentry_scope_t *scope); void sentry__scope_set_fingerprint_va( sentry_scope_t *scope, const char *fingerprint, va_list va); void sentry__scope_set_fingerprint_nva(sentry_scope_t *scope, const char *fingerprint, size_t fingerprint_len, va_list va); -sentry_value_t sentry__scope_get_user(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_user(const sentry_scope_t *scope); sentry_level_t sentry__scope_get_level(const sentry_scope_t *scope); sentry_value_t sentry__scope_add_attachment( diff --git a/src/sentry_session.c b/src/sentry_session.c index 49b90415cc..d340cc7a3f 100644 --- a/src/sentry_session.c +++ b/src/sentry_session.c @@ -220,9 +220,10 @@ sentry_start_session(void) if (options) { options->session = sentry__session_new(scope); if (options->session) { - sentry__session_sync_user(options->session, - sentry__scope_get_user(scope), + sentry_value_t user = sentry__scope_ref_user(scope); + sentry__session_sync_user(options->session, user, options->run ? options->run->installation_id : NULL); + sentry_value_decref(user); sentry__run_write_session(options->run, options->session); } } diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index f84e4f253d..657f7b11fd 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -444,6 +444,16 @@ SENTRY_TEST(scope_fingerprint) TEST_CHECK_JSON_VALUE(sentry_value_get_by_key(event, "fingerprint"), "[\"event1\",\"event2\"]"); + sentry_value_t local_fingerprint + = sentry__scope_ref_fingerprint(local_scope); + sentry_scope_remove_fingerprint(local_scope); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(local_fingerprint), 2); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string( + sentry_value_get_by_index(local_fingerprint, 0)), + "local1"); + sentry_value_decref(local_fingerprint); + sentry_scope_free(local_scope); sentry_value_decref(event); } @@ -661,6 +671,13 @@ SENTRY_TEST(scope_user) TEST_CHECK_JSON_VALUE(sentry_value_get_by_key(event, "user"), "{\"id\":\"3\",\"username\":\"event\"}"); + sentry_value_t local_user = sentry__scope_ref_user(local_scope); + sentry_scope_set_user(local_scope, sentry_value_new_null()); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(local_user, "id")), + "2"); + sentry_value_decref(local_user); + sentry_scope_free(local_scope); sentry_value_decref(event); } @@ -1111,14 +1128,17 @@ SENTRY_TEST(scope_clone) TEST_CHECK(sentry_value_is_null( sentry_value_get_by_key(scope_data, "injected"))); + sentry_value_t scope_user = sentry__scope_ref_user(scope); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - sentry__scope_get_user(scope), "username")), + scope_user, "username")), "original"); - TEST_CHECK(sentry_value_is_null(sentry_value_get_by_key( - sentry__scope_get_user(scope), "injected"))); + TEST_CHECK(sentry_value_is_null( + sentry_value_get_by_key(scope_user, "injected"))); + sentry_value_decref(scope_user); - TEST_CHECK_INT_EQUAL( - sentry_value_get_length(sentry__scope_get_fingerprint(scope)), 2); + sentry_value_t scope_fingerprint = sentry__scope_ref_fingerprint(scope); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope_fingerprint), 2); + sentry_value_decref(scope_fingerprint); } sentry_close(); @@ -1505,6 +1525,10 @@ static void observe_set_user(void *data, sentry_value_t user) { test_observer_data_t *d = (test_observer_data_t *)data; + if (!sentry_value_is_null(d->user)) { + sentry_value_decref(d->user); + } + sentry_value_incref(user); d->user = user; d->was_called = true; } @@ -2518,14 +2542,17 @@ SENTRY_TEST(scope_clone_preserves_data) TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( sentry__scope_get_contexts(clone), "device")), "Xbox"); - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - sentry__scope_get_user(clone), "username")), + sentry_value_t clone_user = sentry__scope_ref_user(clone); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(clone_user, "username")), "alice"); + sentry_value_decref(clone_user); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( sentry__scope_get_extra(clone), "extra_key")), "extra_value"); - TEST_CHECK_INT_EQUAL( - sentry_value_get_length(sentry__scope_get_fingerprint(clone)), 2); + sentry_value_t clone_fingerprint = sentry__scope_ref_fingerprint(clone); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(clone_fingerprint), 2); + sentry_value_decref(clone_fingerprint); TEST_CHECK(sentry__scope_get_level(clone) == SENTRY_LEVEL_WARNING); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key( @@ -2613,7 +2640,9 @@ SENTRY_TEST(scope_clear) TEST_CHECK(sentry_value_get_length(sentry__scope_get_tags(scope)) == 1); TEST_CHECK( sentry_value_get_length(sentry__scope_get_attributes(scope)) == 1); - TEST_CHECK(!sentry_value_is_null(sentry__scope_get_user(scope))); + sentry_value_t scope_user = sentry__scope_ref_user(scope); + TEST_CHECK(!sentry_value_is_null(scope_user)); + sentry_value_decref(scope_user); sentry_scope_clear(scope); @@ -2623,8 +2652,13 @@ SENTRY_TEST(scope_clear) TEST_CHECK(sentry_value_get_length(sentry__scope_get_contexts(scope)) == 0); TEST_CHECK( sentry_value_get_length(sentry__scope_get_attributes(scope)) == 0); - TEST_CHECK(sentry_value_is_null(sentry__scope_get_user(scope))); - TEST_CHECK(sentry_value_is_null(sentry__scope_get_fingerprint(scope))); + scope_user = sentry__scope_ref_user(scope); + TEST_CHECK(sentry_value_is_null(scope_user)); + sentry_value_decref(scope_user); + + sentry_value_t scope_fingerprint = sentry__scope_ref_fingerprint(scope); + TEST_CHECK(sentry_value_is_null(scope_fingerprint)); + sentry_value_decref(scope_fingerprint); TEST_CHECK_INT_EQUAL(sentry__scope_get_level(scope), SENTRY_LEVEL_ERROR); sentry_value_t crumbs = sentry__ringbuffer_to_list(sentry__scope_get_breadcrumbs(scope)); From 6ff86c0f8f76ed712ba850fbaabe38e9abf8f780 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 5 Aug 2026 22:58:10 +0200 Subject: [PATCH 07/28] refactor(scope): store string fields as values 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. --- src/sentry_scope.c | 184 ++++++++++++++++++++++++---------------- src/sentry_scope.h | 18 ++-- src/sentry_session.c | 14 ++- src/sentry_value.c | 8 ++ src/sentry_value.h | 6 ++ tests/unit/test_scope.c | 44 ++++++++-- tests/unit/test_value.c | 20 +++++ tests/unit/tests.inc | 1 + 8 files changed, 205 insertions(+), 90 deletions(-) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 7acf6b1530..611209b46d 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -54,6 +54,13 @@ static sentry_mutex_t g_lock = SENTRY__MUTEX_INIT; = (sentry__rwlock_write_lock(&_locked_scope->rwlock), true); \ _locked_once; _locked_once = false) +#define SENTRY_SCOPE_NOTIFY_OWNED(Scope, Callback, Value) \ + do { \ + sentry_value_t _notify_value = (Value); \ + SENTRY_SCOPE_NOTIFY(Scope, Callback, _notify_value); \ + sentry_value_decref(_notify_value); \ + } while (0) + static sentry_value_t get_client_sdk(void) { @@ -108,9 +115,9 @@ generate_propagation_context(sentry_value_t propagation_context) static void init_scope(sentry_scope_t *scope) { - scope->release = NULL; - scope->environment = NULL; - scope->transaction = NULL; + scope->release = sentry_value_new_null(); + scope->environment = sentry_value_new_null(); + scope->transaction = sentry_value_new_null(); scope->fingerprint = sentry_value_new_null(); scope->user = sentry_value_new_null(); scope->tags = sentry_value_new_object(); @@ -156,9 +163,9 @@ get_scope(void) static void cleanup_scope(sentry_scope_t *scope) { - sentry_free(scope->release); - sentry_free(scope->environment); - sentry_free(scope->transaction); + sentry_value_decref(scope->release); + sentry_value_decref(scope->environment); + sentry_value_decref(scope->transaction); sentry_value_decref(scope->fingerprint); sentry_value_decref(scope->user); sentry_value_decref(scope->tags); @@ -476,9 +483,18 @@ sentry_scope_clone(const sentry_scope_t *scope) sentry__rwlock_init(&clone->rwlock); - clone->release = sentry__string_clone(scope->release); - clone->environment = sentry__string_clone(scope->environment); - clone->transaction = sentry__string_clone(scope->transaction); + sentry_value_t release = sentry__scope_ref_release(scope); + clone->release = sentry__value_clone(release); + sentry_value_decref(release); + + sentry_value_t environment = sentry__scope_ref_environment(scope); + clone->environment = sentry__value_clone(environment); + sentry_value_decref(environment); + + sentry_value_t transaction = sentry__scope_ref_transaction(scope); + clone->transaction = sentry__value_clone(transaction); + sentry_value_decref(transaction); + sentry_value_t fingerprint = sentry__scope_ref_fingerprint(scope); clone->fingerprint = sentry__value_clone(fingerprint); sentry_value_decref(fingerprint); @@ -593,10 +609,9 @@ sentry__scope_update_dsc(sentry_scope_t *scope, const sentry_options_t *options) "sample_rand"); sentry_value_set_by_key(dsc, "sample_rand", sample_rand); sentry_value_incref(sample_rand); + sentry_value_set_by_key(dsc, "release", sentry__scope_ref_release(scope)); sentry_value_set_by_key( - dsc, "release", sentry_value_new_string(scope->release)); - sentry_value_set_by_key( - dsc, "environment", sentry_value_new_string(scope->environment)); + dsc, "environment", sentry__scope_ref_environment(scope)); scope->dynamic_sampling_context = dsc; } @@ -747,6 +762,12 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, 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) { \ + SET(Key, sentry_value_incref(Source)); \ + } \ + } while (0) #define PLACE_VALUE(Key, Source) \ do { \ if (IS_NULL(Key) && !sentry_value_is_null(Source)) { \ @@ -763,9 +784,15 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, PLACE_STRING("platform", "native"); - PLACE_STRING("release", scope->release); + sentry_value_t release = sentry__scope_ref_release(scope); + PLACE_STRING_VALUE("release", release); + sentry_value_decref(release); + PLACE_STRING("dist", options->dist); - PLACE_STRING("environment", scope->environment); + + sentry_value_t environment = sentry__scope_ref_environment(scope); + PLACE_STRING_VALUE("environment", environment); + sentry_value_decref(environment); // is not transaction and has no level if (IS_NULL("type") && IS_NULL("level")) { @@ -797,7 +824,10 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, PLACE_CLONED_VALUE("fingerprint", fingerprint); sentry_value_decref(fingerprint); - PLACE_STRING("transaction", scope->transaction); + sentry_value_t transaction = sentry__scope_ref_transaction(scope); + PLACE_STRING_VALUE("transaction", transaction); + sentry_value_decref(transaction); + PLACE_VALUE("sdk", scope->client_sdk); sentry_value_t event_tags = sentry_value_get_by_key(event, "tags"); @@ -888,6 +918,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, #undef PLACE_CLONED_VALUE #undef PLACE_VALUE +#undef PLACE_STRING_VALUE #undef PLACE_STRING #undef SET #undef IS_NULL @@ -918,17 +949,11 @@ sentry__scope_ref_user(const sentry_scope_t *scope) void sentry_scope_set_user(sentry_scope_t *scope, sentry_value_t user) { - sentry_value_t old_user = sentry_value_new_null(); - sentry_value_t notify_user = sentry_value_new_null(); SCOPE_WRITE_LOCK(scope) { - old_user = scope->user; - scope->user = user; - notify_user = sentry_value_incref(user); + sentry__value_replace(&scope->user, sentry_value_incref(user)); } - sentry_value_decref(old_user); - SENTRY_SCOPE_NOTIFY(scope, set_user, notify_user); - sentry_value_decref(notify_user); + SENTRY_SCOPE_NOTIFY_OWNED(scope, set_user, user); } sentry_value_t @@ -1229,34 +1254,24 @@ sentry_scope_set_fingerprints( return; } - sentry_value_t old_fingerprint = sentry_value_new_null(); - sentry_value_t notify_fingerprint = sentry_value_new_null(); SCOPE_WRITE_LOCK(scope) { - old_fingerprint = scope->fingerprint; - scope->fingerprint = fingerprints; - notify_fingerprint = sentry_value_incref(fingerprints); + sentry__value_replace( + &scope->fingerprint, sentry_value_incref(fingerprints)); } - sentry_value_decref(old_fingerprint); - SENTRY_SCOPE_NOTIFY(scope, set_fingerprint, notify_fingerprint); - sentry_value_decref(notify_fingerprint); + SENTRY_SCOPE_NOTIFY_OWNED(scope, set_fingerprint, fingerprints); } void sentry_scope_remove_fingerprint(sentry_scope_t *scope) { sentry_value_t fingerprint = sentry_value_new_null(); - sentry_value_t old_fingerprint = sentry_value_new_null(); - sentry_value_t notify_fingerprint = sentry_value_new_null(); SCOPE_WRITE_LOCK(scope) { - old_fingerprint = scope->fingerprint; - scope->fingerprint = fingerprint; - notify_fingerprint = sentry_value_incref(fingerprint); + sentry__value_replace( + &scope->fingerprint, sentry_value_incref(fingerprint)); } - sentry_value_decref(old_fingerprint); - SENTRY_SCOPE_NOTIFY(scope, set_fingerprint, notify_fingerprint); - sentry_value_decref(notify_fingerprint); + SENTRY_SCOPE_NOTIFY_OWNED(scope, set_fingerprint, fingerprint); } sentry_level_t @@ -1445,58 +1460,80 @@ sentry__scope_restore_span(sentry_scope_t *scope, sentry_span_t *span) return true; } -const char * -sentry__scope_get_release(const sentry_scope_t *scope) +sentry_value_t +sentry__scope_ref_release(const sentry_scope_t *scope) { - return scope->release; + sentry_value_t release = sentry_value_new_null(); + SCOPE_READ_LOCK(scope) { release = sentry_value_incref(scope->release); } + return release; } void sentry__scope_set_release_n( sentry_scope_t *scope, const char *release, size_t release_len) { - sentry_free(scope->release); - scope->release = sentry__string_clone_n(release, release_len); - sentry_value_set_by_key(scope->dynamic_sampling_context, "release", - sentry_value_new_string(scope->release)); - SENTRY_SCOPE_NOTIFY(scope, set_release, scope->release); + sentry_value_t value = sentry_value_new_string_n(release, release_len); + SCOPE_WRITE_LOCK(scope) + { + sentry__value_replace(&scope->release, sentry_value_incref(value)); + sentry_value_set_by_key(scope->dynamic_sampling_context, "release", + sentry_value_incref(value)); + } + SENTRY_SCOPE_NOTIFY_OWNED(scope, set_release, value); } -const char * -sentry__scope_get_environment(const sentry_scope_t *scope) +sentry_value_t +sentry__scope_ref_environment(const sentry_scope_t *scope) { - return scope->environment; + sentry_value_t environment = sentry_value_new_null(); + SCOPE_READ_LOCK(scope) + { + environment = sentry_value_incref(scope->environment); + } + return environment; } void sentry__scope_set_environment_n( sentry_scope_t *scope, const char *environment, size_t environment_len) { - sentry_free(scope->environment); - scope->environment = sentry__string_clone_n(environment, environment_len); - sentry_value_set_by_key(scope->dynamic_sampling_context, "environment", - sentry_value_new_string(scope->environment)); - SENTRY_SCOPE_NOTIFY(scope, set_environment, scope->environment); + sentry_value_t value + = sentry_value_new_string_n(environment, environment_len); + SCOPE_WRITE_LOCK(scope) + { + sentry__value_replace(&scope->environment, sentry_value_incref(value)); + sentry_value_set_by_key(scope->dynamic_sampling_context, "environment", + sentry_value_incref(value)); + } + SENTRY_SCOPE_NOTIFY_OWNED(scope, set_environment, value); } -const char * -sentry__scope_get_transaction(const sentry_scope_t *scope) +sentry_value_t +sentry__scope_ref_transaction(const sentry_scope_t *scope) { - return scope->transaction; + sentry_value_t transaction = sentry_value_new_null(); + SCOPE_READ_LOCK(scope) + { + transaction = sentry_value_incref(scope->transaction); + } + return transaction; } void sentry__scope_set_transaction_n( sentry_scope_t *scope, const char *transaction, size_t transaction_len) { - sentry_free(scope->transaction); - scope->transaction = sentry__string_clone_n(transaction, transaction_len); - - if (scope->transaction_object) { - sentry_transaction_set_name_n( - scope->transaction_object, transaction, transaction_len); + sentry_value_t value + = sentry_value_new_string_n(transaction, transaction_len); + SCOPE_WRITE_LOCK(scope) + { + sentry__value_replace(&scope->transaction, sentry_value_incref(value)); + if (scope->transaction_object) { + sentry_transaction_set_name_n( + scope->transaction_object, transaction, transaction_len); + } } - SENTRY_SCOPE_NOTIFY(scope, set_transaction, scope->transaction); + SENTRY_SCOPE_NOTIFY_OWNED(scope, set_transaction, value); } sentry_uuid_t @@ -1649,16 +1686,19 @@ sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, attributes, os_version, "string", "os.version"); } } - if (scope->environment) { + sentry_value_t environment = sentry__scope_ref_environment(scope); + if (!sentry_value_is_null(environment)) { sentry__value_add_attribute(attributes, - sentry_value_new_string(scope->environment), "string", - "sentry.environment"); + sentry_value_incref(environment), "string", "sentry.environment"); } - if (scope->release) { - sentry__value_add_attribute(attributes, - sentry_value_new_string(scope->release), "string", - "sentry.release"); + sentry_value_decref(environment); + + sentry_value_t release = sentry__scope_ref_release(scope); + if (!sentry_value_is_null(release)) { + sentry__value_add_attribute(attributes, sentry_value_incref(release), + "string", "sentry.release"); } + sentry_value_decref(release); } sentry_uuid_t diff --git a/src/sentry_scope.h b/src/sentry_scope.h index 79dce2b6c7..ba56cad57a 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -28,9 +28,9 @@ typedef struct sentry_scope_observer_s { void (*clear)(void *data); - void (*set_release)(void *data, const char *release); - void (*set_environment)(void *data, const char *environment); - void (*set_transaction)(void *data, const char *transaction); + void (*set_release)(void *data, sentry_value_t release); + void (*set_environment)(void *data, sentry_value_t environment); + void (*set_transaction)(void *data, sentry_value_t transaction); void (*set_fingerprint)(void *data, sentry_value_t fingerprint); void (*set_level)(void *data, sentry_level_t level); void (*set_user)(void *data, sentry_value_t user); @@ -56,9 +56,9 @@ typedef struct sentry_scope_observer_s { struct sentry_scope_s { sentry_rwlock_t rwlock; - char *release; - char *environment; - char *transaction; + sentry_value_t release; + sentry_value_t environment; + sentry_value_t transaction; sentry_value_t fingerprint; sentry_value_t user; sentry_value_t tags; @@ -154,15 +154,15 @@ void sentry__scope_apply_to_event(const sentry_scope_t *scope, const sentry_options_t *options, sentry_value_t event, sentry_scope_mode_t mode); -const char *sentry__scope_get_release(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_release(const sentry_scope_t *scope); void sentry__scope_set_release_n( sentry_scope_t *scope, const char *release, size_t release_len); -const char *sentry__scope_get_environment(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_environment(const sentry_scope_t *scope); void sentry__scope_set_environment_n( sentry_scope_t *scope, const char *environment, size_t environment_len); -const char *sentry__scope_get_transaction(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_transaction(const sentry_scope_t *scope); void sentry__scope_set_transaction_n( sentry_scope_t *scope, const char *transaction, size_t transaction_len); diff --git a/src/sentry_session.c b/src/sentry_session.c index d340cc7a3f..267365f949 100644 --- a/src/sentry_session.c +++ b/src/sentry_session.c @@ -50,9 +50,17 @@ status_from_string(const char *status) sentry_session_t * sentry__session_new(const sentry_scope_t *scope) { - char *release = sentry__string_clone(sentry__scope_get_release(scope)); - char *environment - = sentry__string_clone(sentry__scope_get_environment(scope)); + sentry_value_t release_value = sentry__scope_ref_release(scope); + char *release = sentry_value_is_null(release_value) + ? NULL + : sentry__string_clone(sentry_value_as_string(release_value)); + sentry_value_decref(release_value); + + sentry_value_t environment_value = sentry__scope_ref_environment(scope); + char *environment = sentry_value_is_null(environment_value) + ? NULL + : sentry__string_clone(sentry_value_as_string(environment_value)); + sentry_value_decref(environment_value); if (!release) { sentry_free(environment); diff --git a/src/sentry_value.c b/src/sentry_value.c index 52332357aa..2cb4e84368 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -447,6 +447,14 @@ sentry_value_decref(sentry_value_t value) return thing ? 1 : 0; } +void +sentry__value_replace(sentry_value_t *target, sentry_value_t value) +{ + sentry_value_t old_value = *target; + *target = value; + sentry_value_decref(old_value); +} + size_t sentry_value_refcount(sentry_value_t value) { diff --git a/src/sentry_value.h b/src/sentry_value.h index e6428158d5..bd19c29910 100644 --- a/src/sentry_value.h +++ b/src/sentry_value.h @@ -87,6 +87,12 @@ void sentry__value_foreach_key_value(sentry_value_t value, int sentry__value_set_by_key_owned( sentry_value_t value, char *key, size_t key_len, sentry_value_t v); +/** + * Replaces `*target` with `value`. + * Takes ownership of `value` and releases the previous `*target`. + */ +void sentry__value_replace(sentry_value_t *target, sentry_value_t value); + /** * Removes a value by key and returns the owned object key on success. * The caller must free the returned key. diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index 657f7b11fd..617e007559 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -1423,10 +1423,11 @@ deferred_flush_scope( } static void -observe_set_release(void *data, const char *release) +observe_set_release(void *data, sentry_value_t release) { test_observer_data_t *d = (test_observer_data_t *)data; - d->release = sentry_value_new_string(release); + sentry_value_decref(d->release); + d->release = sentry_value_incref(release); d->was_called = true; } @@ -1445,18 +1446,20 @@ observe_clear_wrapped(void *data) } static void -observe_set_environment(void *data, const char *environment) +observe_set_environment(void *data, sentry_value_t environment) { test_observer_data_t *d = (test_observer_data_t *)data; - d->environment = sentry_value_new_string(environment); + sentry_value_decref(d->environment); + d->environment = sentry_value_incref(environment); d->was_called = true; } static void -observe_set_transaction(void *data, const char *transaction) +observe_set_transaction(void *data, sentry_value_t transaction) { test_observer_data_t *d = (test_observer_data_t *)data; - d->transaction = sentry_value_new_string(transaction); + sentry_value_decref(d->transaction); + d->transaction = sentry_value_incref(transaction); d->was_called = true; } @@ -1920,6 +1923,15 @@ SENTRY_TEST(scope_observer_release) TEST_CHECK(d.was_called); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(d.release), "my-release"); + SENTRY_WITH_SCOPE_MUT (scope) { + sentry_value_t release = sentry__scope_ref_release(scope); + sentry__scope_set_release_n( + scope, "next-release", sizeof("next-release") - 1); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(release), "my-release"); + sentry_value_decref(release); + } + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(d.release), "next-release"); + sentry_value_decref(d.release); sentry_close(); } @@ -1942,6 +1954,15 @@ SENTRY_TEST(scope_observer_environment) TEST_CHECK(d.was_called); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(d.environment), "my-env"); + SENTRY_WITH_SCOPE_MUT (scope) { + sentry_value_t environment = sentry__scope_ref_environment(scope); + sentry__scope_set_environment_n( + scope, "next-env", sizeof("next-env") - 1); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(environment), "my-env"); + sentry_value_decref(environment); + } + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(d.environment), "next-env"); + sentry_value_decref(d.environment); sentry_close(); } @@ -1965,6 +1986,17 @@ SENTRY_TEST(scope_observer_transaction) TEST_CHECK_STRING_EQUAL( sentry_value_as_string(d.transaction), "my-transaction"); + SENTRY_WITH_SCOPE_MUT (scope) { + sentry_value_t transaction = sentry__scope_ref_transaction(scope); + sentry__scope_set_transaction_n( + scope, "next-transaction", sizeof("next-transaction") - 1); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(transaction), "my-transaction"); + sentry_value_decref(transaction); + } + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(d.transaction), "next-transaction"); + sentry_value_decref(d.transaction); sentry_close(); } diff --git a/tests/unit/test_value.c b/tests/unit/test_value.c index f461e2f9df..120d19f51b 100644 --- a/tests/unit/test_value.c +++ b/tests/unit/test_value.c @@ -2207,3 +2207,23 @@ SENTRY_TEST(value_refcount) TEST_CHECK_INT_EQUAL(1, sentry_value_refcount(obj)); TEST_CHECK(!sentry_value_decref(obj)); } + +SENTRY_TEST(value_replace) +{ + sentry_value_t target = sentry_value_new_string("old"); + sentry_value_t old = sentry_value_incref(target); + sentry_value_t replacement = sentry_value_new_string("new"); + + sentry__value_replace(&target, replacement); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(target), "new"); + TEST_CHECK_INT_EQUAL(1, sentry_value_refcount(target)); + TEST_CHECK_INT_EQUAL(1, sentry_value_refcount(old)); + + sentry_value_decref(old); + sentry_value_decref(target); + + target = sentry_value_new_object(); + sentry__value_replace(&target, sentry_value_incref(target)); + TEST_CHECK_INT_EQUAL(1, sentry_value_refcount(target)); + sentry_value_decref(target); +} diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 391063f970..2363e711c0 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -505,6 +505,7 @@ XX(value_object_merge_nested) XX(value_object_merge_shallow) XX(value_object_merge_shallow_nested) XX(value_refcount) +XX(value_replace) XX(value_remove_by_null_key) XX(value_set_by_null_key) XX(value_set_stacktrace) From 9a2b29ae7f09d6fdf233e03f27c0a57bbbcd8646 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 6 Aug 2026 10:52:43 +0200 Subject: [PATCH 08/28] refactor(scope): move locked payload into scope data --- src/backends/sentry_backend_native.c | 2 +- src/integrations/sentry_integration_wer.c | 21 +- src/sentry_core.c | 22 +- src/sentry_scope.c | 785 ++++++++++++++-------- src/sentry_scope.h | 38 +- src/sentry_tracing.h | 4 +- tests/unit/test_attachments.c | 3 +- tests/unit/test_basic.c | 3 +- tests/unit/test_scope.c | 12 +- 9 files changed, 548 insertions(+), 342 deletions(-) diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 909cb72c8e..19feac815f 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -763,7 +763,7 @@ native_backend_write_attachments(const sentry_path_t *event_path) return; } SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = scope->attachments; + sentry_value_t attachments = sentry__scope_get_attachments(scope); if (sentry_value_get_length(attachments) == 0) { continue; } diff --git a/src/integrations/sentry_integration_wer.c b/src/integrations/sentry_integration_wer.c index bf1c5eaf93..e2895bc31c 100644 --- a/src/integrations/sentry_integration_wer.c +++ b/src/integrations/sentry_integration_wer.c @@ -207,12 +207,14 @@ wer_clear(void *data) return; } - sentry__value_foreach_key_value(scope->tags, wer_cleanup_tag, wer_data); + sentry__value_foreach_key_value( + sentry__scope_get_tags(scope), wer_cleanup_tag, wer_data); - size_t len = sentry_value_get_length(scope->attachments); + sentry_value_t attachments = sentry__scope_get_attachments(scope); + size_t len = sentry_value_get_length(attachments); for (size_t i = 0; i < len; i++) { wer_remove_attachment( - wer_data, sentry_value_get_by_index(scope->attachments, i)); + wer_data, sentry_value_get_by_index(attachments, i)); } } @@ -238,10 +240,11 @@ register_wer( if (sentry__scope_add_observer(scope, observer)) { wer_data->scope = scope; wer_data->observer = observer; - size_t len = sentry_value_get_length(scope->attachments); + sentry_value_t attachments = sentry__scope_get_attachments(scope); + size_t len = sentry_value_get_length(attachments); for (size_t i = 0; i < len; i++) { wer_add_attachment( - wer_data, sentry_value_get_by_index(scope->attachments, i)); + wer_data, sentry_value_get_by_index(attachments, i)); } } } @@ -256,12 +259,14 @@ unregister_wer( return; } - sentry__value_foreach_key_value(scope->tags, wer_cleanup_tag, wer_data); + sentry__value_foreach_key_value( + sentry__scope_get_tags(scope), wer_cleanup_tag, wer_data); - size_t len = sentry_value_get_length(scope->attachments); + sentry_value_t attachments = sentry__scope_get_attachments(scope); + size_t len = sentry_value_get_length(attachments); for (size_t i = 0; i < len; i++) { wer_remove_attachment( - wer_data, sentry_value_get_by_index(scope->attachments, i)); + wer_data, sentry_value_get_by_index(attachments, i)); } sentry__scope_remove_observer(scope, wer_data->observer); diff --git a/src/sentry_core.c b/src/sentry_core.c index 5dd5822547..6d948a77e8 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -706,9 +706,11 @@ sentry__prepare_event(const sentry_options_t *options, sentry_value_t event, } SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = scope->attachments; + sentry_value_t attachments = sentry__scope_get_attachments(scope); if (local_scope - && sentry_value_get_length(local_scope->attachments) > 0) { + && sentry_value_get_length( + sentry__scope_get_attachments(local_scope)) + > 0) { // all attachments merged from multiple scopes sentry__attachments_extend(&all_attachments, sentry__scope_get_attachments(local_scope)); @@ -846,13 +848,15 @@ prepare_user_feedback(const sentry_options_t *options, sentry__attachments_extend(&all_attachments, hint->attachments); } if (local_scope) { - sentry__attachments_extend(&all_attachments, local_scope->attachments); + sentry__attachments_extend( + &all_attachments, sentry__scope_get_attachments(local_scope)); } SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = scope->attachments; + sentry_value_t attachments = sentry__scope_get_attachments(scope); if (sentry_value_get_length(all_attachments) > 0) { - sentry__attachments_extend(&all_attachments, scope->attachments); + sentry__attachments_extend( + &all_attachments, sentry__scope_get_attachments(scope)); attachments = all_attachments; } sentry__envelope_add_attachments(envelope, attachments, options); @@ -2044,8 +2048,8 @@ sentry_clear_attachments(void) { SENTRY_WITH_OPTIONS (options) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_value_t attachments = scope->attachments; - scope->attachments = sentry__attachments_new(); + sentry_value_t attachments + = sentry__scope_take_attachments(scope); size_t len = sentry_value_get_length(attachments); for (size_t i = 0; i < len; i++) { sentry_value_t attachment @@ -2071,8 +2075,8 @@ sentry_remove_attachment(sentry_uuid_t attachment_id) SENTRY_WITH_OPTIONS (options) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_value_t removed = sentry__attachments_remove( - scope->attachments, &attachment_id); + sentry_value_t removed + = sentry__scope_remove_attachment(scope, &attachment_id); if (!sentry_value_is_null(removed)) { if (options->backend && options->backend->remove_attachment_func) { diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 611209b46d..6ee7480d9d 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -29,29 +29,65 @@ #endif static bool g_scope_initialized = false; -static sentry_scope_t g_scope = { 0 }; #ifdef SENTRY__MUTEX_INIT_DYN SENTRY__MUTEX_INIT_DYN(g_lock) #else static sentry_mutex_t g_lock = SENTRY__MUTEX_INIT; #endif +struct sentry_scope_data_s { + sentry_rwlock_t rwlock; + + sentry_value_t release; + sentry_value_t environment; + sentry_value_t transaction; + sentry_value_t fingerprint; + sentry_value_t user; + sentry_value_t tags; + sentry_value_t extra; + sentry_value_t attributes; + sentry_value_t contexts; + sentry_value_t propagation_context; + sentry_ringbuffer_t *breadcrumbs; + sentry_value_t dynamic_sampling_context; + sentry_level_t level; + sentry_uuid_t last_event_id; + sentry_value_t client_sdk; + sentry_value_t attachments; + + // The span attached to this scope, if any. + // + // Conceptually, every transaction is a span, so it should be possible to + // attach spans or transactions to a scope. But sentry_span_t and + // sentry_transaction_t are unrelated types in the native SDK, so we need + // two distinct pointers. At most one of them should ever be non-null. + // Whenever possible, `transaction` should pull its value from the + // `name` property nested in transaction_object or span. + sentry_transaction_t *transaction_object; + sentry_span_t *span; + bool trace_managed; +}; + +static sentry_scope_t g_scope = { 0 }; +static sentry_scope_data_t g_scope_data = { 0 }; + #define SCOPE_READ_LOCK(Scope) \ - for (const sentry_scope_t *_locked_scope = (Scope); _locked_scope; \ - sentry__rwlock_read_unlock((sentry_rwlock_t *)&_locked_scope->rwlock), \ - _locked_scope = NULL) \ + for (const sentry_scope_data_t *_locked_data = (Scope)->data; \ + _locked_data; \ + sentry__rwlock_read_unlock((sentry_rwlock_t *)&_locked_data->rwlock), \ + _locked_data = NULL) \ for (bool _locked_once \ = (sentry__rwlock_read_lock( \ - (sentry_rwlock_t *)&_locked_scope->rwlock), \ + (sentry_rwlock_t *)&_locked_data->rwlock), \ true); \ _locked_once; _locked_once = false) #define SCOPE_WRITE_LOCK(Scope) \ - for (sentry_scope_t *_locked_scope = (Scope); _locked_scope; \ - sentry__rwlock_write_unlock(&_locked_scope->rwlock), \ - _locked_scope = NULL) \ + for (sentry_scope_data_t *_locked_data = (Scope)->data; _locked_data; \ + sentry__rwlock_write_unlock(&_locked_data->rwlock), \ + _locked_data = NULL) \ for (bool _locked_once \ - = (sentry__rwlock_write_lock(&_locked_scope->rwlock), true); \ + = (sentry__rwlock_write_lock(&_locked_data->rwlock), true); \ _locked_once; _locked_once = false) #define SENTRY_SCOPE_NOTIFY_OWNED(Scope, Callback, Value) \ @@ -95,6 +131,61 @@ get_client_sdk(void) return client_sdk; } +static bool +data_set_by_key(sentry_scope_data_t *data, sentry_value_t *object, + const char *key, sentry_value_t value) +{ + sentry__rwlock_write_lock(&data->rwlock); + int rv = sentry_value_set_by_key(*object, key, value); + sentry__rwlock_write_unlock(&data->rwlock); + return rv == 0; +} + +static bool +data_set_by_key_n(sentry_scope_data_t *data, sentry_value_t *object, + const char *key, size_t key_len, sentry_value_t value) +{ + sentry__rwlock_write_lock(&data->rwlock); + int rv = sentry_value_set_by_key_n(*object, key, key_len, value); + sentry__rwlock_write_unlock(&data->rwlock); + return rv == 0; +} + +static bool +data_remove_by_key( + sentry_scope_data_t *data, sentry_value_t *object, const char *key) +{ + sentry__rwlock_write_lock(&data->rwlock); + int rv = sentry_value_remove_by_key(*object, key); + sentry__rwlock_write_unlock(&data->rwlock); + return rv == 0; +} + +static char * +data_remove_and_take_key_n(sentry_scope_data_t *data, sentry_value_t *object, + const char *key, size_t key_len) +{ + sentry__rwlock_write_lock(&data->rwlock); + char *rv = sentry__value_remove_and_take_key_n(*object, key, key_len); + sentry__rwlock_write_unlock(&data->rwlock); + return rv; +} + +static bool +data_update_context_by_key_n(sentry_scope_data_t *data, const char *key, + size_t key_len, sentry_value_t value) +{ + sentry__rwlock_write_lock(&data->rwlock); + sentry_value_t context + = sentry_value_get_by_key_n(data->contexts, key, key_len); + if (!sentry_value_is_null(context)) { + sentry__value_merge_objects(value, context); + } + int rv = sentry_value_set_by_key_n(data->contexts, key, key_len, value); + sentry__rwlock_write_unlock(&data->rwlock); + return rv == 0; +} + static void generate_propagation_context(sentry_value_t propagation_context) { @@ -113,32 +204,85 @@ generate_propagation_context(sentry_value_t propagation_context) } static void -init_scope(sentry_scope_t *scope) -{ - scope->release = sentry_value_new_null(); - scope->environment = sentry_value_new_null(); - scope->transaction = sentry_value_new_null(); - scope->fingerprint = sentry_value_new_null(); - scope->user = sentry_value_new_null(); - scope->tags = sentry_value_new_object(); - scope->extra = sentry_value_new_object(); - scope->attributes = sentry_value_new_object(); - scope->contexts = sentry_value_new_object(); - scope->propagation_context = sentry_value_new_object(); - scope->breadcrumbs = sentry__ringbuffer_new(SENTRY_BREADCRUMBS_MAX); - scope->dynamic_sampling_context = sentry_value_new_object(); - scope->level = SENTRY_LEVEL_ERROR; - scope->last_event_id = sentry_uuid_nil(); - scope->client_sdk = sentry_value_new_null(); - scope->attachments = sentry__attachments_new(); - scope->transaction_object = NULL; - scope->span = NULL; - scope->trace_managed = true; +init_data(sentry_scope_data_t *data) +{ + data->release = sentry_value_new_null(); + data->environment = sentry_value_new_null(); + data->transaction = sentry_value_new_null(); + data->fingerprint = sentry_value_new_null(); + data->user = sentry_value_new_null(); + data->tags = sentry_value_new_object(); + data->extra = sentry_value_new_object(); + data->attributes = sentry_value_new_object(); + data->contexts = sentry_value_new_object(); + data->propagation_context = sentry_value_new_object(); + data->breadcrumbs = sentry__ringbuffer_new(SENTRY_BREADCRUMBS_MAX); + data->dynamic_sampling_context = sentry_value_new_object(); + data->level = SENTRY_LEVEL_ERROR; + data->last_event_id = sentry_uuid_nil(); + data->client_sdk = sentry_value_new_null(); + data->attachments = sentry__attachments_new(); + data->transaction_object = NULL; + data->span = NULL; + data->trace_managed = true; +} + +static void +cleanup_data(sentry_scope_data_t *data) +{ + sentry_value_decref(data->release); + sentry_value_decref(data->environment); + sentry_value_decref(data->transaction); + sentry_value_decref(data->fingerprint); + sentry_value_decref(data->user); + sentry_value_decref(data->tags); + sentry_value_decref(data->extra); + sentry_value_decref(data->attributes); + sentry_value_decref(data->contexts); + sentry_value_decref(data->propagation_context); + sentry__ringbuffer_free(data->breadcrumbs); + sentry_value_decref(data->dynamic_sampling_context); + sentry_value_decref(data->client_sdk); + sentry_value_decref(data->attachments); + sentry__transaction_decref(data->transaction_object); + sentry__span_decref(data->span); +} + +static sentry_scope_data_t * +new_data(void) +{ + sentry_scope_data_t *data = SENTRY_MAKE(sentry_scope_data_t); + if (data) { + sentry__rwlock_init(&data->rwlock); + init_data(data); + } + return data; +} + +static void +free_data(sentry_scope_data_t *data) +{ + if (!data) { + return; + } + cleanup_data(data); + sentry__rwlock_free(&data->rwlock); + sentry_free(data); +} + +static bool +init_scope(sentry_scope_t *scope, sentry_scope_data_t *data) +{ + scope->data = data ? data : new_data(); + if (!scope->data) { + return false; + } scope->observers = NULL; scope->num_observers = 0; scope->is_notifying = 0; scope->pending_flush = false; scope->one_shot = false; + return true; } static sentry_scope_t * @@ -149,11 +293,16 @@ get_scope(void) } memset(&g_scope, 0, sizeof(sentry_scope_t)); - sentry__rwlock_init(&g_scope.rwlock); - init_scope(&g_scope); - g_scope.user = sentry_value_new_object(); - sentry_value_set_by_key(g_scope.contexts, "os", sentry__get_os_context()); - g_scope.client_sdk = get_client_sdk(); + memset(&g_scope_data, 0, sizeof(sentry_scope_data_t)); + sentry__rwlock_init(&g_scope_data.rwlock); + init_data(&g_scope_data); + if (!init_scope(&g_scope, &g_scope_data)) { + return &g_scope; + } + g_scope.data->user = sentry_value_new_object(); + sentry_value_set_by_key( + g_scope.data->contexts, "os", sentry__get_os_context()); + g_scope.data->client_sdk = get_client_sdk(); g_scope_initialized = true; @@ -161,24 +310,8 @@ get_scope(void) } static void -cleanup_scope(sentry_scope_t *scope) +cleanup_observers(sentry_scope_t *scope) { - sentry_value_decref(scope->release); - sentry_value_decref(scope->environment); - sentry_value_decref(scope->transaction); - sentry_value_decref(scope->fingerprint); - sentry_value_decref(scope->user); - sentry_value_decref(scope->tags); - sentry_value_decref(scope->extra); - sentry_value_decref(scope->attributes); - sentry_value_decref(scope->contexts); - sentry_value_decref(scope->propagation_context); - sentry__ringbuffer_free(scope->breadcrumbs); - sentry_value_decref(scope->dynamic_sampling_context); - sentry_value_decref(scope->client_sdk); - sentry_value_decref(scope->attachments); - sentry__transaction_decref(scope->transaction_object); - sentry__span_decref(scope->span); for (size_t i = 0; i < scope->num_observers; i++) { sentry_free(scope->observers[i]); } @@ -188,6 +321,14 @@ cleanup_scope(sentry_scope_t *scope) scope->pending_flush = false; } +static void +cleanup_scope(sentry_scope_t *scope) +{ + free_data(scope->data); + scope->data = NULL; + cleanup_observers(scope); +} + void sentry__scope_cleanup(void) { @@ -195,8 +336,10 @@ sentry__scope_cleanup(void) sentry__mutex_lock(&g_lock); if (g_scope_initialized) { g_scope_initialized = false; - cleanup_scope(&g_scope); - sentry__rwlock_free(&g_scope.rwlock); + cleanup_data(g_scope.data); + sentry__rwlock_free(&g_scope.data->rwlock); + g_scope.data = NULL; + cleanup_observers(&g_scope); } sentry__mutex_unlock(&g_lock); } @@ -351,8 +494,10 @@ sentry_scope_new(void) return NULL; } - sentry__rwlock_init(&scope->rwlock); - init_scope(scope); + if (!init_scope(scope, NULL)) { + sentry_free(scope); + return NULL; + } return scope; } @@ -364,22 +509,22 @@ sentry_scope_free(sentry_scope_t *scope) } cleanup_scope(scope); - sentry__rwlock_free(&scope->rwlock); sentry_free(scope); } bool sentry__scope_is_one_shot(const sentry_scope_t *scope) { - bool one_shot = false; - SCOPE_READ_LOCK(scope) { one_shot = scope->one_shot; } - return one_shot; + return scope && scope->one_shot; } void sentry__scope_set_one_shot(sentry_scope_t *scope, bool one_shot) { - SCOPE_WRITE_LOCK(scope) { scope->one_shot = one_shot; } + if (!scope) { + return; + } + scope->one_shot = one_shot; } void @@ -405,19 +550,19 @@ sentry__scope_apply_options(sentry_scope_t *scope, sentry_options_t *options) { if (options->sdk_name) { sentry_value_t sdk_name = sentry_value_new_string(options->sdk_name); - sentry_value_set_by_key(scope->client_sdk, "name", sdk_name); + sentry_value_set_by_key(scope->data->client_sdk, "name", sdk_name); } - sentry_value_freeze(scope->client_sdk); - generate_propagation_context(scope->propagation_context); + sentry_value_freeze(scope->data->client_sdk); + generate_propagation_context(scope->data->propagation_context); sentry__scope_set_release_n( scope, options->release, sentry__guarded_strlen(options->release)); sentry__scope_set_environment_n(scope, options->environment, sentry__guarded_strlen(options->environment)); - scope->attachments = options->attachments; + scope->data->attachments = options->attachments; options->attachments = NULL; sentry__ringbuffer_set_max_size( - scope->breadcrumbs, options->max_breadcrumbs); + scope->data->breadcrumbs, options->max_breadcrumbs); sentry__scope_update_dsc(scope, options); } @@ -438,35 +583,25 @@ sentry_scope_clear(sentry_scope_t *scope) } sentry__scope_end_notify(scope); - sentry_scope_observer_t **observers = scope->observers; - size_t num_observers = scope->num_observers; - size_t is_notifying = scope->is_notifying; - bool pending_flush = scope->pending_flush; - scope->observers = NULL; - scope->num_observers = 0; - // Keep the propagation and dynamic sampling contexts across clears so // telemetry captured afterwards continues on the same trace. - bool trace_managed = sentry__scope_is_trace_managed(scope); - sentry_value_t propagation_context = scope->propagation_context; - sentry_value_t dynamic_sampling_context = scope->dynamic_sampling_context; - sentry_value_incref(propagation_context); - sentry_value_incref(dynamic_sampling_context); - bool one_shot = sentry__scope_is_one_shot(scope); + sentry_scope_data_t *data = scope->data; + sentry__rwlock_write_lock(&data->rwlock); + bool trace_managed = data->trace_managed; + sentry_value_t propagation_context + = sentry_value_incref(data->propagation_context); + sentry_value_t dynamic_sampling_context + = sentry_value_incref(data->dynamic_sampling_context); - cleanup_scope(scope); - init_scope(scope); + cleanup_data(data); + init_data(data); - sentry_value_decref(scope->propagation_context); - sentry_value_decref(scope->dynamic_sampling_context); - scope->propagation_context = propagation_context; - scope->dynamic_sampling_context = dynamic_sampling_context; - sentry__scope_set_trace_managed(scope, trace_managed); - sentry__scope_set_one_shot(scope, one_shot); - scope->observers = observers; - scope->num_observers = num_observers; - scope->is_notifying = is_notifying; - scope->pending_flush = pending_flush; + 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; + sentry__rwlock_write_unlock(&data->rwlock); } sentry_scope_t * @@ -481,50 +616,48 @@ sentry_scope_clone(const sentry_scope_t *scope) return NULL; } - sentry__rwlock_init(&clone->rwlock); - - sentry_value_t release = sentry__scope_ref_release(scope); - clone->release = sentry__value_clone(release); - sentry_value_decref(release); - - sentry_value_t environment = sentry__scope_ref_environment(scope); - clone->environment = sentry__value_clone(environment); - sentry_value_decref(environment); - - sentry_value_t transaction = sentry__scope_ref_transaction(scope); - clone->transaction = sentry__value_clone(transaction); - sentry_value_decref(transaction); + clone->data = SENTRY_MAKE(sentry_scope_data_t); + if (!clone->data) { + sentry_free(clone); + return NULL; + } + sentry__rwlock_init(&clone->data->rwlock); - sentry_value_t fingerprint = sentry__scope_ref_fingerprint(scope); - clone->fingerprint = sentry__value_clone(fingerprint); - sentry_value_decref(fingerprint); + SCOPE_READ_LOCK(scope) + { + clone->data->release = sentry__value_clone(scope->data->release); + clone->data->environment + = sentry__value_clone(scope->data->environment); + clone->data->transaction + = sentry__value_clone(scope->data->transaction); + clone->data->fingerprint + = sentry__value_clone(scope->data->fingerprint); + clone->data->user = sentry__value_clone(scope->data->user); + clone->data->tags = sentry__value_clone(scope->data->tags); + clone->data->extra = sentry__value_clone(scope->data->extra); + clone->data->contexts = sentry__value_clone(scope->data->contexts); + clone->data->attributes = sentry__value_clone(scope->data->attributes); + clone->data->propagation_context + = sentry__value_clone(scope->data->propagation_context); + clone->data->breadcrumbs + = sentry__ringbuffer_clone(scope->data->breadcrumbs); + clone->data->dynamic_sampling_context + = sentry__value_clone(scope->data->dynamic_sampling_context); + if (sentry_value_is_frozen(scope->data->dynamic_sampling_context)) { + sentry_value_freeze(clone->data->dynamic_sampling_context); + } + clone->data->level = scope->data->level; + clone->data->last_event_id = scope->data->last_event_id; + clone->data->client_sdk = sentry__value_clone(scope->data->client_sdk); + sentry__attachments_extend( + &clone->data->attachments, scope->data->attachments); - sentry_value_t user = sentry__scope_ref_user(scope); - clone->user = sentry__value_clone(user); - sentry_value_decref(user); - clone->tags = sentry__value_clone(scope->tags); - clone->extra = sentry__value_clone(scope->extra); - clone->attributes = sentry__value_clone(scope->attributes); - clone->contexts = sentry__value_clone(scope->contexts); - clone->propagation_context - = sentry__value_clone(scope->propagation_context); - clone->breadcrumbs = sentry__ringbuffer_clone(scope->breadcrumbs); - clone->dynamic_sampling_context - = sentry__value_clone(scope->dynamic_sampling_context); - if (sentry_value_is_frozen(scope->dynamic_sampling_context)) { - sentry_value_freeze(clone->dynamic_sampling_context); - } - clone->level = sentry__scope_get_level(scope); - clone->last_event_id = sentry_scope_get_last_event_id(scope); - clone->client_sdk = sentry__value_clone(scope->client_sdk); - clone->attachments = sentry__attachments_new(); - sentry__attachments_extend(&clone->attachments, scope->attachments); - - clone->transaction_object = scope->transaction_object; - sentry__transaction_incref(clone->transaction_object); - clone->span = scope->span; - sentry__span_incref(clone->span); - clone->trace_managed = sentry__scope_is_trace_managed(scope); + clone->data->transaction_object = scope->data->transaction_object; + sentry__transaction_incref(clone->data->transaction_object); + clone->data->span = scope->data->span; + sentry__span_incref(clone->data->span); + clone->data->trace_managed = scope->data->trace_managed; + } return clone; } @@ -532,62 +665,62 @@ sentry_scope_clone(const sentry_scope_t *scope) sentry_value_t sentry__scope_get_propagation_context(const sentry_scope_t *scope) { - return scope->propagation_context; + return scope->data->propagation_context; } sentry_value_t sentry__scope_get_trace_context(const sentry_scope_t *scope) { - return sentry_value_get_by_key(scope->propagation_context, "trace"); + return sentry_value_get_by_key(scope->data->propagation_context, "trace"); } void sentry__scope_set_propagation_context( sentry_scope_t *scope, const char *key, sentry_value_t value) { - sentry_value_set_by_key(scope->propagation_context, key, value); + sentry_value_set_by_key(scope->data->propagation_context, key, value); } void sentry__scope_regenerate_propagation_context(sentry_scope_t *scope) { - generate_propagation_context(scope->propagation_context); + generate_propagation_context(scope->data->propagation_context); } bool sentry__scope_is_trace_managed(const sentry_scope_t *scope) { bool managed = false; - SCOPE_READ_LOCK(scope) { managed = scope->trace_managed; } + SCOPE_READ_LOCK(scope) { managed = scope->data->trace_managed; } return managed; } void sentry__scope_set_trace_managed(sentry_scope_t *scope, bool managed) { - SCOPE_WRITE_LOCK(scope) { scope->trace_managed = managed; } + SCOPE_WRITE_LOCK(scope) { scope->data->trace_managed = managed; } } sentry_value_t sentry__scope_get_dsc(const sentry_scope_t *scope) { - return scope->dynamic_sampling_context; + return scope->data->dynamic_sampling_context; } void sentry__scope_freeze_dsc(sentry_scope_t *scope, sentry_value_t incoming) { - sentry_value_decref(scope->dynamic_sampling_context); + sentry_value_decref(scope->data->dynamic_sampling_context); sentry_value_t dsc = sentry_value_new_object(); sentry__value_merge_objects(dsc, incoming); sentry_value_freeze(dsc); - scope->dynamic_sampling_context = dsc; + scope->data->dynamic_sampling_context = dsc; } void sentry__scope_update_dsc(sentry_scope_t *scope, const sentry_options_t *options) { - sentry_value_decref(scope->dynamic_sampling_context); + sentry_value_decref(scope->data->dynamic_sampling_context); sentry_value_t dsc = sentry_value_new_object(); if (options->dsn) { @@ -605,7 +738,7 @@ sentry__scope_update_dsc(sentry_scope_t *scope, const sentry_options_t *options) dsc, "sample_rate", sentry_value_new_double(1.0)); } sentry_value_t sample_rand = sentry_value_get_by_key( - sentry_value_get_by_key(scope->propagation_context, "trace"), + sentry_value_get_by_key(scope->data->propagation_context, "trace"), "sample_rand"); sentry_value_set_by_key(dsc, "sample_rand", sample_rand); sentry_value_incref(sample_rand); @@ -613,7 +746,7 @@ sentry__scope_update_dsc(sentry_scope_t *scope, const sentry_options_t *options) sentry_value_set_by_key( dsc, "environment", sentry__scope_ref_environment(scope)); - scope->dynamic_sampling_context = dsc; + scope->data->dynamic_sampling_context = dsc; } #if !defined(SENTRY_PLATFORM_NX) @@ -722,10 +855,10 @@ sentry__symbolize_stacktrace(sentry_value_t stacktrace) static sentry_value_t get_span_or_transaction(const sentry_scope_t *scope) { - if (scope->span) { - return scope->span->inner; - } else if (scope->transaction_object) { - return scope->transaction_object->inner; + if (scope->data->span) { + return scope->data->span->inner; + } else if (scope->data->transaction_object) { + return scope->data->transaction_object->inner; } else { return sentry_value_new_null(); } @@ -828,27 +961,35 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, PLACE_STRING_VALUE("transaction", transaction); sentry_value_decref(transaction); - PLACE_VALUE("sdk", scope->client_sdk); + PLACE_VALUE("sdk", scope->data->client_sdk); - sentry_value_t event_tags = sentry_value_get_by_key(event, "tags"); - if (sentry_value_is_null(event_tags)) { - if (!sentry_value_is_null(scope->tags)) { - PLACE_CLONED_VALUE("tags", scope->tags); + SCOPE_READ_LOCK(scope) + { + sentry_value_t event_tags = sentry_value_get_by_key(event, "tags"); + if (sentry_value_is_null(event_tags)) { + if (!sentry_value_is_null(scope->data->tags)) { + PLACE_CLONED_VALUE("tags", scope->data->tags); + } + } else { + sentry__value_merge_objects(event_tags, scope->data->tags); } - } else { - sentry__value_merge_objects(event_tags, scope->tags); - } - sentry_value_t event_extra = sentry_value_get_by_key(event, "extra"); - if (sentry_value_is_null(event_extra)) { - if (!sentry_value_is_null(scope->extra)) { - PLACE_CLONED_VALUE("extra", scope->extra); + + sentry_value_t event_extra = sentry_value_get_by_key(event, "extra"); + if (sentry_value_is_null(event_extra)) { + if (!sentry_value_is_null(scope->data->extra)) { + PLACE_CLONED_VALUE("extra", scope->data->extra); + } + } else { + sentry__value_merge_objects(event_extra, scope->data->extra); } - } else { - sentry__value_merge_objects(event_extra, scope->extra); } bool is_transaction = sentry__event_is_transaction(event); - sentry_value_t contexts = sentry__value_clone(scope->contexts); + sentry_value_t contexts = sentry_value_new_null(); + SCOPE_READ_LOCK(scope) + { + contexts = sentry__value_clone(scope->data->contexts); + } if (is_transaction && !sentry_value_is_null(contexts)) { sentry_value_remove_by_key(contexts, "trace"); } @@ -881,7 +1022,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, if (!is_transaction && sentry_value_is_null(scope_trace) && sentry_value_is_null( sentry_value_get_by_key(event_contexts, "trace"))) { - sentry__value_merge_objects(contexts, scope->propagation_context); + sentry__value_merge_objects(contexts, scope->data->propagation_context); } if (sentry_value_is_null(event_contexts)) { PLACE_VALUE("contexts", contexts); @@ -894,7 +1035,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, sentry_value_t event_breadcrumbs = sentry_value_get_by_key(event, "breadcrumbs"); sentry_value_t scope_breadcrumbs - = sentry__ringbuffer_to_list(scope->breadcrumbs); + = sentry__ringbuffer_to_list(scope->data->breadcrumbs); sentry_value_set_by_key(event, "breadcrumbs", sentry__value_merge_breadcrumbs(event_breadcrumbs, scope_breadcrumbs, options->max_breadcrumbs)); @@ -927,7 +1068,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, void sentry_scope_add_breadcrumb(sentry_scope_t *scope, sentry_value_t breadcrumb) { - if (sentry__ringbuffer_append(scope->breadcrumbs, breadcrumb) == 0) { + if (sentry__ringbuffer_append(scope->data->breadcrumbs, breadcrumb) == 0) { SENTRY_SCOPE_NOTIFY(scope, add_breadcrumb, breadcrumb); } } @@ -935,14 +1076,14 @@ sentry_scope_add_breadcrumb(sentry_scope_t *scope, sentry_value_t breadcrumb) const sentry_ringbuffer_t * sentry__scope_get_breadcrumbs(const sentry_scope_t *scope) { - return scope->breadcrumbs; + return scope->data->breadcrumbs; } sentry_value_t sentry__scope_ref_user(const sentry_scope_t *scope) { sentry_value_t user = sentry_value_new_null(); - SCOPE_READ_LOCK(scope) { user = sentry_value_incref(scope->user); } + SCOPE_READ_LOCK(scope) { user = sentry_value_incref(scope->data->user); } return user; } @@ -951,7 +1092,7 @@ sentry_scope_set_user(sentry_scope_t *scope, sentry_value_t user) { SCOPE_WRITE_LOCK(scope) { - sentry__value_replace(&scope->user, sentry_value_incref(user)); + sentry__value_replace(&scope->data->user, sentry_value_incref(user)); } SENTRY_SCOPE_NOTIFY_OWNED(scope, set_user, user); } @@ -959,15 +1100,14 @@ sentry_scope_set_user(sentry_scope_t *scope, sentry_value_t user) sentry_value_t sentry__scope_get_tags(const sentry_scope_t *scope) { - return scope->tags; + return scope->data->tags; } void sentry_scope_set_tag(sentry_scope_t *scope, const char *key, const char *value) { - if (sentry_value_set_by_key( - scope->tags, key, sentry_value_new_string(value)) - == 0) { + if (data_set_by_key(scope->data, &scope->data->tags, key, + sentry_value_new_string(value))) { SENTRY_SCOPE_NOTIFY(scope, set_tag, key, value); } } @@ -976,11 +1116,20 @@ void sentry_scope_set_tag_n(sentry_scope_t *scope, const char *key, size_t key_len, const char *value, size_t value_len) { - char *k = sentry__string_clone_n(key, key_len); - sentry_value_t v = sentry_value_new_string_n(value, value_len); - if (sentry__value_set_by_key_owned(scope->tags, k, key_len, v) == 0) { - SENTRY_SCOPE_NOTIFY(scope, set_tag, k, sentry_value_as_string(v)); + sentry_value_t tag_value = sentry_value_new_string_n(value, value_len); + char *notify_key = sentry__string_clone_n(key, key_len); + if (!notify_key) { + sentry_value_decref(tag_value); + return; } + + if (data_set_by_key_n(scope->data, &scope->data->tags, key, key_len, + sentry_value_incref(tag_value))) { + SENTRY_SCOPE_NOTIFY( + scope, set_tag, notify_key, sentry_value_as_string(tag_value)); + } + sentry_free(notify_key); + sentry_value_decref(tag_value); } static void @@ -1004,7 +1153,7 @@ sentry_scope_set_tags(sentry_scope_t *scope, sentry_value_t tags) void sentry__scope_remove_tag(sentry_scope_t *scope, const char *key) { - if (sentry_value_remove_by_key(scope->tags, key) == 0) { + if (data_remove_by_key(scope->data, &scope->data->tags, key)) { SENTRY_SCOPE_NOTIFY(scope, remove_tag, key); } } @@ -1013,7 +1162,8 @@ void sentry__scope_remove_tag_n( sentry_scope_t *scope, const char *key, size_t key_len) { - char *k = sentry__value_remove_and_take_key_n(scope->tags, key, key_len); + char *k = data_remove_and_take_key_n( + scope->data, &scope->data->tags, key, key_len); if (k) { SENTRY_SCOPE_NOTIFY(scope, remove_tag, k); } @@ -1023,32 +1173,42 @@ sentry__scope_remove_tag_n( sentry_value_t sentry__scope_get_extra(const sentry_scope_t *scope) { - return scope->extra; + return scope->data->extra; } void sentry_scope_set_extra( sentry_scope_t *scope, const char *key, sentry_value_t value) { - if (sentry_value_set_by_key(scope->extra, key, value) == 0) { + if (data_set_by_key(scope->data, &scope->data->extra, key, + sentry_value_incref(value))) { SENTRY_SCOPE_NOTIFY(scope, set_extra, key, value); } + sentry_value_decref(value); } void sentry_scope_set_extra_n(sentry_scope_t *scope, const char *key, size_t key_len, sentry_value_t value) { - char *k = sentry__string_clone_n(key, key_len); - if (sentry__value_set_by_key_owned(scope->extra, k, key_len, value) == 0) { - SENTRY_SCOPE_NOTIFY(scope, set_extra, k, value); + char *notify_key = sentry__string_clone_n(key, key_len); + if (!notify_key) { + sentry_value_decref(value); + return; } + + if (data_set_by_key_n(scope->data, &scope->data->extra, key, key_len, + sentry_value_incref(value))) { + SENTRY_SCOPE_NOTIFY(scope, set_extra, notify_key, value); + } + sentry_free(notify_key); + sentry_value_decref(value); } void sentry__scope_remove_extra(sentry_scope_t *scope, const char *key) { - if (sentry_value_remove_by_key(scope->extra, key) == 0) { + if (data_remove_by_key(scope->data, &scope->data->extra, key)) { SENTRY_SCOPE_NOTIFY(scope, remove_extra, key); } } @@ -1057,7 +1217,8 @@ void sentry__scope_remove_extra_n( sentry_scope_t *scope, const char *key, size_t key_len) { - char *k = sentry__value_remove_and_take_key_n(scope->extra, key, key_len); + char *k = data_remove_and_take_key_n( + scope->data, &scope->data->extra, key, key_len); if (k) { SENTRY_SCOPE_NOTIFY(scope, remove_extra, k); } @@ -1082,52 +1243,61 @@ sentry_scope_set_attribute_n(sentry_scope_t *scope, const char *key, sentry_value_decref(attribute); return; } - sentry_value_set_by_key_n(scope->attributes, key, key_len, attribute); + sentry_value_set_by_key_n(scope->data->attributes, key, key_len, attribute); } sentry_value_t sentry__scope_get_attributes(const sentry_scope_t *scope) { - return scope->attributes; + return scope->data->attributes; } void sentry_scope_remove_attribute(sentry_scope_t *scope, const char *key) { - sentry_value_remove_by_key(scope->attributes, key); + sentry_value_remove_by_key(scope->data->attributes, key); } void sentry_scope_remove_attribute_n( sentry_scope_t *scope, const char *key, size_t key_len) { - sentry_value_remove_by_key_n(scope->attributes, key, key_len); + sentry_value_remove_by_key_n(scope->data->attributes, key, key_len); } sentry_value_t sentry__scope_get_contexts(const sentry_scope_t *scope) { - return scope->contexts; + return scope->data->contexts; } void sentry_scope_set_context( sentry_scope_t *scope, const char *key, sentry_value_t value) { - if (sentry_value_set_by_key(scope->contexts, key, value) == 0) { + if (data_set_by_key(scope->data, &scope->data->contexts, key, + sentry_value_incref(value))) { SENTRY_SCOPE_NOTIFY(scope, set_context, key, value); } + sentry_value_decref(value); } void sentry_scope_set_context_n(sentry_scope_t *scope, const char *key, size_t key_len, sentry_value_t value) { - char *k = sentry__string_clone_n(key, key_len); - if (sentry__value_set_by_key_owned(scope->contexts, k, key_len, value) - == 0) { - SENTRY_SCOPE_NOTIFY(scope, set_context, k, value); + char *notify_key = sentry__string_clone_n(key, key_len); + if (!notify_key) { + sentry_value_decref(value); + return; } + + if (data_set_by_key_n(scope->data, &scope->data->contexts, key, key_len, + sentry_value_incref(value))) { + SENTRY_SCOPE_NOTIFY(scope, set_context, notify_key, value); + } + sentry_free(notify_key); + sentry_value_decref(value); } void @@ -1142,28 +1312,24 @@ void sentry_scope_update_context_n(sentry_scope_t *scope, const char *key, size_t key_len, sentry_value_t value) { - sentry_value_t context - = sentry_value_get_by_key_n(scope->contexts, key, key_len); - char *k = sentry__string_clone_n(key, key_len); - if (sentry_value_is_null(context)) { - if (sentry__value_set_by_key_owned(scope->contexts, k, key_len, value) - != 0) { - return; - } - } else { - sentry__value_merge_objects(value, context); - if (sentry__value_set_by_key_owned(scope->contexts, k, key_len, value) - != 0) { - return; - } + char *notify_key = sentry__string_clone_n(key, key_len); + if (!notify_key) { + sentry_value_decref(value); + return; } - SENTRY_SCOPE_NOTIFY(scope, set_context, k, value); + + if (data_update_context_by_key_n( + scope->data, key, key_len, sentry_value_incref(value))) { + SENTRY_SCOPE_NOTIFY(scope, set_context, notify_key, value); + } + sentry_free(notify_key); + sentry_value_decref(value); } void sentry__scope_remove_context(sentry_scope_t *scope, const char *key) { - if (sentry_value_remove_by_key(scope->contexts, key) == 0) { + if (data_remove_by_key(scope->data, &scope->data->contexts, key)) { SENTRY_SCOPE_NOTIFY(scope, remove_context, key); } } @@ -1172,8 +1338,8 @@ void sentry__scope_remove_context_n( sentry_scope_t *scope, const char *key, size_t key_len) { - char *k - = sentry__value_remove_and_take_key_n(scope->contexts, key, key_len); + char *k = data_remove_and_take_key_n( + scope->data, &scope->data->contexts, key, key_len); if (k) { SENTRY_SCOPE_NOTIFY(scope, remove_context, k); } @@ -1186,7 +1352,7 @@ sentry__scope_ref_fingerprint(const sentry_scope_t *scope) sentry_value_t fingerprint = sentry_value_new_null(); SCOPE_READ_LOCK(scope) { - fingerprint = sentry_value_incref(scope->fingerprint); + fingerprint = sentry_value_incref(scope->data->fingerprint); } return fingerprint; } @@ -1257,7 +1423,7 @@ sentry_scope_set_fingerprints( SCOPE_WRITE_LOCK(scope) { sentry__value_replace( - &scope->fingerprint, sentry_value_incref(fingerprints)); + &scope->data->fingerprint, sentry_value_incref(fingerprints)); } SENTRY_SCOPE_NOTIFY_OWNED(scope, set_fingerprint, fingerprints); } @@ -1269,7 +1435,7 @@ sentry_scope_remove_fingerprint(sentry_scope_t *scope) SCOPE_WRITE_LOCK(scope) { sentry__value_replace( - &scope->fingerprint, sentry_value_incref(fingerprint)); + &scope->data->fingerprint, sentry_value_incref(fingerprint)); } SENTRY_SCOPE_NOTIFY_OWNED(scope, set_fingerprint, fingerprint); } @@ -1278,14 +1444,20 @@ sentry_level_t sentry__scope_get_level(const sentry_scope_t *scope) { sentry_level_t level = SENTRY_LEVEL_ERROR; - SCOPE_READ_LOCK(scope) { level = scope->level; } + SCOPE_READ_LOCK(scope) { level = scope->data->level; } return level; } +sentry_value_t +sentry__scope_get_client_sdk(const sentry_scope_t *scope) +{ + return scope->data->client_sdk; +} + void sentry_scope_set_level(sentry_scope_t *scope, sentry_level_t level) { - SCOPE_WRITE_LOCK(scope) { scope->level = level; } + SCOPE_WRITE_LOCK(scope) { scope->data->level = level; } SENTRY_SCOPE_NOTIFY(scope, set_level, level); } @@ -1312,19 +1484,41 @@ sentry_scope_set_span(sentry_scope_t *scope, sentry_span_t *span) scope->span = span; } +sentry_value_t +sentry__scope_get_attachments(const sentry_scope_t *scope) +{ + return scope->data->attachments; +} + sentry_value_t sentry__scope_add_attachment(sentry_scope_t *scope, sentry_value_t attachment) { - size_t len = sentry_value_get_length(scope->attachments); + size_t len = sentry_value_get_length(scope->data->attachments); sentry_value_t added - = sentry__attachments_add(&scope->attachments, attachment); + = sentry__attachments_add(&scope->data->attachments, attachment); if (!sentry_value_is_null(added) - && sentry_value_get_length(scope->attachments) > len) { + && sentry_value_get_length(scope->data->attachments) > len) { SENTRY_SCOPE_NOTIFY(scope, add_attachment, added); } return added; } +sentry_value_t +sentry__scope_take_attachments(sentry_scope_t *scope) +{ + sentry_value_t attachments = scope->data->attachments; + scope->data->attachments = sentry__attachments_new(); + return attachments; +} + +sentry_value_t +sentry__scope_remove_attachment( + sentry_scope_t *scope, const sentry_uuid_t *attachment_id) +{ + return sentry__attachments_remove( + scope->data->attachments, attachment_id); +} + sentry_uuid_t sentry_scope_add_attachment(sentry_scope_t *scope, sentry_value_t attachment) { @@ -1342,7 +1536,7 @@ sentry_scope_add_attachment(sentry_scope_t *scope, sentry_value_t attachment) sentry_transaction_t * sentry__scope_get_transaction_object(const sentry_scope_t *scope) { - return scope->transaction_object; + return scope->data->transaction_object; } static bool @@ -1358,21 +1552,21 @@ sentry__scope_set_transaction_object( sentry_scope_t *scope, sentry_transaction_t *transaction) { sentry__transaction_incref(transaction); - sentry__span_decref(scope->span); - scope->span = NULL; - sentry__transaction_decref(scope->transaction_object); - scope->transaction_object = transaction; + sentry__span_decref(scope->data->span); + scope->data->span = NULL; + sentry__transaction_decref(scope->data->transaction_object); + scope->data->transaction_object = transaction; } bool sentry__scope_remove_transaction_object( sentry_scope_t *scope, sentry_transaction_t *transaction) { - if (!transaction || scope->transaction_object != transaction) { + if (!transaction || scope->data->transaction_object != transaction) { return false; } - scope->transaction_object = NULL; + scope->data->transaction_object = NULL; sentry__transaction_decref(transaction); return true; } @@ -1383,13 +1577,14 @@ sentry__scope_remove_transaction_value( { const char *span_id = sentry_value_as_string( sentry_value_get_by_key(transaction, "span_id")); - if (!scope->transaction_object - || !value_has_span_id(scope->transaction_object->inner, span_id)) { + if (!scope->data->transaction_object + || !value_has_span_id( + scope->data->transaction_object->inner, span_id)) { return false; } - sentry_transaction_t *transaction_object = scope->transaction_object; - scope->transaction_object = NULL; + sentry_transaction_t *transaction_object = scope->data->transaction_object; + scope->data->transaction_object = NULL; sentry__transaction_decref(transaction_object); return true; } @@ -1398,38 +1593,38 @@ bool sentry__scope_restore_transaction_object( sentry_scope_t *scope, sentry_transaction_t *transaction) { - if (scope->transaction_object || !transaction) { + if (scope->data->transaction_object || !transaction) { return false; } - scope->transaction_object = transaction; + scope->data->transaction_object = transaction; return true; } sentry_span_t * sentry__scope_get_span(const sentry_scope_t *scope) { - return scope->span; + return scope->data->span; } void sentry__scope_set_span(sentry_scope_t *scope, sentry_span_t *span) { sentry__span_incref(span); - sentry__transaction_decref(scope->transaction_object); - scope->transaction_object = NULL; - sentry__span_decref(scope->span); - scope->span = span; + sentry__transaction_decref(scope->data->transaction_object); + scope->data->transaction_object = NULL; + sentry__span_decref(scope->data->span); + scope->data->span = span; } bool sentry__scope_remove_span(sentry_scope_t *scope, sentry_span_t *span) { - if (!span || scope->span != span) { + if (!span || scope->data->span != span) { return false; } - scope->span = NULL; + scope->data->span = NULL; sentry__span_decref(span); return true; } @@ -1439,12 +1634,13 @@ sentry__scope_remove_span_value(sentry_scope_t *scope, sentry_value_t span) { const char *span_id = sentry_value_as_string(sentry_value_get_by_key(span, "span_id")); - if (!scope->span || !value_has_span_id(scope->span->inner, span_id)) { + if (!scope->data->span + || !value_has_span_id(scope->data->span->inner, span_id)) { return false; } - sentry_span_t *scope_span = scope->span; - scope->span = NULL; + sentry_span_t *scope_span = scope->data->span; + scope->data->span = NULL; sentry__span_decref(scope_span); return true; } @@ -1452,11 +1648,11 @@ sentry__scope_remove_span_value(sentry_scope_t *scope, sentry_value_t span) bool sentry__scope_restore_span(sentry_scope_t *scope, sentry_span_t *span) { - if (scope->span || !span) { + if (scope->data->span || !span) { return false; } - scope->span = span; + scope->data->span = span; return true; } @@ -1464,7 +1660,10 @@ sentry_value_t sentry__scope_ref_release(const sentry_scope_t *scope) { sentry_value_t release = sentry_value_new_null(); - SCOPE_READ_LOCK(scope) { release = sentry_value_incref(scope->release); } + SCOPE_READ_LOCK(scope) + { + release = sentry_value_incref(scope->data->release); + } return release; } @@ -1475,9 +1674,10 @@ sentry__scope_set_release_n( sentry_value_t value = sentry_value_new_string_n(release, release_len); SCOPE_WRITE_LOCK(scope) { - sentry__value_replace(&scope->release, sentry_value_incref(value)); - sentry_value_set_by_key(scope->dynamic_sampling_context, "release", - sentry_value_incref(value)); + sentry__value_replace( + &scope->data->release, sentry_value_incref(value)); + sentry_value_set_by_key(scope->data->dynamic_sampling_context, + "release", sentry_value_incref(value)); } SENTRY_SCOPE_NOTIFY_OWNED(scope, set_release, value); } @@ -1488,7 +1688,7 @@ sentry__scope_ref_environment(const sentry_scope_t *scope) sentry_value_t environment = sentry_value_new_null(); SCOPE_READ_LOCK(scope) { - environment = sentry_value_incref(scope->environment); + environment = sentry_value_incref(scope->data->environment); } return environment; } @@ -1501,9 +1701,10 @@ sentry__scope_set_environment_n( = sentry_value_new_string_n(environment, environment_len); SCOPE_WRITE_LOCK(scope) { - sentry__value_replace(&scope->environment, sentry_value_incref(value)); - sentry_value_set_by_key(scope->dynamic_sampling_context, "environment", - sentry_value_incref(value)); + sentry__value_replace( + &scope->data->environment, sentry_value_incref(value)); + sentry_value_set_by_key(scope->data->dynamic_sampling_context, + "environment", sentry_value_incref(value)); } SENTRY_SCOPE_NOTIFY_OWNED(scope, set_environment, value); } @@ -1514,7 +1715,7 @@ sentry__scope_ref_transaction(const sentry_scope_t *scope) sentry_value_t transaction = sentry_value_new_null(); SCOPE_READ_LOCK(scope) { - transaction = sentry_value_incref(scope->transaction); + transaction = sentry_value_incref(scope->data->transaction); } return transaction; } @@ -1527,10 +1728,11 @@ sentry__scope_set_transaction_n( = sentry_value_new_string_n(transaction, transaction_len); SCOPE_WRITE_LOCK(scope) { - sentry__value_replace(&scope->transaction, sentry_value_incref(value)); - if (scope->transaction_object) { + sentry__value_replace( + &scope->data->transaction, sentry_value_incref(value)); + if (scope->data->transaction_object) { sentry_transaction_set_name_n( - scope->transaction_object, transaction, transaction_len); + scope->data->transaction_object, transaction, transaction_len); } } SENTRY_SCOPE_NOTIFY_OWNED(scope, set_transaction, value); @@ -1605,31 +1807,32 @@ void sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, sentry_value_t telemetry, sentry_value_t attributes) { - sentry__value_merge_objects_shallow(attributes, scope->attributes); + sentry__value_merge_objects_shallow(attributes, scope->data->attributes); // a span on the scope MUST take precedence over the propagation context sentry_value_t trace_id = sentry_value_get_by_key( - sentry_value_get_by_key(scope->propagation_context, "trace"), + sentry_value_get_by_key(scope->data->propagation_context, "trace"), "trace_id"); sentry_value_t parent_span_id = sentry_value_new_object(); - if (scope->transaction_object) { + if (scope->data->transaction_object) { sentry_value_t span_id = sentry_value_get_by_key( - scope->transaction_object->inner, "span_id"); + scope->data->transaction_object->inner, "span_id"); sentry_value_incref(span_id); sentry_value_set_by_key(parent_span_id, "value", span_id); trace_id = sentry_value_get_by_key( - scope->transaction_object->inner, "trace_id"); - } else if (scope->span) { + scope->data->transaction_object->inner, "trace_id"); + } else if (scope->data->span) { sentry_value_t span_id - = sentry_value_get_by_key(scope->span->inner, "span_id"); + = sentry_value_get_by_key(scope->data->span->inner, "span_id"); sentry_value_incref(span_id); sentry_value_set_by_key(parent_span_id, "value", span_id); - trace_id = sentry_value_get_by_key(scope->span->inner, "trace_id"); + trace_id + = sentry_value_get_by_key(scope->data->span->inner, "trace_id"); } sentry_value_set_by_key( parent_span_id, "type", sentry_value_new_string("string")); - if ((scope->transaction_object || scope->span) + if ((scope->data->transaction_object || scope->data->span) && sentry_value_is_null(sentry_value_get_by_key( attributes, "sentry.trace.parent_span_id"))) { sentry_value_set_by_key( @@ -1670,22 +1873,30 @@ sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, } sentry_value_decref(user); - sentry_value_t os_context = sentry_value_get_by_key(scope->contexts, "os"); - if (!sentry_value_is_null(os_context)) { - sentry_value_t os_name = sentry_value_get_by_key(os_context, "name"); - sentry_value_t os_version - = sentry_value_get_by_key(os_context, "version"); - if (!sentry_value_is_null(os_name)) { - sentry_value_incref(os_name); - sentry__value_add_attribute( - attributes, os_name, "string", "os.name"); - } - if (!sentry_value_is_null(os_version)) { - sentry_value_incref(os_version); - sentry__value_add_attribute( - attributes, os_version, "string", "os.version"); + sentry_value_t os_name = sentry_value_new_null(); + sentry_value_t os_version = sentry_value_new_null(); + SCOPE_READ_LOCK(scope) + { + sentry_value_t os_context + = sentry_value_get_by_key(scope->data->contexts, "os"); + if (!sentry_value_is_null(os_context)) { + os_name = sentry_value_incref( + sentry_value_get_by_key(os_context, "name")); + os_version = sentry_value_incref( + sentry_value_get_by_key(os_context, "version")); } } + if (!sentry_value_is_null(os_name)) { + sentry__value_add_attribute(attributes, os_name, "string", "os.name"); + } else { + sentry_value_decref(os_name); + } + if (!sentry_value_is_null(os_version)) { + sentry__value_add_attribute( + attributes, os_version, "string", "os.version"); + } else { + sentry_value_decref(os_version); + } sentry_value_t environment = sentry__scope_ref_environment(scope); if (!sentry_value_is_null(environment)) { sentry__value_add_attribute(attributes, @@ -1705,7 +1916,9 @@ sentry_uuid_t sentry_scope_get_last_event_id(const sentry_scope_t *scope) { sentry_uuid_t event_id = sentry_uuid_nil(); - SCOPE_READ_LOCK(scope) { event_id = scope->last_event_id; } + if (scope) { + SCOPE_READ_LOCK(scope) { event_id = scope->data->last_event_id; } + } return event_id; } @@ -1713,7 +1926,9 @@ void sentry__scope_set_last_event_id( sentry_scope_t *scope, sentry_uuid_t event_id) { - SCOPE_WRITE_LOCK(scope) { scope->last_event_id = event_id; } + if (scope) { + SCOPE_WRITE_LOCK(scope) { scope->data->last_event_id = event_id; } + } } void diff --git a/src/sentry_scope.h b/src/sentry_scope.h index ba56cad57a..e98e213a31 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -50,40 +50,13 @@ typedef struct sentry_scope_observer_s { void (*remove_attachment)(void *data, sentry_value_t attachment); } sentry_scope_observer_t; +typedef struct sentry_scope_data_s sentry_scope_data_t; + /** * This represents the current scope. */ struct sentry_scope_s { - sentry_rwlock_t rwlock; - - sentry_value_t release; - sentry_value_t environment; - sentry_value_t transaction; - sentry_value_t fingerprint; - sentry_value_t user; - sentry_value_t tags; - sentry_value_t extra; - sentry_value_t attributes; - sentry_value_t contexts; - sentry_value_t propagation_context; - sentry_ringbuffer_t *breadcrumbs; - sentry_value_t dynamic_sampling_context; - sentry_level_t level; - sentry_uuid_t last_event_id; - sentry_value_t client_sdk; - sentry_value_t attachments; - - // The span attached to this scope, if any. - // - // Conceptually, every transaction is a span, so it should be possible to - // attach spans or transactions to a scope. But sentry_span_t and - // sentry_transaction_t are unrelated types in the native SDK, so we need - // two distinct pointers. At most one of them should ever be non-null. - // Whenever possible, `transaction` should pull its value from the - // `name` property nested in transaction_object or span. - sentry_transaction_t *transaction_object; - sentry_span_t *span; - bool trace_managed; + sentry_scope_data_t *data; sentry_scope_observer_t **observers; size_t num_observers; @@ -173,9 +146,14 @@ void sentry__scope_set_fingerprint_nva(sentry_scope_t *scope, const char *fingerprint, size_t fingerprint_len, va_list va); sentry_value_t sentry__scope_ref_user(const sentry_scope_t *scope); sentry_level_t sentry__scope_get_level(const sentry_scope_t *scope); +sentry_value_t sentry__scope_get_client_sdk(const sentry_scope_t *scope); +sentry_value_t sentry__scope_get_attachments(const sentry_scope_t *scope); sentry_value_t sentry__scope_add_attachment( sentry_scope_t *scope, sentry_value_t attachment); +sentry_value_t sentry__scope_take_attachments(sentry_scope_t *scope); +sentry_value_t sentry__scope_remove_attachment( + sentry_scope_t *scope, const sentry_uuid_t *attachment_id); sentry_value_t sentry__scope_get_tags(const sentry_scope_t *scope); void sentry__scope_remove_tag(sentry_scope_t *scope, const char *key); void sentry__scope_remove_tag_n( diff --git a/src/sentry_tracing.h b/src/sentry_tracing.h index 5ba423d170..2c9907794a 100644 --- a/src/sentry_tracing.h +++ b/src/sentry_tracing.h @@ -59,8 +59,8 @@ void sentry__transaction_remove_child( /** * Finishes the active transaction (if any) with `status`, closing out every * in-flight child span in leaf-first order and returning the tx payload. - * `scope->span` / `scope->transaction_object` are preserved so a - * subsequently-captured crash event still inherits the active trace context. + * The scope span / transaction object are preserved so a subsequently-captured + * crash event still inherits the active trace context. * Returns null if nothing is active. */ sentry_value_t sentry__trace_finish(sentry_span_status_t status); diff --git a/tests/unit/test_attachments.c b/tests/unit/test_attachments.c index 066e8de8a2..73d50bcbee 100644 --- a/tests/unit/test_attachments.c +++ b/tests/unit/test_attachments.c @@ -17,7 +17,8 @@ static void add_scope_attachments(sentry_envelope_t *envelope) { SENTRY_WITH_SCOPE (scope) { - sentry__envelope_add_attachments(envelope, scope->attachments, NULL); + sentry__envelope_add_attachments( + envelope, sentry__scope_get_attachments(scope), NULL); } } diff --git a/tests/unit/test_basic.c b/tests/unit/test_basic.c index b08f2551ab..9e4a06667a 100644 --- a/tests/unit/test_basic.c +++ b/tests/unit/test_basic.c @@ -425,7 +425,8 @@ SENTRY_TEST(client_sdk_integrations) SENTRY_WITH_SCOPE (scope) { sentry_value_t integrations - = sentry_value_get_by_key(scope->client_sdk, "integrations"); + = sentry_value_get_by_key( + sentry__scope_get_client_sdk(scope), "integrations"); size_t integration_count = sentry_value_get_length(integrations); TEST_CHECK(integration_count > 0); TEST_CHECK_STRING_EQUAL( diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index 617e007559..a39a9146b9 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -2566,7 +2566,7 @@ SENTRY_TEST(scope_clone_preserves_data) sentry_scope_t *clone = sentry_scope_clone(scope); sentry_value_decref( - sentry__attachments_remove(scope->attachments, &attachment_id)); + sentry__scope_remove_attachment(scope, &attachment_id)); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( sentry__scope_get_tags(clone), "tag_key")), @@ -2595,11 +2595,13 @@ SENTRY_TEST(scope_clone_preserves_data) TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(clone), 1); // Attachments are deep-copied into an independent list. - TEST_CHECK_INT_EQUAL(sentry_value_get_length(clone->attachments), 1); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope->attachments), 0); - TEST_CHECK(clone->attachments._bits != scope->attachments._bits); + sentry_value_t clone_attachments = sentry__scope_get_attachments(clone); + sentry_value_t scope_attachments = sentry__scope_get_attachments(scope); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(clone_attachments), 1); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope_attachments), 0); + TEST_CHECK(clone_attachments._bits != scope_attachments._bits); sentry_value_t clone_attachment - = sentry_value_get_by_index(clone->attachments, 0); + = sentry_value_get_by_index(clone_attachments, 0); TEST_CHECK_STRING_EQUAL( sentry__attachment_get_filename(clone_attachment), "file.bin"); TEST_CHECK_INT_EQUAL(sentry__attachment_get_size(clone_attachment), 7); From 0828f8f161ee760e76aa0c3d73300243499150f0 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 6 Aug 2026 14:35:59 +0200 Subject: [PATCH 09/28] refactor(scope): hide direct scope data access Move scope payload access behind sentry_scope_data_t helpers so scope-level code no longer reaches through scope->data to individual fields. --- src/sentry_scope.c | 1522 +++++++++++++++++++++++++++------------ src/sentry_scope.h | 2 +- tests/unit/test_basic.c | 5 +- 3 files changed, 1048 insertions(+), 481 deletions(-) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 6ee7480d9d..c277367cd2 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -71,9 +71,8 @@ struct sentry_scope_data_s { static sentry_scope_t g_scope = { 0 }; static sentry_scope_data_t g_scope_data = { 0 }; -#define SCOPE_READ_LOCK(Scope) \ - for (const sentry_scope_data_t *_locked_data = (Scope)->data; \ - _locked_data; \ +#define DATA_READ_LOCK(Data) \ + for (const sentry_scope_data_t *_locked_data = (Data); _locked_data; \ sentry__rwlock_read_unlock((sentry_rwlock_t *)&_locked_data->rwlock), \ _locked_data = NULL) \ for (bool _locked_once \ @@ -82,8 +81,8 @@ static sentry_scope_data_t g_scope_data = { 0 }; true); \ _locked_once; _locked_once = false) -#define SCOPE_WRITE_LOCK(Scope) \ - for (sentry_scope_data_t *_locked_data = (Scope)->data; _locked_data; \ +#define DATA_WRITE_LOCK(Data) \ + for (sentry_scope_data_t *_locked_data = (Data); _locked_data; \ sentry__rwlock_write_unlock(&_locked_data->rwlock), \ _locked_data = NULL) \ for (bool _locked_once \ @@ -131,46 +130,6 @@ get_client_sdk(void) return client_sdk; } -static bool -data_set_by_key(sentry_scope_data_t *data, sentry_value_t *object, - const char *key, sentry_value_t value) -{ - sentry__rwlock_write_lock(&data->rwlock); - int rv = sentry_value_set_by_key(*object, key, value); - sentry__rwlock_write_unlock(&data->rwlock); - return rv == 0; -} - -static bool -data_set_by_key_n(sentry_scope_data_t *data, sentry_value_t *object, - const char *key, size_t key_len, sentry_value_t value) -{ - sentry__rwlock_write_lock(&data->rwlock); - int rv = sentry_value_set_by_key_n(*object, key, key_len, value); - sentry__rwlock_write_unlock(&data->rwlock); - return rv == 0; -} - -static bool -data_remove_by_key( - sentry_scope_data_t *data, sentry_value_t *object, const char *key) -{ - sentry__rwlock_write_lock(&data->rwlock); - int rv = sentry_value_remove_by_key(*object, key); - sentry__rwlock_write_unlock(&data->rwlock); - return rv == 0; -} - -static char * -data_remove_and_take_key_n(sentry_scope_data_t *data, sentry_value_t *object, - const char *key, size_t key_len) -{ - sentry__rwlock_write_lock(&data->rwlock); - char *rv = sentry__value_remove_and_take_key_n(*object, key, key_len); - sentry__rwlock_write_unlock(&data->rwlock); - return rv; -} - static bool data_update_context_by_key_n(sentry_scope_data_t *data, const char *key, size_t key_len, sentry_value_t value) @@ -228,46 +187,959 @@ init_data(sentry_scope_data_t *data) } static void -cleanup_data(sentry_scope_data_t *data) +cleanup_data(sentry_scope_data_t *data) +{ + sentry_value_decref(data->release); + sentry_value_decref(data->environment); + sentry_value_decref(data->transaction); + sentry_value_decref(data->fingerprint); + sentry_value_decref(data->user); + sentry_value_decref(data->tags); + sentry_value_decref(data->extra); + sentry_value_decref(data->attributes); + sentry_value_decref(data->contexts); + sentry_value_decref(data->propagation_context); + sentry__ringbuffer_free(data->breadcrumbs); + sentry_value_decref(data->dynamic_sampling_context); + sentry_value_decref(data->client_sdk); + sentry_value_decref(data->attachments); + sentry__transaction_decref(data->transaction_object); + sentry__span_decref(data->span); +} + +static sentry_scope_data_t * +new_data(void) +{ + sentry_scope_data_t *data = SENTRY_MAKE(sentry_scope_data_t); + if (data) { + sentry__rwlock_init(&data->rwlock); + init_data(data); + } + return data; +} + +static void +free_data(sentry_scope_data_t *data) +{ + if (!data) { + return; + } + cleanup_data(data); + sentry__rwlock_free(&data->rwlock); + sentry_free(data); +} + +static void +cleanup_global_data(sentry_scope_data_t *data) +{ + cleanup_data(data); + sentry__rwlock_free(&data->rwlock); +} + +static void +init_global_data(sentry_scope_data_t *data) +{ + sentry__value_replace(&data->user, sentry_value_new_object()); + sentry_value_set_by_key(data->contexts, "os", sentry__get_os_context()); + sentry__value_replace(&data->client_sdk, get_client_sdk()); +} + +static void +data_apply_options(sentry_scope_data_t *data, sentry_options_t *options) +{ + DATA_WRITE_LOCK(data) + { + if (options->sdk_name) { + sentry_value_t sdk_name + = sentry_value_new_string(options->sdk_name); + sentry_value_set_by_key(data->client_sdk, "name", sdk_name); + } + sentry_value_t integrations + = sentry_value_get_by_key(data->client_sdk, "integrations"); + for (size_t i = 0; i < options->num_integrations; i++) { + const char *name = options->integrations[i]->name; + if (!name) { + continue; + } + if (sentry_value_is_null(integrations)) { + integrations = sentry_value_new_list(); + sentry_value_set_by_key( + data->client_sdk, "integrations", integrations); + } + sentry_value_append(integrations, sentry_value_new_string(name)); + } + sentry_value_freeze(data->client_sdk); + generate_propagation_context(data->propagation_context); + sentry_value_decref(data->attachments); + data->attachments = options->attachments; + options->attachments = sentry_value_new_null(); + sentry__ringbuffer_set_max_size( + data->breadcrumbs, options->max_breadcrumbs); + } +} + +static void +clear_data(sentry_scope_data_t *data) +{ + DATA_WRITE_LOCK(data) + { + bool trace_managed = data->trace_managed; + sentry_value_t propagation_context + = sentry_value_incref(data->propagation_context); + sentry_value_t dynamic_sampling_context + = sentry_value_incref(data->dynamic_sampling_context); + + 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; + } +} + +static sentry_scope_data_t * +clone_data(const sentry_scope_data_t *source) +{ + sentry_scope_data_t *clone = SENTRY_MAKE(sentry_scope_data_t); + if (!clone) { + return NULL; + } + + sentry__rwlock_init(&clone->rwlock); + DATA_READ_LOCK(source) + { + clone->release = sentry__value_clone(source->release); + clone->environment = sentry__value_clone(source->environment); + clone->transaction = sentry__value_clone(source->transaction); + clone->fingerprint = sentry__value_clone(source->fingerprint); + clone->user = sentry__value_clone(source->user); + clone->tags = sentry__value_clone(source->tags); + clone->extra = sentry__value_clone(source->extra); + clone->attributes = sentry__value_clone(source->attributes); + clone->contexts = sentry__value_clone(source->contexts); + clone->propagation_context + = sentry__value_clone(source->propagation_context); + clone->breadcrumbs = sentry__ringbuffer_clone(source->breadcrumbs); + clone->dynamic_sampling_context + = sentry__value_clone(source->dynamic_sampling_context); + if (sentry_value_is_frozen(source->dynamic_sampling_context)) { + sentry_value_freeze(clone->dynamic_sampling_context); + } + clone->level = source->level; + clone->last_event_id = source->last_event_id; + clone->client_sdk = sentry__value_clone(source->client_sdk); + sentry__attachments_extend(&clone->attachments, source->attachments); + clone->transaction_object = source->transaction_object; + sentry__transaction_incref(clone->transaction_object); + clone->span = source->span; + sentry__span_incref(clone->span); + clone->trace_managed = source->trace_managed; + } + + return clone; +} + +static sentry_value_t +data_get_propagation_context(const sentry_scope_data_t *data) +{ + return data->propagation_context; +} + +static sentry_value_t +data_ref_propagation_context(const sentry_scope_data_t *data) +{ + sentry_value_t propagation_context = sentry_value_new_null(); + DATA_READ_LOCK(data) + { + propagation_context = sentry_value_incref(data->propagation_context); + } + return propagation_context; +} + +static sentry_value_t +data_get_trace_context(const sentry_scope_data_t *data) +{ + return sentry_value_get_by_key(data->propagation_context, "trace"); +} + +static void +data_set_propagation_context( + sentry_scope_data_t *data, const char *key, sentry_value_t value) +{ + DATA_WRITE_LOCK(data) + { + sentry_value_set_by_key(data->propagation_context, key, value); + } +} + +static void +data_regenerate_propagation_context(sentry_scope_data_t *data) +{ + DATA_WRITE_LOCK(data) + { + generate_propagation_context(data->propagation_context); + } +} + +static bool +data_is_trace_managed(const sentry_scope_data_t *data) +{ + bool managed = false; + DATA_READ_LOCK(data) { managed = data->trace_managed; } + return managed; +} + +static void +data_set_trace_managed(sentry_scope_data_t *data, bool managed) +{ + DATA_WRITE_LOCK(data) { data->trace_managed = managed; } +} + +static sentry_value_t +data_get_dsc(const sentry_scope_data_t *data) +{ + return data->dynamic_sampling_context; +} + +static void +data_freeze_dsc(sentry_scope_data_t *data, sentry_value_t incoming) +{ + sentry_value_t dsc = sentry_value_new_object(); + sentry__value_merge_objects(dsc, incoming); + sentry_value_freeze(dsc); + DATA_WRITE_LOCK(data) + { + sentry__value_replace(&data->dynamic_sampling_context, dsc); + } +} + +static void +data_update_dsc(sentry_scope_data_t *data, const sentry_options_t *options) +{ + sentry_value_t dsc = sentry_value_new_object(); + + if (options->dsn) { + sentry_value_set_by_key(dsc, "public_key", + sentry_value_new_string(options->dsn->public_key)); + } + const char *org_id = sentry__options_get_org_id(options); + if (org_id) { + sentry_value_set_by_key(dsc, "org_id", sentry_value_new_string(org_id)); + } + sentry_value_set_by_key(dsc, "sample_rate", + sentry_value_new_double(options->traces_sample_rate)); + if (options->traces_sampler) { + sentry_value_set_by_key( + dsc, "sample_rate", sentry_value_new_double(1.0)); + } + + DATA_WRITE_LOCK(data) + { + sentry_value_t sample_rand = sentry_value_get_by_key( + sentry_value_get_by_key(data->propagation_context, "trace"), + "sample_rand"); + sentry_value_set_by_key( + dsc, "sample_rand", sentry_value_incref(sample_rand)); + sentry_value_set_by_key( + dsc, "release", sentry_value_incref(data->release)); + sentry_value_set_by_key( + dsc, "environment", sentry_value_incref(data->environment)); + sentry__value_replace(&data->dynamic_sampling_context, dsc); + } +} + +static sentry_value_t +data_ref_client_sdk(const sentry_scope_data_t *data) +{ + sentry_value_t client_sdk = sentry_value_new_null(); + DATA_READ_LOCK(data) { client_sdk = sentry_value_incref(data->client_sdk); } + return client_sdk; +} + +static void +data_apply_tags_and_extra(const sentry_scope_data_t *data, sentry_value_t event) +{ + DATA_READ_LOCK(data) + { + sentry_value_t event_tags = sentry_value_get_by_key(event, "tags"); + if (sentry_value_is_null(event_tags)) { + if (!sentry_value_is_null(data->tags)) { + sentry_value_set_by_key( + event, "tags", sentry__value_clone(data->tags)); + } + } else { + sentry__value_merge_objects(event_tags, data->tags); + } + + sentry_value_t event_extra = sentry_value_get_by_key(event, "extra"); + if (sentry_value_is_null(event_extra)) { + if (!sentry_value_is_null(data->extra)) { + sentry_value_set_by_key( + event, "extra", sentry__value_clone(data->extra)); + } + } else { + sentry__value_merge_objects(event_extra, data->extra); + } + } +} + +static sentry_value_t +data_clone_contexts(const sentry_scope_data_t *data) +{ + sentry_value_t contexts = sentry_value_new_null(); + DATA_READ_LOCK(data) { contexts = sentry__value_clone(data->contexts); } + return contexts; +} + +static sentry_value_t +data_ref_span_or_transaction(const sentry_scope_data_t *data) +{ + sentry_value_t value = sentry_value_new_null(); + DATA_READ_LOCK(data) + { + if (data->span) { + value = sentry_value_incref(data->span->inner); + } else if (data->transaction_object) { + value = sentry_value_incref(data->transaction_object->inner); + } + } + return value; +} + +static sentry_value_t +data_breadcrumbs_to_list(const sentry_scope_data_t *data) +{ + sentry_value_t breadcrumbs = sentry_value_new_null(); + DATA_READ_LOCK(data) + { + breadcrumbs = sentry__ringbuffer_to_list(data->breadcrumbs); + } + return breadcrumbs; +} + +static bool +data_add_breadcrumb(sentry_scope_data_t *data, sentry_value_t breadcrumb) +{ + bool added = false; + DATA_WRITE_LOCK(data) + { + added = sentry__ringbuffer_append(data->breadcrumbs, breadcrumb) == 0; + } + return added; +} + +static const sentry_ringbuffer_t * +data_get_breadcrumbs(const sentry_scope_data_t *data) +{ + return data->breadcrumbs; +} + +static sentry_value_t +data_ref_user(const sentry_scope_data_t *data) +{ + sentry_value_t user = sentry_value_new_null(); + DATA_READ_LOCK(data) { user = sentry_value_incref(data->user); } + return user; +} + +static void +data_set_user(sentry_scope_data_t *data, sentry_value_t user) +{ + DATA_WRITE_LOCK(data) + { + sentry__value_replace(&data->user, sentry_value_incref(user)); + } +} + +static sentry_value_t +data_get_tags(const sentry_scope_data_t *data) +{ + return data->tags; +} + +static bool +data_set_tag(sentry_scope_data_t *data, const char *key, sentry_value_t value) +{ + bool did_set = false; + DATA_WRITE_LOCK(data) + { + did_set = sentry_value_set_by_key(data->tags, key, value) == 0; + } + return did_set; +} + +static bool +data_set_tag_n(sentry_scope_data_t *data, const char *key, size_t key_len, + sentry_value_t value) +{ + bool did_set = false; + DATA_WRITE_LOCK(data) + { + did_set + = sentry_value_set_by_key_n(data->tags, key, key_len, value) == 0; + } + return did_set; +} + +static bool +data_remove_tag(sentry_scope_data_t *data, const char *key) +{ + bool removed = false; + DATA_WRITE_LOCK(data) + { + removed = sentry_value_remove_by_key(data->tags, key) == 0; + } + return removed; +} + +static char * +data_remove_tag_n(sentry_scope_data_t *data, const char *key, size_t key_len) +{ + char *removed_key = NULL; + DATA_WRITE_LOCK(data) + { + removed_key + = sentry__value_remove_and_take_key_n(data->tags, key, key_len); + } + return removed_key; +} + +static sentry_value_t +data_get_extra(const sentry_scope_data_t *data) +{ + return data->extra; +} + +static bool +data_set_extra(sentry_scope_data_t *data, const char *key, sentry_value_t value) +{ + bool did_set = false; + DATA_WRITE_LOCK(data) + { + did_set = sentry_value_set_by_key(data->extra, key, value) == 0; + } + return did_set; +} + +static bool +data_set_extra_n(sentry_scope_data_t *data, const char *key, size_t key_len, + sentry_value_t value) +{ + bool did_set = false; + DATA_WRITE_LOCK(data) + { + did_set + = sentry_value_set_by_key_n(data->extra, key, key_len, value) == 0; + } + return did_set; +} + +static bool +data_remove_extra(sentry_scope_data_t *data, const char *key) +{ + bool removed = false; + DATA_WRITE_LOCK(data) + { + removed = sentry_value_remove_by_key(data->extra, key) == 0; + } + return removed; +} + +static char * +data_remove_extra_n(sentry_scope_data_t *data, const char *key, size_t key_len) +{ + char *removed_key = NULL; + DATA_WRITE_LOCK(data) + { + removed_key + = sentry__value_remove_and_take_key_n(data->extra, key, key_len); + } + return removed_key; +} + +static sentry_value_t +data_get_attributes(const sentry_scope_data_t *data) +{ + return data->attributes; +} + +static bool +data_set_attribute_n(sentry_scope_data_t *data, const char *key, size_t key_len, + sentry_value_t attribute) +{ + bool did_set = false; + DATA_WRITE_LOCK(data) + { + did_set = sentry_value_set_by_key_n( + data->attributes, key, key_len, attribute) + == 0; + } + return did_set; +} + +static void +data_remove_attribute(sentry_scope_data_t *data, const char *key) +{ + DATA_WRITE_LOCK(data) { sentry_value_remove_by_key(data->attributes, key); } +} + +static void +data_remove_attribute_n( + sentry_scope_data_t *data, const char *key, size_t key_len) +{ + DATA_WRITE_LOCK(data) + { + sentry_value_remove_by_key_n(data->attributes, key, key_len); + } +} + +static sentry_value_t +data_get_contexts(const sentry_scope_data_t *data) +{ + return data->contexts; +} + +static bool +data_set_context( + sentry_scope_data_t *data, const char *key, sentry_value_t value) +{ + bool did_set = false; + DATA_WRITE_LOCK(data) + { + did_set = sentry_value_set_by_key(data->contexts, key, value) == 0; + } + return did_set; +} + +static bool +data_set_context_n(sentry_scope_data_t *data, const char *key, size_t key_len, + sentry_value_t value) +{ + bool did_set = false; + DATA_WRITE_LOCK(data) + { + did_set = sentry_value_set_by_key_n(data->contexts, key, key_len, value) + == 0; + } + return did_set; +} + +static bool +data_update_context_n(sentry_scope_data_t *data, const char *key, + size_t key_len, sentry_value_t value) +{ + return data_update_context_by_key_n(data, key, key_len, value); +} + +static bool +data_remove_context(sentry_scope_data_t *data, const char *key) +{ + bool removed = false; + DATA_WRITE_LOCK(data) + { + removed = sentry_value_remove_by_key(data->contexts, key) == 0; + } + return removed; +} + +static char * +data_remove_context_n( + sentry_scope_data_t *data, const char *key, size_t key_len) +{ + char *removed_key = NULL; + DATA_WRITE_LOCK(data) + { + removed_key + = sentry__value_remove_and_take_key_n(data->contexts, key, key_len); + } + return removed_key; +} + +static sentry_value_t +data_ref_fingerprint(const sentry_scope_data_t *data) +{ + sentry_value_t fingerprint = sentry_value_new_null(); + DATA_READ_LOCK(data) + { + fingerprint = sentry_value_incref(data->fingerprint); + } + return fingerprint; +} + +static void +data_set_fingerprint(sentry_scope_data_t *data, sentry_value_t fingerprint) +{ + DATA_WRITE_LOCK(data) + { + sentry__value_replace( + &data->fingerprint, sentry_value_incref(fingerprint)); + } +} + +static sentry_level_t +data_get_level(const sentry_scope_data_t *data) +{ + sentry_level_t level = SENTRY_LEVEL_ERROR; + DATA_READ_LOCK(data) { level = data->level; } + return level; +} + +static void +data_set_level(sentry_scope_data_t *data, sentry_level_t level) +{ + DATA_WRITE_LOCK(data) { data->level = level; } +} + +static sentry_uuid_t +data_get_last_event_id(const sentry_scope_data_t *data) +{ + sentry_uuid_t event_id = sentry_uuid_nil(); + DATA_READ_LOCK(data) { event_id = data->last_event_id; } + return event_id; +} + +static void +data_set_last_event_id(sentry_scope_data_t *data, sentry_uuid_t event_id) +{ + DATA_WRITE_LOCK(data) { data->last_event_id = event_id; } +} + +static sentry_value_t +data_get_attachments(const sentry_scope_data_t *data) +{ + return data->attachments; +} + +static sentry_value_t +data_add_attachment(sentry_scope_data_t *data, sentry_value_t attachment, + bool *did_add) +{ + sentry_value_t added = sentry_value_new_null(); + DATA_WRITE_LOCK(data) + { + size_t len = sentry_value_get_length(data->attachments); + added = sentry__attachments_add(&data->attachments, attachment); + *did_add = !sentry_value_is_null(added) + && sentry_value_get_length(data->attachments) > len; + } + return added; +} + +static sentry_value_t +data_remove_attachment( + sentry_scope_data_t *data, const sentry_uuid_t *attachment_id) +{ + sentry_value_t removed = sentry_value_new_null(); + DATA_WRITE_LOCK(data) + { + removed = sentry__attachments_remove(data->attachments, attachment_id); + } + return removed; +} + +static sentry_value_t +data_take_attachments(sentry_scope_data_t *data) +{ + sentry_value_t attachments = sentry_value_new_null(); + DATA_WRITE_LOCK(data) + { + attachments = data->attachments; + data->attachments = sentry__attachments_new(); + } + return attachments; +} + +static sentry_transaction_t * +data_get_transaction_object(const sentry_scope_data_t *data) +{ + sentry_transaction_t *transaction = NULL; + DATA_READ_LOCK(data) { transaction = data->transaction_object; } + return transaction; +} + +static bool +value_has_span_id(sentry_value_t value, const char *span_id) +{ + const char *value_span_id + = sentry_value_as_string(sentry_value_get_by_key(value, "span_id")); + return sentry__string_eq(value_span_id, span_id); +} + +static void +data_set_transaction_object( + sentry_scope_data_t *data, sentry_transaction_t *transaction) +{ + sentry__transaction_incref(transaction); + DATA_WRITE_LOCK(data) + { + sentry__span_decref(data->span); + data->span = NULL; + sentry__transaction_decref(data->transaction_object); + data->transaction_object = transaction; + } +} + +static bool +data_remove_transaction_object( + sentry_scope_data_t *data, sentry_transaction_t *transaction) +{ + bool removed = false; + DATA_WRITE_LOCK(data) + { + if (transaction && data->transaction_object == transaction) { + data->transaction_object = NULL; + removed = true; + } + } + if (removed) { + sentry__transaction_decref(transaction); + } + return removed; +} + +static bool +data_remove_transaction_value( + sentry_scope_data_t *data, sentry_value_t transaction) +{ + const char *span_id = sentry_value_as_string( + sentry_value_get_by_key(transaction, "span_id")); + sentry_transaction_t *transaction_object = NULL; + DATA_WRITE_LOCK(data) + { + if (data->transaction_object + && value_has_span_id(data->transaction_object->inner, span_id)) { + transaction_object = data->transaction_object; + data->transaction_object = NULL; + } + } + sentry__transaction_decref(transaction_object); + return transaction_object != NULL; +} + +static bool +data_restore_transaction_object( + sentry_scope_data_t *data, sentry_transaction_t *transaction) +{ + bool restored = false; + DATA_WRITE_LOCK(data) + { + if (!data->transaction_object && transaction) { + data->transaction_object = transaction; + restored = true; + } + } + return restored; +} + +static sentry_span_t * +data_get_span(const sentry_scope_data_t *data) +{ + sentry_span_t *span = NULL; + DATA_READ_LOCK(data) { span = data->span; } + return span; +} + +static void +data_set_span(sentry_scope_data_t *data, sentry_span_t *span) +{ + sentry__span_incref(span); + DATA_WRITE_LOCK(data) + { + sentry__transaction_decref(data->transaction_object); + data->transaction_object = NULL; + sentry__span_decref(data->span); + data->span = span; + } +} + +static bool +data_remove_span(sentry_scope_data_t *data, sentry_span_t *span) +{ + bool removed = false; + DATA_WRITE_LOCK(data) + { + if (span && data->span == span) { + data->span = NULL; + removed = true; + } + } + if (removed) { + sentry__span_decref(span); + } + return removed; +} + +static bool +data_remove_span_value(sentry_scope_data_t *data, sentry_value_t span) +{ + const char *span_id + = sentry_value_as_string(sentry_value_get_by_key(span, "span_id")); + sentry_span_t *scope_span = NULL; + DATA_WRITE_LOCK(data) + { + if (data->span && value_has_span_id(data->span->inner, span_id)) { + scope_span = data->span; + data->span = NULL; + } + } + sentry__span_decref(scope_span); + return scope_span != NULL; +} + +static bool +data_restore_span(sentry_scope_data_t *data, sentry_span_t *span) +{ + bool restored = false; + DATA_WRITE_LOCK(data) + { + if (!data->span && span) { + data->span = span; + restored = true; + } + } + return restored; +} + +static sentry_value_t +data_ref_release(const sentry_scope_data_t *data) +{ + sentry_value_t release = sentry_value_new_null(); + DATA_READ_LOCK(data) { release = sentry_value_incref(data->release); } + return release; +} + +static void +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)); + } +} + +static sentry_value_t +data_ref_environment(const sentry_scope_data_t *data) +{ + sentry_value_t environment = sentry_value_new_null(); + DATA_READ_LOCK(data) + { + environment = sentry_value_incref(data->environment); + } + return environment; +} + +static void +data_set_environment(sentry_scope_data_t *data, sentry_value_t value) { - sentry_value_decref(data->release); - sentry_value_decref(data->environment); - sentry_value_decref(data->transaction); - sentry_value_decref(data->fingerprint); - sentry_value_decref(data->user); - sentry_value_decref(data->tags); - sentry_value_decref(data->extra); - sentry_value_decref(data->attributes); - sentry_value_decref(data->contexts); - sentry_value_decref(data->propagation_context); - sentry__ringbuffer_free(data->breadcrumbs); - sentry_value_decref(data->dynamic_sampling_context); - sentry_value_decref(data->client_sdk); - sentry_value_decref(data->attachments); - sentry__transaction_decref(data->transaction_object); - sentry__span_decref(data->span); + DATA_WRITE_LOCK(data) + { + sentry__value_replace(&data->environment, sentry_value_incref(value)); + sentry_value_set_by_key(data->dynamic_sampling_context, "environment", + sentry_value_incref(value)); + } } -static sentry_scope_data_t * -new_data(void) +static sentry_value_t +data_ref_transaction(const sentry_scope_data_t *data) { - sentry_scope_data_t *data = SENTRY_MAKE(sentry_scope_data_t); - if (data) { - sentry__rwlock_init(&data->rwlock); - init_data(data); + sentry_value_t transaction = sentry_value_new_null(); + DATA_READ_LOCK(data) + { + transaction = sentry_value_incref(data->transaction); } - return data; + return transaction; } static void -free_data(sentry_scope_data_t *data) +data_set_transaction(sentry_scope_data_t *data, sentry_value_t value, + const char *transaction, size_t transaction_len) { - if (!data) { - return; + DATA_WRITE_LOCK(data) + { + sentry__value_replace(&data->transaction, sentry_value_incref(value)); + if (data->transaction_object) { + sentry_transaction_set_name_n( + data->transaction_object, transaction, transaction_len); + } + } +} + +static void +data_apply_to_telemetry(const sentry_scope_data_t *data, + sentry_value_t telemetry, sentry_value_t attributes) +{ + sentry_value_t os_name = sentry_value_new_null(); + sentry_value_t os_version = sentry_value_new_null(); + + DATA_READ_LOCK(data) + { + sentry__value_merge_objects_shallow(attributes, data->attributes); + + sentry_value_t trace_id = sentry_value_get_by_key( + sentry_value_get_by_key(data->propagation_context, "trace"), + "trace_id"); + + sentry_value_t parent_span_id = sentry_value_new_object(); + bool has_parent_span = false; + if (data->transaction_object) { + sentry_value_t span_id = sentry_value_get_by_key( + data->transaction_object->inner, "span_id"); + sentry_value_set_by_key( + parent_span_id, "value", sentry_value_incref(span_id)); + trace_id = sentry_value_get_by_key( + data->transaction_object->inner, "trace_id"); + has_parent_span = true; + } else if (data->span) { + sentry_value_t span_id + = sentry_value_get_by_key(data->span->inner, "span_id"); + sentry_value_set_by_key( + parent_span_id, "value", sentry_value_incref(span_id)); + trace_id = sentry_value_get_by_key(data->span->inner, "trace_id"); + has_parent_span = true; + } + sentry_value_set_by_key( + parent_span_id, "type", sentry_value_new_string("string")); + if (has_parent_span + && sentry_value_is_null(sentry_value_get_by_key( + attributes, "sentry.trace.parent_span_id"))) { + sentry_value_set_by_key( + attributes, "sentry.trace.parent_span_id", parent_span_id); + } else { + sentry_value_decref(parent_span_id); + } + if (!sentry_value_is_null(trace_id) + && sentry_value_is_null( + sentry_value_get_by_key(telemetry, "trace_id"))) { + sentry_value_set_by_key( + telemetry, "trace_id", sentry_value_incref(trace_id)); + } + + sentry_value_t os_context + = sentry_value_get_by_key(data->contexts, "os"); + if (!sentry_value_is_null(os_context)) { + os_name = sentry_value_incref( + sentry_value_get_by_key(os_context, "name")); + os_version = sentry_value_incref( + sentry_value_get_by_key(os_context, "version")); + } + } + + if (!sentry_value_is_null(os_name)) { + sentry__value_add_attribute(attributes, os_name, "string", "os.name"); + } else { + sentry_value_decref(os_name); + } + if (!sentry_value_is_null(os_version)) { + sentry__value_add_attribute( + attributes, os_version, "string", "os.version"); + } else { + sentry_value_decref(os_version); } - cleanup_data(data); - sentry__rwlock_free(&data->rwlock); - sentry_free(data); } static bool @@ -299,10 +1171,7 @@ get_scope(void) if (!init_scope(&g_scope, &g_scope_data)) { return &g_scope; } - g_scope.data->user = sentry_value_new_object(); - sentry_value_set_by_key( - g_scope.data->contexts, "os", sentry__get_os_context()); - g_scope.data->client_sdk = get_client_sdk(); + init_global_data(g_scope.data); g_scope_initialized = true; @@ -336,8 +1205,7 @@ sentry__scope_cleanup(void) sentry__mutex_lock(&g_lock); if (g_scope_initialized) { g_scope_initialized = false; - cleanup_data(g_scope.data); - sentry__rwlock_free(&g_scope.data->rwlock); + cleanup_global_data(g_scope.data); g_scope.data = NULL; cleanup_observers(&g_scope); } @@ -548,22 +1416,11 @@ sentry_local_scope_new(void) void sentry__scope_apply_options(sentry_scope_t *scope, sentry_options_t *options) { - if (options->sdk_name) { - sentry_value_t sdk_name = sentry_value_new_string(options->sdk_name); - sentry_value_set_by_key(scope->data->client_sdk, "name", sdk_name); - } - sentry_value_freeze(scope->data->client_sdk); - generate_propagation_context(scope->data->propagation_context); + data_apply_options(scope->data, options); sentry__scope_set_release_n( scope, options->release, sentry__guarded_strlen(options->release)); sentry__scope_set_environment_n(scope, options->environment, sentry__guarded_strlen(options->environment)); - scope->data->attachments = options->attachments; - options->attachments = NULL; - - sentry__ringbuffer_set_max_size( - scope->data->breadcrumbs, options->max_breadcrumbs); - sentry__scope_update_dsc(scope, options); } @@ -583,25 +1440,7 @@ sentry_scope_clear(sentry_scope_t *scope) } sentry__scope_end_notify(scope); - // Keep the propagation and dynamic sampling contexts across clears so - // telemetry captured afterwards continues on the same trace. - sentry_scope_data_t *data = scope->data; - sentry__rwlock_write_lock(&data->rwlock); - bool trace_managed = data->trace_managed; - sentry_value_t propagation_context - = sentry_value_incref(data->propagation_context); - sentry_value_t dynamic_sampling_context - = sentry_value_incref(data->dynamic_sampling_context); - - 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; - sentry__rwlock_write_unlock(&data->rwlock); + clear_data(scope->data); } sentry_scope_t * @@ -616,48 +1455,11 @@ sentry_scope_clone(const sentry_scope_t *scope) return NULL; } - clone->data = SENTRY_MAKE(sentry_scope_data_t); + clone->data = clone_data(scope->data); if (!clone->data) { sentry_free(clone); return NULL; } - sentry__rwlock_init(&clone->data->rwlock); - - SCOPE_READ_LOCK(scope) - { - clone->data->release = sentry__value_clone(scope->data->release); - clone->data->environment - = sentry__value_clone(scope->data->environment); - clone->data->transaction - = sentry__value_clone(scope->data->transaction); - clone->data->fingerprint - = sentry__value_clone(scope->data->fingerprint); - clone->data->user = sentry__value_clone(scope->data->user); - clone->data->tags = sentry__value_clone(scope->data->tags); - clone->data->extra = sentry__value_clone(scope->data->extra); - clone->data->contexts = sentry__value_clone(scope->data->contexts); - clone->data->attributes = sentry__value_clone(scope->data->attributes); - clone->data->propagation_context - = sentry__value_clone(scope->data->propagation_context); - clone->data->breadcrumbs - = sentry__ringbuffer_clone(scope->data->breadcrumbs); - clone->data->dynamic_sampling_context - = sentry__value_clone(scope->data->dynamic_sampling_context); - if (sentry_value_is_frozen(scope->data->dynamic_sampling_context)) { - sentry_value_freeze(clone->data->dynamic_sampling_context); - } - clone->data->level = scope->data->level; - clone->data->last_event_id = scope->data->last_event_id; - clone->data->client_sdk = sentry__value_clone(scope->data->client_sdk); - sentry__attachments_extend( - &clone->data->attachments, scope->data->attachments); - - clone->data->transaction_object = scope->data->transaction_object; - sentry__transaction_incref(clone->data->transaction_object); - clone->data->span = scope->data->span; - sentry__span_incref(clone->data->span); - clone->data->trace_managed = scope->data->trace_managed; - } return clone; } @@ -665,88 +1467,56 @@ sentry_scope_clone(const sentry_scope_t *scope) sentry_value_t sentry__scope_get_propagation_context(const sentry_scope_t *scope) { - return scope->data->propagation_context; + return data_get_propagation_context(scope->data); } sentry_value_t sentry__scope_get_trace_context(const sentry_scope_t *scope) { - return sentry_value_get_by_key(scope->data->propagation_context, "trace"); + return data_get_trace_context(scope->data); } void sentry__scope_set_propagation_context( sentry_scope_t *scope, const char *key, sentry_value_t value) { - sentry_value_set_by_key(scope->data->propagation_context, key, value); + data_set_propagation_context(scope->data, key, value); } void sentry__scope_regenerate_propagation_context(sentry_scope_t *scope) { - generate_propagation_context(scope->data->propagation_context); + data_regenerate_propagation_context(scope->data); } bool sentry__scope_is_trace_managed(const sentry_scope_t *scope) { - bool managed = false; - SCOPE_READ_LOCK(scope) { managed = scope->data->trace_managed; } - return managed; + return data_is_trace_managed(scope->data); } void sentry__scope_set_trace_managed(sentry_scope_t *scope, bool managed) { - SCOPE_WRITE_LOCK(scope) { scope->data->trace_managed = managed; } + data_set_trace_managed(scope->data, managed); } sentry_value_t sentry__scope_get_dsc(const sentry_scope_t *scope) { - return scope->data->dynamic_sampling_context; + return data_get_dsc(scope->data); } void sentry__scope_freeze_dsc(sentry_scope_t *scope, sentry_value_t incoming) { - sentry_value_decref(scope->data->dynamic_sampling_context); - sentry_value_t dsc = sentry_value_new_object(); - sentry__value_merge_objects(dsc, incoming); - sentry_value_freeze(dsc); - scope->data->dynamic_sampling_context = dsc; + data_freeze_dsc(scope->data, incoming); } void sentry__scope_update_dsc(sentry_scope_t *scope, const sentry_options_t *options) { - sentry_value_decref(scope->data->dynamic_sampling_context); - sentry_value_t dsc = sentry_value_new_object(); - - if (options->dsn) { - sentry_value_set_by_key(dsc, "public_key", - sentry_value_new_string(options->dsn->public_key)); - } - const char *org_id = sentry__options_get_org_id(options); - if (org_id) { - sentry_value_set_by_key(dsc, "org_id", sentry_value_new_string(org_id)); - } - sentry_value_set_by_key(dsc, "sample_rate", - sentry_value_new_double(options->traces_sample_rate)); - if (options->traces_sampler) { - sentry_value_set_by_key( - dsc, "sample_rate", sentry_value_new_double(1.0)); - } - sentry_value_t sample_rand = sentry_value_get_by_key( - sentry_value_get_by_key(scope->data->propagation_context, "trace"), - "sample_rand"); - sentry_value_set_by_key(dsc, "sample_rand", sample_rand); - sentry_value_incref(sample_rand); - sentry_value_set_by_key(dsc, "release", sentry__scope_ref_release(scope)); - sentry_value_set_by_key( - dsc, "environment", sentry__scope_ref_environment(scope)); - - scope->data->dynamic_sampling_context = dsc; + data_update_dsc(scope->data, options); } #if !defined(SENTRY_PLATFORM_NX) @@ -852,25 +1622,28 @@ sentry__symbolize_stacktrace(sentry_value_t stacktrace) } #endif +#ifdef SENTRY_UNITTEST static sentry_value_t -get_span_or_transaction(const sentry_scope_t *scope) +data_get_span_or_transaction(const sentry_scope_data_t *data) { - if (scope->data->span) { - return scope->data->span->inner; - } else if (scope->data->transaction_object) { - return scope->data->transaction_object->inner; - } else { - return sentry_value_new_null(); + sentry_value_t value = sentry_value_new_null(); + DATA_READ_LOCK(data) + { + if (data->span) { + value = data->span->inner; + } else if (data->transaction_object) { + value = data->transaction_object->inner; + } } + return value; } -#ifdef SENTRY_UNITTEST sentry_value_t sentry__scope_get_span_or_transaction(void) { sentry_value_t result = sentry_value_new_null(); SENTRY_WITH_SCOPE (scope) { - result = get_span_or_transaction(scope); + result = data_get_span_or_transaction(scope->data); } return result; } @@ -961,35 +1734,14 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, PLACE_STRING_VALUE("transaction", transaction); sentry_value_decref(transaction); - PLACE_VALUE("sdk", scope->data->client_sdk); - - SCOPE_READ_LOCK(scope) - { - sentry_value_t event_tags = sentry_value_get_by_key(event, "tags"); - if (sentry_value_is_null(event_tags)) { - if (!sentry_value_is_null(scope->data->tags)) { - PLACE_CLONED_VALUE("tags", scope->data->tags); - } - } else { - sentry__value_merge_objects(event_tags, scope->data->tags); - } + sentry_value_t client_sdk = data_ref_client_sdk(scope->data); + PLACE_VALUE("sdk", client_sdk); + sentry_value_decref(client_sdk); - sentry_value_t event_extra = sentry_value_get_by_key(event, "extra"); - if (sentry_value_is_null(event_extra)) { - if (!sentry_value_is_null(scope->data->extra)) { - PLACE_CLONED_VALUE("extra", scope->data->extra); - } - } else { - sentry__value_merge_objects(event_extra, scope->data->extra); - } - } + data_apply_tags_and_extra(scope->data, event); bool is_transaction = sentry__event_is_transaction(event); - sentry_value_t contexts = sentry_value_new_null(); - SCOPE_READ_LOCK(scope) - { - contexts = sentry__value_clone(scope->data->contexts); - } + sentry_value_t contexts = data_clone_contexts(scope->data); if (is_transaction && !sentry_value_is_null(contexts)) { sentry_value_remove_by_key(contexts, "trace"); } @@ -999,7 +1751,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, sentry_value_t scoped_txn_or_span = sentry_value_new_null(); sentry_value_t scope_trace = sentry_value_new_null(); if (!is_transaction) { - scoped_txn_or_span = get_span_or_transaction(scope); + scoped_txn_or_span = data_ref_span_or_transaction(scope->data); scope_trace = sentry__value_get_trace_context(scoped_txn_or_span); } if (!sentry_value_is_null(scope_trace)) { @@ -1015,6 +1767,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, } sentry_value_set_by_key(contexts, "trace", scope_trace); } + sentry_value_decref(scoped_txn_or_span); // merge contexts sourced from scope into the event sentry_value_t event_contexts = sentry_value_get_by_key(event, "contexts"); @@ -1022,7 +1775,10 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, if (!is_transaction && sentry_value_is_null(scope_trace) && sentry_value_is_null( sentry_value_get_by_key(event_contexts, "trace"))) { - sentry__value_merge_objects(contexts, scope->data->propagation_context); + sentry_value_t propagation_context + = data_ref_propagation_context(scope->data); + sentry__value_merge_objects(contexts, propagation_context); + sentry_value_decref(propagation_context); } if (sentry_value_is_null(event_contexts)) { PLACE_VALUE("contexts", contexts); @@ -1035,7 +1791,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, sentry_value_t event_breadcrumbs = sentry_value_get_by_key(event, "breadcrumbs"); sentry_value_t scope_breadcrumbs - = sentry__ringbuffer_to_list(scope->data->breadcrumbs); + = data_breadcrumbs_to_list(scope->data); sentry_value_set_by_key(event, "breadcrumbs", sentry__value_merge_breadcrumbs(event_breadcrumbs, scope_breadcrumbs, options->max_breadcrumbs)); @@ -1068,7 +1824,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, void sentry_scope_add_breadcrumb(sentry_scope_t *scope, sentry_value_t breadcrumb) { - if (sentry__ringbuffer_append(scope->data->breadcrumbs, breadcrumb) == 0) { + if (data_add_breadcrumb(scope->data, breadcrumb)) { SENTRY_SCOPE_NOTIFY(scope, add_breadcrumb, breadcrumb); } } @@ -1076,38 +1832,32 @@ sentry_scope_add_breadcrumb(sentry_scope_t *scope, sentry_value_t breadcrumb) const sentry_ringbuffer_t * sentry__scope_get_breadcrumbs(const sentry_scope_t *scope) { - return scope->data->breadcrumbs; + return data_get_breadcrumbs(scope->data); } sentry_value_t sentry__scope_ref_user(const sentry_scope_t *scope) { - sentry_value_t user = sentry_value_new_null(); - SCOPE_READ_LOCK(scope) { user = sentry_value_incref(scope->data->user); } - return user; + return data_ref_user(scope->data); } void sentry_scope_set_user(sentry_scope_t *scope, sentry_value_t user) { - SCOPE_WRITE_LOCK(scope) - { - sentry__value_replace(&scope->data->user, sentry_value_incref(user)); - } + data_set_user(scope->data, user); SENTRY_SCOPE_NOTIFY_OWNED(scope, set_user, user); } sentry_value_t sentry__scope_get_tags(const sentry_scope_t *scope) { - return scope->data->tags; + return data_get_tags(scope->data); } void sentry_scope_set_tag(sentry_scope_t *scope, const char *key, const char *value) { - if (data_set_by_key(scope->data, &scope->data->tags, key, - sentry_value_new_string(value))) { + if (data_set_tag(scope->data, key, sentry_value_new_string(value))) { SENTRY_SCOPE_NOTIFY(scope, set_tag, key, value); } } @@ -1123,8 +1873,8 @@ sentry_scope_set_tag_n(sentry_scope_t *scope, const char *key, size_t key_len, return; } - if (data_set_by_key_n(scope->data, &scope->data->tags, key, key_len, - sentry_value_incref(tag_value))) { + if (data_set_tag_n( + scope->data, key, key_len, sentry_value_incref(tag_value))) { SENTRY_SCOPE_NOTIFY( scope, set_tag, notify_key, sentry_value_as_string(tag_value)); } @@ -1153,7 +1903,7 @@ sentry_scope_set_tags(sentry_scope_t *scope, sentry_value_t tags) void sentry__scope_remove_tag(sentry_scope_t *scope, const char *key) { - if (data_remove_by_key(scope->data, &scope->data->tags, key)) { + if (data_remove_tag(scope->data, key)) { SENTRY_SCOPE_NOTIFY(scope, remove_tag, key); } } @@ -1162,8 +1912,7 @@ void sentry__scope_remove_tag_n( sentry_scope_t *scope, const char *key, size_t key_len) { - char *k = data_remove_and_take_key_n( - scope->data, &scope->data->tags, key, key_len); + char *k = data_remove_tag_n(scope->data, key, key_len); if (k) { SENTRY_SCOPE_NOTIFY(scope, remove_tag, k); } @@ -1173,15 +1922,14 @@ sentry__scope_remove_tag_n( sentry_value_t sentry__scope_get_extra(const sentry_scope_t *scope) { - return scope->data->extra; + return data_get_extra(scope->data); } void sentry_scope_set_extra( sentry_scope_t *scope, const char *key, sentry_value_t value) { - if (data_set_by_key(scope->data, &scope->data->extra, key, - sentry_value_incref(value))) { + if (data_set_extra(scope->data, key, sentry_value_incref(value))) { SENTRY_SCOPE_NOTIFY(scope, set_extra, key, value); } sentry_value_decref(value); @@ -1197,8 +1945,8 @@ sentry_scope_set_extra_n(sentry_scope_t *scope, const char *key, size_t key_len, return; } - if (data_set_by_key_n(scope->data, &scope->data->extra, key, key_len, - sentry_value_incref(value))) { + if (data_set_extra_n( + scope->data, key, key_len, sentry_value_incref(value))) { SENTRY_SCOPE_NOTIFY(scope, set_extra, notify_key, value); } sentry_free(notify_key); @@ -1208,7 +1956,7 @@ sentry_scope_set_extra_n(sentry_scope_t *scope, const char *key, size_t key_len, void sentry__scope_remove_extra(sentry_scope_t *scope, const char *key) { - if (data_remove_by_key(scope->data, &scope->data->extra, key)) { + if (data_remove_extra(scope->data, key)) { SENTRY_SCOPE_NOTIFY(scope, remove_extra, key); } } @@ -1217,8 +1965,7 @@ void sentry__scope_remove_extra_n( sentry_scope_t *scope, const char *key, size_t key_len) { - char *k = data_remove_and_take_key_n( - scope->data, &scope->data->extra, key, key_len); + char *k = data_remove_extra_n(scope->data, key, key_len); if (k) { SENTRY_SCOPE_NOTIFY(scope, remove_extra, k); } @@ -1243,40 +1990,39 @@ sentry_scope_set_attribute_n(sentry_scope_t *scope, const char *key, sentry_value_decref(attribute); return; } - sentry_value_set_by_key_n(scope->data->attributes, key, key_len, attribute); + data_set_attribute_n(scope->data, key, key_len, attribute); } sentry_value_t sentry__scope_get_attributes(const sentry_scope_t *scope) { - return scope->data->attributes; + return data_get_attributes(scope->data); } void sentry_scope_remove_attribute(sentry_scope_t *scope, const char *key) { - sentry_value_remove_by_key(scope->data->attributes, key); + data_remove_attribute(scope->data, key); } void sentry_scope_remove_attribute_n( sentry_scope_t *scope, const char *key, size_t key_len) { - sentry_value_remove_by_key_n(scope->data->attributes, key, key_len); + data_remove_attribute_n(scope->data, key, key_len); } sentry_value_t sentry__scope_get_contexts(const sentry_scope_t *scope) { - return scope->data->contexts; + return data_get_contexts(scope->data); } void sentry_scope_set_context( sentry_scope_t *scope, const char *key, sentry_value_t value) { - if (data_set_by_key(scope->data, &scope->data->contexts, key, - sentry_value_incref(value))) { + if (data_set_context(scope->data, key, sentry_value_incref(value))) { SENTRY_SCOPE_NOTIFY(scope, set_context, key, value); } sentry_value_decref(value); @@ -1292,8 +2038,8 @@ sentry_scope_set_context_n(sentry_scope_t *scope, const char *key, return; } - if (data_set_by_key_n(scope->data, &scope->data->contexts, key, key_len, - sentry_value_incref(value))) { + if (data_set_context_n( + scope->data, key, key_len, sentry_value_incref(value))) { SENTRY_SCOPE_NOTIFY(scope, set_context, notify_key, value); } sentry_free(notify_key); @@ -1318,7 +2064,7 @@ sentry_scope_update_context_n(sentry_scope_t *scope, const char *key, return; } - if (data_update_context_by_key_n( + if (data_update_context_n( scope->data, key, key_len, sentry_value_incref(value))) { SENTRY_SCOPE_NOTIFY(scope, set_context, notify_key, value); } @@ -1329,7 +2075,7 @@ sentry_scope_update_context_n(sentry_scope_t *scope, const char *key, void sentry__scope_remove_context(sentry_scope_t *scope, const char *key) { - if (data_remove_by_key(scope->data, &scope->data->contexts, key)) { + if (data_remove_context(scope->data, key)) { SENTRY_SCOPE_NOTIFY(scope, remove_context, key); } } @@ -1338,8 +2084,7 @@ void sentry__scope_remove_context_n( sentry_scope_t *scope, const char *key, size_t key_len) { - char *k = data_remove_and_take_key_n( - scope->data, &scope->data->contexts, key, key_len); + char *k = data_remove_context_n(scope->data, key, key_len); if (k) { SENTRY_SCOPE_NOTIFY(scope, remove_context, k); } @@ -1349,12 +2094,7 @@ sentry__scope_remove_context_n( sentry_value_t sentry__scope_ref_fingerprint(const sentry_scope_t *scope) { - sentry_value_t fingerprint = sentry_value_new_null(); - SCOPE_READ_LOCK(scope) - { - fingerprint = sentry_value_incref(scope->data->fingerprint); - } - return fingerprint; + return data_ref_fingerprint(scope->data); } void @@ -1420,11 +2160,7 @@ sentry_scope_set_fingerprints( return; } - SCOPE_WRITE_LOCK(scope) - { - sentry__value_replace( - &scope->data->fingerprint, sentry_value_incref(fingerprints)); - } + data_set_fingerprint(scope->data, fingerprints); SENTRY_SCOPE_NOTIFY_OWNED(scope, set_fingerprint, fingerprints); } @@ -1432,32 +2168,26 @@ void sentry_scope_remove_fingerprint(sentry_scope_t *scope) { sentry_value_t fingerprint = sentry_value_new_null(); - SCOPE_WRITE_LOCK(scope) - { - sentry__value_replace( - &scope->data->fingerprint, sentry_value_incref(fingerprint)); - } + data_set_fingerprint(scope->data, fingerprint); SENTRY_SCOPE_NOTIFY_OWNED(scope, set_fingerprint, fingerprint); } sentry_level_t sentry__scope_get_level(const sentry_scope_t *scope) { - sentry_level_t level = SENTRY_LEVEL_ERROR; - SCOPE_READ_LOCK(scope) { level = scope->data->level; } - return level; + return data_get_level(scope->data); } sentry_value_t -sentry__scope_get_client_sdk(const sentry_scope_t *scope) +sentry__scope_ref_client_sdk(const sentry_scope_t *scope) { - return scope->data->client_sdk; + return data_ref_client_sdk(scope->data); } void sentry_scope_set_level(sentry_scope_t *scope, sentry_level_t level) { - SCOPE_WRITE_LOCK(scope) { scope->data->level = level; } + data_set_level(scope->data, level); SENTRY_SCOPE_NOTIFY(scope, set_level, level); } @@ -1487,17 +2217,16 @@ sentry_scope_set_span(sentry_scope_t *scope, sentry_span_t *span) sentry_value_t sentry__scope_get_attachments(const sentry_scope_t *scope) { - return scope->data->attachments; + return data_get_attachments(scope->data); } sentry_value_t sentry__scope_add_attachment(sentry_scope_t *scope, sentry_value_t attachment) { - size_t len = sentry_value_get_length(scope->data->attachments); + bool did_add = false; sentry_value_t added - = sentry__attachments_add(&scope->data->attachments, attachment); - if (!sentry_value_is_null(added) - && sentry_value_get_length(scope->data->attachments) > len) { + = data_add_attachment(scope->data, attachment, &did_add); + if (did_add) { SENTRY_SCOPE_NOTIFY(scope, add_attachment, added); } return added; @@ -1506,17 +2235,14 @@ sentry__scope_add_attachment(sentry_scope_t *scope, sentry_value_t attachment) sentry_value_t sentry__scope_take_attachments(sentry_scope_t *scope) { - sentry_value_t attachments = scope->data->attachments; - scope->data->attachments = sentry__attachments_new(); - return attachments; + return data_take_attachments(scope->data); } sentry_value_t sentry__scope_remove_attachment( sentry_scope_t *scope, const sentry_uuid_t *attachment_id) { - return sentry__attachments_remove( - scope->data->attachments, attachment_id); + return data_remove_attachment(scope->data, attachment_id); } sentry_uuid_t @@ -1536,135 +2262,71 @@ sentry_scope_add_attachment(sentry_scope_t *scope, sentry_value_t attachment) sentry_transaction_t * sentry__scope_get_transaction_object(const sentry_scope_t *scope) { - return scope->data->transaction_object; -} - -static bool -value_has_span_id(sentry_value_t value, const char *span_id) -{ - const char *value_span_id - = sentry_value_as_string(sentry_value_get_by_key(value, "span_id")); - return sentry__string_eq(value_span_id, span_id); + return data_get_transaction_object(scope->data); } void sentry__scope_set_transaction_object( sentry_scope_t *scope, sentry_transaction_t *transaction) { - sentry__transaction_incref(transaction); - sentry__span_decref(scope->data->span); - scope->data->span = NULL; - sentry__transaction_decref(scope->data->transaction_object); - scope->data->transaction_object = transaction; + data_set_transaction_object(scope->data, transaction); } bool sentry__scope_remove_transaction_object( sentry_scope_t *scope, sentry_transaction_t *transaction) { - if (!transaction || scope->data->transaction_object != transaction) { - return false; - } - - scope->data->transaction_object = NULL; - sentry__transaction_decref(transaction); - return true; + return data_remove_transaction_object(scope->data, transaction); } bool sentry__scope_remove_transaction_value( sentry_scope_t *scope, sentry_value_t transaction) { - const char *span_id = sentry_value_as_string( - sentry_value_get_by_key(transaction, "span_id")); - if (!scope->data->transaction_object - || !value_has_span_id( - scope->data->transaction_object->inner, span_id)) { - return false; - } - - sentry_transaction_t *transaction_object = scope->data->transaction_object; - scope->data->transaction_object = NULL; - sentry__transaction_decref(transaction_object); - return true; + return data_remove_transaction_value(scope->data, transaction); } bool sentry__scope_restore_transaction_object( sentry_scope_t *scope, sentry_transaction_t *transaction) { - if (scope->data->transaction_object || !transaction) { - return false; - } - - scope->data->transaction_object = transaction; - return true; + return data_restore_transaction_object(scope->data, transaction); } sentry_span_t * sentry__scope_get_span(const sentry_scope_t *scope) { - return scope->data->span; + return data_get_span(scope->data); } void sentry__scope_set_span(sentry_scope_t *scope, sentry_span_t *span) { - sentry__span_incref(span); - sentry__transaction_decref(scope->data->transaction_object); - scope->data->transaction_object = NULL; - sentry__span_decref(scope->data->span); - scope->data->span = span; + data_set_span(scope->data, span); } bool sentry__scope_remove_span(sentry_scope_t *scope, sentry_span_t *span) { - if (!span || scope->data->span != span) { - return false; - } - - scope->data->span = NULL; - sentry__span_decref(span); - return true; + return data_remove_span(scope->data, span); } bool sentry__scope_remove_span_value(sentry_scope_t *scope, sentry_value_t span) { - const char *span_id - = sentry_value_as_string(sentry_value_get_by_key(span, "span_id")); - if (!scope->data->span - || !value_has_span_id(scope->data->span->inner, span_id)) { - return false; - } - - sentry_span_t *scope_span = scope->data->span; - scope->data->span = NULL; - sentry__span_decref(scope_span); - return true; + return data_remove_span_value(scope->data, span); } bool sentry__scope_restore_span(sentry_scope_t *scope, sentry_span_t *span) { - if (scope->data->span || !span) { - return false; - } - - scope->data->span = span; - return true; + return data_restore_span(scope->data, span); } sentry_value_t sentry__scope_ref_release(const sentry_scope_t *scope) { - sentry_value_t release = sentry_value_new_null(); - SCOPE_READ_LOCK(scope) - { - release = sentry_value_incref(scope->data->release); - } - return release; + return data_ref_release(scope->data); } void @@ -1672,25 +2334,14 @@ sentry__scope_set_release_n( sentry_scope_t *scope, const char *release, size_t release_len) { sentry_value_t value = sentry_value_new_string_n(release, release_len); - SCOPE_WRITE_LOCK(scope) - { - sentry__value_replace( - &scope->data->release, sentry_value_incref(value)); - sentry_value_set_by_key(scope->data->dynamic_sampling_context, - "release", sentry_value_incref(value)); - } + data_set_release(scope->data, value); SENTRY_SCOPE_NOTIFY_OWNED(scope, set_release, value); } sentry_value_t sentry__scope_ref_environment(const sentry_scope_t *scope) { - sentry_value_t environment = sentry_value_new_null(); - SCOPE_READ_LOCK(scope) - { - environment = sentry_value_incref(scope->data->environment); - } - return environment; + return data_ref_environment(scope->data); } void @@ -1699,25 +2350,14 @@ sentry__scope_set_environment_n( { sentry_value_t value = sentry_value_new_string_n(environment, environment_len); - SCOPE_WRITE_LOCK(scope) - { - sentry__value_replace( - &scope->data->environment, sentry_value_incref(value)); - sentry_value_set_by_key(scope->data->dynamic_sampling_context, - "environment", sentry_value_incref(value)); - } + data_set_environment(scope->data, value); SENTRY_SCOPE_NOTIFY_OWNED(scope, set_environment, value); } sentry_value_t sentry__scope_ref_transaction(const sentry_scope_t *scope) { - sentry_value_t transaction = sentry_value_new_null(); - SCOPE_READ_LOCK(scope) - { - transaction = sentry_value_incref(scope->data->transaction); - } - return transaction; + return data_ref_transaction(scope->data); } void @@ -1726,15 +2366,7 @@ sentry__scope_set_transaction_n( { sentry_value_t value = sentry_value_new_string_n(transaction, transaction_len); - SCOPE_WRITE_LOCK(scope) - { - sentry__value_replace( - &scope->data->transaction, sentry_value_incref(value)); - if (scope->data->transaction_object) { - sentry_transaction_set_name_n( - scope->data->transaction_object, transaction, transaction_len); - } - } + data_set_transaction(scope->data, value, transaction, transaction_len); SENTRY_SCOPE_NOTIFY_OWNED(scope, set_transaction, value); } @@ -1807,45 +2439,7 @@ void sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, sentry_value_t telemetry, sentry_value_t attributes) { - sentry__value_merge_objects_shallow(attributes, scope->data->attributes); - - // a span on the scope MUST take precedence over the propagation context - sentry_value_t trace_id = sentry_value_get_by_key( - sentry_value_get_by_key(scope->data->propagation_context, "trace"), - "trace_id"); - - sentry_value_t parent_span_id = sentry_value_new_object(); - if (scope->data->transaction_object) { - sentry_value_t span_id = sentry_value_get_by_key( - scope->data->transaction_object->inner, "span_id"); - sentry_value_incref(span_id); - sentry_value_set_by_key(parent_span_id, "value", span_id); - trace_id = sentry_value_get_by_key( - scope->data->transaction_object->inner, "trace_id"); - } else if (scope->data->span) { - sentry_value_t span_id - = sentry_value_get_by_key(scope->data->span->inner, "span_id"); - sentry_value_incref(span_id); - sentry_value_set_by_key(parent_span_id, "value", span_id); - trace_id - = sentry_value_get_by_key(scope->data->span->inner, "trace_id"); - } - sentry_value_set_by_key( - parent_span_id, "type", sentry_value_new_string("string")); - if ((scope->data->transaction_object || scope->data->span) - && sentry_value_is_null(sentry_value_get_by_key( - attributes, "sentry.trace.parent_span_id"))) { - sentry_value_set_by_key( - attributes, "sentry.trace.parent_span_id", parent_span_id); - } else { - sentry_value_decref(parent_span_id); - } - if (!sentry_value_is_null(trace_id) - && sentry_value_is_null( - sentry_value_get_by_key(telemetry, "trace_id"))) { - sentry_value_incref(trace_id); - sentry_value_set_by_key(telemetry, "trace_id", trace_id); - } + data_apply_to_telemetry(scope->data, telemetry, attributes); sentry_value_t user = sentry__scope_ref_user(scope); if (!sentry_value_is_null(user)) { @@ -1873,30 +2467,6 @@ sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, } sentry_value_decref(user); - sentry_value_t os_name = sentry_value_new_null(); - sentry_value_t os_version = sentry_value_new_null(); - SCOPE_READ_LOCK(scope) - { - sentry_value_t os_context - = sentry_value_get_by_key(scope->data->contexts, "os"); - if (!sentry_value_is_null(os_context)) { - os_name = sentry_value_incref( - sentry_value_get_by_key(os_context, "name")); - os_version = sentry_value_incref( - sentry_value_get_by_key(os_context, "version")); - } - } - if (!sentry_value_is_null(os_name)) { - sentry__value_add_attribute(attributes, os_name, "string", "os.name"); - } else { - sentry_value_decref(os_name); - } - if (!sentry_value_is_null(os_version)) { - sentry__value_add_attribute( - attributes, os_version, "string", "os.version"); - } else { - sentry_value_decref(os_version); - } sentry_value_t environment = sentry__scope_ref_environment(scope); if (!sentry_value_is_null(environment)) { sentry__value_add_attribute(attributes, @@ -1915,11 +2485,7 @@ sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, sentry_uuid_t sentry_scope_get_last_event_id(const sentry_scope_t *scope) { - sentry_uuid_t event_id = sentry_uuid_nil(); - if (scope) { - SCOPE_READ_LOCK(scope) { event_id = scope->data->last_event_id; } - } - return event_id; + return scope ? data_get_last_event_id(scope->data) : sentry_uuid_nil(); } void @@ -1927,7 +2493,7 @@ sentry__scope_set_last_event_id( sentry_scope_t *scope, sentry_uuid_t event_id) { if (scope) { - SCOPE_WRITE_LOCK(scope) { scope->data->last_event_id = event_id; } + data_set_last_event_id(scope->data, event_id); } } diff --git a/src/sentry_scope.h b/src/sentry_scope.h index e98e213a31..f04359f989 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -146,7 +146,7 @@ void sentry__scope_set_fingerprint_nva(sentry_scope_t *scope, const char *fingerprint, size_t fingerprint_len, va_list va); sentry_value_t sentry__scope_ref_user(const sentry_scope_t *scope); sentry_level_t sentry__scope_get_level(const sentry_scope_t *scope); -sentry_value_t sentry__scope_get_client_sdk(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_client_sdk(const sentry_scope_t *scope); sentry_value_t sentry__scope_get_attachments(const sentry_scope_t *scope); sentry_value_t sentry__scope_add_attachment( diff --git a/tests/unit/test_basic.c b/tests/unit/test_basic.c index 9e4a06667a..b0bc0971ce 100644 --- a/tests/unit/test_basic.c +++ b/tests/unit/test_basic.c @@ -424,9 +424,9 @@ SENTRY_TEST(client_sdk_integrations) sentry_init(options); SENTRY_WITH_SCOPE (scope) { + sentry_value_t client_sdk = sentry__scope_ref_client_sdk(scope); sentry_value_t integrations - = sentry_value_get_by_key( - sentry__scope_get_client_sdk(scope), "integrations"); + = sentry_value_get_by_key(client_sdk, "integrations"); size_t integration_count = sentry_value_get_length(integrations); TEST_CHECK(integration_count > 0); TEST_CHECK_STRING_EQUAL( @@ -450,6 +450,7 @@ SENTRY_TEST(client_sdk_integrations) sentry_value_get_by_index(integrations, integration_index)), "qt"); #endif + sentry_value_decref(client_sdk); } sentry_close(); From 813b7b079034595c92edc85a920dcd9e750bde1a Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 6 Aug 2026 15:24:04 +0200 Subject: [PATCH 10/28] ref containers --- src/integrations/sentry_integration_wer.c | 10 +- src/sentry_scope.c | 40 +++--- src/sentry_scope.h | 8 +- tests/unit/test_scope.c | 161 ++++++++++++++-------- 4 files changed, 138 insertions(+), 81 deletions(-) diff --git a/src/integrations/sentry_integration_wer.c b/src/integrations/sentry_integration_wer.c index e2895bc31c..87130a7be0 100644 --- a/src/integrations/sentry_integration_wer.c +++ b/src/integrations/sentry_integration_wer.c @@ -207,8 +207,9 @@ wer_clear(void *data) return; } - sentry__value_foreach_key_value( - sentry__scope_get_tags(scope), wer_cleanup_tag, wer_data); + sentry_value_t tags = sentry__scope_ref_tags(scope); + sentry__value_foreach_key_value(tags, wer_cleanup_tag, wer_data); + sentry_value_decref(tags); sentry_value_t attachments = sentry__scope_get_attachments(scope); size_t len = sentry_value_get_length(attachments); @@ -259,8 +260,9 @@ unregister_wer( return; } - sentry__value_foreach_key_value( - sentry__scope_get_tags(scope), wer_cleanup_tag, wer_data); + sentry_value_t tags = sentry__scope_ref_tags(scope); + sentry__value_foreach_key_value(tags, wer_cleanup_tag, wer_data); + sentry_value_decref(tags); sentry_value_t attachments = sentry__scope_get_attachments(scope); size_t len = sentry_value_get_length(attachments); diff --git a/src/sentry_scope.c b/src/sentry_scope.c index c277367cd2..8aaa1b36b4 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -555,9 +555,11 @@ data_set_user(sentry_scope_data_t *data, sentry_value_t user) } static sentry_value_t -data_get_tags(const sentry_scope_data_t *data) +data_ref_tags(const sentry_scope_data_t *data) { - return data->tags; + sentry_value_t tags = sentry_value_new_null(); + DATA_READ_LOCK(data) { tags = sentry_value_incref(data->tags); } + return tags; } static bool @@ -608,9 +610,11 @@ data_remove_tag_n(sentry_scope_data_t *data, const char *key, size_t key_len) } static sentry_value_t -data_get_extra(const sentry_scope_data_t *data) +data_ref_extra(const sentry_scope_data_t *data) { - return data->extra; + sentry_value_t extra = sentry_value_new_null(); + DATA_READ_LOCK(data) { extra = sentry_value_incref(data->extra); } + return extra; } static bool @@ -661,9 +665,11 @@ data_remove_extra_n(sentry_scope_data_t *data, const char *key, size_t key_len) } static sentry_value_t -data_get_attributes(const sentry_scope_data_t *data) +data_ref_attributes(const sentry_scope_data_t *data) { - return data->attributes; + sentry_value_t attributes = sentry_value_new_null(); + DATA_READ_LOCK(data) { attributes = sentry_value_incref(data->attributes); } + return attributes; } static bool @@ -697,9 +703,11 @@ data_remove_attribute_n( } static sentry_value_t -data_get_contexts(const sentry_scope_data_t *data) +data_ref_contexts(const sentry_scope_data_t *data) { - return data->contexts; + sentry_value_t contexts = sentry_value_new_null(); + DATA_READ_LOCK(data) { contexts = sentry_value_incref(data->contexts); } + return contexts; } static bool @@ -1849,9 +1857,9 @@ sentry_scope_set_user(sentry_scope_t *scope, sentry_value_t user) } sentry_value_t -sentry__scope_get_tags(const sentry_scope_t *scope) +sentry__scope_ref_tags(const sentry_scope_t *scope) { - return data_get_tags(scope->data); + return data_ref_tags(scope->data); } void @@ -1920,9 +1928,9 @@ sentry__scope_remove_tag_n( } sentry_value_t -sentry__scope_get_extra(const sentry_scope_t *scope) +sentry__scope_ref_extra(const sentry_scope_t *scope) { - return data_get_extra(scope->data); + return data_ref_extra(scope->data); } void @@ -1994,9 +2002,9 @@ sentry_scope_set_attribute_n(sentry_scope_t *scope, const char *key, } sentry_value_t -sentry__scope_get_attributes(const sentry_scope_t *scope) +sentry__scope_ref_attributes(const sentry_scope_t *scope) { - return data_get_attributes(scope->data); + return data_ref_attributes(scope->data); } void @@ -2013,9 +2021,9 @@ sentry_scope_remove_attribute_n( } sentry_value_t -sentry__scope_get_contexts(const sentry_scope_t *scope) +sentry__scope_ref_contexts(const sentry_scope_t *scope) { - return data_get_contexts(scope->data); + return data_ref_contexts(scope->data); } void diff --git a/src/sentry_scope.h b/src/sentry_scope.h index f04359f989..b9c7329c21 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -154,19 +154,19 @@ sentry_value_t sentry__scope_add_attachment( sentry_value_t sentry__scope_take_attachments(sentry_scope_t *scope); sentry_value_t sentry__scope_remove_attachment( sentry_scope_t *scope, const sentry_uuid_t *attachment_id); -sentry_value_t sentry__scope_get_tags(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_tags(const sentry_scope_t *scope); void sentry__scope_remove_tag(sentry_scope_t *scope, const char *key); void sentry__scope_remove_tag_n( sentry_scope_t *scope, const char *key, size_t key_len); -sentry_value_t sentry__scope_get_extra(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_extra(const sentry_scope_t *scope); void sentry__scope_remove_extra(sentry_scope_t *scope, const char *key); void sentry__scope_remove_extra_n( sentry_scope_t *scope, const char *key, size_t key_len); -sentry_value_t sentry__scope_get_attributes(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_attributes(const sentry_scope_t *scope); -sentry_value_t sentry__scope_get_contexts(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_contexts(const sentry_scope_t *scope); void sentry__scope_remove_context(sentry_scope_t *scope, const char *key); void sentry__scope_remove_context_n( sentry_scope_t *scope, const char *key, size_t key_len); diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index a39a9146b9..00901fcb8c 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -13,6 +13,28 @@ #define TEST_CHECK_UUID_EQUAL(Actual, Expected) \ TEST_CHECK(memcmp(&(Actual), &(Expected), sizeof(sentry_uuid_t)) == 0) +typedef sentry_value_t (*scope_value_ref_t)(const sentry_scope_t *scope); + +static sentry_value_t +scope_value_get_by_key( + scope_value_ref_t ref, const sentry_scope_t *scope, const char *key) +{ + sentry_value_t values = ref(scope); + sentry_value_t value + = sentry_value_incref(sentry_value_get_by_key(values, key)); + sentry_value_decref(values); + return value; +} + +static size_t +scope_value_get_length(scope_value_ref_t ref, const sentry_scope_t *scope) +{ + sentry_value_t value = ref(scope); + size_t length = sentry_value_get_length(value); + sentry_value_decref(value); + return length; +} + SENTRY_TEST(scope_contexts) { SENTRY_TEST_OPTIONS_NEW(options); @@ -123,14 +145,15 @@ SENTRY_TEST(scope_update_context) sentry_update_context("device", device); SENTRY_WITH_SCOPE (scope) { - sentry_value_t ctx = sentry_value_get_by_key( - sentry__scope_get_contexts(scope), "device"); + sentry_value_t ctx = scope_value_get_by_key( + sentry__scope_ref_contexts, scope, "device"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "model")), "Xbox Series X"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "family")), "Xbox"); + sentry_value_decref(ctx); } } @@ -143,8 +166,8 @@ SENTRY_TEST(scope_update_context) sentry_update_context("device", extra); SENTRY_WITH_SCOPE (scope) { - sentry_value_t ctx = sentry_value_get_by_key( - sentry__scope_get_contexts(scope), "device"); + sentry_value_t ctx = scope_value_get_by_key( + sentry__scope_ref_contexts, scope, "device"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "model")), "PC"); @@ -155,6 +178,7 @@ SENTRY_TEST(scope_update_context) sentry_value_as_string( sentry_value_get_by_key(ctx, "cpu_description")), "some cpu"); + sentry_value_decref(ctx); } } @@ -166,11 +190,12 @@ SENTRY_TEST(scope_update_context) sentry_value_set_by_key(os, "name", sentry_value_new_string("SteamOS")); sentry_scope_update_context(local_scope, "os", os); - sentry_value_t ctx = sentry_value_get_by_key( - sentry__scope_get_contexts(local_scope), "os"); + sentry_value_t ctx = scope_value_get_by_key( + sentry__scope_ref_contexts, local_scope, "os"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "name")), "SteamOS"); + sentry_value_decref(ctx); // scoped update overwrites existing keys sentry_value_t os2 = sentry_value_new_object(); @@ -178,14 +203,15 @@ SENTRY_TEST(scope_update_context) sentry_value_set_by_key(os2, "version", sentry_value_new_string("6.1")); sentry_scope_update_context(local_scope, "os", os2); - ctx = sentry_value_get_by_key( - sentry__scope_get_contexts(local_scope), "os"); + ctx = scope_value_get_by_key( + sentry__scope_ref_contexts, local_scope, "os"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "name")), "Linux"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "version")), "6.1"); + sentry_value_decref(ctx); sentry_scope_free(local_scope); } @@ -1113,20 +1139,22 @@ SENTRY_TEST(scope_clone) // scope values must not be corrupted by before_send modifications SENTRY_WITH_SCOPE (scope) { sentry_value_t scope_gpu - = sentry_value_get_by_key(sentry__scope_get_contexts(scope), "gpu"); + = scope_value_get_by_key(sentry__scope_ref_contexts, scope, "gpu"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(scope_gpu, "name")), "original"); TEST_CHECK(sentry_value_is_null( sentry_value_get_by_key(scope_gpu, "injected"))); + sentry_value_decref(scope_gpu); sentry_value_t scope_data - = sentry_value_get_by_key(sentry__scope_get_extra(scope), "data"); + = scope_value_get_by_key(sentry__scope_ref_extra, scope, "data"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(scope_data, "key")), "original"); TEST_CHECK(sentry_value_is_null( sentry_value_get_by_key(scope_data, "injected"))); + sentry_value_decref(scope_data); sentry_value_t scope_user = sentry__scope_ref_user(scope); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( @@ -1155,7 +1183,7 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute("valid_key", valid_attr); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = sentry__scope_get_attributes(scope); + sentry_value_t attributes = sentry__scope_ref_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "valid_key"); @@ -1167,6 +1195,7 @@ SENTRY_TEST(scope_global_attributes) TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( retrieved_attr, "value")), "test_value"); + sentry_value_decref(attributes); } // Test that invalid attributes (missing 'value' or 'type') are not set @@ -1177,12 +1206,13 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute("invalid_no_value", invalid_attr_no_value); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = sentry__scope_get_attributes(scope); + sentry_value_t attributes = sentry__scope_ref_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "invalid_no_value"); // Check that the attribute was NOT set TEST_CHECK(sentry_value_is_null(retrieved_attr)); + sentry_value_decref(attributes); } // Test invalid attribute missing 'type' @@ -1193,24 +1223,26 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute("invalid_no_type", invalid_attr_no_type); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = sentry__scope_get_attributes(scope); + sentry_value_t attributes = sentry__scope_ref_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "invalid_no_type"); // Check that the attribute was NOT set TEST_CHECK(sentry_value_is_null(retrieved_attr)); + sentry_value_decref(attributes); } // Test removing an attribute sentry_remove_attribute("valid_key"); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = sentry__scope_get_attributes(scope); + sentry_value_t attributes = sentry__scope_ref_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "valid_key"); // Check that the attribute was removed TEST_CHECK(sentry_value_is_null(retrieved_attr)); + sentry_value_decref(attributes); } // Test setting attribute with _n variant @@ -1219,7 +1251,7 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute_n("key_n", 5, attr_n); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = sentry__scope_get_attributes(scope); + sentry_value_t attributes = sentry__scope_ref_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "key_n"); @@ -1234,6 +1266,7 @@ SENTRY_TEST(scope_global_attributes) TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( retrieved_attr, "unit")), "percent"); + sentry_value_decref(attributes); } sentry_close(); @@ -1254,7 +1287,7 @@ SENTRY_TEST(scope_local_attributes) sentry_value_new_attribute(sentry_value_new_string("global"), NULL)); SENTRY_WITH_SCOPE (global_scope) { - sentry_value_t attributes = sentry__scope_get_attributes(global_scope); + sentry_value_t attributes = sentry__scope_ref_attributes(global_scope); // Verify global attributes are set TEST_CHECK_STRING_EQUAL( @@ -1269,6 +1302,7 @@ SENTRY_TEST(scope_local_attributes) sentry_value_as_string(sentry_value_get_by_key( sentry_value_get_by_key(attributes, "scope"), "value")), "global"); + sentry_value_decref(attributes); } SENTRY_WITH_SCOPE (global_scope) { @@ -1283,7 +1317,7 @@ SENTRY_TEST(scope_local_attributes) sentry_value_new_attribute(sentry_value_new_string("local"), NULL)); sentry_value_t local_attributes - = sentry__scope_get_attributes(local_scope); + = sentry__scope_ref_attributes(local_scope); // Verify local attributes are set TEST_CHECK_STRING_EQUAL( @@ -1301,7 +1335,7 @@ SENTRY_TEST(scope_local_attributes) // Verify global scope still has its own attributes sentry_value_t global_attributes - = sentry__scope_get_attributes(global_scope); + = sentry__scope_ref_attributes(global_scope); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key( sentry_value_get_by_key(global_attributes, "all"), "value")), @@ -1310,6 +1344,8 @@ SENTRY_TEST(scope_local_attributes) sentry_value_as_string(sentry_value_get_by_key( sentry_value_get_by_key(global_attributes, "global"), "value")), "global"); + sentry_value_decref(global_attributes); + sentry_value_decref(local_attributes); sentry_scope_free(local_scope); } @@ -1318,7 +1354,7 @@ SENTRY_TEST(scope_local_attributes) sentry_remove_attribute("all"); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = sentry__scope_get_attributes(scope); + sentry_value_t attributes = sentry__scope_ref_attributes(scope); TEST_CHECK( sentry_value_is_null(sentry_value_get_by_key(attributes, "all"))); // Other attributes should still exist @@ -1326,6 +1362,7 @@ SENTRY_TEST(scope_local_attributes) sentry_value_get_by_key(attributes, "global"))); TEST_CHECK(!sentry_value_is_null( sentry_value_get_by_key(attributes, "scope"))); + sentry_value_decref(attributes); } // Test _n variants with local scope @@ -1335,7 +1372,7 @@ SENTRY_TEST(scope_local_attributes) sentry_value_new_attribute(sentry_value_new_int32(100), "percent")); sentry_value_t local_attributes - = sentry__scope_get_attributes(local_scope); + = sentry__scope_ref_attributes(local_scope); sentry_value_t attr = sentry_value_get_by_key(local_attributes, "test_key"); @@ -1353,6 +1390,7 @@ SENTRY_TEST(scope_local_attributes) sentry_scope_remove_attribute_n(local_scope, "test_key", 8); TEST_CHECK(sentry_value_is_null( sentry_value_get_by_key(local_attributes, "test_key"))); + sentry_value_decref(local_attributes); sentry_scope_free(local_scope); } @@ -1368,9 +1406,10 @@ SENTRY_TEST(scope_local_attributes) sentry_scope_set_attribute(local_scope, "invalid", invalid_attr); sentry_value_t local_attributes - = sentry__scope_get_attributes(local_scope); + = sentry__scope_ref_attributes(local_scope); TEST_CHECK(sentry_value_is_null( sentry_value_get_by_key(local_attributes, "invalid"))); + sentry_value_decref(local_attributes); sentry_scope_free(local_scope); } @@ -1680,7 +1719,7 @@ SENTRY_TEST(scope_observer_clear) sentry_scope_clear(scope); TEST_CHECK(d.was_cleared); TEST_CHECK(sentry__scope_has_observers(scope)); - TEST_CHECK(sentry_value_get_length(sentry__scope_get_tags(scope)) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_ref_tags, scope) == 0); d.was_called = false; sentry_scope_set_tag(scope, "after", "clear"); @@ -1707,7 +1746,7 @@ SENTRY_TEST(scope_observer_clear) TEST_CHECK(observer_data.was_called); TEST_CHECK(observer_data.was_cleared); TEST_CHECK(sentry__scope_has_observers(scope)); - TEST_CHECK(sentry_value_get_length(sentry__scope_get_tags(scope)) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_ref_tags, scope) == 0); sentry_value_decref(observer_data.tags); sentry_scope_free(scope); @@ -2455,7 +2494,7 @@ SENTRY_TEST(scope_set_attribute_invalid_decref_value) sentry_value_decref(no_type); TEST_CHECK_INT_EQUAL( - sentry_value_get_length(sentry__scope_get_attributes(scope)), 0); + scope_value_get_length(sentry__scope_ref_attributes, scope), 0); sentry_scope_free(scope); } @@ -2473,7 +2512,7 @@ SENTRY_TEST(scope_set_attribute_null_key_decref_value) sentry_value_decref(v); TEST_CHECK_INT_EQUAL( - sentry_value_get_length(sentry__scope_get_attributes(scope)), 0); + scope_value_get_length(sentry__scope_ref_attributes, scope), 0); sentry_scope_free(scope); } @@ -2517,9 +2556,10 @@ SENTRY_TEST(scope_clone_independence) TEST_CHECK(!sentry__scope_is_one_shot(clone)); // The clone carries over the source's data. - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - sentry__scope_get_tags(clone), "shared")), - "original"); + sentry_value_t clone_tag + = scope_value_get_by_key(sentry__scope_ref_tags, clone, "shared"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(clone_tag), "original"); + sentry_value_decref(clone_tag); TEST_CHECK(sentry__scope_get_level(clone) == SENTRY_LEVEL_WARNING); TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(clone), 1); @@ -2529,11 +2569,14 @@ SENTRY_TEST(scope_clone_independence) sentry_scope_add_breadcrumb( clone, sentry_value_new_breadcrumb(NULL, "second")); - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - sentry__scope_get_tags(scope), "shared")), - "original"); - TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(sentry__scope_get_tags(scope), "clone_only"))); + sentry_value_t scope_tag + = scope_value_get_by_key(sentry__scope_ref_tags, scope, "shared"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(scope_tag), "original"); + sentry_value_decref(scope_tag); + sentry_value_t clone_only + = scope_value_get_by_key(sentry__scope_ref_tags, scope, "clone_only"); + TEST_CHECK(sentry_value_is_null(clone_only)); + sentry_value_decref(clone_only); TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(scope), 1); TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(clone), 2); @@ -2568,30 +2611,33 @@ SENTRY_TEST(scope_clone_preserves_data) sentry_value_decref( sentry__scope_remove_attachment(scope, &attachment_id)); - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - sentry__scope_get_tags(clone), "tag_key")), - "tag_value"); - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - sentry__scope_get_contexts(clone), "device")), - "Xbox"); + sentry_value_t clone_tag + = scope_value_get_by_key(sentry__scope_ref_tags, clone, "tag_key"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(clone_tag), "tag_value"); + sentry_value_decref(clone_tag); + sentry_value_t clone_context + = scope_value_get_by_key(sentry__scope_ref_contexts, clone, "device"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(clone_context), "Xbox"); + sentry_value_decref(clone_context); sentry_value_t clone_user = sentry__scope_ref_user(clone); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(clone_user, "username")), "alice"); sentry_value_decref(clone_user); - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - sentry__scope_get_extra(clone), "extra_key")), - "extra_value"); + sentry_value_t clone_extra + = scope_value_get_by_key(sentry__scope_ref_extra, clone, "extra_key"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(clone_extra), "extra_value"); + sentry_value_decref(clone_extra); sentry_value_t clone_fingerprint = sentry__scope_ref_fingerprint(clone); TEST_CHECK_INT_EQUAL(sentry_value_get_length(clone_fingerprint), 2); sentry_value_decref(clone_fingerprint); TEST_CHECK(sentry__scope_get_level(clone) == SENTRY_LEVEL_WARNING); - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key( - sentry_value_get_by_key( - sentry__scope_get_attributes(clone), "attr_key"), - "value")), + sentry_value_t clone_attribute = scope_value_get_by_key( + sentry__scope_ref_attributes, clone, "attr_key"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + clone_attribute, "value")), "attr_value"); + sentry_value_decref(clone_attribute); TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(clone), 1); // Attachments are deep-copied into an independent list. @@ -2671,9 +2717,9 @@ SENTRY_TEST(scope_clear) sentry_scope_add_breadcrumb( scope, sentry_value_new_breadcrumb(NULL, "crumb")); - TEST_CHECK(sentry_value_get_length(sentry__scope_get_tags(scope)) == 1); + TEST_CHECK(scope_value_get_length(sentry__scope_ref_tags, scope) == 1); TEST_CHECK( - sentry_value_get_length(sentry__scope_get_attributes(scope)) == 1); + scope_value_get_length(sentry__scope_ref_attributes, scope) == 1); sentry_value_t scope_user = sentry__scope_ref_user(scope); TEST_CHECK(!sentry_value_is_null(scope_user)); sentry_value_decref(scope_user); @@ -2681,11 +2727,11 @@ SENTRY_TEST(scope_clear) sentry_scope_clear(scope); // Everything is reset to the state of a fresh scope. - TEST_CHECK(sentry_value_get_length(sentry__scope_get_tags(scope)) == 0); - TEST_CHECK(sentry_value_get_length(sentry__scope_get_extra(scope)) == 0); - TEST_CHECK(sentry_value_get_length(sentry__scope_get_contexts(scope)) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_ref_tags, scope) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_ref_extra, scope) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_ref_contexts, scope) == 0); TEST_CHECK( - sentry_value_get_length(sentry__scope_get_attributes(scope)) == 0); + scope_value_get_length(sentry__scope_ref_attributes, scope) == 0); scope_user = sentry__scope_ref_user(scope); TEST_CHECK(sentry_value_is_null(scope_user)); sentry_value_decref(scope_user); @@ -2710,7 +2756,7 @@ SENTRY_TEST(scope_clear) // The cleared scope is still usable. sentry_scope_set_tag(scope, "after", "clear"); - TEST_CHECK(sentry_value_get_length(sentry__scope_get_tags(scope)) == 1); + TEST_CHECK(scope_value_get_length(sentry__scope_ref_tags, scope) == 1); sentry_scope_free(scope); sentry_close(); @@ -2903,9 +2949,10 @@ SENTRY_TEST(scope_capture_user_owned) // The scope was applied but not freed, so reading and reusing it is safe // (a use-after-free here would trip the sanitizers). - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - sentry__scope_get_tags(scope), "run")), - "first"); + sentry_value_t tag + = scope_value_get_by_key(sentry__scope_ref_tags, scope, "run"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(tag), "first"); + sentry_value_decref(tag); sentry_scope_set_tag(scope, "run", "second"); sentry_scope_capture_event(scope, From d9ba2a8062cc6bb11d78e6e09bfbb893db85f360 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 7 Aug 2026 11:27:45 +0200 Subject: [PATCH 11/28] breadcrumbs --- src/backends/sentry_backend_crashpad.cpp | 3 +-- src/sentry_scope.c | 12 +++--------- src/sentry_scope.h | 3 +-- tests/unit/test_scope.c | 6 ++---- tests/unit/tests.inc | 2 +- 5 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 1d576387db..87b20ca756 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -516,8 +516,7 @@ crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) // written above and stays breadcrumb-free SENTRY_WITH_SCOPE (scope) { sentry_value_set_by_key(crash_event, "breadcrumbs", - sentry__ringbuffer_to_list( - sentry__scope_get_breadcrumbs(scope))); + sentry__scope_breadcrumbs_to_list(scope)); } sentry__session_replay_flush_pending( diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 8aaa1b36b4..58a65567ca 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -531,12 +531,6 @@ data_add_breadcrumb(sentry_scope_data_t *data, sentry_value_t breadcrumb) return added; } -static const sentry_ringbuffer_t * -data_get_breadcrumbs(const sentry_scope_data_t *data) -{ - return data->breadcrumbs; -} - static sentry_value_t data_ref_user(const sentry_scope_data_t *data) { @@ -1837,10 +1831,10 @@ sentry_scope_add_breadcrumb(sentry_scope_t *scope, sentry_value_t breadcrumb) } } -const sentry_ringbuffer_t * -sentry__scope_get_breadcrumbs(const sentry_scope_t *scope) +sentry_value_t +sentry__scope_breadcrumbs_to_list(const sentry_scope_t *scope) { - return data_get_breadcrumbs(scope->data); + return data_breadcrumbs_to_list(scope->data); } sentry_value_t diff --git a/src/sentry_scope.h b/src/sentry_scope.h index b9c7329c21..2eaaa00580 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -178,8 +178,7 @@ void sentry__scope_set_propagation_context( sentry_scope_t *scope, const char *key, sentry_value_t value); void sentry__scope_regenerate_propagation_context(sentry_scope_t *scope); -const sentry_ringbuffer_t *sentry__scope_get_breadcrumbs( - const sentry_scope_t *scope); +sentry_value_t sentry__scope_breadcrumbs_to_list(const sentry_scope_t *scope); sentry_transaction_t *sentry__scope_get_transaction_object( const sentry_scope_t *scope); diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index 00901fcb8c..09deb9b4db 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -2533,8 +2533,7 @@ SENTRY_TEST(scope_ownership) static size_t scope_breadcrumb_count(const sentry_scope_t *scope) { - sentry_value_t breadcrumbs - = sentry__ringbuffer_to_list(sentry__scope_get_breadcrumbs(scope)); + sentry_value_t breadcrumbs = sentry__scope_breadcrumbs_to_list(scope); size_t count = sentry_value_get_length(breadcrumbs); sentry_value_decref(breadcrumbs); return count; @@ -2740,8 +2739,7 @@ SENTRY_TEST(scope_clear) TEST_CHECK(sentry_value_is_null(scope_fingerprint)); sentry_value_decref(scope_fingerprint); TEST_CHECK_INT_EQUAL(sentry__scope_get_level(scope), SENTRY_LEVEL_ERROR); - sentry_value_t crumbs - = sentry__ringbuffer_to_list(sentry__scope_get_breadcrumbs(scope)); + sentry_value_t crumbs = sentry__scope_breadcrumbs_to_list(scope); TEST_CHECK(sentry_value_get_length(crumbs) == 0); sentry_value_decref(crumbs); diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 2363e711c0..b5d2e8f1a8 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -505,8 +505,8 @@ XX(value_object_merge_nested) XX(value_object_merge_shallow) XX(value_object_merge_shallow_nested) XX(value_refcount) -XX(value_replace) XX(value_remove_by_null_key) +XX(value_replace) XX(value_set_by_null_key) XX(value_set_stacktrace) XX(value_string) From 9e3bbba1d0d9686bc6528dfd63f8a75b73616556 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 7 Aug 2026 12:22:35 +0200 Subject: [PATCH 12/28] trace_context --- src/sentry_core.c | 17 ++++++++------ src/sentry_envelope.c | 6 +++-- src/sentry_scope.c | 49 +++++++++++++++++++++++++++++---------- src/sentry_scope.h | 4 +++- src/sentry_tracing.c | 3 ++- tests/unit/test_logs.c | 7 ++++-- tests/unit/test_tracing.c | 10 ++++---- 7 files changed, 67 insertions(+), 29 deletions(-) diff --git a/src/sentry_core.c b/src/sentry_core.c index 6d948a77e8..fb43fa8767 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -1354,9 +1354,11 @@ sentry_transaction_start_ts(sentry_transaction_context_t *opaque_tx_ctx, // captured outside this transaction also carry the new // trace_id, and align the tx's trace_id with it. sentry__scope_regenerate_propagation_context(scope); - sentry_value_t scope_trace_id = sentry_value_get_by_key( - sentry__scope_get_trace_context(scope), "trace_id"); - sentry_value_incref(scope_trace_id); + sentry_value_t trace_context + = sentry__scope_ref_trace_context(scope); + sentry_value_t scope_trace_id = sentry_value_incref( + sentry_value_get_by_key(trace_context, "trace_id")); + sentry_value_decref(trace_context); sentry_value_set_by_key(tx, "trace_id", scope_trace_id); sentry_value_remove_by_key(tx, "parent_span_id"); sentry_value_remove_by_key(tx, "sampled"); @@ -1369,8 +1371,10 @@ sentry_transaction_start_ts(sentry_transaction_context_t *opaque_tx_ctx, double sample_rand = 1.0; SENTRY_WITH_SCOPE (scope) { - sample_rand = sentry_value_as_double(sentry_value_get_by_key( - sentry__scope_get_trace_context(scope), "sample_rand")); + sentry_value_t trace_context = sentry__scope_ref_trace_context(scope); + sample_rand = sentry_value_as_double( + sentry_value_get_by_key(trace_context, "sample_rand")); + sentry_value_decref(trace_context); } sentry_sampling_context_t sampling_ctx = { opaque_tx_ctx, custom_sampling_ctx, NULL, sample_rand }; @@ -1442,8 +1446,7 @@ sentry__transaction_finish_value( = sentry_value_get_by_key(tx, "trace_id"); sentry_value_incref(txn_trace_id); - sentry_value_set_by_key(sentry__scope_get_trace_context(scope), - "trace_id", txn_trace_id); + sentry__scope_set_trace_context(scope, "trace_id", txn_trace_id); } } // The sampling decision should already be made for transactions diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index 672e38deeb..a30f4f80a4 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -416,8 +416,10 @@ sentry__envelope_add_event(sentry_envelope_t *envelope, sentry_value_t event) double sample_rand = (double)NAN; SENTRY_WITH_SCOPE (scope) { dsc = sentry__value_clone(sentry__scope_get_dsc(scope)); - sample_rand = sentry_value_as_double(sentry_value_get_by_key( - sentry__scope_get_trace_context(scope), "sample_rand")); + sentry_value_t trace_context = sentry__scope_ref_trace_context(scope); + sample_rand = sentry_value_as_double( + sentry_value_get_by_key(trace_context, "sample_rand")); + sentry_value_decref(trace_context); } if (!sentry_value_is_null(dsc)) { sentry_value_t trace_id = sentry_value_get_by_key( diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 58a65567ca..7a3116ae18 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -359,12 +359,6 @@ data_ref_propagation_context(const sentry_scope_data_t *data) return propagation_context; } -static sentry_value_t -data_get_trace_context(const sentry_scope_data_t *data) -{ - return sentry_value_get_by_key(data->propagation_context, "trace"); -} - static void data_set_propagation_context( sentry_scope_data_t *data, const char *key, sentry_value_t value) @@ -384,6 +378,30 @@ data_regenerate_propagation_context(sentry_scope_data_t *data) } } +static sentry_value_t +data_ref_trace_context(const sentry_scope_data_t *data) +{ + sentry_value_t trace_context = sentry_value_new_null(); + DATA_READ_LOCK(data) + { + trace_context = sentry_value_incref( + sentry_value_get_by_key(data->propagation_context, "trace")); + } + return trace_context; +} + +static void +data_set_trace_context( + sentry_scope_data_t *data, const char *key, sentry_value_t value) +{ + DATA_WRITE_LOCK(data) + { + sentry_value_set_by_key( + sentry_value_get_by_key(data->propagation_context, "trace"), key, + value); + } +} + static bool data_is_trace_managed(const sentry_scope_data_t *data) { @@ -1472,12 +1490,6 @@ sentry__scope_get_propagation_context(const sentry_scope_t *scope) return data_get_propagation_context(scope->data); } -sentry_value_t -sentry__scope_get_trace_context(const sentry_scope_t *scope) -{ - return data_get_trace_context(scope->data); -} - void sentry__scope_set_propagation_context( sentry_scope_t *scope, const char *key, sentry_value_t value) @@ -1491,6 +1503,19 @@ sentry__scope_regenerate_propagation_context(sentry_scope_t *scope) data_regenerate_propagation_context(scope->data); } +sentry_value_t +sentry__scope_ref_trace_context(const sentry_scope_t *scope) +{ + return data_ref_trace_context(scope->data); +} + +void +sentry__scope_set_trace_context( + sentry_scope_t *scope, const char *key, sentry_value_t value) +{ + data_set_trace_context(scope->data, key, value); +} + bool sentry__scope_is_trace_managed(const sentry_scope_t *scope) { diff --git a/src/sentry_scope.h b/src/sentry_scope.h index 2eaaa00580..8d595c368f 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -173,10 +173,12 @@ void sentry__scope_remove_context_n( sentry_value_t sentry__scope_get_propagation_context( const sentry_scope_t *scope); -sentry_value_t sentry__scope_get_trace_context(const sentry_scope_t *scope); void sentry__scope_set_propagation_context( sentry_scope_t *scope, const char *key, sentry_value_t value); void sentry__scope_regenerate_propagation_context(sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_trace_context(const sentry_scope_t *scope); +void sentry__scope_set_trace_context( + sentry_scope_t *scope, const char *key, sentry_value_t value); sentry_value_t sentry__scope_breadcrumbs_to_list(const sentry_scope_t *scope); diff --git a/src/sentry_tracing.c b/src/sentry_tracing.c index a812942208..ffe5d64733 100644 --- a/src/sentry_tracing.c +++ b/src/sentry_tracing.c @@ -96,7 +96,7 @@ transaction_context_new_n(sentry_slice_t name, sentry_slice_t operation) sentry_value_new_string_n(name.ptr, name.len)); SENTRY_WITH_SCOPE_MUT (scope) { - sentry_value_t trace_context = sentry__scope_get_trace_context(scope); + sentry_value_t trace_context = sentry__scope_ref_trace_context(scope); if (!sentry__scope_is_trace_managed(scope) && !sentry_value_is_null(trace_context)) { // The trace is managed from outside, so we use the propagation @@ -109,6 +109,7 @@ transaction_context_new_n(sentry_slice_t name, sentry_slice_t operation) sentry__value_clone( sentry_value_get_by_key(trace_context, "parent_span_id"))); } + sentry_value_decref(trace_context); } return transaction_context; diff --git a/tests/unit/test_logs.c b/tests/unit/test_logs.c index 13182ff536..440cd637c4 100644 --- a/tests/unit/test_logs.c +++ b/tests/unit/test_logs.c @@ -765,10 +765,13 @@ SENTRY_TEST(scope_capture_log_trace_id) SENTRY_LOG_RETURN_SUCCESS); SENTRY_WITH_SCOPE (global_scope) { + sentry_value_t trace_context + = sentry__scope_ref_trace_context(global_scope); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( captured_log, "trace_id")), - sentry_value_as_string(sentry_value_get_by_key( - sentry__scope_get_trace_context(global_scope), "trace_id"))); + sentry_value_as_string( + sentry_value_get_by_key(trace_context, "trace_id"))); + sentry_value_decref(trace_context); } sentry_scope_free(scope); diff --git a/tests/unit/test_tracing.c b/tests/unit/test_tracing.c index 31fc613c28..fc7ad9a353 100644 --- a/tests/unit/test_tracing.c +++ b/tests/unit/test_tracing.c @@ -1566,7 +1566,7 @@ SENTRY_TEST(set_trace) SENTRY_WITH_SCOPE (scope) { sentry_value_t propagation_trace_context - = sentry__scope_get_trace_context(scope); + = sentry__scope_ref_trace_context(scope); TEST_CHECK(!sentry_value_is_null(propagation_trace_context)); CHECK_STRING_PROPERTY(propagation_trace_context, "type", "trace"); @@ -1579,6 +1579,7 @@ SENTRY_TEST(set_trace) sentry_value_get_by_key(propagation_trace_context, "span_id")); TEST_ASSERT(!!span_id); TEST_CHECK(strlen(span_id) > 0); + sentry_value_decref(propagation_trace_context); } sentry_close(); @@ -2426,11 +2427,12 @@ SENTRY_TEST(strict_continuation_no_baggage_forks) // Scope propagation follows the fork: no lingering upstream trace_id. SENTRY_WITH_SCOPE (scope) { - const char *scope_trace_id - = sentry_value_as_string(sentry_value_get_by_key( - sentry__scope_get_trace_context(scope), "trace_id")); + sentry_value_t trace_context = sentry__scope_ref_trace_context(scope); + const char *scope_trace_id = sentry_value_as_string( + sentry_value_get_by_key(trace_context, "trace_id")); TEST_CHECK(strcmp(scope_trace_id, UPSTREAM_TRACE_ID) != 0); TEST_CHECK_STRING_EQUAL(scope_trace_id, trace_id); + sentry_value_decref(trace_context); } sentry_transaction_finish(tx); From e821d6e73ffb2c752ab580df2802c4de0520b6fe Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 7 Aug 2026 12:27:26 +0200 Subject: [PATCH 13/28] propagation_context --- src/sentry_scope.c | 10 ++-------- src/sentry_scope.h | 2 +- tests/unit/test_scope.c | 8 +++++--- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 7a3116ae18..9ad5d389dc 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -342,12 +342,6 @@ clone_data(const sentry_scope_data_t *source) return clone; } -static sentry_value_t -data_get_propagation_context(const sentry_scope_data_t *data) -{ - return data->propagation_context; -} - static sentry_value_t data_ref_propagation_context(const sentry_scope_data_t *data) { @@ -1485,9 +1479,9 @@ sentry_scope_clone(const sentry_scope_t *scope) } sentry_value_t -sentry__scope_get_propagation_context(const sentry_scope_t *scope) +sentry__scope_ref_propagation_context(const sentry_scope_t *scope) { - return data_get_propagation_context(scope->data); + return data_ref_propagation_context(scope->data); } void diff --git a/src/sentry_scope.h b/src/sentry_scope.h index 8d595c368f..3a46cd6479 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -171,7 +171,7 @@ void sentry__scope_remove_context(sentry_scope_t *scope, const char *key); void sentry__scope_remove_context_n( sentry_scope_t *scope, const char *key, size_t key_len); -sentry_value_t sentry__scope_get_propagation_context( +sentry_value_t sentry__scope_ref_propagation_context( const sentry_scope_t *scope); void sentry__scope_set_propagation_context( sentry_scope_t *scope, const char *key, sentry_value_t value); diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index 09deb9b4db..c151a3d2c6 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -2744,10 +2744,12 @@ SENTRY_TEST(scope_clear) sentry_value_decref(crumbs); // ... except the trace, which is preserved. - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key( - sentry__scope_get_propagation_context(scope), "marker")), + sentry_value_t propagation_context + = sentry__scope_ref_propagation_context(scope); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + propagation_context, "marker")), "keep"); + sentry_value_decref(propagation_context); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( sentry__scope_get_dsc(scope), "marker")), "keep"); From 2949eec74628e099515b81ba5b1508cc673763a1 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 7 Aug 2026 12:36:22 +0200 Subject: [PATCH 14/28] dsc --- src/sentry_envelope.c | 4 +-- src/sentry_scope.c | 58 +++++++++++++++++++++++++++++++++------ src/sentry_scope.h | 6 +++- src/sentry_tracing.c | 3 +- tests/unit/test_scope.c | 12 +++++--- tests/unit/test_tracing.c | 12 +++++--- 6 files changed, 74 insertions(+), 21 deletions(-) diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index a30f4f80a4..d02d0e50a7 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -415,7 +415,7 @@ sentry__envelope_add_event(sentry_envelope_t *envelope, sentry_value_t event) sentry_value_t dsc = sentry_value_new_null(); double sample_rand = (double)NAN; SENTRY_WITH_SCOPE (scope) { - dsc = sentry__value_clone(sentry__scope_get_dsc(scope)); + dsc = sentry__scope_clone_dsc(scope); sentry_value_t trace_context = sentry__scope_ref_trace_context(scope); sample_rand = sentry_value_as_double( sentry_value_get_by_key(trace_context, "sample_rand")); @@ -497,7 +497,7 @@ sentry__envelope_add_transaction( sentry_value_t dsc = sentry_value_new_null(); SENTRY_WITH_SCOPE (scope) { - dsc = sentry__value_clone(sentry__scope_get_dsc(scope)); + dsc = sentry__scope_clone_dsc(scope); } if (!sentry_value_is_null(dsc)) { diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 9ad5d389dc..91aa981676 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -410,12 +410,6 @@ data_set_trace_managed(sentry_scope_data_t *data, bool managed) DATA_WRITE_LOCK(data) { data->trace_managed = managed; } } -static sentry_value_t -data_get_dsc(const sentry_scope_data_t *data) -{ - return data->dynamic_sampling_context; -} - static void data_freeze_dsc(sentry_scope_data_t *data, sentry_value_t incoming) { @@ -428,6 +422,40 @@ data_freeze_dsc(sentry_scope_data_t *data, sentry_value_t incoming) } } +static sentry_value_t +data_ref_dsc(const sentry_scope_data_t *data) +{ + sentry_value_t dsc = sentry_value_new_null(); + DATA_READ_LOCK(data) + { + dsc = sentry_value_incref(data->dynamic_sampling_context); + } + return dsc; +} + +static sentry_value_t +data_clone_dsc(const sentry_scope_data_t *data) +{ + sentry_value_t dsc = sentry_value_new_null(); + DATA_READ_LOCK(data) + { + dsc = sentry__value_clone(data->dynamic_sampling_context); + } + return dsc; +} + +static void +data_foreach_dsc(const sentry_scope_data_t *data, + void (*callback)(const char *key, sentry_value_t value, void *userdata), + void *userdata) +{ + DATA_READ_LOCK(data) + { + sentry__value_foreach_key_value( + data->dynamic_sampling_context, callback, userdata); + } +} + static void data_update_dsc(sentry_scope_data_t *data, const sentry_options_t *options) { @@ -1523,9 +1551,23 @@ sentry__scope_set_trace_managed(sentry_scope_t *scope, bool managed) } sentry_value_t -sentry__scope_get_dsc(const sentry_scope_t *scope) +sentry__scope_ref_dsc(const sentry_scope_t *scope) +{ + return data_ref_dsc(scope->data); +} + +sentry_value_t +sentry__scope_clone_dsc(const sentry_scope_t *scope) +{ + return data_clone_dsc(scope->data); +} + +void +sentry__scope_foreach_dsc(const sentry_scope_t *scope, + void (*callback)(const char *key, sentry_value_t value, void *userdata), + void *userdata) { - return data_get_dsc(scope->data); + data_foreach_dsc(scope->data, callback, userdata); } void diff --git a/src/sentry_scope.h b/src/sentry_scope.h index 3a46cd6479..9859fd104c 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -261,7 +261,11 @@ void sentry__scope_end_notify(sentry_scope_t *scope); sentry__scope_end_notify(scope); \ } while (0) -sentry_value_t sentry__scope_get_dsc(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_dsc(const sentry_scope_t *scope); +sentry_value_t sentry__scope_clone_dsc(const sentry_scope_t *scope); +void sentry__scope_foreach_dsc(const sentry_scope_t *scope, + void (*callback)(const char *key, sentry_value_t value, void *userdata), + void *userdata); /** * Rebuilds the scope's dynamic sampling context (DSC) from the SDK options diff --git a/src/sentry_tracing.c b/src/sentry_tracing.c index ffe5d64733..4aba3a925e 100644 --- a/src/sentry_tracing.c +++ b/src/sentry_tracing.c @@ -961,8 +961,7 @@ sentry__span_iter_headers(sentry_value_t span, sentry__stringbuilder_append(&sb, sentry_value_as_string(trace_id)); SENTRY_WITH_SCOPE (scope) { - sentry__value_foreach_key_value( - sentry__scope_get_dsc(scope), append_baggage_member, &sb); + sentry__scope_foreach_dsc(scope, append_baggage_member, &sb); } char *baggage = sentry__stringbuilder_into_string(&sb); diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index c151a3d2c6..606be09dca 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -2698,8 +2698,10 @@ SENTRY_TEST(scope_clear) // dynamic sampling context. sentry__scope_set_propagation_context( scope, "marker", sentry_value_new_string("keep")); - sentry_value_set_by_key(sentry__scope_get_dsc(scope), "marker", - sentry_value_new_string("keep")); + sentry_value_t dsc = sentry_value_new_object(); + sentry_value_set_by_key(dsc, "marker", sentry_value_new_string("keep")); + sentry__scope_freeze_dsc(scope, dsc); + sentry_value_decref(dsc); sentry_scope_set_tag(scope, "tag", "value"); sentry_scope_set_extra(scope, "extra", sentry_value_new_string("value")); @@ -2750,9 +2752,11 @@ SENTRY_TEST(scope_clear) propagation_context, "marker")), "keep"); sentry_value_decref(propagation_context); - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - sentry__scope_get_dsc(scope), "marker")), + sentry_value_t scope_dsc = sentry__scope_ref_dsc(scope); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(scope_dsc, "marker")), "keep"); + sentry_value_decref(scope_dsc); // The cleared scope is still usable. sentry_scope_set_tag(scope, "after", "clear"); diff --git a/tests/unit/test_tracing.c b/tests/unit/test_tracing.c index fc7ad9a353..320d8895af 100644 --- a/tests/unit/test_tracing.c +++ b/tests/unit/test_tracing.c @@ -2477,16 +2477,20 @@ SENTRY_TEST(set_trace_rebuilds_dsc_sample_rand) double init_sample_rand = 0.0; SENTRY_WITH_SCOPE (scope) { - init_sample_rand = sentry_value_as_double(sentry_value_get_by_key( - sentry__scope_get_dsc(scope), "sample_rand")); + sentry_value_t dsc = sentry__scope_ref_dsc(scope); + init_sample_rand = sentry_value_as_double( + sentry_value_get_by_key(dsc, "sample_rand")); + sentry_value_decref(dsc); } sentry_set_trace("11112222333344445555666677778888", "1234567812345678"); double new_sample_rand = -1.0; SENTRY_WITH_SCOPE (scope) { - new_sample_rand = sentry_value_as_double(sentry_value_get_by_key( - sentry__scope_get_dsc(scope), "sample_rand")); + sentry_value_t dsc = sentry__scope_ref_dsc(scope); + new_sample_rand = sentry_value_as_double( + sentry_value_get_by_key(dsc, "sample_rand")); + sentry_value_decref(dsc); } // sample_rand is regenerated for the new trace, so the DSC must reflect // the fresh value, not the init-time one. From 16b07d899cfc1c98386635c372cebb866d102b4e Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 7 Aug 2026 13:10:37 +0200 Subject: [PATCH 15/28] span/transaction_object --- src/sentry_scope.c | 49 ++++++++++++------------ src/sentry_scope.h | 4 +- src/sentry_tracing.c | 10 +---- tests/unit/test_scope.c | 79 +++++++++++++++++++++++++++------------ tests/unit/test_tracing.c | 22 ++++++++--- 5 files changed, 102 insertions(+), 62 deletions(-) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 91aa981676..bb4619108b 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -839,14 +839,18 @@ static sentry_uuid_t data_get_last_event_id(const sentry_scope_data_t *data) { sentry_uuid_t event_id = sentry_uuid_nil(); - DATA_READ_LOCK(data) { event_id = data->last_event_id; } + DATA_READ_LOCK (data) { + event_id = data->last_event_id; + } return event_id; } static void data_set_last_event_id(sentry_scope_data_t *data, sentry_uuid_t event_id) { - DATA_WRITE_LOCK(data) { data->last_event_id = event_id; } + DATA_WRITE_LOCK (data) { + data->last_event_id = event_id; + } } static sentry_value_t @@ -895,10 +899,14 @@ data_take_attachments(sentry_scope_data_t *data) } static sentry_transaction_t * -data_get_transaction_object(const sentry_scope_data_t *data) +data_ref_transaction_object(const sentry_scope_data_t *data) { sentry_transaction_t *transaction = NULL; - DATA_READ_LOCK(data) { transaction = data->transaction_object; } + DATA_READ_LOCK(data) + { + transaction = data->transaction_object; + sentry__transaction_incref(transaction); + } return transaction; } @@ -977,10 +985,14 @@ data_restore_transaction_object( } static sentry_span_t * -data_get_span(const sentry_scope_data_t *data) +data_ref_span(const sentry_scope_data_t *data) { sentry_span_t *span = NULL; - DATA_READ_LOCK(data) { span = data->span; } + DATA_READ_LOCK(data) + { + span = data->span; + sentry__span_incref(span); + } return span; } @@ -2258,23 +2270,13 @@ void sentry_scope_set_transaction_object( sentry_scope_t *scope, sentry_transaction_t *tx) { - sentry__span_decref(scope->span); - scope->span = NULL; - // incref before decref, so rebinding the same object cannot free it - sentry__transaction_incref(tx); - sentry__transaction_decref(scope->transaction_object); - scope->transaction_object = tx; + sentry__scope_set_transaction_object(scope, tx); } void sentry_scope_set_span(sentry_scope_t *scope, sentry_span_t *span) { - sentry__transaction_decref(scope->transaction_object); - scope->transaction_object = NULL; - // incref before decref, so rebinding the same object cannot free it - sentry__span_incref(span); - sentry__span_decref(scope->span); - scope->span = span; + sentry__scope_set_span(scope, span); } sentry_value_t @@ -2323,9 +2325,9 @@ sentry_scope_add_attachment(sentry_scope_t *scope, sentry_value_t attachment) } sentry_transaction_t * -sentry__scope_get_transaction_object(const sentry_scope_t *scope) +sentry__scope_ref_transaction_object(const sentry_scope_t *scope) { - return data_get_transaction_object(scope->data); + return data_ref_transaction_object(scope->data); } void @@ -2357,9 +2359,9 @@ sentry__scope_restore_transaction_object( } sentry_span_t * -sentry__scope_get_span(const sentry_scope_t *scope) +sentry__scope_ref_span(const sentry_scope_t *scope) { - return data_get_span(scope->data); + return data_ref_span(scope->data); } void @@ -2552,8 +2554,7 @@ sentry_scope_get_last_event_id(const sentry_scope_t *scope) } void -sentry__scope_set_last_event_id( - sentry_scope_t *scope, sentry_uuid_t event_id) +sentry__scope_set_last_event_id(sentry_scope_t *scope, sentry_uuid_t event_id) { if (scope) { data_set_last_event_id(scope->data, event_id); diff --git a/src/sentry_scope.h b/src/sentry_scope.h index 9859fd104c..ba5fb1e339 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -182,7 +182,7 @@ void sentry__scope_set_trace_context( sentry_value_t sentry__scope_breadcrumbs_to_list(const sentry_scope_t *scope); -sentry_transaction_t *sentry__scope_get_transaction_object( +sentry_transaction_t *sentry__scope_ref_transaction_object( const sentry_scope_t *scope); void sentry__scope_set_transaction_object( sentry_scope_t *scope, sentry_transaction_t *transaction); @@ -193,7 +193,7 @@ bool sentry__scope_remove_transaction_value( bool sentry__scope_restore_transaction_object( sentry_scope_t *scope, sentry_transaction_t *transaction); -sentry_span_t *sentry__scope_get_span(const sentry_scope_t *scope); +sentry_span_t *sentry__scope_ref_span(const sentry_scope_t *scope); void sentry__scope_set_span(sentry_scope_t *scope, sentry_span_t *span); bool sentry__scope_remove_span(sentry_scope_t *scope, sentry_span_t *span); bool sentry__scope_remove_span_value( diff --git a/src/sentry_tracing.c b/src/sentry_tracing.c index 4aba3a925e..e9efc7a5b6 100644 --- a/src/sentry_tracing.c +++ b/src/sentry_tracing.c @@ -1015,14 +1015,8 @@ save_active_trace(void) { saved_trace_t s = { 0 }; SENTRY_WITH_SCOPE (scope) { - s.saved_span = sentry__scope_get_span(scope); - if (s.saved_span) { - sentry__span_incref(s.saved_span); - } - s.saved_tx_obj = sentry__scope_get_transaction_object(scope); - if (s.saved_tx_obj) { - sentry__transaction_incref(s.saved_tx_obj); - } + s.saved_span = sentry__scope_ref_span(scope); + s.saved_tx_obj = sentry__scope_ref_transaction_object(scope); } s.active_tx = s.saved_span && s.saved_span->transaction ? s.saved_span->transaction diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index 606be09dca..1a66b19e29 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -2671,13 +2671,17 @@ SENTRY_TEST(scope_clone_shares_span) sentry_scope_t *clone = NULL; sentry_transaction_t *scope_txn = NULL; SENTRY_WITH_SCOPE (scope) { - scope_txn = sentry__scope_get_transaction_object(scope); + scope_txn = sentry__scope_ref_transaction_object(scope); clone = sentry_scope_clone(scope); } // The active transaction is shared by reference, not dropped or duplicated. TEST_CHECK(scope_txn != NULL); - TEST_CHECK(sentry__scope_get_transaction_object(clone) == scope_txn); + sentry_transaction_t *clone_txn + = sentry__scope_ref_transaction_object(clone); + TEST_CHECK(clone_txn == scope_txn); + sentry__transaction_decref(clone_txn); + sentry__transaction_decref(scope_txn); // The shared reference keeps the transaction alive for the original: the // clone can be freed and the transaction still finished safely. @@ -3013,8 +3017,8 @@ SENTRY_TEST(scope_bind_transaction_object) // After unbinding, event falls back to the propagation context. TEST_ASSERT(!sentry_value_is_null(trace)); SENTRY_WITH_SCOPE (global_scope) { - sentry_value_t propagation_trace = sentry_value_get_by_key( - global_scope->propagation_context, "trace"); + sentry_value_t propagation_trace + = sentry__scope_ref_trace_context(global_scope); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(trace, "trace_id")), sentry_value_as_string( @@ -3023,6 +3027,7 @@ SENTRY_TEST(scope_bind_transaction_object) sentry_value_as_string(sentry_value_get_by_key(trace, "span_id")), sentry_value_as_string( sentry_value_get_by_key(propagation_trace, "span_id"))); + sentry_value_decref(propagation_trace); } sentry_value_decref(trace); @@ -3052,8 +3057,13 @@ SENTRY_TEST(scope_bind_span) // Binding a scope of our own leaves the global scope alone. SENTRY_WITH_SCOPE (global_scope) { - TEST_CHECK(global_scope->span == NULL); - TEST_CHECK(global_scope->transaction_object == NULL); + sentry_span_t *global_span = sentry__scope_ref_span(global_scope); + sentry_transaction_t *global_tx + = sentry__scope_ref_transaction_object(global_scope); + TEST_CHECK(global_span == NULL); + TEST_CHECK(global_tx == NULL); + sentry__span_decref(global_span); + sentry__transaction_decref(global_tx); } sentry_scope_capture_event(scope, @@ -3074,7 +3084,9 @@ SENTRY_TEST(scope_bind_span) // TODO: Finishing a span releases the caller's reference and only clears // the global scope. A user-owned scope still stamps that finished span onto // later events; this acknowledges the current behavior until it changes. - TEST_CHECK_PTR_EQUAL(scope->span, span); + sentry_span_t *bound_span = sentry__scope_ref_span(scope); + TEST_CHECK_PTR_EQUAL(bound_span, span); + sentry__span_decref(bound_span); sentry_value_decref(trace); sentry_scope_free(scope); @@ -3099,17 +3111,30 @@ SENTRY_TEST(scope_bind_span_or_transaction_not_both) sentry_scope_t *scope = sentry_scope_new(); sentry_scope_set_span(scope, span); sentry_scope_set_transaction_object(scope, tx); - TEST_CHECK(scope->span == NULL); - TEST_CHECK_PTR_EQUAL(scope->transaction_object, tx); + sentry_span_t *bound_span = sentry__scope_ref_span(scope); + sentry_transaction_t *bound_tx + = sentry__scope_ref_transaction_object(scope); + TEST_CHECK(bound_span == NULL); + TEST_CHECK_PTR_EQUAL(bound_tx, tx); + sentry__span_decref(bound_span); + sentry__transaction_decref(bound_tx); sentry_scope_set_span(scope, span); - TEST_CHECK(scope->transaction_object == NULL); - TEST_CHECK_PTR_EQUAL(scope->span, span); + bound_tx = sentry__scope_ref_transaction_object(scope); + bound_span = sentry__scope_ref_span(scope); + TEST_CHECK(bound_tx == NULL); + TEST_CHECK_PTR_EQUAL(bound_span, span); + sentry__transaction_decref(bound_tx); + sentry__span_decref(bound_span); // Passing null unbinds both. sentry_scope_set_transaction_object(scope, NULL); - TEST_CHECK(scope->span == NULL); - TEST_CHECK(scope->transaction_object == NULL); + bound_span = sentry__scope_ref_span(scope); + bound_tx = sentry__scope_ref_transaction_object(scope); + TEST_CHECK(bound_span == NULL); + TEST_CHECK(bound_tx == NULL); + sentry__span_decref(bound_span); + sentry__transaction_decref(bound_tx); sentry_scope_free(scope); sentry_span_finish(span); @@ -3138,21 +3163,25 @@ SENTRY_TEST(scope_rebind_same_object) // Rebinding what is already bound must not drop that last reference (a // use-after-free here would trip the sanitizers). - sentry_scope_set_span(scope, scope->span); - TEST_CHECK_PTR_EQUAL(scope->span, span); + sentry_scope_set_span(scope, span); + sentry_span_t *bound_span = sentry__scope_ref_span(scope); + TEST_CHECK_PTR_EQUAL(bound_span, span); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - scope->span->inner, "description")), + bound_span->inner, "description")), "select"); + sentry__span_decref(bound_span); sentry_scope_set_transaction_object(scope, tx); sentry_transaction_finish(tx); - sentry_scope_set_transaction_object(scope, scope->transaction_object); - TEST_CHECK_PTR_EQUAL(scope->transaction_object, tx); - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key( - scope->transaction_object->inner, "transaction")), + sentry_scope_set_transaction_object(scope, tx); + sentry_transaction_t *bound_tx + = sentry__scope_ref_transaction_object(scope); + TEST_CHECK_PTR_EQUAL(bound_tx, tx); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + bound_tx->inner, "transaction")), "txn"); + sentry__transaction_decref(bound_tx); sentry_scope_free(scope); @@ -3174,15 +3203,19 @@ SENTRY_TEST(scope_clone_keeps_bound_span) sentry_scope_t *scope = sentry_scope_new(); sentry_scope_set_span(scope, span); sentry_scope_t *clone = sentry_scope_clone(scope); - TEST_CHECK_PTR_EQUAL(clone->span, span); + sentry_span_t *clone_span = sentry__scope_ref_span(clone); + TEST_CHECK_PTR_EQUAL(clone_span, span); + sentry__span_decref(clone_span); // The clone owns its binding, so it outlives the original scope and caller // reference. sentry_scope_free(scope); sentry_span_finish(span); + clone_span = sentry__scope_ref_span(clone); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - clone->span->inner, "description")), + clone_span->inner, "description")), "select"); + sentry__span_decref(clone_span); sentry_scope_free(clone); sentry_transaction_finish(tx); diff --git a/tests/unit/test_tracing.c b/tests/unit/test_tracing.c index 320d8895af..be4ef66ec5 100644 --- a/tests/unit/test_tracing.c +++ b/tests/unit/test_tracing.c @@ -852,7 +852,9 @@ SENTRY_TEST(trace_finish) // Scope still points at the (finished) span so a subsequent crash event // inherits its trace context. SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(sentry__scope_get_span(scope) != NULL); + sentry_span_t *scope_span = sentry__scope_ref_span(scope); + TEST_CHECK(scope_span != NULL); + sentry__span_decref(scope_span); } sentry__span_decref(grand); @@ -934,13 +936,19 @@ SENTRY_TEST(discard_transaction) sentry_set_transaction_object(tx); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(sentry__scope_get_transaction_object(scope) == tx); + sentry_transaction_t *scope_tx + = sentry__scope_ref_transaction_object(scope); + TEST_CHECK(scope_tx == tx); + sentry__transaction_decref(scope_tx); } sentry_transaction_discard(tx); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(sentry__scope_get_transaction_object(scope) == NULL); + sentry_transaction_t *scope_tx + = sentry__scope_ref_transaction_object(scope); + TEST_CHECK(scope_tx == NULL); + sentry__transaction_decref(scope_tx); } sentry_close(); @@ -970,13 +978,17 @@ SENTRY_TEST(discard_span) sentry_set_span(span); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(sentry__scope_get_span(scope) == span); + sentry_span_t *scope_span = sentry__scope_ref_span(scope); + TEST_CHECK(scope_span == span); + sentry__span_decref(scope_span); } sentry_span_discard(span); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(sentry__scope_get_span(scope) == NULL); + sentry_span_t *scope_span = sentry__scope_ref_span(scope); + TEST_CHECK(scope_span == NULL); + sentry__span_decref(scope_span); } TEST_CHECK_INT_EQUAL( sentry_value_get_length(sentry_value_get_by_key(tx->inner, "spans")), From 41717ae99529171b4bdf6c5fe8b52f6ef02f3f67 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 7 Aug 2026 13:21:45 +0200 Subject: [PATCH 16/28] restore --- src/sentry_scope.c | 23 ++++-------------- src/sentry_scope.h | 2 +- tests/unit/test_scope.c | 50 +++++++++++++++++++++++++++++++++++++++ tests/unit/test_tracing.c | 25 +++++++++++++------- tests/unit/tests.inc | 1 + 5 files changed, 72 insertions(+), 29 deletions(-) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index bb4619108b..186d62777e 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -976,7 +976,7 @@ data_restore_transaction_object( bool restored = false; DATA_WRITE_LOCK(data) { - if (!data->transaction_object && transaction) { + if (!data->transaction_object && !data->span && transaction) { data->transaction_object = transaction; restored = true; } @@ -1049,7 +1049,7 @@ data_restore_span(sentry_scope_data_t *data, sentry_span_t *span) bool restored = false; DATA_WRITE_LOCK(data) { - if (!data->span && span) { + if (!data->span && !data->transaction_object && span) { data->span = span; restored = true; } @@ -1698,27 +1698,12 @@ sentry__symbolize_stacktrace(sentry_value_t stacktrace) #endif #ifdef SENTRY_UNITTEST -static sentry_value_t -data_get_span_or_transaction(const sentry_scope_data_t *data) -{ - sentry_value_t value = sentry_value_new_null(); - DATA_READ_LOCK(data) - { - if (data->span) { - value = data->span->inner; - } else if (data->transaction_object) { - value = data->transaction_object->inner; - } - } - return value; -} - sentry_value_t -sentry__scope_get_span_or_transaction(void) +sentry__scope_ref_span_or_transaction(void) { sentry_value_t result = sentry_value_new_null(); SENTRY_WITH_SCOPE (scope) { - result = data_get_span_or_transaction(scope->data); + result = data_ref_span_or_transaction(scope->data); } return result; } diff --git a/src/sentry_scope.h b/src/sentry_scope.h index ba5fb1e339..cf092a660f 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -304,6 +304,6 @@ void sentry__scope_capture_envelope(sentry_scope_t *scope, // this is only used in unit tests #ifdef SENTRY_UNITTEST -sentry_value_t sentry__scope_get_span_or_transaction(void); +sentry_value_t sentry__scope_ref_span_or_transaction(void); bool sentry__scope_has_observers(const sentry_scope_t *scope); #endif diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index 1a66b19e29..ea756e8f06 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -2691,6 +2691,56 @@ SENTRY_TEST(scope_clone_shares_span) sentry_close(); } +SENTRY_TEST(scope_restore_trace) +{ + SENTRY_TEST_OPTIONS_NEW(options); + sentry_options_set_traces_sample_rate(options, 1.0); + sentry_init(options); + + sentry_scope_t *scope = sentry_scope_new(); + sentry_transaction_context_t *tx_ctx + = sentry_transaction_context_new("txn", NULL); + sentry_transaction_t *tx + = sentry_transaction_start(tx_ctx, sentry_value_new_null()); + sentry_span_t *span = sentry_transaction_start_child(tx, "op", NULL); + + sentry__scope_set_transaction_object(scope, tx); + TEST_CHECK(!sentry__scope_restore_span(scope, span)); + + sentry_span_t *scope_span = sentry__scope_ref_span(scope); + TEST_CHECK(scope_span == NULL); + sentry__span_decref(scope_span); + sentry_transaction_t *scope_tx + = sentry__scope_ref_transaction_object(scope); + TEST_CHECK(scope_tx == tx); + sentry__transaction_decref(scope_tx); + + sentry_scope_free(scope); + sentry__span_decref(span); + sentry__transaction_decref(tx); + + scope = sentry_scope_new(); + tx_ctx = sentry_transaction_context_new("txn", NULL); + tx = sentry_transaction_start(tx_ctx, sentry_value_new_null()); + span = sentry_transaction_start_child(tx, "op", NULL); + + sentry__scope_set_span(scope, span); + TEST_CHECK(!sentry__scope_restore_transaction_object(scope, tx)); + + scope_tx = sentry__scope_ref_transaction_object(scope); + TEST_CHECK(scope_tx == NULL); + sentry__transaction_decref(scope_tx); + scope_span = sentry__scope_ref_span(scope); + TEST_CHECK(scope_span == span); + sentry__span_decref(scope_span); + + sentry_scope_free(scope); + sentry__span_decref(span); + sentry__transaction_decref(tx); + + sentry_close(); +} + SENTRY_TEST(scope_clear) { SENTRY_TEST_OPTIONS_NEW(options); diff --git a/tests/unit/test_tracing.c b/tests/unit/test_tracing.c index be4ef66ec5..02e2a6197f 100644 --- a/tests/unit/test_tracing.c +++ b/tests/unit/test_tracing.c @@ -420,12 +420,14 @@ SENTRY_TEST(multiple_transactions) = sentry_transaction_start(tx_ctx, sentry_value_new_null()); sentry_set_transaction_object(tx); - sentry_value_t scope_tx = sentry__scope_get_span_or_transaction(); + sentry_value_t scope_tx = sentry__scope_ref_span_or_transaction(); CHECK_STRING_PROPERTY(scope_tx, "transaction", "wow!"); + sentry_value_decref(scope_tx); sentry_uuid_t event_id = sentry_transaction_finish(tx); - scope_tx = sentry__scope_get_span_or_transaction(); + scope_tx = sentry__scope_ref_span_or_transaction(); TEST_CHECK(sentry_value_is_null(scope_tx)); + sentry_value_decref(scope_tx); TEST_CHECK(!sentry_uuid_is_nil(&event_id)); // Set transaction on scope twice, back-to-back without finishing the first @@ -437,8 +439,9 @@ SENTRY_TEST(multiple_transactions) tx_ctx = sentry_transaction_context_new("wowee!", NULL); tx = sentry_transaction_start(tx_ctx, sentry_value_new_null()); sentry_set_transaction_object(tx); - scope_tx = sentry__scope_get_span_or_transaction(); + scope_tx = sentry__scope_ref_span_or_transaction(); CHECK_STRING_PROPERTY(scope_tx, "transaction", "wowee!"); + sentry_value_decref(scope_tx); event_id = sentry_transaction_finish(tx); TEST_CHECK(!sentry_uuid_is_nil(&event_id)); @@ -520,20 +523,21 @@ SENTRY_TEST(spans_on_scope) // Peek into the transaction's span list and make sure everything is // good - sentry_value_t scope_tx = sentry__scope_get_span_or_transaction(); - const char *trace_id - = sentry_value_as_string(sentry_value_get_by_key(scope_tx, "trace_id")); - const char *parent_span_id - = sentry_value_as_string(sentry_value_get_by_key(scope_tx, "span_id")); + sentry_value_t scope_tx = sentry__scope_ref_span_or_transaction(); + char *trace_id = sentry__string_clone( + sentry_value_as_string(sentry_value_get_by_key(scope_tx, "trace_id"))); + char *parent_span_id = sentry__string_clone( + sentry_value_as_string(sentry_value_get_by_key(scope_tx, "span_id"))); // Don't track the span yet TEST_CHECK(IS_NULL(scope_tx, "spans")); + sentry_value_decref(scope_tx); // Sanity check that child isn't finished yet TEST_CHECK(IS_NULL(child, "timestamp")); sentry_span_finish(opaque_child); - scope_tx = sentry__scope_get_span_or_transaction(); + scope_tx = sentry__scope_ref_span_or_transaction(); TEST_CHECK(!IS_NULL(scope_tx, "spans")); sentry_value_t spans = sentry_value_get_by_key(scope_tx, "spans"); TEST_CHECK_INT_EQUAL(sentry_value_get_length(spans), 1); @@ -546,6 +550,9 @@ SENTRY_TEST(spans_on_scope) CHECK_STRING_PROPERTY(stored_child, "description", "goose"); // Should be finished TEST_CHECK(!IS_NULL(stored_child, "timestamp")); + sentry_value_decref(scope_tx); + sentry_free(trace_id); + sentry_free(parent_span_id); sentry__transaction_decref(opaque_tx); diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index b5d2e8f1a8..f2cc1bba35 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -363,6 +363,7 @@ XX(scope_ownership) XX(scope_propagation_context) XX(scope_rebind_same_object) XX(scope_remove_fingerprint_capture) +XX(scope_restore_trace) XX(scope_set_attribute_invalid_decref_value) XX(scope_set_attribute_null_key_decref_value) XX(scope_tags) From 768a86a7efd62e8336f2719c2076029e21a46c33 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 7 Aug 2026 16:18:42 +0200 Subject: [PATCH 17/28] lock observers --- src/sentry_scope.c | 40 ++++++++++++++++++++++++++++++++++++---- src/sentry_scope.h | 11 ++++++----- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 186d62777e..269c2f2294 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -1207,6 +1207,7 @@ init_scope(sentry_scope_t *scope, sentry_scope_data_t *data) scope->num_observers = 0; scope->is_notifying = 0; scope->pending_flush = false; + sentry__mutex_init(&scope->observers_lock); scope->one_shot = false; return true; } @@ -1235,13 +1236,16 @@ get_scope(void) static void cleanup_observers(sentry_scope_t *scope) { + sentry__mutex_lock(&scope->observers_lock); for (size_t i = 0; i < scope->num_observers; i++) { sentry_free(scope->observers[i]); } sentry_free(scope->observers); scope->observers = NULL; scope->num_observers = 0; + scope->is_notifying = 0; scope->pending_flush = false; + sentry__mutex_unlock(&scope->observers_lock); } static void @@ -1250,6 +1254,7 @@ cleanup_scope(sentry_scope_t *scope) free_data(scope->data); scope->data = NULL; cleanup_observers(scope); + sentry__mutex_free(&scope->observers_lock); } void @@ -1262,6 +1267,7 @@ sentry__scope_cleanup(void) 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); } @@ -1279,6 +1285,7 @@ unlock_scope(bool flush) { SENTRY__MUTEX_INIT_DYN_ONCE(g_lock); + sentry__mutex_lock(&g_scope.observers_lock); if (g_scope.is_notifying > 0) { // defer the flush requested by a reentrant scope change g_scope.pending_flush = flush || g_scope.pending_flush; @@ -1288,6 +1295,7 @@ unlock_scope(bool flush) flush = flush || g_scope.pending_flush; g_scope.pending_flush = false; } + sentry__mutex_unlock(&g_scope.observers_lock); // we try to unlock the scope as soon as possible. The // backend will do its own `WITH_SCOPE` internally. @@ -1327,10 +1335,12 @@ sentry__scope_add_observer( return false; } + sentry__mutex_lock(&scope->observers_lock); size_t new_count = scope->num_observers + 1; sentry_scope_observer_t **new_array = sentry__calloc(new_count, sizeof(sentry_scope_observer_t *)); if (!new_array) { + sentry__mutex_unlock(&scope->observers_lock); sentry_free(observer); return false; } @@ -1342,6 +1352,7 @@ sentry__scope_add_observer( new_array[scope->num_observers] = observer; scope->observers = new_array; scope->num_observers = new_count; + sentry__mutex_unlock(&scope->observers_lock); return true; } @@ -1349,7 +1360,13 @@ void sentry__scope_remove_observer( sentry_scope_t *scope, sentry_scope_observer_t *observer) { - if (!observer || !scope->observers) { + if (!observer) { + return; + } + + sentry__mutex_lock(&scope->observers_lock); + if (!scope->observers) { + sentry__mutex_unlock(&scope->observers_lock); return; } @@ -1362,6 +1379,7 @@ sentry__scope_remove_observer( if (scope->is_notifying) { // avoid shifting the array while SENTRY_SCOPE_NOTIFY is iterating scope->observers[i] = NULL; + sentry__mutex_unlock(&scope->observers_lock); return; } for (size_t j = i + 1; j < scope->num_observers; j++) { @@ -1372,13 +1390,16 @@ sentry__scope_remove_observer( sentry_free(scope->observers); scope->observers = NULL; } + sentry__mutex_unlock(&scope->observers_lock); return; } + sentry__mutex_unlock(&scope->observers_lock); } size_t sentry__scope_begin_notify(sentry_scope_t *scope) { + sentry__mutex_lock(&scope->observers_lock); scope->is_notifying++; return scope->num_observers; } @@ -1387,9 +1408,11 @@ void sentry__scope_end_notify(sentry_scope_t *scope) { if (--scope->is_notifying > 0) { + sentry__mutex_unlock(&scope->observers_lock); return; } if (!scope->observers) { + sentry__mutex_unlock(&scope->observers_lock); return; } @@ -1406,6 +1429,7 @@ sentry__scope_end_notify(sentry_scope_t *scope) sentry_free(scope->observers); scope->observers = NULL; } + sentry__mutex_unlock(&scope->observers_lock); } sentry_scope_t * @@ -1509,8 +1533,13 @@ sentry_scope_clone(const sentry_scope_t *scope) return NULL; } - clone->data = clone_data(scope->data); - if (!clone->data) { + sentry_scope_data_t *data = clone_data(scope->data); + if (!data) { + sentry_free(clone); + return NULL; + } + if (!init_scope(clone, data)) { + free_data(data); sentry_free(clone); return NULL; } @@ -1711,7 +1740,10 @@ sentry__scope_ref_span_or_transaction(void) bool sentry__scope_has_observers(const sentry_scope_t *scope) { - return scope->num_observers > 0; + sentry__mutex_lock((sentry_mutex_t *)&scope->observers_lock); + bool has_observers = scope->num_observers > 0; + sentry__mutex_unlock((sentry_mutex_t *)&scope->observers_lock); + return has_observers; } #endif diff --git a/src/sentry_scope.h b/src/sentry_scope.h index cf092a660f..9b912030be 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -13,7 +13,7 @@ * Scope observer — one callback per scope property. * * Implementors set the function pointers they care about. NULL pointers are - * skipped. Callbacks are invoked while the scope lock is held. + * skipped. Callbacks are invoked while the scope observer lock is held. * The data pointer is passed as the first argument to each callback. * * Note: callback arguments are borrowed and valid only for the duration of the @@ -58,6 +58,7 @@ typedef struct sentry_scope_data_s sentry_scope_data_t; struct sentry_scope_s { sentry_scope_data_t *data; + sentry_mutex_t observers_lock; sentry_scope_observer_t **observers; size_t num_observers; size_t is_notifying; @@ -230,8 +231,8 @@ sentry_scope_observer_t *sentry__scope_observer_new(void); * Register a scope observer. * * Takes ownership of `observer`; the caller must not free it after this call. - * Must be called while holding the scope lock. Registration order is respected - * — observers are notified in registration order. + * Registration order is respected: observers are notified in registration + * order. */ bool sentry__scope_add_observer( sentry_scope_t *scope, sentry_scope_observer_t *observer); @@ -239,8 +240,8 @@ bool sentry__scope_add_observer( /** * Remove a scope observer. * - * Frees `observer` if it is registered. Must be called while holding the scope - * lock. Does nothing if `observer` is NULL or not registered. + * Frees `observer` if it is registered. Does nothing if `observer` is NULL or + * not registered. */ void sentry__scope_remove_observer( sentry_scope_t *scope, sentry_scope_observer_t *observer); From b3a38df11b2b78cfebdcfabb0855d3f24e24a35a Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 7 Aug 2026 16:40:09 +0200 Subject: [PATCH 18/28] finally :D --- src/sentry_scope.c | 35 ++++++++++++++++++++++++++++------- src/sentry_scope.h | 26 ++++++++++++++++++-------- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 269c2f2294..dfa0fa1a4e 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -1280,10 +1280,22 @@ sentry__scope_lock(void) return get_scope(); } -static void -unlock_scope(bool flush) +sentry_scope_t * +sentry__scope_begin(void) { SENTRY__MUTEX_INIT_DYN_ONCE(g_lock); + sentry__mutex_lock(&g_lock); + sentry_scope_t *scope = get_scope(); + sentry__mutex_unlock(&g_lock); + return scope; +} + +static void +finish_scope(bool flush, bool unlock) +{ + if (unlock) { + SENTRY__MUTEX_INIT_DYN_ONCE(g_lock); + } sentry__mutex_lock(&g_scope.observers_lock); if (g_scope.is_notifying > 0) { @@ -1297,9 +1309,12 @@ unlock_scope(bool flush) } sentry__mutex_unlock(&g_scope.observers_lock); - // we try to unlock the scope as soon as possible. The - // backend will do its own `WITH_SCOPE` internally. - sentry__mutex_unlock(&g_lock); + if (unlock) { + // we try to unlock the scope as soon as possible. The + // backend will do its own `WITH_SCOPE` internally. + sentry__mutex_unlock(&g_lock); + } + if (flush) { SENTRY_WITH_OPTIONS (options) { if (options->backend && options->backend->flush_scope_func) { @@ -1312,13 +1327,19 @@ unlock_scope(bool flush) void sentry__scope_unlock(void) { - unlock_scope(false); + finish_scope(false, true); } void sentry__scope_flush_unlock(void) { - unlock_scope(true); + finish_scope(true, true); +} + +void +sentry__scope_end(bool flush) +{ + finish_scope(flush, false); } sentry_scope_observer_t * diff --git a/src/sentry_scope.h b/src/sentry_scope.h index 9b912030be..d2caee46be 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -90,6 +90,11 @@ typedef enum { */ sentry_scope_t *sentry__scope_lock(void); +/** + * This will return the global scope, initializing it if needed. + */ +sentry_scope_t *sentry__scope_begin(void); + /** * Release the lock on the global scope. */ @@ -118,6 +123,12 @@ void sentry__scope_free_one_shot(sentry_scope_t *scope); */ void sentry__scope_flush_unlock(void); +/** + * This will finish a global scope access, optionally notifying any backend of + * scope changes. + */ +void sentry__scope_end(bool flush); + /** * This will merge the requested data which is in the given `scope` to the given * `event`. @@ -205,18 +216,17 @@ bool sentry__scope_is_trace_managed(const sentry_scope_t *scope); void sentry__scope_set_trace_managed(sentry_scope_t *scope, bool managed); /** - * These are convenience macros to automatically lock/unlock the global scope - * inside a code block. + * These are convenience macros to access the global scope inside a code block. */ #define SENTRY_WITH_SCOPE(Scope) \ - for (const sentry_scope_t *Scope = sentry__scope_lock(); Scope; \ - sentry__scope_unlock(), Scope = NULL) + for (const sentry_scope_t *Scope = sentry__scope_begin(); Scope; \ + sentry__scope_end(false), Scope = NULL) #define SENTRY_WITH_SCOPE_MUT(Scope) \ - for (sentry_scope_t *Scope = sentry__scope_lock(); Scope; \ - sentry__scope_flush_unlock(), Scope = NULL) + for (sentry_scope_t *Scope = sentry__scope_begin(); Scope; \ + sentry__scope_end(true), Scope = NULL) #define SENTRY_WITH_SCOPE_MUT_NO_FLUSH(Scope) \ - for (sentry_scope_t *Scope = sentry__scope_lock(); Scope; \ - sentry__scope_unlock(), Scope = NULL) + for (sentry_scope_t *Scope = sentry__scope_begin(); Scope; \ + sentry__scope_end(false), Scope = NULL) /** * Allocate and zero-initialize a scope observer. From 61892ba196c58ced9016b6fa2719d3aaad7ef271 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 7 Aug 2026 16:57:23 +0200 Subject: [PATCH 19/28] fix formatting --- .clang-format | 2 +- src/sentry_scope.c | 220 ++++++++++++++++++++------------------------- 2 files changed, 100 insertions(+), 122 deletions(-) diff --git a/.clang-format b/.clang-format index 84446160fd..b8f96ae156 100644 --- a/.clang-format +++ b/.clang-format @@ -4,5 +4,5 @@ IndentPPDirectives: AfterHash ColumnLimit: 80 AlwaysBreakAfterDefinitionReturnType: All PointerAlignment: Right -ForEachMacros: ['SENTRY_WITH_SCOPE', 'SENTRY_WITH_SCOPE_MUT', 'SENTRY_WITH_SCOPE_MUT_NO_FLUSH', 'SENTRY_WITH_OPTIONS', 'SENTRY_WITH_OPTIONS_MUT'] +ForEachMacros: ['SENTRY_WITH_SCOPE', 'SENTRY_WITH_SCOPE_MUT', 'SENTRY_WITH_SCOPE_MUT_NO_FLUSH', 'SENTRY_WITH_OPTIONS', 'SENTRY_WITH_OPTIONS_MUT', 'DATA_READ_LOCK', 'DATA_WRITE_LOCK'] InsertNewlineAtEOF: True diff --git a/src/sentry_scope.c b/src/sentry_scope.c index dfa0fa1a4e..4441c87ede 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -247,8 +247,7 @@ init_global_data(sentry_scope_data_t *data) static void data_apply_options(sentry_scope_data_t *data, sentry_options_t *options) { - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { if (options->sdk_name) { sentry_value_t sdk_name = sentry_value_new_string(options->sdk_name); @@ -281,8 +280,7 @@ data_apply_options(sentry_scope_data_t *data, sentry_options_t *options) static void clear_data(sentry_scope_data_t *data) { - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { bool trace_managed = data->trace_managed; sentry_value_t propagation_context = sentry_value_incref(data->propagation_context); @@ -309,8 +307,7 @@ clone_data(const sentry_scope_data_t *source) } sentry__rwlock_init(&clone->rwlock); - DATA_READ_LOCK(source) - { + DATA_READ_LOCK (source) { clone->release = sentry__value_clone(source->release); clone->environment = sentry__value_clone(source->environment); clone->transaction = sentry__value_clone(source->transaction); @@ -346,8 +343,7 @@ static sentry_value_t data_ref_propagation_context(const sentry_scope_data_t *data) { sentry_value_t propagation_context = sentry_value_new_null(); - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { propagation_context = sentry_value_incref(data->propagation_context); } return propagation_context; @@ -357,8 +353,7 @@ static void data_set_propagation_context( sentry_scope_data_t *data, const char *key, sentry_value_t value) { - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { sentry_value_set_by_key(data->propagation_context, key, value); } } @@ -366,8 +361,7 @@ data_set_propagation_context( static void data_regenerate_propagation_context(sentry_scope_data_t *data) { - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { generate_propagation_context(data->propagation_context); } } @@ -376,8 +370,7 @@ static sentry_value_t data_ref_trace_context(const sentry_scope_data_t *data) { sentry_value_t trace_context = sentry_value_new_null(); - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { trace_context = sentry_value_incref( sentry_value_get_by_key(data->propagation_context, "trace")); } @@ -388,8 +381,7 @@ static void data_set_trace_context( sentry_scope_data_t *data, const char *key, sentry_value_t value) { - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { sentry_value_set_by_key( sentry_value_get_by_key(data->propagation_context, "trace"), key, value); @@ -400,14 +392,18 @@ static bool data_is_trace_managed(const sentry_scope_data_t *data) { bool managed = false; - DATA_READ_LOCK(data) { managed = data->trace_managed; } + DATA_READ_LOCK (data) { + managed = data->trace_managed; + } return managed; } static void data_set_trace_managed(sentry_scope_data_t *data, bool managed) { - DATA_WRITE_LOCK(data) { data->trace_managed = managed; } + DATA_WRITE_LOCK (data) { + data->trace_managed = managed; + } } static void @@ -416,8 +412,7 @@ data_freeze_dsc(sentry_scope_data_t *data, sentry_value_t incoming) sentry_value_t dsc = sentry_value_new_object(); sentry__value_merge_objects(dsc, incoming); sentry_value_freeze(dsc); - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { sentry__value_replace(&data->dynamic_sampling_context, dsc); } } @@ -426,8 +421,7 @@ static sentry_value_t data_ref_dsc(const sentry_scope_data_t *data) { sentry_value_t dsc = sentry_value_new_null(); - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { dsc = sentry_value_incref(data->dynamic_sampling_context); } return dsc; @@ -437,8 +431,7 @@ static sentry_value_t data_clone_dsc(const sentry_scope_data_t *data) { sentry_value_t dsc = sentry_value_new_null(); - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { dsc = sentry__value_clone(data->dynamic_sampling_context); } return dsc; @@ -449,8 +442,7 @@ data_foreach_dsc(const sentry_scope_data_t *data, void (*callback)(const char *key, sentry_value_t value, void *userdata), void *userdata) { - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { sentry__value_foreach_key_value( data->dynamic_sampling_context, callback, userdata); } @@ -476,8 +468,7 @@ data_update_dsc(sentry_scope_data_t *data, const sentry_options_t *options) dsc, "sample_rate", sentry_value_new_double(1.0)); } - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { sentry_value_t sample_rand = sentry_value_get_by_key( sentry_value_get_by_key(data->propagation_context, "trace"), "sample_rand"); @@ -495,15 +486,16 @@ static sentry_value_t data_ref_client_sdk(const sentry_scope_data_t *data) { sentry_value_t client_sdk = sentry_value_new_null(); - DATA_READ_LOCK(data) { client_sdk = sentry_value_incref(data->client_sdk); } + DATA_READ_LOCK (data) { + client_sdk = sentry_value_incref(data->client_sdk); + } return client_sdk; } static void data_apply_tags_and_extra(const sentry_scope_data_t *data, sentry_value_t event) { - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { sentry_value_t event_tags = sentry_value_get_by_key(event, "tags"); if (sentry_value_is_null(event_tags)) { if (!sentry_value_is_null(data->tags)) { @@ -530,7 +522,9 @@ static sentry_value_t data_clone_contexts(const sentry_scope_data_t *data) { sentry_value_t contexts = sentry_value_new_null(); - DATA_READ_LOCK(data) { contexts = sentry__value_clone(data->contexts); } + DATA_READ_LOCK (data) { + contexts = sentry__value_clone(data->contexts); + } return contexts; } @@ -538,8 +532,7 @@ static sentry_value_t data_ref_span_or_transaction(const sentry_scope_data_t *data) { sentry_value_t value = sentry_value_new_null(); - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { if (data->span) { value = sentry_value_incref(data->span->inner); } else if (data->transaction_object) { @@ -553,8 +546,7 @@ static sentry_value_t data_breadcrumbs_to_list(const sentry_scope_data_t *data) { sentry_value_t breadcrumbs = sentry_value_new_null(); - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { breadcrumbs = sentry__ringbuffer_to_list(data->breadcrumbs); } return breadcrumbs; @@ -564,8 +556,7 @@ static bool data_add_breadcrumb(sentry_scope_data_t *data, sentry_value_t breadcrumb) { bool added = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { added = sentry__ringbuffer_append(data->breadcrumbs, breadcrumb) == 0; } return added; @@ -575,15 +566,16 @@ static sentry_value_t data_ref_user(const sentry_scope_data_t *data) { sentry_value_t user = sentry_value_new_null(); - DATA_READ_LOCK(data) { user = sentry_value_incref(data->user); } + DATA_READ_LOCK (data) { + user = sentry_value_incref(data->user); + } return user; } static void data_set_user(sentry_scope_data_t *data, sentry_value_t user) { - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { sentry__value_replace(&data->user, sentry_value_incref(user)); } } @@ -592,7 +584,9 @@ static sentry_value_t data_ref_tags(const sentry_scope_data_t *data) { sentry_value_t tags = sentry_value_new_null(); - DATA_READ_LOCK(data) { tags = sentry_value_incref(data->tags); } + DATA_READ_LOCK (data) { + tags = sentry_value_incref(data->tags); + } return tags; } @@ -600,8 +594,7 @@ static bool data_set_tag(sentry_scope_data_t *data, const char *key, sentry_value_t value) { bool did_set = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { did_set = sentry_value_set_by_key(data->tags, key, value) == 0; } return did_set; @@ -612,8 +605,7 @@ data_set_tag_n(sentry_scope_data_t *data, const char *key, size_t key_len, sentry_value_t value) { bool did_set = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { did_set = sentry_value_set_by_key_n(data->tags, key, key_len, value) == 0; } @@ -624,8 +616,7 @@ static bool data_remove_tag(sentry_scope_data_t *data, const char *key) { bool removed = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { removed = sentry_value_remove_by_key(data->tags, key) == 0; } return removed; @@ -635,8 +626,7 @@ static char * data_remove_tag_n(sentry_scope_data_t *data, const char *key, size_t key_len) { char *removed_key = NULL; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { removed_key = sentry__value_remove_and_take_key_n(data->tags, key, key_len); } @@ -647,7 +637,9 @@ static sentry_value_t data_ref_extra(const sentry_scope_data_t *data) { sentry_value_t extra = sentry_value_new_null(); - DATA_READ_LOCK(data) { extra = sentry_value_incref(data->extra); } + DATA_READ_LOCK (data) { + extra = sentry_value_incref(data->extra); + } return extra; } @@ -655,8 +647,7 @@ static bool data_set_extra(sentry_scope_data_t *data, const char *key, sentry_value_t value) { bool did_set = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { did_set = sentry_value_set_by_key(data->extra, key, value) == 0; } return did_set; @@ -667,8 +658,7 @@ data_set_extra_n(sentry_scope_data_t *data, const char *key, size_t key_len, sentry_value_t value) { bool did_set = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { did_set = sentry_value_set_by_key_n(data->extra, key, key_len, value) == 0; } @@ -679,8 +669,7 @@ static bool data_remove_extra(sentry_scope_data_t *data, const char *key) { bool removed = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { removed = sentry_value_remove_by_key(data->extra, key) == 0; } return removed; @@ -690,8 +679,7 @@ static char * data_remove_extra_n(sentry_scope_data_t *data, const char *key, size_t key_len) { char *removed_key = NULL; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { removed_key = sentry__value_remove_and_take_key_n(data->extra, key, key_len); } @@ -702,7 +690,9 @@ static sentry_value_t data_ref_attributes(const sentry_scope_data_t *data) { sentry_value_t attributes = sentry_value_new_null(); - DATA_READ_LOCK(data) { attributes = sentry_value_incref(data->attributes); } + DATA_READ_LOCK (data) { + attributes = sentry_value_incref(data->attributes); + } return attributes; } @@ -711,8 +701,7 @@ data_set_attribute_n(sentry_scope_data_t *data, const char *key, size_t key_len, sentry_value_t attribute) { bool did_set = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { did_set = sentry_value_set_by_key_n( data->attributes, key, key_len, attribute) == 0; @@ -723,15 +712,16 @@ data_set_attribute_n(sentry_scope_data_t *data, const char *key, size_t key_len, static void data_remove_attribute(sentry_scope_data_t *data, const char *key) { - DATA_WRITE_LOCK(data) { sentry_value_remove_by_key(data->attributes, key); } + DATA_WRITE_LOCK (data) { + sentry_value_remove_by_key(data->attributes, key); + } } static void data_remove_attribute_n( sentry_scope_data_t *data, const char *key, size_t key_len) { - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { sentry_value_remove_by_key_n(data->attributes, key, key_len); } } @@ -740,7 +730,9 @@ static sentry_value_t data_ref_contexts(const sentry_scope_data_t *data) { sentry_value_t contexts = sentry_value_new_null(); - DATA_READ_LOCK(data) { contexts = sentry_value_incref(data->contexts); } + DATA_READ_LOCK (data) { + contexts = sentry_value_incref(data->contexts); + } return contexts; } @@ -749,8 +741,7 @@ data_set_context( sentry_scope_data_t *data, const char *key, sentry_value_t value) { bool did_set = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { did_set = sentry_value_set_by_key(data->contexts, key, value) == 0; } return did_set; @@ -761,8 +752,7 @@ data_set_context_n(sentry_scope_data_t *data, const char *key, size_t key_len, sentry_value_t value) { bool did_set = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { did_set = sentry_value_set_by_key_n(data->contexts, key, key_len, value) == 0; } @@ -780,8 +770,7 @@ static bool data_remove_context(sentry_scope_data_t *data, const char *key) { bool removed = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { removed = sentry_value_remove_by_key(data->contexts, key) == 0; } return removed; @@ -792,8 +781,7 @@ data_remove_context_n( sentry_scope_data_t *data, const char *key, size_t key_len) { char *removed_key = NULL; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { removed_key = sentry__value_remove_and_take_key_n(data->contexts, key, key_len); } @@ -804,8 +792,7 @@ static sentry_value_t data_ref_fingerprint(const sentry_scope_data_t *data) { sentry_value_t fingerprint = sentry_value_new_null(); - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { fingerprint = sentry_value_incref(data->fingerprint); } return fingerprint; @@ -814,8 +801,7 @@ data_ref_fingerprint(const sentry_scope_data_t *data) static void data_set_fingerprint(sentry_scope_data_t *data, sentry_value_t fingerprint) { - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { sentry__value_replace( &data->fingerprint, sentry_value_incref(fingerprint)); } @@ -825,14 +811,18 @@ static sentry_level_t data_get_level(const sentry_scope_data_t *data) { sentry_level_t level = SENTRY_LEVEL_ERROR; - DATA_READ_LOCK(data) { level = data->level; } + DATA_READ_LOCK (data) { + level = data->level; + } return level; } static void data_set_level(sentry_scope_data_t *data, sentry_level_t level) { - DATA_WRITE_LOCK(data) { data->level = level; } + DATA_WRITE_LOCK (data) { + data->level = level; + } } static sentry_uuid_t @@ -864,12 +854,16 @@ data_add_attachment(sentry_scope_data_t *data, sentry_value_t attachment, bool *did_add) { sentry_value_t added = sentry_value_new_null(); - DATA_WRITE_LOCK(data) - { + if (did_add) { + *did_add = false; + } + DATA_WRITE_LOCK (data) { size_t len = sentry_value_get_length(data->attachments); added = sentry__attachments_add(&data->attachments, attachment); - *did_add = !sentry_value_is_null(added) - && sentry_value_get_length(data->attachments) > len; + if (did_add) { + *did_add = !sentry_value_is_null(added) + && sentry_value_get_length(data->attachments) > len; + } } return added; } @@ -879,8 +873,7 @@ data_remove_attachment( sentry_scope_data_t *data, const sentry_uuid_t *attachment_id) { sentry_value_t removed = sentry_value_new_null(); - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { removed = sentry__attachments_remove(data->attachments, attachment_id); } return removed; @@ -890,8 +883,7 @@ static sentry_value_t data_take_attachments(sentry_scope_data_t *data) { sentry_value_t attachments = sentry_value_new_null(); - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { attachments = data->attachments; data->attachments = sentry__attachments_new(); } @@ -902,8 +894,7 @@ static sentry_transaction_t * data_ref_transaction_object(const sentry_scope_data_t *data) { sentry_transaction_t *transaction = NULL; - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { transaction = data->transaction_object; sentry__transaction_incref(transaction); } @@ -923,8 +914,7 @@ data_set_transaction_object( sentry_scope_data_t *data, sentry_transaction_t *transaction) { sentry__transaction_incref(transaction); - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { sentry__span_decref(data->span); data->span = NULL; sentry__transaction_decref(data->transaction_object); @@ -937,8 +927,7 @@ data_remove_transaction_object( sentry_scope_data_t *data, sentry_transaction_t *transaction) { bool removed = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { if (transaction && data->transaction_object == transaction) { data->transaction_object = NULL; removed = true; @@ -957,8 +946,7 @@ data_remove_transaction_value( const char *span_id = sentry_value_as_string( sentry_value_get_by_key(transaction, "span_id")); sentry_transaction_t *transaction_object = NULL; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { if (data->transaction_object && value_has_span_id(data->transaction_object->inner, span_id)) { transaction_object = data->transaction_object; @@ -974,8 +962,7 @@ data_restore_transaction_object( sentry_scope_data_t *data, sentry_transaction_t *transaction) { bool restored = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { if (!data->transaction_object && !data->span && transaction) { data->transaction_object = transaction; restored = true; @@ -988,8 +975,7 @@ static sentry_span_t * data_ref_span(const sentry_scope_data_t *data) { sentry_span_t *span = NULL; - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { span = data->span; sentry__span_incref(span); } @@ -1000,8 +986,7 @@ static void data_set_span(sentry_scope_data_t *data, sentry_span_t *span) { sentry__span_incref(span); - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { sentry__transaction_decref(data->transaction_object); data->transaction_object = NULL; sentry__span_decref(data->span); @@ -1013,8 +998,7 @@ static bool data_remove_span(sentry_scope_data_t *data, sentry_span_t *span) { bool removed = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { if (span && data->span == span) { data->span = NULL; removed = true; @@ -1032,8 +1016,7 @@ data_remove_span_value(sentry_scope_data_t *data, sentry_value_t span) const char *span_id = sentry_value_as_string(sentry_value_get_by_key(span, "span_id")); sentry_span_t *scope_span = NULL; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { if (data->span && value_has_span_id(data->span->inner, span_id)) { scope_span = data->span; data->span = NULL; @@ -1047,8 +1030,7 @@ static bool data_restore_span(sentry_scope_data_t *data, sentry_span_t *span) { bool restored = false; - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { if (!data->span && !data->transaction_object && span) { data->span = span; restored = true; @@ -1061,15 +1043,16 @@ static sentry_value_t data_ref_release(const sentry_scope_data_t *data) { sentry_value_t release = sentry_value_new_null(); - DATA_READ_LOCK(data) { release = sentry_value_incref(data->release); } + DATA_READ_LOCK (data) { + release = sentry_value_incref(data->release); + } return release; } static void data_set_release(sentry_scope_data_t *data, sentry_value_t value) { - DATA_WRITE_LOCK(data) - { + 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)); @@ -1080,8 +1063,7 @@ static sentry_value_t data_ref_environment(const sentry_scope_data_t *data) { sentry_value_t environment = sentry_value_new_null(); - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { environment = sentry_value_incref(data->environment); } return environment; @@ -1090,8 +1072,7 @@ data_ref_environment(const sentry_scope_data_t *data) static void data_set_environment(sentry_scope_data_t *data, sentry_value_t value) { - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { sentry__value_replace(&data->environment, sentry_value_incref(value)); sentry_value_set_by_key(data->dynamic_sampling_context, "environment", sentry_value_incref(value)); @@ -1102,8 +1083,7 @@ static sentry_value_t data_ref_transaction(const sentry_scope_data_t *data) { sentry_value_t transaction = sentry_value_new_null(); - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { transaction = sentry_value_incref(data->transaction); } return transaction; @@ -1113,8 +1093,7 @@ static void data_set_transaction(sentry_scope_data_t *data, sentry_value_t value, const char *transaction, size_t transaction_len) { - DATA_WRITE_LOCK(data) - { + DATA_WRITE_LOCK (data) { sentry__value_replace(&data->transaction, sentry_value_incref(value)); if (data->transaction_object) { sentry_transaction_set_name_n( @@ -1130,8 +1109,7 @@ data_apply_to_telemetry(const sentry_scope_data_t *data, sentry_value_t os_name = sentry_value_new_null(); sentry_value_t os_version = sentry_value_new_null(); - DATA_READ_LOCK(data) - { + DATA_READ_LOCK (data) { sentry__value_merge_objects_shallow(attributes, data->attributes); sentry_value_t trace_id = sentry_value_get_by_key( From bc99dba02a8af04c12ea6f5392aaaa4525ee1c40 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Sun, 9 Aug 2026 21:26:08 +0200 Subject: [PATCH 20/28] fix(scope): Keep global scope alive during access Track active scope accesses and delay cleanup until they finish, while releasing the lifetime guard before backend scope flushing. --- src/sentry_scope.c | 26 ++++++++++ tests/unit/test_concurrency.c | 91 +++++++++++++++++++++++++++++++++++ tests/unit/tests.inc | 1 + 3 files changed, 118 insertions(+) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 4441c87ede..bc314c793d 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -29,6 +29,9 @@ #endif static bool g_scope_initialized = false; +static size_t g_scope_accesses = 0; +static bool g_scope_idle_initialized = false; +static sentry_cond_t g_scope_idle; #ifdef SENTRY__MUTEX_INIT_DYN SENTRY__MUTEX_INIT_DYN(g_lock) #else @@ -1240,6 +1243,9 @@ sentry__scope_cleanup(void) { SENTRY__MUTEX_INIT_DYN_ONCE(g_lock); sentry__mutex_lock(&g_lock); + while (g_scope_accesses > 0) { + sentry__cond_wait(&g_scope_idle, &g_lock); + } if (g_scope_initialized) { g_scope_initialized = false; cleanup_global_data(g_scope.data); @@ -1263,11 +1269,29 @@ sentry__scope_begin(void) { SENTRY__MUTEX_INIT_DYN_ONCE(g_lock); sentry__mutex_lock(&g_lock); + if (!g_scope_idle_initialized) { + sentry__cond_init(&g_scope_idle); + g_scope_idle_initialized = true; + } sentry_scope_t *scope = get_scope(); + g_scope_accesses++; sentry__mutex_unlock(&g_lock); return scope; } +static void +end_scope_access(void) +{ + SENTRY__MUTEX_INIT_DYN_ONCE(g_lock); + sentry__mutex_lock(&g_lock); + assert(g_scope_accesses > 0); + g_scope_accesses--; + if (g_scope_accesses == 0) { + sentry__cond_wake(&g_scope_idle); + } + sentry__mutex_unlock(&g_lock); +} + static void finish_scope(bool flush, bool unlock) { @@ -1291,6 +1315,8 @@ finish_scope(bool flush, bool unlock) // we try to unlock the scope as soon as possible. The // backend will do its own `WITH_SCOPE` internally. sentry__mutex_unlock(&g_lock); + } else { + end_scope_access(); } if (flush) { diff --git a/tests/unit/test_concurrency.c b/tests/unit/test_concurrency.c index cdc3c739aa..3e1de13d24 100644 --- a/tests/unit/test_concurrency.c +++ b/tests/unit/test_concurrency.c @@ -3,6 +3,7 @@ #include "sentry_envelope.h" #include "sentry_options.h" #include "sentry_path.h" +#include "sentry_scope.h" #include "sentry_testsupport.h" #include "sentry_transport.h" @@ -241,3 +242,93 @@ SENTRY_TEST(concurrent_uninit) sentry_close(); } + +typedef struct { + sentry_mutex_t lock; + sentry_cond_t access_signal; + sentry_cond_t cleanup_signal; + bool access_started; + bool release_access; + bool cleanup_started; + bool cleanup_finished; +} scope_cleanup_state_t; + +SENTRY_THREAD_FN +scope_access_thread(void *data) +{ + scope_cleanup_state_t *state = data; + sentry_scope_t *scope = sentry__scope_begin(); + + sentry__mutex_lock(&state->lock); + state->access_started = true; + sentry__cond_wake(&state->access_signal); + while (!state->release_access) { + sentry__cond_wait(&state->access_signal, &state->lock); + } + sentry__mutex_unlock(&state->lock); + + (void)sentry__scope_get_level(scope); + sentry__scope_end(false); + return 0; +} + +SENTRY_THREAD_FN +scope_cleanup_thread(void *data) +{ + scope_cleanup_state_t *state = data; + + sentry__mutex_lock(&state->lock); + state->cleanup_started = true; + sentry__cond_wake(&state->cleanup_signal); + sentry__mutex_unlock(&state->lock); + + sentry__scope_cleanup(); + + sentry__mutex_lock(&state->lock); + state->cleanup_finished = true; + sentry__cond_wake(&state->cleanup_signal); + sentry__mutex_unlock(&state->lock); + return 0; +} + +SENTRY_TEST(scope_cleanup) +{ + scope_cleanup_state_t state = { 0 }; + sentry__mutex_init(&state.lock); + sentry__cond_init(&state.access_signal); + sentry__cond_init(&state.cleanup_signal); + + sentry_threadid_t access_thread; + sentry__thread_init(&access_thread); + TEST_ASSERT_INT_EQUAL( + sentry__thread_spawn(&access_thread, scope_access_thread, &state), 0); + + sentry__mutex_lock(&state.lock); + while (!state.access_started) { + sentry__cond_wait(&state.access_signal, &state.lock); + } + sentry__mutex_unlock(&state.lock); + + sentry_threadid_t cleanup_thread; + sentry__thread_init(&cleanup_thread); + TEST_ASSERT_INT_EQUAL( + sentry__thread_spawn(&cleanup_thread, scope_cleanup_thread, &state), 0); + + sentry__mutex_lock(&state.lock); + while (!state.cleanup_started) { + sentry__cond_wait(&state.cleanup_signal, &state.lock); + } + sentry__cond_wait_timeout(&state.cleanup_signal, &state.lock, 250); + TEST_CHECK(!state.cleanup_finished); + state.release_access = true; + sentry__cond_wake(&state.access_signal); + sentry__mutex_unlock(&state.lock); + + sentry__thread_join(access_thread); + sentry__thread_free(&access_thread); + sentry__thread_join(cleanup_thread); + sentry__thread_free(&cleanup_thread); + + TEST_CHECK(state.cleanup_finished); + sentry__mutex_free(&state.lock); +} diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index f2cc1bba35..260ed10fe8 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -329,6 +329,7 @@ XX(scope_capture_metric_one_shot) XX(scope_capture_metric_user_owned) XX(scope_capture_unlocked) XX(scope_capture_user_owned) +XX(scope_cleanup) XX(scope_clear) XX(scope_clone) XX(scope_clone_independence) From 8b02357fa8bf42511ff74493f21526b3f2c67038 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 12 Aug 2026 09:21:04 +0200 Subject: [PATCH 21/28] restore locks for attachments --- src/backends/sentry_backend_native.c | 5 ++- src/integrations/sentry_integration_wer.c | 33 ++++++++--------- src/sentry_core.c | 43 +++++++++++++---------- src/sentry_scope.c | 17 +++++---- src/sentry_scope.h | 2 +- tests/unit/test_attachments.c | 5 +-- tests/unit/test_scope.c | 9 ++--- 7 files changed, 64 insertions(+), 50 deletions(-) diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 19feac815f..78309990c8 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -763,12 +763,14 @@ native_backend_write_attachments(const sentry_path_t *event_path) return; } SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = sentry__scope_get_attachments(scope); + sentry_value_t attachments = sentry__scope_ref_attachments(scope); if (sentry_value_get_length(attachments) == 0) { + sentry_value_decref(attachments); continue; } sentry_path_t *run_path = sentry__path_dir(event_path); if (!run_path) { + sentry_value_decref(attachments); continue; } sentry_path_t *attach_list_path @@ -817,6 +819,7 @@ native_backend_write_attachments(const sentry_path_t *event_path) sentry__path_free(attach_list_path); } sentry__path_free(run_path); + sentry_value_decref(attachments); } } diff --git a/src/integrations/sentry_integration_wer.c b/src/integrations/sentry_integration_wer.c index 87130a7be0..77b54dcf00 100644 --- a/src/integrations/sentry_integration_wer.c +++ b/src/integrations/sentry_integration_wer.c @@ -191,6 +191,18 @@ wer_remove_attachment(void *UNUSED(data), sentry_value_t attachment) } } +static void +wer_for_each_attachment( + sentry_scope_t *scope, void *data, void (*callback)(void *, sentry_value_t)) +{ + sentry_value_t attachments = sentry__scope_ref_attachments(scope); + size_t len = sentry_value_get_length(attachments); + for (size_t i = 0; i < len; i++) { + callback(data, sentry_value_get_by_index(attachments, i)); + } + sentry_value_decref(attachments); +} + static void wer_cleanup_tag(const char *key, sentry_value_t UNUSED(value), void *data) { @@ -211,12 +223,7 @@ wer_clear(void *data) sentry__value_foreach_key_value(tags, wer_cleanup_tag, wer_data); sentry_value_decref(tags); - sentry_value_t attachments = sentry__scope_get_attachments(scope); - size_t len = sentry_value_get_length(attachments); - for (size_t i = 0; i < len; i++) { - wer_remove_attachment( - wer_data, sentry_value_get_by_index(attachments, i)); - } + wer_for_each_attachment(scope, wer_data, wer_remove_attachment); } static void @@ -241,12 +248,7 @@ register_wer( if (sentry__scope_add_observer(scope, observer)) { wer_data->scope = scope; wer_data->observer = observer; - sentry_value_t attachments = sentry__scope_get_attachments(scope); - size_t len = sentry_value_get_length(attachments); - for (size_t i = 0; i < len; i++) { - wer_add_attachment( - wer_data, sentry_value_get_by_index(attachments, i)); - } + wer_for_each_attachment(scope, wer_data, wer_add_attachment); } } @@ -264,12 +266,7 @@ unregister_wer( sentry__value_foreach_key_value(tags, wer_cleanup_tag, wer_data); sentry_value_decref(tags); - sentry_value_t attachments = sentry__scope_get_attachments(scope); - size_t len = sentry_value_get_length(attachments); - for (size_t i = 0; i < len; i++) { - wer_remove_attachment( - wer_data, sentry_value_get_by_index(attachments, i)); - } + wer_for_each_attachment(scope, wer_data, wer_remove_attachment); sentry__scope_remove_observer(scope, wer_data->observer); wer_data->scope = NULL; diff --git a/src/sentry_core.c b/src/sentry_core.c index fb43fa8767..c5961db518 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -706,17 +706,20 @@ sentry__prepare_event(const sentry_options_t *options, sentry_value_t event, } SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = sentry__scope_get_attachments(scope); - if (local_scope - && sentry_value_get_length( - sentry__scope_get_attachments(local_scope)) - > 0) { - // all attachments merged from multiple scopes - sentry__attachments_extend(&all_attachments, - sentry__scope_get_attachments(local_scope)); - sentry__attachments_extend( - &all_attachments, sentry__scope_get_attachments(scope)); - attachments = all_attachments; + sentry_value_t global_attachments + = sentry__scope_ref_attachments(scope); + sentry_value_t attachments = global_attachments; + if (local_scope) { + sentry_value_t local_attachments + = sentry__scope_ref_attachments(local_scope); + if (sentry_value_get_length(local_attachments) > 0) { + // all attachments merged from multiple scopes + sentry__attachments_extend(&all_attachments, local_attachments); + sentry__attachments_extend( + &all_attachments, global_attachments); + attachments = all_attachments; + } + sentry_value_decref(local_attachments); } // otherwise only global scope has attachments sentry__envelope_add_attachments(envelope, attachments, options); @@ -724,6 +727,7 @@ sentry__prepare_event(const sentry_options_t *options, sentry_value_t event, sentry__cache_attachment_refs(envelope, attachments, options, options->run->cache_path, options->run->run_path); } + sentry_value_decref(global_attachments); } sentry_value_decref(all_attachments); @@ -848,15 +852,18 @@ prepare_user_feedback(const sentry_options_t *options, sentry__attachments_extend(&all_attachments, hint->attachments); } if (local_scope) { - sentry__attachments_extend( - &all_attachments, sentry__scope_get_attachments(local_scope)); + sentry_value_t local_attachments + = sentry__scope_ref_attachments(local_scope); + sentry__attachments_extend(&all_attachments, local_attachments); + sentry_value_decref(local_attachments); } SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = sentry__scope_get_attachments(scope); + sentry_value_t global_attachments + = sentry__scope_ref_attachments(scope); + sentry_value_t attachments = global_attachments; if (sentry_value_get_length(all_attachments) > 0) { - sentry__attachments_extend( - &all_attachments, sentry__scope_get_attachments(scope)); + sentry__attachments_extend(&all_attachments, global_attachments); attachments = all_attachments; } sentry__envelope_add_attachments(envelope, attachments, options); @@ -864,6 +871,7 @@ prepare_user_feedback(const sentry_options_t *options, sentry__cache_attachment_refs(envelope, attachments, options, options->run->cache_path, options->run->run_path); } + sentry_value_decref(global_attachments); } sentry_value_decref(all_attachments); @@ -2051,8 +2059,7 @@ sentry_clear_attachments(void) { SENTRY_WITH_OPTIONS (options) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_value_t attachments - = sentry__scope_take_attachments(scope); + sentry_value_t attachments = sentry__scope_take_attachments(scope); size_t len = sentry_value_get_length(attachments); for (size_t i = 0; i < len; i++) { sentry_value_t attachment diff --git a/src/sentry_scope.c b/src/sentry_scope.c index bc314c793d..ea3c10f1d5 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -331,6 +331,7 @@ clone_data(const sentry_scope_data_t *source) clone->level = source->level; clone->last_event_id = source->last_event_id; clone->client_sdk = sentry__value_clone(source->client_sdk); + clone->attachments = sentry__attachments_new(); sentry__attachments_extend(&clone->attachments, source->attachments); clone->transaction_object = source->transaction_object; sentry__transaction_incref(clone->transaction_object); @@ -847,14 +848,18 @@ data_set_last_event_id(sentry_scope_data_t *data, sentry_uuid_t event_id) } static sentry_value_t -data_get_attachments(const sentry_scope_data_t *data) +data_ref_attachments(const sentry_scope_data_t *data) { - return data->attachments; + sentry_value_t attachments = sentry_value_new_null(); + DATA_READ_LOCK (data) { + attachments = sentry_value_incref(data->attachments); + } + return attachments; } static sentry_value_t -data_add_attachment(sentry_scope_data_t *data, sentry_value_t attachment, - bool *did_add) +data_add_attachment( + sentry_scope_data_t *data, sentry_value_t attachment, bool *did_add) { sentry_value_t added = sentry_value_new_null(); if (did_add) { @@ -2322,9 +2327,9 @@ sentry_scope_set_span(sentry_scope_t *scope, sentry_span_t *span) } sentry_value_t -sentry__scope_get_attachments(const sentry_scope_t *scope) +sentry__scope_ref_attachments(const sentry_scope_t *scope) { - return data_get_attachments(scope->data); + return data_ref_attachments(scope->data); } sentry_value_t diff --git a/src/sentry_scope.h b/src/sentry_scope.h index d2caee46be..4b91b61f0d 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -160,7 +160,7 @@ sentry_value_t sentry__scope_ref_user(const sentry_scope_t *scope); sentry_level_t sentry__scope_get_level(const sentry_scope_t *scope); sentry_value_t sentry__scope_ref_client_sdk(const sentry_scope_t *scope); -sentry_value_t sentry__scope_get_attachments(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_attachments(const sentry_scope_t *scope); sentry_value_t sentry__scope_add_attachment( sentry_scope_t *scope, sentry_value_t attachment); sentry_value_t sentry__scope_take_attachments(sentry_scope_t *scope); diff --git a/tests/unit/test_attachments.c b/tests/unit/test_attachments.c index 73d50bcbee..db223b1c5a 100644 --- a/tests/unit/test_attachments.c +++ b/tests/unit/test_attachments.c @@ -17,8 +17,9 @@ static void add_scope_attachments(sentry_envelope_t *envelope) { SENTRY_WITH_SCOPE (scope) { - sentry__envelope_add_attachments( - envelope, sentry__scope_get_attachments(scope), NULL); + sentry_value_t attachments = sentry__scope_ref_attachments(scope); + sentry__envelope_add_attachments(envelope, attachments, NULL); + sentry_value_decref(attachments); } } diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index ea756e8f06..2e92ab9d38 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -2607,8 +2607,7 @@ SENTRY_TEST(scope_clone_preserves_data) TEST_CHECK(!sentry_uuid_is_nil(&attachment_id)); sentry_scope_t *clone = sentry_scope_clone(scope); - sentry_value_decref( - sentry__scope_remove_attachment(scope, &attachment_id)); + sentry_value_decref(sentry__scope_remove_attachment(scope, &attachment_id)); sentry_value_t clone_tag = scope_value_get_by_key(sentry__scope_ref_tags, clone, "tag_key"); @@ -2640,8 +2639,8 @@ SENTRY_TEST(scope_clone_preserves_data) TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(clone), 1); // Attachments are deep-copied into an independent list. - sentry_value_t clone_attachments = sentry__scope_get_attachments(clone); - sentry_value_t scope_attachments = sentry__scope_get_attachments(scope); + sentry_value_t clone_attachments = sentry__scope_ref_attachments(clone); + sentry_value_t scope_attachments = sentry__scope_ref_attachments(scope); TEST_CHECK_INT_EQUAL(sentry_value_get_length(clone_attachments), 1); TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope_attachments), 0); TEST_CHECK(clone_attachments._bits != scope_attachments._bits); @@ -2650,6 +2649,8 @@ SENTRY_TEST(scope_clone_preserves_data) TEST_CHECK_STRING_EQUAL( sentry__attachment_get_filename(clone_attachment), "file.bin"); TEST_CHECK_INT_EQUAL(sentry__attachment_get_size(clone_attachment), 7); + sentry_value_decref(scope_attachments); + sentry_value_decref(clone_attachments); sentry_scope_free(clone); sentry_scope_free(scope); From 2629b0784fe149a4ea6773f32e75642e98693d39 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 12 Aug 2026 18:02:04 +0200 Subject: [PATCH 22/28] clone (cow) attachments guard against concurrent modifications --- src/backends/sentry_backend_native.c | 2 +- src/integrations/sentry_integration_wer.c | 2 +- src/sentry_core.c | 12 +++-- src/sentry_scope.c | 8 +-- src/sentry_scope.h | 9 +++- tests/unit/test_attachments.c | 6 ++- tests/unit/test_scope.c | 66 ++++++++++++++++++++++- tests/unit/tests.inc | 1 + 8 files changed, 90 insertions(+), 16 deletions(-) diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 78309990c8..b87d05ddbd 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -763,7 +763,7 @@ native_backend_write_attachments(const sentry_path_t *event_path) return; } SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = sentry__scope_ref_attachments(scope); + sentry_value_t attachments = sentry__scope_clone_attachments(scope); if (sentry_value_get_length(attachments) == 0) { sentry_value_decref(attachments); continue; diff --git a/src/integrations/sentry_integration_wer.c b/src/integrations/sentry_integration_wer.c index 77b54dcf00..73ac8ee92c 100644 --- a/src/integrations/sentry_integration_wer.c +++ b/src/integrations/sentry_integration_wer.c @@ -195,7 +195,7 @@ static void wer_for_each_attachment( sentry_scope_t *scope, void *data, void (*callback)(void *, sentry_value_t)) { - sentry_value_t attachments = sentry__scope_ref_attachments(scope); + sentry_value_t attachments = sentry__scope_clone_attachments(scope); size_t len = sentry_value_get_length(attachments); for (size_t i = 0; i < len; i++) { callback(data, sentry_value_get_by_index(attachments, i)); diff --git a/src/sentry_core.c b/src/sentry_core.c index c5961db518..41a40e9c76 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -707,11 +707,11 @@ sentry__prepare_event(const sentry_options_t *options, sentry_value_t event, SENTRY_WITH_SCOPE (scope) { sentry_value_t global_attachments - = sentry__scope_ref_attachments(scope); + = sentry__scope_clone_attachments(scope); sentry_value_t attachments = global_attachments; if (local_scope) { sentry_value_t local_attachments - = sentry__scope_ref_attachments(local_scope); + = sentry__scope_clone_attachments(local_scope); if (sentry_value_get_length(local_attachments) > 0) { // all attachments merged from multiple scopes sentry__attachments_extend(&all_attachments, local_attachments); @@ -853,14 +853,14 @@ prepare_user_feedback(const sentry_options_t *options, } if (local_scope) { sentry_value_t local_attachments - = sentry__scope_ref_attachments(local_scope); + = sentry__scope_clone_attachments(local_scope); sentry__attachments_extend(&all_attachments, local_attachments); sentry_value_decref(local_attachments); } SENTRY_WITH_SCOPE (scope) { sentry_value_t global_attachments - = sentry__scope_ref_attachments(scope); + = sentry__scope_clone_attachments(scope); sentry_value_t attachments = global_attachments; if (sentry_value_get_length(all_attachments) > 0) { sentry__attachments_extend(&all_attachments, global_attachments); @@ -2010,7 +2010,9 @@ sentry_add_attachment(sentry_value_t attachment) sentry_value_t added = sentry_value_new_null(); SENTRY_WITH_SCOPE_MUT (scope) { - added = sentry__attachments_find(scope->attachments, attachment); + sentry_value_t attachments = sentry__scope_clone_attachments(scope); + added = sentry__attachments_find(attachments, attachment); + sentry_value_decref(attachments); if (sentry_value_is_null(added)) { if (options->backend && options->backend->add_attachment_func) { options->backend->add_attachment_func( diff --git a/src/sentry_scope.c b/src/sentry_scope.c index ea3c10f1d5..bfe798edac 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -848,11 +848,11 @@ data_set_last_event_id(sentry_scope_data_t *data, sentry_uuid_t event_id) } static sentry_value_t -data_ref_attachments(const sentry_scope_data_t *data) +data_clone_attachments(const sentry_scope_data_t *data) { sentry_value_t attachments = sentry_value_new_null(); DATA_READ_LOCK (data) { - attachments = sentry_value_incref(data->attachments); + attachments = sentry__value_clone(data->attachments); } return attachments; } @@ -2327,9 +2327,9 @@ sentry_scope_set_span(sentry_scope_t *scope, sentry_span_t *span) } sentry_value_t -sentry__scope_ref_attachments(const sentry_scope_t *scope) +sentry__scope_clone_attachments(const sentry_scope_t *scope) { - return data_ref_attachments(scope->data); + return data_clone_attachments(scope->data); } sentry_value_t diff --git a/src/sentry_scope.h b/src/sentry_scope.h index 4b91b61f0d..543a6899d2 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -160,7 +160,14 @@ sentry_value_t sentry__scope_ref_user(const sentry_scope_t *scope); sentry_level_t sentry__scope_get_level(const sentry_scope_t *scope); sentry_value_t sentry__scope_ref_client_sdk(const sentry_scope_t *scope); -sentry_value_t sentry__scope_ref_attachments(const sentry_scope_t *scope); +/** + * Returns an owned copy-on-write snapshot of the scope's attachment list. + * + * The list remains stable after the scope data read lock is released. Published + * attachment elements remain shared because they are frozen. The caller must + * release the snapshot with `sentry_value_decref`. + */ +sentry_value_t sentry__scope_clone_attachments(const sentry_scope_t *scope); sentry_value_t sentry__scope_add_attachment( sentry_scope_t *scope, sentry_value_t attachment); sentry_value_t sentry__scope_take_attachments(sentry_scope_t *scope); diff --git a/tests/unit/test_attachments.c b/tests/unit/test_attachments.c index db223b1c5a..9c3fd86ce6 100644 --- a/tests/unit/test_attachments.c +++ b/tests/unit/test_attachments.c @@ -17,7 +17,7 @@ static void add_scope_attachments(sentry_envelope_t *envelope) { SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = sentry__scope_ref_attachments(scope); + sentry_value_t attachments = sentry__scope_clone_attachments(scope); sentry__envelope_add_attachments(envelope, attachments, NULL); sentry_value_decref(attachments); } @@ -390,7 +390,9 @@ SENTRY_TEST(attachment_properties) sentry_init(options); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope->attachments), 0); + sentry_value_t attachments = sentry__scope_clone_attachments(scope); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(attachments), 0); + sentry_value_decref(attachments); } TEST_CHECK(sentry_value_is_null(sentry_attachment_from_file(NULL))); diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index 2e92ab9d38..b7269611ce 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -2639,8 +2639,8 @@ SENTRY_TEST(scope_clone_preserves_data) TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(clone), 1); // Attachments are deep-copied into an independent list. - sentry_value_t clone_attachments = sentry__scope_ref_attachments(clone); - sentry_value_t scope_attachments = sentry__scope_ref_attachments(scope); + sentry_value_t clone_attachments = sentry__scope_clone_attachments(clone); + sentry_value_t scope_attachments = sentry__scope_clone_attachments(scope); TEST_CHECK_INT_EQUAL(sentry_value_get_length(clone_attachments), 1); TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope_attachments), 0); TEST_CHECK(clone_attachments._bits != scope_attachments._bits); @@ -2656,6 +2656,68 @@ SENTRY_TEST(scope_clone_preserves_data) sentry_scope_free(scope); } +SENTRY_TEST(scope_attachments) +{ + sentry_scope_t *scope = sentry_scope_new(); + sentry_uuid_t first_id = sentry_scope_add_attachment( + scope, sentry_attachment_from_bytes("first", 5, "first.txt")); + sentry_uuid_t second_id = sentry_scope_add_attachment( + scope, sentry_attachment_from_bytes("second", 6, "second.txt")); + TEST_CHECK(!sentry_uuid_is_nil(&first_id)); + TEST_CHECK(!sentry_uuid_is_nil(&second_id)); + + sentry_value_t snapshot = sentry__scope_clone_attachments(scope); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(snapshot), 2); + TEST_CHECK_STRING_EQUAL( + sentry__attachment_get_filename(sentry_value_get_by_index(snapshot, 0)), + "first.txt"); + TEST_CHECK_STRING_EQUAL( + sentry__attachment_get_filename(sentry_value_get_by_index(snapshot, 1)), + "second.txt"); + + sentry_uuid_t third_id = sentry_scope_add_attachment( + scope, sentry_attachment_from_bytes("third", 5, "third.txt")); + TEST_CHECK(!sentry_uuid_is_nil(&third_id)); + + sentry_value_t current = sentry__scope_clone_attachments(scope); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(current), 3); + TEST_CHECK_STRING_EQUAL( + sentry__attachment_get_filename(sentry_value_get_by_index(current, 2)), + "third.txt"); + sentry_value_decref(current); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(snapshot), 2); + + sentry_value_t removed = sentry__scope_remove_attachment(scope, &first_id); + TEST_CHECK(!sentry_value_is_null(removed)); + sentry_value_decref(removed); + + current = sentry__scope_clone_attachments(scope); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(current), 2); + TEST_CHECK_STRING_EQUAL( + sentry__attachment_get_filename(sentry_value_get_by_index(current, 0)), + "second.txt"); + TEST_CHECK_STRING_EQUAL( + sentry__attachment_get_filename(sentry_value_get_by_index(current, 1)), + "third.txt"); + sentry_value_decref(current); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(snapshot), 2); + TEST_CHECK_STRING_EQUAL( + sentry__attachment_get_filename(sentry_value_get_by_index(snapshot, 0)), + "first.txt"); + + sentry_scope_clear(scope); + current = sentry__scope_clone_attachments(scope); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(current), 0); + sentry_value_decref(current); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(snapshot), 2); + TEST_CHECK_STRING_EQUAL( + sentry__attachment_get_filename(sentry_value_get_by_index(snapshot, 1)), + "second.txt"); + + sentry_value_decref(snapshot); + sentry_scope_free(scope); +} + SENTRY_TEST(scope_clone_shares_span) { SENTRY_TEST_OPTIONS_NEW(options); diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 260ed10fe8..4dab95907e 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -316,6 +316,7 @@ XX(rwlock_write_blocks_writer) XX(sampling_before_send) XX(sampling_decision) XX(sampling_transaction) +XX(scope_attachments) XX(scope_bind_span) XX(scope_bind_span_or_transaction_not_both) XX(scope_bind_transaction_object) From f70a7c61c5d8393c4542ab76c52be077b5eecd9e Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 18 Aug 2026 13:28:32 +0200 Subject: [PATCH 23/28] clean up --- .clang-format | 2 +- src/sentry_scope.c | 1750 ++++++++++++--------------------- src/sentry_scope.h | 47 +- tests/unit/test_concurrency.c | 5 +- tests/unit/test_scope.c | 7 + tests/unit/test_tracing.c | 20 +- 6 files changed, 706 insertions(+), 1125 deletions(-) diff --git a/.clang-format b/.clang-format index b8f96ae156..b6bcfbfd22 100644 --- a/.clang-format +++ b/.clang-format @@ -4,5 +4,5 @@ IndentPPDirectives: AfterHash ColumnLimit: 80 AlwaysBreakAfterDefinitionReturnType: All PointerAlignment: Right -ForEachMacros: ['SENTRY_WITH_SCOPE', 'SENTRY_WITH_SCOPE_MUT', 'SENTRY_WITH_SCOPE_MUT_NO_FLUSH', 'SENTRY_WITH_OPTIONS', 'SENTRY_WITH_OPTIONS_MUT', 'DATA_READ_LOCK', 'DATA_WRITE_LOCK'] +ForEachMacros: ['SENTRY_WITH_SCOPE', 'SENTRY_WITH_SCOPE_MUT', 'SENTRY_WITH_SCOPE_MUT_NO_FLUSH', 'SENTRY_WITH_OPTIONS', 'SENTRY_WITH_OPTIONS_MUT', 'SENTRY_SCOPE_READ_LOCK', 'SENTRY_SCOPE_WRITE_LOCK'] InsertNewlineAtEOF: True diff --git a/src/sentry_scope.c b/src/sentry_scope.c index bfe798edac..4c9d33d7a0 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -28,16 +28,6 @@ # define SENTRY_BACKEND "native" #endif -static bool g_scope_initialized = false; -static size_t g_scope_accesses = 0; -static bool g_scope_idle_initialized = false; -static sentry_cond_t g_scope_idle; -#ifdef SENTRY__MUTEX_INIT_DYN -SENTRY__MUTEX_INIT_DYN(g_lock) -#else -static sentry_mutex_t g_lock = SENTRY__MUTEX_INIT; -#endif - struct sentry_scope_data_s { sentry_rwlock_t rwlock; @@ -71,10 +61,18 @@ struct sentry_scope_data_s { bool trace_managed; }; +static bool g_scope_initialized = false; static sentry_scope_t g_scope = { 0 }; static sentry_scope_data_t g_scope_data = { 0 }; +static bool g_scope_idle_initialized = false; +static sentry_cond_t g_scope_idle; +#ifdef SENTRY__MUTEX_INIT_DYN +SENTRY__MUTEX_INIT_DYN(g_lock) +#else +static sentry_mutex_t g_lock = SENTRY__MUTEX_INIT; +#endif -#define DATA_READ_LOCK(Data) \ +#define SENTRY_SCOPE_READ_LOCK(Data) \ for (const sentry_scope_data_t *_locked_data = (Data); _locked_data; \ sentry__rwlock_read_unlock((sentry_rwlock_t *)&_locked_data->rwlock), \ _locked_data = NULL) \ @@ -84,7 +82,7 @@ static sentry_scope_data_t g_scope_data = { 0 }; true); \ _locked_once; _locked_once = false) -#define DATA_WRITE_LOCK(Data) \ +#define SENTRY_SCOPE_WRITE_LOCK(Data) \ for (sentry_scope_data_t *_locked_data = (Data); _locked_data; \ sentry__rwlock_write_unlock(&_locked_data->rwlock), \ _locked_data = NULL) \ @@ -133,40 +131,8 @@ get_client_sdk(void) return client_sdk; } -static bool -data_update_context_by_key_n(sentry_scope_data_t *data, const char *key, - size_t key_len, sentry_value_t value) -{ - sentry__rwlock_write_lock(&data->rwlock); - sentry_value_t context - = sentry_value_get_by_key_n(data->contexts, key, key_len); - if (!sentry_value_is_null(context)) { - sentry__value_merge_objects(value, context); - } - int rv = sentry_value_set_by_key_n(data->contexts, key, key_len, value); - sentry__rwlock_write_unlock(&data->rwlock); - return rv == 0; -} - -static void -generate_propagation_context(sentry_value_t propagation_context) -{ - sentry_value_set_by_key( - propagation_context, "trace", sentry_value_new_object()); - sentry_uuid_t trace_id = sentry_uuid_new_v4(); - sentry_uuid_t span_id = sentry_uuid_new_v4(); - sentry_value_set_by_key( - sentry_value_get_by_key(propagation_context, "trace"), "trace_id", - sentry__value_new_internal_uuid(&trace_id)); - sentry_value_set_by_key( - sentry_value_get_by_key(propagation_context, "trace"), "span_id", - sentry__value_new_span_uuid(&span_id)); - sentry__generate_sample_rand( - sentry_value_get_by_key(propagation_context, "trace")); -} - static void -init_data(sentry_scope_data_t *data) +init_scope_data(sentry_scope_data_t *data) { data->release = sentry_value_new_null(); data->environment = sentry_value_new_null(); @@ -190,7 +156,7 @@ init_data(sentry_scope_data_t *data) } static void -cleanup_data(sentry_scope_data_t *data) +cleanup_scope_data(sentry_scope_data_t *data) { sentry_value_decref(data->release); sentry_value_decref(data->environment); @@ -211,23 +177,23 @@ cleanup_data(sentry_scope_data_t *data) } static sentry_scope_data_t * -new_data(void) +new_scope_data(void) { sentry_scope_data_t *data = SENTRY_MAKE(sentry_scope_data_t); if (data) { sentry__rwlock_init(&data->rwlock); - init_data(data); + init_scope_data(data); } return data; } static void -free_data(sentry_scope_data_t *data) +free_scope_data(sentry_scope_data_t *data) { if (!data) { return; } - cleanup_data(data); + cleanup_scope_data(data); sentry__rwlock_free(&data->rwlock); sentry_free(data); } @@ -235,7 +201,7 @@ free_data(sentry_scope_data_t *data) static void cleanup_global_data(sentry_scope_data_t *data) { - cleanup_data(data); + cleanup_scope_data(data); sentry__rwlock_free(&data->rwlock); } @@ -248,50 +214,17 @@ init_global_data(sentry_scope_data_t *data) } static void -data_apply_options(sentry_scope_data_t *data, sentry_options_t *options) -{ - DATA_WRITE_LOCK (data) { - if (options->sdk_name) { - sentry_value_t sdk_name - = sentry_value_new_string(options->sdk_name); - sentry_value_set_by_key(data->client_sdk, "name", sdk_name); - } - sentry_value_t integrations - = sentry_value_get_by_key(data->client_sdk, "integrations"); - for (size_t i = 0; i < options->num_integrations; i++) { - const char *name = options->integrations[i]->name; - if (!name) { - continue; - } - if (sentry_value_is_null(integrations)) { - integrations = sentry_value_new_list(); - sentry_value_set_by_key( - data->client_sdk, "integrations", integrations); - } - sentry_value_append(integrations, sentry_value_new_string(name)); - } - sentry_value_freeze(data->client_sdk); - generate_propagation_context(data->propagation_context); - sentry_value_decref(data->attachments); - data->attachments = options->attachments; - options->attachments = sentry_value_new_null(); - sentry__ringbuffer_set_max_size( - data->breadcrumbs, options->max_breadcrumbs); - } -} - -static void -clear_data(sentry_scope_data_t *data) +clear_scope_data(sentry_scope_data_t *data) { - DATA_WRITE_LOCK (data) { + SENTRY_SCOPE_WRITE_LOCK (data) { bool trace_managed = data->trace_managed; sentry_value_t propagation_context = sentry_value_incref(data->propagation_context); sentry_value_t dynamic_sampling_context = sentry_value_incref(data->dynamic_sampling_context); - cleanup_data(data); - init_data(data); + cleanup_scope_data(data); + init_scope_data(data); sentry_value_decref(data->propagation_context); sentry_value_decref(data->dynamic_sampling_context); @@ -302,7 +235,7 @@ clear_data(sentry_scope_data_t *data) } static sentry_scope_data_t * -clone_data(const sentry_scope_data_t *source) +clone_scope_data(const sentry_scope_data_t *source) { sentry_scope_data_t *clone = SENTRY_MAKE(sentry_scope_data_t); if (!clone) { @@ -310,7 +243,7 @@ clone_data(const sentry_scope_data_t *source) } sentry__rwlock_init(&clone->rwlock); - DATA_READ_LOCK (source) { + SENTRY_SCOPE_READ_LOCK (source) { clone->release = sentry__value_clone(source->release); clone->environment = sentry__value_clone(source->environment); clone->transaction = sentry__value_clone(source->transaction); @@ -335,857 +268,79 @@ clone_data(const sentry_scope_data_t *source) sentry__attachments_extend(&clone->attachments, source->attachments); clone->transaction_object = source->transaction_object; sentry__transaction_incref(clone->transaction_object); - clone->span = source->span; - sentry__span_incref(clone->span); - clone->trace_managed = source->trace_managed; - } - - return clone; -} - -static sentry_value_t -data_ref_propagation_context(const sentry_scope_data_t *data) -{ - sentry_value_t propagation_context = sentry_value_new_null(); - DATA_READ_LOCK (data) { - propagation_context = sentry_value_incref(data->propagation_context); - } - return propagation_context; -} - -static void -data_set_propagation_context( - sentry_scope_data_t *data, const char *key, sentry_value_t value) -{ - DATA_WRITE_LOCK (data) { - sentry_value_set_by_key(data->propagation_context, key, value); - } -} - -static void -data_regenerate_propagation_context(sentry_scope_data_t *data) -{ - DATA_WRITE_LOCK (data) { - generate_propagation_context(data->propagation_context); - } -} - -static sentry_value_t -data_ref_trace_context(const sentry_scope_data_t *data) -{ - sentry_value_t trace_context = sentry_value_new_null(); - DATA_READ_LOCK (data) { - trace_context = sentry_value_incref( - sentry_value_get_by_key(data->propagation_context, "trace")); - } - return trace_context; -} - -static void -data_set_trace_context( - sentry_scope_data_t *data, const char *key, sentry_value_t value) -{ - DATA_WRITE_LOCK (data) { - sentry_value_set_by_key( - sentry_value_get_by_key(data->propagation_context, "trace"), key, - value); - } -} - -static bool -data_is_trace_managed(const sentry_scope_data_t *data) -{ - bool managed = false; - DATA_READ_LOCK (data) { - managed = data->trace_managed; - } - return managed; -} - -static void -data_set_trace_managed(sentry_scope_data_t *data, bool managed) -{ - DATA_WRITE_LOCK (data) { - data->trace_managed = managed; - } -} - -static void -data_freeze_dsc(sentry_scope_data_t *data, sentry_value_t incoming) -{ - sentry_value_t dsc = sentry_value_new_object(); - sentry__value_merge_objects(dsc, incoming); - sentry_value_freeze(dsc); - DATA_WRITE_LOCK (data) { - sentry__value_replace(&data->dynamic_sampling_context, dsc); - } -} - -static sentry_value_t -data_ref_dsc(const sentry_scope_data_t *data) -{ - sentry_value_t dsc = sentry_value_new_null(); - DATA_READ_LOCK (data) { - dsc = sentry_value_incref(data->dynamic_sampling_context); - } - return dsc; -} - -static sentry_value_t -data_clone_dsc(const sentry_scope_data_t *data) -{ - sentry_value_t dsc = sentry_value_new_null(); - DATA_READ_LOCK (data) { - dsc = sentry__value_clone(data->dynamic_sampling_context); - } - return dsc; -} - -static void -data_foreach_dsc(const sentry_scope_data_t *data, - void (*callback)(const char *key, sentry_value_t value, void *userdata), - void *userdata) -{ - DATA_READ_LOCK (data) { - sentry__value_foreach_key_value( - data->dynamic_sampling_context, callback, userdata); - } -} - -static void -data_update_dsc(sentry_scope_data_t *data, const sentry_options_t *options) -{ - sentry_value_t dsc = sentry_value_new_object(); - - if (options->dsn) { - sentry_value_set_by_key(dsc, "public_key", - sentry_value_new_string(options->dsn->public_key)); - } - const char *org_id = sentry__options_get_org_id(options); - if (org_id) { - sentry_value_set_by_key(dsc, "org_id", sentry_value_new_string(org_id)); - } - sentry_value_set_by_key(dsc, "sample_rate", - sentry_value_new_double(options->traces_sample_rate)); - if (options->traces_sampler) { - sentry_value_set_by_key( - dsc, "sample_rate", sentry_value_new_double(1.0)); - } - - DATA_WRITE_LOCK (data) { - sentry_value_t sample_rand = sentry_value_get_by_key( - sentry_value_get_by_key(data->propagation_context, "trace"), - "sample_rand"); - sentry_value_set_by_key( - dsc, "sample_rand", sentry_value_incref(sample_rand)); - sentry_value_set_by_key( - dsc, "release", sentry_value_incref(data->release)); - sentry_value_set_by_key( - dsc, "environment", sentry_value_incref(data->environment)); - sentry__value_replace(&data->dynamic_sampling_context, dsc); - } -} - -static sentry_value_t -data_ref_client_sdk(const sentry_scope_data_t *data) -{ - sentry_value_t client_sdk = sentry_value_new_null(); - DATA_READ_LOCK (data) { - client_sdk = sentry_value_incref(data->client_sdk); - } - return client_sdk; -} - -static void -data_apply_tags_and_extra(const sentry_scope_data_t *data, sentry_value_t event) -{ - DATA_READ_LOCK (data) { - sentry_value_t event_tags = sentry_value_get_by_key(event, "tags"); - if (sentry_value_is_null(event_tags)) { - if (!sentry_value_is_null(data->tags)) { - sentry_value_set_by_key( - event, "tags", sentry__value_clone(data->tags)); - } - } else { - sentry__value_merge_objects(event_tags, data->tags); - } - - sentry_value_t event_extra = sentry_value_get_by_key(event, "extra"); - if (sentry_value_is_null(event_extra)) { - if (!sentry_value_is_null(data->extra)) { - sentry_value_set_by_key( - event, "extra", sentry__value_clone(data->extra)); - } - } else { - sentry__value_merge_objects(event_extra, data->extra); - } - } -} - -static sentry_value_t -data_clone_contexts(const sentry_scope_data_t *data) -{ - sentry_value_t contexts = sentry_value_new_null(); - DATA_READ_LOCK (data) { - contexts = sentry__value_clone(data->contexts); - } - return contexts; -} - -static sentry_value_t -data_ref_span_or_transaction(const sentry_scope_data_t *data) -{ - sentry_value_t value = sentry_value_new_null(); - DATA_READ_LOCK (data) { - if (data->span) { - value = sentry_value_incref(data->span->inner); - } else if (data->transaction_object) { - value = sentry_value_incref(data->transaction_object->inner); - } - } - return value; -} - -static sentry_value_t -data_breadcrumbs_to_list(const sentry_scope_data_t *data) -{ - sentry_value_t breadcrumbs = sentry_value_new_null(); - DATA_READ_LOCK (data) { - breadcrumbs = sentry__ringbuffer_to_list(data->breadcrumbs); - } - return breadcrumbs; -} - -static bool -data_add_breadcrumb(sentry_scope_data_t *data, sentry_value_t breadcrumb) -{ - bool added = false; - DATA_WRITE_LOCK (data) { - added = sentry__ringbuffer_append(data->breadcrumbs, breadcrumb) == 0; - } - return added; -} - -static sentry_value_t -data_ref_user(const sentry_scope_data_t *data) -{ - sentry_value_t user = sentry_value_new_null(); - DATA_READ_LOCK (data) { - user = sentry_value_incref(data->user); - } - return user; -} - -static void -data_set_user(sentry_scope_data_t *data, sentry_value_t user) -{ - DATA_WRITE_LOCK (data) { - sentry__value_replace(&data->user, sentry_value_incref(user)); - } -} - -static sentry_value_t -data_ref_tags(const sentry_scope_data_t *data) -{ - sentry_value_t tags = sentry_value_new_null(); - DATA_READ_LOCK (data) { - tags = sentry_value_incref(data->tags); - } - return tags; -} - -static bool -data_set_tag(sentry_scope_data_t *data, const char *key, sentry_value_t value) -{ - bool did_set = false; - DATA_WRITE_LOCK (data) { - did_set = sentry_value_set_by_key(data->tags, key, value) == 0; - } - return did_set; -} - -static bool -data_set_tag_n(sentry_scope_data_t *data, const char *key, size_t key_len, - sentry_value_t value) -{ - bool did_set = false; - DATA_WRITE_LOCK (data) { - did_set - = sentry_value_set_by_key_n(data->tags, key, key_len, value) == 0; - } - return did_set; -} - -static bool -data_remove_tag(sentry_scope_data_t *data, const char *key) -{ - bool removed = false; - DATA_WRITE_LOCK (data) { - removed = sentry_value_remove_by_key(data->tags, key) == 0; - } - return removed; -} - -static char * -data_remove_tag_n(sentry_scope_data_t *data, const char *key, size_t key_len) -{ - char *removed_key = NULL; - DATA_WRITE_LOCK (data) { - removed_key - = sentry__value_remove_and_take_key_n(data->tags, key, key_len); - } - return removed_key; -} - -static sentry_value_t -data_ref_extra(const sentry_scope_data_t *data) -{ - sentry_value_t extra = sentry_value_new_null(); - DATA_READ_LOCK (data) { - extra = sentry_value_incref(data->extra); - } - return extra; -} - -static bool -data_set_extra(sentry_scope_data_t *data, const char *key, sentry_value_t value) -{ - bool did_set = false; - DATA_WRITE_LOCK (data) { - did_set = sentry_value_set_by_key(data->extra, key, value) == 0; - } - return did_set; -} - -static bool -data_set_extra_n(sentry_scope_data_t *data, const char *key, size_t key_len, - sentry_value_t value) -{ - bool did_set = false; - DATA_WRITE_LOCK (data) { - did_set - = sentry_value_set_by_key_n(data->extra, key, key_len, value) == 0; - } - return did_set; -} - -static bool -data_remove_extra(sentry_scope_data_t *data, const char *key) -{ - bool removed = false; - DATA_WRITE_LOCK (data) { - removed = sentry_value_remove_by_key(data->extra, key) == 0; - } - return removed; -} - -static char * -data_remove_extra_n(sentry_scope_data_t *data, const char *key, size_t key_len) -{ - char *removed_key = NULL; - DATA_WRITE_LOCK (data) { - removed_key - = sentry__value_remove_and_take_key_n(data->extra, key, key_len); - } - return removed_key; -} - -static sentry_value_t -data_ref_attributes(const sentry_scope_data_t *data) -{ - sentry_value_t attributes = sentry_value_new_null(); - DATA_READ_LOCK (data) { - attributes = sentry_value_incref(data->attributes); - } - return attributes; -} - -static bool -data_set_attribute_n(sentry_scope_data_t *data, const char *key, size_t key_len, - sentry_value_t attribute) -{ - bool did_set = false; - DATA_WRITE_LOCK (data) { - did_set = sentry_value_set_by_key_n( - data->attributes, key, key_len, attribute) - == 0; - } - return did_set; -} - -static void -data_remove_attribute(sentry_scope_data_t *data, const char *key) -{ - DATA_WRITE_LOCK (data) { - sentry_value_remove_by_key(data->attributes, key); - } -} - -static void -data_remove_attribute_n( - sentry_scope_data_t *data, const char *key, size_t key_len) -{ - DATA_WRITE_LOCK (data) { - sentry_value_remove_by_key_n(data->attributes, key, key_len); - } -} - -static sentry_value_t -data_ref_contexts(const sentry_scope_data_t *data) -{ - sentry_value_t contexts = sentry_value_new_null(); - DATA_READ_LOCK (data) { - contexts = sentry_value_incref(data->contexts); - } - return contexts; -} - -static bool -data_set_context( - sentry_scope_data_t *data, const char *key, sentry_value_t value) -{ - bool did_set = false; - DATA_WRITE_LOCK (data) { - did_set = sentry_value_set_by_key(data->contexts, key, value) == 0; - } - return did_set; -} - -static bool -data_set_context_n(sentry_scope_data_t *data, const char *key, size_t key_len, - sentry_value_t value) -{ - bool did_set = false; - DATA_WRITE_LOCK (data) { - did_set = sentry_value_set_by_key_n(data->contexts, key, key_len, value) - == 0; - } - return did_set; -} - -static bool -data_update_context_n(sentry_scope_data_t *data, const char *key, - size_t key_len, sentry_value_t value) -{ - return data_update_context_by_key_n(data, key, key_len, value); -} - -static bool -data_remove_context(sentry_scope_data_t *data, const char *key) -{ - bool removed = false; - DATA_WRITE_LOCK (data) { - removed = sentry_value_remove_by_key(data->contexts, key) == 0; - } - return removed; -} - -static char * -data_remove_context_n( - sentry_scope_data_t *data, const char *key, size_t key_len) -{ - char *removed_key = NULL; - DATA_WRITE_LOCK (data) { - removed_key - = sentry__value_remove_and_take_key_n(data->contexts, key, key_len); - } - return removed_key; -} - -static sentry_value_t -data_ref_fingerprint(const sentry_scope_data_t *data) -{ - sentry_value_t fingerprint = sentry_value_new_null(); - DATA_READ_LOCK (data) { - fingerprint = sentry_value_incref(data->fingerprint); - } - return fingerprint; -} - -static void -data_set_fingerprint(sentry_scope_data_t *data, sentry_value_t fingerprint) -{ - DATA_WRITE_LOCK (data) { - sentry__value_replace( - &data->fingerprint, sentry_value_incref(fingerprint)); - } -} - -static sentry_level_t -data_get_level(const sentry_scope_data_t *data) -{ - sentry_level_t level = SENTRY_LEVEL_ERROR; - DATA_READ_LOCK (data) { - level = data->level; - } - return level; -} - -static void -data_set_level(sentry_scope_data_t *data, sentry_level_t level) -{ - DATA_WRITE_LOCK (data) { - data->level = level; - } -} - -static sentry_uuid_t -data_get_last_event_id(const sentry_scope_data_t *data) -{ - sentry_uuid_t event_id = sentry_uuid_nil(); - DATA_READ_LOCK (data) { - event_id = data->last_event_id; - } - return event_id; -} - -static void -data_set_last_event_id(sentry_scope_data_t *data, sentry_uuid_t event_id) -{ - DATA_WRITE_LOCK (data) { - data->last_event_id = event_id; - } -} - -static sentry_value_t -data_clone_attachments(const sentry_scope_data_t *data) -{ - sentry_value_t attachments = sentry_value_new_null(); - DATA_READ_LOCK (data) { - attachments = sentry__value_clone(data->attachments); - } - return attachments; -} - -static sentry_value_t -data_add_attachment( - sentry_scope_data_t *data, sentry_value_t attachment, bool *did_add) -{ - sentry_value_t added = sentry_value_new_null(); - if (did_add) { - *did_add = false; - } - DATA_WRITE_LOCK (data) { - size_t len = sentry_value_get_length(data->attachments); - added = sentry__attachments_add(&data->attachments, attachment); - if (did_add) { - *did_add = !sentry_value_is_null(added) - && sentry_value_get_length(data->attachments) > len; - } - } - return added; -} - -static sentry_value_t -data_remove_attachment( - sentry_scope_data_t *data, const sentry_uuid_t *attachment_id) -{ - sentry_value_t removed = sentry_value_new_null(); - DATA_WRITE_LOCK (data) { - removed = sentry__attachments_remove(data->attachments, attachment_id); - } - return removed; -} - -static sentry_value_t -data_take_attachments(sentry_scope_data_t *data) -{ - sentry_value_t attachments = sentry_value_new_null(); - DATA_WRITE_LOCK (data) { - attachments = data->attachments; - data->attachments = sentry__attachments_new(); - } - return attachments; -} - -static sentry_transaction_t * -data_ref_transaction_object(const sentry_scope_data_t *data) -{ - sentry_transaction_t *transaction = NULL; - DATA_READ_LOCK (data) { - transaction = data->transaction_object; - sentry__transaction_incref(transaction); - } - return transaction; -} - -static bool -value_has_span_id(sentry_value_t value, const char *span_id) -{ - const char *value_span_id - = sentry_value_as_string(sentry_value_get_by_key(value, "span_id")); - return sentry__string_eq(value_span_id, span_id); -} - -static void -data_set_transaction_object( - sentry_scope_data_t *data, sentry_transaction_t *transaction) -{ - sentry__transaction_incref(transaction); - DATA_WRITE_LOCK (data) { - sentry__span_decref(data->span); - data->span = NULL; - sentry__transaction_decref(data->transaction_object); - data->transaction_object = transaction; - } -} - -static bool -data_remove_transaction_object( - sentry_scope_data_t *data, sentry_transaction_t *transaction) -{ - bool removed = false; - DATA_WRITE_LOCK (data) { - if (transaction && data->transaction_object == transaction) { - data->transaction_object = NULL; - removed = true; - } - } - if (removed) { - sentry__transaction_decref(transaction); - } - return removed; -} - -static bool -data_remove_transaction_value( - sentry_scope_data_t *data, sentry_value_t transaction) -{ - const char *span_id = sentry_value_as_string( - sentry_value_get_by_key(transaction, "span_id")); - sentry_transaction_t *transaction_object = NULL; - DATA_WRITE_LOCK (data) { - if (data->transaction_object - && value_has_span_id(data->transaction_object->inner, span_id)) { - transaction_object = data->transaction_object; - data->transaction_object = NULL; - } - } - sentry__transaction_decref(transaction_object); - return transaction_object != NULL; -} - -static bool -data_restore_transaction_object( - sentry_scope_data_t *data, sentry_transaction_t *transaction) -{ - bool restored = false; - DATA_WRITE_LOCK (data) { - if (!data->transaction_object && !data->span && transaction) { - data->transaction_object = transaction; - restored = true; - } - } - return restored; -} - -static sentry_span_t * -data_ref_span(const sentry_scope_data_t *data) -{ - sentry_span_t *span = NULL; - DATA_READ_LOCK (data) { - span = data->span; - sentry__span_incref(span); - } - return span; -} - -static void -data_set_span(sentry_scope_data_t *data, sentry_span_t *span) -{ - sentry__span_incref(span); - DATA_WRITE_LOCK (data) { - sentry__transaction_decref(data->transaction_object); - data->transaction_object = NULL; - sentry__span_decref(data->span); - data->span = span; - } -} - -static bool -data_remove_span(sentry_scope_data_t *data, sentry_span_t *span) -{ - bool removed = false; - DATA_WRITE_LOCK (data) { - if (span && data->span == span) { - data->span = NULL; - removed = true; - } - } - if (removed) { - sentry__span_decref(span); - } - return removed; -} - -static bool -data_remove_span_value(sentry_scope_data_t *data, sentry_value_t span) -{ - const char *span_id - = sentry_value_as_string(sentry_value_get_by_key(span, "span_id")); - sentry_span_t *scope_span = NULL; - DATA_WRITE_LOCK (data) { - if (data->span && value_has_span_id(data->span->inner, span_id)) { - scope_span = data->span; - data->span = NULL; - } - } - sentry__span_decref(scope_span); - return scope_span != NULL; -} - -static bool -data_restore_span(sentry_scope_data_t *data, sentry_span_t *span) -{ - bool restored = false; - DATA_WRITE_LOCK (data) { - if (!data->span && !data->transaction_object && span) { - data->span = span; - restored = true; - } - } - return restored; -} - -static sentry_value_t -data_ref_release(const sentry_scope_data_t *data) -{ - sentry_value_t release = sentry_value_new_null(); - DATA_READ_LOCK (data) { - release = sentry_value_incref(data->release); - } - return release; -} - -static void -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)); - } -} - -static sentry_value_t -data_ref_environment(const sentry_scope_data_t *data) -{ - sentry_value_t environment = sentry_value_new_null(); - DATA_READ_LOCK (data) { - environment = sentry_value_incref(data->environment); - } - return environment; -} - -static void -data_set_environment(sentry_scope_data_t *data, sentry_value_t value) -{ - DATA_WRITE_LOCK (data) { - sentry__value_replace(&data->environment, sentry_value_incref(value)); - sentry_value_set_by_key(data->dynamic_sampling_context, "environment", - sentry_value_incref(value)); + clone->span = source->span; + sentry__span_incref(clone->span); + clone->trace_managed = source->trace_managed; } -} -static sentry_value_t -data_ref_transaction(const sentry_scope_data_t *data) -{ - sentry_value_t transaction = sentry_value_new_null(); - DATA_READ_LOCK (data) { - transaction = sentry_value_incref(data->transaction); - } - return transaction; + return clone; } static void -data_set_transaction(sentry_scope_data_t *data, sentry_value_t value, - const char *transaction, size_t transaction_len) +generate_propagation_context(sentry_value_t propagation_context) { - DATA_WRITE_LOCK (data) { - sentry__value_replace(&data->transaction, sentry_value_incref(value)); - if (data->transaction_object) { - sentry_transaction_set_name_n( - data->transaction_object, transaction, transaction_len); - } - } + sentry_value_set_by_key( + propagation_context, "trace", sentry_value_new_object()); + sentry_uuid_t trace_id = sentry_uuid_new_v4(); + sentry_uuid_t span_id = sentry_uuid_new_v4(); + sentry_value_set_by_key( + sentry_value_get_by_key(propagation_context, "trace"), "trace_id", + sentry__value_new_internal_uuid(&trace_id)); + sentry_value_set_by_key( + sentry_value_get_by_key(propagation_context, "trace"), "span_id", + sentry__value_new_span_uuid(&span_id)); + sentry__generate_sample_rand( + sentry_value_get_by_key(propagation_context, "trace")); } -static void -data_apply_to_telemetry(const sentry_scope_data_t *data, - sentry_value_t telemetry, sentry_value_t attributes) +void +sentry__scope_update_dsc(sentry_scope_t *scope, const sentry_options_t *options) { - sentry_value_t os_name = sentry_value_new_null(); - sentry_value_t os_version = sentry_value_new_null(); + sentry_scope_data_t *data = scope->data; + sentry_value_t dsc = sentry_value_new_object(); - DATA_READ_LOCK (data) { - sentry__value_merge_objects_shallow(attributes, data->attributes); + if (options->dsn) { + sentry_value_set_by_key(dsc, "public_key", + sentry_value_new_string(options->dsn->public_key)); + } + const char *org_id = sentry__options_get_org_id(options); + if (org_id) { + sentry_value_set_by_key(dsc, "org_id", sentry_value_new_string(org_id)); + } + sentry_value_set_by_key(dsc, "sample_rate", + sentry_value_new_double(options->traces_sample_rate)); + if (options->traces_sampler) { + sentry_value_set_by_key( + dsc, "sample_rate", sentry_value_new_double(1.0)); + } - sentry_value_t trace_id = sentry_value_get_by_key( + SENTRY_SCOPE_WRITE_LOCK (data) { + sentry_value_t sample_rand = sentry_value_get_by_key( sentry_value_get_by_key(data->propagation_context, "trace"), - "trace_id"); - - sentry_value_t parent_span_id = sentry_value_new_object(); - bool has_parent_span = false; - if (data->transaction_object) { - sentry_value_t span_id = sentry_value_get_by_key( - data->transaction_object->inner, "span_id"); - sentry_value_set_by_key( - parent_span_id, "value", sentry_value_incref(span_id)); - trace_id = sentry_value_get_by_key( - data->transaction_object->inner, "trace_id"); - has_parent_span = true; - } else if (data->span) { - sentry_value_t span_id - = sentry_value_get_by_key(data->span->inner, "span_id"); - sentry_value_set_by_key( - parent_span_id, "value", sentry_value_incref(span_id)); - trace_id = sentry_value_get_by_key(data->span->inner, "trace_id"); - has_parent_span = true; - } + "sample_rand"); sentry_value_set_by_key( - parent_span_id, "type", sentry_value_new_string("string")); - if (has_parent_span - && sentry_value_is_null(sentry_value_get_by_key( - attributes, "sentry.trace.parent_span_id"))) { - sentry_value_set_by_key( - attributes, "sentry.trace.parent_span_id", parent_span_id); - } else { - sentry_value_decref(parent_span_id); - } - if (!sentry_value_is_null(trace_id) - && sentry_value_is_null( - sentry_value_get_by_key(telemetry, "trace_id"))) { - sentry_value_set_by_key( - telemetry, "trace_id", sentry_value_incref(trace_id)); - } - - sentry_value_t os_context - = sentry_value_get_by_key(data->contexts, "os"); - if (!sentry_value_is_null(os_context)) { - os_name = sentry_value_incref( - sentry_value_get_by_key(os_context, "name")); - os_version = sentry_value_incref( - sentry_value_get_by_key(os_context, "version")); - } + dsc, "sample_rand", sentry_value_incref(sample_rand)); + sentry_value_set_by_key( + dsc, "release", sentry_value_incref(data->release)); + sentry_value_set_by_key( + dsc, "environment", sentry_value_incref(data->environment)); + sentry__value_replace(&data->dynamic_sampling_context, dsc); } +} - if (!sentry_value_is_null(os_name)) { - sentry__value_add_attribute(attributes, os_name, "string", "os.name"); - } else { - sentry_value_decref(os_name); - } - if (!sentry_value_is_null(os_version)) { - sentry__value_add_attribute( - attributes, os_version, "string", "os.version"); - } else { - sentry_value_decref(os_version); - } +static bool +value_has_span_id(sentry_value_t value, const char *span_id) +{ + const char *value_span_id + = sentry_value_as_string(sentry_value_get_by_key(value, "span_id")); + return sentry__string_eq(value_span_id, span_id); } static bool init_scope(sentry_scope_t *scope, sentry_scope_data_t *data) { - scope->data = data ? data : new_data(); + scope->refcount = 1; + scope->data = data ? data : new_scope_data(); if (!scope->data) { return false; } @@ -1208,7 +363,7 @@ get_scope(void) memset(&g_scope, 0, sizeof(sentry_scope_t)); memset(&g_scope_data, 0, sizeof(sentry_scope_data_t)); sentry__rwlock_init(&g_scope_data.rwlock); - init_data(&g_scope_data); + init_scope_data(&g_scope_data); if (!init_scope(&g_scope, &g_scope_data)) { return &g_scope; } @@ -1237,18 +392,57 @@ cleanup_observers(sentry_scope_t *scope) static void cleanup_scope(sentry_scope_t *scope) { - free_data(scope->data); + free_scope_data(scope->data); scope->data = NULL; cleanup_observers(scope); sentry__mutex_free(&scope->observers_lock); } +sentry_scope_t * +sentry__scope_incref(sentry_scope_t *scope) +{ + if (scope) { + sentry__atomic_fetch_and_add(&scope->refcount, 1); + } + return scope; +} + +void +sentry__scope_decref(sentry_scope_t *scope) +{ + if (!scope) { + return; + } + + if (scope == &g_scope) { + SENTRY__MUTEX_INIT_DYN_ONCE(g_lock); + sentry__mutex_lock(&g_lock); + long refcount = sentry__atomic_fetch_and_add(&scope->refcount, -1); + assert(refcount > 1); + if (refcount == 2 && g_scope_idle_initialized) { + sentry__cond_wake(&g_scope_idle); + } + sentry__mutex_unlock(&g_lock); + return; + } + + if (sentry__atomic_fetch_and_add(&scope->refcount, -1) != 1) { + return; + } + cleanup_scope(scope); + sentry_free(scope); +} + void sentry__scope_cleanup(void) { SENTRY__MUTEX_INIT_DYN_ONCE(g_lock); sentry__mutex_lock(&g_lock); - while (g_scope_accesses > 0) { + if (!g_scope_idle_initialized) { + sentry__cond_init(&g_scope_idle); + g_scope_idle_initialized = true; + } + while (g_scope_initialized && sentry__atomic_fetch(&g_scope.refcount) > 1) { sentry__cond_wait(&g_scope_idle, &g_lock); } if (g_scope_initialized) { @@ -1262,15 +456,7 @@ sentry__scope_cleanup(void) } sentry_scope_t * -sentry__scope_lock(void) -{ - SENTRY__MUTEX_INIT_DYN_ONCE(g_lock); - sentry__mutex_lock(&g_lock); - return get_scope(); -} - -sentry_scope_t * -sentry__scope_begin(void) +sentry__scope_getref(void) { SENTRY__MUTEX_INIT_DYN_ONCE(g_lock); sentry__mutex_lock(&g_lock); @@ -1278,51 +464,29 @@ sentry__scope_begin(void) sentry__cond_init(&g_scope_idle); g_scope_idle_initialized = true; } - sentry_scope_t *scope = get_scope(); - g_scope_accesses++; + sentry_scope_t *scope = sentry__scope_incref(get_scope()); sentry__mutex_unlock(&g_lock); return scope; } -static void -end_scope_access(void) -{ - SENTRY__MUTEX_INIT_DYN_ONCE(g_lock); - sentry__mutex_lock(&g_lock); - assert(g_scope_accesses > 0); - g_scope_accesses--; - if (g_scope_accesses == 0) { - sentry__cond_wake(&g_scope_idle); - } - sentry__mutex_unlock(&g_lock); -} - -static void -finish_scope(bool flush, bool unlock) +void +sentry__scope_finish(sentry_scope_t *scope, bool flush) { - if (unlock) { - SENTRY__MUTEX_INIT_DYN_ONCE(g_lock); + if (!scope) { + return; } - sentry__mutex_lock(&g_scope.observers_lock); - if (g_scope.is_notifying > 0) { + sentry__mutex_lock(&scope->observers_lock); + if (scope->is_notifying > 0) { // defer the flush requested by a reentrant scope change - g_scope.pending_flush = flush || g_scope.pending_flush; + scope->pending_flush = flush || scope->pending_flush; flush = false; } else { // consume any flush requested by a reentrant scope change - flush = flush || g_scope.pending_flush; - g_scope.pending_flush = false; - } - sentry__mutex_unlock(&g_scope.observers_lock); - - if (unlock) { - // we try to unlock the scope as soon as possible. The - // backend will do its own `WITH_SCOPE` internally. - sentry__mutex_unlock(&g_lock); - } else { - end_scope_access(); + flush = flush || scope->pending_flush; + scope->pending_flush = false; } + sentry__mutex_unlock(&scope->observers_lock); if (flush) { SENTRY_WITH_OPTIONS (options) { @@ -1333,24 +497,6 @@ finish_scope(bool flush, bool unlock) } } -void -sentry__scope_unlock(void) -{ - finish_scope(false, true); -} - -void -sentry__scope_flush_unlock(void) -{ - finish_scope(true, true); -} - -void -sentry__scope_end(bool flush) -{ - finish_scope(flush, false); -} - sentry_scope_observer_t * sentry__scope_observer_new(void) { @@ -1480,12 +626,7 @@ sentry_scope_new(void) void sentry_scope_free(sentry_scope_t *scope) { - if (!scope) { - return; - } - - cleanup_scope(scope); - sentry_free(scope); + sentry__scope_decref(scope); } bool @@ -1524,7 +665,35 @@ sentry_local_scope_new(void) void sentry__scope_apply_options(sentry_scope_t *scope, sentry_options_t *options) { - data_apply_options(scope->data, options); + sentry_scope_data_t *data = scope->data; + SENTRY_SCOPE_WRITE_LOCK (data) { + if (options->sdk_name) { + sentry_value_t sdk_name + = sentry_value_new_string(options->sdk_name); + sentry_value_set_by_key(data->client_sdk, "name", sdk_name); + } + sentry_value_t integrations + = sentry_value_get_by_key(data->client_sdk, "integrations"); + for (size_t i = 0; i < options->num_integrations; i++) { + const char *name = options->integrations[i]->name; + if (!name) { + continue; + } + if (sentry_value_is_null(integrations)) { + integrations = sentry_value_new_list(); + sentry_value_set_by_key( + data->client_sdk, "integrations", integrations); + } + sentry_value_append(integrations, sentry_value_new_string(name)); + } + sentry_value_freeze(data->client_sdk); + generate_propagation_context(data->propagation_context); + sentry_value_decref(data->attachments); + data->attachments = options->attachments; + options->attachments = sentry_value_new_null(); + sentry__ringbuffer_set_max_size( + data->breadcrumbs, options->max_breadcrumbs); + } sentry__scope_set_release_n( scope, options->release, sentry__guarded_strlen(options->release)); sentry__scope_set_environment_n(scope, options->environment, @@ -1548,7 +717,7 @@ sentry_scope_clear(sentry_scope_t *scope) } sentry__scope_end_notify(scope); - clear_data(scope->data); + clear_scope_data(scope->data); } sentry_scope_t * @@ -1563,13 +732,13 @@ sentry_scope_clone(const sentry_scope_t *scope) return NULL; } - sentry_scope_data_t *data = clone_data(scope->data); + sentry_scope_data_t *data = clone_scope_data(scope->data); if (!data) { sentry_free(clone); return NULL; } if (!init_scope(clone, data)) { - free_data(data); + free_scope_data(data); sentry_free(clone); return NULL; } @@ -1580,57 +749,89 @@ sentry_scope_clone(const sentry_scope_t *scope) sentry_value_t sentry__scope_ref_propagation_context(const sentry_scope_t *scope) { - return data_ref_propagation_context(scope->data); + sentry_value_t propagation_context = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + propagation_context + = sentry_value_incref(scope->data->propagation_context); + } + return propagation_context; } void sentry__scope_set_propagation_context( sentry_scope_t *scope, const char *key, sentry_value_t value) { - data_set_propagation_context(scope->data, key, value); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry_value_set_by_key(scope->data->propagation_context, key, value); + } } void sentry__scope_regenerate_propagation_context(sentry_scope_t *scope) { - data_regenerate_propagation_context(scope->data); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + generate_propagation_context(scope->data->propagation_context); + } } sentry_value_t sentry__scope_ref_trace_context(const sentry_scope_t *scope) { - return data_ref_trace_context(scope->data); + sentry_value_t trace_context = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + trace_context = sentry_value_incref( + sentry_value_get_by_key(scope->data->propagation_context, "trace")); + } + return trace_context; } void sentry__scope_set_trace_context( sentry_scope_t *scope, const char *key, sentry_value_t value) { - data_set_trace_context(scope->data, key, value); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry_value_set_by_key( + sentry_value_get_by_key(scope->data->propagation_context, "trace"), + key, value); + } } bool sentry__scope_is_trace_managed(const sentry_scope_t *scope) { - return data_is_trace_managed(scope->data); + bool managed = false; + SENTRY_SCOPE_READ_LOCK (scope->data) { + managed = scope->data->trace_managed; + } + return managed; } void sentry__scope_set_trace_managed(sentry_scope_t *scope, bool managed) { - data_set_trace_managed(scope->data, managed); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + scope->data->trace_managed = managed; + } } sentry_value_t sentry__scope_ref_dsc(const sentry_scope_t *scope) { - return data_ref_dsc(scope->data); + sentry_value_t dsc = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + dsc = sentry_value_incref(scope->data->dynamic_sampling_context); + } + return dsc; } sentry_value_t sentry__scope_clone_dsc(const sentry_scope_t *scope) { - return data_clone_dsc(scope->data); + sentry_value_t dsc = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + dsc = sentry__value_clone(scope->data->dynamic_sampling_context); + } + return dsc; } void @@ -1638,19 +839,21 @@ sentry__scope_foreach_dsc(const sentry_scope_t *scope, void (*callback)(const char *key, sentry_value_t value, void *userdata), void *userdata) { - data_foreach_dsc(scope->data, callback, userdata); + SENTRY_SCOPE_READ_LOCK (scope->data) { + sentry__value_foreach_key_value( + scope->data->dynamic_sampling_context, callback, userdata); + } } void sentry__scope_freeze_dsc(sentry_scope_t *scope, sentry_value_t incoming) { - data_freeze_dsc(scope->data, incoming); -} - -void -sentry__scope_update_dsc(sentry_scope_t *scope, const sentry_options_t *options) -{ - data_update_dsc(scope->data, options); + sentry_value_t dsc = sentry_value_new_object(); + sentry__value_merge_objects(dsc, incoming); + sentry_value_freeze(dsc); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry__value_replace(&scope->data->dynamic_sampling_context, dsc); + } } #if !defined(SENTRY_PLATFORM_NX) @@ -1757,16 +960,6 @@ sentry__symbolize_stacktrace(sentry_value_t stacktrace) #endif #ifdef SENTRY_UNITTEST -sentry_value_t -sentry__scope_ref_span_or_transaction(void) -{ - sentry_value_t result = sentry_value_new_null(); - SENTRY_WITH_SCOPE (scope) { - result = data_ref_span_or_transaction(scope->data); - } - return result; -} - bool sentry__scope_has_observers(const sentry_scope_t *scope) { @@ -1856,14 +1049,37 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, PLACE_STRING_VALUE("transaction", transaction); sentry_value_decref(transaction); - sentry_value_t client_sdk = data_ref_client_sdk(scope->data); + sentry_value_t client_sdk = sentry__scope_ref_client_sdk(scope); PLACE_VALUE("sdk", client_sdk); sentry_value_decref(client_sdk); - data_apply_tags_and_extra(scope->data, event); + SENTRY_SCOPE_READ_LOCK (scope->data) { + sentry_value_t event_tags = sentry_value_get_by_key(event, "tags"); + if (sentry_value_is_null(event_tags)) { + if (!sentry_value_is_null(scope->data->tags)) { + sentry_value_set_by_key( + event, "tags", sentry__value_clone(scope->data->tags)); + } + } else { + sentry__value_merge_objects(event_tags, scope->data->tags); + } + + sentry_value_t event_extra = sentry_value_get_by_key(event, "extra"); + if (sentry_value_is_null(event_extra)) { + if (!sentry_value_is_null(scope->data->extra)) { + sentry_value_set_by_key( + event, "extra", sentry__value_clone(scope->data->extra)); + } + } else { + sentry__value_merge_objects(event_extra, scope->data->extra); + } + } bool is_transaction = sentry__event_is_transaction(event); - sentry_value_t contexts = data_clone_contexts(scope->data); + sentry_value_t contexts = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + contexts = sentry__value_clone(scope->data->contexts); + } if (is_transaction && !sentry_value_is_null(contexts)) { sentry_value_remove_by_key(contexts, "trace"); } @@ -1873,7 +1089,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, sentry_value_t scoped_txn_or_span = sentry_value_new_null(); sentry_value_t scope_trace = sentry_value_new_null(); if (!is_transaction) { - scoped_txn_or_span = data_ref_span_or_transaction(scope->data); + scoped_txn_or_span = sentry__scope_ref_span_or_transaction(scope); scope_trace = sentry__value_get_trace_context(scoped_txn_or_span); } if (!sentry_value_is_null(scope_trace)) { @@ -1898,7 +1114,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, && sentry_value_is_null( sentry_value_get_by_key(event_contexts, "trace"))) { sentry_value_t propagation_context - = data_ref_propagation_context(scope->data); + = sentry__scope_ref_propagation_context(scope); sentry__value_merge_objects(contexts, propagation_context); sentry_value_decref(propagation_context); } @@ -1913,7 +1129,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, sentry_value_t event_breadcrumbs = sentry_value_get_by_key(event, "breadcrumbs"); sentry_value_t scope_breadcrumbs - = data_breadcrumbs_to_list(scope->data); + = sentry__scope_breadcrumbs_to_list(scope); sentry_value_set_by_key(event, "breadcrumbs", sentry__value_merge_breadcrumbs(event_breadcrumbs, scope_breadcrumbs, options->max_breadcrumbs)); @@ -1946,7 +1162,12 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, void sentry_scope_add_breadcrumb(sentry_scope_t *scope, sentry_value_t breadcrumb) { - if (data_add_breadcrumb(scope->data, breadcrumb)) { + bool added = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + added = sentry__ringbuffer_append(scope->data->breadcrumbs, breadcrumb) + == 0; + } + if (added) { SENTRY_SCOPE_NOTIFY(scope, add_breadcrumb, breadcrumb); } } @@ -1954,32 +1175,52 @@ sentry_scope_add_breadcrumb(sentry_scope_t *scope, sentry_value_t breadcrumb) sentry_value_t sentry__scope_breadcrumbs_to_list(const sentry_scope_t *scope) { - return data_breadcrumbs_to_list(scope->data); + sentry_value_t breadcrumbs = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + breadcrumbs = sentry__ringbuffer_to_list(scope->data->breadcrumbs); + } + return breadcrumbs; } sentry_value_t sentry__scope_ref_user(const sentry_scope_t *scope) { - return data_ref_user(scope->data); + sentry_value_t user = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + user = sentry_value_incref(scope->data->user); + } + return user; } void sentry_scope_set_user(sentry_scope_t *scope, sentry_value_t user) { - data_set_user(scope->data, user); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry__value_replace(&scope->data->user, sentry_value_incref(user)); + } SENTRY_SCOPE_NOTIFY_OWNED(scope, set_user, user); } sentry_value_t sentry__scope_ref_tags(const sentry_scope_t *scope) { - return data_ref_tags(scope->data); + sentry_value_t tags = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + tags = sentry_value_incref(scope->data->tags); + } + return tags; } void sentry_scope_set_tag(sentry_scope_t *scope, const char *key, const char *value) { - if (data_set_tag(scope->data, key, sentry_value_new_string(value))) { + sentry_value_t tag_value = sentry_value_new_string(value); + bool did_set = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + did_set + = sentry_value_set_by_key(scope->data->tags, key, tag_value) == 0; + } + if (did_set) { SENTRY_SCOPE_NOTIFY(scope, set_tag, key, value); } } @@ -1995,8 +1236,14 @@ sentry_scope_set_tag_n(sentry_scope_t *scope, const char *key, size_t key_len, return; } - if (data_set_tag_n( - scope->data, key, key_len, sentry_value_incref(tag_value))) { + sentry_value_t stored_value = sentry_value_incref(tag_value); + bool did_set = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + did_set = sentry_value_set_by_key_n( + scope->data->tags, key, key_len, stored_value) + == 0; + } + if (did_set) { SENTRY_SCOPE_NOTIFY( scope, set_tag, notify_key, sentry_value_as_string(tag_value)); } @@ -2025,7 +1272,11 @@ sentry_scope_set_tags(sentry_scope_t *scope, sentry_value_t tags) void sentry__scope_remove_tag(sentry_scope_t *scope, const char *key) { - if (data_remove_tag(scope->data, key)) { + bool removed = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + removed = sentry_value_remove_by_key(scope->data->tags, key) == 0; + } + if (removed) { SENTRY_SCOPE_NOTIFY(scope, remove_tag, key); } } @@ -2034,24 +1285,38 @@ void sentry__scope_remove_tag_n( sentry_scope_t *scope, const char *key, size_t key_len) { - char *k = data_remove_tag_n(scope->data, key, key_len); - if (k) { - SENTRY_SCOPE_NOTIFY(scope, remove_tag, k); + char *removed_key = NULL; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + removed_key = sentry__value_remove_and_take_key_n( + scope->data->tags, key, key_len); } - sentry_free(k); + if (removed_key) { + SENTRY_SCOPE_NOTIFY(scope, remove_tag, removed_key); + } + sentry_free(removed_key); } sentry_value_t sentry__scope_ref_extra(const sentry_scope_t *scope) { - return data_ref_extra(scope->data); + sentry_value_t extra = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + extra = sentry_value_incref(scope->data->extra); + } + return extra; } void sentry_scope_set_extra( sentry_scope_t *scope, const char *key, sentry_value_t value) { - if (data_set_extra(scope->data, key, sentry_value_incref(value))) { + sentry_value_t stored_value = sentry_value_incref(value); + bool did_set = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + did_set = sentry_value_set_by_key(scope->data->extra, key, stored_value) + == 0; + } + if (did_set) { SENTRY_SCOPE_NOTIFY(scope, set_extra, key, value); } sentry_value_decref(value); @@ -2067,8 +1332,14 @@ sentry_scope_set_extra_n(sentry_scope_t *scope, const char *key, size_t key_len, return; } - if (data_set_extra_n( - scope->data, key, key_len, sentry_value_incref(value))) { + sentry_value_t stored_value = sentry_value_incref(value); + bool did_set = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + did_set = sentry_value_set_by_key_n( + scope->data->extra, key, key_len, stored_value) + == 0; + } + if (did_set) { SENTRY_SCOPE_NOTIFY(scope, set_extra, notify_key, value); } sentry_free(notify_key); @@ -2078,7 +1349,11 @@ sentry_scope_set_extra_n(sentry_scope_t *scope, const char *key, size_t key_len, void sentry__scope_remove_extra(sentry_scope_t *scope, const char *key) { - if (data_remove_extra(scope->data, key)) { + bool removed = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + removed = sentry_value_remove_by_key(scope->data->extra, key) == 0; + } + if (removed) { SENTRY_SCOPE_NOTIFY(scope, remove_extra, key); } } @@ -2087,11 +1362,15 @@ void sentry__scope_remove_extra_n( sentry_scope_t *scope, const char *key, size_t key_len) { - char *k = data_remove_extra_n(scope->data, key, key_len); - if (k) { - SENTRY_SCOPE_NOTIFY(scope, remove_extra, k); + char *removed_key = NULL; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + removed_key = sentry__value_remove_and_take_key_n( + scope->data->extra, key, key_len); + } + if (removed_key) { + SENTRY_SCOPE_NOTIFY(scope, remove_extra, removed_key); } - sentry_free(k); + sentry_free(removed_key); } void @@ -2112,39 +1391,61 @@ sentry_scope_set_attribute_n(sentry_scope_t *scope, const char *key, sentry_value_decref(attribute); return; } - data_set_attribute_n(scope->data, key, key_len, attribute); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry_value_set_by_key_n( + scope->data->attributes, key, key_len, attribute); + } } sentry_value_t sentry__scope_ref_attributes(const sentry_scope_t *scope) { - return data_ref_attributes(scope->data); + sentry_value_t attributes = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + attributes = sentry_value_incref(scope->data->attributes); + } + return attributes; } void sentry_scope_remove_attribute(sentry_scope_t *scope, const char *key) { - data_remove_attribute(scope->data, key); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry_value_remove_by_key(scope->data->attributes, key); + } } void sentry_scope_remove_attribute_n( sentry_scope_t *scope, const char *key, size_t key_len) { - data_remove_attribute_n(scope->data, key, key_len); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry_value_remove_by_key_n(scope->data->attributes, key, key_len); + } } sentry_value_t sentry__scope_ref_contexts(const sentry_scope_t *scope) { - return data_ref_contexts(scope->data); + sentry_value_t contexts = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + contexts = sentry_value_incref(scope->data->contexts); + } + return contexts; } void sentry_scope_set_context( sentry_scope_t *scope, const char *key, sentry_value_t value) { - if (data_set_context(scope->data, key, sentry_value_incref(value))) { + sentry_value_t stored_value = sentry_value_incref(value); + bool did_set = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + did_set + = sentry_value_set_by_key(scope->data->contexts, key, stored_value) + == 0; + } + if (did_set) { SENTRY_SCOPE_NOTIFY(scope, set_context, key, value); } sentry_value_decref(value); @@ -2160,8 +1461,14 @@ sentry_scope_set_context_n(sentry_scope_t *scope, const char *key, return; } - if (data_set_context_n( - scope->data, key, key_len, sentry_value_incref(value))) { + sentry_value_t stored_value = sentry_value_incref(value); + bool did_set = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + did_set = sentry_value_set_by_key_n( + scope->data->contexts, key, key_len, stored_value) + == 0; + } + if (did_set) { SENTRY_SCOPE_NOTIFY(scope, set_context, notify_key, value); } sentry_free(notify_key); @@ -2186,8 +1493,19 @@ sentry_scope_update_context_n(sentry_scope_t *scope, const char *key, return; } - if (data_update_context_n( - scope->data, key, key_len, sentry_value_incref(value))) { + sentry_value_t stored_value = sentry_value_incref(value); + bool did_set = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry_value_t context + = sentry_value_get_by_key_n(scope->data->contexts, key, key_len); + if (!sentry_value_is_null(context)) { + sentry__value_merge_objects(stored_value, context); + } + did_set = sentry_value_set_by_key_n( + scope->data->contexts, key, key_len, stored_value) + == 0; + } + if (did_set) { SENTRY_SCOPE_NOTIFY(scope, set_context, notify_key, value); } sentry_free(notify_key); @@ -2197,7 +1515,11 @@ sentry_scope_update_context_n(sentry_scope_t *scope, const char *key, void sentry__scope_remove_context(sentry_scope_t *scope, const char *key) { - if (data_remove_context(scope->data, key)) { + bool removed = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + removed = sentry_value_remove_by_key(scope->data->contexts, key) == 0; + } + if (removed) { SENTRY_SCOPE_NOTIFY(scope, remove_context, key); } } @@ -2206,17 +1528,25 @@ void sentry__scope_remove_context_n( sentry_scope_t *scope, const char *key, size_t key_len) { - char *k = data_remove_context_n(scope->data, key, key_len); - if (k) { - SENTRY_SCOPE_NOTIFY(scope, remove_context, k); + char *removed_key = NULL; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + removed_key = sentry__value_remove_and_take_key_n( + scope->data->contexts, key, key_len); } - sentry_free(k); + if (removed_key) { + SENTRY_SCOPE_NOTIFY(scope, remove_context, removed_key); + } + sentry_free(removed_key); } sentry_value_t sentry__scope_ref_fingerprint(const sentry_scope_t *scope) { - return data_ref_fingerprint(scope->data); + sentry_value_t fingerprint = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + fingerprint = sentry_value_incref(scope->data->fingerprint); + } + return fingerprint; } void @@ -2282,7 +1612,10 @@ sentry_scope_set_fingerprints( return; } - data_set_fingerprint(scope->data, fingerprints); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry__value_replace( + &scope->data->fingerprint, sentry_value_incref(fingerprints)); + } SENTRY_SCOPE_NOTIFY_OWNED(scope, set_fingerprint, fingerprints); } @@ -2290,26 +1623,39 @@ void sentry_scope_remove_fingerprint(sentry_scope_t *scope) { sentry_value_t fingerprint = sentry_value_new_null(); - data_set_fingerprint(scope->data, fingerprint); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry__value_replace( + &scope->data->fingerprint, sentry_value_incref(fingerprint)); + } SENTRY_SCOPE_NOTIFY_OWNED(scope, set_fingerprint, fingerprint); } sentry_level_t sentry__scope_get_level(const sentry_scope_t *scope) { - return data_get_level(scope->data); + sentry_level_t level = SENTRY_LEVEL_ERROR; + SENTRY_SCOPE_READ_LOCK (scope->data) { + level = scope->data->level; + } + return level; } sentry_value_t sentry__scope_ref_client_sdk(const sentry_scope_t *scope) { - return data_ref_client_sdk(scope->data); + sentry_value_t client_sdk = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + client_sdk = sentry_value_incref(scope->data->client_sdk); + } + return client_sdk; } void sentry_scope_set_level(sentry_scope_t *scope, sentry_level_t level) { - data_set_level(scope->data, level); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + scope->data->level = level; + } SENTRY_SCOPE_NOTIFY(scope, set_level, level); } @@ -2329,15 +1675,24 @@ sentry_scope_set_span(sentry_scope_t *scope, sentry_span_t *span) sentry_value_t sentry__scope_clone_attachments(const sentry_scope_t *scope) { - return data_clone_attachments(scope->data); + sentry_value_t attachments = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + attachments = sentry__value_clone(scope->data->attachments); + } + return attachments; } sentry_value_t sentry__scope_add_attachment(sentry_scope_t *scope, sentry_value_t attachment) { bool did_add = false; - sentry_value_t added - = data_add_attachment(scope->data, attachment, &did_add); + sentry_value_t added = sentry_value_new_null(); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + size_t len = sentry_value_get_length(scope->data->attachments); + added = sentry__attachments_add(&scope->data->attachments, attachment); + did_add = !sentry_value_is_null(added) + && sentry_value_get_length(scope->data->attachments) > len; + } if (did_add) { SENTRY_SCOPE_NOTIFY(scope, add_attachment, added); } @@ -2347,14 +1702,24 @@ sentry__scope_add_attachment(sentry_scope_t *scope, sentry_value_t attachment) sentry_value_t sentry__scope_take_attachments(sentry_scope_t *scope) { - return data_take_attachments(scope->data); + sentry_value_t attachments = sentry_value_new_null(); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + attachments = scope->data->attachments; + scope->data->attachments = sentry__attachments_new(); + } + return attachments; } sentry_value_t sentry__scope_remove_attachment( sentry_scope_t *scope, const sentry_uuid_t *attachment_id) { - return data_remove_attachment(scope->data, attachment_id); + sentry_value_t removed = sentry_value_new_null(); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + removed = sentry__attachments_remove( + scope->data->attachments, attachment_id); + } + return removed; } sentry_uuid_t @@ -2374,71 +1739,169 @@ sentry_scope_add_attachment(sentry_scope_t *scope, sentry_value_t attachment) sentry_transaction_t * sentry__scope_ref_transaction_object(const sentry_scope_t *scope) { - return data_ref_transaction_object(scope->data); + sentry_transaction_t *transaction = NULL; + SENTRY_SCOPE_READ_LOCK (scope->data) { + transaction = scope->data->transaction_object; + sentry__transaction_incref(transaction); + } + return transaction; } void sentry__scope_set_transaction_object( sentry_scope_t *scope, sentry_transaction_t *transaction) { - data_set_transaction_object(scope->data, transaction); + sentry__transaction_incref(transaction); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry__span_decref(scope->data->span); + scope->data->span = NULL; + sentry__transaction_decref(scope->data->transaction_object); + scope->data->transaction_object = transaction; + } } bool sentry__scope_remove_transaction_object( sentry_scope_t *scope, sentry_transaction_t *transaction) { - return data_remove_transaction_object(scope->data, transaction); + bool removed = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + if (transaction && scope->data->transaction_object == transaction) { + scope->data->transaction_object = NULL; + removed = true; + } + } + if (removed) { + sentry__transaction_decref(transaction); + } + return removed; } bool sentry__scope_remove_transaction_value( sentry_scope_t *scope, sentry_value_t transaction) { - return data_remove_transaction_value(scope->data, transaction); + const char *span_id = sentry_value_as_string( + sentry_value_get_by_key(transaction, "span_id")); + sentry_transaction_t *transaction_object = NULL; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + if (scope->data->transaction_object + && value_has_span_id( + scope->data->transaction_object->inner, span_id)) { + transaction_object = scope->data->transaction_object; + scope->data->transaction_object = NULL; + } + } + sentry__transaction_decref(transaction_object); + return transaction_object != NULL; } bool sentry__scope_restore_transaction_object( sentry_scope_t *scope, sentry_transaction_t *transaction) { - return data_restore_transaction_object(scope->data, transaction); + bool restored = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + if (!scope->data->transaction_object && !scope->data->span + && transaction) { + scope->data->transaction_object = transaction; + restored = true; + } + } + return restored; } sentry_span_t * sentry__scope_ref_span(const sentry_scope_t *scope) { - return data_ref_span(scope->data); + sentry_span_t *span = NULL; + SENTRY_SCOPE_READ_LOCK (scope->data) { + span = scope->data->span; + sentry__span_incref(span); + } + return span; +} + +sentry_value_t +sentry__scope_ref_span_or_transaction(const sentry_scope_t *scope) +{ + sentry_value_t value = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + if (scope->data->span) { + value = sentry_value_incref(scope->data->span->inner); + } else if (scope->data->transaction_object) { + value = sentry_value_incref(scope->data->transaction_object->inner); + } + } + return value; } void sentry__scope_set_span(sentry_scope_t *scope, sentry_span_t *span) { - data_set_span(scope->data, span); + sentry__span_incref(span); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry__transaction_decref(scope->data->transaction_object); + scope->data->transaction_object = NULL; + sentry__span_decref(scope->data->span); + scope->data->span = span; + } } bool sentry__scope_remove_span(sentry_scope_t *scope, sentry_span_t *span) { - return data_remove_span(scope->data, span); + bool removed = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + if (span && scope->data->span == span) { + scope->data->span = NULL; + removed = true; + } + } + if (removed) { + sentry__span_decref(span); + } + return removed; } bool sentry__scope_remove_span_value(sentry_scope_t *scope, sentry_value_t span) { - return data_remove_span_value(scope->data, span); + const char *span_id + = sentry_value_as_string(sentry_value_get_by_key(span, "span_id")); + sentry_span_t *scope_span = NULL; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + if (scope->data->span + && value_has_span_id(scope->data->span->inner, span_id)) { + scope_span = scope->data->span; + scope->data->span = NULL; + } + } + sentry__span_decref(scope_span); + return scope_span != NULL; } bool sentry__scope_restore_span(sentry_scope_t *scope, sentry_span_t *span) { - return data_restore_span(scope->data, span); + bool restored = false; + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + if (!scope->data->span && !scope->data->transaction_object && span) { + scope->data->span = span; + restored = true; + } + } + return restored; } sentry_value_t sentry__scope_ref_release(const sentry_scope_t *scope) { - return data_ref_release(scope->data); + sentry_value_t release = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + release = sentry_value_incref(scope->data->release); + } + return release; } void @@ -2446,14 +1909,23 @@ sentry__scope_set_release_n( sentry_scope_t *scope, const char *release, size_t release_len) { sentry_value_t value = sentry_value_new_string_n(release, release_len); - data_set_release(scope->data, value); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry__value_replace( + &scope->data->release, sentry_value_incref(value)); + sentry_value_set_by_key(scope->data->dynamic_sampling_context, + "release", sentry_value_incref(value)); + } SENTRY_SCOPE_NOTIFY_OWNED(scope, set_release, value); } sentry_value_t sentry__scope_ref_environment(const sentry_scope_t *scope) { - return data_ref_environment(scope->data); + sentry_value_t environment = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + environment = sentry_value_incref(scope->data->environment); + } + return environment; } void @@ -2462,14 +1934,23 @@ sentry__scope_set_environment_n( { sentry_value_t value = sentry_value_new_string_n(environment, environment_len); - data_set_environment(scope->data, value); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry__value_replace( + &scope->data->environment, sentry_value_incref(value)); + sentry_value_set_by_key(scope->data->dynamic_sampling_context, + "environment", sentry_value_incref(value)); + } SENTRY_SCOPE_NOTIFY_OWNED(scope, set_environment, value); } sentry_value_t sentry__scope_ref_transaction(const sentry_scope_t *scope) { - return data_ref_transaction(scope->data); + sentry_value_t transaction = sentry_value_new_null(); + SENTRY_SCOPE_READ_LOCK (scope->data) { + transaction = sentry_value_incref(scope->data->transaction); + } + return transaction; } void @@ -2478,7 +1959,14 @@ sentry__scope_set_transaction_n( { sentry_value_t value = sentry_value_new_string_n(transaction, transaction_len); - data_set_transaction(scope->data, value, transaction, transaction_len); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + sentry__value_replace( + &scope->data->transaction, sentry_value_incref(value)); + if (scope->data->transaction_object) { + sentry_transaction_set_name_n( + scope->data->transaction_object, transaction, transaction_len); + } + } SENTRY_SCOPE_NOTIFY_OWNED(scope, set_transaction, value); } @@ -2551,7 +2039,73 @@ void sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, sentry_value_t telemetry, sentry_value_t attributes) { - data_apply_to_telemetry(scope->data, telemetry, attributes); + const sentry_scope_data_t *data = scope->data; + sentry_value_t os_name = sentry_value_new_null(); + sentry_value_t os_version = sentry_value_new_null(); + + SENTRY_SCOPE_READ_LOCK (data) { + sentry__value_merge_objects_shallow(attributes, data->attributes); + + sentry_value_t trace_id = sentry_value_get_by_key( + sentry_value_get_by_key(data->propagation_context, "trace"), + "trace_id"); + + sentry_value_t parent_span_id = sentry_value_new_object(); + bool has_parent_span = false; + if (data->transaction_object) { + sentry_value_t span_id = sentry_value_get_by_key( + data->transaction_object->inner, "span_id"); + sentry_value_set_by_key( + parent_span_id, "value", sentry_value_incref(span_id)); + trace_id = sentry_value_get_by_key( + data->transaction_object->inner, "trace_id"); + has_parent_span = true; + } else if (data->span) { + sentry_value_t span_id + = sentry_value_get_by_key(data->span->inner, "span_id"); + sentry_value_set_by_key( + parent_span_id, "value", sentry_value_incref(span_id)); + trace_id = sentry_value_get_by_key(data->span->inner, "trace_id"); + has_parent_span = true; + } + sentry_value_set_by_key( + parent_span_id, "type", sentry_value_new_string("string")); + if (has_parent_span + && sentry_value_is_null(sentry_value_get_by_key( + attributes, "sentry.trace.parent_span_id"))) { + sentry_value_set_by_key( + attributes, "sentry.trace.parent_span_id", parent_span_id); + } else { + sentry_value_decref(parent_span_id); + } + if (!sentry_value_is_null(trace_id) + && sentry_value_is_null( + sentry_value_get_by_key(telemetry, "trace_id"))) { + sentry_value_set_by_key( + telemetry, "trace_id", sentry_value_incref(trace_id)); + } + + sentry_value_t os_context + = sentry_value_get_by_key(data->contexts, "os"); + if (!sentry_value_is_null(os_context)) { + os_name = sentry_value_incref( + sentry_value_get_by_key(os_context, "name")); + os_version = sentry_value_incref( + sentry_value_get_by_key(os_context, "version")); + } + } + + if (!sentry_value_is_null(os_name)) { + sentry__value_add_attribute(attributes, os_name, "string", "os.name"); + } else { + sentry_value_decref(os_name); + } + if (!sentry_value_is_null(os_version)) { + sentry__value_add_attribute( + attributes, os_version, "string", "os.version"); + } else { + sentry_value_decref(os_version); + } sentry_value_t user = sentry__scope_ref_user(scope); if (!sentry_value_is_null(user)) { @@ -2597,14 +2151,22 @@ sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, sentry_uuid_t sentry_scope_get_last_event_id(const sentry_scope_t *scope) { - return scope ? data_get_last_event_id(scope->data) : sentry_uuid_nil(); + sentry_uuid_t event_id = sentry_uuid_nil(); + if (scope) { + SENTRY_SCOPE_READ_LOCK (scope->data) { + event_id = scope->data->last_event_id; + } + } + return event_id; } void sentry__scope_set_last_event_id(sentry_scope_t *scope, sentry_uuid_t event_id) { if (scope) { - data_set_last_event_id(scope->data, event_id); + SENTRY_SCOPE_WRITE_LOCK (scope->data) { + scope->data->last_event_id = event_id; + } } } diff --git a/src/sentry_scope.h b/src/sentry_scope.h index 543a6899d2..fdf0605cd4 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -56,6 +56,7 @@ typedef struct sentry_scope_data_s sentry_scope_data_t; * This represents the current scope. */ struct sentry_scope_s { + long refcount; sentry_scope_data_t *data; sentry_mutex_t observers_lock; @@ -86,19 +87,21 @@ typedef enum { } sentry_scope_mode_t; /** - * This will acquire a lock on the global scope. + * This will return a new reference to the global scope, initializing it if + * needed. */ -sentry_scope_t *sentry__scope_lock(void); +sentry_scope_t *sentry__scope_getref(void); /** - * This will return the global scope, initializing it if needed. + * Increment the refcount and return the scope pointer. */ -sentry_scope_t *sentry__scope_begin(void); +sentry_scope_t *sentry__scope_incref(sentry_scope_t *scope); /** - * Release the lock on the global scope. + * Decrement the refcount and free the scope when the last reference is + * released. */ -void sentry__scope_unlock(void); +void sentry__scope_decref(sentry_scope_t *scope); /** * This will free all the data attached to the global scope @@ -117,17 +120,10 @@ void sentry__scope_set_one_shot(sentry_scope_t *scope, bool one_shot); void sentry__scope_free_one_shot(sentry_scope_t *scope); /** - * This will notify any backend of scope changes. - * This function must be called while holding the scope lock, and it will be - * unlocked internally. + * Finish a global scope access, optionally notifying the backend of changes. + * This does not release the caller's scope reference. */ -void sentry__scope_flush_unlock(void); - -/** - * This will finish a global scope access, optionally notifying any backend of - * scope changes. - */ -void sentry__scope_end(bool flush); +void sentry__scope_finish(sentry_scope_t *scope, bool flush); /** * This will merge the requested data which is in the given `scope` to the given @@ -213,6 +209,8 @@ bool sentry__scope_restore_transaction_object( sentry_scope_t *scope, sentry_transaction_t *transaction); sentry_span_t *sentry__scope_ref_span(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_span_or_transaction( + const sentry_scope_t *scope); void sentry__scope_set_span(sentry_scope_t *scope, sentry_span_t *span); bool sentry__scope_remove_span(sentry_scope_t *scope, sentry_span_t *span); bool sentry__scope_remove_span_value( @@ -226,14 +224,18 @@ void sentry__scope_set_trace_managed(sentry_scope_t *scope, bool managed); * These are convenience macros to access the global scope inside a code block. */ #define SENTRY_WITH_SCOPE(Scope) \ - for (const sentry_scope_t *Scope = sentry__scope_begin(); Scope; \ - sentry__scope_end(false), Scope = NULL) + for (const sentry_scope_t *Scope = sentry__scope_getref(); Scope; \ + sentry__scope_finish((sentry_scope_t *)Scope, false), \ + sentry__scope_decref((sentry_scope_t *)Scope), \ + Scope = NULL) #define SENTRY_WITH_SCOPE_MUT(Scope) \ - for (sentry_scope_t *Scope = sentry__scope_begin(); Scope; \ - sentry__scope_end(true), Scope = NULL) + for (sentry_scope_t *Scope = sentry__scope_getref(); Scope; \ + sentry__scope_finish(Scope, true), sentry__scope_decref(Scope), \ + Scope = NULL) #define SENTRY_WITH_SCOPE_MUT_NO_FLUSH(Scope) \ - for (sentry_scope_t *Scope = sentry__scope_begin(); Scope; \ - sentry__scope_end(false), Scope = NULL) + for (sentry_scope_t *Scope = sentry__scope_getref(); Scope; \ + sentry__scope_finish(Scope, false), sentry__scope_decref(Scope), \ + Scope = NULL) /** * Allocate and zero-initialize a scope observer. @@ -322,6 +324,5 @@ void sentry__scope_capture_envelope(sentry_scope_t *scope, // this is only used in unit tests #ifdef SENTRY_UNITTEST -sentry_value_t sentry__scope_ref_span_or_transaction(void); bool sentry__scope_has_observers(const sentry_scope_t *scope); #endif diff --git a/tests/unit/test_concurrency.c b/tests/unit/test_concurrency.c index 3e1de13d24..cc10a7c5d7 100644 --- a/tests/unit/test_concurrency.c +++ b/tests/unit/test_concurrency.c @@ -257,7 +257,7 @@ SENTRY_THREAD_FN scope_access_thread(void *data) { scope_cleanup_state_t *state = data; - sentry_scope_t *scope = sentry__scope_begin(); + sentry_scope_t *scope = sentry__scope_getref(); sentry__mutex_lock(&state->lock); state->access_started = true; @@ -268,7 +268,8 @@ scope_access_thread(void *data) sentry__mutex_unlock(&state->lock); (void)sentry__scope_get_level(scope); - sentry__scope_end(false); + sentry__scope_finish(scope, false); + sentry__scope_decref(scope); return 0; } diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index b7269611ce..acd69ca43d 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -2527,7 +2527,14 @@ SENTRY_TEST(scope_ownership) sentry_scope_t *user_scope = sentry_scope_new(); TEST_CHECK(!sentry__scope_is_one_shot(user_scope)); + sentry_scope_t *retained_scope = sentry__scope_incref(user_scope); sentry_scope_free(user_scope); + sentry_scope_set_tag(retained_scope, "retained", "true"); + sentry_value_t retained_tag = scope_value_get_by_key( + sentry__scope_ref_tags, retained_scope, "retained"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(retained_tag), "true"); + sentry_value_decref(retained_tag); + sentry__scope_decref(retained_scope); } static size_t diff --git a/tests/unit/test_tracing.c b/tests/unit/test_tracing.c index 02e2a6197f..9b98070f16 100644 --- a/tests/unit/test_tracing.c +++ b/tests/unit/test_tracing.c @@ -398,6 +398,16 @@ before_transport(sentry_envelope_t *envelope, void *data) sentry_envelope_free(envelope); } +static sentry_value_t +ref_scope_span_or_transaction(void) +{ + sentry_value_t value = sentry_value_new_null(); + SENTRY_WITH_SCOPE (scope) { + value = sentry__scope_ref_span_or_transaction(scope); + } + return value; +} + SENTRY_TEST(multiple_transactions) { uint64_t called_transport = 0; @@ -420,12 +430,12 @@ SENTRY_TEST(multiple_transactions) = sentry_transaction_start(tx_ctx, sentry_value_new_null()); sentry_set_transaction_object(tx); - sentry_value_t scope_tx = sentry__scope_ref_span_or_transaction(); + sentry_value_t scope_tx = ref_scope_span_or_transaction(); CHECK_STRING_PROPERTY(scope_tx, "transaction", "wow!"); sentry_value_decref(scope_tx); sentry_uuid_t event_id = sentry_transaction_finish(tx); - scope_tx = sentry__scope_ref_span_or_transaction(); + scope_tx = ref_scope_span_or_transaction(); TEST_CHECK(sentry_value_is_null(scope_tx)); sentry_value_decref(scope_tx); TEST_CHECK(!sentry_uuid_is_nil(&event_id)); @@ -439,7 +449,7 @@ SENTRY_TEST(multiple_transactions) tx_ctx = sentry_transaction_context_new("wowee!", NULL); tx = sentry_transaction_start(tx_ctx, sentry_value_new_null()); sentry_set_transaction_object(tx); - scope_tx = sentry__scope_ref_span_or_transaction(); + scope_tx = ref_scope_span_or_transaction(); CHECK_STRING_PROPERTY(scope_tx, "transaction", "wowee!"); sentry_value_decref(scope_tx); event_id = sentry_transaction_finish(tx); @@ -523,7 +533,7 @@ SENTRY_TEST(spans_on_scope) // Peek into the transaction's span list and make sure everything is // good - sentry_value_t scope_tx = sentry__scope_ref_span_or_transaction(); + sentry_value_t scope_tx = ref_scope_span_or_transaction(); char *trace_id = sentry__string_clone( sentry_value_as_string(sentry_value_get_by_key(scope_tx, "trace_id"))); char *parent_span_id = sentry__string_clone( @@ -537,7 +547,7 @@ SENTRY_TEST(spans_on_scope) sentry_span_finish(opaque_child); - scope_tx = sentry__scope_ref_span_or_transaction(); + scope_tx = ref_scope_span_or_transaction(); TEST_CHECK(!IS_NULL(scope_tx, "spans")); sentry_value_t spans = sentry_value_get_by_key(scope_tx, "spans"); TEST_CHECK_INT_EQUAL(sentry_value_get_length(spans), 1); From fbbfaaeaba80e71edd432bc0d2ce0bd79e75e81a Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 18 Aug 2026 15:27:15 +0200 Subject: [PATCH 24/28] fix concurrent init --- src/sentry_scope.c | 2 ++ src/sentry_scope.h | 12 ++++-------- tests/unit/test_concurrency.c | 1 - 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 4c9d33d7a0..8cc5f7c8c2 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -488,6 +488,8 @@ sentry__scope_finish(sentry_scope_t *scope, bool flush) } sentry__mutex_unlock(&scope->observers_lock); + sentry__scope_decref(scope); + if (flush) { SENTRY_WITH_OPTIONS (options) { if (options->backend && options->backend->flush_scope_func) { diff --git a/src/sentry_scope.h b/src/sentry_scope.h index fdf0605cd4..42881ba833 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -121,7 +121,7 @@ void sentry__scope_free_one_shot(sentry_scope_t *scope); /** * Finish a global scope access, optionally notifying the backend of changes. - * This does not release the caller's scope reference. + * This consumes the caller's scope reference. */ void sentry__scope_finish(sentry_scope_t *scope, bool flush); @@ -225,17 +225,13 @@ void sentry__scope_set_trace_managed(sentry_scope_t *scope, bool managed); */ #define SENTRY_WITH_SCOPE(Scope) \ for (const sentry_scope_t *Scope = sentry__scope_getref(); Scope; \ - sentry__scope_finish((sentry_scope_t *)Scope, false), \ - sentry__scope_decref((sentry_scope_t *)Scope), \ - Scope = NULL) + sentry__scope_finish((sentry_scope_t *)Scope, false), Scope = NULL) #define SENTRY_WITH_SCOPE_MUT(Scope) \ for (sentry_scope_t *Scope = sentry__scope_getref(); Scope; \ - sentry__scope_finish(Scope, true), sentry__scope_decref(Scope), \ - Scope = NULL) + sentry__scope_finish(Scope, true), Scope = NULL) #define SENTRY_WITH_SCOPE_MUT_NO_FLUSH(Scope) \ for (sentry_scope_t *Scope = sentry__scope_getref(); Scope; \ - sentry__scope_finish(Scope, false), sentry__scope_decref(Scope), \ - Scope = NULL) + sentry__scope_finish(Scope, false), Scope = NULL) /** * Allocate and zero-initialize a scope observer. diff --git a/tests/unit/test_concurrency.c b/tests/unit/test_concurrency.c index cc10a7c5d7..e0bdd4133b 100644 --- a/tests/unit/test_concurrency.c +++ b/tests/unit/test_concurrency.c @@ -269,7 +269,6 @@ scope_access_thread(void *data) (void)sentry__scope_get_level(scope); sentry__scope_finish(scope, false); - sentry__scope_decref(scope); return 0; } From 25a7e0ce6a6ab15c2fc5729160c8095c3fecb5a3 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 18 Aug 2026 15:47:43 +0200 Subject: [PATCH 25/28] breadcrumbs: fix use-after-free --- src/sentry_scope.c | 5 ++++- tests/unit/test_scope.c | 21 +++++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 8cc5f7c8c2..542bba9a45 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -1168,9 +1168,12 @@ sentry_scope_add_breadcrumb(sentry_scope_t *scope, sentry_value_t breadcrumb) SENTRY_SCOPE_WRITE_LOCK (scope->data) { added = sentry__ringbuffer_append(scope->data->breadcrumbs, breadcrumb) == 0; + if (added) { + sentry_value_incref(breadcrumb); + } } if (added) { - SENTRY_SCOPE_NOTIFY(scope, add_breadcrumb, breadcrumb); + SENTRY_SCOPE_NOTIFY_OWNED(scope, add_breadcrumb, breadcrumb); } } diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index acd69ca43d..c1c67fea8e 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -1432,6 +1432,7 @@ typedef struct { bool was_called; bool was_cleared; size_t set_tag_count; + sentry_scope_t *scope; } test_observer_data_t; typedef struct { @@ -1579,6 +1580,12 @@ static void observe_add_breadcrumb(void *data, sentry_value_t breadcrumb) { test_observer_data_t *d = (test_observer_data_t *)data; + if (d->scope) { + sentry_scope_t *scope = d->scope; + d->scope = NULL; + sentry_scope_add_breadcrumb( + scope, sentry_value_new_breadcrumb(NULL, "replacement")); + } if (sentry_value_is_null(d->breadcrumbs)) { d->breadcrumbs = sentry_value_new_list(); } @@ -2133,6 +2140,7 @@ SENTRY_TEST(scope_observer_user) SENTRY_TEST(scope_observer_breadcrumbs) { SENTRY_TEST_OPTIONS_NEW(options); + sentry_options_set_max_breadcrumbs(options, 1); sentry_init(options); test_observer_data_t d = { .breadcrumbs = sentry_value_new_null() }; @@ -2141,28 +2149,33 @@ SENTRY_TEST(scope_observer_breadcrumbs) observer->add_breadcrumb = observe_add_breadcrumb; SENTRY_WITH_SCOPE_MUT (scope) { + d.scope = scope; sentry__scope_add_observer(scope, observer); } sentry_add_breadcrumb( sentry_value_new_breadcrumb(NULL, "first breadcrumb")); TEST_CHECK(d.was_called); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(d.breadcrumbs), 1); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(d.breadcrumbs), 2); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key( sentry_value_get_by_index(d.breadcrumbs, 0), "message")), + "replacement"); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key( + sentry_value_get_by_index(d.breadcrumbs, 1), "message")), "first breadcrumb"); sentry_add_breadcrumb( sentry_value_new_breadcrumb("warning", "second breadcrumb")); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(d.breadcrumbs), 2); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(d.breadcrumbs), 3); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key( - sentry_value_get_by_index(d.breadcrumbs, 1), "message")), + sentry_value_get_by_index(d.breadcrumbs, 2), "message")), "second breadcrumb"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key( - sentry_value_get_by_index(d.breadcrumbs, 1), "type")), + sentry_value_get_by_index(d.breadcrumbs, 2), "type")), "warning"); sentry_value_decref(d.breadcrumbs); From f16f103c3ae63cd3ec047aecfd985af5f90d6655 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 19 Aug 2026 11:24:02 +0200 Subject: [PATCH 26/28] fix(attachments): Notify backends through scope observers Register backend attachment observers for the SDK lifetime and remove the attachment callbacks from sentry_backend_t. Backends now react only after an attachment is committed to the scope. This keeps duplicate detection and backend registration in the same serialized scope operation, preventing concurrent additions from registering the same file twice. --- src/backends/sentry_backend_crashpad.cpp | 20 ++++++++------ src/backends/sentry_backend_native.c | 22 ++++++++++----- src/sentry_backend.c | 1 + src/sentry_backend.h | 4 +-- src/sentry_core.c | 34 ++++++++---------------- tests/unit/test_attachments.c | 14 +++++----- 6 files changed, 48 insertions(+), 47 deletions(-) diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 87b20ca756..aba0852152 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -1167,10 +1167,9 @@ make_attachment_path(const sentry_path_t *run_path, sentry_value_t attachment) } static void -crashpad_backend_add_attachment(sentry_backend_t *backend, - sentry_value_t attachment, const sentry_options_t *UNUSED(options)) +crashpad_backend_add_attachment(void *state, sentry_value_t attachment) { - auto *data = static_cast(backend->data); + auto *data = static_cast(state); if (!data || !data->client) { return; } @@ -1199,10 +1198,9 @@ crashpad_backend_add_attachment(sentry_backend_t *backend, } static void -crashpad_backend_remove_attachment( - sentry_backend_t *backend, sentry_value_t attachment) +crashpad_backend_remove_attachment(void *state, sentry_value_t attachment) { - auto *data = static_cast(backend->data); + auto *data = static_cast(state); if (!data || !data->client) { return; } @@ -1258,8 +1256,14 @@ sentry__backend_new(void) backend->prune_database_func = crashpad_backend_prune_database; #if defined(SENTRY_PLATFORM_WINDOWS) || defined(SENTRY_PLATFORM_LINUX) \ || defined(SENTRY_PLATFORM_MACOS) - backend->add_attachment_func = crashpad_backend_add_attachment; - backend->remove_attachment_func = crashpad_backend_remove_attachment; + backend->scope_observer = sentry__scope_observer_new(); + if (backend->scope_observer) { + backend->scope_observer->data = data; + backend->scope_observer->add_attachment + = crashpad_backend_add_attachment; + backend->scope_observer->remove_attachment + = crashpad_backend_remove_attachment; + } #endif backend->data = data; backend->can_capture_after_shutdown = true; diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index b87d05ddbd..91e62d5e2c 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -174,6 +174,7 @@ wer_register_module(uint64_t app_tid) typedef struct { sentry_crash_ipc_t *ipc; pid_t daemon_pid; + sentry_path_t *run_path; sentry_path_t *event_path; sentry_path_t *breadcrumb1_path; sentry_path_t *breadcrumb2_path; @@ -244,6 +245,10 @@ native_backend_startup( return 1; } backend->data = state; + state->run_path = sentry__path_clone(options->run->run_path); + if (backend->scope_observer) { + backend->scope_observer->data = state; + } // Initialize IPC (protected by global synchronization for concurrent // access) @@ -725,6 +730,7 @@ native_backend_free(sentry_backend_t *backend) sentry__path_free(state->breadcrumb1_path); sentry__path_free(state->breadcrumb2_path); sentry__path_free(state->envelope_path); + sentry__path_free(state->run_path); sentry_free(state); } @@ -944,18 +950,19 @@ native_backend_add_breadcrumb(sentry_backend_t *backend, } static void -native_backend_add_attachment(sentry_backend_t *backend, - sentry_value_t attachment, const sentry_options_t *options) +native_backend_add_attachment(void *data, sentry_value_t attachment) { - (void)backend; // Unused + native_backend_state_t *state = (native_backend_state_t *)data; + if (!state) { + return; + } // For buffer attachments, derive a path in the run directory and write to // disk size_t bytes_len = 0; const char *bytes = sentry__attachment_get_bytes(attachment, &bytes_len); if (bytes) { - sentry_path_t *path - = make_attachment_path(options->run->run_path, attachment); + sentry_path_t *path = make_attachment_path(state->run_path, attachment); if (!path) { const char *filename = sentry__attachment_get_filename(attachment); SENTRY_WARNF("failed to create path for native backend attachment " @@ -1150,7 +1157,10 @@ sentry__backend_new(void) backend->except_func = native_backend_except; backend->flush_scope_func = native_backend_flush_scope; backend->add_breadcrumb_func = native_backend_add_breadcrumb; - backend->add_attachment_func = native_backend_add_attachment; + backend->scope_observer = sentry__scope_observer_new(); + if (backend->scope_observer) { + backend->scope_observer->add_attachment = native_backend_add_attachment; + } backend->user_consent_changed_func = native_backend_user_consent_changed; backend->can_capture_after_shutdown = false; diff --git a/src/sentry_backend.c b/src/sentry_backend.c index f16d9ec12a..321da7a5ff 100644 --- a/src/sentry_backend.c +++ b/src/sentry_backend.c @@ -9,5 +9,6 @@ sentry__backend_free(sentry_backend_t *backend) if (backend->free_func) { backend->free_func(backend); } + sentry_free(backend->scope_observer); sentry_free(backend); } diff --git a/src/sentry_backend.h b/src/sentry_backend.h index c1f46a57f3..dffcc98de9 100644 --- a/src/sentry_backend.h +++ b/src/sentry_backend.h @@ -25,9 +25,7 @@ struct sentry_backend_s { void (*user_consent_changed_func)(sentry_backend_t *); uint64_t (*get_last_crash_func)(sentry_backend_t *); void (*prune_database_func)(sentry_backend_t *); - void (*add_attachment_func)( - sentry_backend_t *, sentry_value_t, const sentry_options_t *options); - void (*remove_attachment_func)(sentry_backend_t *, sentry_value_t); + sentry_scope_observer_t *scope_observer; void *data; // Whether this backend still runs after shutdown_func was called. bool can_capture_after_shutdown; diff --git a/src/sentry_core.c b/src/sentry_core.c index 41a40e9c76..ad73770a2f 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -229,6 +229,10 @@ sentry_init(sentry_options_t *options) // it at runtime via the options interface. SENTRY_WITH_SCOPE_MUT (scope) { sentry__scope_apply_options(scope, options); + if (backend && backend->scope_observer + && !sentry__scope_add_observer(scope, backend->scope_observer)) { + backend->scope_observer = NULL; + } register_integrations(scope, options); } if (backend && backend->user_consent_changed_func) { @@ -291,8 +295,13 @@ sentry_close(void) // flushed. This prevents a potential deadlock on the options during // envelope creation. SENTRY_WITH_OPTIONS (options) { - if (options->num_integrations) { + if (options->backend || options->num_integrations) { SENTRY_WITH_SCOPE_MUT_NO_FLUSH (scope) { + if (options->backend && options->backend->scope_observer) { + sentry__scope_remove_observer( + scope, options->backend->scope_observer); + options->backend->scope_observer = NULL; + } unregister_integrations(scope, options); } } @@ -2010,18 +2019,7 @@ sentry_add_attachment(sentry_value_t attachment) sentry_value_t added = sentry_value_new_null(); SENTRY_WITH_SCOPE_MUT (scope) { - sentry_value_t attachments = sentry__scope_clone_attachments(scope); - added = sentry__attachments_find(attachments, attachment); - sentry_value_decref(attachments); - if (sentry_value_is_null(added)) { - if (options->backend && options->backend->add_attachment_func) { - options->backend->add_attachment_func( - options->backend, attachment, options); - } - added = sentry__scope_add_attachment(scope, attachment); - } else { - sentry_value_decref(attachment); - } + added = sentry__scope_add_attachment(scope, attachment); } sentry_options_free((sentry_options_t *)options); sentry_uuid_t uuid = sentry__attachment_get_id(added); @@ -2066,11 +2064,6 @@ sentry_clear_attachments(void) for (size_t i = 0; i < len; i++) { sentry_value_t attachment = sentry_value_get_by_index(attachments, i); - if (options->backend - && options->backend->remove_attachment_func) { - options->backend->remove_attachment_func( - options->backend, attachment); - } SENTRY_SCOPE_NOTIFY(scope, remove_attachment, attachment); } sentry_value_decref(attachments); @@ -2090,11 +2083,6 @@ sentry_remove_attachment(sentry_uuid_t attachment_id) sentry_value_t removed = sentry__scope_remove_attachment(scope, &attachment_id); if (!sentry_value_is_null(removed)) { - if (options->backend - && options->backend->remove_attachment_func) { - options->backend->remove_attachment_func( - options->backend, removed); - } SENTRY_SCOPE_NOTIFY(scope, remove_attachment, removed); } sentry_value_decref(removed); diff --git a/tests/unit/test_attachments.c b/tests/unit/test_attachments.c index 9c3fd86ce6..ba753b260c 100644 --- a/tests/unit/test_attachments.c +++ b/tests/unit/test_attachments.c @@ -24,13 +24,11 @@ add_scope_attachments(sentry_envelope_t *envelope) } static void -count_backend_attachment(sentry_backend_t *backend, sentry_value_t attachment, - const sentry_options_t *options) +count_backend_attachment(void *data, sentry_value_t attachment) { - size_t *count = (size_t *)backend->data; + size_t *count = (size_t *)data; (*count)++; - TEST_CHECK(!sentry_value_is_frozen(attachment)); - TEST_CHECK(!!options); + TEST_CHECK(sentry_value_is_frozen(attachment)); } SENTRY_TEST(attachment_placeholder) @@ -149,8 +147,10 @@ SENTRY_TEST(attachments_add_dedupe) size_t backend_add_count = 0; sentry_backend_t *backend = SENTRY_MAKE(sentry_backend_t); TEST_ASSERT(!!backend); - backend->data = &backend_add_count; - backend->add_attachment_func = count_backend_attachment; + backend->scope_observer = sentry__scope_observer_new(); + TEST_ASSERT(!!backend->scope_observer); + backend->scope_observer->data = &backend_add_count; + backend->scope_observer->add_attachment = count_backend_attachment; sentry_options_set_backend(options, backend); sentry_options_add_attachment(options, SENTRY_TEST_PATH_PREFIX ".a.txt"); sentry_options_add_attachment(options, SENTRY_TEST_PATH_PREFIX ".b.txt"); From abcc49daaea23fe6dbabb0e357451c37f07c6ee8 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 19 Aug 2026 12:30:39 +0200 Subject: [PATCH 27/28] fix cleanup_observers --- src/sentry_scope.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 542bba9a45..43d932643c 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -377,7 +377,6 @@ get_scope(void) static void cleanup_observers(sentry_scope_t *scope) { - sentry__mutex_lock(&scope->observers_lock); for (size_t i = 0; i < scope->num_observers; i++) { sentry_free(scope->observers[i]); } @@ -386,7 +385,6 @@ cleanup_observers(sentry_scope_t *scope) scope->num_observers = 0; scope->is_notifying = 0; scope->pending_flush = false; - sentry__mutex_unlock(&scope->observers_lock); } static void From 135c53c93b7cf1b3a1ef7a941264e0a320039cd4 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 19 Aug 2026 15:44:54 +0200 Subject: [PATCH 28/28] load --- src/backends/sentry_backend_native.c | 2 +- src/integrations/sentry_integration_wer.c | 6 +- src/sentry_core.c | 12 +-- src/sentry_envelope.c | 6 +- src/sentry_scope.c | 40 ++++----- src/sentry_scope.h | 19 ++-- src/sentry_tracing.c | 2 +- tests/unit/test_attachments.c | 4 +- tests/unit/test_logs.c | 2 +- tests/unit/test_scope.c | 105 +++++++++++----------- tests/unit/test_tracing.c | 8 +- 11 files changed, 100 insertions(+), 106 deletions(-) diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 91e62d5e2c..1cb0a9fc16 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -769,7 +769,7 @@ native_backend_write_attachments(const sentry_path_t *event_path) return; } SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = sentry__scope_clone_attachments(scope); + sentry_value_t attachments = sentry__scope_load_attachments(scope); if (sentry_value_get_length(attachments) == 0) { sentry_value_decref(attachments); continue; diff --git a/src/integrations/sentry_integration_wer.c b/src/integrations/sentry_integration_wer.c index 73ac8ee92c..9cfb950bbe 100644 --- a/src/integrations/sentry_integration_wer.c +++ b/src/integrations/sentry_integration_wer.c @@ -195,7 +195,7 @@ static void wer_for_each_attachment( sentry_scope_t *scope, void *data, void (*callback)(void *, sentry_value_t)) { - sentry_value_t attachments = sentry__scope_clone_attachments(scope); + sentry_value_t attachments = sentry__scope_load_attachments(scope); size_t len = sentry_value_get_length(attachments); for (size_t i = 0; i < len; i++) { callback(data, sentry_value_get_by_index(attachments, i)); @@ -219,7 +219,7 @@ wer_clear(void *data) return; } - sentry_value_t tags = sentry__scope_ref_tags(scope); + sentry_value_t tags = sentry__scope_load_tags(scope); sentry__value_foreach_key_value(tags, wer_cleanup_tag, wer_data); sentry_value_decref(tags); @@ -262,7 +262,7 @@ unregister_wer( return; } - sentry_value_t tags = sentry__scope_ref_tags(scope); + sentry_value_t tags = sentry__scope_load_tags(scope); sentry__value_foreach_key_value(tags, wer_cleanup_tag, wer_data); sentry_value_decref(tags); diff --git a/src/sentry_core.c b/src/sentry_core.c index ad73770a2f..6a5a5a7180 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -716,11 +716,11 @@ sentry__prepare_event(const sentry_options_t *options, sentry_value_t event, SENTRY_WITH_SCOPE (scope) { sentry_value_t global_attachments - = sentry__scope_clone_attachments(scope); + = sentry__scope_load_attachments(scope); sentry_value_t attachments = global_attachments; if (local_scope) { sentry_value_t local_attachments - = sentry__scope_clone_attachments(local_scope); + = sentry__scope_load_attachments(local_scope); if (sentry_value_get_length(local_attachments) > 0) { // all attachments merged from multiple scopes sentry__attachments_extend(&all_attachments, local_attachments); @@ -862,14 +862,14 @@ prepare_user_feedback(const sentry_options_t *options, } if (local_scope) { sentry_value_t local_attachments - = sentry__scope_clone_attachments(local_scope); + = sentry__scope_load_attachments(local_scope); sentry__attachments_extend(&all_attachments, local_attachments); sentry_value_decref(local_attachments); } SENTRY_WITH_SCOPE (scope) { sentry_value_t global_attachments - = sentry__scope_clone_attachments(scope); + = sentry__scope_load_attachments(scope); sentry_value_t attachments = global_attachments; if (sentry_value_get_length(all_attachments) > 0) { sentry__attachments_extend(&all_attachments, global_attachments); @@ -1372,7 +1372,7 @@ sentry_transaction_start_ts(sentry_transaction_context_t *opaque_tx_ctx, // trace_id, and align the tx's trace_id with it. sentry__scope_regenerate_propagation_context(scope); sentry_value_t trace_context - = sentry__scope_ref_trace_context(scope); + = sentry__scope_load_trace_context(scope); sentry_value_t scope_trace_id = sentry_value_incref( sentry_value_get_by_key(trace_context, "trace_id")); sentry_value_decref(trace_context); @@ -1388,7 +1388,7 @@ sentry_transaction_start_ts(sentry_transaction_context_t *opaque_tx_ctx, double sample_rand = 1.0; SENTRY_WITH_SCOPE (scope) { - sentry_value_t trace_context = sentry__scope_ref_trace_context(scope); + sentry_value_t trace_context = sentry__scope_load_trace_context(scope); sample_rand = sentry_value_as_double( sentry_value_get_by_key(trace_context, "sample_rand")); sentry_value_decref(trace_context); diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index d02d0e50a7..b30958d144 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -415,8 +415,8 @@ sentry__envelope_add_event(sentry_envelope_t *envelope, sentry_value_t event) sentry_value_t dsc = sentry_value_new_null(); double sample_rand = (double)NAN; SENTRY_WITH_SCOPE (scope) { - dsc = sentry__scope_clone_dsc(scope); - sentry_value_t trace_context = sentry__scope_ref_trace_context(scope); + dsc = sentry__scope_load_dsc(scope); + sentry_value_t trace_context = sentry__scope_load_trace_context(scope); sample_rand = sentry_value_as_double( sentry_value_get_by_key(trace_context, "sample_rand")); sentry_value_decref(trace_context); @@ -497,7 +497,7 @@ sentry__envelope_add_transaction( sentry_value_t dsc = sentry_value_new_null(); SENTRY_WITH_SCOPE (scope) { - dsc = sentry__scope_clone_dsc(scope); + dsc = sentry__scope_load_dsc(scope); } if (!sentry_value_is_null(dsc)) { diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 43d932643c..819c3c3cf0 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -747,12 +747,12 @@ sentry_scope_clone(const sentry_scope_t *scope) } sentry_value_t -sentry__scope_ref_propagation_context(const sentry_scope_t *scope) +sentry__scope_load_propagation_context(const sentry_scope_t *scope) { sentry_value_t propagation_context = sentry_value_new_null(); SENTRY_SCOPE_READ_LOCK (scope->data) { propagation_context - = sentry_value_incref(scope->data->propagation_context); + = sentry__value_clone(scope->data->propagation_context); } return propagation_context; } @@ -775,11 +775,11 @@ sentry__scope_regenerate_propagation_context(sentry_scope_t *scope) } sentry_value_t -sentry__scope_ref_trace_context(const sentry_scope_t *scope) +sentry__scope_load_trace_context(const sentry_scope_t *scope) { sentry_value_t trace_context = sentry_value_new_null(); SENTRY_SCOPE_READ_LOCK (scope->data) { - trace_context = sentry_value_incref( + trace_context = sentry__value_clone( sentry_value_get_by_key(scope->data->propagation_context, "trace")); } return trace_context; @@ -815,17 +815,7 @@ sentry__scope_set_trace_managed(sentry_scope_t *scope, bool managed) } sentry_value_t -sentry__scope_ref_dsc(const sentry_scope_t *scope) -{ - sentry_value_t dsc = sentry_value_new_null(); - SENTRY_SCOPE_READ_LOCK (scope->data) { - dsc = sentry_value_incref(scope->data->dynamic_sampling_context); - } - return dsc; -} - -sentry_value_t -sentry__scope_clone_dsc(const sentry_scope_t *scope) +sentry__scope_load_dsc(const sentry_scope_t *scope) { sentry_value_t dsc = sentry_value_new_null(); SENTRY_SCOPE_READ_LOCK (scope->data) { @@ -1114,7 +1104,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, && sentry_value_is_null( sentry_value_get_by_key(event_contexts, "trace"))) { sentry_value_t propagation_context - = sentry__scope_ref_propagation_context(scope); + = sentry__scope_load_propagation_context(scope); sentry__value_merge_objects(contexts, propagation_context); sentry_value_decref(propagation_context); } @@ -1205,11 +1195,11 @@ sentry_scope_set_user(sentry_scope_t *scope, sentry_value_t user) } sentry_value_t -sentry__scope_ref_tags(const sentry_scope_t *scope) +sentry__scope_load_tags(const sentry_scope_t *scope) { sentry_value_t tags = sentry_value_new_null(); SENTRY_SCOPE_READ_LOCK (scope->data) { - tags = sentry_value_incref(scope->data->tags); + tags = sentry__value_clone(scope->data->tags); } return tags; } @@ -1300,11 +1290,11 @@ sentry__scope_remove_tag_n( } sentry_value_t -sentry__scope_ref_extra(const sentry_scope_t *scope) +sentry__scope_load_extra(const sentry_scope_t *scope) { sentry_value_t extra = sentry_value_new_null(); SENTRY_SCOPE_READ_LOCK (scope->data) { - extra = sentry_value_incref(scope->data->extra); + extra = sentry__value_clone(scope->data->extra); } return extra; } @@ -1401,11 +1391,11 @@ sentry_scope_set_attribute_n(sentry_scope_t *scope, const char *key, } sentry_value_t -sentry__scope_ref_attributes(const sentry_scope_t *scope) +sentry__scope_load_attributes(const sentry_scope_t *scope) { sentry_value_t attributes = sentry_value_new_null(); SENTRY_SCOPE_READ_LOCK (scope->data) { - attributes = sentry_value_incref(scope->data->attributes); + attributes = sentry__value_clone(scope->data->attributes); } return attributes; } @@ -1428,11 +1418,11 @@ sentry_scope_remove_attribute_n( } sentry_value_t -sentry__scope_ref_contexts(const sentry_scope_t *scope) +sentry__scope_load_contexts(const sentry_scope_t *scope) { sentry_value_t contexts = sentry_value_new_null(); SENTRY_SCOPE_READ_LOCK (scope->data) { - contexts = sentry_value_incref(scope->data->contexts); + contexts = sentry__value_clone(scope->data->contexts); } return contexts; } @@ -1676,7 +1666,7 @@ sentry_scope_set_span(sentry_scope_t *scope, sentry_span_t *span) } sentry_value_t -sentry__scope_clone_attachments(const sentry_scope_t *scope) +sentry__scope_load_attachments(const sentry_scope_t *scope) { sentry_value_t attachments = sentry_value_new_null(); SENTRY_SCOPE_READ_LOCK (scope->data) { diff --git a/src/sentry_scope.h b/src/sentry_scope.h index 42881ba833..4f38dcfdbc 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -152,6 +152,7 @@ void sentry__scope_set_fingerprint_va( sentry_scope_t *scope, const char *fingerprint, va_list va); void sentry__scope_set_fingerprint_nva(sentry_scope_t *scope, const char *fingerprint, size_t fingerprint_len, va_list va); + sentry_value_t sentry__scope_ref_user(const sentry_scope_t *scope); sentry_level_t sentry__scope_get_level(const sentry_scope_t *scope); sentry_value_t sentry__scope_ref_client_sdk(const sentry_scope_t *scope); @@ -163,35 +164,36 @@ sentry_value_t sentry__scope_ref_client_sdk(const sentry_scope_t *scope); * attachment elements remain shared because they are frozen. The caller must * release the snapshot with `sentry_value_decref`. */ -sentry_value_t sentry__scope_clone_attachments(const sentry_scope_t *scope); +sentry_value_t sentry__scope_load_attachments(const sentry_scope_t *scope); sentry_value_t sentry__scope_add_attachment( sentry_scope_t *scope, sentry_value_t attachment); sentry_value_t sentry__scope_take_attachments(sentry_scope_t *scope); sentry_value_t sentry__scope_remove_attachment( sentry_scope_t *scope, const sentry_uuid_t *attachment_id); -sentry_value_t sentry__scope_ref_tags(const sentry_scope_t *scope); + +sentry_value_t sentry__scope_load_tags(const sentry_scope_t *scope); void sentry__scope_remove_tag(sentry_scope_t *scope, const char *key); void sentry__scope_remove_tag_n( sentry_scope_t *scope, const char *key, size_t key_len); -sentry_value_t sentry__scope_ref_extra(const sentry_scope_t *scope); +sentry_value_t sentry__scope_load_extra(const sentry_scope_t *scope); void sentry__scope_remove_extra(sentry_scope_t *scope, const char *key); void sentry__scope_remove_extra_n( sentry_scope_t *scope, const char *key, size_t key_len); -sentry_value_t sentry__scope_ref_attributes(const sentry_scope_t *scope); +sentry_value_t sentry__scope_load_attributes(const sentry_scope_t *scope); -sentry_value_t sentry__scope_ref_contexts(const sentry_scope_t *scope); +sentry_value_t sentry__scope_load_contexts(const sentry_scope_t *scope); void sentry__scope_remove_context(sentry_scope_t *scope, const char *key); void sentry__scope_remove_context_n( sentry_scope_t *scope, const char *key, size_t key_len); -sentry_value_t sentry__scope_ref_propagation_context( +sentry_value_t sentry__scope_load_propagation_context( const sentry_scope_t *scope); void sentry__scope_set_propagation_context( sentry_scope_t *scope, const char *key, sentry_value_t value); void sentry__scope_regenerate_propagation_context(sentry_scope_t *scope); -sentry_value_t sentry__scope_ref_trace_context(const sentry_scope_t *scope); +sentry_value_t sentry__scope_load_trace_context(const sentry_scope_t *scope); void sentry__scope_set_trace_context( sentry_scope_t *scope, const char *key, sentry_value_t value); @@ -277,8 +279,7 @@ void sentry__scope_end_notify(sentry_scope_t *scope); sentry__scope_end_notify(scope); \ } while (0) -sentry_value_t sentry__scope_ref_dsc(const sentry_scope_t *scope); -sentry_value_t sentry__scope_clone_dsc(const sentry_scope_t *scope); +sentry_value_t sentry__scope_load_dsc(const sentry_scope_t *scope); void sentry__scope_foreach_dsc(const sentry_scope_t *scope, void (*callback)(const char *key, sentry_value_t value, void *userdata), void *userdata); diff --git a/src/sentry_tracing.c b/src/sentry_tracing.c index e9efc7a5b6..e383b23a32 100644 --- a/src/sentry_tracing.c +++ b/src/sentry_tracing.c @@ -96,7 +96,7 @@ transaction_context_new_n(sentry_slice_t name, sentry_slice_t operation) sentry_value_new_string_n(name.ptr, name.len)); SENTRY_WITH_SCOPE_MUT (scope) { - sentry_value_t trace_context = sentry__scope_ref_trace_context(scope); + sentry_value_t trace_context = sentry__scope_load_trace_context(scope); if (!sentry__scope_is_trace_managed(scope) && !sentry_value_is_null(trace_context)) { // The trace is managed from outside, so we use the propagation diff --git a/tests/unit/test_attachments.c b/tests/unit/test_attachments.c index ba753b260c..b267a477ed 100644 --- a/tests/unit/test_attachments.c +++ b/tests/unit/test_attachments.c @@ -17,7 +17,7 @@ static void add_scope_attachments(sentry_envelope_t *envelope) { SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = sentry__scope_clone_attachments(scope); + sentry_value_t attachments = sentry__scope_load_attachments(scope); sentry__envelope_add_attachments(envelope, attachments, NULL); sentry_value_decref(attachments); } @@ -390,7 +390,7 @@ SENTRY_TEST(attachment_properties) sentry_init(options); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = sentry__scope_clone_attachments(scope); + sentry_value_t attachments = sentry__scope_load_attachments(scope); TEST_CHECK_INT_EQUAL(sentry_value_get_length(attachments), 0); sentry_value_decref(attachments); } diff --git a/tests/unit/test_logs.c b/tests/unit/test_logs.c index 440cd637c4..66a2d4124d 100644 --- a/tests/unit/test_logs.c +++ b/tests/unit/test_logs.c @@ -766,7 +766,7 @@ SENTRY_TEST(scope_capture_log_trace_id) SENTRY_WITH_SCOPE (global_scope) { sentry_value_t trace_context - = sentry__scope_ref_trace_context(global_scope); + = sentry__scope_load_trace_context(global_scope); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( captured_log, "trace_id")), sentry_value_as_string( diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index c1c67fea8e..bfbe08c29e 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -13,13 +13,13 @@ #define TEST_CHECK_UUID_EQUAL(Actual, Expected) \ TEST_CHECK(memcmp(&(Actual), &(Expected), sizeof(sentry_uuid_t)) == 0) -typedef sentry_value_t (*scope_value_ref_t)(const sentry_scope_t *scope); +typedef sentry_value_t (*scope_value_getter_t)(const sentry_scope_t *scope); static sentry_value_t scope_value_get_by_key( - scope_value_ref_t ref, const sentry_scope_t *scope, const char *key) + scope_value_getter_t get, const sentry_scope_t *scope, const char *key) { - sentry_value_t values = ref(scope); + sentry_value_t values = get(scope); sentry_value_t value = sentry_value_incref(sentry_value_get_by_key(values, key)); sentry_value_decref(values); @@ -27,9 +27,9 @@ scope_value_get_by_key( } static size_t -scope_value_get_length(scope_value_ref_t ref, const sentry_scope_t *scope) +scope_value_get_length(scope_value_getter_t get, const sentry_scope_t *scope) { - sentry_value_t value = ref(scope); + sentry_value_t value = get(scope); size_t length = sentry_value_get_length(value); sentry_value_decref(value); return length; @@ -146,7 +146,7 @@ SENTRY_TEST(scope_update_context) SENTRY_WITH_SCOPE (scope) { sentry_value_t ctx = scope_value_get_by_key( - sentry__scope_ref_contexts, scope, "device"); + sentry__scope_load_contexts, scope, "device"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "model")), "Xbox Series X"); @@ -167,7 +167,7 @@ SENTRY_TEST(scope_update_context) SENTRY_WITH_SCOPE (scope) { sentry_value_t ctx = scope_value_get_by_key( - sentry__scope_ref_contexts, scope, "device"); + sentry__scope_load_contexts, scope, "device"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "model")), "PC"); @@ -191,7 +191,7 @@ SENTRY_TEST(scope_update_context) sentry_scope_update_context(local_scope, "os", os); sentry_value_t ctx = scope_value_get_by_key( - sentry__scope_ref_contexts, local_scope, "os"); + sentry__scope_load_contexts, local_scope, "os"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "name")), "SteamOS"); @@ -204,7 +204,7 @@ SENTRY_TEST(scope_update_context) sentry_scope_update_context(local_scope, "os", os2); ctx = scope_value_get_by_key( - sentry__scope_ref_contexts, local_scope, "os"); + sentry__scope_load_contexts, local_scope, "os"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "name")), "Linux"); @@ -1139,7 +1139,7 @@ SENTRY_TEST(scope_clone) // scope values must not be corrupted by before_send modifications SENTRY_WITH_SCOPE (scope) { sentry_value_t scope_gpu - = scope_value_get_by_key(sentry__scope_ref_contexts, scope, "gpu"); + = scope_value_get_by_key(sentry__scope_load_contexts, scope, "gpu"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(scope_gpu, "name")), "original"); @@ -1148,7 +1148,7 @@ SENTRY_TEST(scope_clone) sentry_value_decref(scope_gpu); sentry_value_t scope_data - = scope_value_get_by_key(sentry__scope_ref_extra, scope, "data"); + = scope_value_get_by_key(sentry__scope_load_extra, scope, "data"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(scope_data, "key")), "original"); @@ -1183,7 +1183,7 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute("valid_key", valid_attr); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = sentry__scope_ref_attributes(scope); + sentry_value_t attributes = sentry__scope_load_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "valid_key"); @@ -1206,7 +1206,7 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute("invalid_no_value", invalid_attr_no_value); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = sentry__scope_ref_attributes(scope); + sentry_value_t attributes = sentry__scope_load_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "invalid_no_value"); @@ -1223,7 +1223,7 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute("invalid_no_type", invalid_attr_no_type); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = sentry__scope_ref_attributes(scope); + sentry_value_t attributes = sentry__scope_load_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "invalid_no_type"); @@ -1236,7 +1236,7 @@ SENTRY_TEST(scope_global_attributes) sentry_remove_attribute("valid_key"); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = sentry__scope_ref_attributes(scope); + sentry_value_t attributes = sentry__scope_load_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "valid_key"); @@ -1251,7 +1251,7 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute_n("key_n", 5, attr_n); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = sentry__scope_ref_attributes(scope); + sentry_value_t attributes = sentry__scope_load_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "key_n"); @@ -1287,7 +1287,7 @@ SENTRY_TEST(scope_local_attributes) sentry_value_new_attribute(sentry_value_new_string("global"), NULL)); SENTRY_WITH_SCOPE (global_scope) { - sentry_value_t attributes = sentry__scope_ref_attributes(global_scope); + sentry_value_t attributes = sentry__scope_load_attributes(global_scope); // Verify global attributes are set TEST_CHECK_STRING_EQUAL( @@ -1317,7 +1317,7 @@ SENTRY_TEST(scope_local_attributes) sentry_value_new_attribute(sentry_value_new_string("local"), NULL)); sentry_value_t local_attributes - = sentry__scope_ref_attributes(local_scope); + = sentry__scope_load_attributes(local_scope); // Verify local attributes are set TEST_CHECK_STRING_EQUAL( @@ -1335,7 +1335,7 @@ SENTRY_TEST(scope_local_attributes) // Verify global scope still has its own attributes sentry_value_t global_attributes - = sentry__scope_ref_attributes(global_scope); + = sentry__scope_load_attributes(global_scope); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key( sentry_value_get_by_key(global_attributes, "all"), "value")), @@ -1354,7 +1354,7 @@ SENTRY_TEST(scope_local_attributes) sentry_remove_attribute("all"); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = sentry__scope_ref_attributes(scope); + sentry_value_t attributes = sentry__scope_load_attributes(scope); TEST_CHECK( sentry_value_is_null(sentry_value_get_by_key(attributes, "all"))); // Other attributes should still exist @@ -1372,7 +1372,7 @@ SENTRY_TEST(scope_local_attributes) sentry_value_new_attribute(sentry_value_new_int32(100), "percent")); sentry_value_t local_attributes - = sentry__scope_ref_attributes(local_scope); + = sentry__scope_load_attributes(local_scope); sentry_value_t attr = sentry_value_get_by_key(local_attributes, "test_key"); @@ -1386,8 +1386,11 @@ SENTRY_TEST(scope_local_attributes) sentry_value_as_string(sentry_value_get_by_key(attr, "unit")), "percent"); + sentry_value_decref(local_attributes); + // Remove using _n variant sentry_scope_remove_attribute_n(local_scope, "test_key", 8); + local_attributes = sentry__scope_load_attributes(local_scope); TEST_CHECK(sentry_value_is_null( sentry_value_get_by_key(local_attributes, "test_key"))); sentry_value_decref(local_attributes); @@ -1406,7 +1409,7 @@ SENTRY_TEST(scope_local_attributes) sentry_scope_set_attribute(local_scope, "invalid", invalid_attr); sentry_value_t local_attributes - = sentry__scope_ref_attributes(local_scope); + = sentry__scope_load_attributes(local_scope); TEST_CHECK(sentry_value_is_null( sentry_value_get_by_key(local_attributes, "invalid"))); sentry_value_decref(local_attributes); @@ -1726,7 +1729,7 @@ SENTRY_TEST(scope_observer_clear) sentry_scope_clear(scope); TEST_CHECK(d.was_cleared); TEST_CHECK(sentry__scope_has_observers(scope)); - TEST_CHECK(scope_value_get_length(sentry__scope_ref_tags, scope) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_load_tags, scope) == 0); d.was_called = false; sentry_scope_set_tag(scope, "after", "clear"); @@ -1753,7 +1756,7 @@ SENTRY_TEST(scope_observer_clear) TEST_CHECK(observer_data.was_called); TEST_CHECK(observer_data.was_cleared); TEST_CHECK(sentry__scope_has_observers(scope)); - TEST_CHECK(scope_value_get_length(sentry__scope_ref_tags, scope) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_load_tags, scope) == 0); sentry_value_decref(observer_data.tags); sentry_scope_free(scope); @@ -2507,7 +2510,7 @@ SENTRY_TEST(scope_set_attribute_invalid_decref_value) sentry_value_decref(no_type); TEST_CHECK_INT_EQUAL( - scope_value_get_length(sentry__scope_ref_attributes, scope), 0); + scope_value_get_length(sentry__scope_load_attributes, scope), 0); sentry_scope_free(scope); } @@ -2525,7 +2528,7 @@ SENTRY_TEST(scope_set_attribute_null_key_decref_value) sentry_value_decref(v); TEST_CHECK_INT_EQUAL( - scope_value_get_length(sentry__scope_ref_attributes, scope), 0); + scope_value_get_length(sentry__scope_load_attributes, scope), 0); sentry_scope_free(scope); } @@ -2544,7 +2547,7 @@ SENTRY_TEST(scope_ownership) sentry_scope_free(user_scope); sentry_scope_set_tag(retained_scope, "retained", "true"); sentry_value_t retained_tag = scope_value_get_by_key( - sentry__scope_ref_tags, retained_scope, "retained"); + sentry__scope_load_tags, retained_scope, "retained"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(retained_tag), "true"); sentry_value_decref(retained_tag); sentry__scope_decref(retained_scope); @@ -2576,7 +2579,7 @@ SENTRY_TEST(scope_clone_independence) // The clone carries over the source's data. sentry_value_t clone_tag - = scope_value_get_by_key(sentry__scope_ref_tags, clone, "shared"); + = scope_value_get_by_key(sentry__scope_load_tags, clone, "shared"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(clone_tag), "original"); sentry_value_decref(clone_tag); TEST_CHECK(sentry__scope_get_level(clone) == SENTRY_LEVEL_WARNING); @@ -2589,11 +2592,11 @@ SENTRY_TEST(scope_clone_independence) clone, sentry_value_new_breadcrumb(NULL, "second")); sentry_value_t scope_tag - = scope_value_get_by_key(sentry__scope_ref_tags, scope, "shared"); + = scope_value_get_by_key(sentry__scope_load_tags, scope, "shared"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(scope_tag), "original"); sentry_value_decref(scope_tag); sentry_value_t clone_only - = scope_value_get_by_key(sentry__scope_ref_tags, scope, "clone_only"); + = scope_value_get_by_key(sentry__scope_load_tags, scope, "clone_only"); TEST_CHECK(sentry_value_is_null(clone_only)); sentry_value_decref(clone_only); TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(scope), 1); @@ -2630,11 +2633,11 @@ SENTRY_TEST(scope_clone_preserves_data) sentry_value_decref(sentry__scope_remove_attachment(scope, &attachment_id)); sentry_value_t clone_tag - = scope_value_get_by_key(sentry__scope_ref_tags, clone, "tag_key"); + = scope_value_get_by_key(sentry__scope_load_tags, clone, "tag_key"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(clone_tag), "tag_value"); sentry_value_decref(clone_tag); sentry_value_t clone_context - = scope_value_get_by_key(sentry__scope_ref_contexts, clone, "device"); + = scope_value_get_by_key(sentry__scope_load_contexts, clone, "device"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(clone_context), "Xbox"); sentry_value_decref(clone_context); sentry_value_t clone_user = sentry__scope_ref_user(clone); @@ -2643,7 +2646,7 @@ SENTRY_TEST(scope_clone_preserves_data) "alice"); sentry_value_decref(clone_user); sentry_value_t clone_extra - = scope_value_get_by_key(sentry__scope_ref_extra, clone, "extra_key"); + = scope_value_get_by_key(sentry__scope_load_extra, clone, "extra_key"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(clone_extra), "extra_value"); sentry_value_decref(clone_extra); sentry_value_t clone_fingerprint = sentry__scope_ref_fingerprint(clone); @@ -2651,7 +2654,7 @@ SENTRY_TEST(scope_clone_preserves_data) sentry_value_decref(clone_fingerprint); TEST_CHECK(sentry__scope_get_level(clone) == SENTRY_LEVEL_WARNING); sentry_value_t clone_attribute = scope_value_get_by_key( - sentry__scope_ref_attributes, clone, "attr_key"); + sentry__scope_load_attributes, clone, "attr_key"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( clone_attribute, "value")), "attr_value"); @@ -2659,8 +2662,8 @@ SENTRY_TEST(scope_clone_preserves_data) TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(clone), 1); // Attachments are deep-copied into an independent list. - sentry_value_t clone_attachments = sentry__scope_clone_attachments(clone); - sentry_value_t scope_attachments = sentry__scope_clone_attachments(scope); + sentry_value_t clone_attachments = sentry__scope_load_attachments(clone); + sentry_value_t scope_attachments = sentry__scope_load_attachments(scope); TEST_CHECK_INT_EQUAL(sentry_value_get_length(clone_attachments), 1); TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope_attachments), 0); TEST_CHECK(clone_attachments._bits != scope_attachments._bits); @@ -2686,7 +2689,7 @@ SENTRY_TEST(scope_attachments) TEST_CHECK(!sentry_uuid_is_nil(&first_id)); TEST_CHECK(!sentry_uuid_is_nil(&second_id)); - sentry_value_t snapshot = sentry__scope_clone_attachments(scope); + sentry_value_t snapshot = sentry__scope_load_attachments(scope); TEST_CHECK_INT_EQUAL(sentry_value_get_length(snapshot), 2); TEST_CHECK_STRING_EQUAL( sentry__attachment_get_filename(sentry_value_get_by_index(snapshot, 0)), @@ -2699,7 +2702,7 @@ SENTRY_TEST(scope_attachments) scope, sentry_attachment_from_bytes("third", 5, "third.txt")); TEST_CHECK(!sentry_uuid_is_nil(&third_id)); - sentry_value_t current = sentry__scope_clone_attachments(scope); + sentry_value_t current = sentry__scope_load_attachments(scope); TEST_CHECK_INT_EQUAL(sentry_value_get_length(current), 3); TEST_CHECK_STRING_EQUAL( sentry__attachment_get_filename(sentry_value_get_by_index(current, 2)), @@ -2711,7 +2714,7 @@ SENTRY_TEST(scope_attachments) TEST_CHECK(!sentry_value_is_null(removed)); sentry_value_decref(removed); - current = sentry__scope_clone_attachments(scope); + current = sentry__scope_load_attachments(scope); TEST_CHECK_INT_EQUAL(sentry_value_get_length(current), 2); TEST_CHECK_STRING_EQUAL( sentry__attachment_get_filename(sentry_value_get_by_index(current, 0)), @@ -2726,7 +2729,7 @@ SENTRY_TEST(scope_attachments) "first.txt"); sentry_scope_clear(scope); - current = sentry__scope_clone_attachments(scope); + current = sentry__scope_load_attachments(scope); TEST_CHECK_INT_EQUAL(sentry_value_get_length(current), 0); sentry_value_decref(current); TEST_CHECK_INT_EQUAL(sentry_value_get_length(snapshot), 2); @@ -2855,9 +2858,9 @@ SENTRY_TEST(scope_clear) sentry_scope_add_breadcrumb( scope, sentry_value_new_breadcrumb(NULL, "crumb")); - TEST_CHECK(scope_value_get_length(sentry__scope_ref_tags, scope) == 1); + TEST_CHECK(scope_value_get_length(sentry__scope_load_tags, scope) == 1); TEST_CHECK( - scope_value_get_length(sentry__scope_ref_attributes, scope) == 1); + scope_value_get_length(sentry__scope_load_attributes, scope) == 1); sentry_value_t scope_user = sentry__scope_ref_user(scope); TEST_CHECK(!sentry_value_is_null(scope_user)); sentry_value_decref(scope_user); @@ -2865,11 +2868,11 @@ SENTRY_TEST(scope_clear) sentry_scope_clear(scope); // Everything is reset to the state of a fresh scope. - TEST_CHECK(scope_value_get_length(sentry__scope_ref_tags, scope) == 0); - TEST_CHECK(scope_value_get_length(sentry__scope_ref_extra, scope) == 0); - TEST_CHECK(scope_value_get_length(sentry__scope_ref_contexts, scope) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_load_tags, scope) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_load_extra, scope) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_load_contexts, scope) == 0); TEST_CHECK( - scope_value_get_length(sentry__scope_ref_attributes, scope) == 0); + scope_value_get_length(sentry__scope_load_attributes, scope) == 0); scope_user = sentry__scope_ref_user(scope); TEST_CHECK(sentry_value_is_null(scope_user)); sentry_value_decref(scope_user); @@ -2884,12 +2887,12 @@ SENTRY_TEST(scope_clear) // ... except the trace, which is preserved. sentry_value_t propagation_context - = sentry__scope_ref_propagation_context(scope); + = sentry__scope_load_propagation_context(scope); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( propagation_context, "marker")), "keep"); sentry_value_decref(propagation_context); - sentry_value_t scope_dsc = sentry__scope_ref_dsc(scope); + sentry_value_t scope_dsc = sentry__scope_load_dsc(scope); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(scope_dsc, "marker")), "keep"); @@ -2897,7 +2900,7 @@ SENTRY_TEST(scope_clear) // The cleared scope is still usable. sentry_scope_set_tag(scope, "after", "clear"); - TEST_CHECK(scope_value_get_length(sentry__scope_ref_tags, scope) == 1); + TEST_CHECK(scope_value_get_length(sentry__scope_load_tags, scope) == 1); sentry_scope_free(scope); sentry_close(); @@ -3091,7 +3094,7 @@ SENTRY_TEST(scope_capture_user_owned) // The scope was applied but not freed, so reading and reusing it is safe // (a use-after-free here would trip the sanitizers). sentry_value_t tag - = scope_value_get_by_key(sentry__scope_ref_tags, scope, "run"); + = scope_value_get_by_key(sentry__scope_load_tags, scope, "run"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(tag), "first"); sentry_value_decref(tag); @@ -3151,7 +3154,7 @@ SENTRY_TEST(scope_bind_transaction_object) TEST_ASSERT(!sentry_value_is_null(trace)); SENTRY_WITH_SCOPE (global_scope) { sentry_value_t propagation_trace - = sentry__scope_ref_trace_context(global_scope); + = sentry__scope_load_trace_context(global_scope); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(trace, "trace_id")), sentry_value_as_string( diff --git a/tests/unit/test_tracing.c b/tests/unit/test_tracing.c index 9b98070f16..cb05c7ccea 100644 --- a/tests/unit/test_tracing.c +++ b/tests/unit/test_tracing.c @@ -1595,7 +1595,7 @@ SENTRY_TEST(set_trace) SENTRY_WITH_SCOPE (scope) { sentry_value_t propagation_trace_context - = sentry__scope_ref_trace_context(scope); + = sentry__scope_load_trace_context(scope); TEST_CHECK(!sentry_value_is_null(propagation_trace_context)); CHECK_STRING_PROPERTY(propagation_trace_context, "type", "trace"); @@ -2456,7 +2456,7 @@ SENTRY_TEST(strict_continuation_no_baggage_forks) // Scope propagation follows the fork: no lingering upstream trace_id. SENTRY_WITH_SCOPE (scope) { - sentry_value_t trace_context = sentry__scope_ref_trace_context(scope); + sentry_value_t trace_context = sentry__scope_load_trace_context(scope); const char *scope_trace_id = sentry_value_as_string( sentry_value_get_by_key(trace_context, "trace_id")); TEST_CHECK(strcmp(scope_trace_id, UPSTREAM_TRACE_ID) != 0); @@ -2506,7 +2506,7 @@ SENTRY_TEST(set_trace_rebuilds_dsc_sample_rand) double init_sample_rand = 0.0; SENTRY_WITH_SCOPE (scope) { - sentry_value_t dsc = sentry__scope_ref_dsc(scope); + sentry_value_t dsc = sentry__scope_load_dsc(scope); init_sample_rand = sentry_value_as_double( sentry_value_get_by_key(dsc, "sample_rand")); sentry_value_decref(dsc); @@ -2516,7 +2516,7 @@ SENTRY_TEST(set_trace_rebuilds_dsc_sample_rand) double new_sample_rand = -1.0; SENTRY_WITH_SCOPE (scope) { - sentry_value_t dsc = sentry__scope_ref_dsc(scope); + sentry_value_t dsc = sentry__scope_load_dsc(scope); new_sample_rand = sentry_value_as_double( sentry_value_get_by_key(dsc, "sample_rand")); sentry_value_decref(dsc);