kandi background
kandi background
Explore Kits
kandi background
Explore Kits
Explore all Arduino open source software, libraries, packages, source code, cloud functions and APIs.

Popular New Releases in Arduino

Tasmota v11.1.0 Ostara

Release 3.0.2

Release 1.8.19 Security hotfix release

Marlin 2.0.9.1

Tasmota

Tasmota v11.1.0 Ostara

Arduino

Release 3.0.2

Arduino

Release 1.8.19 Security hotfix release

johnny-five

Marlin

Marlin 2.0.9.1

Popular Libraries in Arduino

Trending New libraries in Arduino

Top Authors in Arduino

1

223 Libraries

5573

2

217 Libraries

18455

3

84 Libraries

1393

4

69 Libraries

2783

5

59 Libraries

20426

6

51 Libraries

788

7

41 Libraries

1309

8

40 Libraries

126

9

39 Libraries

9082

10

38 Libraries

1265

1

223 Libraries

5573

2

217 Libraries

18455

3

84 Libraries

1393

4

69 Libraries

2783

5

59 Libraries

20426

6

51 Libraries

788

7

41 Libraries

1309

8

40 Libraries

126

9

39 Libraries

9082

10

38 Libraries

1265

Trending Kits in Arduino

No Trending Kits are available at this moment for Arduino

Trending Discussions on Arduino

    C++11 multithreaded cancellable slice-based work
    Sending int from python to arduino, but there is an upper limit. How do I solve it?
    exec: "python": executable file not found in $PATH on Arduino IDE
    How to filter a class in Angular?
    Reading Arduino Serial data on C# application but port access denied?
    Stop text from overlapping html and css
    What is the Rust equivalent of Serial.println from the Arduino C++ API?
    How to fix Failed to connect to ESP32: Timed out waiting for packet header error?
    How to make an object take and store an Array of arbitrary, but compile-time known size?
    how to clear oled display in micropython

QUESTION

C++11 multithreaded cancellable slice-based work

Asked 2022-Apr-17 at 18:39

I am trying to create a base class to manage a slice-based workload.
My approach was to create a base abstract class that handles the initialization/termination of the work and inherit from that class in specific classes that only specify the actual work and timings.
I also added the functionality in the base class to reinitialize the workload if a set number of errors occur.

This works as expected in a simple example (given below) and with most workloads that I have but when I try to use this with a specific workload (reading a serial port that's written to by an arduino) it completely messes up the stream read from arduino.
I suspect there is some problem with my approach but I couldn't figure it out...

Here is my code:

sliceWork.h

1#pragma once
2#include <future>
3using namespace ::std;
4
5class sliceWork
6{
7    int sliceIntervalMilliSeconds;
8    int failureCounter;
9    int maxFailsBeforeRestart;
10    char* label = NULL;
11    
12    promise<void> workPromise;
13    thread* workerThread = NULL;
14
15    virtual void init() = 0;
16    virtual bool oneSliceWork() = 0;
17    void work(future<void> future);
18
19public:
20    sliceWork(int sliceInterval, int maxFails, const char* label);
21    ~sliceWork();
22    void initWork();
23    void signalTerminate();
24};
25

sliceWork.cpp

1#pragma once
2#include <future>
3using namespace ::std;
4
5class sliceWork
6{
7    int sliceIntervalMilliSeconds;
8    int failureCounter;
9    int maxFailsBeforeRestart;
10    char* label = NULL;
11    
12    promise<void> workPromise;
13    thread* workerThread = NULL;
14
15    virtual void init() = 0;
16    virtual bool oneSliceWork() = 0;
17    void work(future<void> future);
18
19public:
20    sliceWork(int sliceInterval, int maxFails, const char* label);
21    ~sliceWork();
22    void initWork();
23    void signalTerminate();
24};
25#include <string.h>
26#include "sliceWork.h"
27
28sliceWork::sliceWork(int interval, int maxFails, const char* workLabel)
29{
30    sliceIntervalMilliSeconds = interval;
31    maxFailsBeforeRestart = maxFails;
32    label = new char[strlen(workLabel) + 1];
33    strcpy(label, workLabel);
34}
35
36sliceWork::~sliceWork()
37{
38    if (workerThread != NULL && workerThread->joinable())
39        workerThread->join();
40    printf("destructor %s\n", label);
41    delete label;
42    delete workerThread;
43}
44
45void sliceWork::initWork()
46{
47    failureCounter = 0;
48    init();
49    printf("Init work %s finished!\n", label);
50    future<void> futureWorker = workPromise.get_future();
51    workerThread = new thread(&sliceWork::work, this, move(futureWorker));
52}
53
54void sliceWork::work(future<void> future)
55{
56    using namespace ::std::chrono;
57    steady_clock::time_point t0 = steady_clock::now();
58
59    while (future.wait_for(chrono::milliseconds(1)) == future_status::timeout)
60    {
61        if (duration_cast<chrono::milliseconds>(steady_clock::now() - t0).count() 
62            > sliceIntervalMilliSeconds)
63        {
64            if (!oneSliceWork())
65            {
66                if (++failureCounter > maxFailsBeforeRestart 
67                    && maxFailsBeforeRestart > 0)
68                {
69                    init();
70                    failureCounter = 0;
71                }
72            }
73            t0 = steady_clock::now();
74        }
75    }
76    printf("work terminated for %s!\n", label);
77}
78
79void sliceWork::signalTerminate()
80{
81    printf("request terminate for work %s...\n", label);
82    workPromise.set_value();
83}
84

And here is an example of using it that works as expected:

main.cpp

1#pragma once
2#include <future>
3using namespace ::std;
4
5class sliceWork
6{
7    int sliceIntervalMilliSeconds;
8    int failureCounter;
9    int maxFailsBeforeRestart;
10    char* label = NULL;
11    
12    promise<void> workPromise;
13    thread* workerThread = NULL;
14
15    virtual void init() = 0;
16    virtual bool oneSliceWork() = 0;
17    void work(future<void> future);
18
19public:
20    sliceWork(int sliceInterval, int maxFails, const char* label);
21    ~sliceWork();
22    void initWork();
23    void signalTerminate();
24};
25#include <string.h>
26#include "sliceWork.h"
27
28sliceWork::sliceWork(int interval, int maxFails, const char* workLabel)
29{
30    sliceIntervalMilliSeconds = interval;
31    maxFailsBeforeRestart = maxFails;
32    label = new char[strlen(workLabel) + 1];
33    strcpy(label, workLabel);
34}
35
36sliceWork::~sliceWork()
37{
38    if (workerThread != NULL && workerThread->joinable())
39        workerThread->join();
40    printf("destructor %s\n", label);
41    delete label;
42    delete workerThread;
43}
44
45void sliceWork::initWork()
46{
47    failureCounter = 0;
48    init();
49    printf("Init work %s finished!\n", label);
50    future<void> futureWorker = workPromise.get_future();
51    workerThread = new thread(&sliceWork::work, this, move(futureWorker));
52}
53
54void sliceWork::work(future<void> future)
55{
56    using namespace ::std::chrono;
57    steady_clock::time_point t0 = steady_clock::now();
58
59    while (future.wait_for(chrono::milliseconds(1)) == future_status::timeout)
60    {
61        if (duration_cast<chrono::milliseconds>(steady_clock::now() - t0).count() 
62            > sliceIntervalMilliSeconds)
63        {
64            if (!oneSliceWork())
65            {
66                if (++failureCounter > maxFailsBeforeRestart 
67                    && maxFailsBeforeRestart > 0)
68                {
69                    init();
70                    failureCounter = 0;
71                }
72            }
73            t0 = steady_clock::now();
74        }
75    }
76    printf("work terminated for %s!\n", label);
77}
78
79void sliceWork::signalTerminate()
80{
81    printf("request terminate for work %s...\n", label);
82    workPromise.set_value();
83}
84#include <string.h>
85#include "sliceWork.h"
86
87class A : public sliceWork
88{
89    void init() {
90        printf("Init A...\n");
91    }
92
93    bool oneSliceWork() {
94        printf("Working A...\n");
95        return true;
96    }
97public:
98    A(int slice, int max, const char* label) 
99        : sliceWork(slice, max, label) 
100    {
101    }
102};
103
104class B : public sliceWork
105{
106    void init() {
107        printf("Init B...\n");
108    }
109
110    bool oneSliceWork() {
111        printf("Working B...\n");
112        return true;
113    }
114public:
115    B(int slice, int max, const char* label) 
116        : sliceWork(slice, max, label) 
117    {
118    }
119};
120
121class C : public sliceWork
122{
123    void init() {
124        printf("Init C...\n");
125    }
126
127    bool oneSliceWork() {
128        printf("Working C...\n");
129        return false;
130    }
131public:
132    C(int slice, int max, const char* label) 
133        : sliceWork(slice, max, label) 
134    {
135    }
136};
137
138int main()
139{
140     {
141         A a(1000, 1000, "A");
142         a.initWork();
143         B b(2000, 1000, "B" );
144         b.initWork();
145         C c(700, 2, "C" );
146         c.initWork();
147         printf("Initializations finished!\n");
148         ::std::this_thread::sleep_for(::std::chrono::seconds(7));
149         a.signalTerminate();
150         ::std::this_thread::sleep_for(::std::chrono::seconds(5));
151         b.signalTerminate();
152         ::std::this_thread::sleep_for(::std::chrono::seconds(4));
153         c.signalTerminate();
154     }
155     getchar();
156     return 0;
157}
158

So, I want to ask if this approach is prone to error because the way I implemented the functionality.

Application is written in C++11 and targets an Raspberry PI 3b+ running the Raspberry's flavor of Debian 11 (Raspbian), if that is relevant.

ANSWER

Answered 2022-Apr-17 at 13:21

General Observations:

While the code compiles and runs on Windows 10 in Visual Studio 2019, there are multiple problems here, not necessarily with threading.

  1. Without knowing what the expected output for the test case it is very difficult to determine if the code is running correctly.
  2. Object oriented programming rules are being broken, there doesn't seem to be any encapsulation. All of the variables and methods are public.
  3. This looks more like C code that C++ code except for the classes themselves.
  4. I am providing a review to address the C++ issues, we don't debug problems on code review, and we would need to see the the Arduno code to help debug it (on Stack Overflow, not Code Review).

Avoid using namespace std;

If you are coding professionally you probably should get out of the habit of using the using namespace std; statement. The code will more clearly define where cout and other identifiers are coming from (std::cin, std::cout). As you start using namespaces in your code it is better to identify where each function comes from because there may be function name collisions from different namespaces. The identifiercout you may override within your own classes, and you may override the operator << in your own classes as well. This stack overflow question discusses this in more detail.

Never put using namespace std into a header file.

Declare Public Variables and Methods at the Top of the Class

A general best practice is to declare public variables and methods at the top of the class followed by the protected variables and methods and then finally the private variables and methods. This makes it easier for the users of the class to find the interfaces they need.

While the C++ language does provide a default for variables and methods declared at the top of the class, the code is easier to read and maintain if the public, protected and private keywords are explicit.

Prefer C++ I/O Over C Programming I/O

While C++ is backward compatible with C, using printf() in C++ is exceptionally rare, std::cin and std::cout are preferred in C++.

A Base Class Does Not Need to be an Abstract Class

You can provide default functions for the virtual methods init() and oneSliceWork(). This would reduce the repetition of code in the test case, and still allow for the virtual methods to be overwritten when necessary.

copy icondownload icon

1#pragma once
2#include &lt;future&gt;
3using namespace ::std;
4
5class sliceWork
6{
7    int sliceIntervalMilliSeconds;
8    int failureCounter;
9    int maxFailsBeforeRestart;
10    char* label = NULL;
11    
12    promise&lt;void&gt; workPromise;
13    thread* workerThread = NULL;
14
15    virtual void init() = 0;
16    virtual bool oneSliceWork() = 0;
17    void work(future&lt;void&gt; future);
18
19public:
20    sliceWork(int sliceInterval, int maxFails, const char* label);
21    ~sliceWork();
22    void initWork();
23    void signalTerminate();
24};
25#include &lt;string.h&gt;
26#include &quot;sliceWork.h&quot;
27
28sliceWork::sliceWork(int interval, int maxFails, const char* workLabel)
29{
30    sliceIntervalMilliSeconds = interval;
31    maxFailsBeforeRestart = maxFails;
32    label = new char[strlen(workLabel) + 1];
33    strcpy(label, workLabel);
34}
35
36sliceWork::~sliceWork()
37{
38    if (workerThread != NULL &amp;&amp; workerThread-&gt;joinable())
39        workerThread-&gt;join();
40    printf(&quot;destructor %s\n&quot;, label);
41    delete label;
42    delete workerThread;
43}
44
45void sliceWork::initWork()
46{
47    failureCounter = 0;
48    init();
49    printf(&quot;Init work %s finished!\n&quot;, label);
50    future&lt;void&gt; futureWorker = workPromise.get_future();
51    workerThread = new thread(&amp;sliceWork::work, this, move(futureWorker));
52}
53
54void sliceWork::work(future&lt;void&gt; future)
55{
56    using namespace ::std::chrono;
57    steady_clock::time_point t0 = steady_clock::now();
58
59    while (future.wait_for(chrono::milliseconds(1)) == future_status::timeout)
60    {
61        if (duration_cast&lt;chrono::milliseconds&gt;(steady_clock::now() - t0).count() 
62            &gt; sliceIntervalMilliSeconds)
63        {
64            if (!oneSliceWork())
65            {
66                if (++failureCounter &gt; maxFailsBeforeRestart 
67                    &amp;&amp; maxFailsBeforeRestart &gt; 0)
68                {
69                    init();
70                    failureCounter = 0;
71                }
72            }
73            t0 = steady_clock::now();
74        }
75    }
76    printf(&quot;work terminated for %s!\n&quot;, label);
77}
78
79void sliceWork::signalTerminate()
80{
81    printf(&quot;request terminate for work %s...\n&quot;, label);
82    workPromise.set_value();
83}
84#include &lt;string.h&gt;
85#include &quot;sliceWork.h&quot;
86
87class A : public sliceWork
88{
89    void init() {
90        printf(&quot;Init A...\n&quot;);
91    }
92
93    bool oneSliceWork() {
94        printf(&quot;Working A...\n&quot;);
95        return true;
96    }
97public:
98    A(int slice, int max, const char* label) 
99        : sliceWork(slice, max, label) 
100    {
101    }
102};
103
104class B : public sliceWork
105{
106    void init() {
107        printf(&quot;Init B...\n&quot;);
108    }
109
110    bool oneSliceWork() {
111        printf(&quot;Working B...\n&quot;);
112        return true;
113    }
114public:
115    B(int slice, int max, const char* label) 
116        : sliceWork(slice, max, label) 
117    {
118    }
119};
120
121class C : public sliceWork
122{
123    void init() {
124        printf(&quot;Init C...\n&quot;);
125    }
126
127    bool oneSliceWork() {
128        printf(&quot;Working C...\n&quot;);
129        return false;
130    }
131public:
132    C(int slice, int max, const char* label) 
133        : sliceWork(slice, max, label) 
134    {
135    }
136};
137
138int main()
139{
140     {
141         A a(1000, 1000, &quot;A&quot;);
142         a.initWork();
143         B b(2000, 1000, &quot;B&quot; );
144         b.initWork();
145         C c(700, 2, &quot;C&quot; );
146         c.initWork();
147         printf(&quot;Initializations finished!\n&quot;);
148         ::std::this_thread::sleep_for(::std::chrono::seconds(7));
149         a.signalTerminate();
150         ::std::this_thread::sleep_for(::std::chrono::seconds(5));
151         b.signalTerminate();
152         ::std::this_thread::sleep_for(::std::chrono::seconds(4));
153         c.signalTerminate();
154     }
155     getchar();
156     return 0;
157}
158#pragma once
159#include &lt;iostream&gt;
160#include &lt;future&gt;
161
162class sliceWork
163{
164public:
165    sliceWork(int sliceInterval, int maxFails, const char* label);
166    ~sliceWork();
167    void initWork();
168    void signalTerminate();
169
170protected:
171    virtual void init()
172    {
173        std::cout &lt;&lt; &quot;Init &quot; &lt;&lt; label &lt;&lt; &quot;..\n&quot;;
174    }
175    virtual bool oneSliceWork()
176    {
177        std::cout &lt;&lt; &quot;Working &quot; &lt;&lt; label &lt;&lt; &quot;..\n&quot;;
178        return true;
179    }
180    void work(std::future&lt;void&gt; future);
181
182private:
183    int sliceIntervalMilliSeconds;
184    int failureCounter;
185    int maxFailsBeforeRestart;
186    char* label = NULL;
187    std::promise&lt;void&gt; workPromise;
188    std::thread* workerThread = NULL;
189};
190

main.cpp

copy icondownload icon

1#pragma once
2#include &lt;future&gt;
3using namespace ::std;
4
5class sliceWork
6{
7    int sliceIntervalMilliSeconds;
8    int failureCounter;
9    int maxFailsBeforeRestart;
10    char* label = NULL;
11    
12    promise&lt;void&gt; workPromise;
13    thread* workerThread = NULL;
14
15    virtual void init() = 0;
16    virtual bool oneSliceWork() = 0;
17    void work(future&lt;void&gt; future);
18
19public:
20    sliceWork(int sliceInterval, int maxFails, const char* label);
21    ~sliceWork();
22    void initWork();
23    void signalTerminate();
24};
25#include &lt;string.h&gt;
26#include &quot;sliceWork.h&quot;
27
28sliceWork::sliceWork(int interval, int maxFails, const char* workLabel)
29{
30    sliceIntervalMilliSeconds = interval;
31    maxFailsBeforeRestart = maxFails;
32    label = new char[strlen(workLabel) + 1];
33    strcpy(label, workLabel);
34}
35
36sliceWork::~sliceWork()
37{
38    if (workerThread != NULL &amp;&amp; workerThread-&gt;joinable())
39        workerThread-&gt;join();
40    printf(&quot;destructor %s\n&quot;, label);
41    delete label;
42    delete workerThread;
43}
44
45void sliceWork::initWork()
46{
47    failureCounter = 0;
48    init();
49    printf(&quot;Init work %s finished!\n&quot;, label);
50    future&lt;void&gt; futureWorker = workPromise.get_future();
51    workerThread = new thread(&amp;sliceWork::work, this, move(futureWorker));
52}
53
54void sliceWork::work(future&lt;void&gt; future)
55{
56    using namespace ::std::chrono;
57    steady_clock::time_point t0 = steady_clock::now();
58
59    while (future.wait_for(chrono::milliseconds(1)) == future_status::timeout)
60    {
61        if (duration_cast&lt;chrono::milliseconds&gt;(steady_clock::now() - t0).count() 
62            &gt; sliceIntervalMilliSeconds)
63        {
64            if (!oneSliceWork())
65            {
66                if (++failureCounter &gt; maxFailsBeforeRestart 
67                    &amp;&amp; maxFailsBeforeRestart &gt; 0)
68                {
69                    init();
70                    failureCounter = 0;
71                }
72            }
73            t0 = steady_clock::now();
74        }
75    }
76    printf(&quot;work terminated for %s!\n&quot;, label);
77}
78
79void sliceWork::signalTerminate()
80{
81    printf(&quot;request terminate for work %s...\n&quot;, label);
82    workPromise.set_value();
83}
84#include &lt;string.h&gt;
85#include &quot;sliceWork.h&quot;
86
87class A : public sliceWork
88{
89    void init() {
90        printf(&quot;Init A...\n&quot;);
91    }
92
93    bool oneSliceWork() {
94        printf(&quot;Working A...\n&quot;);
95        return true;
96    }
97public:
98    A(int slice, int max, const char* label) 
99        : sliceWork(slice, max, label) 
100    {
101    }
102};
103
104class B : public sliceWork
105{
106    void init() {
107        printf(&quot;Init B...\n&quot;);
108    }
109
110    bool oneSliceWork() {
111        printf(&quot;Working B...\n&quot;);
112        return true;
113    }
114public:
115    B(int slice, int max, const char* label) 
116        : sliceWork(slice, max, label) 
117    {
118    }
119};
120
121class C : public sliceWork
122{
123    void init() {
124        printf(&quot;Init C...\n&quot;);
125    }
126
127    bool oneSliceWork() {
128        printf(&quot;Working C...\n&quot;);
129        return false;
130    }
131public:
132    C(int slice, int max, const char* label) 
133        : sliceWork(slice, max, label) 
134    {
135    }
136};
137
138int main()
139{
140     {
141         A a(1000, 1000, &quot;A&quot;);
142         a.initWork();
143         B b(2000, 1000, &quot;B&quot; );
144         b.initWork();
145         C c(700, 2, &quot;C&quot; );
146         c.initWork();
147         printf(&quot;Initializations finished!\n&quot;);
148         ::std::this_thread::sleep_for(::std::chrono::seconds(7));
149         a.signalTerminate();
150         ::std::this_thread::sleep_for(::std::chrono::seconds(5));
151         b.signalTerminate();
152         ::std::this_thread::sleep_for(::std::chrono::seconds(4));
153         c.signalTerminate();
154     }
155     getchar();
156     return 0;
157}
158#pragma once
159#include &lt;iostream&gt;
160#include &lt;future&gt;
161
162class sliceWork
163{
164public:
165    sliceWork(int sliceInterval, int maxFails, const char* label);
166    ~sliceWork();
167    void initWork();
168    void signalTerminate();
169
170protected:
171    virtual void init()
172    {
173        std::cout &lt;&lt; &quot;Init &quot; &lt;&lt; label &lt;&lt; &quot;..\n&quot;;
174    }
175    virtual bool oneSliceWork()
176    {
177        std::cout &lt;&lt; &quot;Working &quot; &lt;&lt; label &lt;&lt; &quot;..\n&quot;;
178        return true;
179    }
180    void work(std::future&lt;void&gt; future);
181
182private:
183    int sliceIntervalMilliSeconds;
184    int failureCounter;
185    int maxFailsBeforeRestart;
186    char* label = NULL;
187    std::promise&lt;void&gt; workPromise;
188    std::thread* workerThread = NULL;
189};
190#include &lt;string.h&gt;
191#include &quot;sliceWork.h&quot;
192
193class A : public sliceWork
194{
195public:
196    A(int slice, int max, const char* label)
197        : sliceWork(slice, max, label)
198    {
199    }
200};
201
202class B : public sliceWork
203{
204public:
205    B(int slice, int max, const char* label)
206        : sliceWork(slice, max, label)
207    {
208    }
209};
210
211class C : public sliceWork
212{
213public:
214    C(int slice, int max, const char* label)
215        : sliceWork(slice, max, label)
216    {
217    }
218};
219
220int main()
221{
222    {
223        A a(1000, 1000, &quot;A&quot;);
224        a.initWork();
225        B b(2000, 1000, &quot;B&quot;);
226        b.initWork();
227        C c(700, 2, &quot;C&quot;);
228        c.initWork();
229        std::cout &lt;&lt; &quot;Initializations finished!\n&quot;;
230        ::std::this_thread::sleep_for(::std::chrono::seconds(7));
231        a.signalTerminate();
232        ::std::this_thread::sleep_for(::std::chrono::seconds(5));
233        b.signalTerminate();
234        ::std::this_thread::sleep_for(::std::chrono::seconds(4));
235        c.signalTerminate();
236    }
237    getchar();
238    return 0;
239}
240

sliceWork.cpp

copy icondownload icon

1#pragma once
2#include &lt;future&gt;
3using namespace ::std;
4
5class sliceWork
6{
7    int sliceIntervalMilliSeconds;
8    int failureCounter;
9    int maxFailsBeforeRestart;
10    char* label = NULL;
11    
12    promise&lt;void&gt; workPromise;
13    thread* workerThread = NULL;
14
15    virtual void init() = 0;
16    virtual bool oneSliceWork() = 0;
17    void work(future&lt;void&gt; future);
18
19public:
20    sliceWork(int sliceInterval, int maxFails, const char* label);
21    ~sliceWork();
22    void initWork();
23    void signalTerminate();
24};
25#include &lt;string.h&gt;
26#include &quot;sliceWork.h&quot;
27
28sliceWork::sliceWork(int interval, int maxFails, const char* workLabel)
29{
30    sliceIntervalMilliSeconds = interval;
31    maxFailsBeforeRestart = maxFails;
32    label = new char[strlen(workLabel) + 1];
33    strcpy(label, workLabel);
34}
35
36sliceWork::~sliceWork()
37{
38    if (workerThread != NULL &amp;&amp; workerThread-&gt;joinable())
39        workerThread-&gt;join();
40    printf(&quot;destructor %s\n&quot;, label);
41    delete label;
42    delete workerThread;
43}
44
45void sliceWork::initWork()
46{
47    failureCounter = 0;
48    init();
49    printf(&quot;Init work %s finished!\n&quot;, label);
50    future&lt;void&gt; futureWorker = workPromise.get_future();
51    workerThread = new thread(&amp;sliceWork::work, this, move(futureWorker));
52}
53
54void sliceWork::work(future&lt;void&gt; future)
55{
56    using namespace ::std::chrono;
57    steady_clock::time_point t0 = steady_clock::now();
58
59    while (future.wait_for(chrono::milliseconds(1)) == future_status::timeout)
60    {
61        if (duration_cast&lt;chrono::milliseconds&gt;(steady_clock::now() - t0).count() 
62            &gt; sliceIntervalMilliSeconds)
63        {
64            if (!oneSliceWork())
65            {
66                if (++failureCounter &gt; maxFailsBeforeRestart 
67                    &amp;&amp; maxFailsBeforeRestart &gt; 0)
68                {
69                    init();
70                    failureCounter = 0;
71                }
72            }
73            t0 = steady_clock::now();
74        }
75    }
76    printf(&quot;work terminated for %s!\n&quot;, label);
77}
78
79void sliceWork::signalTerminate()
80{
81    printf(&quot;request terminate for work %s...\n&quot;, label);
82    workPromise.set_value();
83}
84#include &lt;string.h&gt;
85#include &quot;sliceWork.h&quot;
86
87class A : public sliceWork
88{
89    void init() {
90        printf(&quot;Init A...\n&quot;);
91    }
92
93    bool oneSliceWork() {
94        printf(&quot;Working A...\n&quot;);
95        return true;
96    }
97public:
98    A(int slice, int max, const char* label) 
99        : sliceWork(slice, max, label) 
100    {
101    }
102};
103
104class B : public sliceWork
105{
106    void init() {
107        printf(&quot;Init B...\n&quot;);
108    }
109
110    bool oneSliceWork() {
111        printf(&quot;Working B...\n&quot;);
112        return true;
113    }
114public:
115    B(int slice, int max, const char* label) 
116        : sliceWork(slice, max, label) 
117    {
118    }
119};
120
121class C : public sliceWork
122{
123    void init() {
124        printf(&quot;Init C...\n&quot;);
125    }
126
127    bool oneSliceWork() {
128        printf(&quot;Working C...\n&quot;);
129        return false;
130    }
131public:
132    C(int slice, int max, const char* label) 
133        : sliceWork(slice, max, label) 
134    {
135    }
136};
137
138int main()
139{
140     {
141         A a(1000, 1000, &quot;A&quot;);
142         a.initWork();
143         B b(2000, 1000, &quot;B&quot; );
144         b.initWork();
145         C c(700, 2, &quot;C&quot; );
146         c.initWork();
147         printf(&quot;Initializations finished!\n&quot;);
148         ::std::this_thread::sleep_for(::std::chrono::seconds(7));
149         a.signalTerminate();
150         ::std::this_thread::sleep_for(::std::chrono::seconds(5));
151         b.signalTerminate();
152         ::std::this_thread::sleep_for(::std::chrono::seconds(4));
153         c.signalTerminate();
154     }
155     getchar();
156     return 0;
157}
158#pragma once
159#include &lt;iostream&gt;
160#include &lt;future&gt;
161
162class sliceWork
163{
164public:
165    sliceWork(int sliceInterval, int maxFails, const char* label);
166    ~sliceWork();
167    void initWork();
168    void signalTerminate();
169
170protected:
171    virtual void init()
172    {
173        std::cout &lt;&lt; &quot;Init &quot; &lt;&lt; label &lt;&lt; &quot;..\n&quot;;
174    }
175    virtual bool oneSliceWork()
176    {
177        std::cout &lt;&lt; &quot;Working &quot; &lt;&lt; label &lt;&lt; &quot;..\n&quot;;
178        return true;
179    }
180    void work(std::future&lt;void&gt; future);
181
182private:
183    int sliceIntervalMilliSeconds;
184    int failureCounter;
185    int maxFailsBeforeRestart;
186    char* label = NULL;
187    std::promise&lt;void&gt; workPromise;
188    std::thread* workerThread = NULL;
189};
190#include &lt;string.h&gt;
191#include &quot;sliceWork.h&quot;
192
193class A : public sliceWork
194{
195public:
196    A(int slice, int max, const char* label)
197        : sliceWork(slice, max, label)
198    {
199    }
200};
201
202class B : public sliceWork
203{
204public:
205    B(int slice, int max, const char* label)
206        : sliceWork(slice, max, label)
207    {
208    }
209};
210
211class C : public sliceWork
212{
213public:
214    C(int slice, int max, const char* label)
215        : sliceWork(slice, max, label)
216    {
217    }
218};
219
220int main()
221{
222    {
223        A a(1000, 1000, &quot;A&quot;);
224        a.initWork();
225        B b(2000, 1000, &quot;B&quot;);
226        b.initWork();
227        C c(700, 2, &quot;C&quot;);
228        c.initWork();
229        std::cout &lt;&lt; &quot;Initializations finished!\n&quot;;
230        ::std::this_thread::sleep_for(::std::chrono::seconds(7));
231        a.signalTerminate();
232        ::std::this_thread::sleep_for(::std::chrono::seconds(5));
233        b.signalTerminate();
234        ::std::this_thread::sleep_for(::std::chrono::seconds(4));
235        c.signalTerminate();
236    }
237    getchar();
238    return 0;
239}
240#include &lt;string.h&gt;
241#include &quot;sliceWork.h&quot;
242
243sliceWork::sliceWork(int interval, int maxFails, const char* workLabel)
244{
245    sliceIntervalMilliSeconds = interval;
246    maxFailsBeforeRestart = maxFails;
247    label = new char[strlen(workLabel) + 1];
248    strcpy(label, workLabel);
249}
250
251sliceWork::~sliceWork()
252{
253    if (workerThread != NULL &amp;&amp; workerThread-&gt;joinable())
254        workerThread-&gt;join();
255    printf(&quot;destructor %s\n&quot;, label);
256    delete label;
257    delete workerThread;
258}
259
260void sliceWork::initWork()
261{
262    failureCounter = 0;
263    init();
264    printf(&quot;Init work %s finished!\n&quot;, label);
265    std::future&lt;void&gt; futureWorker = workPromise.get_future();
266    workerThread = new std::thread(&amp;sliceWork::work, this, move(futureWorker));
267}
268
269void sliceWork::work(std::future&lt;void&gt; future)
270{
271    using namespace ::std::chrono;
272    steady_clock::time_point t0 = steady_clock::now();
273
274    while (future.wait_for(std::chrono::milliseconds(1)) == std::future_status::timeout)
275    {
276        if (duration_cast&lt;std::chrono::milliseconds&gt;(steady_clock::now() - t0).count()
277            &gt; sliceIntervalMilliSeconds)
278        {
279            if (!oneSliceWork())
280            {
281                if (++failureCounter &gt; maxFailsBeforeRestart
282                    &amp;&amp; maxFailsBeforeRestart &gt; 0)
283                {
284                    init();
285                    failureCounter = 0;
286                }
287            }
288            t0 = steady_clock::now();
289        }
290    }
291    printf(&quot;work terminated for %s!\n&quot;, label);
292}
293
294void sliceWork::signalTerminate()
295{
296    printf(&quot;request terminate for work %s...\n&quot;, label);
297    workPromise.set_value();
298}
299

Source https://stackoverflow.com/questions/71904521

Community Discussions contain sources that include Stack Exchange Network

    C++11 multithreaded cancellable slice-based work
    Sending int from python to arduino, but there is an upper limit. How do I solve it?
    exec: &quot;python&quot;: executable file not found in $PATH on Arduino IDE
    How to filter a class in Angular?
    Reading Arduino Serial data on C# application but port access denied?
    Stop text from overlapping html and css
    What is the Rust equivalent of Serial.println from the Arduino C++ API?
    How to fix Failed to connect to ESP32: Timed out waiting for packet header error?
    How to make an object take and store an Array of arbitrary, but compile-time known size?
    how to clear oled display in micropython

QUESTION

C++11 multithreaded cancellable slice-based work

Asked 2022-Apr-17 at 18:39

I am trying to create a base class to manage a slice-based workload.
My approach was to create a base abstract class that handles the initialization/termination of the work and inherit from that class in specific classes that only specify the actual work and timings.
I also added the functionality in the base class to reinitialize the workload if a set number of errors occur.

This works as expected in a simple example (given below) and with most workloads that I have but when I try to use this with a specific workload (reading a serial port that's written to by an arduino) it completely messes up the stream read from arduino.
I suspect there is some problem with my approach but I couldn't figure it out...

Here is my code:

sliceWork.h

1#pragma once
2#include &lt;future&gt;
3using namespace ::std;
4
5class sliceWork
6{
7    int sliceIntervalMilliSeconds;
8    int failureCounter;
9    int maxFailsBeforeRestart;
10    char* label = NULL;
11    
12    promise&lt;void&gt; workPromise;
13    thread* workerThread = NULL;
14
15    virtual void init() = 0;
16    virtual bool oneSliceWork() = 0;
17    void work(future&lt;void&gt; future);
18
19public:
20    sliceWork(int sliceInterval, int maxFails, const char* label);
21    ~sliceWork();
22    void initWork();
23    void signalTerminate();
24};
25

sliceWork.cpp

1#pragma once
2#include &lt;future&gt;
3using namespace ::std;
4
5class sliceWork
6{
7    int sliceIntervalMilliSeconds;
8    int failureCounter;
9    int maxFailsBeforeRestart;
10    char* label = NULL;
11    
12    promise&lt;void&gt; workPromise;
13    thread* workerThread = NULL;
14
15    virtual void init() = 0;
16    virtual bool oneSliceWork() = 0;
17    void work(future&lt;void&gt; future);
18
19public:
20    sliceWork(int sliceInterval, int maxFails, const char* label);
21    ~sliceWork();
22    void initWork();
23    void signalTerminate();
24};
25#include &lt;string.h&gt;
26#include &quot;sliceWork.h&quot;
27
28sliceWork::sliceWork(int interval, int maxFails, const char* workLabel)
29{
30    sliceIntervalMilliSeconds = interval;
31    maxFailsBeforeRestart = maxFails;
32    label = new char[strlen(workLabel) + 1];
33    strcpy(label, workLabel);
34}
35
36sliceWork::~sliceWork()
37{
38    if (workerThread != NULL &amp;&amp; workerThread-&gt;joinable())
39        workerThread-&gt;join();
40    printf(&quot;destructor %s\n&quot;, label);
41    delete label;
42    delete workerThread;
43}
44
45void sliceWork::initWork()
46{
47    failureCounter = 0;
48    init();
49    printf(&quot;Init work %s finished!\n&quot;, label);
50    future&lt;void&gt; futureWorker = workPromise.get_future();
51    workerThread = new thread(&amp;sliceWork::work, this, move(futureWorker));
52}
53
54void sliceWork::work(future&lt;void&gt; future)
55{
56    using namespace ::std::chrono;
57    steady_clock::time_point t0 = steady_clock::now();
58
59    while (future.wait_for(chrono::milliseconds(1)) == future_status::timeout)
60    {
61        if (duration_cast&lt;chrono::milliseconds&gt;(steady_clock::now() - t0).count() 
62            &gt; sliceIntervalMilliSeconds)
63        {
64            if (!oneSliceWork())
65            {
66                if (++failureCounter &gt; maxFailsBeforeRestart 
67                    &amp;&amp; maxFailsBeforeRestart &gt; 0)
68                {
69                    init();
70                    failureCounter = 0;
71                }
72            }
73            t0 = steady_clock::now();
74        }
75    }
76    printf(&quot;work terminated for %s!\n&quot;, label);
77}
78
79void sliceWork::signalTerminate()
80{
81    printf(&quot;request terminate for work %s...\n&quot;, label);
82    workPromise.set_value();
83}
84

And here is an example of using it that works as expected:

main.cpp

1#pragma once
2#include &lt;future&gt;
3using namespace ::std;
4
5class sliceWork
6{
7    int sliceIntervalMilliSeconds;
8    int failureCounter;
9    int maxFailsBeforeRestart;
10    char* label = NULL;
11    
12    promise&lt;void&gt; workPromise;
13    thread* workerThread = NULL;
14
15    virtual void init() = 0;
16    virtual bool oneSliceWork() = 0;
17    void work(future&lt;void&gt; future);
18
19public:
20    sliceWork(int sliceInterval, int maxFails, const char* label);
21    ~sliceWork();
22    void initWork();
23    void signalTerminate();
24};
25#include &lt;string.h&gt;
26#include &quot;sliceWork.h&quot;
27
28sliceWork::sliceWork(int interval, int maxFails, const char* workLabel)
29{
30    sliceIntervalMilliSeconds = interval;
31    maxFailsBeforeRestart = maxFails;
32    label = new char[strlen(workLabel) + 1];
33    strcpy(label, workLabel);
34}
35
36sliceWork::~sliceWork()
37{
38    if (workerThread != NULL &amp;&amp; workerThread-&gt;joinable())
39        workerThread-&gt;join();
40    printf(&quot;destructor %s\n&quot;, label);
41    delete label;
42    delete workerThread;
43}
44
45void sliceWork::initWork()
46{
47    failureCounter = 0;
48    init();
49    printf(&quot;Init work %s finished!\n&quot;, label);
50    future&lt;void&gt; futureWorker = workPromise.get_future();
51    workerThread = new thread(&amp;sliceWork::work, this, move(futureWorker));
52}
53
54void sliceWork::work(future&lt;void&gt; future)
55{
56    using namespace ::std::chrono;
57    steady_clock::time_point t0 = steady_clock::now();
58
59    while (future.wait_for(chrono::milliseconds(1)) == future_status::timeout)
60    {
61        if (duration_cast&lt;chrono::milliseconds&gt;(steady_clock::now() - t0).count() 
62            &gt; sliceIntervalMilliSeconds)
63        {
64            if (!oneSliceWork())
65            {
66                if (++failureCounter &gt; maxFailsBeforeRestart 
67                    &amp;&amp; maxFailsBeforeRestart &gt; 0)
68                {
69                    init();
70                    failureCounter = 0;
71                }
72            }
73            t0 = steady_clock::now();
74        }
75    }
76    printf(&quot;work terminated for %s!\n&quot;, label);
77}
78
79void sliceWork::signalTerminate()
80{
81    printf(&quot;request terminate for work %s...\n&quot;, label);
82    workPromise.set_value();
83}
84#include &lt;string.h&gt;
85#include &quot;sliceWork.h&quot;
86
87class A : public sliceWork
88{
89    void init() {
90        printf(&quot;Init A...\n&quot;);
91    }
92
93    bool oneSliceWork() {
94        printf(&quot;Working A...\n&quot;);
95        return true;
96    }
97public:
98    A(int slice, int max, const char* label) 
99        : sliceWork(slice, max, label) 
100    {
101    }
102};
103
104class B : public sliceWork
105{
106    void init() {
107        printf(&quot;Init B...\n&quot;);
108    }
109
110    bool oneSliceWork() {
111        printf(&quot;Working B...\n&quot;);
112        return true;
113    }
114public:
115    B(int slice, int max, const char* label) 
116        : sliceWork(slice, max, label) 
117    {
118    }
119};
120
121class C : public sliceWork
122{
123    void init() {
124        printf(&quot;Init C...\n&quot;);
125    }
126
127    bool oneSliceWork() {
128        printf(&quot;Working C...\n&quot;);
129        return false;
130    }
131public:
132    C(int slice, int max, const char* label) 
133        : sliceWork(slice, max, label) 
134    {
135    }
136};
137
138int main()
139{
140     {
141         A a(1000, 1000, &quot;A&quot;);
142         a.initWork();
143         B b(2000, 1000, &quot;B&quot; );
144         b.initWork();
145         C c(700, 2, &quot;C&quot; );
146         c.initWork();
147         printf(&quot;Initializations finished!\n&quot;);
148         ::std::this_thread::sleep_for(::std::chrono::seconds(7));
149         a.signalTerminate();
150         ::std::this_thread::sleep_for(::std::chrono::seconds(5));
151         b.signalTerminate();
152         ::std::this_thread::sleep_for(::std::chrono::seconds(4));
153         c.signalTerminate();
154     }
155     getchar();
156     return 0;
157}
158

So, I want to ask if this approach is prone to error because the way I implemented the functionality.

Application is written in C++11 and targets an Raspberry PI 3b+ running the Raspberry's flavor of Debian 11 (Raspbian), if that is relevant.

ANSWER

Answered 2022-Apr-17 at 13:21

General Observations:

While the code compiles and runs on Windows 10 in Visual Studio 2019, there are multiple problems here, not necessarily with threading.

  1. Without knowing what the expected output for the test case it is very difficult to determine if the code is running correctly.
  2. Object oriented programming rules are being broken, there doesn't seem to be any encapsulation. All of the variables and methods are public.
  3. This looks more like C code that C++ code except for the classes themselves.
  4. I am providing a review to address the C++ issues, we don't debug problems on code review, and we would need to see the the Arduno code to help debug it (on Stack Overflow, not Code Review).

Avoid using namespace std;

If you are coding professionally you probably should get out of the habit of using the using namespace std; statement. The code will more clearly define where cout and other identifiers are coming from (std::cin, std::cout). As you start using namespaces in your code it is better to identify where each function comes from because there may be function name collisions from different namespaces. The identifiercout you may override within your own classes, and you may override the operator << in your own classes as well. This stack overflow question discusses this in more detail.

Never put using namespace std into a header file.

Declare Public Variables and Methods at the Top of the Class

A general best practice is to declare public variables and methods at the top of the class followed by the protected variables and methods and then finally the private variables and methods. This makes it easier for the users of the class to find the interfaces they need.

While the C++ language does provide a default for variables and methods declared at the top of the class, the code is easier to read and maintain if the public, protected and private keywords are explicit.

Prefer C++ I/O Over C Programming I/O

While C++ is backward compatible with C, using printf() in C++ is exceptionally rare, std::cin and std::cout are preferred in C++.

A Base Class Does Not Need to be an Abstract Class

You can provide default functions for the virtual methods init() and oneSliceWork(). This would reduce the repetition of code in the test case, and still allow for the virtual methods to be overwritten when necessary.

copy icondownload icon

1#pragma once
2#include &lt;future&gt;
3using namespace ::std;
4
5class sliceWork
6{
7    int sliceIntervalMilliSeconds;
8    int failureCounter;
9    int maxFailsBeforeRestart;
10    char* label = NULL;
11    
12    promise&lt;void&gt; workPromise;
13    thread* workerThread = NULL;
14
15    virtual void init() = 0;
16    virtual bool oneSliceWork() = 0;
17    void work(future&lt;void&gt; future);
18
19public:
20    sliceWork(int sliceInterval, int maxFails, const char* label);
21    ~sliceWork();
22    void initWork();
23    void signalTerminate();
24};
25#include &lt;string.h&gt;
26#include &quot;sliceWork.h&quot;
27
28sliceWork::sliceWork(int interval, int maxFails, const char* workLabel)
29{
30    sliceIntervalMilliSeconds = interval;
31    maxFailsBeforeRestart = maxFails;
32    label = new char[strlen(workLabel) + 1];
33    strcpy(label, workLabel);
34}
35
36sliceWork::~sliceWork()
37{
38    if (workerThread != NULL &amp;&amp; workerThread-&gt;joinable())
39        workerThread-&gt;join();
40    printf(&quot;destructor %s\n&quot;, label);
41    delete label;
42    delete workerThread;
43}
44
45void sliceWork::initWork()
46{
47    failureCounter = 0;
48    init();
49    printf(&quot;Init work %s finished!\n&quot;, label);
50    future&lt;void&gt; futureWorker = workPromise.get_future();
51    workerThread = new thread(&amp;sliceWork::work, this, move(futureWorker));
52}
53
54void sliceWork::work(future&lt;void&gt; future)
55{
56    using namespace ::std::chrono;
57    steady_clock::time_point t0 = steady_clock::now();
58
59    while (future.wait_for(chrono::milliseconds(1)) == future_status::timeout)
60    {
61        if (duration_cast&lt;chrono::milliseconds&gt;(steady_clock::now() - t0).count() 
62            &gt; sliceIntervalMilliSeconds)
63        {
64            if (!oneSliceWork())
65            {
66                if (++failureCounter &gt; maxFailsBeforeRestart 
67                    &amp;&amp; maxFailsBeforeRestart &gt; 0)
68                {
69                    init();
70                    failureCounter = 0;
71                }
72            }
73            t0 = steady_clock::now();
74        }
75    }
76    printf(&quot;work terminated for %s!\n&quot;, label);
77}
78
79void sliceWork::signalTerminate()
80{
81    printf(&quot;request terminate for work %s...\n&quot;, label);
82    workPromise.set_value();
83}
84#include &lt;string.h&gt;
85#include &quot;sliceWork.h&quot;
86
87class A : public sliceWork
88{
89    void init() {
90        printf(&quot;Init A...\n&quot;);
91    }
92
93    bool oneSliceWork() {
94        printf(&quot;Working A...\n&quot;);
95        return true;
96    }
97public:
98    A(int slice, int max, const char* label) 
99        : sliceWork(slice, max, label) 
100    {
101    }
102};
103
104class B : public sliceWork
105{
106    void init() {
107        printf(&quot;Init B...\n&quot;);
108    }
109
110    bool oneSliceWork() {
111        printf(&quot;Working B...\n&quot;);
112        return true;
113    }
114public:
115    B(int slice, int max, const char* label) 
116        : sliceWork(slice, max, label) 
117    {
118    }
119};
120
121class C : public sliceWork
122{
123    void init() {
124        printf(&quot;Init C...\n&quot;);
125    }
126
127    bool oneSliceWork() {
128        printf(&quot;Working C...\n&quot;);
129        return false;
130    }
131public:
132    C(int slice, int max, const char* label) 
133        : sliceWork(slice, max, label) 
134    {
135    }
136};
137
138int main()
139{
140     {
141         A a(1000, 1000, &quot;A&quot;);
142         a.initWork();
143         B b(2000, 1000, &quot;B&quot; );
144         b.initWork();
145         C c(700, 2, &quot;C&quot; );
146         c.initWork();
147         printf(&quot;Initializations finished!\n&quot;);
148         ::std::this_thread::sleep_for(::std::chrono::seconds(7));
149         a.signalTerminate();
150         ::std::this_thread::sleep_for(::std::chrono::seconds(5));
151         b.signalTerminate();
152         ::std::this_thread::sleep_for(::std::chrono::seconds(4));
153         c.signalTerminate();
154     }
155     getchar();
156     return 0;
157}
158#pragma once
159#include &lt;iostream&gt;
160#include &lt;future&gt;
161
162class sliceWork
163{
164public:
165    sliceWork(int sliceInterval, int maxFails, const char* label);
166    ~sliceWork();
167    void initWork();
168    void signalTerminate();
169
170protected:
171    virtual void init()
172    {
173        std::cout &lt;&lt; &quot;Init &quot; &lt;&lt; label &lt;&lt; &quot;..\n&quot;;
174    }
175    virtual bool oneSliceWork()
176    {
177        std::cout &lt;&lt; &quot;Working &quot; &lt;&lt; label &lt;&lt; &quot;..\n&quot;;
178        return true;
179    }
180    void work(std::future&lt;void&gt; future);
181
182private:
183    int sliceIntervalMilliSeconds;
184    int failureCounter;
185    int maxFailsBeforeRestart;
186    char* label = NULL;
187    std::promise&lt;void&gt; workPromise;
188    std::thread* workerThread = NULL;
189};
190

main.cpp

copy icondownload icon

1#pragma once
2#include &lt;future&gt;
3using namespace ::std;
4
5class sliceWork
6{
7    int sliceIntervalMilliSeconds;
8    int failureCounter;
9    int maxFailsBeforeRestart;
10    char* label = NULL;
11    
12    promise&lt;void&gt; workPromise;
13    thread* workerThread = NULL;
14
15    virtual void init() = 0;
16    virtual bool oneSliceWork() = 0;
17    void work(future&lt;void&gt; future);
18
19public:
20    sliceWork(int sliceInterval, int maxFails, const char* label);
21    ~sliceWork();
22    void initWork();
23    void signalTerminate();
24};
25#include &lt;string.h&gt;
26#include &quot;sliceWork.h&quot;
27
28sliceWork::sliceWork(int interval, int maxFails, const char* workLabel)
29{
30    sliceIntervalMilliSeconds = interval;
31    maxFailsBeforeRestart = maxFails;
32    label = new char[strlen(workLabel) + 1];
33    strcpy(label, workLabel);
34}
35
36sliceWork::~sliceWork()
37{
38    if (workerThread != NULL &amp;&amp; workerThread-&gt;joinable())
39        workerThread-&gt;join();
40    printf(&quot;destructor %s\n&quot;, label);
41    delete label;
42    delete workerThread;
43}
44
45void sliceWork::initWork()
46{
47    failureCounter = 0;
48    init();
49    printf(&quot;Init work %s finished!\n&quot;, label);
50    future&lt;void&gt; futureWorker = workPromise.get_future();
51    workerThread = new thread(&amp;sliceWork::work, this, move(futureWorker));
52}
53
54void sliceWork::work(future&lt;void&gt; future)
55{
56    using namespace ::std::chrono;
57    steady_clock::time_point t0 = steady_clock::now();
58
59    while (future.wait_for(chrono::milliseconds(1)) == future_status::timeout)
60    {
61        if (duration_cast&lt;chrono::milliseconds&gt;(steady_clock::now() - t0).count() 
62            &gt; sliceIntervalMilliSeconds)
63        {
64            if (!oneSliceWork())
65            {
66                if (++failureCounter &gt; maxFailsBeforeRestart 
67                    &amp;&amp; maxFailsBeforeRestart &gt; 0)
68                {
69                    init();
70                    failureCounter = 0;
71                }
72            }
73            t0 = steady_clock::now();
74        }
75    }
76    printf(&quot;work terminated for %s!\n&quot;, label);
77}
78
79void sliceWork::signalTerminate()
80{
81    printf(&quot;request terminate for work %s...\n&quot;, label);
82    workPromise.set_value();
83}
84#include &lt;string.h&gt;
85#include &quot;sliceWork.h&quot;
86
87class A : public sliceWork
88{
89    void init() {
90        printf(&quot;Init A...\n&quot;);
91    }
92
93    bool oneSliceWork() {
94        printf(&quot;Working A...\n&quot;);
95        return true;
96    }
97public:
98    A(int slice, int max, const char* label) 
99        : sliceWork(slice, max, label) 
100    {
101    }
102};
103
104class B : public sliceWork
105{
106    void init() {
107        printf(&quot;Init B...\n&quot;);
108    }
109
110    bool oneSliceWork() {
111        printf(&quot;Working B...\n&quot;);
112        return true;
113    }
114public:
115    B(int slice, int max, const char* label) 
116        : sliceWork(slice, max, label) 
117    {
118    }
119};
120
121class C : public sliceWork
122{
123    void init() {
124        printf(&quot;Init C...\n&quot;);
125    }
126
127    bool oneSliceWork() {
128        printf(&quot;Working C...\n&quot;);
129        return false;
130    }
131public:
132    C(int slice, int max, const char* label) 
133        : sliceWork(slice, max, label) 
134    {
135    }
136};
137
138int main()
139{
140     {
141         A a(1000, 1000, &quot;A&quot;);
142         a.initWork();
143         B b(2000, 1000, &quot;B&quot; );
144         b.initWork();
145         C c(700, 2, &quot;C&quot; );
146         c.initWork();
147         printf(&quot;Initializations finished!\n&quot;);
148         ::std::this_thread::sleep_for(::std::chrono::seconds(7));
149         a.signalTerminate();
150         ::std::this_thread::sleep_for(::std::chrono::seconds(5));
151         b.signalTerminate();
152         ::std::this_thread::sleep_for(::std::chrono::seconds(4));
153         c.signalTerminate();
154     }
155     getchar();
156     return 0;
157}
158#pragma once
159#include &lt;iostream&gt;
160#include &lt;future&gt;
161
162class sliceWork
163{
164public:
165    sliceWork(int sliceInterval, int maxFails, const char* label);
166    ~sliceWork();
167    void initWork();
168    void signalTerminate();
169
170protected:
171    virtual void init()
172    {
173        std::cout &lt;&lt; &quot;Init &quot; &lt;&lt; label &lt;&lt; &quot;..\n&quot;;
174    }
175    virtual bool oneSliceWork()
176    {
177        std::cout &lt;&lt; &quot;Working &quot; &lt;&lt; label &lt;&lt; &quot;..\n&quot;;
178        return true;
179    }
180    void work(std::future&lt;void&gt; future);
181
182private:
183    int sliceIntervalMilliSeconds;
184    int failureCounter;
185    int maxFailsBeforeRestart;
186    char* label = NULL;
187    std::promise&lt;void&gt; workPromise;
188    std::thread* workerThread = NULL;
189};
190#include &lt;string.h&gt;
191#include &quot;sliceWork.h&quot;
192
193class A : public sliceWork
194{
195public:
196    A(int slice, int max, const char* label)
197        : sliceWork(slice, max, label)
198    {
199    }
200};
201
202class B : public sliceWork
203{
204public:
205    B(int slice, int max, const char* label)
206        : sliceWork(slice, max, label)
207    {
208    }
209};
210
211class C : public sliceWork
212{
213public:
214    C(int slice, int max, const char* label)
215        : sliceWork(slice, max, label)
216    {
217    }
218};
219
220int main()
221{
222    {
223        A a(1000, 1000, &quot;A&quot;);
224        a.initWork();
225        B b(2000, 1000, &quot;B&quot;);
226        b.initWork();
227        C c(700, 2, &quot;C&quot;);
228        c.initWork();
229        std::cout &lt;&lt; &quot;Initializations finished!\n&quot;;
230        ::std::this_thread::sleep_for(::std::chrono::seconds(7));
231        a.signalTerminate();
232        ::std::this_thread::sleep_for(::std::chrono::seconds(5));
233        b.signalTerminate();
234        ::std::this_thread::sleep_for(::std::chrono::seconds(4));
235        c.signalTerminate();
236    }
237    getchar();
238    return 0;
239}
240

sliceWork.cpp

copy icondownload icon

1#pragma once
2#include &lt;future&gt;
3using namespace ::std;
4
5class sliceWork
6{
7    int sliceIntervalMilliSeconds;
8    int failureCounter;
9    int maxFailsBeforeRestart;
10    char* label = NULL;
11    
12    promise&lt;void&gt; workPromise;
13    thread* workerThread = NULL;
14
15    virtual void init() = 0;
16    virtual bool oneSliceWork() = 0;