Merge pull request #1279 from spevans/pr_thread_name

diff --git a/CoreFoundation/Base.subproj/CFPlatform.c b/CoreFoundation/Base.subproj/CFPlatform.c
index 70597d1..5aefe20 100644
--- a/CoreFoundation/Base.subproj/CFPlatform.c
+++ b/CoreFoundation/Base.subproj/CFPlatform.c
@@ -1316,15 +1316,18 @@
     return thread;
 }
 
-CF_SWIFT_EXPORT void _CFThreadSetName(const char *_Nullable name) {
+CF_SWIFT_EXPORT int _CFThreadSetName(pthread_t thread, const char *_Nonnull name) {
 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
-    pthread_setname_np(name);
+    if (pthread_equal(pthread_self(), thread)) {
+        return pthread_setname_np(name);
+    }
+    return EINVAL;
 #elif DEPLOYMENT_TARGET_LINUX
-    pthread_setname_np(pthread_self(), name);
+    return pthread_setname_np(thread, name);
 #endif
 }
 
-CF_SWIFT_EXPORT int _CFThreadGetName(char *buf, int length) {
+CF_SWIFT_EXPORT int _CFThreadGetName(char *_Nonnull buf, int length) {
 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
     return pthread_getname_np(pthread_self(), buf, length);
 #elif DEPLOYMENT_TARGET_ANDROID
diff --git a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
index 2651cd8..57e2f12 100644
--- a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
+++ b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
@@ -316,8 +316,8 @@
 
 CF_EXPORT _CFThreadRef _CFThreadCreate(const _CFThreadAttributes attrs, void *_Nullable (* _Nonnull startfn)(void *_Nullable), void *restrict _Nullable context);
 
-CF_SWIFT_EXPORT void _CFThreadSetName(const char *_Nullable name);
-CF_SWIFT_EXPORT int _CFThreadGetName(char *buf, int length);
+CF_SWIFT_EXPORT int _CFThreadSetName(pthread_t thread, const char *_Nonnull name);
+CF_SWIFT_EXPORT int _CFThreadGetName(char *_Nonnull buf, int length);
 
 CF_EXPORT Boolean _CFCharacterSetIsLongCharacterMember(CFCharacterSetRef theSet, UTF32Char theChar);
 CF_EXPORT CFCharacterSetRef _CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet);
diff --git a/Foundation/Thread.swift b/Foundation/Thread.swift
index dae27cd..1489969 100644
--- a/Foundation/Thread.swift
+++ b/Foundation/Thread.swift
@@ -43,6 +43,9 @@
 private func NSThreadStart(_ context: UnsafeMutableRawPointer?) -> UnsafeMutableRawPointer? {
     let thread: Thread = NSObject.unretainedReference(context!)
     Thread._currentThread.set(thread)
+    if let name = thread.name {
+        _CFThreadSetName(pthread_self(), name)
+    }
     thread._status = .executing
     thread.main()
     thread._status = .finished
@@ -141,11 +144,12 @@
     }
 
     internal var _main: () -> Void = {}
-#if os(OSX) || os(iOS) || CYGWIN
-    private var _thread: pthread_t? = nil
-#elseif os(Linux) || os(Android)
+#if os(Android)
     private var _thread = pthread_t()
+#else
+    private var _thread: pthread_t? = nil
 #endif
+
 #if CYGWIN
     internal var _attr : pthread_attr_t? = nil
 #else
@@ -202,8 +206,8 @@
 
     open var name: String? {
         didSet {
-            if _thread == Thread.current._thread {
-                _CFThreadSetName(name)
+            if let thread = _thread {
+                _CFThreadSetName(thread, name ?? "" )
             }
         }
     }
diff --git a/TestFoundation/TestThread.swift b/TestFoundation/TestThread.swift
index f5b224b..6f09c8d 100644
--- a/TestFoundation/TestThread.swift
+++ b/TestFoundation/TestThread.swift
@@ -59,34 +59,70 @@
     }
     
     func test_threadName() {
-        let thread = Thread()
-        XCTAssertNil(thread.name)
 
-        func getPThreadName() -> String? {
-            var buf = [Int8](repeating: 0, count: 16)
+        // Compare the name set in pthreads()
+        func compareThreadName(to name: String) {
+            var buf = [Int8](repeating: 0, count: 128)
+#if os(OSX) || os(iOS)
+            // Dont use _CF functions on macOS as it will break testing with Darwin's native Foundation.
+            let r = pthread_getname_np(pthread_self(), &buf, buf.count)
+#else
             let r = _CFThreadGetName(&buf, Int32(buf.count))
-
-            guard r == 0 else {
-                return nil
+#endif
+            if r == 0 {
+                XCTAssertEqual(String(cString: buf), name)
+            } else {
+                XCTFail("Cant get thread name")
             }
-            return String(cString: buf)
         }
 
+        // No name is set initially
+        XCTAssertNil(Thread.current.name)
+
+#if os(Linux) // Linux sets the initial thread name to the process name.
+        compareThreadName(to: "TestFoundation")
+#else
+        compareThreadName(to: "")
+#endif
+        Thread.current.name = "mainThread"
+        XCTAssertEqual(Thread.mainThread.name, "mainThread")
+
+        let condition = NSCondition()
+        condition.lock()
+
         let thread2 = Thread() {
-            Thread.current.name = "Thread2"
-            XCTAssertEqual(Thread.current.name, "Thread2")
-            XCTAssertEqual(Thread.current.name, getPThreadName())
-        }
+            XCTAssertEqual(Thread.current.name, "Thread2-1")
+            compareThreadName(to: "Thread2-1")
 
+            Thread.current.name = "Thread2-2"
+            XCTAssertEqual(Thread.current.name, "Thread2-2")
+            compareThreadName(to: "Thread2-2")
+
+            Thread.current.name = "12345678901234567890"
+            XCTAssertEqual(Thread.current.name, "12345678901234567890")
+#if os(OSX) || os(iOS)
+            compareThreadName(to: "12345678901234567890")
+#elseif os(Linux)
+            // pthread_setname_np() only allows 15 characters on Linux, so setting it fails
+            // and the previous name will still be there.
+            compareThreadName(to: "Thread2-2")
+#endif
+            condition.lock()
+            condition.signal()
+            condition.unlock()
+        }
+        thread2.name = "Thread2-1"
         thread2.start()
 
-        Thread.current.name = "CurrentThread"
-        XCTAssertEqual(Thread.current.name, getPThreadName())
+        // Allow 1 second for thread2 to finish
+        XCTAssertTrue(condition.wait(until: Date(timeIntervalSinceNow: 1)))
+        condition.unlock()
 
+        XCTAssertEqual(Thread.current.name, "mainThread")
+        XCTAssertEqual(Thread.mainThread.name, "mainThread")
         let thread3 = Thread()
         thread3.name = "Thread3"
         XCTAssertEqual(thread3.name, "Thread3")
-        XCTAssertNotEqual(thread3.name, getPThreadName())
     }
 
     func test_mainThread() {