Skip to content

[ Thread Safety ] boost::lockfree::queue<T> data race when T is a pointer type #120

@summer-5

Description

@summer-5

Issue Summary

When using boost::lockfree::queue<T> with T as a pointer type (e.g., PGconn*), concurrent pop() operations may return the same front node to multiple threads, even if the return value is checked. This violates the queue's thread-safety guarantee.

Reproduction Steps

  1. Define a queue with pointer type (e.g., PGconn* pointers): boost::lockfree::queue<PGconn*> unsafe_queue(1024);
  2. Concurrently call push() and pop() from multiple threads and track the number of times each PGconn* is popped and pushed.

Expected Behavior

Each enqueued PGconn* should be pushed and popped equal times.

Actual Behavior

For boost::lockfree::queue<PGconn*>, some PGconn* pointers are popped much more times than they are pushed.
For boost::lockfree::queue<Conn> (where Conn wraps PGconn*), the behavior is correct (pop count == push count).

Example Code

// Thread-unsafe case
boost::lockfree::queue<PGconn*> unsafe_queue(1024);
// Concurrent pushes and pops will yield duplicate PGconn* pointers.

// Thread-safe case
struct Conn { PGconn* pgconn; };
boost::lockfree::queue<Conn> safe_queue(1024);
// Concurrent pushes and pops behave correctly.

Root Cause Analysis

The issue likely stems from how pointer types are handled internally in boost::lockfree::queue. The queue may fail to atomically update the head node when T is a pointer, leading to race conditions. This does not occur when T is a user-defined type (e.g., Conn), possibly due to stricter memory ordering or type-specific handling.

Every time an element is dequeued, its number of entries and exits is counted once. The number of pops is always greater than or equal to pushes. There has never been a situation where the number of pops is less than pushes. Therefore, the problem is likely to lie in the pop().

Environment

Boost version: 1.83.0
Compiler: gcc/g++ 13.3
OS: Linux x86_64

Additional Notes

The bug may also affect other pointer types (int*, custom class pointers, etc.), but only PGconn* is explicitly tested.
The lock-free context used for push() and pop() operations is confirmed to be correct (no external races).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions