Skip to content

Commit 72010af

Browse files
committed
Foundation: port NSThread to Windows
Implement the Thread interfaces for Windows. Extend the CoreFoundation interface to support thread stack reservation sizing.
1 parent 99c2281 commit 72010af

File tree

3 files changed

+118
-8
lines changed

3 files changed

+118
-8
lines changed

CoreFoundation/Base.subproj/CFPlatform.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,9 +1359,20 @@ void _CFThreadSpecificSet(_CFThreadSpecificKey key, CFTypeRef _Nullable value) {
13591359

13601360
_CFThreadRef _CFThreadCreate(const _CFThreadAttributes attrs, void *_Nullable (* _Nonnull startfn)(void *_Nullable), void *_CF_RESTRICT _Nullable context) {
13611361
#if DEPLOYMENT_TARGET_WINDOWS
1362-
return (_CFThreadRef)_beginthreadex(NULL, 0,
1362+
DWORD dwCreationFlags = 0;
1363+
DWORD dwStackSize = 0;
1364+
if (attrs.dwSizeOfAttributes >=
1365+
offsetof(struct _CFThreadAttributes,
1366+
dwThreadStackReservation) + sizeof(dwStackSize)) {
1367+
dwStackSize = attrs.dwThreadStackReservation;
1368+
if (dwStackSize) {
1369+
dwCreationFlags |= STACK_SIZE_PARAM_IS_A_RESERVATION;
1370+
}
1371+
}
1372+
1373+
return (_CFThreadRef)_beginthreadex(NULL, dwStackSize,
13631374
(_beginthreadex_proc_type)startfn,
1364-
context, 0, NULL);
1375+
context, dwCreationFlags, NULL);
13651376
#else
13661377
_CFThreadRef thread;
13671378
pthread_create(&thread, &attrs, startfn, context);

CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,10 @@ CF_EXPORT void CFLog1(CFLogLevel lev, CFStringRef message);
347347

348348
#if DEPLOYMENT_TARGET_WINDOWS
349349
typedef HANDLE _CFThreadRef;
350-
typedef DWORD _CFThreadAttributes;
350+
typedef struct _CFThreadAttributes {
351+
DWORD dwSizeOfAttributes;
352+
DWORD dwThreadStackReservation;
353+
} _CFThreadAttributes;
351354
typedef DWORD _CFThreadSpecificKey;
352355
#elif _POSIX_THREADS
353356
typedef pthread_t _CFThreadRef;

Foundation/Thread.swift

Lines changed: 101 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ private func NSThreadStart(_ context: UnsafeMutableRawPointer?) -> UnsafeMutable
3838
let thread: Thread = NSObject.unretainedReference(context!)
3939
Thread._currentThread.set(thread)
4040
if let name = thread.name {
41+
#if os(Windows)
42+
_CFThreadSetName(GetCurrentThread(), name)
43+
#else
4144
_CFThreadSetName(pthread_self(), name)
45+
#endif
4246
}
4347
thread._status = .executing
4448
thread.main()
@@ -55,7 +59,11 @@ open class Thread : NSObject {
5559
if Thread.isMainThread {
5660
return mainThread
5761
} else {
62+
#if os(Windows)
63+
return Thread(thread: GetCurrentThread())
64+
#else
5865
return Thread(thread: pthread_self())
66+
#endif
5967
}
6068
}
6169
}
@@ -89,6 +97,19 @@ open class Thread : NSObject {
8997
}
9098

9199
open class func sleep(until date: Date) {
100+
#if os(Windows)
101+
var hTimer: HANDLE = CreateWaitableTimerW(nil, TRUE, nil)
102+
// FIXME(compnerd) how to check that hTimer is not NULL?
103+
defer { CloseHandle(hTimer) }
104+
105+
// the timeout is in 100ns units
106+
var liTimeout: LARGE_INTEGER =
107+
LARGE_INTEGER(QuadPart: LONGLONG(date.timeIntervalSinceReferenceDate) * -10000000)
108+
if SetWaitableTimer(hTimer, &liTimeout, 0, nil, nil, FALSE) == FALSE {
109+
return
110+
}
111+
WaitForSingleObject(hTimer, WinSDK.INFINITE)
112+
#else
92113
let start_ut = CFGetSystemUptime()
93114
let start_at = CFAbsoluteTimeGetCurrent()
94115
let end_at = date.timeIntervalSinceReferenceDate
@@ -109,9 +130,23 @@ open class Thread : NSObject {
109130
}
110131
ti = end_ut - CFGetSystemUptime()
111132
}
133+
#endif
112134
}
113135

114136
open class func sleep(forTimeInterval interval: TimeInterval) {
137+
#if os(Windows)
138+
var hTimer: HANDLE = CreateWaitableTimerW(nil, TRUE, nil)
139+
// FIXME(compnerd) how to check that hTimer is not NULL?
140+
defer { CloseHandle(hTimer) }
141+
142+
// the timeout is in 100ns units
143+
var liTimeout: LARGE_INTEGER =
144+
LARGE_INTEGER(QuadPart: LONGLONG(interval) * -10000000)
145+
if SetWaitableTimer(hTimer, &liTimeout, 0, nil, nil, FALSE) == FALSE {
146+
return
147+
}
148+
WaitForSingleObject(hTimer, WinSDK.INFINITE)
149+
#else
115150
var ti = interval
116151
let start_ut = CFGetSystemUptime()
117152
let end_ut = start_ut + ti
@@ -130,37 +165,48 @@ open class Thread : NSObject {
130165
}
131166
ti = end_ut - CFGetSystemUptime()
132167
}
168+
#endif
133169
}
134170

135171
open class func exit() {
136172
Thread.current._status = .finished
173+
#if os(Windows)
174+
ExitThread(0)
175+
#else
137176
pthread_exit(nil)
177+
#endif
138178
}
139179

140180
internal var _main: () -> Void = {}
141-
private var _thread: pthread_t? = nil
181+
private var _thread: _CFThreadRef? = nil
142182

143-
#if CYGWIN
183+
#if os(Windows) && !CYGWIN
184+
internal var _attr: _CFThreadAttributes =
185+
_CFThreadAttributes(dwSizeOfAttributes: MemoryLayout<_CFThreadAttributes>.size,
186+
dwThreadStackReservation: 0)
187+
#elseif CYGWIN
144188
internal var _attr : pthread_attr_t? = nil
145189
#else
146190
internal var _attr = pthread_attr_t()
147191
#endif
148192
internal var _status = _NSThreadStatus.initialized
149193
internal var _cancelled = false
150-
194+
151195
open private(set) var threadDictionary: NSMutableDictionary = NSMutableDictionary()
152196

153-
internal init(thread: pthread_t) {
154-
// Note: even on Darwin this is a non-optional pthread_t; this is only used for valid threads, which are never null pointers.
197+
fileprivate init(thread: _CFThreadRef) {
198+
// Note: even on Darwin this is a non-optional _CFThreadRef; this is only used for valid threads, which are never null pointers.
155199
_thread = thread
156200
}
157201

158202
public override init() {
203+
#if !os(Windows)
159204
let _ = withUnsafeMutablePointer(to: &_attr) { attr in
160205
pthread_attr_init(attr)
161206
pthread_attr_setscope(attr, Int32(PTHREAD_SCOPE_SYSTEM))
162207
pthread_attr_setdetachstate(attr, Int32(PTHREAD_CREATE_DETACHED))
163208
}
209+
#endif
164210
}
165211

166212
public convenience init(block: @escaping () -> Swift.Void) {
@@ -202,6 +248,19 @@ open class Thread : NSObject {
202248
}
203249
}
204250

251+
#if os(Windows)
252+
open var stackSize: Int {
253+
get {
254+
var ulLowLimit: ULONG_PTR = 0
255+
var ulHighLimit: ULONG_PTR = 0
256+
GetCurrentThreadStackLimits(&ulLowLimit, &ulHighLimit)
257+
return Int(ulLowLimit)
258+
}
259+
set {
260+
_attr.dwThreadStackReservation = newValue
261+
}
262+
}
263+
#else
205264
open var stackSize: Int {
206265
get {
207266
var size: Int = 0
@@ -223,6 +282,7 @@ open class Thread : NSObject {
223282
}
224283
}
225284
}
285+
#endif
226286

227287
open var isExecuting: Bool {
228288
return _status == .executing
@@ -252,6 +312,9 @@ open class Thread : NSObject {
252312
defer { addrs.deallocate() }
253313
#if os(Android)
254314
let count = 0
315+
#elseif os(Windows)
316+
let count = RtlCaptureStackBackTrace(0, DWORD(maxSupportedStackDepth),
317+
addrs, nil)
255318
#else
256319
let count = backtrace(addrs, Int32(maxSupportedStackDepth))
257320
#endif
@@ -270,6 +333,39 @@ open class Thread : NSObject {
270333
open class var callStackSymbols: [String] {
271334
#if os(Android)
272335
return []
336+
#elseif os(Windows)
337+
let hProcess: HANDLE = GetCurrentProcess()
338+
SymSetOptions(DWORD(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS))
339+
if SymInitializeW(hProcess, nil, TRUE) == FALSE {
340+
return []
341+
}
342+
return backtraceAddresses { (addresses, count) in
343+
var symbols: [String] = []
344+
345+
var buffer: UnsafeMutablePointer<Int8> =
346+
UnsafeMutablePointer<Int8>
347+
.allocate(capacity: MemoryLayout<SYMBOL_INFO>.size + 128)
348+
defer { buffer.deallocate() }
349+
350+
buffer.withMemoryRebound(to: SYMBOL_INFO.self, capacity: 1) {
351+
$0.pointee.SizeOfStruct = ULONG(MemoryLayout<SYMBOL_INFO>.size)
352+
$0.pointee.MaxNameLen = 128
353+
354+
var address = addresses
355+
for _ in 1...count {
356+
var dwDisplacement: DWORD64 = 0
357+
if SymFromAddr(hProcess, unsafeBitCast(address.pointee,
358+
to: DWORD64.self),
359+
&dwDisplacement, $0) == FALSE {
360+
symbols.append("\($0.pointee)")
361+
} else {
362+
symbols.append(String(cString: &$0.pointee.Name))
363+
}
364+
address = address.successor()
365+
}
366+
}
367+
return symbols
368+
}
273369
#else
274370
return backtraceAddresses({ (addrs, count) in
275371
var symbols: [String] = []

0 commit comments

Comments
 (0)