10
10
//
11
11
//===----------------------------------------------------------------------===//
12
12
13
+ #if canImport(Darwin)
14
+ import Darwin
15
+
16
+ struct ScopeGuard {
17
+ private let lock : os_unfair_lock_t
18
+ init ( allocator: BumpPtrAllocator ) {
19
+ let storage = allocator. allocate ( os_unfair_lock. self, count: 1 ) . baseAddress!
20
+ storage. initialize ( to: os_unfair_lock ( ) )
21
+ self . lock = os_unfair_lock_t ( storage)
22
+ }
23
+
24
+ func deinitialize( ) { }
25
+
26
+ func withGuard< T> ( body: ( ) throws -> T ) rethrows -> T {
27
+ os_unfair_lock_lock ( lock)
28
+ defer { os_unfair_lock_unlock ( lock) }
29
+ return try body ( )
30
+ }
31
+ }
32
+
33
+ #elseif canImport(Glibc)
34
+ import Glibc
35
+
36
+ struct ScopeGuard {
37
+ private let lock : UnsafeMutablePointer < pthread_mutex_t >
38
+ init ( allocator: BumpPtrAllocator ) {
39
+ let storage = allocator. allocate ( pthread_mutex_t. self, count: 1 ) . baseAddress!
40
+ storage. initialize ( to: pthread_mutex_t ( ) )
41
+ pthread_mutex_init ( storage, nil )
42
+ self . lock = storage
43
+ }
44
+ func deinitialize( ) {
45
+ pthread_mutex_destroy ( self . lock)
46
+ }
47
+ func withGuard< T> ( body: ( ) throws -> T ) rethrows -> T {
48
+ pthread_mutex_lock ( self . lock)
49
+ defer { pthread_mutex_unlock ( self . lock) }
50
+ return try body ( )
51
+ }
52
+ }
53
+
54
+ #else
55
+ // FIXME: Support other platforms.
56
+
57
+ /// Dummy mutex that doesn't actually guard at all.
58
+ class ScopeGuard {
59
+ init ( ) { }
60
+ func deinitialize( ) { }
61
+ func withGuard< T> ( body: ( ) throws -> T ) rethrows -> T {
62
+ return try body ( )
63
+ }
64
+ }
65
+ #endif
66
+
13
67
public class SyntaxArena {
14
68
15
69
@_spi ( RawSyntax)
16
70
public typealias ParseTriviaFunction = ( _ source: SyntaxText , _ position: TriviaPosition ) -> [ RawTriviaPiece ]
17
71
72
+ /// Thread safe guard.
73
+ private let lock : ScopeGuard
74
+ private var singleThreadMode : Bool
75
+
18
76
/// Bump-pointer allocator for all "intern" methods.
19
77
private let allocator : BumpPtrAllocator
20
78
/// Source file buffer the Syntax tree represents.
@@ -30,26 +88,53 @@ public class SyntaxArena {
30
88
31
89
@_spi ( RawSyntax)
32
90
public init ( parseTriviaFunction: @escaping ParseTriviaFunction ) {
33
- allocator = BumpPtrAllocator ( )
91
+ let allocator = BumpPtrAllocator ( )
92
+ self . lock = ScopeGuard ( allocator: allocator)
93
+ self . singleThreadMode = false
94
+ self . allocator = allocator
34
95
children = [ ]
35
96
sourceBuffer = . init( start: nil , count: 0 )
36
97
hasParent = false
37
98
self . parseTriviaFunction = parseTriviaFunction
38
99
}
39
100
101
+ deinit {
102
+ // NOTE: We don't make `ScopeGuard` a class and `deinit` in it to
103
+ // deinitialize it because the actual lock value is in `allocator`, and we
104
+ // want to make sure to deinitialize the lock before destroying the allocator.
105
+ lock. deinitialize ( )
106
+ }
107
+
40
108
public convenience init ( ) {
41
109
self . init ( parseTriviaFunction: _defaultParseTriviaFunction ( _: _: ) )
42
110
}
43
111
112
+ private func withGuard< R> ( _ body: ( ) throws -> R ) rethrows -> R {
113
+ if self . singleThreadMode {
114
+ return try body ( )
115
+ } else {
116
+ return try self . lock. withGuard ( body: body)
117
+ }
118
+ }
119
+
120
+ public func assumingSingleThread< R> ( body: ( ) throws -> R ) rethrows -> R {
121
+ let oldValue = self . singleThreadMode
122
+ defer { self . singleThreadMode = oldValue }
123
+ self . singleThreadMode = true
124
+ return try body ( )
125
+ }
126
+
44
127
/// Copies a source buffer in to the memory this arena manages, and returns
45
128
/// the interned buffer.
46
129
///
47
130
/// The interned buffer is guaranteed to be null-terminated.
48
131
/// `contains(address _:)` is faster if the address is inside the memory
49
132
/// range this function returned.
50
133
public func internSourceBuffer( _ buffer: UnsafeBufferPointer < UInt8 > ) -> UnsafeBufferPointer < UInt8 > {
134
+ let allocated = self . withGuard {
135
+ allocator. allocate ( UInt8 . self, count: buffer. count + /* for NULL */1 )
136
+ }
51
137
precondition ( sourceBuffer. baseAddress == nil , " SourceBuffer should only be set once. " )
52
- let allocated = allocator. allocate ( UInt8 . self, count: buffer. count + /* for NULL */1 )
53
138
_ = allocated. initialize ( from: buffer)
54
139
55
140
// NULL terminate.
@@ -69,20 +154,27 @@ public class SyntaxArena {
69
154
/// Allocates a buffer of `RawSyntax?` with the given count, then returns the
70
155
/// uninitlialized memory range as a `UnsafeMutableBufferPointer<RawSyntax?>`.
71
156
func allocateRawSyntaxBuffer( count: Int ) -> UnsafeMutableBufferPointer < RawSyntax ? > {
72
- return allocator. allocate ( RawSyntax ? . self, count: count)
157
+ return self . withGuard {
158
+ allocator. allocate ( RawSyntax ? . self, count: count)
159
+ }
73
160
}
74
161
75
162
/// Allcates a buffer of `RawTriviaPiece` with the given count, then returns
76
163
/// the uninitialized memory range as a `UnsafeMutableBufferPointer<RawTriviaPiece>`.
77
164
func allocateRawTriviaPieceBuffer(
78
- count: Int ) -> UnsafeMutableBufferPointer < RawTriviaPiece > {
79
- return allocator. allocate ( RawTriviaPiece . self, count: count)
165
+ count: Int
166
+ ) -> UnsafeMutableBufferPointer < RawTriviaPiece > {
167
+ return self . withGuard {
168
+ allocator. allocate ( RawTriviaPiece . self, count: count)
80
169
}
170
+ }
81
171
82
172
/// Allcates a buffer of `UInt8` with the given count, then returns the
83
173
/// uninitialized memory range as a `UnsafeMutableBufferPointer<UInt8>`.
84
174
func allocateTextBuffer( count: Int ) -> UnsafeMutableBufferPointer < UInt8 > {
85
- return allocator. allocate ( UInt8 . self, count: count)
175
+ return self . withGuard {
176
+ allocator. allocate ( UInt8 . self, count: count)
177
+ }
86
178
}
87
179
88
180
/// Copies the contents of a `SyntaxText` to the memory this arena manages,
@@ -114,7 +206,9 @@ public class SyntaxArena {
114
206
/// Copies a `RawSyntaxData` to the memory this arena manages, and retuns the
115
207
/// pointer to the destination.
116
208
func intern( _ value: RawSyntaxData ) -> UnsafePointer < RawSyntaxData > {
117
- let allocated = allocator. allocate ( RawSyntaxData . self, count: 1 ) . baseAddress!
209
+ let allocated = self . withGuard {
210
+ allocator. allocate ( RawSyntaxData . self, count: 1 ) . baseAddress!
211
+ }
118
212
allocated. initialize ( to: value)
119
213
return UnsafePointer ( allocated)
120
214
}
@@ -128,21 +222,26 @@ public class SyntaxArena {
128
222
/// See also `RawSyntax.layout()`.
129
223
func addChild( _ arenaRef: SyntaxArenaRef ) {
130
224
if SyntaxArenaRef ( self ) == arenaRef { return }
131
-
132
225
let other = arenaRef. value
133
226
134
- precondition (
135
- !self . hasParent,
136
- " an arena can't have a new child once it's owned by other arenas " )
137
-
138
- other. hasParent = true
139
- children. insert ( other)
227
+ other. withGuard {
228
+ self . withGuard {
229
+ precondition (
230
+ !self . hasParent,
231
+ " an arena can't have a new child once it's owned by other arenas " )
232
+
233
+ other. hasParent = true
234
+ children. insert ( other)
235
+ }
236
+ }
140
237
}
141
238
142
239
/// Recursively checks if this arena contains given `arena` as a descendant.
143
240
func contains( arena: SyntaxArena ) -> Bool {
144
- return children. contains { child in
145
- child === arena || child. contains ( arena: arena)
241
+ self . withGuard {
242
+ children. contains { child in
243
+ child === arena || child. contains ( arena: arena)
244
+ }
146
245
}
147
246
}
148
247
@@ -154,7 +253,7 @@ public class SyntaxArena {
154
253
public func contains( text: SyntaxText ) -> Bool {
155
254
return ( text. isEmpty ||
156
255
sourceBufferContains ( text. baseAddress!) ||
157
- allocator. contains ( address: text. baseAddress!) )
256
+ self . withGuard ( { allocator. contains ( address: text. baseAddress!) } ) )
158
257
}
159
258
160
259
@_spi ( RawSyntax)
0 commit comments