Is the usage of list(d.items()) in the example below safe?
import threading
n = 2000
d = {}
def dict_to_list():
while True:
list(d.items()) # is this safe to do?
def modify():
for i in range(n):
d[i] = i
if __name__ == "__main__":
t1 = threading.Thread(target=dict_to_list, daemon=True)
t1.start()
t2 = threading.Thread(target=modify, daemon=True)
t2.start()
t2.join()
The background behind this question is that an iterator over a dictionary item view checks on every step whether the dictionary size changed, as the following example illustrates.
d = {}
view = d.items() # this is an iterable
it = iter(view) # this is an iterator
d[1] = 1
print(list(view)) # this is ok, it prints [(1, 1)]
print(list(it)) # this raises a RuntimeError because the size of the dictionary changed
So if the call to list(...) in the first example above can be interrupted (i.e., the thread t1 could release the GIL), the first example might cause RuntimeErrors to occur in thread t1. There are sources that claim the operation is not atomic, see here. However, I haven't been able to get the first example to crash.
I understand that the safe thing to do here would be to use some locks instead of trying to rely on the atomicity of certain operations. However, I'm debugging an issue in a third party library that uses similar code and that I cannot necessarily change directly.