Skip to content

Commit 1f2810b

Browse files
authored
Merge pull request #2440 from ahoppen/ahoppen/has-parent-atomic
Make `SyntaxArena.hasParent` an atomic boolean
2 parents 7c8b0d3 + 232c4b8 commit 1f2810b

File tree

5 files changed

+84
-10
lines changed

5 files changed

+84
-10
lines changed

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,9 @@ if (SWIFTSYNTAX_ENABLE_ASSERTIONS)
8888
)
8989
endif()
9090

91+
add_compile_definitions(
92+
$<$<COMPILE_LANGUAGE:Swift>:SWIFT_SYNTAX_BUILD_USING_CMAKE>
93+
)
94+
9195
add_subdirectory(Sources)
9296
add_subdirectory(cmake/modules)

Package.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ let package = Package(
3131
targets: [
3232
// MARK: - Internal helper targets
3333

34+
.target(
35+
name: "_AtomicBool"
36+
),
37+
3438
.target(
3539
name: "_InstructionCounter"
3640
),
@@ -116,7 +120,7 @@ let package = Package(
116120

117121
.target(
118122
name: "SwiftSyntax",
119-
dependencies: ["SwiftSyntax509", "SwiftSyntax510", "SwiftSyntax511"],
123+
dependencies: ["_AtomicBool", "SwiftSyntax509", "SwiftSyntax510", "SwiftSyntax511"],
120124
exclude: ["CMakeLists.txt"],
121125
swiftSettings: swiftSyntaxSwiftSettings
122126
),

Sources/SwiftSyntax/SyntaxArena.swift

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,23 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#if SWIFT_SYNTAX_BUILD_USING_CMAKE
14+
// The CMake bulid of swift-syntax does not build the _AtomicBool module because swift-syntax's CMake build is
15+
// Swift-only. Fake an `AtomicBool` type that is not actually atomic. This should be acceptable for the following
16+
// reasons:
17+
// - `AtomicBool` is only used for the `hasParent` assertion, so release compilers don't rely on it
18+
// - The compiler is single-threaded so it it is safe from race conditions on `AtomicBool`.
19+
fileprivate struct AtomicBool {
20+
var value: Bool
21+
22+
init(initialValue: Bool) {
23+
self.value = initialValue
24+
}
25+
}
26+
#else
27+
import _AtomicBool
28+
#endif
29+
1330
/// A syntax arena owns the memory for all syntax nodes within it.
1431
///
1532
/// The following is only relevant if you are accessing the raw syntax tree using
@@ -52,7 +69,7 @@ public class SyntaxArena {
5269
///
5370
/// - Important: This is only intended to be used for assertions to catch
5471
/// retain cycles in syntax arenas.
55-
var hasParent: Bool
72+
fileprivate var hasParent: AtomicBool
5673
#endif
5774

5875
/// Construct a new ``SyntaxArena`` in which syntax nodes can be allocated.
@@ -64,7 +81,7 @@ public class SyntaxArena {
6481
self.allocator = BumpPtrAllocator(initialSlabSize: slabSize)
6582
self.childRefs = []
6683
#if DEBUG || SWIFTSYNTAX_ENABLE_ASSERTIONS
67-
self.hasParent = false
84+
self.hasParent = AtomicBool(initialValue: false)
6885
#endif
6986
}
7087

@@ -141,7 +158,7 @@ public class SyntaxArena {
141158

142159
#if DEBUG || SWIFTSYNTAX_ENABLE_ASSERTIONS
143160
precondition(
144-
!self.hasParent,
161+
!self.hasParent.value,
145162
"an arena can't have a new child once it's owned by other arenas"
146163
)
147164
#endif
@@ -285,16 +302,12 @@ struct SyntaxArenaRef: Hashable, @unchecked Sendable {
285302
#if DEBUG || SWIFTSYNTAX_ENABLE_ASSERTIONS
286303
/// Accessor for ther underlying's `SyntaxArena.hasParent`
287304
var hasParent: Bool {
288-
// FIXME: This accesses mutable state across actor boundaries and is thus not concurrency-safe.
289-
// We should use an atomic for `hasParent` to make it concurrency safe.
290-
value.hasParent
305+
value.hasParent.value
291306
}
292307

293308
/// Sets the `SyntaxArena.hasParent` on the referenced arena.
294309
func setHasParent(_ newValue: Bool) {
295-
// FIXME: This modifies mutable state across actor boundaries and is thus not concurrency-safe.
296-
// We should use an atomic for `hasParent` to make it concurrency safe.
297-
value.hasParent = newValue
310+
value.hasParent.value = newValue
298311
}
299312
#endif
300313

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include <stdbool.h>
14+
15+
typedef struct {
16+
_Atomic(bool) value;
17+
} AtomicBool;
18+
19+
__attribute__((swift_name("AtomicBool.init(initialValue:)")))
20+
AtomicBool atomic_bool_create(bool initialValue);
21+
22+
__attribute__((swift_name("getter:AtomicBool.value(self:)")))
23+
bool atomic_bool_get(AtomicBool *atomic);
24+
25+
__attribute__((swift_name("setter:AtomicBool.value(self:_:)")))
26+
void atomic_bool_set(AtomicBool *atomic, bool newValue);

Sources/_AtomicBool/src/AtomicBool.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "AtomicBool.h"
14+
15+
AtomicBool atomic_bool_create(bool initialValue) {
16+
AtomicBool atomic;
17+
atomic.value = initialValue;
18+
return atomic;
19+
}
20+
21+
bool atomic_bool_get(AtomicBool *atomic) {
22+
return atomic->value;
23+
}
24+
25+
void atomic_bool_set(AtomicBool *atomic, bool newValue) {
26+
atomic->value = newValue;
27+
}

0 commit comments

Comments
 (0)