Skip to content

Commit 3cb0757

Browse files
prachigauriarPrachi Gauriar
andauthored
Change ConfigVariableReader from a struct to a class (#10)
- Add a private MutableState struct to hold mutable state behind a Mutex, making ConfigVariableReader Sendable. This enables variable registration after the reader has been shared with other components. - Mark ConfigVariableReader as a final class and add a computed registeredVariables property that reads through the mutex. - Change register from a mutating function to a non-mutating one that writes through the mutex. Co-authored-by: Prachi Gauriar <prachi@gauriar.com>
1 parent e4cc18f commit 3cb0757

2 files changed

Lines changed: 35 additions & 19 deletions

File tree

Sources/DevConfiguration/Core/ConfigVariableReader.swift

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import Configuration
99
import DevFoundation
1010
import OSLog
11+
import Synchronization
1112

1213
/// Provides access to configuration values queried by a `ConfigVariable`.
1314
///
@@ -39,7 +40,14 @@ import OSLog
3940
///
4041
/// The reader never throws. If resolution fails, it returns the variable’s default value and posts a
4142
/// ``ConfigVariableAccessFailedEvent`` to the event bus.
42-
public struct ConfigVariableReader {
43+
public final class ConfigVariableReader: Sendable {
44+
/// The mutable state of a ``ConfigVariableReader``, protected by a `Mutex`.
45+
private struct MutableState: Sendable {
46+
/// The variables that have been registered with the reader, keyed by their configuration key.
47+
var registeredVariables: [ConfigKey: RegisteredConfigVariable] = [:]
48+
}
49+
50+
4351
/// The access reporter that is used to report configuration access events.
4452
public let accessReporter: any AccessReporter
4553

@@ -54,8 +62,8 @@ public struct ConfigVariableReader {
5462
/// The event bus used to post diagnostic events like ``ConfigVariableDecodingFailedEvent``.
5563
public let eventBus: EventBus
5664

57-
/// The variables that have been registered with this reader, keyed by their configuration key.
58-
private(set) var registeredVariables: [ConfigKey: RegisteredConfigVariable] = [:]
65+
/// The mutable state protected by a mutex.
66+
private let mutableState = Mutex(MutableState())
5967

6068
/// The logger used for registration diagnostics.
6169
private static let logger = Logger(subsystem: "DevConfiguration", category: "ConfigVariableReader")
@@ -68,7 +76,7 @@ public struct ConfigVariableReader {
6876
/// - Parameters:
6977
/// - providers: The configuration providers, queried in order until a value is found.
7078
/// - eventBus: The event bus that telemetry events are posted on.
71-
public init(providers: [any ConfigProvider], eventBus: EventBus) {
79+
public convenience init(providers: [any ConfigProvider], eventBus: EventBus) {
7280
self.init(
7381
providers: providers,
7482
accessReporter: EventBusAccessReporter(eventBus: eventBus),
@@ -91,6 +99,12 @@ public struct ConfigVariableReader {
9199
self.providers = providers
92100
self.eventBus = eventBus
93101
}
102+
103+
104+
/// The variables that have been registered with this reader, keyed by their configuration key.
105+
var registeredVariables: [ConfigKey: RegisteredConfigVariable] {
106+
mutableState.withLock { $0.registeredVariables }
107+
}
94108
}
95109

96110

@@ -107,7 +121,7 @@ extension ConfigVariableReader {
107121
/// warning is logged, and an assertion failure is triggered.
108122
///
109123
/// - Parameter variable: The configuration variable to register.
110-
public mutating func register<Value>(_ variable: ConfigVariable<Value>) {
124+
public func register<Value>(_ variable: ConfigVariable<Value>) {
111125
let defaultContent: ConfigContent
112126
do {
113127
defaultContent = try variable.content.encode(variable.defaultValue)
@@ -117,17 +131,19 @@ extension ConfigVariableReader {
117131
return
118132
}
119133

120-
if registeredVariables[variable.key] != nil {
121-
assertionFailure("Config variable '\(variable.key)' is already registered")
122-
Self.logger.error("Config variable '\(variable.key)' is already registered; overwriting")
123-
}
134+
mutableState.withLock { state in
135+
if state.registeredVariables[variable.key] != nil {
136+
assertionFailure("Config variable '\(variable.key)' is already registered")
137+
Self.logger.error("Config variable '\(variable.key)' is already registered; overwriting")
138+
}
124139

125-
registeredVariables[variable.key] = RegisteredConfigVariable(
126-
key: variable.key,
127-
defaultContent: defaultContent,
128-
secrecy: variable.secrecy,
129-
metadata: variable.metadata
130-
)
140+
state.registeredVariables[variable.key] = RegisteredConfigVariable(
141+
key: variable.key,
142+
defaultContent: defaultContent,
143+
secrecy: variable.secrecy,
144+
metadata: variable.metadata
145+
)
146+
}
131147
}
132148
}
133149

Tests/DevConfigurationTests/Unit Tests/Core/ConfigVariableReaderRegistrationTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ struct ConfigVariableReaderRegistrationTests: RandomValueGenerating {
2020
@Test
2121
mutating func registerStoresVariableWithCorrectProperties() {
2222
// set up
23-
var reader = ConfigVariableReader(providers: [InMemoryProvider(values: [:])], eventBus: EventBus())
23+
let reader = ConfigVariableReader(providers: [InMemoryProvider(values: [:])], eventBus: EventBus())
2424

2525
var metadata = ConfigVariableMetadata()
2626
metadata[TestTeamMetadataKey.self] = randomAlphanumericString()
@@ -47,7 +47,7 @@ struct ConfigVariableReaderRegistrationTests: RandomValueGenerating {
4747
@Test
4848
mutating func registerMultipleVariablesStoresAll() {
4949
// set up
50-
var reader = ConfigVariableReader(providers: [InMemoryProvider(values: [:])], eventBus: EventBus())
50+
let reader = ConfigVariableReader(providers: [InMemoryProvider(values: [:])], eventBus: EventBus())
5151
let key1 = randomConfigKey()
5252
let key2 = randomConfigKey()
5353
let variable1 = ConfigVariable(key: key1, defaultValue: randomBool())
@@ -68,7 +68,7 @@ struct ConfigVariableReaderRegistrationTests: RandomValueGenerating {
6868
@Test
6969
func registerDuplicateKeyHalts() async {
7070
await #expect(processExitsWith: .failure) {
71-
var reader = ConfigVariableReader(
71+
let reader = ConfigVariableReader(
7272
providers: [InMemoryProvider(values: [:])],
7373
eventBus: EventBus()
7474
)
@@ -84,7 +84,7 @@ struct ConfigVariableReaderRegistrationTests: RandomValueGenerating {
8484
@Test
8585
func registerWithEncodeFailureHalts() async {
8686
await #expect(processExitsWith: .failure) {
87-
var reader = ConfigVariableReader(
87+
let reader = ConfigVariableReader(
8888
providers: [InMemoryProvider(values: [:])],
8989
eventBus: EventBus()
9090
)

0 commit comments

Comments
 (0)