Redo UF2 discovery for Windows compatibility (#2853)

Windows Python doesn't seem to kill the worker Thread properly when the IDE
is exited, leading to a) multiple Python3 instances on a PC after many uses
and b) errors updating the core when it tries to re-install Python3 while
still having the older version's EXE loaded and in use.

When an exception happens on the input() call under Windows, it seems like it
can still leave a thread running.  Add one more flag, caught in a global
exception handler.

Works around https://github.com/arduino/arduino-cli/issues/2867
This commit is contained in:
Earle F. Philhower, III 2025-03-14 10:08:37 -07:00 committed by GitHub
parent cdf0a65d0f
commit 15d1c6813a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -5,25 +5,35 @@ import sys
import time
import threading
toolspath = os.path.dirname(os.path.realpath(__file__))
try:
sys.path.insert(0, os.path.join(toolspath, ".")) # Add pyserial dir to search path
sys.path.insert(0, os.path.join(toolspath, ".")) # Add uf2conv dir to search path
import uf2conv # If this fails, we can't continue and will bomb below
except ImportError:
sys.stderr.write("uf2conv not found next to this tool.\n")
sys.exit(1)
scannerStop = threading.Event()
dropDead = False
scannerGo = False
class ScannerDarkly(threading.Thread):
def scanner():
global scannerGo
scannerGo = True
boards = False
while scannerGo:
loopTime = 0.0 # Set to 0 for 1st pass to get immediate response for arduino-cli, then bumped to 2.0 for ongoing checks
# https://stackoverflow.com/questions/12435211/threading-timer-repeat-function-every-n-seconds
def __init__(self, event):
threading.Thread.__init__(self)
self.stopped = event
def run(self):
global dropDead
boards = False;
while not self.stopped.wait(self.loopTime):
if self.stopped.is_set() or dropDead:
return
self.loopTime = 2.0
l = uf2conv.get_drives()
if (len(l) > 0) and scannerGo and not boards:
if (len(l) > 0) and not boards:
boards = True
print ("""{
"eventType": "add",
@ -39,7 +49,7 @@ def scanner():
}
}
}""", flush=True)
elif (len(l) == 0) and scannerGo and boards:
elif (len(l) == 0) and boards:
boards = False
print("""{
"eventType": "remove",
@ -48,13 +58,12 @@ def scanner():
"protocol": "uf2conv"
}
}""", flush = True)
n = time.time() + 2
while scannerGo and (time.time() < n):
time.sleep(.1)
scannerGo = True
def main():
global scannerGo
global scannerStop
global dropDead
try:
while True:
cmdline = input()
cmd = cmdline.split()[0]
@ -70,15 +79,13 @@ def main():
"message": "OK"
}""", flush = True);
elif cmd == "STOP":
scannerGo = False
while not scannerGo:
time.sleep(.1)
scannerStop.set()
print("""{
"eventType": "stop",
"message": "OK"
}""", flush = True)
elif cmd == "QUIT":
scannerGo = False
scannerStop.set()
print("""{
"eventType": "quit",
"message": "OK"
@ -113,8 +120,9 @@ def main():
"eventType": "start_sync",
"message": "OK"
}""", flush = True)
scannerGo = True
threading.Thread(target = scanner).start()
time.sleep(.5)
thread = ScannerDarkly(scannerStop)
thread.start()
except:
dropDead = True
main()