Re: [GD-General] Totally Safe lock-free FIFO for arbitrary-sized items.
Brought to you by:
vexxed72
|
From: Andras B. <and...@gm...> - 2006-08-20 17:21:03
|
Hmm, I have one more question regarding your implementation: You put =
barrier() after the increments, because otherwise get() might think that=
=
the FIFO is empty, when it's not, and put() might think that the FIFO is=
=
full, when it's not. I believe that putting in these memory barriers won=
't =
solve the problem, because what if the threads get switched after the =
increment, but before executing the barrier()?? Even using =
interlocked_increment won't help, because only one thread is changing th=
e =
value, and again, what if the thread switch occurs after the store and =
before the increment?
Of course, none of this really matters, because it should not result in =
=
corrupt data in any way, I just think that those barriers are unnecessar=
y.
Andras
On Wed, 09 Aug 2006 14:10:50 -0600, Jon Watte <hp...@mi...> =
wrote:
>
> And, to confuse things even more, if you have strict aliasing options
> turned on, the compiler may even decide to re-order the stores. Thus,
> the Totally Safe lock-free FIFO for arbitrary-sized items looks like =
> this:
>
> /* Lock-free FIFO by Jon Watte.
> * Stored can be a type of any size.
> * Size must be power of 2.
> * This FIFO is correct on systems with strongly consistent
> * caches (such as Intel). It may require write barriers on
> * systems with weakly consistent caches. Insert such a
> * barrier in the inline function barrier() if that's the case.
> */
> template<typename Stored, size_t Size>
> class LockFreeFifo {
> public:
> LockFreeFifo() { head =3D 0; tail =3D 0; }
>
> /* Get an item from the FIFO.
> * Return true if an item was there and was copied,
> * false if the FIFO is empty.
> */
> bool get(Stored & out) {
> if (head - tail > 0) {
> out =3D fifo_[tail & (Size-1)];
> ++tail;
> barrier(); /* make sure writer knows FIFO isn't full */
> return true;
> }
> return false;
> }
>
> /* Put an item into the FIFO.
> * Return true if it fit and was copied,
> * false if the FIFO is full.
> */
> bool put(Stored & in) {
> if (head - tail < Size) {
> fifo_[head & (Size-1)] =3D in;
> barrier(); /* make sure FIFO is updated before head */
> ++head;
> barrier(); /* make sure reader can find the new item */
> return true;
> }
> return false;
> }
>
> private:
>
> inline void barrier() {
> /* If your platform has a weakly consistent cache (i e,
> * not Intel) then you need to insert a barrier instruction
> * (such as EIEIO on PPC) here.
> */
> #warning "Use the intrinsic specific to your platform, if needed."
> }
>
> /* Make these volatile, to avoid strict type aliasing analysis to =
=
> allow
> * the compiler to re-order stores.
> */
> volatile unsigned int head;
> volatile unsigned int tail;
> volatile Stored fifo_[Size];
> };
>
>
>
> Jon Watte wrote:
>> Note that the problem is with weakly _consistent_ cahces, not just
>> weakly ordered -- i e, if cache line A can flush before cache line B,=
it
>> doesn't matter, as long as the mutliple CPUs implement cache snooping=
to
>> stay consistent (which is the case on Intel). On PPC, you may end up
>> wanting to use the cache line reservations with a spin loop.
|