@@ -462,34 +462,57 @@ open class XMLParser : NSObject {
462
462
463
463
open var allowedExternalEntityURLs : Set < URL > ?
464
464
465
- /// The current parser is stored in a task local variable to allow for
466
- /// concurrent parsing in different tasks with different parsers.
467
- ///
468
- /// Rationale for `@unchecked Sendable`:
469
- /// While the ``XMLParser`` class itself is not `Sendable`, `TaskLocal`
470
- /// requires the value type to be `Sendable`. The sendability requirement
471
- /// of `TaskLocal` is only for the "default" value and values set with
472
- /// `withValue` will not be shared between tasks.
473
- /// So as long as 1. the default value is safe to be shared between tasks
474
- /// and 2. the `Sendable` conformance of `_CurrentParser` is not used
475
- /// outside of `TaskLocal`, it is safe to mark it as `@unchecked Sendable`.
476
- private struct _CurrentParser : @unchecked Sendable {
477
- let parser : XMLParser ?
478
-
479
- static var `default` : _CurrentParser {
480
- return _CurrentParser ( parser: nil )
465
+ /// The current parser context for the current thread.
466
+ private class _CurrentParserContext {
467
+ var _stack : [ XMLParser ] = [ ]
468
+ var _current : XMLParser ? {
469
+ return _stack. last
481
470
}
482
471
}
483
472
484
- @TaskLocal
485
- private static var _currentParser : _CurrentParser = . default
473
+ #if os(WASI)
474
+ /// The current parser associated with the current thread. (assuming no multi-threading)
475
+ /// FIXME: Unify the implementation with the other platforms once we unlock `threadDictionary`
476
+ /// or migrate to `FoundationEssentials._ThreadLocal`.
477
+ private static nonisolated ( unsafe) var _currentParserContext : _CurrentParserContext ?
478
+ #else
479
+ /// The current parser associated with the current thread.
480
+ private static var _currentParserContext : _CurrentParserContext ? {
481
+ get {
482
+ return Thread . current. threadDictionary [ " __CurrentNSXMLParser " ] as? _CurrentParserContext
483
+ }
484
+ set {
485
+ Thread . current. threadDictionary [ " __CurrentNSXMLParser " ] = newValue
486
+ }
487
+ }
488
+ #endif
486
489
490
+ /// The current parser associated with the current thread.
487
491
internal static func currentParser( ) -> XMLParser ? {
488
- return _currentParser. parser
492
+ if let ctx = _currentParserContext {
493
+ return ctx. _current
494
+ }
495
+ return nil
489
496
}
490
-
497
+
498
+ /// Execute the given closure with the current parser set to the given parser.
491
499
internal static func withCurrentParser< R> ( _ parser: XMLParser , _ body: ( ) -> R ) -> R {
492
- return self . $_currentParser. withValue ( _CurrentParser ( parser: parser) , operation: body)
500
+ var ctx : _CurrentParserContext
501
+ if let current = _currentParserContext {
502
+ // Use the existing context if it exists
503
+ ctx = current
504
+ } else {
505
+ // Create a new context in TLS
506
+ ctx = _CurrentParserContext ( )
507
+ _currentParserContext = ctx
508
+ }
509
+ // Push the parser onto the stack
510
+ ctx. _stack. append ( parser)
511
+ defer {
512
+ // Pop the parser off the stack
513
+ ctx. _stack. removeLast ( )
514
+ }
515
+ return body ( )
493
516
}
494
517
495
518
internal func _handleParseResult( _ parseResult: Int32 ) -> Bool {
0 commit comments