notify from a private vector of objects


I have a class , "request", which has a vector of "exector" objects. Each executor object executes something asynchronously.
How can I find out if at least one of the executors has completed?

This is a skeleton of the two classes ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <memory>
#include <chrono>
#include <iostream>
#include <vector>
#include <condition_variable>
#include <thread>
#include <future>
#include <cstdlib>

class executor {
  private:
    bool is_complete {false};
    int exec_id ;
  public:
    executor(int eid) : exec_id{eid} { }
    void execute_something() {
      std::srand(std::time(0));
      int seconds_delay = (std::rand() % (10 + 1));
      std::this_thread::sleep_for(std::chrono::seconds(seconds_delay));
      is_complete = true;
    }
    bool has_completed() const { return is_complete; }
};

class request {
  private:
    std::vector<executor> v_executor;
    std::mutex mx;
    std::condition_variable cond;
  public:

    request::request(int exec_cnt)  { 
      for (int i{0}; i<exec_cnt; i++) 
        v_executor.emplace_back(i);    
    }

    void exec_all () {
      std::vector<std::future<void>> futures;
    
      for ( auto & e : v_executor )
        futures.push_back(std::async([&e] () { e.execute_something(); } ));

      for (auto & fut : futures)
        fut.get();
    }

    bool is_at_least_one_complete()  {
      std::unique_lock<std::mutex> lk(mx);
      cond.wait(lk, [](){ return   ;} );
    }
  
};

int main() {
  
  request rq{3};

  return 0;
}

As you can see I've create a condition variable, but not sure how to use it in this context or even if I in fact need one?
Any suggestions would be appreciated??
Last edited on
With a std::future, you can call the member function wait_for with a timeout of zero seconds, and if the return from the function call is future_status::ready , it's finished.

I cannot have a timeout though, since the length of time is arbitrary and there is no time limit as such.
I don't follow. You asked:

How can I find out if at least one of the executors has completed?


You can find out if at least one of the executors has completed by calling each future's wait_for function with a timeout of zero. If any one future says future_status::ready, then it has completed.

I never realised that a timeout of zero enables one to check if a std::future has finished or not.

I'm still not 100% sure though how I can obtain the first executor without doing some kind of loop until one of the futures is ready?

Is this code something like you had in mind?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <chrono>
#include <iostream>
#include <vector>
#include <thread>
#include <future>
#include <cstdlib>

class executor {
  private:
    bool is_complete {false};
    int exec_id ;
  public:
    executor(int eid) : exec_id{eid} { }
    void execute_something() {
      std::srand(std::time(0));
      int seconds_delay = (std::rand() % (10 + 1));
      std::this_thread::sleep_for(std::chrono::seconds(seconds_delay));
      is_complete = true;
    }
    bool has_completed() const { return is_complete; }
};

class request {
  private:
    std::vector<executor> v_executor;
  public:

    request::request(int exec_cnt)  { 
      for (int i{0}; i<exec_cnt; i++) 
        v_executor.emplace_back(i);    
    }

    void exec_all () {
      std::vector<std::future<void>> futures;
    
      for ( auto & e : v_executor ) {
        futures.push_back(std::async([&e] () { e.execute_something(); } ))
      }

      /* But what if none of them are ready when looping */
      for (auto & fut : futures)
        fut.wait_for(std::chrono::seconds(0)) == std::future_status::ready;

      for (auto & fut : futures)
        fut.get();
    }

 
};

int main() {

  request rq{3};

  return 0;
}



I've also created the following solution, which seems to work, and uses a kind of callback:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <memory>
#include <chrono>
#include <iostream>
#include <vector>
#include <thread>
#include <future>
#include <cstdlib>

class executor;

class request;

class executor {
  int exec_id ;
  request& req;
  bool is_complete {false};

  public:
    executor(int eid, request& request_);
    void execute_something() ;
    bool has_completed() const { return is_complete; }
};


class request {
  private:
    bool first_done{false};
    std::vector<executor> v_executor;
    std::mutex mx;
  public:

    request(int exec_cnt)  { 
      for (int i{0}; i<exec_cnt; i++) 
        v_executor.emplace_back(i, *this);    
    }
    
    void communicate_to_client (int exec_id) {
      std::lock_guard<std::mutex> lk(mx);
      if(!first_done)  {
        first_done = true;
        std::cout << "Ready - " << exec_id << std::endl;
      }  
    }

    void exec_all () {
      std::vector<std::future<void>> futures;
    
      for ( auto & e : v_executor )
        futures.push_back(std::async([&e] () { e.execute_something(); } ));

      for (auto & fut : futures)
        fut.get();
    }
};

    executor::executor(int eid, request& request_) : exec_id{eid}, req{request_} { }
    void executor::execute_something() {
      std::srand(std::time(0));
      int seconds_delay = (std::rand() % (3 + 1));
      std::this_thread::sleep_for(std::chrono::seconds(seconds_delay));
      is_complete = true;
      req.communicate_to_client(exec_id);
    }

int main() {
  
  request rq{3};

  rq.exec_all();

  return 0;
}


I don't have a solution which uses std::future::ready and std::future_status, but the second code block works.
How can I implement something like the second code block using std::future::ready and std::future_status without doing a while loop?

Last edited on
Topic archived. No new replies allowed.