From 2910fcbb930cb976b28f543c09d3281300c85406 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 b62af46e86..630617867d 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 bf2bb2d017..bb03ea331d 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) { @@ -760,9 +712,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 @@ -988,11 +941,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); } } @@ -1006,12 +955,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); } } @@ -1091,9 +1035,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); } } @@ -1101,12 +1043,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); } } @@ -1130,9 +1067,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); } } @@ -1140,12 +1075,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); } } @@ -1218,7 +1148,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); } } @@ -1246,9 +1176,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); } } @@ -1256,12 +1184,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); } } @@ -1327,7 +1250,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)) { @@ -1348,8 +1271,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); } } @@ -1365,15 +1288,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); } } @@ -1436,11 +1351,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"); @@ -1455,8 +1368,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 }; @@ -1502,10 +1414,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); @@ -1523,27 +1432,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); } } @@ -1604,7 +1501,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); } } @@ -1612,7 +1509,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); } } @@ -1764,18 +1661,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 @@ -1834,10 +1720,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 0c122bffce..36419ce226 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 592cfe03d01bf9fd15d492d1cdb472685441251c 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 de089e3f396b44633c0edde6d67c6d34752877c2 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 01ac4478b9..94360ad432 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 @@ -198,6 +199,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), \ @@ -278,6 +410,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 @@ -359,6 +492,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 08f6d6f60c..cfbaa4dca9 100644 --- a/tests/unit/test_sync.c +++ b/tests/unit/test_sync.c @@ -4,6 +4,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 f280c56675..ffcecf5fd8 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -335,6 +335,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 2db6aa83516e017904c1e2ce337d98906dbae3f0 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 94360ad432..4a8b89bc82 100644 --- a/src/sentry_sync.h +++ b/src/sentry_sync.h @@ -494,12 +494,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; \ @@ -507,42 +517,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 85ea15be20ad2ec0d019c5482426945bc709730b 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 bb03ea331d..def7f1f8f6 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -473,7 +473,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 1cebda0ce54eeb46023529fa2d187e9f84678032 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 ff0b6d4a589840791ab45c5902d2038cd97299b1 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 2267643e91..626cd9b725 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -449,6 +449,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 61bb84221b..83a5032816 100644 --- a/tests/unit/test_value.c +++ b/tests/unit/test_value.c @@ -2211,3 +2211,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 ffcecf5fd8..4c29028767 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -533,6 +533,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 691d2acc656bcbb833271c9f7204879141c6d006 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 286d0873b2..31869f193e 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -753,7 +753,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 def7f1f8f6..7dca5bfa33 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -708,9 +708,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)); @@ -848,13 +850,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); @@ -2046,8 +2050,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 @@ -2073,8 +2077,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 e0436b5029a59e66369db693a92390f257db3c8e 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 af5cffdaa733f6a6f1dd05c3fdd7045eb9c17ad7 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 88e616229b420489982cb79c1ee91f8229265ba8 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 630617867d..283289e2b6 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 4c29028767..ad3fa9955a 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -533,8 +533,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 22476af2a96491b087e68f919bffa0d490465b46 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 7dca5bfa33..0273ff14cf 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -1356,9 +1356,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"); @@ -1371,8 +1373,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 }; @@ -1444,8 +1448,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 36419ce226..09b4de9528 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 95a44ac17cf9d50ac655675da6722dd5072a6630 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 48f09d32d283ba94263c3e65ebadd9b3f905752b 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 7ab213cb0b3ef10edbef25dff7dc7f2f7e5e59eb 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 59ae8b06c1fd4352d2dbd46072f3e46d9012b460 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 ad3fa9955a..bb5192673b 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -392,6 +392,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 7356f203dcfebcc431853dc8db2fb8edcf8e9344 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 777bba32407ebe3e241bac9d06102ebb82199b0d 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 f6e3d85bd147af8b7e670d1fd7ebb1998ba78316 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 2e9f4973c8bd9eb2645952b20d3a5b3e0c8f76bc 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 bb5192673b..9307614495 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -358,6 +358,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 5aaba3dd0cf00dd4652ded5b9c565f65211e10fe 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 31869f193e..30b6d81d4a 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -753,12 +753,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 @@ -807,6 +809,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 0273ff14cf..d87ad296a8 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -708,17 +708,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); @@ -726,6 +729,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); @@ -850,15 +854,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); @@ -866,6 +873,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); @@ -2053,8 +2061,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 d530e7ac19644a0136a5b9290a068e3d7c84ef9a 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 30b6d81d4a..2c2b1e5c50 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -753,7 +753,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 d87ad296a8..931d9c456f 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -709,11 +709,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); @@ -855,14 +855,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); @@ -2012,7 +2012,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 9307614495..09820e8705 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -345,6 +345,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 3c2dcd5a4dbef3358a27a7551df8e6a6850be66f 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 1452b9ea31b1d6c1f77749181f7cb4f192ad4687 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 5344fd9d99e9cfc9d2d7c8bb4ee45ed097d43f85 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 28113fd16fe973a348ffbd6919d5d0f521f0ae8a 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 283289e2b6..6475391bd7 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -1168,10 +1168,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; } @@ -1200,10 +1199,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; } @@ -1259,8 +1257,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 2c2b1e5c50..842d7d0500 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) @@ -715,6 +720,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); } @@ -934,18 +940,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 " @@ -1140,7 +1147,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 931d9c456f..95aa5467e8 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); } } @@ -2012,18 +2021,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); @@ -2068,11 +2066,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); @@ -2092,11 +2085,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 ba539a7ccf5ecd0c75a3a2f74c644d8c09efa281 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 fd41b34d1cb2116c4beb33ec046317115a787560 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 842d7d0500..1eb0ffcfa1 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -759,7 +759,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 95aa5467e8..071d4a7ae3 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -718,11 +718,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); @@ -864,14 +864,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); @@ -1374,7 +1374,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); @@ -1390,7 +1390,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 09b4de9528..527b63c0ed 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);