Skip to content

Commit 7a0921d

Browse files
committed
clientContextImpl: Cap the number and age of beacons
Each beacon has an associated mutex. If we allocate too many beacons on resource constrained systems, i.e. RTEMS, we may run out of resources and crash.
1 parent 4e8b332 commit 7a0921d

File tree

2 files changed

+107
-5
lines changed

2 files changed

+107
-5
lines changed

src/remote/pv/beaconHandler.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
#include <pv/remote.h>
2727
#include <pv/pvAccess.h>
2828

29+
namespace
30+
{
31+
class InternalClientContextImpl;
32+
class BeaconCleanupHandler;
33+
}
34+
2935
namespace epics {
3036
namespace pvAccess {
3137

@@ -85,6 +91,10 @@ class BeaconHandler
8591
* First beacon flag.
8692
*/
8793
bool _first;
94+
/**
95+
* Callback for cleaning up the beacon
96+
*/
97+
std::shared_ptr<BeaconCleanupHandler> _callback;
8898

8999
/**
90100
* Update beacon.
@@ -100,6 +110,8 @@ class BeaconHandler
100110
ServerGUID const &guid,
101111
epics::pvData::int16 sequentalID,
102112
epics::pvData::int16 changeCount);
113+
114+
friend class ::InternalClientContextImpl;
103115
};
104116

105117
}

src/remoteClient/clientContextImpl.cpp

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ using std::tr1::static_pointer_cast;
4949
using namespace std;
5050
using namespace epics::pvData;
5151

52+
static const float maxBeaconLifetime = 180.f * 2.f;
53+
static const int maxTrackedBeacons = 2048;
54+
5255
namespace epics {
5356
namespace pvAccess {
5457

@@ -3038,7 +3041,43 @@ enum ContextState {
30383041
};
30393042

30403043

3044+
/**
3045+
* Handles cleanup of old beacons.
3046+
*/
3047+
class BeaconCleanupHandler
3048+
{
3049+
public:
3050+
POINTER_DEFINITIONS(BeaconCleanupHandler);
3051+
3052+
class Callback : public TimerCallback
3053+
{
3054+
public:
3055+
Callback(BeaconCleanupHandler& handler) : m_handler(handler)
3056+
{
3057+
}
3058+
3059+
virtual void callback() OVERRIDE FINAL;
3060+
virtual void timerStopped() OVERRIDE FINAL;
30413061

3062+
BeaconCleanupHandler& m_handler;
3063+
};
3064+
3065+
BeaconCleanupHandler(InternalClientContextImpl& impl, osiSockAddr addr);
3066+
~BeaconCleanupHandler();
3067+
3068+
/**
3069+
* Extend the lifetime of the beacon, resetting removal countdown to 0
3070+
*/
3071+
void touch() { epicsAtomicSetIntT(&m_count, 0); }
3072+
3073+
private:
3074+
void remove();
3075+
3076+
std::shared_ptr<BeaconCleanupHandler::Callback> m_callback;
3077+
osiSockAddr m_from;
3078+
InternalClientContextImpl& m_impl;
3079+
int m_count;
3080+
};
30423081

30433082
class InternalClientContextImpl :
30443083
public ClientContextImpl,
@@ -4058,6 +4097,12 @@ class InternalClientContextImpl :
40584097

40594098
m_timer->close();
40604099

4100+
// Remove all beacons
4101+
{
4102+
Lock guard(m_beaconMapMutex);
4103+
m_beaconHandlers.clear();
4104+
}
4105+
40614106
m_channelSearchManager->cancel();
40624107

40634108
// this will also close all PVA transports
@@ -4079,11 +4124,6 @@ class InternalClientContextImpl :
40794124
while ((transportCount = m_transportRegistry.size()) && tries--)
40804125
epicsThreadSleep(0.025);
40814126

4082-
{
4083-
Lock guard(m_beaconMapMutex);
4084-
m_beaconHandlers.clear();
4085-
}
4086-
40874127
if (transportCount)
40884128
LOG(logLevelDebug, "PVA client context destroyed with %u transport(s) active.", (unsigned)transportCount);
40894129
}
@@ -4351,12 +4391,25 @@ class InternalClientContextImpl :
43514391
BeaconHandler::shared_pointer handler;
43524392
if (it == m_beaconHandlers.end())
43534393
{
4394+
/* If we're tracking too many beacons, we'll just ignore this one */
4395+
if (m_beaconHandlers.size() >= maxTrackedBeacons)
4396+
{
4397+
char ipa[64];
4398+
sockAddrToDottedIP(&responseFrom->sa, ipa, sizeof(ipa));
4399+
LOG(logLevelDebug, "Tracked beacon limit reached (%d), ignoring %s\n", maxTrackedBeacons, ipa);
4400+
return nullptr;
4401+
}
4402+
43544403
// stores weak_ptr
43554404
handler.reset(new BeaconHandler(internal_from_this(), responseFrom));
4405+
handler->_callback.reset(new BeaconCleanupHandler(*this, *responseFrom));
43564406
m_beaconHandlers[*responseFrom] = handler;
43574407
}
43584408
else
4409+
{
43594410
handler = it->second;
4411+
handler->_callback->touch(); /* Update the callback's latest use time */
4412+
}
43604413
return handler;
43614414
}
43624415

@@ -4556,8 +4609,45 @@ class InternalClientContextImpl :
45564609
Configuration::shared_pointer m_configuration;
45574610

45584611
TransportRegistry::transportVector_t m_flushTransports;
4612+
4613+
friend class BeaconCleanupHandler;
45594614
};
45604615

4616+
4617+
BeaconCleanupHandler::BeaconCleanupHandler(InternalClientContextImpl& impl, osiSockAddr addr) :
4618+
m_from(addr),
4619+
m_impl(impl),
4620+
m_count(0)
4621+
{
4622+
m_callback.reset(new Callback(*this));
4623+
m_impl.m_timer->schedulePeriodic(m_callback, maxBeaconLifetime / 4, maxBeaconLifetime / 4);
4624+
}
4625+
4626+
BeaconCleanupHandler::~BeaconCleanupHandler()
4627+
{
4628+
m_impl.m_timer->cancel(m_callback);
4629+
}
4630+
4631+
void BeaconCleanupHandler::Callback::callback()
4632+
{
4633+
if (epicsAtomicIncrIntT(&m_handler.m_count) == 5) {
4634+
printf("Beacon deleted\n");
4635+
m_handler.remove();
4636+
}
4637+
}
4638+
4639+
void BeaconCleanupHandler::Callback::timerStopped()
4640+
{
4641+
m_handler.remove();
4642+
}
4643+
4644+
void BeaconCleanupHandler::remove()
4645+
{
4646+
Lock guard(m_impl.m_beaconMapMutex);
4647+
m_impl.m_timer->cancel(m_callback);
4648+
m_impl.m_beaconHandlers.erase(m_from);
4649+
}
4650+
45614651
size_t InternalClientContextImpl::num_instances;
45624652
size_t InternalClientContextImpl::InternalChannelImpl::num_instances;
45634653
size_t InternalClientContextImpl::InternalChannelImpl::num_active;

0 commit comments

Comments
 (0)