Swift standard library on platforms without threading support?

I'm very interested know if there's an easy way to get existing Swift standard library working on platforms that don't support threading, like AVR and WebAssembly.

From what I saw in the standard library and runtime source code, there's a strong assumption of threading support in stdlib, e.g. all reference counting code relies on working <atomic> header, which can't be imported on platforms without threading:

#ifdef _LIBCPP_HAS_NO_THREADS
#error <atomic> is not supported on this single threaded system
#endif

Maybe someone have already done some work to port the standard library and runtime to an environment without threading support or maybe there's some preprocessor or build script configuration flag that I missed?

Thanks.

I'm not a developer of Swift or stdlib but I think there is a good chance that move-only types proposed for Swift 5 would solve this issue.

@carlos4242 has been working on an alternative standard library implementation for AVR:

It would be interesting to add a configuration flag to disable threading support in the "normal" runtime and standard library. Nobody's done that yet to my knowledge. For that specific issue, though, it is incorrect to avoid using <atomic> in a single-threaded system; I'm not sure why that #if is there. <atomic> is still meaningful for a single-threaded system for atomicity relative to signals and other preemption mechanisms.

4 Likes

As far as I understand move only types will not be introduced with Swift 5, there is no such proposal, but there is consensus that they should come eventually someday and somehow. Current proposals that mention and are motivated by the introduction of move only types aim to avoid post Swift 5 ABI breakage, when they really become an issue…

2 Likes

Move-only types won't impact whether the runtime needs to be thread safe on multithreaded platforms.

4 Likes

My uStdlib is very, very basic and is almost more of a research project than anything else. I'm very happy to share what I have and the progress/thoughts I'm making but I'm wondering where is best to put it? It's not production ready in any way (and may never be any time soon). Also it's currently fairly heavily slanted to the sort of things i personally want in my AVR code.

What would be a good way to share it?

(The 16 bit pointer fixes for the compiler will come as a separate PR which I think will be mergeable. The PR for AVR support on it's own will be a different patch!)

2 Likes

Well, that's what libcxx has there at the moment ¯\_(ツ)_/¯

I'm not quite sure though if signals make any sense on WebAssembly or AVR, especially if programs for the latter are supposed to run on bare metal?

What would be the best approach for this then? My initial plan was to do something like

#ifndef __wasm__ 
#include <atomic> 
#endif

in Swift runtime headers and to reimplement all users of <atomic> with a nonatomic #ifdefed version? Or maybe it's worth implementing something like SingleThreadedAtomic.{h,cpp} that would stub all required atomic primitives with single-threaded "no-op" implementation?

I’m not familiar enough with wasm, but on bare metal hardware platforms, interrupts could still be a concern. It’s unfortunate libc++ doesn’t have a single-threaded atomic implementation, but having a stubbed-out version of atomic, mutex, and other synchronization primitives would be nice to minimize the amount of conditional code we need in the runtime itself.

Just FYI, JavaScript has Atomics in its spec, but it has more or less been stalled for the last 3 years, probably due to security vulnerabilities (Spectre and Meltdown). ECMAScript® 2024 Language Specification

Discussions suggest it’s only temporarily disabled, but I wouldn’t hold my breath SharedArrayBuffer - JavaScript | MDN

There currently isn’t good browser support Can I use... Support tables for HTML5, CSS3, etc

I think it’s intended that WebAssembly gets a similar implementation after MVP. But I imagine it will have the same implementation complexities. https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md

Overall with that in mind I think this is the most pragmatic, expedient, maintainable, and future proof solution:

It seems like those Atomics would primarily be for use with shared memory among web workers, since Javascript is not normally multithreaded. The Swift runtime would probably not allocate runtime data structures for a process into shared memory, so it doesn't seem immediately necessary to use those.

Yeah, I don’t think there’s any immediate need for trying to use WASM atomics, especially as the equivalent is barely supported in JavaScript.

I just wanted to make sure those resources were known, as there seemed to be some uncertainty in this thread about WASM’s current and future threading capabilities.

Those are the only atomics in WASM. The last link describes their use well, but it’s only for shared memory. From that link;

All atomic memory accesses require a shared linear memory

There’s precedent for implementing malloc in linear memory in asm.js, but I’m not suggesting we do that here, they had other reasons for doing that (ie. mixing gc with manual memory management).

For context: how to implement this for Swift is still an ongoing discussion on GitHub.

Do you have a link to the discussion on Github?

The conversation tracks WASM implementation progress in general, the atomics part starts roughly here Unable to compile a simple Swift file (maybe not possible?) · Issue #2427 · emscripten-core/emscripten · GitHub

It’s more or less the most active discussion of progress on WASM with Swift that I’m aware of.

could you share a github repo? I'd be more than happy to look at it and help if I can :)

AVR does not support libc++ nor STL.

as with atomic, it is indeed needed on bare metal AVR as soon as you're dealing with 16bit or more variables* that are shared with ISR and/or threads if you're using a RTOS.

*for 8-bit AVR MCU

I'm diving back in here because I'm looking at getting some significant parts of the Swift runtime onto AVR now (classes, reference counting).

I'm hitting exactly this pain point. I'm using libcxx to supply libstdc++ type compatibility for AVR so that the runtime will compile and link. Headers like type_traits and atomic will need to work I think. I've revived a discussion with the avr-rust guys, who are the most active llvm people when it comes to AVR. If they don't have an answer, I'll probably need to reach out to llvm's libcxx people or the llvm dev mailing list to get some color on this subject.

Like @Joe_Groff says, I think this does need some support in atomic, because of interrupts on AVR.

In my opinion, signals and threads are not a thing on normal bare metal, which makes life a lot simpler. But interrupts are. And I wouldn't want to tell developers "you can never access class instances, or any heap allocated value types like Arrays, from within interrupt handlers". That would make interrupt handlers pretty hard to use for anything of substance. I might make it a stipulation in the first versions but a working bare metal libcxx seems a better solution.

(FYI, my stdlib isn't yet open source but will be one day. This is partly for personal reasons, but mostly because I don't have the time to make it anywhere near good enough for the wider world to look at! It is very, very hacked together, very AVR specific, in fact very atmega328p specific and very much an early support version, and probably very buggy. That all said, I'm happy to privately send people my work in progress code snippets where they have an interest.)

2 Likes