1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-21 10:26:06 +03:00

polledTimeout: add option to use CPU count instead of millis() (#5870)

* polledTimeout: add option to use CPU count instead of millis()

* use more "using" alias

* more c++/clear code, using typename (thanks @devyte)

* rename class name to include unit, introduce timeMax() and check it with assert()

* remove useless defines

* improve api readability, add micro-second unit

* update example

* mock: emulate getCycleCount, add/fix polledTimeout CI test

* + nano-seconds, assert -> message, comments, host test

* allow 0 for timeout (enables immediate timeout, fix division by 0)

* typo, set member instead of local variable

* unify error message

* slight change on checkExpired() allows "never expired"
also removed printed message, add YieldAndDelay, simplify calculations

* remove traces of debug.h/cpp in this PR

* include missing <limits> header

* back to original expired test, introduce boolean _neverExpires, fix reset(), getTimeout() is invalid

* fix expiredOneShot with _timeout==0 check

* reenable getTimeout()

* expose checkExpired with unit conversion

* fix timing comments, move critical code to iram

* add member ::neverExpires and use it where relevant

* improve clarity

* remove exposed checkExpired(), adapt LEAmDNS with equivalent

* add API ::resetToNeverExpires(), use it in LEAmDNS

* remove offending constness from ::flagged() LEAmDNS (due do API fix in PolledTimeout)

* simplify "Fast" base classes

* minor variable rename

* Fix examples

* compliance with good c++ manners

* minor changes for consistency

* add missing const

* expired() and bool() moved to iram

* constexpr compensation computing

* add/update comments

* move neverExpires and alwaysExpired
This commit is contained in:
david gauchard 2019-04-05 15:50:53 +02:00 committed by Develo
parent f0eb5509a0
commit 9a2ed274f3
15 changed files with 344 additions and 92 deletions

View File

@ -200,15 +200,20 @@ class EspClass {
bool eraseConfig(); bool eraseConfig();
inline uint32_t getCycleCount(); #ifndef CORE_MOCK
inline
#endif
uint32_t getCycleCount();
}; };
#ifndef CORE_MOCK
uint32_t EspClass::getCycleCount() uint32_t EspClass::getCycleCount()
{ {
uint32_t ccount; uint32_t ccount;
__asm__ __volatile__("esync; rsr %0,ccount":"=a" (ccount)); __asm__ __volatile__("esync; rsr %0,ccount":"=a" (ccount));
return ccount; return ccount;
} }
#endif
extern EspClass ESP; extern EspClass ESP;

View File

@ -139,7 +139,7 @@ size_t HardwareSerial::readBytes(char* buffer, size_t size)
while (got < size) while (got < size)
{ {
esp8266::polledTimeout::oneShot timeOut(_timeout); esp8266::polledTimeout::oneShotFastMs timeOut(_timeout);
size_t avail; size_t avail;
while ((avail = available()) == 0 && !timeOut); while ((avail = available()) == 0 && !timeOut);
if (avail == 0) if (avail == 0)

View File

@ -23,6 +23,8 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include <limits>
#include <Arduino.h> #include <Arduino.h>
namespace esp8266 namespace esp8266
@ -45,19 +47,112 @@ struct YieldOrSkip
static void execute() {delay(0);} static void execute() {delay(0);}
}; };
template <unsigned long delayMs>
struct YieldAndDelayMs
{
static void execute() {delay(delayMs);}
};
} //YieldPolicy } //YieldPolicy
namespace TimePolicy
{
template <bool PeriodicT, typename YieldPolicyT = YieldPolicy::DoNothing> struct TimeSourceMillis
{
// time policy in milli-seconds based on millis()
using timeType = decltype(millis());
static timeType time() {return millis();}
static constexpr timeType ticksPerSecond = 1000;
static constexpr timeType ticksPerSecondMax = 1000;
};
struct TimeSourceCycles
{
// time policy based on ESP.getCycleCount()
// this particular time measurement is intended to be called very often
// (every loop, every yield)
using timeType = decltype(ESP.getCycleCount());
static timeType time() {return ESP.getCycleCount();}
static constexpr timeType ticksPerSecond = F_CPU; // 80'000'000 or 160'000'000 Hz
static constexpr timeType ticksPerSecondMax = 160000000; // 160MHz
};
template <typename TimeSourceType, unsigned long long second_th>
// "second_th" units of timeType for one second
struct TimeUnit
{
using timeType = typename TimeSourceType::timeType;
#if __GNUC__ < 5
// gcc-4.8 cannot compile the constexpr-only version of this function
// using #defines instead luckily works
static constexpr timeType computeRangeCompensation ()
{
#define number_of_secondTh_in_one_tick ((1.0 * second_th) / ticksPerSecond)
#define fractional (number_of_secondTh_in_one_tick - (long)number_of_secondTh_in_one_tick)
return ({
fractional == 0?
1: // no need for compensation
(number_of_secondTh_in_one_tick / fractional) + 0.5; // scalar multiplier allowing exact division
});
#undef number_of_secondTh_in_one_tick
#undef fractional
}
#else
static constexpr timeType computeRangeCompensation ()
{
return ({
constexpr double number_of_secondTh_in_one_tick = (1.0 * second_th) / ticksPerSecond;
constexpr double fractional = number_of_secondTh_in_one_tick - (long)number_of_secondTh_in_one_tick;
fractional == 0?
1: // no need for compensation
(number_of_secondTh_in_one_tick / fractional) + 0.5; // scalar multiplier allowing exact division
});
}
#endif
static constexpr timeType ticksPerSecond = TimeSourceType::ticksPerSecond;
static constexpr timeType ticksPerSecondMax = TimeSourceType::ticksPerSecondMax;
static constexpr timeType rangeCompensate = computeRangeCompensation();
static constexpr timeType user2UnitMultiplierMax = (ticksPerSecondMax * rangeCompensate) / second_th;
static constexpr timeType user2UnitMultiplier = (ticksPerSecond * rangeCompensate) / second_th;
static constexpr timeType user2UnitDivider = rangeCompensate;
// std::numeric_limits<timeType>::max() is reserved
static constexpr timeType timeMax = (std::numeric_limits<timeType>::max() - 1) / user2UnitMultiplierMax;
static timeType toTimeTypeUnit (const timeType userUnit) {return (userUnit * user2UnitMultiplier) / user2UnitDivider;}
static timeType toUserUnit (const timeType internalUnit) {return (internalUnit * user2UnitDivider) / user2UnitMultiplier;}
static timeType time () {return TimeSourceType::time();}
};
using TimeMillis = TimeUnit< TimeSourceMillis, 1000 >;
using TimeFastMillis = TimeUnit< TimeSourceCycles, 1000 >;
using TimeFastMicros = TimeUnit< TimeSourceCycles, 1000000 >;
using TimeFastNanos = TimeUnit< TimeSourceCycles, 1000000000 >;
} //TimePolicy
template <bool PeriodicT, typename YieldPolicyT = YieldPolicy::DoNothing, typename TimePolicyT = TimePolicy::TimeMillis>
class timeoutTemplate class timeoutTemplate
{ {
public: public:
using timeType = decltype(millis()); using timeType = typename TimePolicyT::timeType;
timeoutTemplate(timeType timeout) static constexpr timeType alwaysExpired = 0;
: _timeout(timeout), _start(millis()) static constexpr timeType neverExpires = std::numeric_limits<timeType>::max();
{} static constexpr timeType rangeCompensate = TimePolicyT::rangeCompensate; //debug
timeoutTemplate(const timeType userTimeout)
{
reset(userTimeout);
}
ICACHE_RAM_ATTR
bool expired() bool expired()
{ {
YieldPolicyT::execute(); //in case of DoNothing: gets optimized away YieldPolicyT::execute(); //in case of DoNothing: gets optimized away
@ -66,37 +161,69 @@ public:
return expiredOneShot(); return expiredOneShot();
} }
ICACHE_RAM_ATTR
operator bool() operator bool()
{ {
return expired(); return expired();
} }
void reset(const timeType newTimeout) bool canExpire () const
{
return !_neverExpires;
}
bool canWait () const
{
return _timeout != alwaysExpired;
}
void reset(const timeType newUserTimeout)
{ {
_timeout = newTimeout;
reset(); reset();
_timeout = TimePolicyT::toTimeTypeUnit(newUserTimeout);
_neverExpires = (newUserTimeout < 0) || (newUserTimeout > timeMax());
} }
void reset() void reset()
{ {
_start = millis(); _start = TimePolicyT::time();
}
void resetToNeverExpires ()
{
_timeout = alwaysExpired + 1; // because canWait() has precedence
_neverExpires = true;
} }
timeType getTimeout() const timeType getTimeout() const
{ {
return _timeout; return TimePolicyT::toUserUnit(_timeout);
} }
bool checkExpired(const timeType t) const static constexpr timeType timeMax()
{ {
return (t - _start) >= _timeout; return TimePolicyT::timeMax;
}
private:
ICACHE_RAM_ATTR
bool checkExpired(const timeType internalUnit) const
{
// canWait() is not checked here
// returns "can expire" and "time expired"
return (!_neverExpires) && ((internalUnit - _start) >= _timeout);
} }
protected: protected:
ICACHE_RAM_ATTR
bool expiredRetrigger() bool expiredRetrigger()
{ {
timeType current = millis(); if (!canWait())
return true;
timeType current = TimePolicyT::time();
if(checkExpired(current)) if(checkExpired(current))
{ {
unsigned long n = (current - _start) / _timeout; //how many _timeouts periods have elapsed, will usually be 1 (current - _start >= _timeout) unsigned long n = (current - _start) / _timeout; //how many _timeouts periods have elapsed, will usually be 1 (current - _start >= _timeout)
@ -106,23 +233,50 @@ protected:
return false; return false;
} }
ICACHE_RAM_ATTR
bool expiredOneShot() const bool expiredOneShot() const
{ {
return checkExpired(millis()); // returns "always expired" or "has expired"
return !canWait() || checkExpired(TimePolicyT::time());
} }
timeType _timeout; timeType _timeout;
timeType _start; timeType _start;
bool _neverExpires;
}; };
using oneShot = polledTimeout::timeoutTemplate<false>; // legacy type names, deprecated (unit is milliseconds)
using periodic = polledTimeout::timeoutTemplate<true>;
using oneShot = polledTimeout::timeoutTemplate<false> /*__attribute__((deprecated("use oneShotMs")))*/;
using periodic = polledTimeout::timeoutTemplate<true> /*__attribute__((deprecated("use periodicMs")))*/;
// standard versions (based on millis())
// timeMax() is 49.7 days ((2^32)-2 ms)
using oneShotMs = polledTimeout::timeoutTemplate<false>;
using periodicMs = polledTimeout::timeoutTemplate<true>;
// Time policy based on ESP.getCycleCount(), and intended to be called very often:
// "Fast" versions sacrifices time range for improved precision and reduced execution time (by 86%)
// (cpu cycles for ::expired(): 372 (millis()) vs 52 (ESP.getCycleCount()))
// timeMax() values:
// Ms: max is 26843 ms (26.8 s)
// Us: max is 26843545 us (26.8 s)
// Ns: max is 1073741823 ns ( 1.07 s)
// (time policy based on ESP.getCycleCount() is intended to be called very often)
using oneShotFastMs = polledTimeout::timeoutTemplate<false, YieldPolicy::DoNothing, TimePolicy::TimeFastMillis>;
using periodicFastMs = polledTimeout::timeoutTemplate<true, YieldPolicy::DoNothing, TimePolicy::TimeFastMillis>;
using oneShotFastUs = polledTimeout::timeoutTemplate<false, YieldPolicy::DoNothing, TimePolicy::TimeFastMicros>;
using periodicFastUs = polledTimeout::timeoutTemplate<true, YieldPolicy::DoNothing, TimePolicy::TimeFastMicros>;
using oneShotFastNs = polledTimeout::timeoutTemplate<false, YieldPolicy::DoNothing, TimePolicy::TimeFastNanos>;
using periodicFastNs = polledTimeout::timeoutTemplate<true, YieldPolicy::DoNothing, TimePolicy::TimeFastNanos>;
} //polledTimeout } //polledTimeout
/* A 1-shot timeout that auto-yields when in CONT can be built as follows: /* A 1-shot timeout that auto-yields when in CONT can be built as follows:
* using oneShotYield = esp8266::polledTimeout::timeoutTemplate<false, esp8266::polledTimeout::YieldPolicy::YieldOrSkip>; * using oneShotYieldMs = esp8266::polledTimeout::timeoutTemplate<false, esp8266::polledTimeout::YieldPolicy::YieldOrSkip>;
* *
* Other policies can be implemented by the user, e.g.: simple yield that panics in SYS, and the polledTimeout types built as needed as shown above, without modifying this file. * Other policies can be implemented by the user, e.g.: simple yield that panics in SYS, and the polledTimeout types built as needed as shown above, without modifying this file.
*/ */

View File

@ -35,7 +35,7 @@
WiFiServer statusServer(TCP_PORT); WiFiServer statusServer(TCP_PORT);
WiFiUDP udp; WiFiUDP udp;
esp8266::polledTimeout::periodic statusPeriod(STATUSDELAY_MS); esp8266::polledTimeout::periodicMs showStatusOnSerialNow(STATUSDELAY_MS);
void fqdn(Print& out, const String& fqdn) { void fqdn(Print& out, const String& fqdn) {
out.print(F("resolving ")); out.print(F("resolving "));
@ -149,7 +149,7 @@ void setup() {
Serial.print(F(" - UDP server on port ")); Serial.print(F(" - UDP server on port "));
Serial.println(UDP_PORT); Serial.println(UDP_PORT);
statusPeriod.reset(); showStatusOnSerialNow.reset();
} }
unsigned long statusTimeMs = 0; unsigned long statusTimeMs = 0;
@ -182,7 +182,7 @@ void loop() {
} }
if (statusPeriod) { if (showStatusOnSerialNow) {
status(Serial); status(Serial);
} }

View File

@ -268,7 +268,7 @@ void loop(void) {
// Allow MDNS processing // Allow MDNS processing
MDNS.update(); MDNS.update();
static esp8266::polledTimeout::periodic timeout(UPDATE_CYCLE); static esp8266::polledTimeout::periodicMs timeout(UPDATE_CYCLE);
if (timeout.expired()) { if (timeout.expired()) {
if (hMDNSService) { if (hMDNSService) {

View File

@ -907,7 +907,7 @@ protected:
struct stcProbeInformation { struct stcProbeInformation {
enuProbingStatus m_ProbingStatus; enuProbingStatus m_ProbingStatus;
uint8_t m_u8SentCount; // Used for probes and announcements uint8_t m_u8SentCount; // Used for probes and announcements
esp8266::polledTimeout::oneShot m_Timeout; // Used for probes and announcements esp8266::polledTimeout::oneShotMs m_Timeout; // Used for probes and announcements
//clsMDNSTimeFlag m_TimeFlag; // Used for probes and announcements //clsMDNSTimeFlag m_TimeFlag; // Used for probes and announcements
bool m_bConflict; bool m_bConflict;
bool m_bTiebreakNeeded; bool m_bTiebreakNeeded;
@ -975,13 +975,13 @@ protected:
const timeoutLevel_t TIMEOUTLEVEL_FINAL = 100; const timeoutLevel_t TIMEOUTLEVEL_FINAL = 100;
uint32_t m_u32TTL; uint32_t m_u32TTL;
esp8266::polledTimeout::oneShot m_TTLTimeout; esp8266::polledTimeout::oneShotMs m_TTLTimeout;
timeoutLevel_t m_timeoutLevel; timeoutLevel_t m_timeoutLevel;
stcTTL(void); stcTTL(void);
bool set(uint32_t p_u32TTL); bool set(uint32_t p_u32TTL);
bool flagged(void) const; bool flagged(void);
bool restart(void); bool restart(void);
bool prepareDeletion(void); bool prepareDeletion(void);
@ -1078,7 +1078,7 @@ protected:
MDNSServiceQueryCallbackFunc m_fnCallback; MDNSServiceQueryCallbackFunc m_fnCallback;
bool m_bLegacyQuery; bool m_bLegacyQuery;
uint8_t m_u8SentCount; uint8_t m_u8SentCount;
esp8266::polledTimeout::oneShot m_ResendTimeout; esp8266::polledTimeout::oneShotMs m_ResendTimeout;
bool m_bAwaitingAnswers; bool m_bAwaitingAnswers;
stcAnswer* m_pAnswers; stcAnswer* m_pAnswers;

View File

@ -1047,7 +1047,7 @@ bool MDNSResponder::_updateProbeStatus(void) {
m_HostProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; m_HostProbeInformation.m_ProbingStatus = ProbingStatus_InProgress;
} }
else if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && // Probing AND else if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && // Probing AND
(m_HostProbeInformation.m_Timeout.checkExpired(millis()))) { // Time for next probe (m_HostProbeInformation.m_Timeout.expired())) { // Time for next probe
if (MDNS_PROBE_COUNT > m_HostProbeInformation.m_u8SentCount) { // Send next probe if (MDNS_PROBE_COUNT > m_HostProbeInformation.m_u8SentCount) { // Send next probe
if ((bResult = _sendHostProbe())) { if ((bResult = _sendHostProbe())) {
@ -1059,7 +1059,7 @@ bool MDNSResponder::_updateProbeStatus(void) {
else { // Probing finished else { // Probing finished
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host probing.\n"));); DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host probing.\n")););
m_HostProbeInformation.m_ProbingStatus = ProbingStatus_Done; m_HostProbeInformation.m_ProbingStatus = ProbingStatus_Done;
m_HostProbeInformation.m_Timeout.reset(std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max()); m_HostProbeInformation.m_Timeout.resetToNeverExpires();
if (m_HostProbeInformation.m_fnHostProbeResultCallback) { if (m_HostProbeInformation.m_fnHostProbeResultCallback) {
m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, true); m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, true);
} }
@ -1071,7 +1071,7 @@ bool MDNSResponder::_updateProbeStatus(void) {
} }
} // else: Probing already finished OR waiting for next time slot } // else: Probing already finished OR waiting for next time slot
else if ((ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) && else if ((ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) &&
(m_HostProbeInformation.m_Timeout.checkExpired(std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max()))) { (m_HostProbeInformation.m_Timeout.expired())) {
if ((bResult = _announce(true, false))) { // Don't announce services here if ((bResult = _announce(true, false))) { // Don't announce services here
++m_HostProbeInformation.m_u8SentCount; ++m_HostProbeInformation.m_u8SentCount;
@ -1081,7 +1081,7 @@ bool MDNSResponder::_updateProbeStatus(void) {
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Announcing host (%lu).\n\n"), m_HostProbeInformation.m_u8SentCount);); DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Announcing host (%lu).\n\n"), m_HostProbeInformation.m_u8SentCount););
} }
else { else {
m_HostProbeInformation.m_Timeout.reset(std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max()); m_HostProbeInformation.m_Timeout.resetToNeverExpires();
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host announcing.\n\n"));); DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host announcing.\n\n")););
} }
} }
@ -1096,7 +1096,7 @@ bool MDNSResponder::_updateProbeStatus(void) {
pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_InProgress;
} }
else if ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing AND else if ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing AND
(pService->m_ProbeInformation.m_Timeout.checkExpired(millis()))) { // Time for next probe (pService->m_ProbeInformation.m_Timeout.expired())) { // Time for next probe
if (MDNS_PROBE_COUNT > pService->m_ProbeInformation.m_u8SentCount) { // Send next probe if (MDNS_PROBE_COUNT > pService->m_ProbeInformation.m_u8SentCount) { // Send next probe
if ((bResult = _sendServiceProbe(*pService))) { if ((bResult = _sendServiceProbe(*pService))) {
@ -1108,7 +1108,7 @@ bool MDNSResponder::_updateProbeStatus(void) {
else { // Probing finished else { // Probing finished
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done service probing %s.%s.%s\n\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done service probing %s.%s.%s\n\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol););
pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_Done; pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_Done;
pService->m_ProbeInformation.m_Timeout.reset(std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max()); pService->m_ProbeInformation.m_Timeout.resetToNeverExpires();
if (pService->m_ProbeInformation.m_fnServiceProbeResultCallback) { if (pService->m_ProbeInformation.m_fnServiceProbeResultCallback) {
pService->m_ProbeInformation.m_fnServiceProbeResultCallback(pService->m_pcName, pService, true); pService->m_ProbeInformation.m_fnServiceProbeResultCallback(pService->m_pcName, pService, true);
} }
@ -1119,7 +1119,7 @@ bool MDNSResponder::_updateProbeStatus(void) {
} }
} // else: Probing already finished OR waiting for next time slot } // else: Probing already finished OR waiting for next time slot
else if ((ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) && else if ((ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) &&
(pService->m_ProbeInformation.m_Timeout.checkExpired(millis()))) { (pService->m_ProbeInformation.m_Timeout.expired())) {
if ((bResult = _announceService(*pService))) { // Announce service if ((bResult = _announceService(*pService))) { // Announce service
++pService->m_ProbeInformation.m_u8SentCount; ++pService->m_ProbeInformation.m_u8SentCount;
@ -1129,7 +1129,7 @@ bool MDNSResponder::_updateProbeStatus(void) {
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Announcing service %s.%s.%s (%lu)\n\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol, pService->m_ProbeInformation.m_u8SentCount);); DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Announcing service %s.%s.%s (%lu)\n\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol, pService->m_ProbeInformation.m_u8SentCount););
} }
else { else {
pService->m_ProbeInformation.m_Timeout.reset(std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max()); pService->m_ProbeInformation.m_Timeout.resetToNeverExpires();
DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done service announcing for %s.%s.%s\n\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done service announcing for %s.%s.%s\n\n"), (pService->m_pcName ?: m_pcHostname), pService->m_pcService, pService->m_pcProtocol););
} }
} }
@ -1441,13 +1441,13 @@ bool MDNSResponder::_checkServiceQueryCache(void) {
// Resend dynamic service queries, if not already done often enough // Resend dynamic service queries, if not already done often enough
if ((!pServiceQuery->m_bLegacyQuery) && if ((!pServiceQuery->m_bLegacyQuery) &&
(MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) && (MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) &&
(pServiceQuery->m_ResendTimeout.checkExpired(millis()))) { (pServiceQuery->m_ResendTimeout.expired())) {
if ((bResult = _sendMDNSServiceQuery(*pServiceQuery))) { if ((bResult = _sendMDNSServiceQuery(*pServiceQuery))) {
++pServiceQuery->m_u8SentCount; ++pServiceQuery->m_u8SentCount;
pServiceQuery->m_ResendTimeout.reset((MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) pServiceQuery->m_ResendTimeout.reset((MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount)
? (MDNS_DYNAMIC_QUERY_RESEND_DELAY * (pServiceQuery->m_u8SentCount - 1)) ? (MDNS_DYNAMIC_QUERY_RESEND_DELAY * (pServiceQuery->m_u8SentCount - 1))
: std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max()); : esp8266::polledTimeout::oneShotMs::neverExpires);
} }
DEBUG_EX_INFO( DEBUG_EX_INFO(
DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: %s to resend service query!"), (bResult ? "Succeeded" : "FAILED")); DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: %s to resend service query!"), (bResult ? "Succeeded" : "FAILED"));

View File

@ -1159,7 +1159,7 @@ bool MDNSResponder::stcMDNS_RRAnswerGeneric::clear(void) {
MDNSResponder::stcProbeInformation::stcProbeInformation(void) MDNSResponder::stcProbeInformation::stcProbeInformation(void)
: m_ProbingStatus(ProbingStatus_WaitingForData), : m_ProbingStatus(ProbingStatus_WaitingForData),
m_u8SentCount(0), m_u8SentCount(0),
m_Timeout(std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max()), m_Timeout(esp8266::polledTimeout::oneShotMs::neverExpires),
m_bConflict(false), m_bConflict(false),
m_bTiebreakNeeded(false), m_bTiebreakNeeded(false),
m_fnHostProbeResultCallback(0), m_fnHostProbeResultCallback(0),
@ -1173,7 +1173,7 @@ bool MDNSResponder::stcProbeInformation::clear(bool p_bClearUserdata /*= false*/
m_ProbingStatus = ProbingStatus_WaitingForData; m_ProbingStatus = ProbingStatus_WaitingForData;
m_u8SentCount = 0; m_u8SentCount = 0;
m_Timeout.reset(std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max()); m_Timeout.resetToNeverExpires();
m_bConflict = false; m_bConflict = false;
m_bTiebreakNeeded = false; m_bTiebreakNeeded = false;
if (p_bClearUserdata) { if (p_bClearUserdata) {
@ -1421,7 +1421,7 @@ bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated(void) con
*/ */
MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(void) MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(void)
: m_u32TTL(0), : m_u32TTL(0),
m_TTLTimeout(std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max()), m_TTLTimeout(esp8266::polledTimeout::oneShotMs::neverExpires),
m_timeoutLevel(TIMEOUTLEVEL_UNSET) { m_timeoutLevel(TIMEOUTLEVEL_UNSET) {
} }
@ -1438,7 +1438,7 @@ bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TT
} }
else { else {
m_timeoutLevel = TIMEOUTLEVEL_UNSET; // undef m_timeoutLevel = TIMEOUTLEVEL_UNSET; // undef
m_TTLTimeout.reset(std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max()); m_TTLTimeout.resetToNeverExpires();
} }
return true; return true;
} }
@ -1446,11 +1446,11 @@ bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TT
/* /*
* MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged
*/ */
bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged(void) const { bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged(void) {
return ((m_u32TTL) && return ((m_u32TTL) &&
(TIMEOUTLEVEL_UNSET != m_timeoutLevel) && (TIMEOUTLEVEL_UNSET != m_timeoutLevel) &&
(m_TTLTimeout.checkExpired(millis()))); (m_TTLTimeout.expired()));
} }
/* /*
@ -1468,7 +1468,7 @@ bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart(void) {
} }
else { else {
bResult = false; bResult = false;
m_TTLTimeout.reset(std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max()); m_TTLTimeout.resetToNeverExpires();
m_timeoutLevel = TIMEOUTLEVEL_UNSET; m_timeoutLevel = TIMEOUTLEVEL_UNSET;
} }
return bResult; return bResult;
@ -1498,7 +1498,7 @@ bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel(vo
*/ */
unsigned long MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout(void) const { unsigned long MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout(void) const {
uint32_t u32Timeout = std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max(); uint32_t u32Timeout = esp8266::polledTimeout::oneShotMs::neverExpires;
if (TIMEOUTLEVEL_BASE == m_timeoutLevel) { // 80% if (TIMEOUTLEVEL_BASE == m_timeoutLevel) { // 80%
u32Timeout = (m_u32TTL * 800); // to milliseconds u32Timeout = (m_u32TTL * 800); // to milliseconds
@ -1922,7 +1922,7 @@ MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery(void)
m_fnCallback(0), m_fnCallback(0),
m_bLegacyQuery(false), m_bLegacyQuery(false),
m_u8SentCount(0), m_u8SentCount(0),
m_ResendTimeout(std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max()), m_ResendTimeout(esp8266::polledTimeout::oneShotMs::neverExpires),
m_bAwaitingAnswers(true), m_bAwaitingAnswers(true),
m_pAnswers(0) { m_pAnswers(0) {
@ -1945,7 +1945,7 @@ bool MDNSResponder::stcMDNSServiceQuery::clear(void) {
m_fnCallback = 0; m_fnCallback = 0;
m_bLegacyQuery = false; m_bLegacyQuery = false;
m_u8SentCount = 0; m_u8SentCount = 0;
m_ResendTimeout.reset(std::numeric_limits<esp8266::polledTimeout::oneShot::timeType>::max()); m_ResendTimeout.resetToNeverExpires();
m_bAwaitingAnswers = true; m_bAwaitingAnswers = true;
while (m_pAnswers) { while (m_pAnswers) {
stcAnswer* pNext = m_pAnswers->m_pNext; stcAnswer* pNext = m_pAnswers->m_pNext;

View File

@ -73,7 +73,7 @@ private:
DEBUGV("SDFS: Clear FAT/DIR writeStart failed"); DEBUGV("SDFS: Clear FAT/DIR writeStart failed");
return false; return false;
} }
esp8266::polledTimeout::periodic timeToYield(5); // Yield every 5ms of runtime esp8266::polledTimeout::periodicFastMs timeToYield(5); // Yield every 5ms of runtime
for (uint32_t i = 0; i < count; i++) { for (uint32_t i = 0; i < count; i++) {
if (timeToYield) { if (timeToYield) {
delay(0); // WDT feed delay(0); // WDT feed

View File

@ -23,7 +23,7 @@ void setup() {
} }
void loop() { void loop() {
using periodic = esp8266::polledTimeout::periodic; using periodic = esp8266::polledTimeout::periodicMs;
static periodic nextPing(1000); static periodic nextPing(1000);
if (nextPing) { if (nextPing) {

View File

@ -24,7 +24,7 @@ void setup() {
byte x = 0; byte x = 0;
void loop() { void loop() {
using periodic = esp8266::polledTimeout::periodic; using periodic = esp8266::polledTimeout::periodicMs;
static periodic nextPing(1000); static periodic nextPing(1000);
if (nextPing) { if (nextPing) {

View File

@ -38,20 +38,34 @@ void ledToggle() {
} }
esp8266::polledTimeout::periodicFastUs halfPeriod(500000); //use fully qualified type and avoid importing all ::esp8266 namespace to the global namespace
esp8266::polledTimeout::periodic halfPeriod(500); //use fully qualified type and avoid importing all ::esp8266 namespace to the global namespace
// the setup function runs only once at start // the setup function runs only once at start
void setup() { void setup() {
Serial.begin(115200);
Serial.println();
Serial.printf("periodic/oneShotMs::timeMax() = %u ms\n", (uint32_t)esp8266::polledTimeout::periodicMs::timeMax());
Serial.printf("periodic/oneShotFastMs::timeMax() = %u ms\n", (uint32_t)esp8266::polledTimeout::periodicFastMs::timeMax());
Serial.printf("periodic/oneShotFastUs::timeMax() = %u us\n", (uint32_t)esp8266::polledTimeout::periodicFastUs::timeMax());
Serial.printf("periodic/oneShotFastNs::timeMax() = %u ns\n", (uint32_t)esp8266::polledTimeout::periodicFastNs::timeMax());
#if 0 // 1 for debugging polledTimeout
Serial.printf("periodic/oneShotMs::rangeCompensate = %u\n", (uint32_t)esp8266::polledTimeout::periodicMs::rangeCompensate);
Serial.printf("periodic/oneShotFastMs::rangeCompensate = %u\n", (uint32_t)esp8266::polledTimeout::periodicFastMs::rangeCompensate);
Serial.printf("periodic/oneShotFastUs::rangeCompensate = %u\n", (uint32_t)esp8266::polledTimeout::periodicFastUs::rangeCompensate);
Serial.printf("periodic/oneShotFastNs::rangeCompensate = %u\n", (uint32_t)esp8266::polledTimeout::periodicFastNs::rangeCompensate);
#endif
pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output
using esp8266::polledTimeout::oneShot; //import the type to the local namespace using esp8266::polledTimeout::oneShotMs; //import the type to the local namespace
//STEP1; turn the led ON //STEP1; turn the led ON
ledOn(); ledOn();
//STEP2: wait for ON timeout //STEP2: wait for ON timeout
oneShot timeoutOn(2000); oneShotMs timeoutOn(2000);
while (!timeoutOn) { while (!timeoutOn) {
yield(); yield();
} }
@ -60,7 +74,7 @@ void setup() {
ledOff(); ledOff();
//STEP4: wait for OFF timeout to assure the led is kept off for this time before exiting setup //STEP4: wait for OFF timeout to assure the led is kept off for this time before exiting setup
oneShot timeoutOff(2000); oneShotMs timeoutOff(2000);
while (!timeoutOff) { while (!timeoutOff) {
yield(); yield();
} }

View File

@ -32,6 +32,8 @@
#include <Esp.h> #include <Esp.h>
#include <eboot_command.h> #include <eboot_command.h>
#include <sys/time.h>
#include <stdlib.h> #include <stdlib.h>
unsigned long long operator"" _kHz(unsigned long long x) { unsigned long long operator"" _kHz(unsigned long long x) {
@ -215,3 +217,9 @@ void EspClass::resetFreeContStack()
{ {
} }
uint32_t EspClass::getCycleCount()
{
timeval t;
gettimeofday(&t, NULL);
return (((uint64_t)t.tv_sec) * 1000000 + t.tv_usec) * (F_CPU / 1000000);
}

View File

@ -29,6 +29,8 @@
DEALINGS WITH THE SOFTWARE. DEALINGS WITH THE SOFTWARE.
*/ */
#define CORE_MOCK 1
// include host's STL before any other include file // include host's STL before any other include file
// because core definition like max() is in the way // because core definition like max() is in the way
@ -138,8 +140,6 @@ void mock_stop_spiffs ();
// //
#define CORE_MOCK 1
#define ARDUINO 267 #define ARDUINO 267
#define ESP8266 1 #define ESP8266 1
#define A0 0 #define A0 0

View File

@ -1,6 +1,9 @@
#include <catch.hpp> #include <catch.hpp>
#include "PolledTimeout.h" #include "PolledTimeout.h"
#define mockverbose printf
#include "common/MockEsp.cpp" // getCycleCount
//This won't work for //This won't work for
template<typename argT> template<typename argT>
inline bool inline bool
@ -10,15 +13,83 @@ fuzzycomp(argT a, argT b)
return (std::max(a,b) - std::min(a,b) <= epsilon); return (std::max(a,b) - std::min(a,b) <= epsilon);
} }
TEST_CASE("OneShot Timeout 500000000ns (0.5s)", "[polledTimeout]")
{
using esp8266::polledTimeout::oneShotFastNs;
using timeType = oneShotFastNs::timeType;
timeType before, after, delta;
Serial.println("OneShot Timeout 500000000ns (0.5s)");
oneShotFastNs timeout(500000000);
before = micros();
while(!timeout.expired())
yield();
after = micros();
delta = after - before;
Serial.printf("delta = %u\n", delta);
REQUIRE(fuzzycomp(delta/1000, (timeType)500));
Serial.print("reset\n");
timeout.reset();
before = micros();
while(!timeout)
yield();
after = micros();
delta = after - before;
Serial.printf("delta = %u\n", delta);
REQUIRE(fuzzycomp(delta/1000, (timeType)500));
}
TEST_CASE("OneShot Timeout 3000000us", "[polledTimeout]")
{
using esp8266::polledTimeout::oneShotFastUs;
using timeType = oneShotFastUs::timeType;
timeType before, after, delta;
Serial.println("OneShot Timeout 3000000us");
oneShotFastUs timeout(3000000);
before = micros();
while(!timeout.expired())
yield();
after = micros();
delta = after - before;
Serial.printf("delta = %u\n", delta);
REQUIRE(fuzzycomp(delta/1000, (timeType)3000));
Serial.print("reset\n");
timeout.reset();
before = micros();
while(!timeout)
yield();
after = micros();
delta = after - before;
Serial.printf("delta = %u\n", delta);
REQUIRE(fuzzycomp(delta/1000, (timeType)3000));
}
TEST_CASE("OneShot Timeout 3000ms", "[polledTimeout]") TEST_CASE("OneShot Timeout 3000ms", "[polledTimeout]")
{ {
using esp8266::polledTimeout::oneShot; using esp8266::polledTimeout::oneShotMs;
using timeType = oneShot::timeType; using timeType = oneShotMs::timeType;
timeType before, after, delta; timeType before, after, delta;
Serial.println("OneShot Timeout 3000ms"); Serial.println("OneShot Timeout 3000ms");
oneShot timeout(3000); oneShotMs timeout(3000);
before = millis(); before = millis();
while(!timeout.expired()) while(!timeout.expired())
yield(); yield();
@ -46,13 +117,13 @@ TEST_CASE("OneShot Timeout 3000ms", "[polledTimeout]")
TEST_CASE("OneShot Timeout 3000ms reset to 1000ms", "[polledTimeout]") TEST_CASE("OneShot Timeout 3000ms reset to 1000ms", "[polledTimeout]")
{ {
using esp8266::polledTimeout::oneShot; using esp8266::polledTimeout::oneShotMs;
using timeType = oneShot::timeType; using timeType = oneShotMs::timeType;
timeType before, after, delta; timeType before, after, delta;
Serial.println("OneShot Timeout 3000ms"); Serial.println("OneShot Timeout 3000ms");
oneShot timeout(3000); oneShotMs timeout(3000);
before = millis(); before = millis();
while(!timeout.expired()) while(!timeout.expired())
yield(); yield();
@ -80,13 +151,13 @@ TEST_CASE("OneShot Timeout 3000ms reset to 1000ms", "[polledTimeout]")
TEST_CASE("Periodic Timeout 1T 3000ms", "[polledTimeout]") TEST_CASE("Periodic Timeout 1T 3000ms", "[polledTimeout]")
{ {
using esp8266::polledTimeout::periodic; using esp8266::polledTimeout::periodicMs;
using timeType = periodic::timeType; using timeType = periodicMs::timeType;
timeType before, after, delta; timeType before, after, delta;
Serial.println("Periodic Timeout 1T 3000ms"); Serial.println("Periodic Timeout 1T 3000ms");
periodic timeout(3000); periodicMs timeout(3000);
before = millis(); before = millis();
while(!timeout) while(!timeout)
yield(); yield();
@ -112,15 +183,15 @@ TEST_CASE("Periodic Timeout 1T 3000ms", "[polledTimeout]")
TEST_CASE("Periodic Timeout 10T 1000ms", "[polledTimeout]") TEST_CASE("Periodic Timeout 10T 1000ms", "[polledTimeout]")
{ {
using esp8266::polledTimeout::periodic; using esp8266::polledTimeout::periodicMs;
using timeType = periodic::timeType; using timeType = periodicMs::timeType;
timeType before, after, delta; timeType before, after, delta;
Serial.println("Periodic 10T Timeout 1000ms"); Serial.println("Periodic 10T Timeout 1000ms");
int counter = 10; int counter = 10;
periodic timeout(1000); periodicMs timeout(1000);
before = millis(); before = millis();
while(1) while(1)
{ {
@ -142,14 +213,14 @@ TEST_CASE("Periodic Timeout 10T 1000ms", "[polledTimeout]")
TEST_CASE("OneShot Timeout 3000ms reset to 1000ms custom yield", "[polledTimeout]") TEST_CASE("OneShot Timeout 3000ms reset to 1000ms custom yield", "[polledTimeout]")
{ {
using YieldOrSkipPolicy = esp8266::polledTimeout::YieldPolicy::YieldOrSkip; using YieldOrSkipPolicy = esp8266::polledTimeout::YieldPolicy::YieldOrSkip;
using oneShotYield = esp8266::polledTimeout::timeoutTemplate<false, YieldOrSkipPolicy>; using oneShotMsYield = esp8266::polledTimeout::timeoutTemplate<false, YieldOrSkipPolicy>;
using timeType = oneShotYield::timeType; using timeType = oneShotMsYield::timeType;
timeType before, after, delta; timeType before, after, delta;
Serial.println("OneShot Timeout 3000ms"); Serial.println("OneShot Timeout 3000ms");
oneShotYield timeout(3000); oneShotMsYield timeout(3000);
before = millis(); before = millis();
while(!timeout.expired()); while(!timeout.expired());
after = millis(); after = millis();