From e8c5aff84d4e7a526d06fa2d8b11321370baab6e Mon Sep 17 00:00:00 2001
From: Kevin Hester <kevinh@geeksville.com>
Date: Sat, 10 Oct 2020 15:32:31 +0800
Subject: [PATCH 1/5] add runOrDelay() for power efficient sleeping

---
 Thread.cpp           | 15 +++++++++++----
 Thread.h             |  7 ++++++-
 ThreadController.cpp | 37 +++++++++++++++++++++++++++++++++++++
 ThreadController.h   |  6 ++++++
 4 files changed, 60 insertions(+), 5 deletions(-)

diff --git a/Thread.cpp b/Thread.cpp
index cd29d98..25b276b 100644
--- a/Thread.cpp
+++ b/Thread.cpp
@@ -31,12 +31,19 @@ void Thread::setInterval(unsigned long _interval){
 	_cached_next_run = last_run + interval;
 }
 
-bool Thread::shouldRun(unsigned long time){
-	// If the "sign" bit is set the signed difference would be negative
-	bool time_remaining = (time - _cached_next_run) & 0x80000000;
+long Thread::tillRun(unsigned long time){
+	if(!enabled)
+		return LONG_MAX;
+	else {
+		long time_remaining = (long) (_cached_next_run - time);
+
+		return time_remaining;
+	}
+}
 
+bool Thread::shouldRun(unsigned long time){
 	// Exceeded the time limit, AND is enabled? Then should run...
-	return !time_remaining && enabled;
+	return tillRun(time) <= 0;
 }
 
 void Thread::onRun(void (*callback)(void)){
diff --git a/Thread.h b/Thread.h
index 0e580a9..fec4242 100644
--- a/Thread.h
+++ b/Thread.h
@@ -74,11 +74,16 @@ class Thread{
 	virtual void setInterval(unsigned long _interval);
 
 	// Return if the Thread should be runned or not
-	virtual bool shouldRun(unsigned long time);
+	// Note: no longer virtual - instead override tillRun
+	bool shouldRun(unsigned long time);
 
 	// Default is to check whether it should run "now"
 	bool shouldRun() { return shouldRun(millis()); }
 
+    // Return # of msecs till this thread will run again (or MAXINT if it is disabled).  
+	// If it is overdue the value will be negative
+	virtual long tillRun(unsigned long time);
+
 	// Callback set
 	void onRun(void (*callback)(void));
 
diff --git a/ThreadController.cpp b/ThreadController.cpp
index 7d8e41c..58c7c08 100644
--- a/ThreadController.cpp
+++ b/ThreadController.cpp
@@ -38,6 +38,43 @@ void ThreadController::run(){
 	runned();
 }
 
+// Try to run our threads, return how long we can sleep before next needed
+long ThreadController::runOrDelay(){
+	// Run this thread before
+	if(_onRun != NULL)
+		_onRun();
+
+	unsigned long time = millis();
+	int checks = 0;
+	long tillNext = LONG_MAX;
+	nextThread = NULL;
+	for(int i = 0; i < MAX_THREADS && checks < cached_size; i++){
+		// Object exists? Is enabled? Timeout exceeded?
+		Thread *t = thread[i];
+		if(t){
+			checks++;
+
+			long threadNext = t->tillRun(time);
+			if(threadNext <= 0){ // This thread is ready to run right now
+				t->run();
+				// threadNext = t->tillRun(time); // Check when the current thread's new deadline
+		
+				tillNext = 0; // we ran something this tick, therefore tell caller it should skip the delay this time
+				nextThread = t;
+			}
+			else if(threadNext < tillNext) {
+				tillNext = threadNext;
+				nextThread = t;
+			}
+		}
+	}
+
+	// ThreadController extends Thread, so we should flag as runned thread
+	runned();
+
+	return tillNext;
+}
+
 
 /*
 	List controller (boring part)
diff --git a/ThreadController.h b/ThreadController.h
index 8e2888c..84022f5 100644
--- a/ThreadController.h
+++ b/ThreadController.h
@@ -31,6 +31,9 @@ class ThreadController: public Thread{
 	// run() Method is overrided
 	void run();
 
+	// Try to run our threads, return how long we can sleep before next needed.  
+	long runOrDelay();
+
 	// Adds a thread in the first available slot (remove first)
 	// Returns if the Thread could be added or not
 	bool add(Thread* _thread);
@@ -48,6 +51,9 @@ class ThreadController: public Thread{
 	// Return the I Thread on the array
 	// Returns NULL if none found
 	Thread* get(int index);
+
+	// For debugging it is useful to know the next child thread we want to execute
+	Thread* nextThread = NULL;
 };
 
 #endif

From 992fafcfd476494203146e87c489642e5745102f Mon Sep 17 00:00:00 2001
From: Kevin Hester <kevinh@geeksville.com>
Date: Sun, 11 Oct 2020 08:13:56 +0800
Subject: [PATCH 2/5] add canSleep for threads that shouldn't wake the
 scheduler

---
 Thread.h             | 3 +++
 ThreadController.cpp | 8 +++++---
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/Thread.h b/Thread.h
index fec4242..7571010 100644
--- a/Thread.h
+++ b/Thread.h
@@ -60,6 +60,9 @@ class Thread{
 	// If the current Thread is enabled or not
 	bool enabled;
 
+	// If true this thread wanting to run will not be used to prevent sleeping in runWithDelay
+	bool canSleep = false;
+
 	// ID of the Thread (initialized from memory adr.)
 	int ThreadID;
 
diff --git a/ThreadController.cpp b/ThreadController.cpp
index 58c7c08..4875234 100644
--- a/ThreadController.cpp
+++ b/ThreadController.cpp
@@ -59,10 +59,12 @@ long ThreadController::runOrDelay(){
 				t->run();
 				// threadNext = t->tillRun(time); // Check when the current thread's new deadline
 		
-				tillNext = 0; // we ran something this tick, therefore tell caller it should skip the delay this time
-				nextThread = t;
+				if(!t->canSleep) {
+					tillNext = 0; // we ran something this tick, therefore tell caller it should skip the delay this time
+					nextThread = t;
+				}
 			}
-			else if(threadNext < tillNext) {
+			else if(threadNext < tillNext && !t->canSleep) {
 				tillNext = threadNext;
 				nextThread = t;
 			}

From 6dd2272a5a6f4aeab09a23066d774382864b41f6 Mon Sep 17 00:00:00 2001
From: Kevin Hester <kevinh@geeksville.com>
Date: Sun, 11 Oct 2020 08:51:42 +0800
Subject: [PATCH 3/5] Use ANSI name for LONG_MAX - fix build for nrf52

---
 Thread.cpp           | 2 +-
 ThreadController.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Thread.cpp b/Thread.cpp
index 25b276b..ac0e6bd 100644
--- a/Thread.cpp
+++ b/Thread.cpp
@@ -33,7 +33,7 @@ void Thread::setInterval(unsigned long _interval){
 
 long Thread::tillRun(unsigned long time){
 	if(!enabled)
-		return LONG_MAX;
+		return __LONG_MAX__;
 	else {
 		long time_remaining = (long) (_cached_next_run - time);
 
diff --git a/ThreadController.cpp b/ThreadController.cpp
index 4875234..82a5363 100644
--- a/ThreadController.cpp
+++ b/ThreadController.cpp
@@ -46,7 +46,7 @@ long ThreadController::runOrDelay(){
 
 	unsigned long time = millis();
 	int checks = 0;
-	long tillNext = LONG_MAX;
+	long tillNext = __LONG_MAX__;
 	nextThread = NULL;
 	for(int i = 0; i < MAX_THREADS && checks < cached_size; i++){
 		// Object exists? Is enabled? Timeout exceeded?

From 333ffd09b596977c217ba25da4258f588b462ac6 Mon Sep 17 00:00:00 2001
From: Kevin Hester <kevinh@geeksville.com>
Date: Sun, 11 Oct 2020 09:13:56 +0800
Subject: [PATCH 4/5] fix thread id to also work on 64 bit OSes

---
 Thread.cpp           | 2 +-
 Thread.h             | 2 +-
 ThreadController.cpp | 2 +-
 ThreadController.h   | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/Thread.cpp b/Thread.cpp
index ac0e6bd..b5c3dc3 100644
--- a/Thread.cpp
+++ b/Thread.cpp
@@ -6,7 +6,7 @@ Thread::Thread(void (*callback)(void), unsigned long _interval){
 	_cached_next_run = 0;
 	last_run = millis();
 
-	ThreadID = (int)this;
+	ThreadID = (size_t)this;
 	#ifdef USE_THREAD_NAMES
 		ThreadName = "Thread ";
 		ThreadName = ThreadName + ThreadID;
diff --git a/Thread.h b/Thread.h
index 7571010..9fde34a 100644
--- a/Thread.h
+++ b/Thread.h
@@ -64,7 +64,7 @@ class Thread{
 	bool canSleep = false;
 
 	// ID of the Thread (initialized from memory adr.)
-	int ThreadID;
+	size_t ThreadID;
 
 	#ifdef USE_THREAD_NAMES
 		// Thread Name (used for better UI).
diff --git a/ThreadController.cpp b/ThreadController.cpp
index 82a5363..68ea202 100644
--- a/ThreadController.cpp
+++ b/ThreadController.cpp
@@ -102,7 +102,7 @@ bool ThreadController::add(Thread* _thread){
 	return false;
 }
 
-void ThreadController::remove(int id){
+void ThreadController::remove(size_t id){
 	// Find Threads with the id, and removes
 	for(int i = 0; i < MAX_THREADS; i++){
 		if(thread[i]->ThreadID == id){
diff --git a/ThreadController.h b/ThreadController.h
index 84022f5..cb8bef9 100644
--- a/ThreadController.h
+++ b/ThreadController.h
@@ -39,7 +39,7 @@ class ThreadController: public Thread{
 	bool add(Thread* _thread);
 
 	// remove the thread (given the Thread* or ThreadID)
-	void remove(int _id);
+	void remove(size_t _id);
 	void remove(Thread* _thread);
 
 	// Removes all threads

From 72921ac222eed6f526ba1682023cee290d9aa1b3 Mon Sep 17 00:00:00 2001
From: Kevin Hester <kevinh@geeksville.com>
Date: Sun, 14 Feb 2021 15:03:50 +0800
Subject: [PATCH 5/5] Increase max threads to 32

---
 ThreadController.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ThreadController.h b/ThreadController.h
index cb8bef9..2ed46a3 100644
--- a/ThreadController.h
+++ b/ThreadController.h
@@ -19,7 +19,7 @@
 #include "Thread.h"
 #include "inttypes.h"
 
-#define MAX_THREADS		15
+#define MAX_THREADS		32
 
 class ThreadController: public Thread{
 protected: