| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See http://swift.org/LICENSE.txt for license information |
| // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| |
| |
| #if os(OSX) || os(iOS) |
| import Darwin |
| #elseif os(Linux) |
| import Glibc |
| #endif |
| |
| import CoreFoundation |
| |
| private func disposeTLS(ctx: UnsafeMutablePointer<Void>) -> Void { |
| Unmanaged<AnyObject>.fromOpaque(OpaquePointer(ctx)).release() |
| } |
| |
| internal class NSThreadSpecific<T: AnyObject> { |
| |
| private var NSThreadSpecificKeySet = false |
| private var NSThreadSpecificKeyLock = NSLock() |
| private var NSThreadSpecificKey = pthread_key_t() |
| |
| private var key: pthread_key_t { |
| NSThreadSpecificKeyLock.lock() |
| if !NSThreadSpecificKeySet { |
| withUnsafeMutablePointer(&NSThreadSpecificKey) { key in |
| NSThreadSpecificKeySet = pthread_key_create(key, disposeTLS) == 0 |
| } |
| } |
| NSThreadSpecificKeyLock.unlock() |
| return NSThreadSpecificKey |
| } |
| |
| internal func get(generator: (Void) -> T) -> T { |
| let specific = pthread_getspecific(self.key) |
| if specific != nil { |
| return Unmanaged<T>.fromOpaque(OpaquePointer(specific)).takeUnretainedValue() |
| } else { |
| let value = generator() |
| pthread_setspecific(self.key, UnsafePointer<Void>(OpaquePointer(bitPattern: Unmanaged<AnyObject>.passRetained(value)))) |
| return value |
| } |
| } |
| |
| internal func set(value: T) { |
| let specific = pthread_getspecific(self.key) |
| var previous: Unmanaged<T>? |
| if specific != nil { |
| previous = Unmanaged<T>.fromOpaque(OpaquePointer(specific)) |
| } |
| if let prev = previous { |
| if prev.takeUnretainedValue() === value { |
| return |
| } |
| } |
| pthread_setspecific(self.key, UnsafePointer<Void>(OpaquePointer(bitPattern: Unmanaged<AnyObject>.passRetained(value)))) |
| if let prev = previous { |
| prev.release() |
| } |
| } |
| } |
| |
| internal enum _NSThreadStatus { |
| case Initialized |
| case Starting |
| case Executing |
| case Finished |
| } |
| |
| private func NSThreadStart(context: UnsafeMutablePointer<Void>) -> UnsafeMutablePointer<Void> { |
| let unmanaged: Unmanaged<NSThread> = Unmanaged.fromOpaque(OpaquePointer(context)) |
| let thread = unmanaged.takeUnretainedValue() |
| NSThread._currentThread.set(thread) |
| thread._status = _NSThreadStatus.Executing |
| thread.main() |
| thread._status = _NSThreadStatus.Finished |
| unmanaged.release() |
| return nil |
| } |
| |
| public class NSThread : NSObject { |
| |
| static internal var _currentThread = NSThreadSpecific<NSThread>() |
| public static func currentThread() -> NSThread { |
| return NSThread._currentThread.get() { |
| return NSThread(thread: pthread_self()) |
| } |
| } |
| |
| /// Alternative API for detached thread creation |
| /// - Experiment: This is a draft API currently under consideration for official import into Foundation as a suitable alternative to creation via selector |
| /// - Note: Since this API is under consideration it may be either removed or revised in the near future |
| public class func detachNewThread(main: (Void) -> Void) { |
| let t = NSThread(main) |
| t.start() |
| } |
| |
| public class func isMultiThreaded() -> Bool { |
| return true |
| } |
| |
| public class func sleepUntilDate(date: NSDate) { |
| let start_ut = CFGetSystemUptime() |
| let start_at = CFAbsoluteTimeGetCurrent() |
| let end_at = date.timeIntervalSinceReferenceDate |
| var ti = end_at - start_at |
| let end_ut = start_ut + ti |
| while (0.0 < ti) { |
| var __ts__ = timespec(tv_sec: LONG_MAX, tv_nsec: 0) |
| if ti < Double(LONG_MAX) { |
| var integ = 0.0 |
| let frac: Double = withUnsafeMutablePointer(&integ) { integp in |
| return modf(ti, integp) |
| } |
| __ts__.tv_sec = Int(integ) |
| __ts__.tv_nsec = Int(frac * 1000000000.0) |
| } |
| withUnsafePointer(&__ts__) { ts in |
| nanosleep(ts, nil) |
| } |
| ti = end_ut - CFGetSystemUptime() |
| } |
| } |
| |
| public class func sleepForTimeInterval(interval: NSTimeInterval) { |
| var ti = interval |
| let start_ut = CFGetSystemUptime() |
| let end_ut = start_ut + ti |
| while 0.0 < ti { |
| var __ts__ = timespec(tv_sec: LONG_MAX, tv_nsec: 0) |
| if ti < Double(LONG_MAX) { |
| var integ = 0.0 |
| let frac: Double = withUnsafeMutablePointer(&integ) { integp in |
| return modf(ti, integp) |
| } |
| __ts__.tv_sec = Int(integ) |
| __ts__.tv_nsec = Int(frac * 1000000000.0) |
| } |
| withUnsafePointer(&__ts__) { ts in |
| nanosleep(ts, nil) |
| } |
| ti = end_ut - CFGetSystemUptime() |
| } |
| } |
| |
| public class func exit() { |
| pthread_exit(nil) |
| } |
| |
| internal var _main: (Void) -> Void = {} |
| #if os(OSX) || os(iOS) |
| private var _thread: pthread_t = nil |
| #elseif os(Linux) |
| private var _thread = pthread_t() |
| #endif |
| internal var _attr = pthread_attr_t() |
| internal var _status = _NSThreadStatus.Initialized |
| internal var _cancelled = false |
| /// - Note: this differs from the Darwin implementation in that the keys must be Strings |
| public var threadDictionary = [String:AnyObject]() |
| |
| internal init(thread: pthread_t) { |
| _thread = thread |
| } |
| |
| public init(_ main: (Void) -> Void) { |
| _main = main |
| withUnsafeMutablePointer(&_attr) { attr in |
| pthread_attr_init(attr) |
| } |
| } |
| |
| public func start() { |
| precondition(_status == .Initialized, "attempting to start a thread that has already been started") |
| _status = .Starting |
| if _cancelled { |
| _status = .Finished |
| return |
| } |
| withUnsafeMutablePointers(&_thread, &_attr) { thread, attr in |
| let ptr = Unmanaged.passRetained(self) |
| pthread_create(thread, attr, NSThreadStart, UnsafeMutablePointer(OpaquePointer(bitPattern: ptr))) |
| } |
| } |
| |
| public func main() { |
| _main() |
| } |
| |
| public var stackSize: Int { |
| get { |
| var size: Int = 0 |
| return withUnsafeMutablePointers(&_attr, &size) { attr, sz in |
| pthread_attr_getstacksize(attr, sz) |
| return sz.pointee |
| } |
| } |
| set { |
| // just don't allow a stack size more than 1GB on any platform |
| var s = newValue |
| if (1 << 30) < s { |
| s = 1 << 30 |
| } |
| withUnsafeMutablePointer(&_attr) { attr in |
| pthread_attr_setstacksize(attr, s) |
| } |
| } |
| } |
| |
| public var executing: Bool { |
| return _status == .Executing |
| } |
| |
| public var finished: Bool { |
| return _status == .Finished |
| } |
| |
| public var cancelled: Bool { |
| return _cancelled |
| } |
| |
| public func cancel() { |
| _cancelled = true |
| } |
| |
| public class func callStackReturnAddresses() -> [NSNumber] { |
| NSUnimplemented() |
| } |
| |
| public class func callStackSymbols() -> [String] { |
| NSUnimplemented() |
| } |
| } |