mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
183 lines
6.2 KiB
Swift
183 lines
6.2 KiB
Swift
//===--- 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<Int> {
|
|
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<UTF16.CodeUnit>
|
|
) -> 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<UTF8.CodeUnit>
|
|
) -> 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
|
|
}
|