TransWikia.com

simplfied observer pattern with std::shared_ptr/weak_ptr

Stack Overflow Asked by fatun on November 17, 2021

Here is a simplified observer pattern:

  1. one creator creates a profile when it starts and "destroy" it when it is done.
  2. zero, one or more observers try to "look at" the profile at any time.

To implement it, the trick is that observers shall refcnt profile, so the last observer (or creator) can safely destroy it.

I can do it without shared_ptr/weak_ptr, but I wonder if using them can avoid re-inventing wheels.

Here is my code:

#include <iostream>
#include <memory>
#include <thread>
#include <cassert>

volatile bool playing = true;

class Profile {
public:
    int a_;
    Profile(int v) {a_ = v;}
};

std::shared_ptr<Profile> g_profile{ nullptr };

void observer() {
    do {
        // observe profile if I can
        std::weak_ptr<Profile> weak = g_profile;
        if (auto prof = weak.lock()) {
            auto a = prof->a_;
            // if prof is stable, I shall see the same a_
            assert(a == prof->a_);
        }
        else {
            std::cout << ".";
        }
    } while (playing);
}

void creator() {
    do {
        // create profile when I start
        g_profile.reset(new Profile(std::rand()));
        std::weak_ptr<Profile> weak = g_profile;
        assert(weak.lock() != nullptr);
        
        // doing some work ...

        // destroy profile when I am done
        g_profile.reset();
    } while (playing);
}

void timer() {
    std::this_thread::sleep_for(std::chrono::seconds(10));
    playing = false;
}

int main() {
    std::thread cr{ creator };
    std::thread ob{ observer };
    std::thread tm{ timer };
    cr.join();ob.join();tm.join();

    // no memory leak
}

But the program crashes either at
std::weak_ptr<Profile> weak = g_profile or assert(a == prof->a_). So here are my questions:

  1. do you have a pointer implementing observer pattern (or variant) with shared_ptr/weak_ptr?
  2. what’s wrong with the above code? Can you make it right?

One Answer

You have undefined bahavior when one thread reads from the shared pointer g_profile (observer) while the other thread writes to it (when creator calls std::shared_ptr::reset)

If you want to use the shared_ptr from two threads you'll have to use a lock or atomic_shared_ptr.

Also volatile does not guarantee any synchronization as it does in java. See this answer.

Answered by Mike van Dyke on November 17, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP