0

I have long-running function in my C++ 11 application that is essentially sub_main. I need to inform this function about situation risen in different thread. Requirements:

  • The situation can be risen at any time
  • The application should handle the situation at most at single place (it can leave it unhandled as well)
  • It does not need to be ultra fast nor not-delayed in execution

As of now I considered two option:

  1. Pass object factory with factoree that contain internal queue of situations. The object is then polled at regular interval inside sub_main for new situations. Polled object is removed from queue. When application decide it will not handle the situation in particular place, it can move it back to global situations queue and next constructed factoree will get the situation (so it can be handled elsewhere in sub_main)
  2. Analogous situation, but instead of polling, use lambda function that updates local variable inside sub_main as needed. This version does not use continous polling, but is less readable

Both seems a bit complicated and I am wondering if I missed a more simple solution. The problem is the sub_main is being implemented outside of my library and I am providing end-user with my situations

PiotrK
  • 4,210
  • 6
  • 45
  • 65
  • You could use a variable and a mutex? – Jake Freeman Mar 13 '18 at 18:04
  • @JakeFreeman Which variable? Global variable? Passed variable? Which type? – PiotrK Mar 13 '18 at 18:06
  • To clarify a global variable @piotrK – Jake Freeman Mar 13 '18 at 18:07
  • @JakeFreeman Global variables are really awful for that sort of things... imagine my client request I have more than one `sub_main` running at the same time (which is actually likely in this kind of project) – PiotrK Mar 13 '18 at 18:08
  • 1
    You might want to look at the [Chain-of-responsibility pattern](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern). – Phil Brubaker Mar 13 '18 at 18:09
  • @PhilBrubaker Thats a nice lead, I missed that one! – PiotrK Mar 13 '18 at 18:13
  • As asked, this has very little to do with C++, but is a basic inter-thread communication design. The standard C++ library does not provide thread-safe containers (like a queue) for passing work around. You can roll your own, or pick one that is already written (and probably more mature than what you will write). [RecursionSW](http://recursionsoftwareinc.com/) and [ZeroMQ](http://zeromq.org/) are just two that I'm familiar with. On a more primitive level are things like Posix semaphores and condition variables. – jwm Mar 13 '18 at 18:21
  • 1
    You may be describing the interaction of `std::promise` and `std::future`. – Drew Dormann Mar 13 '18 at 18:27
  • @jwm I've already have simple concurrent queue based on different (not-mine) stack overflow question. But the question itself is inside C++ domain, so any solution that utilize STL/Boost/whatever 11 has offered is preffered – PiotrK Mar 13 '18 at 18:37
  • @DrewDormann Can you elaborate a bit more...? – PiotrK Mar 13 '18 at 18:37
  • @PiotrK can you comment as to whether [this post](https://stackoverflow.com/questions/12620186) solves your problem? A promise, in any thread, would produce a `situation`. While a future, in any thread, could detect and receive that `situation`. – Drew Dormann Mar 13 '18 at 18:42
  • 1
    I would refrain from global variables. They get messy if you have multiple threads that affect the same variable. What i do is pass a callback function to each thread and use that as a notification gateway from the threads back to the caller. – Kevbo Mar 13 '18 at 19:54
  • @DrewDormann Is it only single-time working? There can be (and will be) more than one `situation` to be handled – PiotrK Mar 14 '18 at 03:47

1 Answers1

1

I ended up using suggestion of @PhilBrubaker and Chain of Responsibility:

void sub_main(std::shared_ptr<situation_register> situation_register)
{

    std::unique_ptr<handler_instance> instance =
        situation_register->register(priority::main, [=](situation* s)
        {
            switch(s->get_type())
            {
                 case situation::screensaver:
                      s->get<situation_screensaver>()->prevent();
                      s->mark_as_handled();
                 break;
            }
        });


}

In this case, if function does NOT explicit says the situation was handled it will be passed to next registered handler (they are sorted based on priority). The handler_instance is lightweight object that will deregister handler in its destructor.

PiotrK
  • 4,210
  • 6
  • 45
  • 65