Libdispatch API - how to support LLP64 platforms?

Libdispatch uses the long and unsigned long types in its implementation and API. This means that all functions which use those types work on 32bit values on LLP64 platforms and on 64bit values on LLP64 platforms like Windows.

Now there isn't much of a problem here as long as we care just about the libdispatch C API. But things get murky once we look at the Swift API because it assumes that long == Int would be universally true which isn't the case on Windows and in fact this is the major reason why the Swift overlay doesn't even compile on Windows.

Some possible solutions to this:

  1. Stick #if os(Windows) checks into the Swift overlay to handle the difference between non-Windows and Windows like this:

    public init(value: Int) {
       #if os(Windows)
       let rawValue = Int32(value)
       let rawValue = Int(value)
       __wrapped = dispatch_semaphore_create(rawValue)

    Problem: this solution makes it look like as if the Swift API supports the full range of 64bit values but the underlying implementation actually doesn't.

  2. Make the libdispatch Swift API vend different signatures on LLP64 platforms:

    #if os(Windows)
    public init(value: Int32)
    public init(value: Int) 

    Problem: introduces more cross-platform differences into Swift code which has to work on macOS, Linux and Windows. Code which uses just libdispatch Swift APIs should not have to work around syntactical differences between different platforms.

  3. Change the libdisaptch implementation and API such that it uses explicitly sized types from C's inttypes.h. E.g. use int64_t instead of long if we really want 64bit values.

Ideas, options?

cc @MadCoder @compnerd

Should the overlay use the CLong and CUnsignedLong type aliases?

public init(value: Int) {
  __wrapped = dispatch_semaphore_create(CLong(value))

While CLong and CUnsignedLong would work here, I feel like the best solution is for the Dispatch headers to use size_t or to typedef the arguments per-platform. The intention is to vend a machine-width type, so the API should be explicit about that.

Complicating matters a little is that Swift on Windows won't import any of the standard types (int, long, long long etc.) as Int, since there's no logical way to do that. I described my reasoning behind those mappings in the linked PR. That means that even if you e.g. typedef long long machine_width_int that will still get imported as an Int64, so #if blocks would still be needed in the overlays.

On the other hand, size_t does get imported as Int on all platforms, and I think a few other types do as well; using those types throughout Dispatch and Foundation seems best.

1 Like

I agree and think that replacing uses of long with ssize_t and unsigned long with size_t in both the API and implementation of libdispatch is probably the best way forward since this would give us a consistent cross-platform Swift API which is my opinion a very important goal here. Using ssize_t and size_t should allow us to preserve the signedness of the types in use today.

The Windows SDK doesn't define ssize_t directly but we can define it like this in the base.h:

#include <basetsd.h>
typedef SSIZE_T size_t

From my crude testing it looks like that both size_t and ssize_t are mapped to Swift as Int.

I guess I'll give this a shot and see what happens.