Skip to content

Foundation: port NSThread to Windows #1856

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions CoreFoundation/Base.subproj/CFPlatform.c
Original file line number Diff line number Diff line change
Expand Up @@ -1359,9 +1359,20 @@ void _CFThreadSpecificSet(_CFThreadSpecificKey key, CFTypeRef _Nullable value) {

_CFThreadRef _CFThreadCreate(const _CFThreadAttributes attrs, void *_Nullable (* _Nonnull startfn)(void *_Nullable), void *_CF_RESTRICT _Nullable context) {
#if DEPLOYMENT_TARGET_WINDOWS
return (_CFThreadRef)_beginthreadex(NULL, 0,
DWORD dwCreationFlags = 0;
DWORD dwStackSize = 0;
if (attrs.dwSizeOfAttributes >=
offsetof(struct _CFThreadAttributes,
dwThreadStackReservation) + sizeof(dwStackSize)) {
dwStackSize = attrs.dwThreadStackReservation;
if (dwStackSize) {
dwCreationFlags |= STACK_SIZE_PARAM_IS_A_RESERVATION;
}
}

return (_CFThreadRef)_beginthreadex(NULL, dwStackSize,
(_beginthreadex_proc_type)startfn,
context, 0, NULL);
context, dwCreationFlags, NULL);
#else
_CFThreadRef thread;
pthread_create(&thread, &attrs, startfn, context);
Expand Down
5 changes: 4 additions & 1 deletion CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,10 @@ CF_EXPORT void CFLog1(CFLogLevel lev, CFStringRef message);

#if DEPLOYMENT_TARGET_WINDOWS
typedef HANDLE _CFThreadRef;
typedef DWORD _CFThreadAttributes;
typedef struct _CFThreadAttributes {
DWORD dwSizeOfAttributes;
DWORD dwThreadStackReservation;
} _CFThreadAttributes;
typedef DWORD _CFThreadSpecificKey;
#elif _POSIX_THREADS
typedef pthread_t _CFThreadRef;
Expand Down
2 changes: 1 addition & 1 deletion Foundation/NSLock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ extension NSLock {
open class NSConditionLock : NSObject, NSLocking {
internal var _cond = NSCondition()
internal var _value: Int
internal var _thread: _CFThreadRef?
internal var _thread: _swift_CFThreadRef?

public convenience override init() {
self.init(condition: 0)
Expand Down
113 changes: 108 additions & 5 deletions Foundation/Thread.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@

import CoreFoundation

// WORKAROUND_SR9811
#if os(Windows)
internal typealias _swift_CFThreadRef = HANDLE
#else
internal typealias _swift_CFThreadRef = pthread_t
#endif

internal class NSThreadSpecific<T: NSObject> {
private var key = _CFThreadSpecificKeyCreate()

Expand Down Expand Up @@ -38,7 +45,11 @@ private func NSThreadStart(_ context: UnsafeMutableRawPointer?) -> UnsafeMutable
let thread: Thread = NSObject.unretainedReference(context!)
Thread._currentThread.set(thread)
if let name = thread.name {
#if os(Windows)
_CFThreadSetName(GetCurrentThread(), name)
#else
_CFThreadSetName(pthread_self(), name)
#endif
}
thread._status = .executing
thread.main()
Expand All @@ -55,7 +66,11 @@ open class Thread : NSObject {
if Thread.isMainThread {
return mainThread
} else {
#if os(Windows)
return Thread(thread: GetCurrentThread())
#else
return Thread(thread: pthread_self())
#endif
}
}
}
Expand Down Expand Up @@ -89,6 +104,19 @@ open class Thread : NSObject {
}

open class func sleep(until date: Date) {
#if os(Windows)
var hTimer: HANDLE = CreateWaitableTimerW(nil, TRUE, nil)
// FIXME(compnerd) how to check that hTimer is not NULL?
defer { CloseHandle(hTimer) }

// the timeout is in 100ns units
var liTimeout: LARGE_INTEGER =
LARGE_INTEGER(QuadPart: LONGLONG(date.timeIntervalSinceReferenceDate) * -10000000)
if SetWaitableTimer(hTimer, &liTimeout, 0, nil, nil, FALSE) == FALSE {
return
}
WaitForSingleObject(hTimer, WinSDK.INFINITE)
#else
let start_ut = CFGetSystemUptime()
let start_at = CFAbsoluteTimeGetCurrent()
let end_at = date.timeIntervalSinceReferenceDate
Expand All @@ -109,9 +137,23 @@ open class Thread : NSObject {
}
ti = end_ut - CFGetSystemUptime()
}
#endif
}

open class func sleep(forTimeInterval interval: TimeInterval) {
#if os(Windows)
var hTimer: HANDLE = CreateWaitableTimerW(nil, TRUE, nil)
// FIXME(compnerd) how to check that hTimer is not NULL?
defer { CloseHandle(hTimer) }

// the timeout is in 100ns units
var liTimeout: LARGE_INTEGER =
LARGE_INTEGER(QuadPart: LONGLONG(interval) * -10000000)
if SetWaitableTimer(hTimer, &liTimeout, 0, nil, nil, FALSE) == FALSE {
return
}
WaitForSingleObject(hTimer, WinSDK.INFINITE)
#else
var ti = interval
let start_ut = CFGetSystemUptime()
let end_ut = start_ut + ti
Expand All @@ -130,37 +172,48 @@ open class Thread : NSObject {
}
ti = end_ut - CFGetSystemUptime()
}
#endif
}

open class func exit() {
Thread.current._status = .finished
#if os(Windows)
ExitThread(0)
#else
pthread_exit(nil)
#endif
}

internal var _main: () -> Void = {}
private var _thread: pthread_t? = nil
private var _thread: _swift_CFThreadRef? = nil

#if CYGWIN
#if os(Windows) && !CYGWIN
internal var _attr: _CFThreadAttributes =
_CFThreadAttributes(dwSizeOfAttributes: MemoryLayout<_CFThreadAttributes>.size,
dwThreadStackReservation: 0)
#elseif 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

open private(set) var threadDictionary: NSMutableDictionary = NSMutableDictionary()

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.
internal init(thread: _swift_CFThreadRef) {
// Note: even on Darwin this is a non-optional _CFThreadRef; this is only used for valid threads, which are never null pointers.
_thread = thread
}

public override init() {
#if !os(Windows)
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))
}
#endif
}

public convenience init(block: @escaping () -> Swift.Void) {
Expand Down Expand Up @@ -202,6 +255,19 @@ open class Thread : NSObject {
}
}

#if os(Windows)
open var stackSize: Int {
get {
var ulLowLimit: ULONG_PTR = 0
var ulHighLimit: ULONG_PTR = 0
GetCurrentThreadStackLimits(&ulLowLimit, &ulHighLimit)
return Int(ulLowLimit)
}
set {
_attr.dwThreadStackReservation = newValue
}
}
#else
open var stackSize: Int {
get {
var size: Int = 0
Expand All @@ -223,6 +289,7 @@ open class Thread : NSObject {
}
}
}
#endif

open var isExecuting: Bool {
return _status == .executing
Expand Down Expand Up @@ -252,6 +319,9 @@ open class Thread : NSObject {
defer { addrs.deallocate() }
#if os(Android)
let count = 0
#elseif os(Windows)
let count = RtlCaptureStackBackTrace(0, DWORD(maxSupportedStackDepth),
addrs, nil)
#else
let count = backtrace(addrs, Int32(maxSupportedStackDepth))
#endif
Expand All @@ -270,6 +340,39 @@ open class Thread : NSObject {
open class var callStackSymbols: [String] {
#if os(Android)
return []
#elseif os(Windows)
let hProcess: HANDLE = GetCurrentProcess()
SymSetOptions(DWORD(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS))
if SymInitializeW(hProcess, nil, TRUE) == FALSE {
return []
}
return backtraceAddresses { (addresses, count) in
var symbols: [String] = []

var buffer: UnsafeMutablePointer<Int8> =
UnsafeMutablePointer<Int8>
.allocate(capacity: MemoryLayout<SYMBOL_INFO>.size + 128)
defer { buffer.deallocate() }

buffer.withMemoryRebound(to: SYMBOL_INFO.self, capacity: 1) {
$0.pointee.SizeOfStruct = ULONG(MemoryLayout<SYMBOL_INFO>.size)
$0.pointee.MaxNameLen = 128

var address = addresses
for _ in 1...count {
var dwDisplacement: DWORD64 = 0
if SymFromAddr(hProcess, unsafeBitCast(address.pointee,
to: DWORD64.self),
&dwDisplacement, $0) == FALSE {
symbols.append("\($0.pointee)")
} else {
symbols.append(String(cString: &$0.pointee.Name))
}
address = address.successor()
}
}
return symbols
}
#else
return backtraceAddresses({ (addrs, count) in
var symbols: [String] = []
Expand Down