1
+ import Dispatch
1
2
import OrderedCollections
2
3
3
4
/**
@@ -34,15 +35,53 @@ public final class GraphQLSchema: @unchecked Sendable {
34
35
let extensionASTNodes : [ SchemaExtensionDefinition ]
35
36
36
37
// Used as a cache for validateSchema().
37
- var validationErrors : [ GraphQLError ] ?
38
+ private var _validationErrors : [ GraphQLError ] ?
39
+ private let validationErrorQueue = DispatchQueue (
40
+ label: " graphql.schema.validationerrors " ,
41
+ attributes: . concurrent
42
+ )
43
+ var validationErrors : [ GraphQLError ] ? {
44
+ get {
45
+ // Reads can occur concurrently.
46
+ return validationErrorQueue. sync {
47
+ _validationErrors
48
+ }
49
+ }
50
+ set {
51
+ // Writes occur sequentially.
52
+ return validationErrorQueue. async ( flags: . barrier) {
53
+ self . _validationErrors = newValue
54
+ }
55
+ }
56
+ }
38
57
39
58
public let queryType : GraphQLObjectType ?
40
59
public let mutationType : GraphQLObjectType ?
41
60
public let subscriptionType : GraphQLObjectType ?
42
61
public let directives : [ GraphQLDirective ]
43
62
public let typeMap : TypeMap
44
- public internal( set) var implementations : [ String : InterfaceImplementations ]
45
- private var subTypeMap : [ String : [ String : Bool ] ] = [ : ]
63
+ public let implementations : [ String : InterfaceImplementations ]
64
+
65
+ // Used as a cache for validateSchema().
66
+ private var _subTypeMap : [ String : [ String : Bool ] ] = [ : ]
67
+ private let subTypeMapQueue = DispatchQueue (
68
+ label: " graphql.schema.subtypeMap " ,
69
+ attributes: . concurrent
70
+ )
71
+ var subTypeMap : [ String : [ String : Bool ] ] {
72
+ get {
73
+ // Reads can occur concurrently.
74
+ return subTypeMapQueue. sync {
75
+ _subTypeMap
76
+ }
77
+ }
78
+ set {
79
+ // Writes occur sequentially.
80
+ return subTypeMapQueue. async ( flags: . barrier) {
81
+ self . _subTypeMap = newValue
82
+ }
83
+ }
84
+ }
46
85
47
86
public init (
48
87
description: String ? = nil ,
@@ -56,7 +95,7 @@ public final class GraphQLSchema: @unchecked Sendable {
56
95
extensionASTNodes: [ SchemaExtensionDefinition ] = [ ] ,
57
96
assumeValid: Bool = false
58
97
) throws {
59
- validationErrors = assumeValid ? [ ] : nil
98
+ _validationErrors = assumeValid ? [ ] : nil
60
99
61
100
self . description = description
62
101
self . extensions = extensions
@@ -109,7 +148,38 @@ public final class GraphQLSchema: @unchecked Sendable {
109
148
var typeMap = TypeMap ( )
110
149
111
150
// Keep track of all implementations by interface name.
112
- implementations = try collectImplementations ( types: Array ( typeMap. values) )
151
+ func collectImplementations(
152
+ types: [ GraphQLNamedType ]
153
+ ) throws -> [ String : InterfaceImplementations ] {
154
+ var implementations : [ String : InterfaceImplementations ] = [ : ]
155
+
156
+ for type in types {
157
+ if let type = type as? GraphQLInterfaceType {
158
+ if implementations [ type. name] == nil {
159
+ implementations [ type. name] = InterfaceImplementations ( )
160
+ }
161
+
162
+ // Store implementations by interface.
163
+ for iface in try type. getInterfaces ( ) {
164
+ implementations [ iface. name] = InterfaceImplementations (
165
+ interfaces: ( implementations [ iface. name] ? . interfaces ?? [ ] ) + [ type]
166
+ )
167
+ }
168
+ }
169
+
170
+ if let type = type as? GraphQLObjectType {
171
+ // Store implementations by objects.
172
+ for iface in try type. getInterfaces ( ) {
173
+ implementations [ iface. name] = InterfaceImplementations (
174
+ objects: ( implementations [ iface. name] ? . objects ?? [ ] ) + [ type]
175
+ )
176
+ }
177
+ }
178
+ }
179
+
180
+ return implementations
181
+ }
182
+ var implementations = try collectImplementations ( types: Array ( typeMap. values) )
113
183
114
184
for namedType in allReferencedTypes. values {
115
185
let typeName = namedType. name
@@ -124,37 +194,38 @@ public final class GraphQLSchema: @unchecked Sendable {
124
194
if let namedType = namedType as? GraphQLInterfaceType {
125
195
// Store implementations by interface.
126
196
for iface in try namedType. getInterfaces ( ) {
127
- let implementations = self . implementations [ iface. name] ?? . init(
197
+ let implementation = implementations [ iface. name] ?? . init(
128
198
objects: [ ] ,
129
199
interfaces: [ ]
130
200
)
131
201
132
- var interfaces = implementations . interfaces
202
+ var interfaces = implementation . interfaces
133
203
interfaces. append ( namedType)
134
- self . implementations [ iface. name] = . init(
135
- objects: implementations . objects,
204
+ implementations [ iface. name] = . init(
205
+ objects: implementation . objects,
136
206
interfaces: interfaces
137
207
)
138
208
}
139
209
} else if let namedType = namedType as? GraphQLObjectType {
140
210
// Store implementations by objects.
141
211
for iface in try namedType. getInterfaces ( ) {
142
- let implementations = self . implementations [ iface. name] ?? . init(
212
+ let implementation = implementations [ iface. name] ?? . init(
143
213
objects: [ ] ,
144
214
interfaces: [ ]
145
215
)
146
216
147
- var objects = implementations . objects
217
+ var objects = implementation . objects
148
218
objects. append ( namedType)
149
- self . implementations [ iface. name] = . init(
219
+ implementations [ iface. name] = . init(
150
220
objects: objects,
151
- interfaces: implementations . interfaces
221
+ interfaces: implementation . interfaces
152
222
)
153
223
}
154
224
}
155
225
}
156
226
157
227
self . typeMap = typeMap
228
+ self . implementations = implementations
158
229
}
159
230
160
231
convenience init ( config: GraphQLSchemaNormalizedConfig ) throws {
@@ -268,7 +339,7 @@ public final class GraphQLSchema: @unchecked Sendable {
268
339
269
340
public typealias TypeMap = OrderedDictionary < String , GraphQLNamedType >
270
341
271
- public struct InterfaceImplementations {
342
+ public struct InterfaceImplementations : Sendable {
272
343
public let objects : [ GraphQLObjectType ]
273
344
public let interfaces : [ GraphQLInterfaceType ]
274
345
@@ -281,38 +352,6 @@ public struct InterfaceImplementations {
281
352
}
282
353
}
283
354
284
- func collectImplementations(
285
- types: [ GraphQLNamedType ]
286
- ) throws -> [ String : InterfaceImplementations ] {
287
- var implementations : [ String : InterfaceImplementations ] = [ : ]
288
-
289
- for type in types {
290
- if let type = type as? GraphQLInterfaceType {
291
- if implementations [ type. name] == nil {
292
- implementations [ type. name] = InterfaceImplementations ( )
293
- }
294
-
295
- // Store implementations by interface.
296
- for iface in try type. getInterfaces ( ) {
297
- implementations [ iface. name] = InterfaceImplementations (
298
- interfaces: ( implementations [ iface. name] ? . interfaces ?? [ ] ) + [ type]
299
- )
300
- }
301
- }
302
-
303
- if let type = type as? GraphQLObjectType {
304
- // Store implementations by objects.
305
- for iface in try type. getInterfaces ( ) {
306
- implementations [ iface. name] = InterfaceImplementations (
307
- objects: ( implementations [ iface. name] ? . objects ?? [ ] ) + [ type]
308
- )
309
- }
310
- }
311
- }
312
-
313
- return implementations
314
- }
315
-
316
355
func typeMapReducer( typeMap: TypeMap , type: GraphQLType ) throws -> TypeMap {
317
356
var typeMap = typeMap
318
357
0 commit comments