Skip to content

Commit 4cf0747

Browse files
committed
echonetMessage: Make sure waiting[host] is decremented in all situations (including any inner exception) to avoid deadlocks
1 parent 15fe487 commit 4cf0747

File tree

1 file changed

+60
-61
lines changed

1 file changed

+60
-61
lines changed

pychonet/echonetapiclient.py

Lines changed: 60 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -282,68 +282,67 @@ async def echonetMessage(self, host, deojgc, deojcc, deojci, esv, opc):
282282
tx_tid = self._next_tx_tid
283283
message_array["TID"] = tx_tid
284284
try:
285-
payload = buildEchonetMsg(message_array)
286-
except TIDError: # Quashing the rollover bug hopefully once and for all...
287-
self._next_tx_tid = 1
288-
tx_tid = self._next_tx_tid
289-
message_array["TID"] = tx_tid
290-
payload = buildEchonetMsg(message_array)
291-
292-
opc_count = len(opc)
293-
if no_res:
294-
is_success = True
295-
else:
296-
is_success = False
297-
tid_data = {}
298-
for opc_data in opc:
299-
if opc_data.get("EDT") is not None:
300-
if isinstance(opc_data["EDT"], int):
301-
tid_data[opc_data["EPC"]] = opc_data["EDT"].to_bytes(
302-
opc_data["PDC"], "big"
303-
)
304-
self._message_list[tx_tid] = tid_data
305-
306-
self._server.send(payload, (host, ENL_PORT))
307-
308-
if not no_res:
309-
not_timeout = False
310-
for x in range(0, self._message_timeout):
311-
# Wait up to 20(0.1*200) seconds depending on the Echonet specifications.
312-
await asyncio.sleep(0.1)
313-
# if tx_tid is not in message list then the message listener has received the message
314-
if self._message_list.get(tx_tid) is None:
315-
# Check OPC count in results
316-
if not is_discover and tx_tid in self._opc_counts:
317-
res_opc_count = self._opc_counts[tx_tid]
318-
del self._opc_counts[tx_tid]
319-
if self._debug_flag:
320-
self._logger(
321-
f"OPC count in results is {res_opc_count}/{opc_count} from IP {host}."
285+
try:
286+
payload = buildEchonetMsg(message_array)
287+
except TIDError: # Quashing the rollover bug hopefully once and for all...
288+
self._next_tx_tid = 1
289+
tx_tid = self._next_tx_tid
290+
message_array["TID"] = tx_tid
291+
payload = buildEchonetMsg(message_array)
292+
293+
opc_count = len(opc)
294+
if no_res:
295+
is_success = True
296+
else:
297+
is_success = False
298+
tid_data = {}
299+
for opc_data in opc:
300+
if opc_data.get("EDT") is not None:
301+
if isinstance(opc_data["EDT"], int):
302+
tid_data[opc_data["EPC"]] = opc_data["EDT"].to_bytes(
303+
opc_data["PDC"], "big"
322304
)
323-
if res_opc_count < opc_count:
324-
self._waiting[host] -= 1
325-
raise EchonetMaxOpcError(res_opc_count)
326-
327-
# transaction sucessful remove from list
328-
if self._failure_list.get(tx_tid, opc_count) < opc_count:
329-
is_success = True
330-
if tx_tid in self._failure_list:
331-
del self._failure_list[tx_tid]
332-
not_timeout = True
333-
break
334-
self._waiting[host] -= 1
335-
if not is_success:
336-
if self._message_list.get(tx_tid) is not None:
337-
del self._message_list[tx_tid]
338-
if not is_discover and self._state[host]["available"] != not_timeout:
339-
self._state[host]["available"] = not_timeout
340-
# Call update callback functions
341-
# key is f"{host}-{deojgc}-{deojcc}-{deojci}"
342-
for key in self._update_callbacks:
343-
if key.startswith(host):
344-
for update_func in self._update_callbacks[key]:
345-
await update_func(False)
346-
else:
305+
self._message_list[tx_tid] = tid_data
306+
307+
self._server.send(payload, (host, ENL_PORT))
308+
309+
if not no_res:
310+
not_timeout = False
311+
for x in range(0, self._message_timeout):
312+
# Wait up to 20(0.1*200) seconds depending on the Echonet specifications.
313+
await asyncio.sleep(0.1)
314+
# if tx_tid is not in message list then the message listener has received the message
315+
if self._message_list.get(tx_tid) is None:
316+
# Check OPC count in results
317+
if not is_discover and tx_tid in self._opc_counts:
318+
res_opc_count = self._opc_counts[tx_tid]
319+
del self._opc_counts[tx_tid]
320+
if self._debug_flag:
321+
self._logger(
322+
f"OPC count in results is {res_opc_count}/{opc_count} from IP {host}."
323+
)
324+
if res_opc_count < opc_count:
325+
raise EchonetMaxOpcError(res_opc_count)
326+
327+
# transaction sucessful remove from list
328+
if self._failure_list.get(tx_tid, opc_count) < opc_count:
329+
is_success = True
330+
if tx_tid in self._failure_list:
331+
del self._failure_list[tx_tid]
332+
not_timeout = True
333+
break
334+
if not is_success:
335+
if self._message_list.get(tx_tid) is not None:
336+
del self._message_list[tx_tid]
337+
if not is_discover and self._state[host]["available"] != not_timeout:
338+
self._state[host]["available"] = not_timeout
339+
# Call update callback functions
340+
# key is f"{host}-{deojgc}-{deojcc}-{deojci}"
341+
for key in self._update_callbacks:
342+
if key.startswith(host):
343+
for update_func in self._update_callbacks[key]:
344+
await update_func(False)
345+
finally:
347346
self._waiting[host] -= 1
348347
return is_success
349348

0 commit comments

Comments
 (0)