Memory leak: sometimes the return ws message arrives before the callback is registered, leaving it registered forever.
Eel version 1.40
Describe the bug
When a call is made from Python to JS, the return in the python side is a function that must be called to be obtain the result. If a callback is provided, that callback is placed in the _call_return_callbacks dictionary. Then, then the return websocket message returns from the JS side, that callback is found on this dict and it is removed and invoked. All good.
The problem is that between the call msg being sent, and the callback be registered, the websocket return message may arrive. This happens from time to time since its all running locally and is pretty fast, and when this happens the callback is registered after the fact and stays there forever.
Essentially, this code runs first:
...
elif 'return' in message:
call_id = message['return']
if call_id in _call_return_callbacks: # <=== This can happen very fast, before the call back is registered
callback, error_callback = _call_return_callbacks.pop(call_id)
if message['status'] == 'ok':
callback(message['value'])
elif message['status'] == 'error' and error_callback is not None:
error_callback(message['error'], message['stack'])
else:
_call_return_values[call_id] = message['value']
Then this one:
def _call_return(call):
global _js_result_timeout
call_id = call['call']
def return_func(callback=None, error_callback=None):
if callback is not None:
_call_return_callbacks[call_id] = (callback, error_callback) # <<==== registered too late
else:
for w in range(_js_result_timeout):
if call_id in _call_return_values:
return _call_return_values.pop(call_id)
sleep(0.001)
return return_func
As a result, both the return value is left in _call_return_values and the callback is left in the _call_return_callbacks dict indefinitely.
To Reproduce Do lots of very quick calls from Python to JS, and see that the number of keys in the two mentioned dicts increases steadily over time. Place some prints in the two locations I mentioned and see that they happen out of order sometimes.
Here is an example for illustration:

After several thousand calls, three thousand were left in the those call backs and return values:

In my system it happens 1 to 2 times per thousand calls from Python to JS, but since I do a lot of calls:

... my program dies overnight.