← Back to context

Comment by rcxdude

18 hours ago

The biggest headache will probably be it getting emitted in inappropriate contexts: where there is no actual means to sched_yield for whatever reason (bare metal, kernel, whatever). The second is just that the behaviour of the infinite loop changes: suddenly you're getting a bunch of extra system calls from your spinning thread instead of just a high CPU usage, which could disguise the issue or perhaps cause problems for other parts of the system. I don't see a good reason for the transformation: pretty much any time you are writing a bare infinite loop like this you don't want anything else to happen (it's also silly that it only happens with a particular spelling of an infinite loop, keeping the others still undefined).

"Emitted in inappropriate contexts" is very much one of the shapes I would expect unpleasant surprises to take, yeah. If you're writing code in C, you often need a lot of control over exactly what's happening. You might, for instance, be writing a .so for use with LD_PRELOAD, where it's important that you know everything being called so you can't accidentally recurse. You might be writing code for a sandbox, where you have an allowlist of permitted syscalls.

  • Exact control is only available in Assembly, minus unavoidable hardware flaws, everything else even a minor compiler update might change the outcome of the code.

> suddenly you're getting a bunch of extra system calls from your spinning thread instead of just a high CPU usage

Isn't the point that the loop was undefined behavior and so the spinning thread might not actually be spinning to begin with? It could be doing anything and sometimes did stuff like run the next block of code.

If you really want an infinite loop that does nothing (not sure why), you can do that now on any standards conforming compiler with some of the methods Sandor described.

  • It being undefined behaviour before doesn't make all possible definitions of that behaviour equally reasonable. The strangest thing to me is that I don't know who this behaviour definition is for. Infinite loops like this are a pattern that's almost entirely mutually exclusive with situations where a scheduler is relevant.

    I'm not too concerned about it being possible to make a loop at all (there's a lot of ways to add a 'side-effect' that will probably result in the same assembly), I'm concerned with a) the strange unwillingness to just define a sensible behaviour in this case, especially when C already has one (and GCC already in practice implements a slightly different but also perfectly reasonable interpretation, both of which work for all the normal ways someone might write such a loop), and b) the huge amount of existing code which uses this construct because for the most part compilers did not actually cause problems with it.

Impl specific. If you're building bare metal, pass -ffreestanding so GCC knows it's not allowed to call OS functions.

arguably there are already several places in the language where things like this can happen. for example, initializing a static function variable has certain thread safety guarantees (two threads entering the function won't step on each other), and while it's nice to not worry about it, this can certainly be a problem if you're trying to stay close to the metal and not pull in any dependencies.

> I don't see a good reason for the transformation: pretty much any time you are writing a bare infinite loop like this you don't want anything else to happen (it's also silly that it only happens with a particular spelling of an infinite loop, keeping the others still undefined).

I'm not disagreeing with you, but two things worth considering are 1) you don't always write loops like that _intentionally_; 2) if a bug like that slips into production system, it would be good to make sure it doesn't starve other threads.

  • This is true. Static initialization will often generate calls to lock functions and that can be a faff to deal with. But I don't see what the point of the sched_yield() is. Using it at all is already a code smell and calling it repeatedly in a tight loop is the kind of thing kernel developers were trying to beat out of application developers decades ago because it just isn't really very helpful (and often actively harmful) with any but the dumbest of schedulers. It's certainly not very useful for avoiding thread starvation.