I have enabled the concurrency warnings in my project. Now I'm getting some warnings I do not know how to fix. Specifically, the compiler complains about system variables and constants:
if y == noErr { ...
Reference to var 'noErr' is not concurrency-safe because it involves shared mutable state
let x = errno
Reference to var 'errno' is not concurrency-safe because it involves shared mutable state
Any suggestion on how to fix the warnings? Thanks.
Oh, that's rather unfortunate. You can safely ignore this warning - noErr obviously does not involve mutable state, shared or otherwise. It's a constant .
It's simple to work-around this issue by declaring your own let noErr: OSStatus = 0 in Swift. But the compiler should be improved to detect this. Could you please file a bug?
errno is mutable state, so at least that much is correct. However, this warning is still overly-enthusiastic, as every modern system implements it with a thread-local variable (so it is not shared mutable state, in the sense that there is no risk of overlapping writes or read/writes), so it is safe. That was a part of POSIX.1c, from 1995. Unfortunately, the compiler doesn't know this.
As a workaround, you could define a C function which gets errno and import that from your Swift project.
It would be great if you could file a bug for this issue, as well.
Thank you for the quick reply and suggested workarounds.
It's simple to work-around this issue by declaring your own let noErr: OSStatus = 0 in Swift. But the compiler should be improved to detect this. Could you please file a bug?
The warning is somewhat correct because if you have an executor hop in-between the call and the fetch to errno that thread local won't apply, right?
I would keep all access to errno in a non async function to make sure that nothing else funky happens. Plus I would guess most times you want to convert that into a throw anyhow.
It depends what you mean by "won't apply"; it will be safe, because only one access is possible at a time on each thread. No synchronisation is needed.
However, if somebody else gets scheduled on your thread (because you gave it up by awaiting on something, and the executor decided to let somebody else have it), they may change the value, so when your code resumes, it won't see the correct error code. Also, the executor might just resume your code on an entirely different thread to the one you were previously on.
But as long as you don't give up the thread before reading errno, it should be fine AFAICT.
Well, as fine as it ever was, which is not very )-:
Ignoring the async/await stuff, the Swift compiler doesn’t offer any guarantees about where it inserts memory management code and both malloc and free set errno. So you could imagine having problems with code like this:
var bytes = [UInt8]("Hello Cruel World!".utf8)
let bytesWritten = write(fd, &bytes, bytes.count)
// A
if bytesWritten < 0 {
// B
throw MyError(errno)
}
if the compiler decided to free the memory backing bytes at either point A or B.
Unfortunately I don’t see a way around this on current systems )-:
It gets worse - unless otherwise stated, even successful operations may set errno:
The value of errno may be set to nonzero by a C standard library function call whether or not there is an error, provided the use of errno is not documented in the description of the function.
[...]
It is meaningful for a program to inspect the contents of errno only after an error might have occurred. More precisely, errno is meaningful only after a library function that sets errno on error has returned an error code.
(C Standard)
The value of errno shall be defined only after a call to a function for which it is explicitly stated to be set and until it is changed by the next function call or if the application assigns it a value. The value of errno should only be examined when it is indicated to be valid by a function's return value.
[...]
No function in this volume of POSIX.1-2017 shall set errno to 0. The setting of errno after a successful call to a function is unspecified unless the description of that function specifies that errno shall not be modified.