text:
Fenced load in bounded queue / v1.2.3

I have commented on the relevant commit (#16), but too late as the commit was merged so I thought I would open an issue so this can be tracked.

Admittedly, I don't know what exactly is the role of the fence here. This fence does not exist in Dmitry Vyukov's original implementation of the queue, so I guess it was added as part of the modifications that ensure that this queue is linearisable (unlike the original queue).
That being said, if the cross-platform solution is indeed to place the load before the fence (this, I do not know) then I am pretty sure that the intel specialization that uses a lock operation instead of an mfence should also keep the load before.
I did look at https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html but could not see where it states that lock + mov (in this order) is equivalent to mov + mfence. In fact, the latest GCC does use the lock optimization and definitely preserves the order, i.e. mov + lock (see this godbolt: https://godbolt.org/z/o3rYdTvYv).

I thought the purpose of that fence was for subsequent relaxed loads (because there were cases where there was no preceding relaxed operation. e.g., pop in the unbounded queue). However, #16 is maybe wrong for the bounded queue because the bounded queue has a preceding relaxed operation. So, I've yanked 1.2.3 for now.
(The relevant commit is https://github.com/crossbeam-rs/crossbeam/commit/7d3f7f05e7dbd83720fc7d4e9308a33b9aeead92 (bounded) and https://github.com/crossbeam-rs/crossbeam/commit/05554e65289951e95735258500a9eeed3ac74065 (unbounded).)

I did look at https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html but could not see where it states that lock + mov (in this order) is equivalent to mov + mfence. In fact, the latest GCC does use the lock optimization and definitely preserves the order, i.e. mov + lock (see this godbolt: https://godbolt.org/z/o3rYdTvYv).

The table under "Note: there is an alternative mapping of C/C++11 to x86..." says that mfence+mov works as a SeqCst load.

alternative: MFENCE,MOV (from memory)

Also, the compare_exchange(...) line should IMO be fenced by two compiler_fence(Ordering::SeqCst), because compare_exchange does not prevent all compiler reordering like an atomic SeqCst fence does.

You are right.

Another thing to consider is whether a fetch-family instruction, as used by GCC, is not preferable than compare_exchange. At least on my machine a fetch is faster.

LLVM also uses lock or instead of lock cmpxchg. However, LLVM can optimize fetch_* to load (https://github.com/llvm/llvm-project/issues/56450), so inline asm is needed.

I did look at https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html but could not see where it states that lock + mov (in this order) is equivalent to mov + mfence. In fact, the latest GCC does use the lock optimization and definitely preserves the order, i.e. mov + lock (see this godbolt: https://godbolt.org/z/o3rYdTvYv).

The table under "Note: there is an alternative mapping of C/C++11 to x86..." says that mfence+mov works as a SeqCst load.

alternative: MFENCE,MOV (from memory)

I see, but the fact that a SeqCst load can be replaced by mfence + mov does not imply the reverse because an atomic fence provides stricter synchronization than a SeqCst load. My guess is that the original author did not only want to perform a SeqCst load but also to have a global barrier, otherwise I expect he would have used a load(Ordering::SeqCst) on non-intel platforms. Though again, I admit I do not fully understand the intent of this fence so I may be wrong about the original intent.

Another thing to consider is whether a fetch-family instruction, as used by GCC, is not preferable than compare_exchange. At least on my machine a fetch is faster.

LLVM also uses lock or instead of lock cmpxchg. However, LLVM can optimize fetch_* to load (llvm/llvm-project#56450), so inline asm is needed.

But it looks like this was a miscompilation? Or did this bug already make it into rustc?

The table under "Note: there is an alternative mapping of C/C++11 to x86..." says that mfence+mov works as a SeqCst load.

Is this compositional though? As in, when mixing both mappings, does the result still behave as intended?
With these mappings it is sometimes the case that the entire program needs to use the same mapping, or else things go wrong.

The table under "Note: there is an alternative mapping of C/C++11 to x86..." says that mfence+mov works as a SeqCst load.

Is this compositional though? As in, when mixing both mappings, does the result still behave as intended?

I don't know if we are not barking up the wrong tree: I don't think the original code was intended to work as a SeqCst load but rather as a full SeqCst barrier, which does much more (for an example see https://github.com/smol-rs/concurrent-queue/pull/18#issuecomment-1196077510).
When it comes to SeqCst fences, my understanding is that using a lock instruction as an alternative to an mfence is well-established, see e.g. https://github.com/microsoft/STL/issues/739 as well as many answers by Peter Cordes on SO.
Unfortunately the above thread does not directly answer the concern about mixing mfence and lock, but I would be a bit surprised if they couldn't be mixed.

With these mappings it is sometimes the case that the entire program needs to use the same mapping, or else things go wrong.

I believe this is true in the case where seqcst store is mapped as mov without fence, otherwise it should not be a problem.
fence+mov is also proposed for optimization of the idempotent RMW on x86 in https://www.hpl.hp.com/techreports/2012/HPL-2012-68.pdf, Section 7, and LLVM actually implements it.

After looking a bit more at the bounded queue, I have the feeling that the fences are meant to prevent spurious reports of empty (pop) or full (push) queues when a concurrent push (resp. pop) operation is ongoing.
Taking the example of pop, the fence ensures that the relaxed load of the head remains before the relaxed load of the tail, which in turns ensures that if a push is ongoing (i.e. the tail has moved but the stamp hasn't been updated and still matches the head), this will be observed. This would explain why it is a full fence and not simply a SeqCst load, as the later does not prevent reordering with relaxed loads.
That's just a theory though, these things are awfully tricky so I may very likely be wrong...

fence+mov is also proposed for optimization of the idempotent RMW on x86 in https://www.hpl.hp.com/techreports/2012/HPL-2012-68.pdf, Section 7, and LLVM actually implements it.

Note that even though this may have been an optimization back then (these links are dated from 2012 and 2014), now the situation seems to have reversed: in most cases an idempotent RMW will be faster than an mfence; some supporting elements:

this relatively recent comment: https://github.com/microsoft/STL/issues/739#issuecomment-618731257,
the fact that GCC>=11 compiles SeqCst fences into an idempotent RMW rather than an mfence as it used too,
this is what the original author of this crate stated so I assume this is what he observed too,
my own benchmarks ;-)

Closing -- 1.2.3 change has been reverted in 1.2.4 (https://github.com/smol-rs/concurrent-queue/pull/18), and x86 full_fence implementation is rewritten with inline assembly in https://github.com/smol-rs/concurrent-queue/pull/47.

authors: RalfJung sbarral taiki-e

repo: smol-rs/concurrent-queue


text:
Add the up/down Device and Interface methods

Add up/down methods to Device and Interface for placing the underlying
device in a up or down state. This interface will also be handy when
adding support for MLDv2.

Thanks for the PR! What's the intended use case?
It seems the new functionality is not yet used for anything, method calls are threaded through and exposed to the user. You can call custom methods on your particular Device with iface.device_mut().up() without needing to add them to the Device trait.
IMO we should add methods to Device when smoltcp itself needs them. Does MLDv2 require up/down?

Does MLDv2 require up/down?

Not explicitly, but there will be requests that we might want to send when we start/stop.

It seems the new functionality is not yet used for anything, method calls are threaded through and exposed to the user. You can
call custom methods on your particular Device with iface.device_mut().up() without needing to add them to the Device trait.
IMO we should add methods to Device when smoltcp itself needs them. Does MLDv2 require up/down?

Yeah, currently this isn't really much different than iface.device_mut.up(). You're 100% right about that. This doesn't have to complicate any existing device implementations, but is something that users would likely have to hand implement. I can't think of a use case where you would not want to implement a up/down method for your device.

Thinking about it, there are 2 related but different things we want:

Actively set the device in an up/down state.
Ask whether the device is up/down.

A device may come up/down "on its own". For example, an Ethernet being plugged/unplugged, WiFi connecting/disconnecting.... Or it may come up/down due to us manually setting it up/down. I imagine an Ethernet driver may report as "up" when we've set it as up AND there's a cable plugged in.
so perhaps all smoltcp needs is the second? Just an fn is_up(&self) -> bool in the trait. This would be useful for DHCP, MLD, IGMP and others to know when the network comes up, to react accordingly.
The first can be done with device-specific methods, calling them through iface.device_mut(). Smoltcp will never need to call them, just the user, so it makes sense for them not to be in the trait.

We discussed yesterday in the chat about the DHCP always needing 5s.
It turned out to be that smoltcp tried to do DHCP DISCOVER while the link was down, and the 5s came from the retry cooldown.
Something like this would give smoltcp opportunity to act accordingly to the link state, as @Dirbaio pointed out.
As it is not the user need to track the up/down state of the link and reset DHCP when the link goes down -> up.
Not sure if it's smoltcp's job to track this though, I'm not that familiar with its design.

source: gharchive/pull-request

authors: Dirbaio dlrobertson korken89

repo: smoltcp-rs/smoltcp

license_type: permissive

license_source: github-api


text:
Is there way to limit the frame generation to a set FPS

Is there way to limit the frame generation to a set FPS, i want to limit it to 30 or 60 fps.

Rate control is often done by the caller app (which can change the clock, etc.), the code sample doesn't have anything like that currently implemented

source: gharchive/issue

authors: ABugNamedBeetle smourier

repo: smourier/VCamSample

license: MIT