Considering the following code, is it possible that the threads may see the state of an object differently, despite they both refer by the same pointer?
using namespace std;
class ProducerAndConsumer{
class DummyObject {
public:
DummyObject() {
sprintf(a, "%d", rand());
}
private:
char a[1000];
};
mutex queue_mutex_;
queue<DummyObject *> queue_;
thread *t1, *t2;
void Produce() {
while (true) {
Sleep(1);
// constructing object without any explicit synchronization
DummyObject *dummy = new DummyObject();
{
lock_guard<mutex> guard(queue_mutex_);
if (queue_.size() > 1000) {
delete dummy;
continue;
}
queue_.push(dummy);
}
}
}
void Consume() {
while (true) {
Sleep(1);
DummyObject *dummy;
{
lock_guard<mutex> guard(queue_mutex_);
if (queue_.empty())
continue;
dummy = queue_.front();
queue_.pop();
}
// Do we have dummy object's visibility issues here?
delete dummy;
}
}
public:
ProducerAndConsumer() {
t1 = new thread(bind(&ProducerAndConsumer::Consume, this));
t2 = new thread(bind(&ProducerAndConsumer::Produce, this));
}
};
Could you say that this example is thread safe? Do mutexes enforce cache trashing? Do mutexes provide more functionality than memory barriers together with atomics?
Considering the following code, is it possible that the threads may see the state of an object differently, despite they both refer by the same pointer?
Answer: No.
Explanation: Acquiring a mutex is an acquire operation and releasing it is a release operation.
When dummy
is pushed onto the queue, the construction must take place prior to the push in order to maintain correct sequencing from the point of view of the pushing thread. The subsequent release of the mutex will ensure that a fence is issued to make the contents of the queue (and all other data you altered up until that point) visible to the other threads.
Similarly in the consumer thread, the sequencing of the assignment to dummy from the queue will be correctly ordered from the point of view of this thread. The acquisition of the mutex will ensure that memory in the DummyObject
is valid.
supporting quote from §1.10.7:
A synchronization operation without an associated memory location is a fence and can be either an acquire fence, a release fence, or both an acquire and release fence.
...
For example, a call that acquires a mutex will perform an acquire operation on the locations comprising the mutex. Correspondingly, a call that releases the same mutex will perform a release operation on those same locations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With