fix: schedule initial search use separate event from the generic work queue

By using `tcp_loop.dispatch` to schedule the initial search for a
channel, we are placing the callback into the same work queue that is
used by e.g. `MonitorBuilder::exec` to schedule the call to
`Channel::build`.  In situations where lots of channels are being
created simultaneously this can result in lots of single channel search
requests being sent because the work queue alternates between calls to
build a channel and the initial search.

In this commit we instead use a dedicated `evevent` to schedule the
initial search to allow the `initialSearchBucket` to be filled before we
send the initial search request. We delay the initial search by 10 ms to
give more time for the bucket to be filled.  See
github.com/mdavidsaver/pvxs/pull/39 for a discussion of how this delay
was chosen.
This commit is contained in:
Thomas Ives
2023-04-14 10:14:30 -07:00
committed by Michael Davidsaver
parent 7ae659678f
commit 07713faff4
2 changed files with 17 additions and 4 deletions
+15 -4
View File
@@ -28,6 +28,7 @@ namespace pvxs {
namespace client {
constexpr timeval bucketInterval{1,0};
constexpr timeval initialSearchDelay{0, 10000}; // 10 ms
constexpr size_t nBuckets = 30u;
// try not to fragment with usual MTU==1500
@@ -498,6 +499,8 @@ ContextImpl::ContextImpl(const Config& conf, const evbase& tcp_loop)
event_new(tcp_loop.base, searchTx6.sock, EV_READ|EV_PERSIST, &ContextImpl::onSearchS, this))
,searchTimer(__FILE__, __LINE__,
event_new(tcp_loop.base, -1, EV_TIMEOUT, &ContextImpl::tickSearchS, this))
,initialSearcher(__FILE__, __LINE__,
event_new(tcp_loop.base, -1, EV_TIMEOUT, &ContextImpl::initialSearchS, this))
,manager(UDPManager::instance(effective.shareUDP()))
,beaconCleaner(__FILE__, __LINE__,
event_new(manager.loop().base, -1, EV_TIMEOUT|EV_PERSIST, &ContextImpl::tickBeaconCleanS, this))
@@ -698,12 +701,11 @@ void ContextImpl::scheduleInitialSearch()
{
if (!initialSearchScheduled)
{
log_debug_printf(setup, "scheduleInitialSearch()%s\n", "");
log_debug_printf(setup, "%s()\n", __func__);
initialSearchScheduled = true;
tcp_loop.dispatch([this]() {
tickSearch(SearchKind::initial);
});
if (event_add(initialSearcher.get(), &initialSearchDelay))
throw std::runtime_error("Unable to schedule initialSearcher");
}
}
@@ -1186,6 +1188,15 @@ void ContextImpl::tickSearchS(evutil_socket_t fd, short evt, void *raw)
}
}
void ContextImpl::initialSearchS(evutil_socket_t fd, short evt, void *raw)
{
try {
static_cast<ContextImpl*>(raw)->tickSearch(SearchKind::initial);
}catch(std::exception& e){
log_exc_printf(io, "Unhandled error in initial search callback: %s\n", e.what());
}
}
void ContextImpl::tickBeaconClean()
{
epicsTimeStamp now;