diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 21dff08523..f717c15881 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -200,15 +200,20 @@ class EspClass { bool eraseConfig(); - inline uint32_t getCycleCount(); +#ifndef CORE_MOCK + inline +#endif + uint32_t getCycleCount(); }; +#ifndef CORE_MOCK uint32_t EspClass::getCycleCount() { uint32_t ccount; __asm__ __volatile__("esync; rsr %0,ccount":"=a" (ccount)); return ccount; } +#endif extern EspClass ESP; diff --git a/cores/esp8266/HardwareSerial.cpp b/cores/esp8266/HardwareSerial.cpp index ebb23ce0ca..6ec94500db 100644 --- a/cores/esp8266/HardwareSerial.cpp +++ b/cores/esp8266/HardwareSerial.cpp @@ -139,7 +139,7 @@ size_t HardwareSerial::readBytes(char* buffer, size_t size) while (got < size) { - esp8266::polledTimeout::oneShot timeOut(_timeout); + esp8266::polledTimeout::oneShotFastMs timeOut(_timeout); size_t avail; while ((avail = available()) == 0 && !timeOut); if (avail == 0) diff --git a/cores/esp8266/PolledTimeout.h b/cores/esp8266/PolledTimeout.h index d13bae200d..91bebaa79b 100644 --- a/cores/esp8266/PolledTimeout.h +++ b/cores/esp8266/PolledTimeout.h @@ -23,6 +23,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include namespace esp8266 @@ -45,19 +47,112 @@ struct YieldOrSkip static void execute() {delay(0);} }; +template +struct YieldAndDelayMs +{ + static void execute() {delay(delayMs);} +}; + } //YieldPolicy +namespace TimePolicy +{ + +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 + // "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 + }); -template + #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::max() is reserved + static constexpr timeType timeMax = (std::numeric_limits::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 class timeoutTemplate { public: - using timeType = decltype(millis()); - - timeoutTemplate(timeType timeout) - : _timeout(timeout), _start(millis()) - {} + using timeType = typename TimePolicyT::timeType; + + static constexpr timeType alwaysExpired = 0; + static constexpr timeType neverExpires = std::numeric_limits::max(); + static constexpr timeType rangeCompensate = TimePolicyT::rangeCompensate; //debug + + timeoutTemplate(const timeType userTimeout) + { + reset(userTimeout); + } + ICACHE_RAM_ATTR bool expired() { YieldPolicyT::execute(); //in case of DoNothing: gets optimized away @@ -66,37 +161,69 @@ class timeoutTemplate return expiredOneShot(); } + ICACHE_RAM_ATTR operator bool() { 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(); + _timeout = TimePolicyT::toTimeTypeUnit(newUserTimeout); + _neverExpires = (newUserTimeout < 0) || (newUserTimeout > timeMax()); } void reset() { - _start = millis(); + _start = TimePolicyT::time(); + } + + void resetToNeverExpires () + { + _timeout = alwaysExpired + 1; // because canWait() has precedence + _neverExpires = true; } 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: - + + ICACHE_RAM_ATTR bool expiredRetrigger() { - timeType current = millis(); + if (!canWait()) + return true; + + timeType current = TimePolicyT::time(); if(checkExpired(current)) { unsigned long n = (current - _start) / _timeout; //how many _timeouts periods have elapsed, will usually be 1 (current - _start >= _timeout) @@ -106,23 +233,50 @@ class timeoutTemplate return false; } + ICACHE_RAM_ATTR bool expiredOneShot() const { - return checkExpired(millis()); + // returns "always expired" or "has expired" + return !canWait() || checkExpired(TimePolicyT::time()); } timeType _timeout; timeType _start; + bool _neverExpires; }; -using oneShot = polledTimeout::timeoutTemplate; -using periodic = polledTimeout::timeoutTemplate; +// legacy type names, deprecated (unit is milliseconds) + +using oneShot = polledTimeout::timeoutTemplate /*__attribute__((deprecated("use oneShotMs")))*/; +using periodic = polledTimeout::timeoutTemplate /*__attribute__((deprecated("use periodicMs")))*/; + +// standard versions (based on millis()) +// timeMax() is 49.7 days ((2^32)-2 ms) + +using oneShotMs = polledTimeout::timeoutTemplate; +using periodicMs = polledTimeout::timeoutTemplate; + +// 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; +using periodicFastMs = polledTimeout::timeoutTemplate; +using oneShotFastUs = polledTimeout::timeoutTemplate; +using periodicFastUs = polledTimeout::timeoutTemplate; +using oneShotFastNs = polledTimeout::timeoutTemplate; +using periodicFastNs = polledTimeout::timeoutTemplate; } //polledTimeout /* A 1-shot timeout that auto-yields when in CONT can be built as follows: - * using oneShotYield = esp8266::polledTimeout::timeoutTemplate; + * using oneShotYieldMs = esp8266::polledTimeout::timeoutTemplate; * * 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. */ diff --git a/libraries/ESP8266WiFi/examples/IPv6/IPv6.ino b/libraries/ESP8266WiFi/examples/IPv6/IPv6.ino index 1257fb778f..1c7b1c081c 100644 --- a/libraries/ESP8266WiFi/examples/IPv6/IPv6.ino +++ b/libraries/ESP8266WiFi/examples/IPv6/IPv6.ino @@ -35,7 +35,7 @@ WiFiServer statusServer(TCP_PORT); WiFiUDP udp; -esp8266::polledTimeout::periodic statusPeriod(STATUSDELAY_MS); +esp8266::polledTimeout::periodicMs showStatusOnSerialNow(STATUSDELAY_MS); void fqdn(Print& out, const String& fqdn) { out.print(F("resolving ")); @@ -149,7 +149,7 @@ void setup() { Serial.print(F(" - UDP server on port ")); Serial.println(UDP_PORT); - statusPeriod.reset(); + showStatusOnSerialNow.reset(); } unsigned long statusTimeMs = 0; @@ -182,7 +182,7 @@ void loop() { } - if (statusPeriod) { + if (showStatusOnSerialNow) { status(Serial); } diff --git a/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_Clock/mDNS_Clock.ino b/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_Clock/mDNS_Clock.ino index 0c6f24ca87..8ea7237f54 100644 --- a/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_Clock/mDNS_Clock.ino +++ b/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_Clock/mDNS_Clock.ino @@ -268,7 +268,7 @@ void loop(void) { // Allow MDNS processing MDNS.update(); - static esp8266::polledTimeout::periodic timeout(UPDATE_CYCLE); + static esp8266::polledTimeout::periodicMs timeout(UPDATE_CYCLE); if (timeout.expired()) { if (hMDNSService) { diff --git a/libraries/ESP8266mDNS/src/LEAmDNS.h b/libraries/ESP8266mDNS/src/LEAmDNS.h index d35df1fbd3..03abcdbb88 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS.h @@ -905,12 +905,12 @@ class MDNSResponder { * stcProbeInformation */ struct stcProbeInformation { - enuProbingStatus m_ProbingStatus; - uint8_t m_u8SentCount; // Used for probes and announcements - esp8266::polledTimeout::oneShot m_Timeout; // Used for probes and announcements - //clsMDNSTimeFlag m_TimeFlag; // Used for probes and announcements - bool m_bConflict; - bool m_bTiebreakNeeded; + enuProbingStatus m_ProbingStatus; + uint8_t m_u8SentCount; // Used for probes and announcements + esp8266::polledTimeout::oneShotMs m_Timeout; // Used for probes and announcements + //clsMDNSTimeFlag m_TimeFlag; // Used for probes and announcements + bool m_bConflict; + bool m_bTiebreakNeeded; MDNSHostProbeFn m_fnHostProbeResultCallback; MDNSServiceProbeFn m_fnServiceProbeResultCallback; @@ -974,14 +974,14 @@ class MDNSResponder { const timeoutLevel_t TIMEOUTLEVEL_INTERVAL = 5; const timeoutLevel_t TIMEOUTLEVEL_FINAL = 100; - uint32_t m_u32TTL; - esp8266::polledTimeout::oneShot m_TTLTimeout; - timeoutLevel_t m_timeoutLevel; + uint32_t m_u32TTL; + esp8266::polledTimeout::oneShotMs m_TTLTimeout; + timeoutLevel_t m_timeoutLevel; stcTTL(void); bool set(uint32_t p_u32TTL); - bool flagged(void) const; + bool flagged(void); bool restart(void); bool prepareDeletion(void); @@ -1073,14 +1073,14 @@ class MDNSResponder { #endif }; - stcMDNSServiceQuery* m_pNext; - stcMDNS_RRDomain m_ServiceTypeDomain; // eg. _http._tcp.local - MDNSServiceQueryCallbackFunc m_fnCallback; - bool m_bLegacyQuery; - uint8_t m_u8SentCount; - esp8266::polledTimeout::oneShot m_ResendTimeout; - bool m_bAwaitingAnswers; - stcAnswer* m_pAnswers; + stcMDNSServiceQuery* m_pNext; + stcMDNS_RRDomain m_ServiceTypeDomain; // eg. _http._tcp.local + MDNSServiceQueryCallbackFunc m_fnCallback; + bool m_bLegacyQuery; + uint8_t m_u8SentCount; + esp8266::polledTimeout::oneShotMs m_ResendTimeout; + bool m_bAwaitingAnswers; + stcAnswer* m_pAnswers; stcMDNSServiceQuery(void); ~stcMDNSServiceQuery(void); diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp index 449361d32b..d5bd4dde61 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp @@ -1047,7 +1047,7 @@ bool MDNSResponder::_updateProbeStatus(void) { m_HostProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; } 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 ((bResult = _sendHostProbe())) { @@ -1059,7 +1059,7 @@ bool MDNSResponder::_updateProbeStatus(void) { else { // Probing finished DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host probing.\n"));); m_HostProbeInformation.m_ProbingStatus = ProbingStatus_Done; - m_HostProbeInformation.m_Timeout.reset(std::numeric_limits::max()); + m_HostProbeInformation.m_Timeout.resetToNeverExpires(); if (m_HostProbeInformation.m_fnHostProbeResultCallback) { 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 if ((ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) && - (m_HostProbeInformation.m_Timeout.checkExpired(std::numeric_limits::max()))) { + (m_HostProbeInformation.m_Timeout.expired())) { if ((bResult = _announce(true, false))) { // Don't announce services here ++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);); } else { - m_HostProbeInformation.m_Timeout.reset(std::numeric_limits::max()); + m_HostProbeInformation.m_Timeout.resetToNeverExpires(); 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; } 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 ((bResult = _sendServiceProbe(*pService))) { @@ -1108,7 +1108,7 @@ bool MDNSResponder::_updateProbeStatus(void) { 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);); pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_Done; - pService->m_ProbeInformation.m_Timeout.reset(std::numeric_limits::max()); + pService->m_ProbeInformation.m_Timeout.resetToNeverExpires(); if (pService->m_ProbeInformation.m_fnServiceProbeResultCallback) { 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 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 ++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);); } else { - pService->m_ProbeInformation.m_Timeout.reset(std::numeric_limits::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);); } } @@ -1441,13 +1441,13 @@ bool MDNSResponder::_checkServiceQueryCache(void) { // Resend dynamic service queries, if not already done often enough if ((!pServiceQuery->m_bLegacyQuery) && (MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) && - (pServiceQuery->m_ResendTimeout.checkExpired(millis()))) { + (pServiceQuery->m_ResendTimeout.expired())) { if ((bResult = _sendMDNSServiceQuery(*pServiceQuery))) { ++pServiceQuery->m_u8SentCount; pServiceQuery->m_ResendTimeout.reset((MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) ? (MDNS_DYNAMIC_QUERY_RESEND_DELAY * (pServiceQuery->m_u8SentCount - 1)) - : std::numeric_limits::max()); + : esp8266::polledTimeout::oneShotMs::neverExpires); } DEBUG_EX_INFO( DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: %s to resend service query!"), (bResult ? "Succeeded" : "FAILED")); diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp index 41c38fe6b1..e54beb50db 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp @@ -1159,7 +1159,7 @@ bool MDNSResponder::stcMDNS_RRAnswerGeneric::clear(void) { MDNSResponder::stcProbeInformation::stcProbeInformation(void) : m_ProbingStatus(ProbingStatus_WaitingForData), m_u8SentCount(0), - m_Timeout(std::numeric_limits::max()), + m_Timeout(esp8266::polledTimeout::oneShotMs::neverExpires), m_bConflict(false), m_bTiebreakNeeded(false), m_fnHostProbeResultCallback(0), @@ -1173,7 +1173,7 @@ bool MDNSResponder::stcProbeInformation::clear(bool p_bClearUserdata /*= false*/ m_ProbingStatus = ProbingStatus_WaitingForData; m_u8SentCount = 0; - m_Timeout.reset(std::numeric_limits::max()); + m_Timeout.resetToNeverExpires(); m_bConflict = false; m_bTiebreakNeeded = false; if (p_bClearUserdata) { @@ -1421,7 +1421,7 @@ bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated(void) con */ MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(void) : m_u32TTL(0), - m_TTLTimeout(std::numeric_limits::max()), + m_TTLTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), m_timeoutLevel(TIMEOUTLEVEL_UNSET) { } @@ -1438,7 +1438,7 @@ bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TT } else { m_timeoutLevel = TIMEOUTLEVEL_UNSET; // undef - m_TTLTimeout.reset(std::numeric_limits::max()); + m_TTLTimeout.resetToNeverExpires(); } return true; } @@ -1446,11 +1446,11 @@ bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TT /* * MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged */ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged(void) const { +bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged(void) { return ((m_u32TTL) && (TIMEOUTLEVEL_UNSET != m_timeoutLevel) && - (m_TTLTimeout.checkExpired(millis()))); + (m_TTLTimeout.expired())); } /* @@ -1468,7 +1468,7 @@ bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart(void) { } else { bResult = false; - m_TTLTimeout.reset(std::numeric_limits::max()); + m_TTLTimeout.resetToNeverExpires(); m_timeoutLevel = TIMEOUTLEVEL_UNSET; } return bResult; @@ -1498,7 +1498,7 @@ bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel(vo */ unsigned long MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout(void) const { - uint32_t u32Timeout = std::numeric_limits::max(); + uint32_t u32Timeout = esp8266::polledTimeout::oneShotMs::neverExpires; if (TIMEOUTLEVEL_BASE == m_timeoutLevel) { // 80% u32Timeout = (m_u32TTL * 800); // to milliseconds @@ -1922,7 +1922,7 @@ MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery(void) m_fnCallback(0), m_bLegacyQuery(false), m_u8SentCount(0), - m_ResendTimeout(std::numeric_limits::max()), + m_ResendTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), m_bAwaitingAnswers(true), m_pAnswers(0) { @@ -1945,7 +1945,7 @@ bool MDNSResponder::stcMDNSServiceQuery::clear(void) { m_fnCallback = 0; m_bLegacyQuery = false; m_u8SentCount = 0; - m_ResendTimeout.reset(std::numeric_limits::max()); + m_ResendTimeout.resetToNeverExpires(); m_bAwaitingAnswers = true; while (m_pAnswers) { stcAnswer* pNext = m_pAnswers->m_pNext; diff --git a/libraries/SDFS/src/SDFSFormatter.h b/libraries/SDFS/src/SDFSFormatter.h index a911a76543..d1fe6c94a7 100644 --- a/libraries/SDFS/src/SDFSFormatter.h +++ b/libraries/SDFS/src/SDFSFormatter.h @@ -73,7 +73,7 @@ class SDFSFormatter { DEBUGV("SDFS: Clear FAT/DIR writeStart failed"); 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++) { if (timeToYield) { delay(0); // WDT feed diff --git a/libraries/Wire/examples/master_reader/master_reader.ino b/libraries/Wire/examples/master_reader/master_reader.ino index fb1316c1d3..323830f847 100644 --- a/libraries/Wire/examples/master_reader/master_reader.ino +++ b/libraries/Wire/examples/master_reader/master_reader.ino @@ -23,7 +23,7 @@ void setup() { } void loop() { - using periodic = esp8266::polledTimeout::periodic; + using periodic = esp8266::polledTimeout::periodicMs; static periodic nextPing(1000); if (nextPing) { diff --git a/libraries/Wire/examples/master_writer/master_writer.ino b/libraries/Wire/examples/master_writer/master_writer.ino index d2a6da8420..1e9719e23c 100644 --- a/libraries/Wire/examples/master_writer/master_writer.ino +++ b/libraries/Wire/examples/master_writer/master_writer.ino @@ -24,7 +24,7 @@ void setup() { byte x = 0; void loop() { - using periodic = esp8266::polledTimeout::periodic; + using periodic = esp8266::polledTimeout::periodicMs; static periodic nextPing(1000); if (nextPing) { diff --git a/libraries/esp8266/examples/BlinkPolledTimeout/BlinkPolledTimeout.ino b/libraries/esp8266/examples/BlinkPolledTimeout/BlinkPolledTimeout.ino index 12aca929f2..11e2aff482 100644 --- a/libraries/esp8266/examples/BlinkPolledTimeout/BlinkPolledTimeout.ino +++ b/libraries/esp8266/examples/BlinkPolledTimeout/BlinkPolledTimeout.ino @@ -38,20 +38,34 @@ void ledToggle() { } - -esp8266::polledTimeout::periodic halfPeriod(500); //use fully qualified type and avoid importing all ::esp8266 namespace to the global namespace +esp8266::polledTimeout::periodicFastUs halfPeriod(500000); //use fully qualified type and avoid importing all ::esp8266 namespace to the global namespace // the setup function runs only once at start 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 - 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 ledOn(); //STEP2: wait for ON timeout - oneShot timeoutOn(2000); + oneShotMs timeoutOn(2000); while (!timeoutOn) { yield(); } @@ -60,7 +74,7 @@ void setup() { ledOff(); //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) { yield(); } diff --git a/tests/host/common/MockEsp.cpp b/tests/host/common/MockEsp.cpp index 244debf932..21a124b80d 100644 --- a/tests/host/common/MockEsp.cpp +++ b/tests/host/common/MockEsp.cpp @@ -32,6 +32,8 @@ #include #include +#include + #include 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); +} diff --git a/tests/host/common/mock.h b/tests/host/common/mock.h index 2db914d783..067411139f 100644 --- a/tests/host/common/mock.h +++ b/tests/host/common/mock.h @@ -29,6 +29,8 @@ DEALINGS WITH THE SOFTWARE. */ +#define CORE_MOCK 1 + // include host's STL before any other include file // 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 ESP8266 1 #define A0 0 diff --git a/tests/host/core/test_PolledTimeout.cpp b/tests/host/core/test_PolledTimeout.cpp index 7746072ecb..37b31d8544 100644 --- a/tests/host/core/test_PolledTimeout.cpp +++ b/tests/host/core/test_PolledTimeout.cpp @@ -1,6 +1,9 @@ #include #include "PolledTimeout.h" +#define mockverbose printf +#include "common/MockEsp.cpp" // getCycleCount + //This won't work for template inline bool @@ -10,15 +13,83 @@ fuzzycomp(argT a, argT b) 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]") { - using esp8266::polledTimeout::oneShot; - using timeType = oneShot::timeType; + using esp8266::polledTimeout::oneShotMs; + using timeType = oneShotMs::timeType; timeType before, after, delta; Serial.println("OneShot Timeout 3000ms"); - oneShot timeout(3000); + oneShotMs timeout(3000); before = millis(); while(!timeout.expired()) yield(); @@ -46,13 +117,13 @@ TEST_CASE("OneShot Timeout 3000ms", "[polledTimeout]") TEST_CASE("OneShot Timeout 3000ms reset to 1000ms", "[polledTimeout]") { - using esp8266::polledTimeout::oneShot; - using timeType = oneShot::timeType; + using esp8266::polledTimeout::oneShotMs; + using timeType = oneShotMs::timeType; timeType before, after, delta; Serial.println("OneShot Timeout 3000ms"); - oneShot timeout(3000); + oneShotMs timeout(3000); before = millis(); while(!timeout.expired()) yield(); @@ -80,13 +151,13 @@ TEST_CASE("OneShot Timeout 3000ms reset to 1000ms", "[polledTimeout]") TEST_CASE("Periodic Timeout 1T 3000ms", "[polledTimeout]") { - using esp8266::polledTimeout::periodic; - using timeType = periodic::timeType; + using esp8266::polledTimeout::periodicMs; + using timeType = periodicMs::timeType; timeType before, after, delta; Serial.println("Periodic Timeout 1T 3000ms"); - periodic timeout(3000); + periodicMs timeout(3000); before = millis(); while(!timeout) yield(); @@ -103,7 +174,7 @@ TEST_CASE("Periodic Timeout 1T 3000ms", "[polledTimeout]") while(!timeout) yield(); after = millis(); - + delta = after - before; Serial.printf("delta = %lu\n", delta); @@ -112,15 +183,15 @@ TEST_CASE("Periodic Timeout 1T 3000ms", "[polledTimeout]") TEST_CASE("Periodic Timeout 10T 1000ms", "[polledTimeout]") { - using esp8266::polledTimeout::periodic; - using timeType = periodic::timeType; + using esp8266::polledTimeout::periodicMs; + using timeType = periodicMs::timeType; timeType before, after, delta; Serial.println("Periodic 10T Timeout 1000ms"); int counter = 10; - periodic timeout(1000); + periodicMs timeout(1000); before = millis(); while(1) { @@ -133,7 +204,7 @@ TEST_CASE("Periodic Timeout 10T 1000ms", "[polledTimeout]") } } after = millis(); - + delta = after - before; Serial.printf("\ndelta = %lu\n", delta); REQUIRE(fuzzycomp(delta, (timeType)10000)); @@ -142,18 +213,18 @@ TEST_CASE("Periodic Timeout 10T 1000ms", "[polledTimeout]") TEST_CASE("OneShot Timeout 3000ms reset to 1000ms custom yield", "[polledTimeout]") { using YieldOrSkipPolicy = esp8266::polledTimeout::YieldPolicy::YieldOrSkip; - using oneShotYield = esp8266::polledTimeout::timeoutTemplate; - using timeType = oneShotYield::timeType; + using oneShotMsYield = esp8266::polledTimeout::timeoutTemplate; + using timeType = oneShotMsYield::timeType; timeType before, after, delta; Serial.println("OneShot Timeout 3000ms"); - oneShotYield timeout(3000); + oneShotMsYield timeout(3000); before = millis(); while(!timeout.expired()); after = millis(); - + delta = after - before; Serial.printf("delta = %lu\n", delta); @@ -166,7 +237,7 @@ TEST_CASE("OneShot Timeout 3000ms reset to 1000ms custom yield", "[polledTimeout before = millis(); while(!timeout); after = millis(); - + delta = after - before; Serial.printf("delta = %lu\n", delta);