     /// - parameters:
     ///     - body: The closure that will accept the `pthread_t`.
     /// - returns: The value returned by `body`.
-    func withUnsafePthread<T>(_ body: (pthread_t) throws -> T) rethrows -> T {
-        return try body(self.pthread)
-    }
+#if !os(Windows)
+    internal func withUnsafePthread<T>(_ body: (pthread_t) throws -> T) rethrows -> T {
+        return try body(self.handle)
+    }
+#endif
 
     /// Get current name of the `NIOThread` or `nil` if not set.
     var currentName: String? {
-        get {
-            // 64 bytes should be good enough as on Linux the limit is usually 16 and it's very unlikely a user will ever set something longer anyway.
-            var chars: [CChar] = Array(repeating: 0, count: 64)
-            return chars.withUnsafeMutableBufferPointer { ptr in
-                guard sys_pthread_getname_np(self.pthread, ptr.baseAddress!, ptr.count) == 0 else {
-                    return nil
-                }
-
-                return String(decoding: UnsafeRawBufferPointer(UnsafeBufferPointer<CChar>(rebasing: ptr.prefix { char in
-                                  char != 0
-                              })),
-                              as: Unicode.UTF8.self)
+#if os(Windows)
+        var pszBuffer: PWSTR?
+        GetThreadDescription(self.handle, &pszBuffer)
+        guard let buffer = pszBuffer else { return nil }
+        let string: String = String(decodingCString: buffer, as: UTF16.self)
+        LocalFree(buffer)
+        return string
+#else
+        // 64 bytes should be good enough as on Linux the limit is usually 16
+        // and it's very unlikely a user will ever set something longer
+        // anyway.
+        var chars: [CChar] = Array(repeating: 0, count: 64)
+        return chars.withUnsafeMutableBufferPointer { ptr in
+            guard sys_pthread_getname_np(self.handle, ptr.baseAddress!, ptr.count) == 0 else {
+                return nil
             }
-        }
+
+            let buffer: UnsafeRawBufferPointer =
+                UnsafeRawBufferPointer(UnsafeBufferPointer<CChar>(rebasing: ptr.prefix { $0 != 0 }))
+            return String(decoding: buffer, as: Unicode.UTF8.self)
+        }
+#endif
     }
 
     func join() {
-        let err = pthread_join(self.pthread, nil)
+#if os(Windows)
+        let dwResult: DWORD = WaitForSingleObject(self.handle, INFINITE)
+        assert(dwResult == WAIT_OBJECT_0, "WaitForSingleObject: \(GetLastError())")
+#else
+        let err = pthread_join(self.handle, nil)
         assert(err == 0, "pthread_join failed with \(err)")
+#endif
     }
 
     /// Spawns and runs some task in a `NIOThread`.

    /* the actual type in there is `Box<(ThreadSpecificVariable<T>, T)>` but we can't use that as C functions can't capture (even types) */
    private typealias BoxedType = Box<(AnyObject, AnyObject)>

    private let key: pthread_key_t

    /// Initialize a new `ThreadSpecificVariable` without a current value (`currentValue == nil`).
    public init() {
        var key = pthread_key_t()
        let pthreadErr = pthread_key_create(&key) { ptr in
            Unmanaged<BoxedType>.fromOpaque((ptr as UnsafeMutableRawPointer?)!).release()
        }
        precondition(pthreadErr == 0, "pthread_key_create failed, error \(pthreadErr)")
        self.key = key
    }

    deinit {
        let pthreadErr = pthread_key_delete(self.key)
        precondition(pthreadErr == 0, "pthread_key_delete failed, error \(pthreadErr)")
    }

    /// Initialize a new `ThreadSpecificVariable` with `value` for the calling thread. After calling this, the calling

    public var currentValue: Value? {
        /// Get the current value for the calling thread.
        get {
          guard let raw = Key.get(for: self.key) else { return nil }
          // parenthesize the return value to silence the cast warning
          return (Unmanaged<BoxedType>
                   .fromOpaque(raw)
                   .takeUnretainedValue()
                   .value.1 as! Value)
        }

        /// Set the current value for the calling threads. The `currentValue` for all other threads remains unchanged.
        set {
            if let raw = Key.get(for: self.key) {
                Unmanaged<BoxedType>.fromOpaque(raw).release()
            }
            Key.set(value: newValue.map { Unmanaged.passRetained(Box((self, $0))).toOpaque() },
                    for: self.key)
        }
    }
}

extension NIOThread: Equatable {
    static func ==(lhs: NIOThread, rhs: NIOThread) -> Bool {
#if os(Windows)
        return CompareObjectHandles(lhs.handle, rhs.handle)
#else
        return pthread_equal(lhs.handle, rhs.handle) != 0
#endif
    }
}
