Skip to content

Commit

Permalink
fix: enhance thread safety of properties
Browse files Browse the repository at this point in the history
  • Loading branch information
sojingle committed Jan 22, 2025
1 parent 98a5f02 commit db1c8b0
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 33 deletions.
20 changes: 14 additions & 6 deletions Sources/Amplitude/Amplitude.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,21 @@ public class Amplitude {
return self.configuration.identifyStorageProvider
}()

lazy var timeline: Timeline = {
return Timeline()
}()
private let timelineLock = NSLock()
private var _timeline: Timeline?
var timeline: Timeline {
timelineLock.synchronizedLazy(&_timeline) {
Timeline()
}
}

lazy var sessions: Sessions = {
return Sessions(amplitude: self)
}()
private var sessionsLock = NSLock()
private var _sessions: Sessions? = nil
var sessions: Sessions {
sessionsLock.synchronizedLazy(&_sessions) {
Sessions(amplitude: self)
}
}

public lazy var logger: (any Logger)? = {
return self.configuration.loggerProvider
Expand Down
13 changes: 13 additions & 0 deletions Sources/Amplitude/Utilities/Atomic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,16 @@ public struct Atomic<T> {
value = newValue
}
}

extension NSLock {
func synchronizedLazy<T>(_ storage: inout T?, initializer: () -> T) -> T {
lock()
defer { unlock() }
if let existing = storage {
return existing
}
let newValue = initializer()
storage = newValue
return newValue
}
}
44 changes: 22 additions & 22 deletions Sources/Amplitude/Utilities/Diagonostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,24 @@
import Foundation

public class Diagnostics {

private static let MAX_ERROR_LOGS = 10

private var malformedEvents: [String]?
private var malformedEvents: [String] = []
private var errorLogs = NSMutableOrderedSet(capacity: 10)

init(){}
private let lock = NSLock()

func addMalformedEvent(_ event: String) {
if malformedEvents == nil {
malformedEvents = [String]()
}
malformedEvents?.append(event)
lock.lock()
defer { lock.unlock() }

malformedEvents.append(event)
}

func addErrorLog(_ log: String) {
lock.lock()
defer { lock.unlock() }

errorLogs.add(log)

// trim to MAX_ERROR_LOGS elements
Expand All @@ -32,33 +34,31 @@ public class Diagnostics {
}
}

func hasDiagnostics() -> Bool {
return (malformedEvents != nil && malformedEvents!.count > 0) || errorLogs.count > 0
}

/**
* Extracts the diagnostics as a JSON string.
* Warning: This will clear stored diagnostics.
* @return JSON string of diagnostics or empty if no diagnostics are present.
*/
func extractDiagonosticsToString() -> String {
if !hasDiagnostics() {
return ""
}
func extractDiagnosticsToString() -> String? {
lock.lock()
defer { lock.unlock() }

guard !malformedEvents.isEmpty || errorLogs.count > 0 else { return nil }

var diagnostics = [String: [String]]()
if malformedEvents != nil && malformedEvents!.count > 0 {
if !malformedEvents.isEmpty {
diagnostics["malformed_events"] = malformedEvents
}
if errorLogs.count > 0, let errorStrings = errorLogs.array as? [String] {
diagnostics["error_logs"] = errorStrings
if errorLogs.count > 0, let errorStrings = errorLogs.array as? [String] {
diagnostics["error_logs"] = errorStrings
}
do {
let data = try JSONSerialization.data(withJSONObject: diagnostics, options: [])
malformedEvents?.removeAll()
malformedEvents.removeAll()
errorLogs.removeAllObjects()
return String(data: data, encoding: .utf8) ?? ""
return String(data: data, encoding: .utf8)
} catch {
return ""
return nil
}
}
}
}
7 changes: 2 additions & 5 deletions Sources/Amplitude/Utilities/HttpClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,10 @@ class HttpClient {
,"options":{"min_id_length":\(minIdLength)}
"""
}
if diagnostics.hasDiagnostics() {
let diagnosticsInfo = diagnostics.extractDiagonosticsToString()
if !diagnosticsInfo.isEmpty {
requestPayload += """
if let diagnosticsInfo = diagnostics.extractDiagnosticsToString(), !diagnosticsInfo.isEmpty {
requestPayload += """
,"request_metadata":{"sdk":\(diagnosticsInfo)}
"""
}
}
requestPayload += "}"
return requestPayload.data(using: .utf8)
Expand Down

0 comments on commit db1c8b0

Please sign in to comment.