Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 47 commits into from
Apr 5, 2019
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
fca1d5d
polledTimeout: add option to use CPU count instead of millis()
d-a-v Mar 13, 2019
6cd506b
Merge branch 'master' into polledcpu
d-a-v Mar 13, 2019
702cea8
use more "using" alias
d-a-v Mar 13, 2019
2afa570
Merge branch 'master' into polledcpu
d-a-v Mar 14, 2019
133c06f
more c++/clear code, using typename (thanks @devyte)
d-a-v Mar 14, 2019
d675eeb
rename class name to include unit, introduce timeMax() and check it w…
d-a-v Mar 15, 2019
f3f7a2d
remove useless defines
d-a-v Mar 15, 2019
a21514a
improve api readability, add micro-second unit
d-a-v Mar 15, 2019
9b8e40b
update example
d-a-v Mar 15, 2019
ba105b7
mock: emulate getCycleCount, add/fix polledTimeout CI test
d-a-v Mar 18, 2019
02909f8
Merge branch 'master' into polledcpu
d-a-v Mar 18, 2019
d1785c6
Merge branch 'master' into polledcpu
d-a-v Mar 18, 2019
ebda468
+ nano-seconds, assert -> message, comments, host test
d-a-v Mar 19, 2019
7a4c4de
allow 0 for timeout (enables immediate timeout, fix division by 0)
d-a-v Mar 19, 2019
2249315
Merge branch 'master' into polledcpu
d-a-v Mar 19, 2019
aa6b2d9
typo, set member instead of local variable
d-a-v Mar 19, 2019
e98c41c
unify error message
d-a-v Mar 19, 2019
714beb3
slight change on checkExpired() allows "never expired"
d-a-v Mar 19, 2019
925408e
remove traces of debug.h/cpp in this PR
d-a-v Mar 19, 2019
412a241
include missing <limits> header
d-a-v Mar 20, 2019
273306a
back to original expired test, introduce boolean _neverExpires, fix r…
d-a-v Mar 20, 2019
531e08e
fix expiredOneShot with _timeout==0 check
d-a-v Mar 20, 2019
a041a66
reenable getTimeout()
d-a-v Mar 20, 2019
70ac794
expose checkExpired with unit conversion
d-a-v Mar 20, 2019
e25b880
fix timing comments, move critical code to iram
d-a-v Mar 20, 2019
a20f8f6
add member ::neverExpires and use it where relevant
d-a-v Mar 20, 2019
8d4c385
improve clarity
d-a-v Mar 20, 2019
ce7e9b9
remove exposed checkExpired(), adapt LEAmDNS with equivalent
d-a-v Mar 20, 2019
0ca22ce
add API ::resetToNeverExpires(), use it in LEAmDNS
d-a-v Mar 20, 2019
3d4b369
Merge branch 'master' into polledcpu
d-a-v Mar 20, 2019
ff660ae
remove offending constness from ::flagged() LEAmDNS (due do API fix i…
d-a-v Mar 21, 2019
393d40f
simplify "Fast" base classes
d-a-v Mar 21, 2019
1f4f73c
Merge branch 'master' into polledcpu
d-a-v Mar 25, 2019
daa5e85
Merge branch 'polledcpu' of github.com:d-a-v/Arduino into polledcpu
d-a-v Mar 25, 2019
96c7fb7
minor variable rename
d-a-v Mar 25, 2019
45c5da2
Merge branch 'master' into polledcpu
d-a-v Mar 28, 2019
4930ed0
Fix examples
d-a-v Mar 28, 2019
f11c6e6
compliance with good c++ manners
d-a-v Apr 2, 2019
9d73d18
Merge branch 'master' into polledcpu
d-a-v Apr 2, 2019
f971d42
minor changes for consistency
d-a-v Apr 2, 2019
b028ea8
add missing const
d-a-v Apr 2, 2019
a02f889
expired() and bool() moved to iram
d-a-v Apr 2, 2019
40d31a5
constexpr compensation computing
d-a-v Apr 2, 2019
75f36a7
add/update comments
d-a-v Apr 3, 2019
0e72db2
Merge branch 'master' into polledcpu
d-a-v Apr 4, 2019
4470ca2
Merge branch 'polledcpu' of github.com:d-a-v/Arduino into polledcpu
d-a-v Apr 4, 2019
0969fee
move neverExpires and alwaysExpired
d-a-v Apr 4, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion cores/esp8266/Esp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion cores/esp8266/HardwareSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
161 changes: 142 additions & 19 deletions cores/esp8266/PolledTimeout.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include <limits>

#include <Arduino.h>

namespace esp8266
Expand All @@ -45,18 +47,86 @@ struct YieldOrSkip
static void execute() {delay(0);}
};

template <unsigned long delayMs>
struct YieldAndDelayMs
{
static void execute() {delay(delayMs);}
};

} //YieldPolicy

namespace TimePolicy
{

struct TimeMillis
{
// time policy in milli-seconds based on millis()

using timeType = decltype(millis());
static timeType time() {return millis();}

// millis to millis
static timeType toTimeTypeUnit (const timeType ms) { return ms; }
static timeType toUserUnit (const timeType ms) { return ms; }

// rollover on 32 bits: 49.7 days (::max() is a "never expires"-reserved value)
static constexpr timeType timeMax() { return std::numeric_limits<timeType>::max() - 1; }
};

struct TimeFastCyclesBase
{
// time policy based on ESP.getCycleCount()

using timeType = decltype(ESP.getCycleCount());
static timeType time() {return ESP.getCycleCount();}

// this particular time measurement is intended to be called very often
// (every loop, every yield)
};

template <const TimeFastCyclesBase::timeType second_th> struct TimeFastCycles: TimeFastCyclesBase
{
static constexpr timeType toTimeTypeUnitMulMax = 160000000 / second_th;
static constexpr timeType toTimeTypeUnitMul = F_CPU / second_th;

static timeType toTimeTypeUnit (const timeType userUnit) { return userUnit * toTimeTypeUnitMul; }
static timeType toUserUnit (const timeType cycles) { return cycles / toTimeTypeUnitMul; }
static constexpr timeType timeMax() { return std::numeric_limits<timeType>::max() / toTimeTypeUnitMulMax; }
};

struct TimeFastMillis: TimeFastCycles<1000>
{
};

struct TimeFastMicros: TimeFastCycles<1000000>
{
};

struct TimeFastNanos: TimeFastCyclesBase
{
static constexpr timeType toTimeTypeUnitMulMax = 160000000 / 40000000; // 4
static constexpr timeType toTimeTypeUnitMul = F_CPU / 40000000; // 2 or 4 (smallest possible)
static constexpr timeType toTimeTypeUnitDiv = 25; // (mul/div=F_CPU/10^9)

static timeType toTimeTypeUnit (const timeType nanos) { return (nanos * toTimeTypeUnitMul) / toTimeTypeUnitDiv; }
static timeType toUserUnit (const timeType cycles) { return (cycles * toTimeTypeUnitDiv) / toTimeTypeUnitMul; }
static constexpr timeType timeMax() { return std::numeric_limits<timeType>::max() / toTimeTypeUnitMulMax; }
};

} //TimePolicy

template <bool PeriodicT, typename YieldPolicyT = YieldPolicy::DoNothing>
template <bool PeriodicT, typename YieldPolicyT = YieldPolicy::DoNothing, typename TimePolicyT = TimePolicy::TimeMillis>
class timeoutTemplate
{
public:
using timeType = decltype(millis());

timeoutTemplate(timeType timeout)
: _timeout(timeout), _start(millis())
{}
using timeType = typename TimePolicyT::timeType;

static constexpr timeType neverExpires = std::numeric_limits<timeType>::max();

timeoutTemplate(const timeType userTimeout)
{
reset(userTimeout);
}

bool expired()
{
Expand All @@ -71,32 +141,60 @@ class timeoutTemplate
return expired();
}

void reset(const timeType newTimeout)
bool canExpire ()
{
return !_neverExpires;
}

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 = 1; // because _timeout==0 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
{
// (_timeout == 0) 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 (_timeout == 0)
// "always expired"
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)
Expand All @@ -106,23 +204,48 @@ class timeoutTemplate
return false;
}

ICACHE_RAM_ATTR
bool expiredOneShot() const
{
return checkExpired(millis());
// returns "always expired" or "has expired"
return (_timeout == 0) || checkExpired(TimePolicyT::time());
}

timeType _timeout;
timeType _start;
bool _neverExpires;
};

using oneShot = polledTimeout::timeoutTemplate<false>;
using periodic = polledTimeout::timeoutTemplate<true>;
// legacy type names, deprecated (unit is milliseconds)

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>;

// "Fast" versions sacrifices time range for improved precision and reduced execution time (by 86%)
// (cpu cycles for ::expired(): 372 (millis()) vs 52 (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)

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


/* 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.
*/
Expand Down
6 changes: 3 additions & 3 deletions libraries/ESP8266WiFi/examples/IPv6/IPv6.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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 "));
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -182,7 +182,7 @@ void loop() {
}


if (statusPeriod) {
if (showStatusOnSerialNow) {
status(Serial);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
36 changes: 18 additions & 18 deletions libraries/ESP8266mDNS/src/LEAmDNS.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Loading