Home > Blockchain >  how to call a function when an asynchronous task is already done?
how to call a function when an asynchronous task is already done?

Time:03-30

I have a set of classes look like this:

class A {
public: 
   std::unique_ptr<B> b; 
   void triggerAsynchronously() {
       // this work is submitted to a queue. 
       b->getC()->signalAsync(); 
   }
}; 

class B {
public: 
   std::shared_ptr<C> c;
   std::shared_ptr<C> getC() const {
     return c; 
   }
   void doSomethingWithSignalFromClassC() {}
};

class C {
public: 
  void signalAsync() {
    // This function is submitted to a queue, and wait until its turn to be executed. 
    // I want to trigger A::b::doSomethingWithSignalFromC() once this function is done (aka end of this block)
  }
}

C::signalAsync() is triggered by A::triggerSignalCAsynchronously(). Note that A::triggerSignalCAsynchronously() returns immediately after submitting works to the queue, but C::signalAsync() is not executed immediately. Once C::signalAsync() is executed and done with its work, it should notify A::b::doSomethingWithSignalFromClassC().

My question is: how to notify B when C::signalAsync() has already finished? From A, we can get a shared_pointer<C>. Therefore, natural approach is to store weak_ptr<B> inside C. However, this does not work as b is a unique pointer. I also think about storing a raw pointer of B inside C, but that seems to go against the unique_ptr design.

Also, storing a raw pointer to A in C seems to be bad:

class A {
  void setA() {
    b->getC()->setA(this); 
  }
}

I have the feeling that something is wrong with my design. If it were you to solve this problem, which directions would you try?

CodePudding user response:

You need to be very precise with what you are asking for. You say

Once C::signalAsync() is executed and done with its work, it should notify A::b::doSomethingWithSignalFromClassC()

'Notify' implies that a thread is waiting somewhere to be woken up to do something - this is complex

But you could also mean, I want singalAsync to call a method on an instance of B. This is simple.

Not knowing the constraints on these calls (do you wont then all or are some in libraries that you cant change etc) makes it harder.

Here is what I would do for the callback case

class C {
    std::shared_ptr<B> callback_;
public:
    C(std::shared_ptr<B> &cb){
         callback_ = cb;
    }
    void signalAsync() {
        // noodle noodle noodle
        callback_->doSomethingWithSignalFromClassC();
  }
}

ie when I construct a C object tell it the obbject to call back on.

Alternatively you could pass that as an arg to signalAsync but maybe thats not possible

Another idea would be to pass a std::function in the constructor so you dont have to hard code whcih method gets called at the end of signalAsync

CodePudding user response:

According to pm100's answer, the usage of std::function actually works. I want to write down what I do in the end, just in case anyone might need it. A little bit more details, in my case, C's header file cannot include B's header file because B already includes C in its header (forward declaration not work). C also could not include A because A is designed to be an Implementation class (Pointer to Implementation idiom); therefore, A header is inside src folder and should not be included anywhere (except for A.cc)

What I did in the end:

class A {
public: 
   std::unique_ptr<B> b; 
   void triggerAsynchronously() {
       // this work is submitted to a queue. 
       b->getC()->signalAsync(); 
   }
   void setFunctionToC() {
       // This happens before triggerAsynchronously()
       auto do_something_when_finished = [&b = b]() {
          b->doSomethingWithSignalFromClassC(); 
       }
       b->getC()->setFunctionToC(do_something_when_finished); 
   }
}; 

class C {
    std::function<void()> call_on_finished; 
public: 
  void setFunctionToC(std::function<void()> f) {
       call_on_finished = f; 
   }
  void signalAsync() {
    // This function is submitted to a queue, and wait until its turn to be executed. 
    // I want to trigger A::b::doSomethingWithSignalFromC() once this function is done (aka end of this block)
     call_on_finished(); 
  }
}
  • Related