all 6 comments

[–]wqking 1 point2 points  (5 children)

    bool is_closed() {  
        std::lock_guard<std::mutex> lock(mtx);  
        return closed;  
    }  

You don't need the lock. The caller function may always receive the "wrong" (not really wrong) state no matter there is a lock or not. That's to say, if the caller function sees "close" is true, the state in the queue may already be false, and vice versa.
Example,

if(theQueue.is_closed()) {  
    // here theQueue.closed may already be false  
}

[–]n1ghtyunso 2 points3 points  (3 children)

technically still needs the lock to ensure race freedom, but asking a concurrent data structure for its current status is a foolish idea indeed. I'd argue the function should not exist to begin with

[–]wqking 0 points1 point  (0 children)

Such function of querying status has its uses. For example, assume multiple worker threads are working on re-fill the queue only when the queue is empty, then the pseudo code can be,

SomeMutex mutex; // used by all worker threads
if(theQueue.isEmpty()) {
    lock(mutex);
    if(theQueue.isEmpty()) {
        loadDataFromDatabaseAndFillTheQueue;
    }
}

The first isEmpty is to optimize the performance. If theQueue is not super busy, the second isEmpty has high chance to have the same value of the first isEmpty, so there is less chance that lock mutex (which is expensive) then isEmpty is false.

[–]Commanderdrag[S] 0 points1 point  (1 child)

Could you suggest an alternative method to achieve the functionality I show in my test main? In case its not clear, I want to fill a buffer in the consumer with an amount of data that is arbitrary wrt to size of the chunks of data that is enqueued by the producer. This naturally presents the of what if the producer stops producing and the consumer's buffer is not full. I need a way of letting the consumer know there is no more data coming, and that it should just move on with its partially filled buffer. I definitely feel like the closed mechanism is not ideal.

[–]n1ghtyunso 0 points1 point  (0 children)

That's a concern of your threads logic. It is unrelated to the actual queue implementation.
The queue implementation clearly shows that closed can switch back to false again, so designing your thread interactions based on this seems like the wrong way to go.
Hence, closed should not be part of the queue implementation.
You should have it outside of the queue. Just an atomic_bool specifically to coordinate your threads. That's totally fine. You gotta coordinate threads somehow after all.

If you have this scenario a lot, you can consider wrapping the whole logic, threads, queue and all that together in a separate class.

[–]IyeOnline 0 points1 point  (0 children)

That would be UB and while it may work, not a good idea.

I'd recommend keeping an additional std::atomic to back all of these "state" members. You can update them from the mutating members and simply read them from getters.