diff --git a/src/async/core/AsyncAtTimer.cpp b/src/async/core/AsyncAtTimer.cpp index bb6f23a1..4bfc64b1 100644 --- a/src/async/core/AsyncAtTimer.cpp +++ b/src/async/core/AsyncAtTimer.cpp @@ -117,7 +117,6 @@ using namespace Async; ****************************************************************************/ AtTimer::AtTimer(void) - : m_expire_offset(0) { timerclear(&m_expire_at); m_timer.expired.connect(mem_fun(*this, &AtTimer::onTimerExpired)); @@ -125,7 +124,6 @@ AtTimer::AtTimer(void) AtTimer::AtTimer(struct tm &tm, bool do_start) - : m_expire_offset(0) { timerclear(&m_expire_at); m_timer.expired.connect(mem_fun(*this, &AtTimer::onTimerExpired)); diff --git a/src/async/core/AsyncAtTimer.h b/src/async/core/AsyncAtTimer.h index 65cf1a28..7aa593bc 100644 --- a/src/async/core/AsyncAtTimer.h +++ b/src/async/core/AsyncAtTimer.h @@ -137,6 +137,8 @@ class AtTimer : public sigc::trackable public: /** * @brief Default constructor + * + * After default construction the timer will be disabled. */ AtTimer(void); @@ -146,7 +148,7 @@ class AtTimer : public sigc::trackable * @param do_start Set to \em true (default) if the timer should start * upon creation */ - AtTimer(struct tm &tm, bool do_start=true); + explicit AtTimer(struct tm &tm, bool do_start=true); /** * @brief Destructor @@ -202,9 +204,9 @@ class AtTimer : public sigc::trackable protected: private: - Timer m_timer; + Timer m_timer {-1}; struct timeval m_expire_at; - int m_expire_offset; + int m_expire_offset {0}; AtTimer(const AtTimer&); AtTimer& operator=(const AtTimer&); diff --git a/src/async/core/AsyncStateMachine.h b/src/async/core/AsyncStateMachine.h index 064ba1bc..9ec55dfb 100644 --- a/src/async/core/AsyncStateMachine.h +++ b/src/async/core/AsyncStateMachine.h @@ -49,6 +49,7 @@ An example of how to use the StateMachine class ****************************************************************************/ #include +#include /**************************************************************************** @@ -173,6 +174,13 @@ class StateMachine static_cast(m_state)->timeoutEvent(); clearTimeout(); }); + m_at_timer.expired.connect( + [&](AtTimer*) + { + assert(m_state != nullptr); + static_cast(m_state)->timeoutAtEvent(); + clearTimeoutAt(); + }); } /** @@ -287,8 +295,7 @@ class StateMachine * * Use this function to set a timeout to occur after the specified number * of milliseconds. The timeoutEvent will be issued after the time has - * expired. The timeout will be automatically cleared when an exit from a - * state occur. + * expired. */ void setTimeout(int timeout_ms) { @@ -296,6 +303,22 @@ class StateMachine m_timer.setEnable(true); } + /** + * @brief Set a timeout after which the timeoutAtEvent is issued + * @param tm The absolute time when the timeout should occur + * @param expire_offset A millisecond offset for the timer expiration + * + * Use this function to set a timeout to occur at the specified absolute + * time, plus or minus the offset value. The time is specified in local + * time. The timeoutAtEvent will be issued after the time has expired. + */ + void setTimeoutAt(struct tm& tm, int expire_offset=0) + { + m_at_timer.setTimeout(tm); + m_at_timer.setExpireOffset(expire_offset); + m_at_timer.start(); + } + /** * @brief Clear a pending timeout * @@ -307,10 +330,22 @@ class StateMachine m_timer.setEnable(false); } + /** + * @brief Clear a pending absolute time timeout + * + * Use this function to immediately cancel a running absolute time timeout + * timer. See \ref setTimeoutAt for more information. + */ + void clearTimeoutAt(void) + { + m_at_timer.stop(); + } + private: - StateTopT* m_state = nullptr; - ContextT* m_ctx = nullptr; - Timer m_timer = -1; + StateTopT* m_state = nullptr; + ContextT* m_ctx = nullptr; + Timer m_timer = -1; + AtTimer m_at_timer; }; /* StateMachine */ @@ -380,7 +415,6 @@ class StateBase : public ParentT { if (dynamic_cast(to) == nullptr) { - ParentT::clearTimeout(); dynamic_cast(this)->exit(); ParentT::exitHandler(to); } @@ -440,6 +474,11 @@ class StateBase : public ParentT assert(!"Async::StateBase: Unhandled timeoutEvent"); } + virtual void timeoutAtEvent(void) override + { + assert(!"Async::StateBase: Unhandled timeoutAtEvent"); + } + }; /* StateBase */ @@ -508,11 +547,24 @@ class StateTopBase * * Use this function to set a timeout to occur after the specified number * of milliseconds. The timeoutEvent will be issued after the time has - * expired. The timeout will be automatically cleared when an exit from a - * state occur. + * expired. */ void setTimeout(int timeout_ms) { m_sm->setTimeout(timeout_ms); } + /** + * @brief Set a timeout after which the timeoutAtEvent is issued + * @param tm The absolute time when the timeout should occur + * @param expire_offset A millisecond offset for the timer expiration + * + * Use this function to set a timeout to occur at the specified absolute + * time, plus or minus the offset value. The time is specified in local + * time. The timeoutAtEvent will be issued after the time has expired. + */ + void setTimeoutAt(struct tm& tm, int expire_offset=0) + { + m_sm->setTimeoutAt(tm, expire_offset); + } + /** * @brief Clear a pending timeout * @@ -521,6 +573,14 @@ class StateTopBase */ void clearTimeout(void) { m_sm->clearTimeout(); } + /** + * @brief Clear a pending absolute time timeout + * + * Use this function to immediately cancel a running absolute time timeout + * timer. See \ref setTimeoutAt for more information. + */ + void clearTimeoutAt(void) { m_sm->clearTimeoutAt(); } + /** * @brief Get the typeid for this state */ @@ -556,6 +616,19 @@ class StateTopBase */ virtual void timeoutEvent(void) = 0; + /** + * @brief Event function called when an absolute time timeout occurs + * + * This event function will be called when an absolute time timeout, + * previously set up using the setTimeoutAt function, has occurred. + * + * As all event functions this is a virtual function which work like any + * other virtual function in C++. The state which is furtherest down in the + * hierarchy, which have the timeoutAtEvent function implemented, will have + * the function called. + */ + virtual void timeoutAtEvent(void) = 0; + private: StateMachineT* m_sm; diff --git a/src/async/core/AsyncTcpPrioClientBase.cpp b/src/async/core/AsyncTcpPrioClientBase.cpp index b728f97c..ffe0bf6a 100644 --- a/src/async/core/AsyncTcpPrioClientBase.cpp +++ b/src/async/core/AsyncTcpPrioClientBase.cpp @@ -30,6 +30,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ****************************************************************************/ +#include #include @@ -132,23 +133,6 @@ class TcpPrioClientBase::Machine ctx.connect_retry_wait.setRandomizePercent(p); } - void setBgConnectMinTime(unsigned t) - { - ctx.bg_connect_retry_wait.setMinTime(t); - } - void setBgConnectMaxTime(unsigned t) - { - ctx.bg_connect_retry_wait.setMaxTime(t); - } - void setBgConnectBackoffPercent(unsigned p) - { - ctx.bg_connect_retry_wait.setBackoffPercent(p); - } - void setBgConnectRandomizePercent(unsigned p) - { - ctx.bg_connect_retry_wait.setRandomizePercent(p); - } - void setLookupParams(const std::string& label, DnsLookup::Type type) { ctx.dns.setLookupParams(label, DnsLookup::Type::SRV); @@ -228,7 +212,7 @@ class TcpPrioClientBase::Machine operator Time() { return time(); } private: - Time m_min_time = 2000; + Time m_min_time = 1000; Time m_max_time = 20000; Percent m_backoff = 50; Percent m_randomize = 10; @@ -247,7 +231,6 @@ class TcpPrioClientBase::Machine DnsSRVList rrs; DnsSRVList::iterator next_rr = rrs.end(); BackoffTime connect_retry_wait; - BackoffTime bg_connect_retry_wait; Context(TcpPrioClientBase *client) : client(client), bg_con(client->newTcpClient()) {} @@ -443,6 +426,11 @@ class TcpPrioClientBase::Machine setTimeout(ctx().connect_retry_wait); } + void exit(void) noexcept + { + clearTimeout(); + } + virtual void timeoutEvent(void) noexcept override { DEBUG_EVENT; @@ -493,11 +481,6 @@ class TcpPrioClientBase::Machine : Async::StateBase { static constexpr auto NAME = "ConnectedLowerPrio"; - void entry(void) noexcept - { - DEBUG_EVENT; - ctx().bg_connect_retry_wait.reset(); - } }; /* StateConnecting */ @@ -508,10 +491,23 @@ class TcpPrioClientBase::Machine void entry(void) noexcept { - setTimeout(ctx().bg_connect_retry_wait.time()); + struct timeval tv; + auto err = gettimeofday(&tv, NULL); + assert(err == 0); + struct tm tm; + time_t timeout_at = tv.tv_sec + 60; + auto tm_ret = localtime_r(&timeout_at, &tm); + assert(tm_ret == &tm); + tm.tm_sec = 0; + setTimeoutAt(tm, std::rand() % 500); } - virtual void timeoutEvent(void) noexcept override + void exit(void) noexcept + { + clearTimeoutAt(); + } + + virtual void timeoutAtEvent(void) noexcept override { DEBUG_EVENT; setState(); @@ -683,30 +679,6 @@ void TcpPrioClientBase::setReconnectRandomizePercent(unsigned p) } -void TcpPrioClientBase::setBgConnectMinTime(unsigned t) -{ - m_machine->setBgConnectMinTime(t); -} - - -void TcpPrioClientBase::setBgConnectMaxTime(unsigned t) -{ - m_machine->setBgConnectMaxTime(t); -} - - -void TcpPrioClientBase::setBgConnectBackoffPercent(unsigned p) -{ - m_machine->setBgConnectBackoffPercent(p); -} - - -void TcpPrioClientBase::setBgConnectRandomizePercent(unsigned p) -{ - m_machine->setBgConnectRandomizePercent(p); -} - - void TcpPrioClientBase::setService(const std::string& srv_name, const std::string& srv_proto, const std::string& srv_domain) diff --git a/src/async/core/AsyncTcpPrioClientBase.h b/src/async/core/AsyncTcpPrioClientBase.h index 7cfd1ab5..366c8b5a 100644 --- a/src/async/core/AsyncTcpPrioClientBase.h +++ b/src/async/core/AsyncTcpPrioClientBase.h @@ -169,30 +169,6 @@ class TcpPrioClientBase : public TcpClientBase */ void setReconnectRandomizePercent(unsigned p); - /** - * @brief Minimum time between background reconnects - * @param t Time in milliseconds - */ - void setBgConnectMinTime(unsigned t); - - /** - * @brief Maximum time between background reconnects - * @param t Time in milliseconds - */ - void setBgConnectMaxTime(unsigned t); - - /** - * @brief Percent to increase background reconnect time with each try - * @param p Percent - */ - void setBgConnectBackoffPercent(unsigned p); - - /** - * @brief Percent to randomize background reconnect time - * @param p Percent - */ - void setBgConnectRandomizePercent(unsigned p); - /** * @brief Use a DNS service resource record for connections * @param srv_name The name of the service diff --git a/src/async/cpp/AsyncCppDnsLookupWorker.cpp b/src/async/cpp/AsyncCppDnsLookupWorker.cpp index d65485ee..b8917650 100644 --- a/src/async/cpp/AsyncCppDnsLookupWorker.cpp +++ b/src/async/cpp/AsyncCppDnsLookupWorker.cpp @@ -181,14 +181,13 @@ bool CppDnsLookupWorker::doLookup(void) m_notifier_watch.setFd(fd[0], FdWatch::FD_WATCH_RD); m_notifier_watch.setEnabled(true); - ThreadContext ctx; - ctx.label = dns().label(); - ctx.type = dns().type(); - ctx.notifier_wr = fd[1]; - ctx.anslen = 0; - ctx.thread_cerr.clear(); - m_result = std::move(std::async(std::launch::async, workerFunc, - std::move(ctx))); + m_ctx = std::unique_ptr(new ThreadContext); + m_ctx->label = dns().label(); + m_ctx->type = dns().type(); + m_ctx->notifier_wr = fd[1]; + m_ctx->anslen = 0; + m_ctx->thread_cerr.clear(); + m_result = std::async(std::launch::async, workerFunc, std::ref(*m_ctx)); return true; @@ -199,13 +198,7 @@ void CppDnsLookupWorker::abortLookup(void) { if (m_result.valid()) { - const ThreadContext& ctx(m_result.get()); - - if (ctx.addrinfo != nullptr) - { - freeaddrinfo(ctx.addrinfo); - } - //m_result = std::move(std::future()); + m_result.get(); } int fd = m_notifier_watch.fd(); @@ -214,6 +207,8 @@ void CppDnsLookupWorker::abortLookup(void) m_notifier_watch.setFd(-1, FdWatch::FD_WATCH_RD); close(fd); } + + m_ctx.reset(); } /* CppDnsLookupWorker::abortLookup */ @@ -239,8 +234,7 @@ void CppDnsLookupWorker::abortLookup(void) * Bugs: *---------------------------------------------------------------------------- */ -CppDnsLookupWorker::ThreadContext CppDnsLookupWorker::workerFunc( - CppDnsLookupWorker::ThreadContext ctx) +void CppDnsLookupWorker::workerFunc(CppDnsLookupWorker::ThreadContext& ctx) { std::ostream& th_cerr = ctx.thread_cerr; @@ -343,8 +337,6 @@ CppDnsLookupWorker::ThreadContext CppDnsLookupWorker::workerFunc( close(ctx.notifier_wr); ctx.notifier_wr = -1; - - return std::move(ctx); } /* CppDnsLookupWorker::workerFunc */ @@ -370,22 +362,22 @@ void CppDnsLookupWorker::notificationReceived(FdWatch *w) close(w->fd()); w->setFd(-1, FdWatch::FD_WATCH_RD); - const ThreadContext& ctx(m_result.get()); + m_result.get(); - const std::string& thread_errstr = ctx.thread_cerr.str(); + const std::string& thread_errstr = m_ctx->thread_cerr.str(); if (!thread_errstr.empty()) { std::cerr << thread_errstr; setLookupFailed(); } - if (ctx.type == DnsResourceRecord::Type::A) + if (m_ctx->type == DnsResourceRecord::Type::A) { - if (ctx.addrinfo != nullptr) + if (m_ctx->addrinfo != nullptr) { struct addrinfo *entry; std::vector the_addresses; - for (entry = ctx.addrinfo; entry != 0; entry = entry->ai_next) + for (entry = m_ctx->addrinfo; entry != 0; entry = entry->ai_next) { IpAddress ip_addr( reinterpret_cast(entry->ai_addr)->sin_addr); @@ -398,23 +390,23 @@ void CppDnsLookupWorker::notificationReceived(FdWatch *w) { the_addresses.push_back(ip_addr); addResourceRecord( - new DnsResourceRecordA(ctx.label, 0, ip_addr)); + new DnsResourceRecordA(m_ctx->label, 0, ip_addr)); } } - freeaddrinfo(ctx.addrinfo); + m_ctx.reset(); } } - else if (ctx.type == DnsResourceRecord::Type::PTR) + else if (m_ctx->type == DnsResourceRecord::Type::PTR) { - if (ctx.host[0] != '\0') + if (m_ctx->host[0] != '\0') { addResourceRecord( - new DnsResourceRecordPTR(ctx.label, 0, ctx.host)); + new DnsResourceRecordPTR(m_ctx->label, 0, m_ctx->host)); } } else { - if (ctx.anslen == -1) + if (m_ctx->anslen == -1) { workerDone(); return; @@ -422,12 +414,12 @@ void CppDnsLookupWorker::notificationReceived(FdWatch *w) char errbuf[256]; ns_msg msg; - int ret = ns_initparse(ctx.answer, ctx.anslen, &msg); + int ret = ns_initparse(m_ctx->answer, m_ctx->anslen, &msg); if (ret == -1) { strerror_r(errno, errbuf, sizeof(errbuf)); std::cerr << "*** WARNING: ns_initparse failed (anslen=" - << ctx.anslen << "): " << errbuf << std::endl; + << m_ctx->anslen << "): " << errbuf << std::endl; setLookupFailed(); workerDone(); return; diff --git a/src/async/cpp/AsyncCppDnsLookupWorker.h b/src/async/cpp/AsyncCppDnsLookupWorker.h index 24150a38..69621573 100644 --- a/src/async/cpp/AsyncCppDnsLookupWorker.h +++ b/src/async/cpp/AsyncCppDnsLookupWorker.h @@ -169,12 +169,22 @@ class CppDnsLookupWorker : public DnsLookupWorker, public sigc::trackable struct addrinfo* addrinfo = nullptr; char host[NI_MAXHOST] = {0}; std::ostringstream thread_cerr; + + ~ThreadContext(void) + { + if (addrinfo != nullptr) + { + freeaddrinfo(addrinfo); + addrinfo = nullptr; + } + } }; - Async::FdWatch m_notifier_watch; - std::future m_result; + Async::FdWatch m_notifier_watch; + std::future m_result; + std::unique_ptr m_ctx; - static ThreadContext workerFunc(ThreadContext ctx); + static void workerFunc(ThreadContext& ctx); void notificationReceived(FdWatch *w); }; /* class CppDnsLookupWorker */ diff --git a/src/versions b/src/versions index f1297d68..8b774bf7 100644 --- a/src/versions +++ b/src/versions @@ -8,10 +8,10 @@ QTEL=1.2.4.99.5 LIBECHOLIB=1.3.3.99.2 # Version for the Async library -LIBASYNC=1.6.99.21 +LIBASYNC=1.6.99.22 # SvxLink versions -SVXLINK=1.7.99.67 +SVXLINK=1.7.99.68 MODULE_HELP=1.0.0 MODULE_PARROT=1.1.1 MODULE_ECHO_LINK=1.5.99.3