Skip to content

Commit 61a3258

Browse files
author
Ignacio Bonafonte
authored
Merge pull request #349 from nachoBonafonte/main
Fix ActivityContextManager contextMap is never being cleaned up
2 parents 610b0d4 + e2e0efa commit 61a3258

File tree

8 files changed

+254
-16
lines changed

8 files changed

+254
-16
lines changed

Package.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ let package = Package(
6565
exclude: ["README.md"]),
6666
.target(name: "SwiftMetricsShim",
6767
dependencies: ["OpenTelemetrySdk",
68-
.product(name: "NIO", package: "swift-nio"),
6968
.product(name: "CoreMetrics", package: "swift-metrics")],
7069
path: "Sources/Importers/SwiftMetricsShim",
7170
exclude: ["README.md"]),
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
//===----------------------------------------------------------------------===//
7+
//
8+
// This source file is part of the Swift Metrics API open source project
9+
//
10+
// Copyright (c) 2018-2019 Apple Inc. and the Swift Metrics API project authors
11+
// Licensed under Apache License v2.0
12+
//
13+
// See LICENSE.txt for license information
14+
// See CONTRIBUTORS.txt for the list of Swift Metrics API project authors
15+
//
16+
// SPDX-License-Identifier: Apache-2.0
17+
//
18+
//===----------------------------------------------------------------------===//
19+
20+
//===----------------------------------------------------------------------===//
21+
//
22+
// This source file is part of the SwiftNIO open source project
23+
//
24+
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
25+
// Licensed under Apache License v2.0
26+
//
27+
// See LICENSE.txt for license information
28+
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
29+
//
30+
// SPDX-License-Identifier: Apache-2.0
31+
//
32+
//===----------------------------------------------------------------------===//
33+
34+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
35+
import Darwin
36+
#else
37+
import Glibc
38+
#endif
39+
40+
/// A threading lock based on `libpthread` instead of `libdispatch`.
41+
///
42+
/// This object provides a lock on top of a single `pthread_mutex_t`. This kind
43+
/// of lock is safe to use with `libpthread`-based threading models, such as the
44+
/// one used by NIO.
45+
internal final class Lock {
46+
fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t> = UnsafeMutablePointer.allocate(capacity: 1)
47+
48+
/// Create a new lock.
49+
public init() {
50+
let err = pthread_mutex_init(self.mutex, nil)
51+
precondition(err == 0, "pthread_mutex_init failed with error \(err)")
52+
}
53+
54+
deinit {
55+
let err = pthread_mutex_destroy(self.mutex)
56+
precondition(err == 0, "pthread_mutex_destroy failed with error \(err)")
57+
self.mutex.deallocate()
58+
}
59+
60+
/// Acquire the lock.
61+
///
62+
/// Whenever possible, consider using `withLock` instead of this method and
63+
/// `unlock`, to simplify lock handling.
64+
public func lock() {
65+
let err = pthread_mutex_lock(self.mutex)
66+
precondition(err == 0, "pthread_mutex_lock failed with error \(err)")
67+
}
68+
69+
/// Release the lock.
70+
///
71+
/// Whenever possible, consider using `withLock` instead of this method and
72+
/// `lock`, to simplify lock handling.
73+
public func unlock() {
74+
let err = pthread_mutex_unlock(self.mutex)
75+
precondition(err == 0, "pthread_mutex_unlock failed with error \(err)")
76+
}
77+
}
78+
79+
extension Lock {
80+
/// Acquire the lock for the duration of the given block.
81+
///
82+
/// This convenience method should be preferred to `lock` and `unlock` in
83+
/// most situations, as it ensures that the lock will be released regardless
84+
/// of how `body` exits.
85+
///
86+
/// - Parameter body: The block to execute while holding the lock.
87+
/// - Returns: The value returned by the block.
88+
@inlinable
89+
internal func withLock<T>(_ body: () throws -> T) rethrows -> T {
90+
self.lock()
91+
defer {
92+
self.unlock()
93+
}
94+
return try body()
95+
}
96+
97+
// specialise Void return (for performance)
98+
@inlinable
99+
internal func withLockVoid(_ body: () throws -> Void) rethrows {
100+
try self.withLock(body)
101+
}
102+
}
103+
104+
/// A threading lock based on `libpthread` instead of `libdispatch`.
105+
///
106+
/// This object provides a lock on top of a single `pthread_mutex_t`. This kind
107+
/// of lock is safe to use with `libpthread`-based threading models, such as the
108+
/// one used by NIO.
109+
internal final class ReadWriteLock {
110+
fileprivate let rwlock: UnsafeMutablePointer<pthread_rwlock_t> = UnsafeMutablePointer.allocate(capacity: 1)
111+
112+
/// Create a new lock.
113+
public init() {
114+
let err = pthread_rwlock_init(self.rwlock, nil)
115+
precondition(err == 0, "pthread_rwlock_init failed with error \(err)")
116+
}
117+
118+
deinit {
119+
let err = pthread_rwlock_destroy(self.rwlock)
120+
precondition(err == 0, "pthread_rwlock_destroy failed with error \(err)")
121+
self.rwlock.deallocate()
122+
}
123+
124+
/// Acquire a reader lock.
125+
///
126+
/// Whenever possible, consider using `withLock` instead of this method and
127+
/// `unlock`, to simplify lock handling.
128+
public func lockRead() {
129+
let err = pthread_rwlock_rdlock(self.rwlock)
130+
precondition(err == 0, "pthread_rwlock_rdlock failed with error \(err)")
131+
}
132+
133+
/// Acquire a writer lock.
134+
///
135+
/// Whenever possible, consider using `withLock` instead of this method and
136+
/// `unlock`, to simplify lock handling.
137+
public func lockWrite() {
138+
let err = pthread_rwlock_wrlock(self.rwlock)
139+
precondition(err == 0, "pthread_rwlock_wrlock failed with error \(err)")
140+
}
141+
142+
/// Release the lock.
143+
///
144+
/// Whenever possible, consider using `withLock` instead of this method and
145+
/// `lock`, to simplify lock handling.
146+
public func unlock() {
147+
let err = pthread_rwlock_unlock(self.rwlock)
148+
precondition(err == 0, "pthread_rwlock_unlock failed with error \(err)")
149+
}
150+
}
151+
152+
extension ReadWriteLock {
153+
/// Acquire the reader lock for the duration of the given block.
154+
///
155+
/// This convenience method should be preferred to `lock` and `unlock` in
156+
/// most situations, as it ensures that the lock will be released regardless
157+
/// of how `body` exits.
158+
///
159+
/// - Parameter body: The block to execute while holding the lock.
160+
/// - Returns: The value returned by the block.
161+
@inlinable
162+
internal func withReaderLock<T>(_ body: () throws -> T) rethrows -> T {
163+
self.lockRead()
164+
defer {
165+
self.unlock()
166+
}
167+
return try body()
168+
}
169+
170+
/// Acquire the writer lock for the duration of the given block.
171+
///
172+
/// This convenience method should be preferred to `lock` and `unlock` in
173+
/// most situations, as it ensures that the lock will be released regardless
174+
/// of how `body` exits.
175+
///
176+
/// - Parameter body: The block to execute while holding the lock.
177+
/// - Returns: The value returned by the block.
178+
@inlinable
179+
internal func withWriterLock<T>(_ body: () throws -> T) rethrows -> T {
180+
self.lockWrite()
181+
defer {
182+
self.unlock()
183+
}
184+
return try body()
185+
}
186+
187+
// specialise Void return (for performance)
188+
@inlinable
189+
internal func withReaderLockVoid(_ body: () throws -> Void) rethrows {
190+
try self.withReaderLock(body)
191+
}
192+
193+
// specialise Void return (for performance)
194+
@inlinable
195+
internal func withWriterLockVoid(_ body: () throws -> Void) rethrows {
196+
try self.withWriterLock(body)
197+
}
198+
}

Sources/Importers/SwiftMetricsShim/SwiftMetricsShim.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55

66
import CoreMetrics
7-
import NIOConcurrencyHelpers
87
import OpenTelemetryApi
98

109
public class OpenTelemetrySwiftMetrics: MetricsFactory {

Sources/OpenTelemetryApi/Context/ActivityContextManager.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ class ActivityContextManager: ContextManager {
2828
}
2929

3030
var objectScope = NSMapTable<AnyObject, ScopeElement>(keyOptions: .weakMemory, valueOptions: .strongMemory)
31-
3231
var contextMap = [os_activity_id_t: [String: AnyObject]]()
3332

3433
func getCurrentContextValue(forKey key: OpenTelemetryContextKeys) -> AnyObject? {
@@ -69,6 +68,14 @@ class ActivityContextManager: ContextManager {
6968
}
7069

7170
func removeContextValue(forKey key: OpenTelemetryContextKeys, value: AnyObject) {
71+
let activityIdent = os_activity_get_identifier(OS_ACTIVITY_CURRENT, nil)
72+
rlock.lock()
73+
contextMap[activityIdent]?[key.rawValue] = nil
74+
if contextMap[activityIdent]?.isEmpty ?? false {
75+
contextMap[activityIdent] = nil
76+
}
77+
print("ContextMap Count: \(contextMap.count)")
78+
rlock.unlock()
7279
if let scope = objectScope.object(forKey: value) {
7380
var scope = scope.scope
7481
os_activity_scope_leave(&scope)

Sources/OpenTelemetryApi/Trace/PropagatedSpan.swift

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Foundation
88
/// The PropagatedSpan is the default Span that is used when no Span
99
/// implementation is available. All operations are no-op except context propagation.
1010
class PropagatedSpan: Span {
11-
var name: String = ""
11+
var name: String
1212

1313
var kind: SpanKind
1414

@@ -25,23 +25,40 @@ class PropagatedSpan: Span {
2525
/// Returns a DefaultSpan with an invalid SpanContext.
2626
convenience init() {
2727
let invalidContext = SpanContext.create(traceId: TraceId(),
28-
spanId: SpanId(),
29-
traceFlags: TraceFlags(),
30-
traceState: TraceState())
31-
self.init(context: invalidContext, kind: .client)
28+
spanId: SpanId(),
29+
traceFlags: TraceFlags(),
30+
traceState: TraceState())
31+
self.init(name: "", context: invalidContext, kind: .client)
3232
}
3333

3434
/// Creates an instance of this class with the SpanContext.
3535
/// - Parameter context: the SpanContext
3636
convenience init(context: SpanContext) {
37-
self.init(context: context, kind: .client)
37+
self.init(name: "", context: context, kind: .client)
3838
}
3939

4040
/// Creates an instance of this class with the SpanContext and Span kind
4141
/// - Parameters:
4242
/// - context: the SpanContext
4343
/// - kind: the SpanKind
44-
init(context: SpanContext, kind: SpanKind) {
44+
convenience init(context: SpanContext, kind: SpanKind) {
45+
self.init(name: "", context: context, kind: kind)
46+
}
47+
48+
/// Creates an instance of this class with the SpanContext and Span name
49+
/// - Parameters:
50+
/// - context: the SpanContext
51+
/// - kind: the SpanKind
52+
convenience init(name: String, context: SpanContext) {
53+
self.init(name: name, context: context, kind: .client)
54+
}
55+
56+
/// Creates an instance of this class with the SpanContext, Span kind and name
57+
/// - Parameters:
58+
/// - context: the SpanContext
59+
/// - kind: the SpanKind
60+
init(name: String, context: SpanContext, kind: SpanKind) {
61+
self.name = name
4562
self.context = context
4663
self.kind = kind
4764
}

Sources/OpenTelemetryApi/Trace/PropagatedSpanBuilder.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,19 @@ class PropagatedSpanBuilder: SpanBuilder {
1010
private var tracer: Tracer
1111
private var isRootSpan: Bool = false
1212
private var spanContext: SpanContext?
13+
private var spanName: String
1314

1415
init(tracer: Tracer, spanName: String) {
1516
self.tracer = tracer
17+
self.spanName = spanName
1618
}
1719

1820
@discardableResult public func startSpan() -> Span {
1921
if spanContext == nil, !isRootSpan {
2022
spanContext = OpenTelemetry.instance.contextProvider.activeSpan?.context
2123
}
22-
return PropagatedSpan(context: spanContext ?? SpanContext.create(traceId: TraceId.random(),
24+
return PropagatedSpan(name: spanName,
25+
context: spanContext ?? SpanContext.create(traceId: TraceId.random(),
2326
spanId: SpanId.random(),
2427
traceFlags: TraceFlags(),
2528
traceState: TraceState()))
@@ -63,5 +66,4 @@ class PropagatedSpanBuilder: SpanBuilder {
6366
func setActive(_ active: Bool) -> Self {
6467
return self
6568
}
66-
6769
}

Tests/OpenTelemetryApiTests/Baggage/DefaultBaggageManagerTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class DefaultBaggageManagerTests: XCTestCase {
6363
XCTAssert(self.defaultBaggageManager.getCurrentBaggage() === self.baggage)
6464
semaphore2.signal()
6565
semaphore.wait()
66-
XCTAssert(self.defaultBaggageManager.getCurrentBaggage() === self.baggage)
66+
XCTAssertNil(self.defaultBaggageManager.getCurrentBaggage())
6767
expec.fulfill()
6868
}
6969
semaphore2.wait()

0 commit comments

Comments
 (0)