Skip to content

Commit 15d1c68

Browse files
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 arduino/arduino-cli#2867
1 parent cdf0a65 commit 15d1c68

File tree

1 file changed

+74
-66
lines changed

1 file changed

+74
-66
lines changed

tools/pluggable_discovery.py

+74-66
Original file line numberDiff line numberDiff line change
@@ -5,89 +5,96 @@
55
import time
66
import threading
77

8-
98
toolspath = os.path.dirname(os.path.realpath(__file__))
109
try:
11-
sys.path.insert(0, os.path.join(toolspath, ".")) # Add pyserial dir to search path
10+
sys.path.insert(0, os.path.join(toolspath, ".")) # Add uf2conv dir to search path
1211
import uf2conv # If this fails, we can't continue and will bomb below
1312
except ImportError:
1413
sys.stderr.write("uf2conv not found next to this tool.\n")
1514
sys.exit(1)
1615

16+
scannerStop = threading.Event()
17+
dropDead = False
1718

18-
scannerGo = False
19+
class ScannerDarkly(threading.Thread):
20+
21+
loopTime = 0.0 # Set to 0 for 1st pass to get immediate response for arduino-cli, then bumped to 2.0 for ongoing checks
22+
23+
# https://stackoverflow.com/questions/12435211/threading-timer-repeat-function-every-n-seconds
24+
def __init__(self, event):
25+
threading.Thread.__init__(self)
26+
self.stopped = event
27+
28+
def run(self):
29+
global dropDead
30+
boards = False;
31+
while not self.stopped.wait(self.loopTime):
32+
if self.stopped.is_set() or dropDead:
33+
return
34+
self.loopTime = 2.0
35+
l = uf2conv.get_drives()
36+
if (len(l) > 0) and not boards:
37+
boards = True
38+
print ("""{
39+
"eventType": "add",
40+
"port": {
41+
"address": "UF2_Board",
42+
"label": "UF2 Board",
43+
"protocol": "uf2conv",
44+
"protocolLabel": "UF2 Devices",
45+
"properties": {
46+
"mac": "ffffffffffff",
47+
"pid" : "0x2e8a",
48+
"vid" : "0x000a"
49+
}
50+
}
51+
}""", flush=True)
52+
elif (len(l) == 0) and boards:
53+
boards = False
54+
print("""{
55+
"eventType": "remove",
56+
"port": {
57+
"address": "UF2_Board",
58+
"protocol": "uf2conv"
59+
}
60+
}""", flush = True)
1961

20-
def scanner():
21-
global scannerGo
22-
scannerGo = True
23-
boards = False
24-
while scannerGo:
25-
l = uf2conv.get_drives()
26-
if (len(l) > 0) and scannerGo and not boards:
27-
boards = True
28-
print ("""{
29-
"eventType": "add",
30-
"port": {
31-
"address": "UF2_Board",
32-
"label": "UF2 Board",
33-
"protocol": "uf2conv",
34-
"protocolLabel": "UF2 Devices",
35-
"properties": {
36-
"mac": "ffffffffffff",
37-
"pid" : "0x2e8a",
38-
"vid" : "0x000a"
39-
}
40-
}
41-
}""", flush=True)
42-
elif (len(l) == 0) and scannerGo and boards:
43-
boards = False
44-
print("""{
45-
"eventType": "remove",
46-
"port": {
47-
"address": "UF2_Board",
48-
"protocol": "uf2conv"
49-
}
50-
}""", flush = True)
51-
n = time.time() + 2
52-
while scannerGo and (time.time() < n):
53-
time.sleep(.1)
54-
scannerGo = True
5562

5663
def main():
57-
global scannerGo
58-
while True:
59-
cmdline = input()
60-
cmd = cmdline.split()[0]
61-
if cmd == "HELLO":
62-
print(""" {
64+
global scannerStop
65+
global dropDead
66+
try:
67+
while True:
68+
cmdline = input()
69+
cmd = cmdline.split()[0]
70+
if cmd == "HELLO":
71+
print(""" {
6372
"eventType": "hello",
6473
"message": "OK",
6574
"protocolVersion": 1
6675
}""", flush = True)
67-
elif cmd == "START":
68-
print("""{
76+
elif cmd == "START":
77+
print("""{
6978
"eventType": "start",
7079
"message": "OK"
7180
}""", flush = True);
72-
elif cmd == "STOP":
73-
scannerGo = False
74-
while not scannerGo:
75-
time.sleep(.1)
76-
print("""{
81+
elif cmd == "STOP":
82+
scannerStop.set()
83+
print("""{
7784
"eventType": "stop",
7885
"message": "OK"
7986
}""", flush = True)
80-
elif cmd == "QUIT":
81-
scannerGo = False
82-
print("""{
87+
elif cmd == "QUIT":
88+
scannerStop.set()
89+
print("""{
8390
"eventType": "quit",
8491
"message": "OK"
8592
}""", flush = True)
86-
return
87-
elif cmd == "LIST":
88-
l = uf2conv.get_drives()
89-
if len(l) > 0:
90-
print ("""{
93+
return
94+
elif cmd == "LIST":
95+
l = uf2conv.get_drives()
96+
if len(l) > 0:
97+
print ("""{
9198
"eventType": "list",
9299
"ports": [
93100
{
@@ -103,18 +110,19 @@ def main():
103110
}
104111
]
105112
}""", flush=True)
106-
else:
107-
print ("""{
113+
else:
114+
print ("""{
108115
"eventType": "list",
109116
"ports": [ ]
110117
}""", flush=True)
111-
elif cmd == "START_SYNC":
112-
print("""{
118+
elif cmd == "START_SYNC":
119+
print("""{
113120
"eventType": "start_sync",
114121
"message": "OK"
115122
}""", flush = True)
116-
scannerGo = True
117-
threading.Thread(target = scanner).start()
118-
time.sleep(.5)
123+
thread = ScannerDarkly(scannerStop)
124+
thread.start()
125+
except:
126+
dropDead = True
119127

120128
main()

0 commit comments

Comments
 (0)