Skip to content

Commit fd8cd8f

Browse files
committed
Add a safe timer example. Other minor updates.
1 parent 81973a7 commit fd8cd8f

File tree

7 files changed

+92
-7
lines changed

7 files changed

+92
-7
lines changed

docs/DETAILS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1831,6 +1831,9 @@ Users create an instance of the timer and register for the expiration. In this c
18311831
m_timer.Expired = MakeDelegate(&myClass, &MyClass::MyCallback, myThread);
18321832
m_timer.Start(std::chrono::milliseconds(1000));
18331833
```
1834+
1835+
See example `SafeTimer.cpp` to prevent a latent callback on a dead object and [Object Lifetime and Async Delegates](#object-lifetime-and-async-delegates) for an explaination.
1836+
18341837
## `std::async` Thread Targeting Example
18351838

18361839
An example combining `std::async`/`std::future` and an asynchronous delegate to target a specific worker thread during communication transmission.

example/sample-code/SafeTimer.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/// @brief Implement the safe timer test to prevent a latent callback
2+
/// on a dead object.
3+
///
4+
/// See Object Lifetime and Async Delegates in DETAILS.md for more information.
5+
6+
#include "SafeTimer.h"
7+
#include "DelegateMQ.h"
8+
9+
using namespace dmq;
10+
using namespace std;
11+
12+
namespace Example
13+
{
14+
// The SafeTimer class safely creates a timer and registers/unregisters
15+
// while preventing any pending timer callbacks on a deleted object.
16+
class SafeTimer : public std::enable_shared_from_this<SafeTimer>
17+
{
18+
public:
19+
SafeTimer() : m_thread("SafeTimer Thread") { }
20+
21+
void Init() {
22+
m_thread.CreateThread();
23+
24+
// Bind the callback with a shared reference. This ensures the object remains
25+
// valid/alive when the timer fires, preventing a crash on a dangling pointer.
26+
m_timer.Expired = MakeDelegate(shared_from_this(), &SafeTimer::OnTimer, m_thread);
27+
m_timer.Start(std::chrono::milliseconds(100));
28+
}
29+
30+
// Unregister in Term to be clean
31+
void Term() {
32+
m_timer.Stop();
33+
m_timer.Expired.Clear();
34+
35+
m_thread.ExitThread();
36+
}
37+
38+
~SafeTimer() {
39+
// Even if Shutdown() wasn't called, the shared delegate prevents a crash
40+
// if a stray timer event is still pending in workerThread.
41+
}
42+
43+
private:
44+
void OnTimer() {
45+
std::cout << "Timer fired safely!" << std::endl;
46+
}
47+
48+
Timer m_timer;
49+
Thread m_thread;
50+
};
51+
52+
void SafeTimerExample() {
53+
auto safeTimer = std::make_shared<SafeTimer>();
54+
safeTimer->Init();
55+
56+
this_thread::sleep_for(chrono::milliseconds(500));
57+
58+
safeTimer->Term();
59+
}
60+
}

example/sample-code/SafeTimer.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef SAFE_TIMER_H
2+
#define SAFE_TIMER_H
3+
4+
namespace Example
5+
{
6+
/// Execute the safe timer test example
7+
void SafeTimerExample();
8+
}
9+
10+
#endif

src/delegate-mq/delegate/DelegateAsync.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -694,10 +694,6 @@ class DelegateMemberAsync<TClass, RetType(Args...)> : public DelegateMember<TCla
694694
// </common_code>
695695
};
696696

697-
// ----------------------------------------------------------------------------------------------
698-
// NEW CLASS: DelegateMemberAsyncShared
699-
// ----------------------------------------------------------------------------------------------
700-
701697
template <class C, class R>
702698
struct DelegateMemberAsyncShared; // Not defined
703699

src/delegate-mq/predef/os/stdlib/Thread.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,17 @@ void Thread::ExitThread()
124124
if (!m_thread)
125125
return;
126126

127-
if (m_watchdogTimer)
127+
if (m_watchdogTimer)
128+
{
128129
m_watchdogTimer->Stop();
129-
if (m_threadTimer)
130+
m_watchdogTimer->Expired.Clear();
131+
}
132+
133+
if (m_threadTimer)
134+
{
130135
m_threadTimer->Stop();
136+
m_threadTimer->Expired.Clear();
137+
}
131138

132139
// Create a new ThreadMsg
133140
std::shared_ptr<ThreadMsg> threadMsg(new ThreadMsg(MSG_EXIT_THREAD, 0));

src/delegate-mq/predef/util/Timer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
/// @brief A timer class provides periodic timer callbacks on the client's
99
/// thread of control. Timer is thread safe.
10+
/// See example SafeTimer.cpp to prevent a latent callback on a dead object.
1011
class Timer
1112
{
1213
public:

test/main.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// "Porting Guide" within DETAILS.md offers complete porting guidance.
99

1010
#include "DelegateMQ.h"
11+
#include "SafeTimer.h"
1112
#include "ProducerConsumer.h"
1213
#include "CountdownLatch.h"
1314
#include "ActiveObject.h"
@@ -292,6 +293,9 @@ void RunAllExamples()
292293
// Run all target types example
293294
AllTargetsExample();
294295

296+
// Run safe timer example
297+
SafeTimerExample();
298+
295299
// Run asynchronous API using delegates example
296300
AsyncAPIExample();
297301

@@ -523,13 +527,17 @@ void RunMiscExamples()
523527
// Could be helpful if T is very large and two or more clients register to receive asynchronous
524528
// callbacks.
525529
CoordinatesHandler coordinatesHandler;
526-
CoordinatesHandler::CoordinatesChanged += MakeDelegate(&CoordinatesChangedCallback, workerThread1);
530+
531+
auto coordDelegate = MakeDelegate(&CoordinatesChangedCallback, workerThread1);
532+
CoordinatesHandler::CoordinatesChanged += coordDelegate;
527533

528534
Coordinates coordinates;
529535
coordinates.x = 11;
530536
coordinates.y = 99;
531537
coordinatesHandler.SetData(coordinates);
532538

539+
CoordinatesHandler::CoordinatesChanged -= coordDelegate;
540+
533541
// Invoke virtual base function example
534542
std::shared_ptr<Base> base = std::make_shared<Derived>();
535543
auto baseDelegate = MakeDelegate(base, &Base::display, workerThread1);

0 commit comments

Comments
 (0)