From c9a757b53414572781daa412de573388bd2f325a Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 20 May 2022 12:02:59 -0500 Subject: [PATCH 1/7] Make RegexComponent and component types Sendable --- Sources/_RegexParser/Regex/AST/AST.swift | 26 +++++----- Sources/_RegexParser/Regex/AST/Atom.swift | 40 +++++++------- .../_RegexParser/Regex/AST/Conditional.swift | 12 ++--- .../Regex/AST/CustomCharClass.swift | 10 ++-- Sources/_RegexParser/Regex/AST/Group.swift | 6 +-- .../Regex/AST/MatchingOptions.swift | 14 ++--- .../Regex/AST/Quantification.swift | 6 +-- .../Regex/Parse/SourceLocation.swift | 3 +- .../_RegexParser/Utility/MissingUnicode.swift | 10 ++-- Sources/_StringProcessing/Regex/Core.swift | 2 +- Sources/_StringProcessing/Regex/DSLTree.swift | 52 +++++++++---------- .../_CharacterClassModel.swift | 12 ++--- 12 files changed, 97 insertions(+), 96 deletions(-) diff --git a/Sources/_RegexParser/Regex/AST/AST.swift b/Sources/_RegexParser/Regex/AST/AST.swift index 44bc10828..5a5c53849 100644 --- a/Sources/_RegexParser/Regex/AST/AST.swift +++ b/Sources/_RegexParser/Regex/AST/AST.swift @@ -30,7 +30,7 @@ extension AST { extension AST { /// A node in the regex AST. public indirect enum Node: - Hashable, _TreeNode //, _ASTPrintable ASTValue, ASTAction + Hashable, _TreeNode, Sendable //, _ASTPrintable ASTValue, ASTAction { /// ... | ... | ... case alternation(Alternation) @@ -143,7 +143,7 @@ extension AST.Node { extension AST { - public struct Alternation: Hashable, _ASTNode { + public struct Alternation: Hashable, Sendable, _ASTNode { public let children: [AST.Node] public let pipes: [SourceLocation] @@ -162,7 +162,7 @@ extension AST { } } - public struct Concatenation: Hashable, _ASTNode { + public struct Concatenation: Hashable, Sendable, _ASTNode { public let children: [AST.Node] public let location: SourceLocation @@ -172,7 +172,7 @@ extension AST { } } - public struct Quote: Hashable, _ASTNode { + public struct Quote: Hashable, Sendable, _ASTNode { public let literal: String public let location: SourceLocation @@ -182,7 +182,7 @@ extension AST { } } - public struct Trivia: Hashable, _ASTNode { + public struct Trivia: Hashable, Sendable, _ASTNode { public let contents: String public let location: SourceLocation @@ -197,7 +197,7 @@ extension AST { } } - public struct Interpolation: Hashable, _ASTNode { + public struct Interpolation: Hashable, Sendable, _ASTNode { public let contents: String public let location: SourceLocation @@ -207,7 +207,7 @@ extension AST { } } - public struct Empty: Hashable, _ASTNode { + public struct Empty: Hashable, Sendable, _ASTNode { public let location: SourceLocation public init(_ location: SourceLocation) { @@ -219,15 +219,15 @@ extension AST { /// /// This is used to model a pattern which should /// not be matched against across varying scopes. - public struct AbsentFunction: Hashable, _ASTNode { - public enum Start: Hashable { + public struct AbsentFunction: Hashable, Sendable, _ASTNode { + public enum Start: Hashable, Sendable { /// `(?~|` case withPipe /// `(?~` case withoutPipe } - public enum Kind: Hashable { + public enum Kind: Hashable, Sendable { /// An absent repeater `(?~absent)`. This is equivalent to `(?~|absent|.*)` /// and therefore matches as long as the pattern `absent` is not matched. case repeater(AST.Node) @@ -261,8 +261,8 @@ extension AST { } } - public struct Reference: Hashable { - public enum Kind: Hashable { + public struct Reference: Hashable, Sendable { + public enum Kind: Hashable, Sendable { // \n \gn \g{n} \g \g'n' (?n) (?(n)... // Oniguruma: \k, \k'n' case absolute(Int) @@ -304,7 +304,7 @@ extension AST { } /// A set of global matching options in a regular expression literal. - public struct GlobalMatchingOptionSequence: Hashable { + public struct GlobalMatchingOptionSequence: Hashable, Sendable { public var options: [AST.GlobalMatchingOption] public init?(_ options: [AST.GlobalMatchingOption]) { diff --git a/Sources/_RegexParser/Regex/AST/Atom.swift b/Sources/_RegexParser/Regex/AST/Atom.swift index eba720f9b..e70c74a2a 100644 --- a/Sources/_RegexParser/Regex/AST/Atom.swift +++ b/Sources/_RegexParser/Regex/AST/Atom.swift @@ -10,7 +10,7 @@ //===----------------------------------------------------------------------===// extension AST { - public struct Atom: Hashable, _ASTNode { + public struct Atom: Hashable, Sendable, _ASTNode { public let kind: Kind public let location: SourceLocation @@ -19,7 +19,7 @@ extension AST { self.location = loc } - public enum Kind: Hashable { + public enum Kind: Hashable, Sendable { /// Just a character /// /// A, \*, \\, ... @@ -113,7 +113,7 @@ extension AST.Atom { } extension AST.Atom { - public struct Scalar: Hashable { + public struct Scalar: Hashable, Sendable { public var value: UnicodeScalar public var location: SourceLocation @@ -123,7 +123,7 @@ extension AST.Atom { } } - public struct ScalarSequence: Hashable { + public struct ScalarSequence: Hashable, Sendable { public var scalars: [Scalar] public var trivia: [AST.Trivia] @@ -145,7 +145,7 @@ extension AST.Atom { // Characters, character types, literals, etc., derived from // an escape sequence. - public enum EscapedBuiltin: Hashable { + public enum EscapedBuiltin: Hashable, Sendable { // TODO: better doc comments // Literal single characters @@ -374,7 +374,7 @@ extension AST.Atom.EscapedBuiltin { } extension AST.Atom { - public struct CharacterProperty: Hashable { + public struct CharacterProperty: Hashable, Sendable { public var kind: Kind /// Whether this is an inverted property e.g '\P{Ll}', '[:^ascii:]'. @@ -397,7 +397,7 @@ extension AST.Atom { } extension AST.Atom.CharacterProperty { - public enum Kind: Hashable { + public enum Kind: Hashable, Sendable { /// Matches any character, equivalent to Oniguruma's '\O'. case any @@ -448,7 +448,7 @@ extension AST.Atom.CharacterProperty { case pcreSpecial(PCRESpecialCategory) case onigurumaSpecial(OnigurumaSpecialProperty) - public enum MapKind: Hashable { + public enum MapKind: Hashable, Sendable { case lowercase case uppercase case titlecase @@ -456,7 +456,7 @@ extension AST.Atom.CharacterProperty { } // TODO: erm, separate out or fold into something? splat it in? - public enum PCRESpecialCategory: String, Hashable { + public enum PCRESpecialCategory: String, Hashable, Sendable { case alphanumeric = "Xan" case posixSpace = "Xps" case perlSpace = "Xsp" @@ -467,7 +467,7 @@ extension AST.Atom.CharacterProperty { extension AST.Atom { /// Anchors and other built-in zero-width assertions. - public enum AssertionKind: String { + public enum AssertionKind: String, Hashable, Sendable { /// \A case startOfSubject = #"\A"# @@ -527,10 +527,10 @@ extension AST.Atom { } extension AST.Atom { - public enum Callout: Hashable { + public enum Callout: Hashable, Sendable { /// A PCRE callout written `(?C...)` - public struct PCRE: Hashable { - public enum Argument: Hashable { + public struct PCRE: Hashable, Sendable { + public enum Argument: Hashable, Sendable { case number(Int) case string(String) } @@ -546,8 +546,8 @@ extension AST.Atom { } /// A named Oniguruma callout written `(*name[tag]{args, ...})` - public struct OnigurumaNamed: Hashable { - public struct ArgList: Hashable { + public struct OnigurumaNamed: Hashable, Sendable { + public struct ArgList: Hashable, Sendable { public var leftBrace: SourceLocation public var args: [AST.Located] public var rightBrace: SourceLocation @@ -577,8 +577,8 @@ extension AST.Atom { } /// An Oniguruma callout 'of contents', written `(?{...}[tag]D)` - public struct OnigurumaOfContents: Hashable { - public enum Direction: Hashable { + public struct OnigurumaOfContents: Hashable, Sendable { + public enum Direction: Hashable, Sendable { case inProgress // > (the default) case inRetraction // < case both // X @@ -625,7 +625,7 @@ extension AST.Atom { extension AST.Atom.Callout { /// A tag specifier `[...]` that can appear in an Oniguruma callout. - public struct OnigurumaTag: Hashable { + public struct OnigurumaTag: Hashable, Sendable { public var leftBracket: SourceLocation public var name: AST.Located public var rightBracket: SourceLocation @@ -643,8 +643,8 @@ extension AST.Atom.Callout { } extension AST.Atom { - public struct BacktrackingDirective: Hashable { - public enum Kind: Hashable { + public struct BacktrackingDirective: Hashable, Sendable { + public enum Kind: Hashable, Sendable { /// (*ACCEPT) case accept diff --git a/Sources/_RegexParser/Regex/AST/Conditional.swift b/Sources/_RegexParser/Regex/AST/Conditional.swift index c382a25b6..987629732 100644 --- a/Sources/_RegexParser/Regex/AST/Conditional.swift +++ b/Sources/_RegexParser/Regex/AST/Conditional.swift @@ -10,7 +10,7 @@ //===----------------------------------------------------------------------===// extension AST { - public struct Conditional: Hashable, _ASTNode { + public struct Conditional: Hashable, Sendable, _ASTNode { public var location: SourceLocation public var condition: Condition @@ -32,8 +32,8 @@ extension AST { } extension AST.Conditional { - public struct Condition: Hashable { - public enum Kind: Hashable { + public struct Condition: Hashable, Sendable { + public enum Kind: Hashable, Sendable { /// Check to see if a certain group was matched. case groupMatched(AST.Reference) @@ -65,7 +65,7 @@ extension AST.Conditional { } extension AST.Conditional.Condition { - public struct PCREVersionNumber: Hashable { + public struct PCREVersionNumber: Hashable, Sendable { public var major: Int public var minor: Int public var location: SourceLocation @@ -76,8 +76,8 @@ extension AST.Conditional.Condition { self.location = location } } - public struct PCREVersionCheck: Hashable { - public enum Kind: Hashable { + public struct PCREVersionCheck: Hashable, Sendable { + public enum Kind: Hashable, Sendable { case equal, greaterThanOrEqual } public var kind: AST.Located diff --git a/Sources/_RegexParser/Regex/AST/CustomCharClass.swift b/Sources/_RegexParser/Regex/AST/CustomCharClass.swift index d28490f25..3bb816b84 100644 --- a/Sources/_RegexParser/Regex/AST/CustomCharClass.swift +++ b/Sources/_RegexParser/Regex/AST/CustomCharClass.swift @@ -11,7 +11,7 @@ extension AST { - public struct CustomCharacterClass: Hashable { + public struct CustomCharacterClass: Hashable, Sendable { public var start: Located public var members: [Member] @@ -27,7 +27,7 @@ extension AST { self.location = sr } - public enum Member: Hashable { + public enum Member: Hashable, Sendable { /// A nested custom character class `[[ab][cd]]` case custom(CustomCharacterClass) @@ -47,7 +47,7 @@ extension AST { /// A binary operator applied to sets of members `abc&&def` case setOperation([Member], Located, [Member]) } - public struct Range: Hashable { + public struct Range: Hashable, Sendable { public var lhs: Atom public var dashLoc: SourceLocation public var rhs: Atom @@ -58,12 +58,12 @@ extension AST { self.rhs = rhs } } - public enum SetOp: String, Hashable { + public enum SetOp: String, Hashable, Sendable { case subtraction = "--" case intersection = "&&" case symmetricDifference = "~~" } - public enum Start: String { + public enum Start: String, Hashable, Sendable { case normal = "[" case inverted = "[^" } diff --git a/Sources/_RegexParser/Regex/AST/Group.swift b/Sources/_RegexParser/Regex/AST/Group.swift index 6fd46abe7..fb8105047 100644 --- a/Sources/_RegexParser/Regex/AST/Group.swift +++ b/Sources/_RegexParser/Regex/AST/Group.swift @@ -10,7 +10,7 @@ //===----------------------------------------------------------------------===// extension AST { - public struct Group: Hashable { + public struct Group: Hashable, Sendable { public let kind: Located public let child: AST.Node @@ -24,7 +24,7 @@ extension AST { self.location = r } - public enum Kind: Hashable { + public enum Kind: Hashable, Sendable { // (...) case capture @@ -116,7 +116,7 @@ extension AST.Group.Kind { } extension AST.Group { - public struct BalancedCapture: Hashable { + public struct BalancedCapture: Hashable, Sendable { /// The name of the group, or nil if the group has no name. public var name: AST.Located? diff --git a/Sources/_RegexParser/Regex/AST/MatchingOptions.swift b/Sources/_RegexParser/Regex/AST/MatchingOptions.swift index d3dbc1666..072d6cfaa 100644 --- a/Sources/_RegexParser/Regex/AST/MatchingOptions.swift +++ b/Sources/_RegexParser/Regex/AST/MatchingOptions.swift @@ -11,8 +11,8 @@ extension AST { /// An option, written in source, that changes matching semantics. - public struct MatchingOption: Hashable { - public enum Kind { + public struct MatchingOption: Hashable, Sendable { + public enum Kind: Hashable, Sendable { // PCRE options case caseInsensitive // i case allowDuplicateGroupNames // J @@ -84,7 +84,7 @@ extension AST { } /// A sequence of matching options, written in source. - public struct MatchingOptionSequence: Hashable { + public struct MatchingOptionSequence: Hashable, Sendable { /// If the sequence starts with a caret '^', its source location, or nil /// otherwise. If this is set, it indicates that all the matching options /// are unset, except the ones in `adding`. @@ -143,10 +143,10 @@ extension AST { /// Unlike `MatchingOptionSequence`, /// these options must appear at the start of the pattern, /// and they apply to the entire pattern. - public struct GlobalMatchingOption: _ASTNode, Hashable { + public struct GlobalMatchingOption: _ASTNode, Hashable, Sendable { /// Determines the definition of a newline for the '.' character class and /// when parsing end-of-line comments. - public enum NewlineMatching: Hashable { + public enum NewlineMatching: Hashable, Sendable { /// (*CR*) case carriageReturnOnly @@ -166,14 +166,14 @@ extension AST { case nulCharacter } /// Determines what `\R` matches. - public enum NewlineSequenceMatching: Hashable { + public enum NewlineSequenceMatching: Hashable, Sendable { /// (*BSR_ANYCRLF) case anyCarriageReturnOrLinefeed /// (*BSR_UNICODE) case anyUnicode } - public enum Kind: Hashable { + public enum Kind: Hashable, Sendable { /// (*LIMIT_DEPTH=d) case limitDepth(Located) diff --git a/Sources/_RegexParser/Regex/AST/Quantification.swift b/Sources/_RegexParser/Regex/AST/Quantification.swift index c6d4f0101..3d2835b0c 100644 --- a/Sources/_RegexParser/Regex/AST/Quantification.swift +++ b/Sources/_RegexParser/Regex/AST/Quantification.swift @@ -10,7 +10,7 @@ //===----------------------------------------------------------------------===// extension AST { - public struct Quantification: Hashable { + public struct Quantification: Hashable, Sendable { public let amount: Located public let kind: Located @@ -36,7 +36,7 @@ extension AST { self.trivia = trivia } - public enum Amount: Hashable { + public enum Amount: Hashable, Sendable { case zeroOrMore // * case oneOrMore // + case zeroOrOne // ? @@ -46,7 +46,7 @@ extension AST { case range(Located, Located) // {n,m} } - public enum Kind: String, Hashable { + public enum Kind: String, Hashable, Sendable { case eager = "" case reluctant = "?" case possessive = "+" diff --git a/Sources/_RegexParser/Regex/Parse/SourceLocation.swift b/Sources/_RegexParser/Regex/Parse/SourceLocation.swift index eb51643bd..ab3aba0e8 100644 --- a/Sources/_RegexParser/Regex/Parse/SourceLocation.swift +++ b/Sources/_RegexParser/Regex/Parse/SourceLocation.swift @@ -11,7 +11,7 @@ extension Source { /// The location in the input of a parsed entity, presented as a region over the input - public struct Location: Hashable { + public struct Location: Hashable, Sendable { public var range: Range public var start: Source.Position { range.lowerBound } @@ -114,6 +114,7 @@ extension AST { } extension AST.Located: Equatable where T: Equatable {} extension AST.Located: Hashable where T: Hashable {} +extension AST.Located: Sendable where T: Sendable {} extension Source.LocatedError: CustomStringConvertible { public var description: String { diff --git a/Sources/_RegexParser/Utility/MissingUnicode.swift b/Sources/_RegexParser/Utility/MissingUnicode.swift index 6cd243f43..4dd628675 100644 --- a/Sources/_RegexParser/Utility/MissingUnicode.swift +++ b/Sources/_RegexParser/Utility/MissingUnicode.swift @@ -19,7 +19,7 @@ extension Unicode { // other script types. /// Character script types. - public enum Script: String, Hashable { + public enum Script: String, Hashable, Sendable { case adlam = "Adlam" case ahom = "Ahom" case anatolianHieroglyphs = "Anatolian_Hieroglyphs" @@ -187,7 +187,7 @@ extension Unicode { /// POSIX character properties not already covered by general categories or /// binary properties. - public enum POSIXProperty: String, Hashable { + public enum POSIXProperty: String, Hashable, Sendable { case alnum = "alnum" case blank = "blank" case graph = "graph" @@ -204,7 +204,7 @@ extension Unicode { /// Unicode.GeneralCategory + cases for "meta categories" such as "L", which /// encompasses Lu | Ll | Lt | Lm | Lo. - public enum ExtendedGeneralCategory: String, Hashable { + public enum ExtendedGeneralCategory: String, Hashable, Sendable { case other = "C" case control = "Cc" case format = "Cf" @@ -254,7 +254,7 @@ extension Unicode { /// A list of Unicode properties that can either be true or false. /// /// https://www.unicode.org/Public/UCD/latest/ucd/PropertyAliases.txt - public enum BinaryProperty: String, Hashable { + public enum BinaryProperty: String, Hashable, Sendable { case asciiHexDigit = "ASCII_Hex_Digit" case alphabetic = "Alphabetic" case bidiControl = "Bidi_Control" @@ -329,7 +329,7 @@ extension Unicode { // property. /// Oniguruma properties that are not covered by Unicode spellings. -public enum OnigurumaSpecialProperty: String, Hashable { +public enum OnigurumaSpecialProperty: String, Hashable, Sendable { case inBasicLatin = "In_Basic_Latin" case inLatin1Supplement = "In_Latin_1_Supplement" case inLatinExtendedA = "In_Latin_Extended_A" diff --git a/Sources/_StringProcessing/Regex/Core.swift b/Sources/_StringProcessing/Regex/Core.swift index 5d2101afe..d466976b1 100644 --- a/Sources/_StringProcessing/Regex/Core.swift +++ b/Sources/_StringProcessing/Regex/Core.swift @@ -14,7 +14,7 @@ /// A type that represents a regular expression. @available(SwiftStdlib 5.7, *) -public protocol RegexComponent { +public protocol RegexComponent: Sendable { associatedtype RegexOutput var regex: Regex { get } } diff --git a/Sources/_StringProcessing/Regex/DSLTree.swift b/Sources/_StringProcessing/Regex/DSLTree.swift index 8ca6dce8d..c3842a7b9 100644 --- a/Sources/_StringProcessing/Regex/DSLTree.swift +++ b/Sources/_StringProcessing/Regex/DSLTree.swift @@ -12,7 +12,7 @@ @_implementationOnly import _RegexParser @_spi(RegexBuilder) -public struct DSLTree { +public struct DSLTree: Sendable { var root: Node init(_ r: Node) { @@ -22,7 +22,7 @@ public struct DSLTree { extension DSLTree { @_spi(RegexBuilder) - public indirect enum Node { + public indirect enum Node: Sendable { /// Matches each node in order. /// /// ... | ... | ... @@ -102,7 +102,7 @@ extension DSLTree { extension DSLTree { @_spi(RegexBuilder) - public enum QuantificationKind { + public enum QuantificationKind: Sendable { /// The default quantification kind, as set by options. case `default` /// An explicitly chosen kind, overriding any options. @@ -120,7 +120,7 @@ extension DSLTree { } @_spi(RegexBuilder) - public struct CustomCharacterClass { + public struct CustomCharacterClass: Sendable { var members: [Member] var isInverted: Bool @@ -153,7 +153,7 @@ extension DSLTree { } @_spi(RegexBuilder) - public enum Member { + public enum Member: Sendable { case atom(Atom) case range(Atom, Atom) case custom(CustomCharacterClass) @@ -169,7 +169,7 @@ extension DSLTree { } @_spi(RegexBuilder) - public enum Atom { + public enum Atom: Sendable { case char(Character) case scalar(Unicode.Scalar) case any @@ -224,21 +224,21 @@ extension Unicode.GeneralCategory { // CollectionConsumer @_spi(RegexBuilder) -public typealias _ConsumerInterface = ( +public typealias _ConsumerInterface = @Sendable ( String, Range ) throws -> String.Index? // Type producing consume // TODO: better name @_spi(RegexBuilder) -public typealias _MatcherInterface = ( +public typealias _MatcherInterface = @Sendable ( String, String.Index, Range ) throws -> (String.Index, Any)? // Character-set (post grapheme segmentation) @_spi(RegexBuilder) public typealias _CharacterPredicateInterface = ( - (Character) -> Bool + @Sendable (Character) -> Bool ) /* @@ -375,7 +375,7 @@ extension DSLTree.Node { } @_spi(RegexBuilder) -public struct ReferenceID: Hashable, Equatable { +public struct ReferenceID: Hashable, Sendable { private static var counter: Int = 0 var base: Int @@ -386,10 +386,10 @@ public struct ReferenceID: Hashable, Equatable { } @_spi(RegexBuilder) -public struct CaptureTransform: Hashable, CustomStringConvertible { - public enum Closure { - case failable((Substring) throws -> Any?) - case nonfailable((Substring) throws -> Any) +public struct CaptureTransform: Hashable, CustomStringConvertible, Sendable { + public enum Closure: Sendable { + case failable(@Sendable (Substring) throws -> Any?) + case nonfailable(@Sendable (Substring) throws -> Any) } public let resultType: Any.Type public let closure: Closure @@ -401,14 +401,14 @@ public struct CaptureTransform: Hashable, CustomStringConvertible { public init( resultType: Any.Type, - _ closure: @escaping (Substring) throws -> Any + _ closure: @Sendable @escaping (Substring) throws -> Any ) { self.init(resultType: resultType, closure: .nonfailable(closure)) } public init( resultType: Any.Type, - _ closure: @escaping (Substring) throws -> Any? + _ closure: @Sendable @escaping (Substring) throws -> Any? ) { self.init(resultType: resultType, closure: .failable(closure)) } @@ -570,7 +570,7 @@ extension DSLTree { @_spi(RegexBuilder) public enum _AST { @_spi(RegexBuilder) - public struct GroupKind { + public struct GroupKind: Sendable { internal var ast: AST.Group.Kind public static var atomicNonCapturing: Self { @@ -585,12 +585,12 @@ extension DSLTree { } @_spi(RegexBuilder) - public struct ConditionKind { + public struct ConditionKind: Sendable { internal var ast: AST.Conditional.Condition.Kind } @_spi(RegexBuilder) - public struct QuantificationKind { + public struct QuantificationKind: Sendable { internal var ast: AST.Quantification.Kind public static var eager: Self { @@ -605,7 +605,7 @@ extension DSLTree { } @_spi(RegexBuilder) - public struct QuantificationAmount { + public struct QuantificationAmount: Sendable { internal var ast: AST.Quantification.Amount public static var zeroOrMore: Self { @@ -632,17 +632,17 @@ extension DSLTree { } @_spi(RegexBuilder) - public struct ASTNode { + public struct ASTNode: Sendable { internal var ast: AST.Node } @_spi(RegexBuilder) - public struct AbsentFunction { + public struct AbsentFunction: Sendable { internal var ast: AST.AbsentFunction } @_spi(RegexBuilder) - public struct AssertionKind { + public struct AssertionKind: Sendable { internal var ast: AST.Atom.AssertionKind public static func startOfSubject(_ inverted: Bool = false) -> Self { @@ -676,17 +676,17 @@ extension DSLTree { } @_spi(RegexBuilder) - public struct Reference { + public struct Reference: Sendable { internal var ast: AST.Reference } @_spi(RegexBuilder) - public struct MatchingOptionSequence { + public struct MatchingOptionSequence: Sendable { internal var ast: AST.MatchingOptionSequence } @_spi(RegexBuilder) - public struct Atom { + public struct Atom: Sendable { internal var ast: AST.Atom } } diff --git a/Sources/_StringProcessing/_CharacterClassModel.swift b/Sources/_StringProcessing/_CharacterClassModel.swift index 85dd1ca37..e026c4b11 100644 --- a/Sources/_StringProcessing/_CharacterClassModel.swift +++ b/Sources/_StringProcessing/_CharacterClassModel.swift @@ -16,7 +16,7 @@ // of parsing or to store in an AST @_spi(RegexBuilder) -public struct _CharacterClassModel: Hashable { +public struct _CharacterClassModel: Hashable, Sendable { /// The actual character class to match. var cc: Representation @@ -28,7 +28,7 @@ public struct _CharacterClassModel: Hashable { var isInverted: Bool = false // TODO: Split out builtin character classes into their own type? - public enum Representation: Hashable { + public enum Representation: Hashable, Sendable { /// Any character case any /// Any grapheme cluster @@ -54,14 +54,14 @@ public struct _CharacterClassModel: Hashable { case custom([CharacterSetComponent]) } - public enum SetOperator: Hashable { + public enum SetOperator: Hashable, Sendable { case subtraction case intersection case symmetricDifference } /// A binary set operation that forms a character class component. - public struct SetOperation: Hashable { + public struct SetOperation: Hashable, Sendable { var lhs: CharacterSetComponent var op: SetOperator var rhs: CharacterSetComponent @@ -78,7 +78,7 @@ public struct _CharacterClassModel: Hashable { } } - public enum CharacterSetComponent: Hashable { + public enum CharacterSetComponent: Hashable, Sendable { case character(Character) case range(ClosedRange) @@ -120,7 +120,7 @@ public struct _CharacterClassModel: Hashable { } } - enum MatchLevel { + enum MatchLevel: Hashable, Sendable { /// Match at the extended grapheme cluster level. case graphemeCluster /// Match at the Unicode scalar level. From 46d7cdebca23f2a4a261757d22eb6dae9522545c Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 20 May 2022 12:13:11 -0500 Subject: [PATCH 2/7] Make the Regex type Sendable Regex stores a `Program` instance, which lazily lowers the DSLTree into a compiled program. In unprotected, this lazy compilation is under concurrency. This change uses UnsafeAtomicLazyReference from `swift-atomics` as storage for the lowered program, which makes storing the compiled program an atomic operation. --- Package.swift | 7 +++++- Sources/_StringProcessing/Regex/Core.swift | 25 +++++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Package.swift b/Package.swift index 526d1d0e1..ea86efe52 100644 --- a/Package.swift +++ b/Package.swift @@ -44,6 +44,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"), + .package(url: "https://github.com/apple/swift-atomics", from: "1.0.0"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -62,7 +63,11 @@ let package = Package( dependencies: []), .target( name: "_StringProcessing", - dependencies: ["_RegexParser", "_CUnicode"], + dependencies: [ + .product(name: "Atomics", package: "swift-atomics"), + "_RegexParser", + "_CUnicode", + ], swiftSettings: publicStdlibSettings), .target( name: "RegexBuilder", diff --git a/Sources/_StringProcessing/Regex/Core.swift b/Sources/_StringProcessing/Regex/Core.swift index d466976b1..7afd35458 100644 --- a/Sources/_StringProcessing/Regex/Core.swift +++ b/Sources/_StringProcessing/Regex/Core.swift @@ -10,7 +10,7 @@ //===----------------------------------------------------------------------===// @_implementationOnly import _RegexParser - +@_implementationOnly import Atomics /// A type that represents a regular expression. @available(SwiftStdlib 5.7, *) @@ -68,11 +68,12 @@ extension Regex { } } + @available(SwiftStdlib 5.7, *) extension Regex { /// A program representation that caches any lowered representation for /// execution. - internal class Program { + internal final class Program: @unchecked Sendable { /// The underlying IR. /// /// FIXME: If Regex is the unit of composition, then it should be a Node instead, @@ -80,8 +81,22 @@ extension Regex { /// likely, compilation/caching. let tree: DSLTree + private final class ProgramBox { + let value: MEProgram + init(_ value: MEProgram) { self.value = value } + } + + private var _loweredProgramStorage: UnsafeAtomicLazyReference + = .create() + /// The program for execution with the matching engine. - lazy private(set) var loweredProgram = try! Compiler(tree: tree).emit() + var loweredProgram: MEProgram { + if let lowered = _loweredProgramStorage.load() { + return lowered.value + } + let lowered = try! ProgramBox(Compiler(tree: tree).emit()) + return _loweredProgramStorage.storeIfNilThenLoad(lowered).value + } init(ast: AST) { self.tree = ast.dslTree @@ -90,6 +105,10 @@ extension Regex { init(tree: DSLTree) { self.tree = tree } + + deinit { + _loweredProgramStorage.destroy() + } } /// The set of matching options that applies to the start of this regex. From d5e6386a96114453eb5530e2251e7d70b48d89cb Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Sun, 29 May 2022 22:49:12 -0500 Subject: [PATCH 3/7] Subset out the used portions of swift-atomics --- Package.swift | 9 +- Sources/_LazyAtomic/AtomicValue.swift | 37 +++++ Sources/_LazyAtomic/Int.swift | 69 ++++++++ Sources/_LazyAtomic/Optional.swift | 21 +++ Sources/_LazyAtomic/Unmanaged.swift | 152 ++++++++++++++++++ .../UnsafeAtomicLazyReference.swift | 95 +++++++++++ Sources/_LazyAtomicShims/_LazyAtomicShims.c | 12 ++ .../include/_LazyAtomicShims.h | 98 +++++++++++ Sources/_StringProcessing/Regex/Core.swift | 2 +- 9 files changed, 492 insertions(+), 3 deletions(-) create mode 100644 Sources/_LazyAtomic/AtomicValue.swift create mode 100644 Sources/_LazyAtomic/Int.swift create mode 100644 Sources/_LazyAtomic/Optional.swift create mode 100644 Sources/_LazyAtomic/Unmanaged.swift create mode 100644 Sources/_LazyAtomic/UnsafeAtomicLazyReference.swift create mode 100644 Sources/_LazyAtomicShims/_LazyAtomicShims.c create mode 100644 Sources/_LazyAtomicShims/include/_LazyAtomicShims.h diff --git a/Package.swift b/Package.swift index 74a2053ae..f18833199 100644 --- a/Package.swift +++ b/Package.swift @@ -44,7 +44,6 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"), - .package(url: "https://github.com/apple/swift-atomics", from: "1.0.0"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -61,12 +60,18 @@ let package = Package( .target( name: "_CUnicode", dependencies: []), + .target( + name: "_LazyAtomicShims", + dependencies: []), + .target( + name: "_LazyAtomic", + dependencies: ["_LazyAtomicShims"]), .target( name: "_StringProcessing", dependencies: [ - .product(name: "Atomics", package: "swift-atomics"), "_RegexParser", "_CUnicode", + "_LazyAtomic", ], swiftSettings: publicStdlibSettings), .target( diff --git a/Sources/_LazyAtomic/AtomicValue.swift b/Sources/_LazyAtomic/AtomicValue.swift new file mode 100644 index 000000000..5175ee3ec --- /dev/null +++ b/Sources/_LazyAtomic/AtomicValue.swift @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +// THIS FILE IS PART OF A SUBSET OF https://github.com/apple/swift-atomics/ + +public protocol AtomicValue { + associatedtype AtomicRepresentation: AtomicStorage + where AtomicRepresentation.Value == Self +} + +public protocol AtomicStorage { + associatedtype Value + + init(_ value: __owned Value) + + __consuming func dispose() -> Value + + @_semantics("atomics.requires_constant_orderings") + static func atomicLoad( + at pointer: UnsafeMutablePointer + ) -> Value + + @_semantics("atomics.requires_constant_orderings") + static func atomicCompareExchange( + expected: Value, + desired: __owned Value, + at pointer: UnsafeMutablePointer + ) -> (exchanged: Bool, original: Value) +} diff --git a/Sources/_LazyAtomic/Int.swift b/Sources/_LazyAtomic/Int.swift new file mode 100644 index 000000000..3915f240e --- /dev/null +++ b/Sources/_LazyAtomic/Int.swift @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +// THIS FILE IS PART OF A SUBSET OF https://github.com/apple/swift-atomics/ + +import _LazyAtomicShims + +extension Int: AtomicValue { + @frozen + public struct AtomicRepresentation { + public typealias Value = Int + + @usableFromInline + var _storage: _AtomicIntStorage + + @inline(__always) @_alwaysEmitIntoClient + public init(_ value: Value) { + self._storage = _sa_prepare_Int(value) + } + + @inline(__always) @_alwaysEmitIntoClient + public func dispose() -> Value { + return _sa_dispose_Int(_storage) + } + } +} + +extension UnsafeMutablePointer +where Pointee == Int.AtomicRepresentation { + @inlinable @inline(__always) + internal var _extract: UnsafeMutablePointer<_AtomicIntStorage> { + // `Int` is layout-compatible with its only stored property. + return UnsafeMutableRawPointer(self) + .assumingMemoryBound(to: _AtomicIntStorage.self) + } +} + +extension Int.AtomicRepresentation: AtomicStorage { + @_semantics("atomics.requires_constant_orderings") + @_transparent @_alwaysEmitIntoClient + public static func atomicLoad( + at pointer: UnsafeMutablePointer + ) -> Value { + _sa_load_acquire_Int(pointer._extract) + } + + @_semantics("atomics.requires_constant_orderings") + @_transparent @_alwaysEmitIntoClient + public static func atomicCompareExchange( + expected: Value, + desired: Value, + at pointer: UnsafeMutablePointer + ) -> (exchanged: Bool, original: Value) { + var expected = expected + let exchanged: Bool + exchanged = _sa_cmpxchg_strong_acq_rel_acquire_Int( + pointer._extract, + &expected, desired) + return (exchanged, expected) + } +} diff --git a/Sources/_LazyAtomic/Optional.swift b/Sources/_LazyAtomic/Optional.swift new file mode 100644 index 000000000..dd4df095d --- /dev/null +++ b/Sources/_LazyAtomic/Optional.swift @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +// THIS FILE IS PART OF A SUBSET OF https://github.com/apple/swift-atomics/ + +public protocol AtomicOptionalWrappable: AtomicValue { + associatedtype AtomicOptionalRepresentation: AtomicStorage + where AtomicOptionalRepresentation.Value == Self? +} + +extension Optional: AtomicValue where Wrapped: AtomicOptionalWrappable { + public typealias AtomicRepresentation = Wrapped.AtomicOptionalRepresentation +} diff --git a/Sources/_LazyAtomic/Unmanaged.swift b/Sources/_LazyAtomic/Unmanaged.swift new file mode 100644 index 000000000..9286c0dff --- /dev/null +++ b/Sources/_LazyAtomic/Unmanaged.swift @@ -0,0 +1,152 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +// THIS FILE IS PART OF A SUBSET OF https://github.com/apple/swift-atomics/ + +extension Unmanaged: AtomicValue { + @frozen + public struct AtomicRepresentation { + public typealias Value = Unmanaged + @usableFromInline internal typealias Storage = Int.AtomicRepresentation + + @usableFromInline + internal let _storage: Storage + + @inline(__always) @_alwaysEmitIntoClient + public init(_ value: Value) { + self._storage = .init(Self._encode(value)) + } + + @inline(__always) @_alwaysEmitIntoClient + public func dispose() -> Value { + Self._decode(_storage.dispose()) + } + } +} + +extension Unmanaged.AtomicRepresentation { + @_transparent @_alwaysEmitIntoClient + @usableFromInline + internal static func _extract( + _ ptr: UnsafeMutablePointer + ) -> UnsafeMutablePointer { + // `Self` is layout-compatible with its only stored property. + return UnsafeMutableRawPointer(ptr) + .assumingMemoryBound(to: Storage.self) + } + + @_transparent @_alwaysEmitIntoClient + internal static func _decode(_ bitPattern: Int) -> Value { + return Unmanaged.fromOpaque(UnsafeRawPointer(bitPattern: bitPattern)!) + } + + @_transparent @_alwaysEmitIntoClient + internal static func _encode(_ value: Value) -> Int { + return Int(bitPattern: value.toOpaque()) + } +} + +extension Unmanaged.AtomicRepresentation: AtomicStorage { + @_semantics("atomics.requires_constant_orderings") + @_transparent @_alwaysEmitIntoClient + public static func atomicLoad( + at pointer: UnsafeMutablePointer + ) -> Value { + let encoded = Storage.atomicLoad(at: _extract(pointer)) + return _decode(encoded) + } + + @_semantics("atomics.requires_constant_orderings") + @_transparent @_alwaysEmitIntoClient + public static func atomicCompareExchange( + expected: Value, + desired: Value, + at pointer: UnsafeMutablePointer + ) -> (exchanged: Bool, original: Value) { + let (exchanged, original) = Storage.atomicCompareExchange( + expected: _encode(expected), + desired: _encode(desired), + at: _extract(pointer)) + return (exchanged, _decode(original)) + } +} + +extension Unmanaged: AtomicOptionalWrappable { + @frozen + public struct AtomicOptionalRepresentation { + public typealias Value = Unmanaged? + @usableFromInline internal typealias Storage = Int.AtomicRepresentation + + @usableFromInline + internal let _storage: Storage + + @inline(__always) @_alwaysEmitIntoClient + public init(_ value: Value) { + self._storage = .init(Self._encode(value)) + } + + @inline(__always) @_alwaysEmitIntoClient + public func dispose() -> Value { + Self._decode(_storage.dispose()) + } + } +} + +extension Unmanaged.AtomicOptionalRepresentation { + @_transparent @_alwaysEmitIntoClient + @usableFromInline + internal static func _extract( + _ ptr: UnsafeMutablePointer + ) -> UnsafeMutablePointer { + // `Self` is layout-compatible with its only stored property. + return UnsafeMutableRawPointer(ptr) + .assumingMemoryBound(to: Storage.self) + } + + @_transparent @_alwaysEmitIntoClient + internal static func _decode(_ bitPattern: Int) -> Value { + guard let opaque = UnsafeRawPointer(bitPattern: bitPattern) else { + return nil + } + return Unmanaged.fromOpaque(opaque) + } + + @_transparent @_alwaysEmitIntoClient + internal static func _encode(_ value: Value) -> Int { + guard let value = value else { return 0 } + return Int(bitPattern: value.toOpaque()) + } +} + +extension Unmanaged.AtomicOptionalRepresentation: AtomicStorage { + @_semantics("atomics.requires_constant_orderings") + @_transparent @_alwaysEmitIntoClient + public static func atomicLoad( + at pointer: UnsafeMutablePointer + ) -> Value { + let encoded = Storage.atomicLoad(at: _extract(pointer)) + return _decode(encoded) + } + + @_semantics("atomics.requires_constant_orderings") + @_transparent @_alwaysEmitIntoClient + public static func atomicCompareExchange( + expected: Value, + desired: Value, + at pointer: UnsafeMutablePointer + ) -> (exchanged: Bool, original: Value) { + let (exchanged, original) = Storage.atomicCompareExchange( + expected: _encode(expected), + desired: _encode(desired), + at: _extract(pointer)) + return (exchanged, _decode(original)) + } +} diff --git a/Sources/_LazyAtomic/UnsafeAtomicLazyReference.swift b/Sources/_LazyAtomic/UnsafeAtomicLazyReference.swift new file mode 100644 index 000000000..4ecdc63a7 --- /dev/null +++ b/Sources/_LazyAtomic/UnsafeAtomicLazyReference.swift @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +// THIS FILE IS PART OF A SUBSET OF https://github.com/apple/swift-atomics/ + +@frozen +public struct UnsafeAtomicLazyReference { + public typealias Value = Instance? + + @usableFromInline + internal typealias _Rep = Optional>.AtomicRepresentation + + @usableFromInline + internal let _ptr: UnsafeMutablePointer<_Rep> + + @_transparent // Debug performance + public init(@_nonEphemeral at pointer: UnsafeMutablePointer) { + // `Storage` is layout-compatible with its only stored property. + _ptr = UnsafeMutableRawPointer(pointer).assumingMemoryBound(to: _Rep.self) + } +} + +#if compiler(>=5.5) && canImport(_Concurrency) +extension UnsafeAtomicLazyReference: @unchecked Sendable +where Instance: Sendable {} +#endif + +extension UnsafeAtomicLazyReference { + @frozen + public struct Storage { + @usableFromInline + internal var _storage: _Rep + + @inlinable @inline(__always) + public init() { + _storage = _Rep(nil) + } + + @inlinable @inline(__always) + @discardableResult + public mutating func dispose() -> Value { + defer { _storage = _Rep(nil) } + return _storage.dispose()?.takeRetainedValue() + } + } +} + +extension UnsafeAtomicLazyReference { + @inlinable + public static func create() -> Self { + let ptr = UnsafeMutablePointer.allocate(capacity: 1) + ptr.initialize(to: Storage()) + return Self(at: ptr) + } + + @discardableResult + @inlinable + public func destroy() -> Value { + // `Storage` is layout-compatible with its only stored property. + let address = UnsafeMutableRawPointer(_ptr) + .assumingMemoryBound(to: Storage.self) + defer { address.deallocate() } + return address.pointee.dispose() + } +} + +extension UnsafeAtomicLazyReference { + public func storeIfNilThenLoad(_ desired: __owned Instance) -> Instance { + let desiredUnmanaged = Unmanaged.passRetained(desired) + let (exchanged, current) = _Rep.atomicCompareExchange( + expected: nil, + desired: desiredUnmanaged, + at: _ptr) + if !exchanged { + // The reference has already been initialized. Balance the retain that + // we performed on `desired`. + desiredUnmanaged.release() + return current!.takeUnretainedValue() + } + return desiredUnmanaged.takeUnretainedValue() + } + + public func load() -> Instance? { + let value = _Rep.atomicLoad(at: _ptr) + return value?.takeUnretainedValue() + } +} diff --git a/Sources/_LazyAtomicShims/_LazyAtomicShims.c b/Sources/_LazyAtomicShims/_LazyAtomicShims.c new file mode 100644 index 000000000..e727ed6ec --- /dev/null +++ b/Sources/_LazyAtomicShims/_LazyAtomicShims.c @@ -0,0 +1,12 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +#include "include/_LazyAtomicShims.h" diff --git a/Sources/_LazyAtomicShims/include/_LazyAtomicShims.h b/Sources/_LazyAtomicShims/include/_LazyAtomicShims.h new file mode 100644 index 000000000..c4965028f --- /dev/null +++ b/Sources/_LazyAtomicShims/include/_LazyAtomicShims.h @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +// THIS FILE IS PART OF A SUBSET OF https://github.com/apple/swift-atomics/ + +#ifndef SWIFT_STDLIB_SHIMS_LAZYATOMIC_H +#define SWIFT_STDLIB_SHIMS_LAZYATOMIC_H + +#include +#include +#include +// The atomic primitives are only needed when this is compiled using Swift's +// Clang Importer. This allows us to continue reling on some Clang extensions +// (see https://github.com/apple/swift-atomics/issues/37). +#if defined(__swift__) +# include +#endif + +#if defined(__swift__) + +#define SWIFTATOMIC_INLINE static inline __attribute__((__always_inline__)) +#define SWIFTATOMIC_SWIFT_NAME(name) __attribute__((swift_name(#name))) + +// Definition of an atomic storage type. +#define SWIFTATOMIC_STORAGE_TYPE(swiftType, cType, storageType) \ + typedef struct { \ + _Atomic(storageType) value; \ + } _sa_##swiftType \ + SWIFTATOMIC_SWIFT_NAME(_Atomic##swiftType##Storage); + +// Storage value initializer +#define SWIFTATOMIC_PREPARE_FN(swiftType, cType, storageType) \ + SWIFTATOMIC_INLINE \ + _sa_##swiftType _sa_prepare_##swiftType(cType value) \ + { \ + _sa_##swiftType storage = { SWIFTATOMIC_ENCODE_##swiftType(value) }; \ + assert(atomic_is_lock_free(&storage.value)); \ + return storage; \ + } + +// Storage value disposal function +#define SWIFTATOMIC_DISPOSE_FN(swiftType, cType, storageType) \ + SWIFTATOMIC_INLINE \ + cType _sa_dispose_##swiftType(_sa_##swiftType storage) \ + { \ + return SWIFTATOMIC_DECODE_##swiftType(storage.value); \ + } + +// Atomic load +#define SWIFTATOMIC_LOAD_FN(swiftType, cType, storageType, order) \ + SWIFTATOMIC_INLINE \ + cType _sa_load_##order##_##swiftType( \ + _sa_##swiftType *ptr) \ + { \ + return SWIFTATOMIC_DECODE_##swiftType( \ + atomic_load_explicit(&ptr->value, \ + memory_order_##order)); \ + } + +// Atomic compare/exchange +#define SWIFTATOMIC_CMPXCHG_FN_SIMPLE(_kind, swiftType, cType, storageType, succ, fail) \ + SWIFTATOMIC_INLINE \ + bool \ + _sa_cmpxchg_##_kind##_##succ##_##fail##_##swiftType( \ + _sa_##swiftType *ptr, \ + cType *expected, \ + cType desired) \ + { \ + return atomic_compare_exchange_##_kind##_explicit( \ + &ptr->value, \ + expected, \ + desired, \ + memory_order_##succ, \ + memory_order_##fail); \ + } + +#define SWIFTATOMIC_DEFINE_TYPE(variant, swiftType, cType, storageType) \ + SWIFTATOMIC_STORAGE_TYPE(swiftType, cType, storageType) \ + SWIFTATOMIC_PREPARE_FN(swiftType, cType, storageType) \ + SWIFTATOMIC_DISPOSE_FN(swiftType, cType, storageType) \ + SWIFTATOMIC_LOAD_FN(swiftType, cType, storageType, acquire) \ + SWIFTATOMIC_CMPXCHG_FN_##variant(strong, swiftType, cType, storageType, acq_rel, acquire) + +#define SWIFTATOMIC_ENCODE_Int(value) (value) +#define SWIFTATOMIC_DECODE_Int(value) (value) +SWIFTATOMIC_DEFINE_TYPE(SIMPLE, Int, intptr_t, intptr_t) + +#endif // __swift__ + +#endif // SWIFT_STDLIB_SHIMS_LAZYATOMIC_H diff --git a/Sources/_StringProcessing/Regex/Core.swift b/Sources/_StringProcessing/Regex/Core.swift index 32e904e6e..2d6b97a3c 100644 --- a/Sources/_StringProcessing/Regex/Core.swift +++ b/Sources/_StringProcessing/Regex/Core.swift @@ -10,7 +10,7 @@ //===----------------------------------------------------------------------===// @_implementationOnly import _RegexParser -@_implementationOnly import Atomics +@_implementationOnly import _LazyAtomic /// A type that represents a regular expression. @available(SwiftStdlib 5.7, *) From f78b9255c3b3f7243c99811afbafea7b7afeff8c Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Sun, 29 May 2022 23:15:45 -0500 Subject: [PATCH 4/7] Add a test for concurrent access This test consistently traps on 'main', but succeeds with this `Sendable` implementation. --- Tests/RegexTests/MatchTests.swift | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Tests/RegexTests/MatchTests.swift b/Tests/RegexTests/MatchTests.swift index d576a08d5..95b865ed9 100644 --- a/Tests/RegexTests/MatchTests.swift +++ b/Tests/RegexTests/MatchTests.swift @@ -1653,5 +1653,35 @@ extension RegexTests { let scalarExpected: [Substring] = ["\u{FE0F}💖🧠", "🧠💖☕"] XCTAssertEqual(scalarMatches.map { $0.0 }, scalarExpected) } + + func testConcurrentAccess() async throws { + for _ in 0..<1000 { + let regex = try Regex(#"abc+d*e?"#) + let strings = [ + "abc", + "abccccccccdddddddddde", + "abcccce", + "abddddde", + ] + let matches = await withTaskGroup(of: Optional.Match>.self) { group -> [Regex.Match] in + var result: [Regex.Match] = [] + + for str in strings { + group.addTask { + str.firstMatch(of: regex) + } + } + + for await match in group { + guard let match = match else { continue } + result.append(match) + } + + return result + } + + XCTAssertEqual(matches.count, 3) + } + } } From 50496c849e147d465bda1bd6c59f214cb546ce58 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Sun, 29 May 2022 23:39:16 -0500 Subject: [PATCH 5/7] Remove atomic protocols All the calls here are concrete; there's no need for conformances --- Sources/_LazyAtomic/AtomicValue.swift | 37 ---------- Sources/_LazyAtomic/Int.swift | 4 +- Sources/_LazyAtomic/Optional.swift | 21 ------ Sources/_LazyAtomic/Unmanaged.swift | 72 +------------------ .../UnsafeAtomicLazyReference.swift | 2 +- 5 files changed, 5 insertions(+), 131 deletions(-) delete mode 100644 Sources/_LazyAtomic/AtomicValue.swift delete mode 100644 Sources/_LazyAtomic/Optional.swift diff --git a/Sources/_LazyAtomic/AtomicValue.swift b/Sources/_LazyAtomic/AtomicValue.swift deleted file mode 100644 index 5175ee3ec..000000000 --- a/Sources/_LazyAtomic/AtomicValue.swift +++ /dev/null @@ -1,37 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// -//===----------------------------------------------------------------------===// - -// THIS FILE IS PART OF A SUBSET OF https://github.com/apple/swift-atomics/ - -public protocol AtomicValue { - associatedtype AtomicRepresentation: AtomicStorage - where AtomicRepresentation.Value == Self -} - -public protocol AtomicStorage { - associatedtype Value - - init(_ value: __owned Value) - - __consuming func dispose() -> Value - - @_semantics("atomics.requires_constant_orderings") - static func atomicLoad( - at pointer: UnsafeMutablePointer - ) -> Value - - @_semantics("atomics.requires_constant_orderings") - static func atomicCompareExchange( - expected: Value, - desired: __owned Value, - at pointer: UnsafeMutablePointer - ) -> (exchanged: Bool, original: Value) -} diff --git a/Sources/_LazyAtomic/Int.swift b/Sources/_LazyAtomic/Int.swift index 3915f240e..ee385b6e3 100644 --- a/Sources/_LazyAtomic/Int.swift +++ b/Sources/_LazyAtomic/Int.swift @@ -13,7 +13,7 @@ import _LazyAtomicShims -extension Int: AtomicValue { +extension Int { @frozen public struct AtomicRepresentation { public typealias Value = Int @@ -43,7 +43,7 @@ where Pointee == Int.AtomicRepresentation { } } -extension Int.AtomicRepresentation: AtomicStorage { +extension Int.AtomicRepresentation { @_semantics("atomics.requires_constant_orderings") @_transparent @_alwaysEmitIntoClient public static func atomicLoad( diff --git a/Sources/_LazyAtomic/Optional.swift b/Sources/_LazyAtomic/Optional.swift deleted file mode 100644 index dd4df095d..000000000 --- a/Sources/_LazyAtomic/Optional.swift +++ /dev/null @@ -1,21 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// -//===----------------------------------------------------------------------===// - -// THIS FILE IS PART OF A SUBSET OF https://github.com/apple/swift-atomics/ - -public protocol AtomicOptionalWrappable: AtomicValue { - associatedtype AtomicOptionalRepresentation: AtomicStorage - where AtomicOptionalRepresentation.Value == Self? -} - -extension Optional: AtomicValue where Wrapped: AtomicOptionalWrappable { - public typealias AtomicRepresentation = Wrapped.AtomicOptionalRepresentation -} diff --git a/Sources/_LazyAtomic/Unmanaged.swift b/Sources/_LazyAtomic/Unmanaged.swift index 9286c0dff..26b4dcc1a 100644 --- a/Sources/_LazyAtomic/Unmanaged.swift +++ b/Sources/_LazyAtomic/Unmanaged.swift @@ -11,75 +11,7 @@ // THIS FILE IS PART OF A SUBSET OF https://github.com/apple/swift-atomics/ -extension Unmanaged: AtomicValue { - @frozen - public struct AtomicRepresentation { - public typealias Value = Unmanaged - @usableFromInline internal typealias Storage = Int.AtomicRepresentation - - @usableFromInline - internal let _storage: Storage - - @inline(__always) @_alwaysEmitIntoClient - public init(_ value: Value) { - self._storage = .init(Self._encode(value)) - } - - @inline(__always) @_alwaysEmitIntoClient - public func dispose() -> Value { - Self._decode(_storage.dispose()) - } - } -} - -extension Unmanaged.AtomicRepresentation { - @_transparent @_alwaysEmitIntoClient - @usableFromInline - internal static func _extract( - _ ptr: UnsafeMutablePointer - ) -> UnsafeMutablePointer { - // `Self` is layout-compatible with its only stored property. - return UnsafeMutableRawPointer(ptr) - .assumingMemoryBound(to: Storage.self) - } - - @_transparent @_alwaysEmitIntoClient - internal static func _decode(_ bitPattern: Int) -> Value { - return Unmanaged.fromOpaque(UnsafeRawPointer(bitPattern: bitPattern)!) - } - - @_transparent @_alwaysEmitIntoClient - internal static func _encode(_ value: Value) -> Int { - return Int(bitPattern: value.toOpaque()) - } -} - -extension Unmanaged.AtomicRepresentation: AtomicStorage { - @_semantics("atomics.requires_constant_orderings") - @_transparent @_alwaysEmitIntoClient - public static func atomicLoad( - at pointer: UnsafeMutablePointer - ) -> Value { - let encoded = Storage.atomicLoad(at: _extract(pointer)) - return _decode(encoded) - } - - @_semantics("atomics.requires_constant_orderings") - @_transparent @_alwaysEmitIntoClient - public static func atomicCompareExchange( - expected: Value, - desired: Value, - at pointer: UnsafeMutablePointer - ) -> (exchanged: Bool, original: Value) { - let (exchanged, original) = Storage.atomicCompareExchange( - expected: _encode(expected), - desired: _encode(desired), - at: _extract(pointer)) - return (exchanged, _decode(original)) - } -} - -extension Unmanaged: AtomicOptionalWrappable { +extension Unmanaged { @frozen public struct AtomicOptionalRepresentation { public typealias Value = Unmanaged? @@ -126,7 +58,7 @@ extension Unmanaged.AtomicOptionalRepresentation { } } -extension Unmanaged.AtomicOptionalRepresentation: AtomicStorage { +extension Unmanaged.AtomicOptionalRepresentation { @_semantics("atomics.requires_constant_orderings") @_transparent @_alwaysEmitIntoClient public static func atomicLoad( diff --git a/Sources/_LazyAtomic/UnsafeAtomicLazyReference.swift b/Sources/_LazyAtomic/UnsafeAtomicLazyReference.swift index 4ecdc63a7..1ae006265 100644 --- a/Sources/_LazyAtomic/UnsafeAtomicLazyReference.swift +++ b/Sources/_LazyAtomic/UnsafeAtomicLazyReference.swift @@ -16,7 +16,7 @@ public struct UnsafeAtomicLazyReference { public typealias Value = Instance? @usableFromInline - internal typealias _Rep = Optional>.AtomicRepresentation + internal typealias _Rep = Unmanaged.AtomicOptionalRepresentation @usableFromInline internal let _ptr: UnsafeMutablePointer<_Rep> From 8db7cd2a0955787371eb0fd6ec992658e54a8790 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Wed, 1 Jun 2022 14:52:13 -0500 Subject: [PATCH 6/7] Use the existing _stdlib_atomic*ARCRef methods Didn't realize these methods existed; no need for shims or wrappers! --- Package.swift | 7 -- Sources/_LazyAtomic/Int.swift | 69 ------------- Sources/_LazyAtomic/Unmanaged.swift | 84 ---------------- .../UnsafeAtomicLazyReference.swift | 95 ------------------ Sources/_LazyAtomicShims/_LazyAtomicShims.c | 12 --- .../include/_LazyAtomicShims.h | 98 ------------------- Sources/_StringProcessing/Regex/Core.swift | 22 +++-- 7 files changed, 14 insertions(+), 373 deletions(-) delete mode 100644 Sources/_LazyAtomic/Int.swift delete mode 100644 Sources/_LazyAtomic/Unmanaged.swift delete mode 100644 Sources/_LazyAtomic/UnsafeAtomicLazyReference.swift delete mode 100644 Sources/_LazyAtomicShims/_LazyAtomicShims.c delete mode 100644 Sources/_LazyAtomicShims/include/_LazyAtomicShims.h diff --git a/Package.swift b/Package.swift index f18833199..2043fee46 100644 --- a/Package.swift +++ b/Package.swift @@ -60,18 +60,11 @@ let package = Package( .target( name: "_CUnicode", dependencies: []), - .target( - name: "_LazyAtomicShims", - dependencies: []), - .target( - name: "_LazyAtomic", - dependencies: ["_LazyAtomicShims"]), .target( name: "_StringProcessing", dependencies: [ "_RegexParser", "_CUnicode", - "_LazyAtomic", ], swiftSettings: publicStdlibSettings), .target( diff --git a/Sources/_LazyAtomic/Int.swift b/Sources/_LazyAtomic/Int.swift deleted file mode 100644 index ee385b6e3..000000000 --- a/Sources/_LazyAtomic/Int.swift +++ /dev/null @@ -1,69 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// -//===----------------------------------------------------------------------===// - -// THIS FILE IS PART OF A SUBSET OF https://github.com/apple/swift-atomics/ - -import _LazyAtomicShims - -extension Int { - @frozen - public struct AtomicRepresentation { - public typealias Value = Int - - @usableFromInline - var _storage: _AtomicIntStorage - - @inline(__always) @_alwaysEmitIntoClient - public init(_ value: Value) { - self._storage = _sa_prepare_Int(value) - } - - @inline(__always) @_alwaysEmitIntoClient - public func dispose() -> Value { - return _sa_dispose_Int(_storage) - } - } -} - -extension UnsafeMutablePointer -where Pointee == Int.AtomicRepresentation { - @inlinable @inline(__always) - internal var _extract: UnsafeMutablePointer<_AtomicIntStorage> { - // `Int` is layout-compatible with its only stored property. - return UnsafeMutableRawPointer(self) - .assumingMemoryBound(to: _AtomicIntStorage.self) - } -} - -extension Int.AtomicRepresentation { - @_semantics("atomics.requires_constant_orderings") - @_transparent @_alwaysEmitIntoClient - public static func atomicLoad( - at pointer: UnsafeMutablePointer - ) -> Value { - _sa_load_acquire_Int(pointer._extract) - } - - @_semantics("atomics.requires_constant_orderings") - @_transparent @_alwaysEmitIntoClient - public static func atomicCompareExchange( - expected: Value, - desired: Value, - at pointer: UnsafeMutablePointer - ) -> (exchanged: Bool, original: Value) { - var expected = expected - let exchanged: Bool - exchanged = _sa_cmpxchg_strong_acq_rel_acquire_Int( - pointer._extract, - &expected, desired) - return (exchanged, expected) - } -} diff --git a/Sources/_LazyAtomic/Unmanaged.swift b/Sources/_LazyAtomic/Unmanaged.swift deleted file mode 100644 index 26b4dcc1a..000000000 --- a/Sources/_LazyAtomic/Unmanaged.swift +++ /dev/null @@ -1,84 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// -//===----------------------------------------------------------------------===// - -// THIS FILE IS PART OF A SUBSET OF https://github.com/apple/swift-atomics/ - -extension Unmanaged { - @frozen - public struct AtomicOptionalRepresentation { - public typealias Value = Unmanaged? - @usableFromInline internal typealias Storage = Int.AtomicRepresentation - - @usableFromInline - internal let _storage: Storage - - @inline(__always) @_alwaysEmitIntoClient - public init(_ value: Value) { - self._storage = .init(Self._encode(value)) - } - - @inline(__always) @_alwaysEmitIntoClient - public func dispose() -> Value { - Self._decode(_storage.dispose()) - } - } -} - -extension Unmanaged.AtomicOptionalRepresentation { - @_transparent @_alwaysEmitIntoClient - @usableFromInline - internal static func _extract( - _ ptr: UnsafeMutablePointer - ) -> UnsafeMutablePointer { - // `Self` is layout-compatible with its only stored property. - return UnsafeMutableRawPointer(ptr) - .assumingMemoryBound(to: Storage.self) - } - - @_transparent @_alwaysEmitIntoClient - internal static func _decode(_ bitPattern: Int) -> Value { - guard let opaque = UnsafeRawPointer(bitPattern: bitPattern) else { - return nil - } - return Unmanaged.fromOpaque(opaque) - } - - @_transparent @_alwaysEmitIntoClient - internal static func _encode(_ value: Value) -> Int { - guard let value = value else { return 0 } - return Int(bitPattern: value.toOpaque()) - } -} - -extension Unmanaged.AtomicOptionalRepresentation { - @_semantics("atomics.requires_constant_orderings") - @_transparent @_alwaysEmitIntoClient - public static func atomicLoad( - at pointer: UnsafeMutablePointer - ) -> Value { - let encoded = Storage.atomicLoad(at: _extract(pointer)) - return _decode(encoded) - } - - @_semantics("atomics.requires_constant_orderings") - @_transparent @_alwaysEmitIntoClient - public static func atomicCompareExchange( - expected: Value, - desired: Value, - at pointer: UnsafeMutablePointer - ) -> (exchanged: Bool, original: Value) { - let (exchanged, original) = Storage.atomicCompareExchange( - expected: _encode(expected), - desired: _encode(desired), - at: _extract(pointer)) - return (exchanged, _decode(original)) - } -} diff --git a/Sources/_LazyAtomic/UnsafeAtomicLazyReference.swift b/Sources/_LazyAtomic/UnsafeAtomicLazyReference.swift deleted file mode 100644 index 1ae006265..000000000 --- a/Sources/_LazyAtomic/UnsafeAtomicLazyReference.swift +++ /dev/null @@ -1,95 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// -//===----------------------------------------------------------------------===// - -// THIS FILE IS PART OF A SUBSET OF https://github.com/apple/swift-atomics/ - -@frozen -public struct UnsafeAtomicLazyReference { - public typealias Value = Instance? - - @usableFromInline - internal typealias _Rep = Unmanaged.AtomicOptionalRepresentation - - @usableFromInline - internal let _ptr: UnsafeMutablePointer<_Rep> - - @_transparent // Debug performance - public init(@_nonEphemeral at pointer: UnsafeMutablePointer) { - // `Storage` is layout-compatible with its only stored property. - _ptr = UnsafeMutableRawPointer(pointer).assumingMemoryBound(to: _Rep.self) - } -} - -#if compiler(>=5.5) && canImport(_Concurrency) -extension UnsafeAtomicLazyReference: @unchecked Sendable -where Instance: Sendable {} -#endif - -extension UnsafeAtomicLazyReference { - @frozen - public struct Storage { - @usableFromInline - internal var _storage: _Rep - - @inlinable @inline(__always) - public init() { - _storage = _Rep(nil) - } - - @inlinable @inline(__always) - @discardableResult - public mutating func dispose() -> Value { - defer { _storage = _Rep(nil) } - return _storage.dispose()?.takeRetainedValue() - } - } -} - -extension UnsafeAtomicLazyReference { - @inlinable - public static func create() -> Self { - let ptr = UnsafeMutablePointer.allocate(capacity: 1) - ptr.initialize(to: Storage()) - return Self(at: ptr) - } - - @discardableResult - @inlinable - public func destroy() -> Value { - // `Storage` is layout-compatible with its only stored property. - let address = UnsafeMutableRawPointer(_ptr) - .assumingMemoryBound(to: Storage.self) - defer { address.deallocate() } - return address.pointee.dispose() - } -} - -extension UnsafeAtomicLazyReference { - public func storeIfNilThenLoad(_ desired: __owned Instance) -> Instance { - let desiredUnmanaged = Unmanaged.passRetained(desired) - let (exchanged, current) = _Rep.atomicCompareExchange( - expected: nil, - desired: desiredUnmanaged, - at: _ptr) - if !exchanged { - // The reference has already been initialized. Balance the retain that - // we performed on `desired`. - desiredUnmanaged.release() - return current!.takeUnretainedValue() - } - return desiredUnmanaged.takeUnretainedValue() - } - - public func load() -> Instance? { - let value = _Rep.atomicLoad(at: _ptr) - return value?.takeUnretainedValue() - } -} diff --git a/Sources/_LazyAtomicShims/_LazyAtomicShims.c b/Sources/_LazyAtomicShims/_LazyAtomicShims.c deleted file mode 100644 index e727ed6ec..000000000 --- a/Sources/_LazyAtomicShims/_LazyAtomicShims.c +++ /dev/null @@ -1,12 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// -//===----------------------------------------------------------------------===// - -#include "include/_LazyAtomicShims.h" diff --git a/Sources/_LazyAtomicShims/include/_LazyAtomicShims.h b/Sources/_LazyAtomicShims/include/_LazyAtomicShims.h deleted file mode 100644 index c4965028f..000000000 --- a/Sources/_LazyAtomicShims/include/_LazyAtomicShims.h +++ /dev/null @@ -1,98 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// -//===----------------------------------------------------------------------===// - -// THIS FILE IS PART OF A SUBSET OF https://github.com/apple/swift-atomics/ - -#ifndef SWIFT_STDLIB_SHIMS_LAZYATOMIC_H -#define SWIFT_STDLIB_SHIMS_LAZYATOMIC_H - -#include -#include -#include -// The atomic primitives are only needed when this is compiled using Swift's -// Clang Importer. This allows us to continue reling on some Clang extensions -// (see https://github.com/apple/swift-atomics/issues/37). -#if defined(__swift__) -# include -#endif - -#if defined(__swift__) - -#define SWIFTATOMIC_INLINE static inline __attribute__((__always_inline__)) -#define SWIFTATOMIC_SWIFT_NAME(name) __attribute__((swift_name(#name))) - -// Definition of an atomic storage type. -#define SWIFTATOMIC_STORAGE_TYPE(swiftType, cType, storageType) \ - typedef struct { \ - _Atomic(storageType) value; \ - } _sa_##swiftType \ - SWIFTATOMIC_SWIFT_NAME(_Atomic##swiftType##Storage); - -// Storage value initializer -#define SWIFTATOMIC_PREPARE_FN(swiftType, cType, storageType) \ - SWIFTATOMIC_INLINE \ - _sa_##swiftType _sa_prepare_##swiftType(cType value) \ - { \ - _sa_##swiftType storage = { SWIFTATOMIC_ENCODE_##swiftType(value) }; \ - assert(atomic_is_lock_free(&storage.value)); \ - return storage; \ - } - -// Storage value disposal function -#define SWIFTATOMIC_DISPOSE_FN(swiftType, cType, storageType) \ - SWIFTATOMIC_INLINE \ - cType _sa_dispose_##swiftType(_sa_##swiftType storage) \ - { \ - return SWIFTATOMIC_DECODE_##swiftType(storage.value); \ - } - -// Atomic load -#define SWIFTATOMIC_LOAD_FN(swiftType, cType, storageType, order) \ - SWIFTATOMIC_INLINE \ - cType _sa_load_##order##_##swiftType( \ - _sa_##swiftType *ptr) \ - { \ - return SWIFTATOMIC_DECODE_##swiftType( \ - atomic_load_explicit(&ptr->value, \ - memory_order_##order)); \ - } - -// Atomic compare/exchange -#define SWIFTATOMIC_CMPXCHG_FN_SIMPLE(_kind, swiftType, cType, storageType, succ, fail) \ - SWIFTATOMIC_INLINE \ - bool \ - _sa_cmpxchg_##_kind##_##succ##_##fail##_##swiftType( \ - _sa_##swiftType *ptr, \ - cType *expected, \ - cType desired) \ - { \ - return atomic_compare_exchange_##_kind##_explicit( \ - &ptr->value, \ - expected, \ - desired, \ - memory_order_##succ, \ - memory_order_##fail); \ - } - -#define SWIFTATOMIC_DEFINE_TYPE(variant, swiftType, cType, storageType) \ - SWIFTATOMIC_STORAGE_TYPE(swiftType, cType, storageType) \ - SWIFTATOMIC_PREPARE_FN(swiftType, cType, storageType) \ - SWIFTATOMIC_DISPOSE_FN(swiftType, cType, storageType) \ - SWIFTATOMIC_LOAD_FN(swiftType, cType, storageType, acquire) \ - SWIFTATOMIC_CMPXCHG_FN_##variant(strong, swiftType, cType, storageType, acq_rel, acquire) - -#define SWIFTATOMIC_ENCODE_Int(value) (value) -#define SWIFTATOMIC_DECODE_Int(value) (value) -SWIFTATOMIC_DEFINE_TYPE(SIMPLE, Int, intptr_t, intptr_t) - -#endif // __swift__ - -#endif // SWIFT_STDLIB_SHIMS_LAZYATOMIC_H diff --git a/Sources/_StringProcessing/Regex/Core.swift b/Sources/_StringProcessing/Regex/Core.swift index 2d6b97a3c..9b4da212b 100644 --- a/Sources/_StringProcessing/Regex/Core.swift +++ b/Sources/_StringProcessing/Regex/Core.swift @@ -10,7 +10,6 @@ //===----------------------------------------------------------------------===// @_implementationOnly import _RegexParser -@_implementationOnly import _LazyAtomic /// A type that represents a regular expression. @available(SwiftStdlib 5.7, *) @@ -86,16 +85,20 @@ extension Regex { init(_ value: MEProgram) { self.value = value } } - private var _loweredProgramStorage: UnsafeAtomicLazyReference - = .create() + private var _loweredProgramStorage: UnsafeMutablePointer = { + let pointer = UnsafeMutablePointer.allocate(capacity: 1) + pointer.initialize(to: nil) + return pointer + }() /// The program for execution with the matching engine. var loweredProgram: MEProgram { - if let lowered = _loweredProgramStorage.load() { - return lowered.value + if let loweredObject = _stdlib_atomicLoadARCRef(object: _loweredProgramStorage) { + return (loweredObject as! ProgramBox).value } - let lowered = try! ProgramBox(Compiler(tree: tree).emit()) - return _loweredProgramStorage.storeIfNilThenLoad(lowered).value + let lowered = try! Compiler(tree: tree).emit() + _stdlib_atomicInitializeARCRef(object: _loweredProgramStorage, desired: ProgramBox(lowered)) + return lowered } init(ast: AST) { @@ -107,7 +110,10 @@ extension Regex { } deinit { - _loweredProgramStorage.destroy() + if _loweredProgramStorage.pointee != nil { + _loweredProgramStorage.deinitialize(count: 1) + } + _loweredProgramStorage.deallocate() } } From 63c4e6a2c1665b239298a7387c95facd2bd517c6 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Wed, 1 Jun 2022 15:47:46 -0500 Subject: [PATCH 7/7] Eliminate extra indirection We only need to synchronize the lazy initialization; accesses either succeed or don't already. --- Sources/_StringProcessing/Regex/Core.swift | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/Sources/_StringProcessing/Regex/Core.swift b/Sources/_StringProcessing/Regex/Core.swift index 9b4da212b..5b975db1c 100644 --- a/Sources/_StringProcessing/Regex/Core.swift +++ b/Sources/_StringProcessing/Regex/Core.swift @@ -85,19 +85,16 @@ extension Regex { init(_ value: MEProgram) { self.value = value } } - private var _loweredProgramStorage: UnsafeMutablePointer = { - let pointer = UnsafeMutablePointer.allocate(capacity: 1) - pointer.initialize(to: nil) - return pointer - }() + /// Do not use directly - all accesses must go through `loweredProgram`. + private var _loweredProgramStorage: AnyObject? = nil /// The program for execution with the matching engine. var loweredProgram: MEProgram { - if let loweredObject = _stdlib_atomicLoadARCRef(object: _loweredProgramStorage) { - return (loweredObject as! ProgramBox).value + if let loweredObject = _loweredProgramStorage as? ProgramBox { + return loweredObject.value } let lowered = try! Compiler(tree: tree).emit() - _stdlib_atomicInitializeARCRef(object: _loweredProgramStorage, desired: ProgramBox(lowered)) + _stdlib_atomicInitializeARCRef(object: &_loweredProgramStorage, desired: ProgramBox(lowered)) return lowered } @@ -108,13 +105,6 @@ extension Regex { init(tree: DSLTree) { self.tree = tree } - - deinit { - if _loweredProgramStorage.pointee != nil { - _loweredProgramStorage.deinitialize(count: 1) - } - _loweredProgramStorage.deallocate() - } } /// The set of matching options that applies to the start of this regex.