Is it Good Idea to Use Swift for Windows / Linux Apps?

On that note, if you can reduce your usage of Foundation to just FoundationEssentials, then you can save on the imports. However many third-party packages that may not need all of Foundation still import it, so I imagine it will take some time for them to be audited and updated.

1 Like

Thread.isMainThread returning false seems like a bug. It would be interesting to know what GetCurrentThread() returns on the main actor. CoreFoundation stores the “main thread” in __initialPthread the first time pthread_main_np() is called (which is what Thread.isMainThread uses under the hood).

The first call to that should be in the constructor when CoreFoundation is loaded. But this is potentially fragile particularly if (Core)Foundation is being dynamically loaded. I would file an issue :)

This code on Windows

import Foundation
import WinSDK

func onMain() {
  print("is main thread = \(Thread.isMainThread); thread = \(GetCurrentThread())")
}

onMain()

let task = Task {
  onMain()
  precondition(#isolation === MainActor.shared)
  onMain()
  return 5
}

print(await task.value)

prints

is main thread = true; thread = Optional(0xfffffffffffffffe)
is main thread = false; thread = Optional(0xfffffffffffffffe)
is main thread = false; thread = Optional(0xfffffffffffffffe)
5

Code is taken from Equivalence of Task and MainActor.assumeIsolated - #18 by mattie

See also @MainActor behaves different on Windows and macOS

Issue already filed: `DispatchQueue.main` is not bound to main thread on Windows · Issue #846 · swiftlang/swift-corelibs-libdispatch · GitHub

1 Like

I've written toy programs in Swift with dwindows. It's a C API and ties in nicely with Swift's C integration. I've tested dwindows on Windows, Linux, Android and iOS.

The API uses GTK-style box packing and alignment, of which there are behavioral differences and bugs across platforms, of course. To me the best approach with dwindows is to use its webview wrapper, with interop between Javascript and native code. The Android APKs I've built with dwindows are quite beefy, but that's on Swift not dwindows. (And currently doesn't work with Swift 6 because something something SwiftGlibC not found.)

Another webview wrapper alternative is webview, Linux, macOS and Windows only.

But that hasn't deterred me...

Great to know. Best wishes! The pluses far outweigh the concerns?

embedded Linux.

I'm running Linux desktop.

It’s just a great language that’s fast to write code that “most likely” works first time. And its integration with C, C++ and now Java is unparalleled. So yes, count me a fan.

2 Likes

Thank you for the tip. I managed to rewrite my experimental code without importing Foundation or even FoundationEssentials. Now my runtime dependencies are just 5 files (6.51 MB), with largest file being swiftCore.dll (5.77 MB), which is completely acceptable.

1 Like

It returns a pseudohandle and is of little value. GetCurrentThreadId() would be better to use here.

Yes, that is roughly what occurs, except we call DuplicateHandle on the GetCurrentThread to resolve the handle to a value and store it. This will be used subsequently to compare the backing kernel object to check if the thread is the main thread. I wonder if the subsequent checks use the psuedo handle and give the wrong result.

1 Like

I rewrote the test code as follows:

import Foundation
import WinSDK

func report() {
    print("is main thread = \(Thread.isMainThread); threadID = \(GetCurrentThreadId()); is main actor = \(#isolation === MainActor.shared)")
}

print("is main thread = \(Thread.isMainThread); threadID = \(GetCurrentThreadId()); is main actor = \(#isolation === MainActor.shared)")
report()

let task = Task {
  print("is main thread = \(Thread.isMainThread); threadID = \(GetCurrentThreadId()); is main actor = \(#isolation === MainActor.shared)")
  report()
}

await task.value

Output:

is main thread = true; threadID = 1068; is main actor = true
is main thread = true; threadID = 1068; is main actor = false
is main thread = false; threadID = 1944; is main actor = true
is main thread = false; threadID = 1944; is main actor = false

It seems that here Thread.isMainThread returns the correct value[1]. The check of main actor returns different values when called directly and when called from the report() function, most likely because the function is nonisolated. It is not clear why main actor is true in the 3rd line of output (direct check from a task).


  1. But there still exist the GitHub issue where it reportedly returns wrong value: `DispatchQueue.main` is not bound to main thread on Windows · Issue #846 · swiftlang/swift-corelibs-libdispatch · GitHub ↩︎

I haven't looked at libdispatch yet, but in CoreFoundation, _CFThreadRef is a Windows handle, we could potentially make that a DWORD containing thread IDs but then _beginthreadex() would leak the handle. But I don't think anything actually ever closes the handle so maybe this is OK.

static _CFThreadRef __initialPthread; // zero is invalid thhread ID

CF_EXPORT int _NS_pthread_main_np() {
    if (__initialPthread == 0)
      __initialPthread = GetCurrentThreadId();
    return GetCurrentThreadId() == __initialPthread;
}

CF_EXPORT bool _NS_pthread_equal(_CFThreadRef t1, _CFThreadRef t2) {
  return t1 == t2;
}

Also _NS_pthread_main_np() should probably use atomic ops.

Yes, the integration with C/C++ is a huge part of migration (that's my situation.)

Some discussion about a bit less performance than C/C++ exists, but I have two perspectives about that: a.) Progress on performance has probably already been made and further progress is planned. and b.) Even as is, the performance is probably significantly superior to a typical garbage-collected and virtual/JIT runtime system.

1 Like

What are you using to verify DLL dependencies? I use DependenciesGui.exe, but am looking for a command line approach for automated distribution.

1 Like

I agree, although as a devil’s advocate position, the JVM is pretty well optimized and conversely, reference counting can be considered a form of GC (with pauses on dealloc storms). Still, I much prefer Swift :slight_smile:

I use copypedeps utility from

The -r command line parameter is especially useful because some dependencies may have their own dependencies.

2 Likes