Skip to content

Commit b643d10

Browse files
committed
[threads-] asynccache/addcol-shell: fix replay/race condition
The straightforward way to assign to d[k] is not thread-safe: d[k] = vd.execAsync(_func, k, *args, **kwargs) because events can happen in this order: 1) _func finishes, assigning func()'s return value to d[k], then 2) execAsync's return value is assigned to d[k], overwriting it. The same failure happens when replay mode temporarily uses execSync in place of every execAsync call. Using setdefault() is safe, assigning to d[k] only if _func hasn't yet done so. The setdefault() technique is commonly said to be atomic on CPython, for a standard dictionary with no custom methods.
1 parent d5c082f commit b643d10

File tree

1 file changed

+3
-1
lines changed

1 file changed

+3
-1
lines changed

visidata/threads.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ 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+
t = vd.execAsync(_func, k, *args, **kwargs)
34+
#atomic read/write to d[k]
35+
d.setdefault(k, t) #2826
3436
return d.get(k)
3537
return _execAsync
3638
return _decorator

0 commit comments

Comments
 (0)