Skip to content

Commit a6aee53

Browse files
committed
[threads-] asynccache/addcol-shell: fix replay/race condition
1 parent d5c082f commit a6aee53

File tree

1 file changed

+11
-1
lines changed

1 file changed

+11
-1
lines changed

visidata/threads.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,17 @@ def _func(k, *args, **kwargs):
3030
def _execAsync(*args, **kwargs):
3131
k = key(*args, **kwargs)
3232
if k not in d:
33-
d[k] = vd.execAsync(_func, k, *args, **kwargs)
33+
# The straightforward way to assign to d[k] is not thread-safe:
34+
# d[k] = vd.execAsync(_func, k, *args, **kwargs)
35+
# because events can happen in this order:
36+
# 1) _func can finish, assigning the return value of func() to d[k], then
37+
# 2) execAsync's return value is assigned to d[k], overwriting the return value of func()
38+
# The same failure happens when replay mode temporarily uses execSync in place of every execAsync call.
39+
t = vd.execAsync(_func, k, *args, **kwargs)
40+
d.setdefault(k, t) #2826
41+
# Using setdefault() is safe, assigning to d[k] only if _func hasn't yet done so.
42+
# The setdefault() technique is commonly said to be atomic on CPython, for a standard
43+
# dictionary with no custom methods.
3444
return d.get(k)
3545
return _execAsync
3646
return _decorator

0 commit comments

Comments
 (0)