Skip to content

Commit adae87a

Browse files
Reduced possible race conditions on disconnect and shitdown threads
Updated Timer callback to use shared_ptr Implemented consistent use of shared_ptr NullPtr types
1 parent 64e6049 commit adae87a

25 files changed

+511
-381
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ set (STTP_SOURCE_FILES
251251
"lib/ManualResetEvent.cpp"
252252
"lib/pugixml.cpp"
253253
"lib/ThreadPool.cpp"
254+
"lib/Timer.cpp"
254255
"lib/data/DataColumn.cpp"
255256
"lib/data/DataRow.cpp"
256257
"lib/data/DataSet.cpp"

src/lib/CommonTypes.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ const datetime_t DateTime::MaxValue(max_date_time);
5353

5454
const datetime_t DateTime::MinValue(min_date_time);
5555

56+
const ThreadPtr ThreadNullPtr = nullptr;
57+
5658
const string Empty::String {};
5759

5860
const datetime_t Empty::DateTime {};

src/lib/CommonTypes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ namespace sttp
301301
typedef boost::iostreams::gzip_compressor GZipCompressor;
302302

303303
typedef SharedPtr<Thread> ThreadPtr;
304+
static const ThreadPtr ThreadNullPtr;
305+
304306
#define ThreadSleep(ms) boost::this_thread::sleep(boost::posix_time::milliseconds(ms))
305307

306308
#if BOOST_LEGACY

src/lib/ThreadPool.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ void ThreadPool::Queue(const uint32_t delay, void* state, const std::function<vo
103103
if (m_disposing)
104104
return;
105105

106-
const TimerPtr waitTimer = NewSharedPtr<Timer>(delay, [&,this,action,state](const Timer* timer, void*)
106+
const TimerPtr waitTimer = NewSharedPtr<Timer>(delay, [&,this,action,state](const TimerPtr& timer, void*)
107107
{
108108
if (m_disposing)
109109
return;
@@ -117,12 +117,12 @@ void ThreadPool::Queue(const uint32_t delay, void* state, const std::function<vo
117117
if (m_disposing)
118118
return;
119119

120-
TimerPtr targetTimer = nullptr;
120+
TimerPtr targetTimer = Timer::NullPtr;
121121

122122
// Find this timer in reference set
123123
for (const auto& currentTimer : m_waitTimers)
124124
{
125-
if (currentTimer.get() == timer)
125+
if (currentTimer == timer)
126126
{
127127
targetTimer = currentTimer;
128128
break;

src/lib/Timer.cpp

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
//******************************************************************************************************
2+
// Timer.cpp - Gbtc
3+
//
4+
// Copyright © 2022, Grid Protection Alliance. All Rights Reserved.
5+
//
6+
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7+
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8+
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
9+
// file except in compliance with the License. You may obtain a copy of the License at:
10+
//
11+
// http://opensource.org/licenses/MIT
12+
//
13+
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14+
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15+
// License for the specific language governing permissions and limitations.
16+
//
17+
// Code Modification History:
18+
// ----------------------------------------------------------------------------------------------------
19+
// 12/10/2022 - J. Ritchie Carroll
20+
// Generated original version of source code.
21+
//
22+
//******************************************************************************************************
23+
24+
#include "Timer.h"
25+
26+
using namespace std;
27+
using namespace sttp;
28+
29+
const TimerPtr Timer::NullPtr = nullptr;
30+
31+
void Timer::TimerThread()
32+
{
33+
m_running = true;
34+
35+
do
36+
{
37+
try
38+
{
39+
const int32_t interval = m_interval;
40+
41+
if (interval > 0)
42+
{
43+
static constexpr int32_t MaxSleepDuration = 500;
44+
const int32_t waits = interval / MaxSleepDuration;
45+
const int32_t remainder = interval % MaxSleepDuration;
46+
47+
for (int32_t i = 0; i < waits && m_running; i++)
48+
ThreadSleep(MaxSleepDuration);
49+
50+
if (remainder > 0 && m_running)
51+
ThreadSleep(remainder);
52+
}
53+
}
54+
catch (boost::thread_interrupted&)
55+
{
56+
m_running = false;
57+
return;
58+
}
59+
60+
if (m_running && m_callback != nullptr)
61+
m_callback(m_this, m_userData);
62+
}
63+
while (m_autoReset && m_running);
64+
65+
m_running = false;
66+
}
67+
68+
Timer::Timer(): Timer(1000, nullptr, false)
69+
{
70+
}
71+
72+
Timer::Timer(const int32_t interval, TimerElapsedCallback callback, const bool autoReset):
73+
m_this(NullPtr),
74+
m_timerThread(ThreadNullPtr),
75+
m_interval(interval),
76+
m_callback(std::move(callback)),
77+
m_userData(nullptr),
78+
m_autoReset(autoReset),
79+
m_running(false)
80+
{
81+
}
82+
83+
Timer::~Timer() noexcept
84+
{
85+
try
86+
{
87+
Stop();
88+
}
89+
catch (...)
90+
{
91+
// ReSharper disable once CppRedundantControlFlowJump
92+
return;
93+
}
94+
}
95+
96+
bool Timer::IsRunning() const
97+
{
98+
return m_running;
99+
}
100+
101+
int32_t Timer::GetInterval() const
102+
{
103+
return m_interval;
104+
}
105+
106+
void Timer::SetInterval(const int32_t value)
107+
{
108+
if (value == m_interval)
109+
return;
110+
111+
const bool restart = m_running;
112+
Stop();
113+
114+
m_interval = value;
115+
116+
if (restart)
117+
Start();
118+
}
119+
120+
TimerElapsedCallback Timer::GetCallback() const
121+
{
122+
return m_callback;
123+
}
124+
125+
void Timer::SetCallback(TimerElapsedCallback value)
126+
{
127+
m_callback = std::move(value);
128+
}
129+
130+
const void* Timer::GetUserData() const
131+
{
132+
return m_userData;
133+
}
134+
135+
void Timer::SetUserData(void* value)
136+
{
137+
m_userData = value;
138+
}
139+
140+
bool Timer::GetAutoReset() const
141+
{
142+
return m_autoReset;
143+
}
144+
145+
void Timer::SetAutoReset(const bool value)
146+
{
147+
m_autoReset = value;
148+
}
149+
150+
void Timer::Start()
151+
{
152+
if (m_callback == nullptr)
153+
throw std::invalid_argument("Cannot start timer, no callback function has been defined.");
154+
155+
if (m_this == nullptr && !weak_from_this().expired())
156+
m_this = shared_from_this();
157+
158+
if (m_running)
159+
Stop();
160+
161+
m_timerThread = NewSharedPtr<sttp::Thread>([this] { TimerThread(); });
162+
}
163+
164+
void Timer::Stop()
165+
{
166+
if (!m_running)
167+
return;
168+
169+
m_running = false;
170+
171+
if (m_timerThread != nullptr)
172+
{
173+
const ThreadPtr timerThread = m_timerThread;
174+
175+
if (timerThread != nullptr)
176+
{
177+
timerThread->interrupt();
178+
179+
if (boost::this_thread::get_id() != timerThread->get_id())
180+
timerThread->join();
181+
}
182+
}
183+
184+
m_timerThread.reset();
185+
}
186+
187+
void Timer::Wait() const
188+
{
189+
try
190+
{
191+
if (m_running && m_timerThread != nullptr)
192+
{
193+
const ThreadPtr timerThread = m_timerThread;
194+
195+
if (timerThread != nullptr)
196+
timerThread->join();
197+
}
198+
}
199+
catch (...)
200+
{
201+
// ReSharper disable once CppRedundantControlFlowJump
202+
return;
203+
}
204+
}
205+
206+
void Timer::EmptyCallback(const TimerPtr&, void*)
207+
{
208+
}
209+
210+
TimerPtr Timer::WaitTimer(const int32_t interval, const bool autoStart)
211+
{
212+
TimerPtr waitTimer = NewSharedPtr<Timer>(interval, EmptyCallback);
213+
214+
if (autoStart)
215+
waitTimer->Start();
216+
217+
return waitTimer;
218+
}

0 commit comments

Comments
 (0)