-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.cpp
More file actions
147 lines (126 loc) · 5.1 KB
/
main.cpp
File metadata and controls
147 lines (126 loc) · 5.1 KB
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
* @file main.cpp
* @brief Self-Test Engine Example Entry Point.
*
* @details
* This example demonstrates a robust, multi-threaded architecture for a
* "Self-Test Engine" using the DelegateMQ library.
*
* **System Architecture:**
* - **SelfTestEngine (Master):** A Singleton State Machine running on its own
* dedicated worker thread. It coordinates the execution of sub-tests.
* - **Sub-Tests:** Independent State Machines (`CentrifugeTest`, `PressureTest`)
* that inherit from a common `SelfTest` base class.
* - **User Interface:** A simulated UI thread that receives status updates
* asynchronously from the Engine.
*
* **Key DelegateMQ Features Demonstrated:**
* 1. **Safe Signals (SignalPtr):**
* - Uses `std::shared_ptr<SignalSafe>` to ensure thread-safety and proper
* lifetime management.
* - The Engine exposes `OnStatus` (static) and `OnCompleted` (instance) signals.
*
* 2. **RAII Connection Management:**
* - Uses `dmq::ScopedConnection` to store connection handles.
* - This guarantees that signals are automatically disconnected if the
* connection object goes out of scope, preventing dangling pointer crashes.
*
* 3. **Asynchronous Marshaling:**
* - `MakeDelegate(..., userInterfaceThread)` is used to transparently marshal
* status callbacks from the Engine Thread to the User Interface Thread.
* - This eliminates the need for manual locking or message queues in application code.
*
* 4. **Hierarchical Coordination:**
* - The Engine subscribes to the `OnCompleted` / `OnFailed` signals of its
* sub-tests to direct the overall test flow.
*
* @see https://github.com/endurodave/DelegateMQ
*/
#include "DelegateMQ.h"
#include "SelfTestEngine.h"
#include <iostream>
#include <thread>
#include <atomic>
#include <chrono>
// @see https://github.com/endurodave/StateMachineWithModernDelegates
// David Lafreniere
using namespace std;
using namespace dmq;
// Flag to control the timer thread
std::atomic<bool> processTimerExit(false);
static void ProcessTimers()
{
while (!processTimerExit.load())
{
// Process all delegate-based timers
dmq::util::Timer::ProcessTimers();
std::this_thread::sleep_for(std::chrono::microseconds(50));
}
}
// A thread to capture self-test status callbacks for output to the "user interface"
dmq::os::Thread userInterfaceThread("UserInterface");
// Simple flag to exit main loop (Atomic for thread safety)
std::atomic<bool> selfTestEngineCompleted(false);
//------------------------------------------------------------------------------
// OnSelfTestEngineStatus
//------------------------------------------------------------------------------
void OnSelfTestEngineStatus(const SelfTestStatus& status)
{
// Output status message to the console "user interface"
cout << status.message.c_str() << endl;
}
//------------------------------------------------------------------------------
// OnSelfTestEngineComplete
//------------------------------------------------------------------------------
void OnSelfTestEngineComplete()
{
selfTestEngineCompleted = true;
}
//------------------------------------------------------------------------------
// main
//------------------------------------------------------------------------------
int main(void)
{
// Start the thread that will run ProcessTimers
std::thread timerThread(ProcessTimers);
// Create the worker threads
userInterfaceThread.CreateThread();
// Initialize instance
SelfTestEngine::GetInstance();
// -------------------------------------------------------------------------
// CONNECT SIGNALS (RAII)
// -------------------------------------------------------------------------
// We must store the connection handles!
// If we don't, ScopedConnection destructs immediately and disconnects.
ScopedConnection statusConn;
ScopedConnection completeConn;
// Register for status updates (Static Signal)
statusConn = SelfTestEngine::OnStatus.Connect(
MakeDelegate(&OnSelfTestEngineStatus, userInterfaceThread)
);
// Register for completion (Instance Signal from base class SelfTest)
completeConn = SelfTestEngine::GetInstance().OnCompleted.Connect(
MakeDelegate(&OnSelfTestEngineComplete, userInterfaceThread)
);
// Start self-test engine
StartData startData;
startData.shortSelfTest = TRUE;
SelfTestEngine::GetInstance().Start(&startData);
// Wait for self-test engine to complete
while (!selfTestEngineCompleted)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
// -------------------------------------------------------------------------
// DISCONNECT
// -------------------------------------------------------------------------
// Explicitly disconnect (optional, as destructors would handle this automatically)
statusConn.Disconnect();
completeConn.Disconnect();
// Exit the worker threads
userInterfaceThread.ExitThread();
SelfTestEngine::GetInstance().GetThread().ExitThread();
// Ensure the timer thread completes before main exits
processTimerExit.store(true);
if (timerThread.joinable())
timerThread.join();
return 0;
}