//===--- ThreadLocalStorage.swift -----------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 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 // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// import SwiftShims // For testing purposes, a thread-safe counter to guarantee that destructors get // called by pthread. #if INTERNAL_CHECKS_ENABLED internal class _TLSAtomicInt { internal var value: Int internal init() { self.value = 0 } internal var valuePtr: UnsafeMutablePointer { return _getUnsafePointerToStoredProperties(self).assumingMemoryBound( to: Int.self) } internal func increment() { _ = _swift_stdlib_atomicFetchAddInt( object: valuePtr, operand: 1) } internal func load() -> Int { return _swift_stdlib_atomicLoadInt(object: valuePtr) } } internal let _destroyTLSCounter = _TLSAtomicInt() public // @testable func _loadDestroyTLSCounter() -> Int { return _destroyTLSCounter.load() } #endif // Thread local storage for all of the Swift standard library // // @moveonly/@pointeronly: shouldn't be used as a value, only through its // pointer. Similarly, shouldn't be created, except by // _initializeThreadLocalStorage. // internal struct _ThreadLocalStorage { // TODO: might be best to abstract uBreakIterator handling and caching into // separate struct. That would also make it easier to maintain multiple ones // and other TLS entries side-by-side. // Save a pre-allocated UBreakIterator, as they are very expensive to set up. // Each thread can reuse their unique break iterator, being careful to reset // the text when it has changed (see below). Even with a naive always-reset // policy, grapheme breaking is 30x faster when using a pre-allocated // UBreakIterator than recreating one. // // private internal var uBreakIterator: OpaquePointer internal var uText: OpaquePointer // TODO: Consider saving two, e.g. for character-by-character comparison // The below cache key tries to avoid resetting uBreakIterator's text when // operating on the same String as before. Avoiding the reset gives a 50% // speedup on grapheme breaking. // // As a invalidation check, save the base address from the last used // StringCore. We can skip resetting the uBreakIterator's text when operating // on a given StringCore when both of these associated references/pointers are // equal to the StringCore's. Note that the owner is weak, to force it to // compare unequal if a new StringCore happens to be created in the same // memory. // // TODO: unowned reference to string owner, base address, and _countAndFlags // private: Should only be called by _initializeThreadLocalStorage internal init(_uBreakIterator: OpaquePointer, _uText: OpaquePointer) { self.uBreakIterator = _uBreakIterator self.uText = _uText } // Get the current thread's TLS pointer. On first call for a given thread, // creates and initializes a new one. internal static func getPointer() -> UnsafeMutablePointer<_ThreadLocalStorage> { return _swift_stdlib_threadLocalStorageGet().assumingMemoryBound( to: _ThreadLocalStorage.self) } internal static func getUBreakIterator( _ bufPtr: UnsafeBufferPointer ) -> OpaquePointer { let tlsPtr = getPointer() let brkIter = tlsPtr[0].uBreakIterator let utext = tlsPtr[0].uText var err = __swift_stdlib_U_ZERO_ERROR let start = bufPtr.baseAddress._unsafelyUnwrappedUnchecked _ = __swift_stdlib_utext_openUChars( utext, start, Int64(bufPtr.count), &err) _precondition(err.isSuccess, "Unexpected utext_openUChars failure") __swift_stdlib_ubrk_setUText(brkIter, utext, &err) _precondition(err.isSuccess, "Unexpected ubrk_setUText failure") return brkIter } internal static func getUBreakIterator( _ bufPtr: UnsafeBufferPointer ) -> OpaquePointer { let tlsPtr = getPointer() let brkIter = tlsPtr[0].uBreakIterator let utext = tlsPtr[0].uText var err = __swift_stdlib_U_ZERO_ERROR let start = bufPtr.baseAddress._unsafelyUnwrappedUnchecked._asCChar _ = __swift_stdlib_utext_openUTF8( utext, start, Int64(bufPtr.count), &err) _precondition(err.isSuccess, "Unexpected utext_openUChars failure") __swift_stdlib_ubrk_setUText(brkIter, utext, &err) _precondition(err.isSuccess, "Unexpected ubrk_setUText failure") return brkIter } } // Destructor to register with pthreads. Responsible for deallocating any memory // owned. @_silgen_name("_stdlib_destroyTLS") internal func _destroyTLS(_ ptr: UnsafeMutableRawPointer?) { _internalInvariant(ptr != nil, "_destroyTLS was called, but with nil...") let tlsPtr = ptr!.assumingMemoryBound(to: _ThreadLocalStorage.self) __swift_stdlib_ubrk_close(tlsPtr[0].uBreakIterator) tlsPtr.deinitialize(count: 1) tlsPtr.deallocate() #if INTERNAL_CHECKS_ENABLED // Log the fact we've destroyed our storage _destroyTLSCounter.increment() #endif } @_silgen_name("_stdlib_createTLS") internal func _createThreadLocalStorage() -> UnsafeMutablePointer<_ThreadLocalStorage> { // Allocate and initialize a UBreakIterator and UText. var err = __swift_stdlib_U_ZERO_ERROR let newUBreakIterator = __swift_stdlib_ubrk_open( /*type:*/ __swift_stdlib_UBRK_CHARACTER, /*locale:*/ nil, /*text:*/ nil, /*textLength:*/ 0, /*status:*/ &err) _precondition(err.isSuccess, "Unexpected ubrk_open failure") // utext_openUTF8 needs a valid pointer, even though we won't read from it var a: Int8 = 0x41 let newUText = __swift_stdlib_utext_openUTF8( /*ut:*/ nil, /*s:*/ &a, /*len:*/ 1, /*status:*/ &err) _precondition(err.isSuccess, "Unexpected utext_openUTF8 failure") let tlsPtr: UnsafeMutablePointer<_ThreadLocalStorage> = UnsafeMutablePointer<_ThreadLocalStorage>.allocate( capacity: 1 ) tlsPtr.initialize(to: _ThreadLocalStorage( _uBreakIterator: newUBreakIterator, _uText: newUText)) return tlsPtr }