10
10
//
11
11
//===----------------------------------------------------------------------===//
12
12
13
+ #if canImport(Darwin)
14
+ import Darwin
15
+
16
+ class ScopeGuard {
17
+ private var lock : os_unfair_lock
18
+ init ( ) {
19
+ self . lock = os_unfair_lock ( )
20
+ }
21
+ func withGuard< T> ( body: ( ) throws -> T ) rethrows -> T {
22
+ os_unfair_lock_lock ( & lock)
23
+ defer { os_unfair_lock_unlock ( & lock) }
24
+ return try body ( )
25
+ }
26
+ }
27
+
28
+ #elseif canImport(Glibc)
29
+ import Glibc
30
+
31
+ class ScopeGuard {
32
+ var lock : pthread_mutex_t
33
+ init ( ) {
34
+ self . lock = pthread_mutex_t ( )
35
+ pthread_mutex_init ( & self . lock, nil )
36
+ }
37
+ deinit {
38
+ pthread_mutex_destroy ( & self . lock)
39
+ }
40
+ func withGuard< T> ( body: ( ) throws -> T ) rethrows -> T {
41
+ pthread_mutex_lock ( & lock)
42
+ defer { pthread_mutex_unlock ( & lock) }
43
+ return try body ( )
44
+ }
45
+ }
46
+ #else
47
+ // FIXME: Support other platforms.
48
+
49
+ /// Dummy mutex that doesn't actually guard at all.
50
+ class ScopeGuard {
51
+ init ( ) { }
52
+ func withGuard< T> ( body: ( ) throws -> T ) rethrows -> T {
53
+ return try body ( )
54
+ }
55
+ }
56
+ #endif
57
+
13
58
public class SyntaxArena {
14
59
15
60
@_spi ( RawSyntax)
16
61
public typealias ParseTriviaFunction = ( _ source: SyntaxText , _ position: TriviaPosition ) -> [ RawTriviaPiece ]
17
62
63
+ /// Thread safe guard.
64
+ private let lock : ScopeGuard
65
+ private var singleThreadMode : Bool
66
+
18
67
/// Bump-pointer allocator for all "intern" methods.
19
68
private let allocator : BumpPtrAllocator
20
69
/// Source file buffer the Syntax tree represents.
@@ -30,6 +79,8 @@ public class SyntaxArena {
30
79
31
80
@_spi ( RawSyntax)
32
81
public init ( parseTriviaFunction: @escaping ParseTriviaFunction ) {
82
+ lock = ScopeGuard ( )
83
+ self . singleThreadMode = false
33
84
allocator = BumpPtrAllocator ( )
34
85
children = [ ]
35
86
sourceBuffer = . init( start: nil , count: 0 )
@@ -41,15 +92,32 @@ public class SyntaxArena {
41
92
self . init ( parseTriviaFunction: _defaultParseTriviaFunction ( _: _: ) )
42
93
}
43
94
95
+ private func withGuard< R> ( body: ( ) throws -> R ) rethrows -> R {
96
+ if self . singleThreadMode {
97
+ return try body ( )
98
+ } else {
99
+ return try self . lock. withGuard ( body: body)
100
+ }
101
+ }
102
+
103
+ public func assumingSingleThread< R> ( body: ( ) throws -> R ) rethrows -> R {
104
+ let oldValue = self . singleThreadMode
105
+ defer { self . singleThreadMode = oldValue }
106
+ self . singleThreadMode = true
107
+ return try body ( )
108
+ }
109
+
44
110
/// Copies a source buffer in to the memory this arena manages, and returns
45
111
/// the interned buffer.
46
112
///
47
113
/// The interned buffer is guaranteed to be null-terminated.
48
114
/// `contains(address _:)` is faster if the address is inside the memory
49
115
/// range this function returned.
50
116
public func internSourceBuffer( _ buffer: UnsafeBufferPointer < UInt8 > ) -> UnsafeBufferPointer < UInt8 > {
117
+ let allocated = lock. withGuard {
118
+ allocator. allocate ( UInt8 . self, count: buffer. count + /* for NULL */1 )
119
+ }
51
120
precondition ( sourceBuffer. baseAddress == nil , " SourceBuffer should only be set once. " )
52
- let allocated = allocator. allocate ( UInt8 . self, count: buffer. count + /* for NULL */1 )
53
121
_ = allocated. initialize ( from: buffer)
54
122
55
123
// NULL terminate.
@@ -69,20 +137,27 @@ public class SyntaxArena {
69
137
/// Allocates a buffer of `RawSyntax?` with the given count, then returns the
70
138
/// uninitlialized memory range as a `UnsafeMutableBufferPointer<RawSyntax?>`.
71
139
func allocateRawSyntaxBuffer( count: Int ) -> UnsafeMutableBufferPointer < RawSyntax ? > {
72
- return allocator. allocate ( RawSyntax ? . self, count: count)
140
+ return self . withGuard {
141
+ allocator. allocate ( RawSyntax ? . self, count: count)
142
+ }
73
143
}
74
144
75
145
/// Allcates a buffer of `RawTriviaPiece` with the given count, then returns
76
146
/// the uninitialized memory range as a `UnsafeMutableBufferPointer<RawTriviaPiece>`.
77
147
func allocateRawTriviaPieceBuffer(
78
- count: Int ) -> UnsafeMutableBufferPointer < RawTriviaPiece > {
79
- return allocator. allocate ( RawTriviaPiece . self, count: count)
148
+ count: Int
149
+ ) -> UnsafeMutableBufferPointer < RawTriviaPiece > {
150
+ return self . withGuard {
151
+ allocator. allocate ( RawTriviaPiece . self, count: count)
80
152
}
153
+ }
81
154
82
155
/// Allcates a buffer of `UInt8` with the given count, then returns the
83
156
/// uninitialized memory range as a `UnsafeMutableBufferPointer<UInt8>`.
84
157
func allocateTextBuffer( count: Int ) -> UnsafeMutableBufferPointer < UInt8 > {
85
- return allocator. allocate ( UInt8 . self, count: count)
158
+ return self . withGuard {
159
+ allocator. allocate ( UInt8 . self, count: count)
160
+ }
86
161
}
87
162
88
163
/// Copies the contents of a `SyntaxText` to the memory this arena manages,
@@ -114,7 +189,9 @@ public class SyntaxArena {
114
189
/// Copies a `RawSyntaxData` to the memory this arena manages, and retuns the
115
190
/// pointer to the destination.
116
191
func intern( _ value: RawSyntaxData ) -> UnsafePointer < RawSyntaxData > {
117
- let allocated = allocator. allocate ( RawSyntaxData . self, count: 1 ) . baseAddress!
192
+ let allocated = lock. withGuard {
193
+ allocator. allocate ( RawSyntaxData . self, count: 1 ) . baseAddress!
194
+ }
118
195
allocated. initialize ( to: value)
119
196
return UnsafePointer ( allocated)
120
197
}
@@ -128,21 +205,26 @@ public class SyntaxArena {
128
205
/// See also `RawSyntax.layout()`.
129
206
func addChild( _ arenaRef: SyntaxArenaRef ) {
130
207
if SyntaxArenaRef ( self ) == arenaRef { return }
131
-
132
208
let other = arenaRef. value
133
209
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)
210
+ other. withGuard {
211
+ self . withGuard {
212
+ precondition (
213
+ !self . hasParent,
214
+ " an arena can't have a new child once it's owned by other arenas " )
215
+
216
+ other. hasParent = true
217
+ children. insert ( other)
218
+ }
219
+ }
140
220
}
141
221
142
222
/// Recursively checks if this arena contains given `arena` as a descendant.
143
223
func contains( arena: SyntaxArena ) -> Bool {
144
- return children. contains { child in
145
- child === arena || child. contains ( arena: arena)
224
+ self . withGuard {
225
+ children. contains { child in
226
+ child === arena || child. contains ( arena: arena)
227
+ }
146
228
}
147
229
}
148
230
0 commit comments