Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  SentryConsentLedger.swift   Sprache: JAVA

 

import Foundation
import os

/// Tracks which deferred crash reports are awaiting a decision, which the user consented to, and the
/// context to restore when a consented event is re-captured.
///
/// All state is guarded by an `OSAllocatedUnfairLock`, so instances are safe to touch from Sentry's
/// queue (`beforeSend`) and from the main actor (the consent alert).
final class SentryConsentLedger: @unchecked Sendable {

    /// A deferred event's `context` as it was at capture time.
    typealias Context = [String: [String: Any]]

    /// Why a report was or was not queued for the user's decision.
    enum Reservation: Equatable {
        /// A slot was taken and the context stored. The caller must answer with
        /// ``recordConsent(eventID:)`` or ``drop(eventID:)``.
        case reserved
        /// The user is already being asked about this report.
        case alreadyPending
        /// ``maxPendingReports`` reports are already waiting for a decision.
        case capReached
    }

    /// Whether a re-captured event may be uploaded, and with which context.
    enum ConsentOutcome {
        /// The user never consented to this event, or the consent was already consumed. It must not
        /// leave the device.
        case notConsented
        /// The user consented. `context` is the crashed run's context to restore, `nil` if the event
        /// carried none.
        case consented(context: Context?)
    }

    private let lock = OSAllocatedUnfairLock()

    /// Event IDs the user agreed to send. Let through exactly once each.
    private var consentedEventIDs: Set<String> = []

    /// `context` of a deferred event as it was at capture time. Re-capturing an event runs the SDK's
    /// event processors again, which would otherwise describe the *current* run instead of the crashed
    /// one.
    private var originalContexts: [String: Context] = [:]

    /// Reports handed to the consent alert and not answered yet. A set rather than a counter, so a
    /// repeated reservation or a repeated answer for the same report cannot make the cap drift.
    private var pendingEventIDs: Set<String> = []

    /// Upper bound on reports waiting for a decision.
    private let maxPendingReports: Int

    init(maxPendingReports: Int) {
        self.maxPendingReports = maxPendingReports
    }

    /// Reserves a slot for a report awaiting the user's decision and stores its context.
    ///
    /// Nothing is stored unless the result is ``Reservation/reserved``.
    func reservePending(eventID: String, context: Context?) -> Reservation {
        lock.withLockUnchecked {
            guard pendingEventIDs.count < maxPendingReports else {
                return .capReached
            }

            // The SDK may run `beforeSend` more than once for one event. Reserving twice would leak a
            // slot and queue a second alert for a report the user is already being asked about.
            guard pendingEventIDs.insert(eventID).inserted else {
                return .alreadyPending
            }

            if let context {
                originalContexts[eventID] = context
            }

            return .reserved
        }
    }

    /// Records the user's consent so the re-captured event is let through on its next pass, and frees
    /// the report's pending slot.
    ///
    /// Ignores a report that is not waiting for a decision, so a repeated answer neither frees a second
    /// slot nor grants consent twice.
    func recordConsent(eventID: String) {
        lock.withLockUnchecked {
            guard pendingEventIDs.remove(eventID) != nil else {
                return
            }

            consentedEventIDs.insert(eventID)
        }
    }

    /// Drops a report the user declined, freeing its pending slot and discarding its stored context.
    ///
    /// Ignores a report that is not waiting for a decision, see ``recordConsent(eventID:)``.
    func drop(eventID: String) {
        lock.withLockUnchecked {
            guard pendingEventIDs.remove(eventID) != nil else {
                return
            }

            originalContexts.removeValue(forKey: eventID)
        }
    }

    /// Consumes the consent for a re-captured event.
    ///
    /// Consent and context are consumed once each, so any further pass of the same event is
    /// ``ConsentOutcome/notConsented``.
    func consumeConsent(eventID: String) -> ConsentOutcome {
        lock.withLockUnchecked {
            guard consentedEventIDs.remove(eventID) != nil else {
                return .notConsented
            }

            return .consented(context: originalContexts.removeValue(forKey: eventID))
        }
    }
}

Messung
CHZG
859510093

¤ Dauer der Verarbeitung: 0.0 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104845
#Domains=499266