//===--- 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 public // @testable let _destroyTLSCounter = _stdlib_AtomicInt() #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 absract 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 var uBreakIterator: 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 init(_uBreakIterator: OpaquePointer) { self.uBreakIterator = _uBreakIterator } // Get the current thread's TLS pointer. On first call for a given thread, // creates and initializes a new one. static internal func getPointer() -> UnsafeMutablePointer<_ThreadLocalStorage> { let tlsRawPtr = _swift_stdlib_thread_getspecific(_tlsKey) if _fastPath(tlsRawPtr != nil) { return tlsRawPtr._unsafelyUnwrappedUnchecked.assumingMemoryBound( to: _ThreadLocalStorage.self) } return _initializeThreadLocalStorage() } // Retrieve our thread's local uBreakIterator and set it up for the given // StringCore. static internal func getUBreakIterator( for core: _StringCore ) -> OpaquePointer { _sanityCheck(core._owner != nil || core._baseAddress != nil, "invalid StringCore") let corePtr: UnsafeMutablePointer = core.startUTF16 return getUBreakIterator( for: UnsafeBufferPointer(start: corePtr, count: core.count)) } static internal func getUBreakIterator( for bufPtr: UnsafeBufferPointer ) -> OpaquePointer { let tlsPtr = getPointer() let brkIter = tlsPtr[0].uBreakIterator var err = __swift_stdlib_U_ZERO_ERROR __swift_stdlib_ubrk_setText( brkIter, bufPtr.baseAddress!, Int32(bufPtr.count), &err) _precondition(err.isSuccess, "Unexpected ubrk_setUText failure") return brkIter } } // Destructor to register with pthreads. Responsible for deallocating any memory // owned. internal func _destroyTLS(_ ptr: UnsafeMutableRawPointer?) { _sanityCheck(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(capacity: 1) #if INTERNAL_CHECKS_ENABLED // Log the fact we've destroyed our storage _destroyTLSCounter.fetchAndAdd(1) #endif } // Lazily created global key for use with pthread TLS internal let _tlsKey: __swift_thread_key_t = { let sentinelValue = __swift_thread_key_t.max var key: __swift_thread_key_t = sentinelValue let success = _swift_stdlib_thread_key_create(&key, _destroyTLS) _sanityCheck(success == 0, "somehow failed to create TLS key") _sanityCheck(key != sentinelValue, "Didn't make a new key") return key }() @inline(never) internal func _initializeThreadLocalStorage() -> UnsafeMutablePointer<_ThreadLocalStorage> { _sanityCheck(_swift_stdlib_thread_getspecific(_tlsKey) == nil, "already initialized") // Create and initialize one. 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") let tlsPtr: UnsafeMutablePointer<_ThreadLocalStorage> = UnsafeMutablePointer<_ThreadLocalStorage>.allocate( capacity: 1 ) tlsPtr.initialize( to: _ThreadLocalStorage(_uBreakIterator: newUBreakIterator) ) let success = _swift_stdlib_thread_setspecific(_tlsKey, tlsPtr) _sanityCheck(success == 0, "setspecific failed") return tlsPtr }