How can I cancel already posted callback:
getIoService()->post(boost::bind(&MyClass::myCallback, this));
and keep other posted callbacks untouched?
The problem is that I have some object that receives events from different thread and I post them to ioservice in order to handle events in main thread. What if at some point I want to delete my object - ioservice will try to execute already posted callbacks in destroyed object. And in this case I can't store any flag in object since it will be removed.
There is a possible solution to use enable_shared_from_this and shared_from_this(), but wondering whether another solution or not.
Thanks
As Sam answered, it is not possible to selectively cancel posted handlers.
If the goal is to prevent calling a member function on an object whose lifetime has expired, then using enable_shared_from_this is the idiomatic solution. One consequence of this approach is that the lifetime of the object is extended to be at least that of the handler. If the object's destructor can be deferred, then consider binding the object to a handler via shared_from_this().
On the other hand, if destruction needs to be immediate, then consider writing a functor that weakly binds to the instance. This question discusses binding to a weak_ptr, and provides some research/discussion links. Here is a simplified complete example of a functor that weakly binds to an object:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
/// #brief Mocked up type.
class MyClass:
public boost::enable_shared_from_this<MyClass>
{
public:
MyClass() { std::cout << "MyClass()" << std::endl; }
~MyClass() { std::cout << "~MyClass()" << std::endl; }
void action() { std::cout << "MyClass::action()" << std::endl; }
};
/// #brief weak_binder is a functor that binds a member function
/// to a weakly managed object instance. I.e. this
/// functor will not extend the life of the instance to
/// which it has been bound.
template <typename Fn,
typename C>
struct weak_binder
{
private:
typedef typename C::element_type element_type;
public:
/// #brief Constructor.
weak_binder(Fn& fn, C& c) : fn_(fn), c_(c)
{}
/// #brief Conditional invoke Fn if C still exists.
void operator()()
{
std::cout << "weak_binder::operator()" << std::endl;
// Create a shared pointer from the weak pointer. If
// succesful, then the object is still alive.
if (boost::shared_ptr<element_type> ptr = c_.lock())
{
// Invoke the function on the object.
(*ptr.*fn_)();
}
}
private:
Fn fn_;
boost::weak_ptr<element_type> c_;
};
/// #brief Helper function to create a functor that weakly
/// binds to a shared object.
template <typename Fn,
typename C>
weak_binder<Fn, C> weak_bind(Fn fn, C c)
{
return weak_binder<Fn, C>(fn, c);
}
int main()
{
boost::asio::io_service io_service;
boost::shared_ptr<MyClass> my_class = boost::make_shared<MyClass>();
// my_class will remain alive for this handler because a shared_ptr
// is bound to handler B, and handler B will only be destroyed after
// handler A has been destroyed.
io_service.post(weak_bind(&MyClass::action,
my_class->shared_from_this())); // A
// my_class will remain alive for this handler because it is bound
// via a shared_ptr.
io_service.post(boost::bind(&MyClass::action,
my_class->shared_from_this())); // B
// my_class will not be alive for this handler, because B will have
// been destroyed, and the my_class is reset before invoking the
// io_service.
io_service.post(weak_bind(&MyClass::action,
my_class->shared_from_this())); // C
// Reset the shared_ptr, resulting in the only remaining shared_ptr
// instance for my_class residing within handler B.
my_class.reset();
io_service.run();
}
And the resulting output:
MyClass()
weak_binder::operator()
MyClass::action()
MyClass::action()
~MyClass()
weak_binder::operator()
As can be observed, MyClass::action() is only invoked twice: once through weak_binder while the instance was alive (handler A), and once through the boost::bind where the instance is maintained via a shared_ptr (handler B). Handler C is invoked, but weak_binder::operator() detects that the instance has been destroyed, resulting in a silent no-op.
You cannot selectively cancel callbacks in such a manner through an io_service. One option is to move the logic to a higher level, such as inside of MyClass. A sample implementation may be:
class MyClass : public boost::enable_shared_from_this<MyClass>
{
public:
typedef boost::shared_ptr<MyClas> Ptr;
static Ptr create( boost::asio::io_service& io_service ) {
const Ptr result( new MyClass );
io_service.post( boost::bind(&MyClass::myCallback, result) );
return result;
}
void myCallback() {
if ( _canceled ) return;
}
void cancel() { _canceled = true; }
private:
MyClass() : _canceled(false) { }
private:
bool _canceled;
};
This class uses a boost::shared_ptr to enforce shared ownership semantics. Doing this gurantees the object lifetime will persist as long as the callback remains in the io_service queue before being dispatched.
Related
A very common scenario for a thread's callback is to inform the caller that it has finished his job. Here's the minimal example:
class task
{
public:
void operator()(std::function<void()>&& callback)
{
std::thread t
{
[c = std::move(callback)]{
std::this_thread::sleep_for(std::chrono::milliseconds{100});
c();
}
};
t.detach();
}
};
class processor
{
public:
void new_task()
{
auto& t = tasks.emplace_back();
t([this]{ if (true/*this object still alives*/) finish_callback(); });
}
private:
void finish_callback()
{
// ...
}
private:
std::vector<task> tasks;
};
In such scenario, we have to support the case when the child task overlives the parent/caller. Is there any common design pattern that allows us to do this?
Theoretically, we may use shared_ptr + enable_shared_from_this + weak_ptr trio in such case, but this forces us to always store the parent object on the heap under shared_ptr. I would rather like to not have such a limitation.
Suppose I have the following code
#include <thread>
#include <iostream>
#include <atomic>
struct FooBase {
void start(){
run_condition_ = true;
t_ = std::thread([this](){
thread_handler();
});
}
virtual ~FooBase(){
run_condition_ = false;
if(t_.joinable())
t_.join();
}
protected:
virtual void thread_handler() = 0;
std::atomic_bool run_condition_{false};
private:
std::thread t_;
};
struct Foo : FooBase {
void thread_handler() override {
while(run_condition_){
std::cout << "Foo derived thread.." << std::endl;
}
}
};
int main(){
Foo f;
f.start();
getchar();
return 0;
}
Here I think because the destructor of the derived class Foo is called before FooBase the thread_handler vtable lookup happens in the base class IF the thread has not yet joined (still running) when the destructor of Foo is done. Since FooBase::thread_handler is pure virtual I am essentially guranteed a sigabort.
How do I guard against this? I hack my way through by not having thread_handler as pure virtual
virtual void thread_handler(){}
But I am lost as to how I can guard against this in the baseclass itself, I can implement a join_thread interface in the base class and call this from every derived class, but this seems cumbersome.
There's two issues here, neither of which match precisely what you described.
Your thread only gets stopped in ~FooBase(). This means that if Foo::thread_handler ever reads or writes to any of its members, they will get destroyed out from under it before the thread is stopped.
It you get to the destructor fast enough, it's possible that start() won't have actually invoked thread_handler() on the new thread by the time Foo gets destroyed - which will lead to the pure virtual call.
Either way, you need to ensure that by the time Foo is destroyed, anything related to thread_handler is done. This implies that every derived class from FooBase has to have, in its destructor:
run_condition_ = false;
if (t_.joinable()) {
t_join();
}
Setting aside that this directly doesn't work because t_ is private (you could wrap that into a protected stop()), it's an awkward design if all of your derived classes need to do something special just to work. You could instead put FooBase into its own class that just takes an arbitrary callable as an argument:
class joining_thread {
public:
joining_thread() = default;
~joining_thread() { stop(); }
bool running() const { return run_condition_.load(); }
template <typename... Args>
void start(Args&&... args) {
run_condition_ = true;
t_ = std::thread(std::forward<Args>(args)...);
}
void stop() {
run_condition_ = false;
if (t_.joinable()) t_.join();
}
private:
std::thread t_;
std::atomic_bool run_condition_{false};
};
And then your Foo can just have that as a member:
class Foo {
public:
void start() {
t_.start([this]{
while (t_.running()) { ... }
});
}
private:
// just make me the last member, so it's destroyed first
joining_thread t_;
};
That's still a little awkward with the whole running() thing, but hopefully the idea makes sense.
What you describe is not possible. You call "start" after you have constructed the object. The object is at that stage valid. You have avoided the common problem of calling a virtual function in the constructor, which would have caused issues. There is something called a memory barrier that is implied by any thread calls, so you can count on the fact the new thread will start with a view of memory that existed at the point it was created. Any thing that existed AND was not changed, is fine.
Your problem (as described in another answer) is that you can exit and destroy the object (and it's vtable), before the thread is complete.
The simplest fix for this is use a packaged task. Calling "get" on the future ensures the task is finished before you continue. Consider the code below
#include "stdafx.h"
#include <thread>
#include <iostream>
#include <atomic>
#include <future>
int main()
{
std::atomic<bool> stop{ false };
std::future<void> sync;
std::packaged_task<void()> task([&stop]()
{
while (!stop)
{
std::cout << "Running\n";
}
});
std::thread thread([&task]() {task();});
getchar();
stop = true;
task.get_future().get();
thread.join();
return 0;
}
I'm looking to run a thread in a base class that constantly calls pure virtual method in that class that's overridden by a derived class.
For starting the thread, I've no issue as I can call an HasInitalized() function after it's been constructed. Therefore the thread is started after the class is fully constructed.
However, as the class' lifetime is managed by a shared_ptr, I cannot call a similar method for stopping the thread. If I stop the thread in the destructor, it will cause a seg-fault as the derived class is destroyed before the base and therefore will try to call a function that's not there.
I'm aware I can call a stop function from the derived class but would rather not have to on every instance of the derived class.
Is there a way around this.
Example:
#include "boost/thread.hpp"
class BaseClass
{
public:
BaseClass()
{
}
// Start the thread
void Start()
{
_thread = boost::thread(&BaseClass::ThreadLoop, this);
}
virtual ~BaseClass()
{
_thread.interrupt();
_thread.join();
}
private:
// Will loop until thread is interupted
void ThreadLoop()
{
try
{
while(true)
{
DoSomethingInDerivedClass();
boost::this_thread::interruption_point();
}
}
catch(...)
{
}
}
boost::thread _thread;
protected:
virtual void DoSomethingInDerivedClass() = 0;
};
class DerivedClass : public BaseClass
{
DerivedClass()
{
}
~DerivedClass()
{
// This gets called before base class destructor.
}
protected:
void DoSomethingInDerivedClass();
};
I don't think you will be able to avoid repeating the call to join the thread in the destructor of each derived class. If a thread depends on a non-static object o, then it's a good idea to have a clear ownership relation to guarantee the validity of the object:
The thread should own o and the destruction of o will be handled by the destructor of the thread object, after the joining.
o should own the thread and should join the thread in it's own destructor.
You've chosen the 2nd approach, except the thread depends on the derived object, but the derived object doesn't own the thread directly but through the sub-object (the base-object). Since the thread depends on the derived object, it must be joined in the derived object's destructor.
You should separate the two behaviours: a class to run and join the thread, the base class for the functional hierarchy.
class Runner {
public:
explicit Runner(std::shared_ptr<BaseClass> ptr) : m_ptr(ptr) {
m_thread = boost::thread(&Runner::ThreadLoop, this);
}
~Runner() {
m_thread.interrupt();
m_thread.join();
}
private:
void ThreadLoop() {
try {
while(true) {
m_ptr->DoSomethingInDerivedClass();
boost::this_thread::interruption_point();
}
} catch(...) {
}
}
std::shared_ptr<BaseClass> m_ptr;
std::thread m_thread;
};
My recommendation would be to use a weak_ptr to know when the object's lifetime is over:
The factory instantiates the (derived) object and stores it in a shared_ptr
The factory instantiates the watchdog class and passes it a weak_ptr to the new object
The watchdog thread can now check if the weak pointer is expired each time it needs to access it. When it is expired, the thread will terminate itself.
Here is an example (instead of a factory, I just used main):
#include <thread>
class BaseClass
{
public:
virtual ~BaseClass() = default;
virtual void DoSomethingInDerivedClass() = 0;
};
class DerivedClass : public BaseClass
{
public:
void DoSomethingInDerivedClass() override {}
};
// Will loop until weak_base expires
void ThreadLoop(std::weak_ptr<BaseClass> weak_base)
{
try
{
while (true)
{
std::shared_ptr<BaseClass> base = weak_base.lock();
if (base) {
base->DoSomethingInDerivedClass();
}
else {
break; // Base is gone. Terminate thread.
}
}
}
catch (...)
{
}
}
int main()
{
std::shared_ptr<DerivedClass> obj = std::make_shared<DerivedClass>();
std::thread([&] { ThreadLoop(obj); }).detach();
return 0;
}
Note that there is no need to explicitly stop the thread, since it will stop itself as soon as it detects that the object's lifetime is over. On the other hand, note that the thread may slightly outlive the lifetime of the being-watchted object, which could be considered bad design (it could e.g. defer program termination). I guess one could work around that by joining with the thread in the base class destructor, after signalling that it should terminate (if not already terminated).
I'm using some API to get a notification. Something like:
NOTIF_HANDLE register_for_notif(CALLBACK func, void* context_for_callback);
void unregister_for_notif(NOTIF_HANDLE notif_to_delete);
I want to wrap it in some decent RAII class that will set an event upon receiving the notification. My problem is how to synchronize it. I wrote something like this:
class NotifClass
{
public:
NotifClass(std::shared_ptr<MyEvent> event):
_event(event),
_notif_handle(register_for_notif(my_notif_callback, (void*)this))
// initialize some other stuff
{
// Initialize some more stuff
}
~NotifClass()
{
unregister_for_notif(_notif_handle);
}
void my_notif_callback(void* context)
{
((NotifClass*)context)->_event->set_event();
}
private:
std::shared_ptr<MyEvent> _event;
NOTIF_HANDLE _notif_handle;
};
But I'm worried about the callback being called during construction\destruction (Maybe in this specific example, shared_ptr will be fine with it, but maybe with other constructed classes it will not be the same).
I will say again - I don't want a very specific solution for this very specific class, but a more general solution for RAII when passing a callback.
Your concerns about synchronisation are a little misplaced.
To summarise your problem, you have some library with which you can register a callback function and (via the void* pointer, or similar) some resources upon which the function acts via a register() function. This same library also provides an unregister() function.
Within your code you neither can, nor should attempt to protect against the possibility that the library can call your callback function after, or while it is being unregistered via the unregister() function: it is the library's responsibility to ensure that the callback cannot be triggered while it is being or after it has been unregistered. The library should worry about synchonisation, mutexes and the rest of that gubbins, not you.
The two responsibilities of your code are to:
ensure you construct the resources upon which the callback acts before registering it, and
ensure that you unregister the callback before you destroy the resources upon which the callback acts.
This inverse order of construction vs destruction is exactly what C++ does with its member variables, and why compilers warn you when you initialise them in the 'wrong' order.
In terms of your example, you need to ensure that 1) register_for_notif() is called after the shared pointer is initialised and 2) unregister_for_notif() is called before the std::shared_ptr (or whatever) is destroyed.
The key to the latter is understanding the order of destruction in a destructor. For a recap, checkout the "Destruction sequence" section of the following cppreference.com page.
First, the body of the destructor is executed;
then the compiler calls the destructors for all non-static non-variant members of the class, in reverse order of declaration.
Your example code is, therefore "safe" (or as safe as it can be), because unregister_for_notif() is called in the destructor body, prior to the destruction of the member variable std::shared_ptr<MyEvent> _event.
An alternative (and in some sense more clearly RAII adherent) way to do this would be to separate the notification handle from the resources upon which the callback function operates by splitting it into its own class. E.g. something like:
class NotifHandle {
public:
NotifHandle(void (*callback_fn)(void *), void * context)
: _handle(register_for_notif(callback_fn, context)) {}
~NotifHandle() { unregister_for_notif(_handle); }
private:
NOTIF_HANDLE _handle;
};
class NotifClass {
public:
NotifClass(std::shared_ptr<MyEvent> event)
: _event(event),
_handle(my_notif_callback, (void*)this) {}
~NotifClass() {}
static void my_notif_callback(void* context) {
((NotifClass*)context)->_event->set_event();
}
private:
std::shared_ptr<MyEvent> _event;
NotifHandle _handle;
};
The important thing is the member variable declaration order: NotifHandle _handle is declared after the resource std::shared_ptr<MyEvent> _event, so the notification is guaranteed to be unregistered before the resource is destroyed.
You can do this with thread-safe accesses to a static container that holds pointers to your live instances. The RAII class constructor adds this to the container and the destructor removes it. The callback function checks the context against the container and returns if it is not present. It will look something like this (not tested):
class NotifyClass {
public:
NotifyClass(const std::shared_ptr<MyEvent>& event)
: event_(event) {
{
// Add to thread-safe collection of instances.
std::lock_guard<std::mutex> lock(mutex_);
instances_.insert(this);
}
// Register the callback at the end of the constructor to
// ensure initialization is complete.
handle_ = register_for_notif(&callback, this);
}
~NotifyClass() {
unregister_for_notif(handle_);
{
// Remove from thread-safe collection of instances.
std::lock_guard<std::mutex> lock(mutex_);
instances_.erase(this);
}
// Guaranteed not to be called from this point so
// further destruction is safe.
}
static void callback(void *context) {
std::shared_ptr<MyEvent> event;
{
// Ignore if the instance does not exist.
std::lock_guard<std::mutex> lock(mutex_);
if (instances_.count(context) == 0)
return;
NotifyClass *instance = static_cast<NotifyClass*>(context);
event = instance->event_;
}
event->set_event();
}
// Rule of Three. Implement if desired.
NotifyClass(const NotifyClass&) = delete;
NotifyClass& operator=(const NotifyClass&) = delete;
private:
// Synchronized associative container of instances.
static std::mutex mutex_;
static std::unordered_set<void*> instances_;
const std::shared_ptr<MyEvent> event_;
NOTIF_HANDLE handle_;
};
Note that the callback increments the shared pointer and releases the lock on the container before using the shared pointer. This prevents a potential deadlock if triggering MyEvent could synchronously create or destroy a NotifyClass instance.
Technically, the above could fail because of address re-use. That is, if one NotifyClass instance is destroyed and a new instance is immediately created at the exact same memory address, then an API callback meant for the old instance conceivably could be delivered to the new instance. For certain usages, perhaps even most usages, this will not matter. If it does matter, then the static container keys must be made globally unique. This can be done by replacing the set with a map and passing the map key instead of a pointer to the API, e.g.:
class NotifyClass {
public:
NotifyClass(const std::shared_ptr<MyEvent>& event)
: event_(event) {
{
// Add to thread-safe collection of instances.
std::lock_guard<std::mutex> lock(mutex_);
key_ = nextKey++;
instances_[key_] = this;
}
// Register the callback at the end of the constructor to
// ensure initialization is complete.
handle_ = register_for_notif(&callback, reinterpret_cast<void *>(key_));
}
~NotifyClass() {
unregister_for_notif(handle_);
{
// Remove from thread-safe collection of instances.
std::lock_guard<std::mutex> lock(mutex_);
instances_.erase(key_);
}
// Guaranteed not to be called from this point so
// further destruction is safe.
}
static void callback(void *context) {
// Ignore if the instance does not exist.
std::shared_ptr<MyEvent> event;
{
std::lock_guard<std::mutex> lock(mutex_);
uintptr_t key = reinterpret_cast<uintptr_t>(context);
auto i = instances_.find(key);
if (i == instances_.end())
return;
NotifyClass *instance = i->second;
event = instance->event_;
}
event->set_event();
}
// Rule of Three. Implement if desired.
NotifyClass(const NotifyClass&) = delete;
NotifyClass& operator=(const NotifyClass&) = delete;
private:
// Synchronized associative container of instances.
static std::mutex mutex_;
static uintptr_t nextKey_;
static std::unordered_map<unsigned long, NotifyClass*> instances_;
const std::shared_ptr<MyEvent> event_;
NOTIF_HANDLE handle_;
uintptr_t key_;
};
There are two common general solutions for RAII callbacks. One is a common interface to a shared_ptr of your object. The other is std::function.
Using a common interface allows for one smart_ptr to control the lifetime of all the callbacks for an object. This is similar to the observer pattern.
class Observer
{
public:
virtual ~Observer() {}
virtual void Callback1() = 0;
virtual void Callback2() = 0;
};
class MyEvent
{
public:
void SignalCallback1()
{
const auto lock = m_spListener.lock();
if (lock) lock->Callback1();
}
void SignalCallback2()
{
const auto lock = m_spListener.lock();
if (lock) lock->Callback2();
}
void RegisterCallbacks(std::shared_ptr<Observer> spListener)
{
m_spListener = spListener;
}
private:
std::weak_ptr<Observer> m_spListener;
};
class NotifClass : public Observer
{
public:
void Callback1() { std::cout << "NotifClass 1" << std::endl; }
void Callback2() { std::cout << "NotifClass 2" << std::endl; }
};
Example use.
MyEvent source;
{
auto notif = std::make_shared<NotifClass>();
source.RegisterCallbacks(notif);
source.SignalCallback1(); // Prints NotifClass 1
}
source.SignalCallback2(); // Doesn't print NotifClass 2
If you use a C style member pointer, you have to worry about the address of the object and the member callback. std::function can encapsulate these two things nicely with a lambda. This allows you to manage the lifetime of each callback individually.
class MyEvent
{
public:
void SignalCallback()
{
const auto lock = m_spListener.lock();
if (lock) (*lock)();
}
void RegisterCallback(std::shared_ptr<std::function<void(void)>> spListener)
{
m_spListener = spListener;
}
private:
std::weak_ptr<std::function<void(void)>> m_spListener;
};
class NotifClass
{
public:
void Callback() { std::cout << "NotifClass 1" << std::endl; }
};
Example use.
MyEvent source;
// This doesn't need to be a smart pointer.
auto notif = std::make_shared<NotifClass>();
{
auto callback = std::make_shared<std::function<void(void)>>(
[notif]()
{
notif->Callback();
});
notif = nullptr; // note the callback already captured notif and will keep it alive
source.RegisterCallback(callback);
source.SignalCallback(); // Prints NotifClass 1
}
source.SignalCallback(); // Doesn't print NotifClass 1
AFAICT, you are concerned that my_notif_callback can be called in parallel to the destructor and context can be a dangling pointer. That is a legitimate concern and I don't think you can solve it with a simple locking mechanism.
Instead, you probably need to use a combination of shared and weak pointers to avoid such dangling pointers. To solve your issue, for example, you can store the event in widget which is a shared_ptr and then you can create a weak_ptr to the widget and pass it as a context to register_for_notif.
In other words, NotifClass has as share_ptr to the Widget and the context is a weak_ptr to the Widget. If you can't lock the weak_ptr the class is already destructed:
class NotifClass
{
public:
NotifClass(const std::shared_ptr<MyEvent>& event):
_widget(std::make_shared<Widget>(event)),
_notif_handle(register_for_notif(my_notif_callback, (void*)new std::weak_ptr<Widget>(_widget)))
// initialize some other stuff
{
// Initialize some more stuff
}
~NotifClass()
{
unregister_for_notif(_notif_handle);
}
static void my_notif_callback(void* context)
{
auto ptr = ((std::weak_ptr<Widget>*)context)->lock();
// If destructed, do not set the event.
if (!ptr) {
return;
}
ptr->_event->set_event();
}
private:
struct Widget {
Widget(const std::shared_ptr<MyEvent>& event)
: _event(event) {}
std::shared_ptr<MyEvent> _event;
};
std::shared_ptr<Widget> _widget;
NOTIF_HANDLE _notif_handle;
};
Note that any functionality you want to add to your NotifClass should actually go into Widget. If you don't have such extra functionalities, you can skip the Widget indirection and use a weak_ptr to event as the context:
class NotifClass
{
public:
NotifClass(const std::shared_ptr<MyEvent>& event):
_event(event),
_notif_handle(register_for_notif(my_notif_callback, (void*)new std::weak_ptr<MyEvent>(event)))
// initialize some other stuff
{
// Initialize some more stuff
}
~NotifClass()
{
unregister_for_notif(_notif_handle);
}
static void my_notif_callback(void* context)
{
auto ptr = ((std::weak_ptr<MyEvent>*)context)->lock();
// If destructed, do not set the event.
if (!ptr) {
return;
}
ptr->set_event();
}
private:
std::shared_ptr<MyEvent> _event;
NOTIF_HANDLE _notif_handle;
};
Moderator warning: In order to request me, to delete this post, simply edit it!
make certain, that the callback object is fully constructed, before registering it. Means, make the callback object a separate class and the registration/deregistration wrapper a separate class.
Then you can chain both classes into a member or base class relationship.
struct A
{ CCallBackObject m_sCallback;
CRegistration m_sRegistration;
A(void)
:m_sCallback(),
m_sRegistration(&m_sCallback)
{
}
};
As an additional benefit, you can reuse the register/unregister wrapper...
If the callback could happen in another thread, I would redesign this software in order to avoid this.
E.g. one could make the shutdown of the main thread (e.g. destruction of this object) wait until all worker threads are shutdown/finished.
I'm having some troubles getting the design which I planned into C++ code.
I have a Manager class, which will create some instances of another class SomeClass. These instances will do some work in seperate threads. One of these instances will be designated as theOne and will notify the manager in some way when it is done performing its task. From that notification, the manager should get the results (if available) of all other instances with some function getData().
I thought I can realize this via a callback style notification. In the sense that the manager registers its getData() function in the instanciated class, which is the theOne, as a callback. If theOne has finished its job, it will just call its isDone() member, which executes the registered callback inside the manager. However doing it this way SomeClass always needs to have a reference to the Manager instance, which I actually don't want, because I only want the manager to know of SomeClass (instances) and not the other way around.
Is there a way I can tackle this problem? Or is my design just not suited for that? If it is not, is there another more suitable design with which I can realize this.
Here is the code I got (which is adapted from here). The calls I'm doing in main() will of course later be called in the respective class members.
#include <functional>
#include <iostream>
class Manager {
public:
void getData(int)
{
std::cout << "Got it!";
}
};
class SomeClass {
public:
SomeClass() : callback() { }
typedef std::function<void(Manager*, int)> Callback;
void registerCallback(const Callback& c)
{
callback = c;
}
void isDone(Manager* p)
{
callback(p, 42);
}
private:
Callback callback;
};
int main()
{
// This should be called in the manager class, which will instanciate its
// SomeClass workers. This works fine.
SomeClass sc;
sc.registerCallback(&Manager::getData);
// This should be called in SomeClass.
// The manager object is needed for the execution of the callback, which I
// want to omit.
Manager cb;
sc.isDone(&cb);
}
You can make the callback generic, and pass a function object that captures a pointer to the Manager:
// in SomeClass
typedef std::function<void(int)> Callback;
Callback callback;
and instantiate as needed. For example, in manager:
// in Manager
using namespace std::placeholders;
auto f1 = std::bind(&Manager::getData, this, _1);
auto f2 = [this](int i) { getData(i); }
SomeClass sc;
sc.registerCallback(f1);
sc.registerCallback(f2);
or anywhere else:
void foo(int i) { std::cout << "foo called with i = " << i << "\n"; }
SomeClass sc;
sc.registerCallback(foo);