blob: 80b4d4c99daffc3725662a6afb07b4f9e234168d [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 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) || CYGWIN
import Glibc
#endif
import CoreFoundation
// for some reason having this take a generic causes a crash...
private func _compiler_crash_fix(_ key: _CFThreadSpecificKey, _ value: AnyObject?) {
_CThreadSpecificSet(key, value)
}
internal class NSThreadSpecific<T: NSObject> {
private var key = _CFThreadSpecificKeyCreate()
internal func get(_ generator: (Void) -> T) -> T {
if let specific = _CFThreadSpecificGet(key) {
return specific as! T
} else {
let value = generator()
_compiler_crash_fix(key, value)
return value
}
}
internal func set(_ value: T) {
_compiler_crash_fix(key, value)
}
}
internal enum _NSThreadStatus {
case initialized
case starting
case executing
case finished
}
private func NSThreadStart(_ context: UnsafeMutableRawPointer?) -> UnsafeMutableRawPointer? {
let thread: Thread = NSObject.unretainedReference(context!)
Thread._currentThread.set(thread)
thread._status = .executing
thread.main()
thread._status = .finished
Thread.releaseReference(context!)
return nil
}
open class Thread : NSObject {
static internal var _currentThread = NSThreadSpecific<Thread>()
open class var current: Thread {
return Thread._currentThread.get() {
return Thread(thread: pthread_self())
}
}
open class var isMainThread: Bool { NSUnimplemented() }
// !!! NSThread's mainThread property is incorrectly exported as "main", which conflicts with its "main" method.
open class var mainThread: Thread { NSUnimplemented() }
/// 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
open class func detachNewThread(_ block: @escaping () -> Swift.Void) {
let t = Thread(block: block)
t.start()
}
open class func isMultiThreaded() -> Bool {
return true
}
open class func sleep(until date: Date) {
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: Int.max, tv_nsec: 0)
if ti < Double(Int.max) {
var integ = 0.0
let frac: Double = withUnsafeMutablePointer(to: &integ) { integp in
return modf(ti, integp)
}
__ts__.tv_sec = Int(integ)
__ts__.tv_nsec = Int(frac * 1000000000.0)
}
let _ = withUnsafePointer(to: &__ts__) { ts in
nanosleep(ts, nil)
}
ti = end_ut - CFGetSystemUptime()
}
}
open class func sleep(forTimeInterval interval: TimeInterval) {
var ti = interval
let start_ut = CFGetSystemUptime()
let end_ut = start_ut + ti
while 0.0 < ti {
var __ts__ = timespec(tv_sec: Int.max, tv_nsec: 0)
if ti < Double(Int.max) {
var integ = 0.0
let frac: Double = withUnsafeMutablePointer(to: &integ) { integp in
return modf(ti, integp)
}
__ts__.tv_sec = Int(integ)
__ts__.tv_nsec = Int(frac * 1000000000.0)
}
let _ = withUnsafePointer(to: &__ts__) { ts in
nanosleep(ts, nil)
}
ti = end_ut - CFGetSystemUptime()
}
}
open class func exit() {
pthread_exit(nil)
}
internal var _main: (Void) -> Void = {}
#if os(OSX) || os(iOS) || CYGWIN
private var _thread: pthread_t? = nil
#elseif os(Linux) || os(Android)
private var _thread = pthread_t()
#endif
#if CYGWIN
internal var _attr : pthread_attr_t? = nil
#else
internal var _attr = pthread_attr_t()
#endif
internal var _status = _NSThreadStatus.initialized
internal var _cancelled = false
/// - Note: this differs from the Darwin implementation in that the keys must be Strings
open var threadDictionary = [String : Any]()
internal init(thread: pthread_t) {
// Note: even on Darwin this is a non-optional pthread_t; this is only used for valid threads, which are never null pointers.
_thread = thread
}
public override init() {
let _ = withUnsafeMutablePointer(to: &_attr) { attr in
pthread_attr_init(attr)
pthread_attr_setscope(attr, Int32(PTHREAD_SCOPE_SYSTEM))
pthread_attr_setdetachstate(attr, Int32(PTHREAD_CREATE_DETACHED))
}
}
public convenience init(block: @escaping () -> Swift.Void) {
self.init()
_main = block
}
open func start() {
precondition(_status == .initialized, "attempting to start a thread that has already been started")
_status = .starting
if _cancelled {
_status = .finished
return
}
#if CYGWIN
if let attr = self._attr {
_thread = self.withRetainedReference {
return _CFThreadCreate(attr, NSThreadStart, $0)
}
} else {
_thread = nil
}
#else
_thread = self.withRetainedReference {
return _CFThreadCreate(self._attr, NSThreadStart, $0)
}
#endif
}
open func main() {
_main()
}
open var name: String? {
didSet {
if _thread == Thread.current._thread {
_CFThreadSetName(name)
}
}
}
open var stackSize: Int {
get {
var size: Int = 0
return withUnsafeMutablePointer(to: &_attr) { attr in
withUnsafeMutablePointer(to: &size) { 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
}
let _ = withUnsafeMutablePointer(to: &_attr) { attr in
pthread_attr_setstacksize(attr, s)
}
}
}
open var isExecuting: Bool {
return _status == .executing
}
open var isFinished: Bool {
return _status == .finished
}
open var isCancelled: Bool {
return _cancelled
}
open var isMainThread: Bool {
NSUnimplemented()
}
open func cancel() {
_cancelled = true
}
open class var callStackReturnAddresses: [NSNumber] {
NSUnimplemented()
}
open class var callStackSymbols: [String] {
NSUnimplemented()
}
}
extension NSNotification.Name {
public static let NSWillBecomeMultiThreaded = NSNotification.Name(rawValue: "NSWillBecomeMultiThreadedNotification")
public static let NSDidBecomeSingleThreaded = NSNotification.Name(rawValue: "NSDidBecomeSingleThreadedNotification")
public static let NSThreadWillExit = NSNotification.Name(rawValue: "NSThreadWillExitNotification")
}