Concurrency warnings about system variables

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.

-- Anton

4 Likes

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 :man_facepalming:.

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.

Karl,

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?

Done. [SR-15702] Using OSStatus constants generates concurrency warnings · Issue #57981 · apple/swift · GitHub

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.

There is one already: [SR-15015] Using Glibc.errno/Darwin.errno generates concurrency warnings · Issue #57344 · apple/swift · GitHub

2 Likes

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.

2 Likes

… 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 )-:

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

3 Likes

It gets worse :slight_smile: - 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.

(POSIX)

So you should always check it immediately before making any other calls. It's a weird system.

As it happens, the next version of POSIX (issue 8, the current latest is issue 7) will specify that free does not modify errno on success.

1 Like

Compiler can remember / restore errno in its inserted code, no?

Edit. Or call some malloc_internal / free_internal, that do not set errno in the first place.

But it's apparently not required to.