-
Notifications
You must be signed in to change notification settings - Fork 382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug in lock-free SpscFifo::size()
& SpscFifo::is_full()
method
#2156
Comments
@elfenpiff I thought SoFi was an overflowing SPSC queue. With this in mind, would it be OK?. I also have doubts about your fix. You still have the problem as the other threads can change the world between the initial write and read position loads, or? I think the contract has to be that only one thread changes the write position |
@budrus you misread the issue title. This is related to the FIFO not the SOFI and the SOFI already has the do-while loop. We just need to port it over. |
Ahh, sorry I was coming from #2160 which is about SOFI. But the name is even |
@budrus yeah, you are right. I totally overlooked that. this is a complete misuse of the |
@elfenpiff closing this issue since the calling |
Required information
Races that lead to random behavior in
SpscFifo::size()
andSpscFifo::is_full()
method.The
SpscFifo
has the contract that at most one thread can callpush
and at most one thread can callpop
concurrently. All other methods can be called without restriction, since they do not modify the fifo.One reasonable assumption is that
size()
returns in the range of[0; capacity[
and that the size it returns was valid at some timepoint in the past.The implementation of the size method looks like:
return m_write_pos.load(std::memory_order_relaxed) - m_read_pos.load(std::memory_order_relaxed);
In the background, C++ will call the free function
uint64_t operator-(uint64_t lhs, uint64_t rhs);
. The evaluation order of the input arguments of the function can be freely chosen by the compiler.Let's assume the following scenario.
m_write_pos == 5
andm_read_pos == 3
SpscFifo::size()
from threadA
. After the value ofm_write_pos.load(..)
- (5) - is acquired the OS interrupts the method.B
and threadC
are performing multiplepush
andpop
operations according to the contract.The new state after the operations is
m_write_pos == 8
andm_read_pos == 6
.A
again, and it continues by reading the value ofm_read_pos.load(..)
- (6) -. Now the subtraction is performed5 - 6 == -1
which lead to an underflow. The underflow of an unsigned integer is well-defined, and the result is2^64
.size()
method returns2^64
.Result: the
size()
method returns garbage values in a concurrent scenario.is_full
method has the same problem, but the severity should not be so severe since it returns a bool, but it can still happen thatis_full
returns a state that never existed in the past. If an algorithm is requiring that at least at some point in time the state was valid, it will fail.Fix
Update the algorithm to
The text was updated successfully, but these errors were encountered: