-
-
Notifications
You must be signed in to change notification settings - Fork 31.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
nogil __len__
and __eq__
break atomicity on sets and dicts
#129519
Labels
interpreter-core
(Objects, Python, Grammar, and Parser dirs)
topic-free-threading
type-bug
An unexpected behavior, bug, or error
Comments
Adding the tests mentioned in the main issue here, with sample outputs.
import threading
import sys
def t0(b1,s,res):
b1.wait()
res.append(s.__len__())
def t1(b1,s,res):
b1.wait()
s.symmetric_difference_update({3, 4, 'b', 'e', 'f'})
def Test():
s = {1, 2, 'a', 'b', 'c', 'd', 'e'}
threads=[]
barrier = threading.Barrier(2)
res = []
threads.append(threading.Thread(target= t0, args=(barrier, s,res)))
threads.append(threading.Thread(target= t1, args=(barrier, s,res)))
for i in range(0, len(threads)):
threads[i].start()
for i in range(0, len(threads)):
threads[i].join()
if res[0] not in { 7, 8 }:
print("found bug: " + str(res))
print("test begin...")
for i in range(0,50000):
threads = []
if i % 1000 == 0:
print(i)
for i in range(0,100):
threads.append(threading.Thread(target= Test))
for t in threads:
t.start()
for t in threads:
t.join()
print("test Done") Output:
import threading
import sys
def t0(b1,s,res):
b1.wait()
s.__isub__({1, 'b', 17, 'd'})
def t1(b1,s,res):
b1.wait()
res.append(s.__len__())
def Test():
s = {'a', 17, 18, 'b', 'c', 'd', 'f'}
threads=[]
barrier = threading.Barrier(2)
res = []
threads.append(threading.Thread(target= t0, args=(barrier, s,res)))
threads.append(threading.Thread(target= t1, args=(barrier, s,res)))
for i in range(0, len(threads)):
threads[i].start()
for i in range(0, len(threads)):
threads[i].join()
if res[0] not in { 4, 7 }:
print("found bug: " + str(res[0]))
print("test begin...")
for i in range(0,50000):
threads = []
if i % 1000 == 0:
print(i)
for i in range(0,100):
threads.append(threading.Thread(target= Test))
for t in threads:
t.start()
for t in threads:
t.join()
print("test Done") Output:
import threading
import sys
def t0(b1,s,res):
b1.wait()
res.append(s.__len__())
def t1(b1,s,res):
b1.wait()
s.update({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 'a'})
def Test():
s = {17, 18, 'a', 'b', 'c', 'd', 'e'}
threads=[]
barrier = threading.Barrier(2)
res = []
threads.append(threading.Thread(target= t0, args=(barrier, s,res)))
threads.append(threading.Thread(target= t1, args=(barrier, s,res)))
for i in range(0, len(threads)):
threads[i].start()
for i in range(0, len(threads)):
threads[i].join()
if res[0] not in { 7, 35 }:
print("found bug: " + str(res[0]))
print("test begin...")
for i in range(0,50000):
threads = []
if i % 1000 == 0:
print(i)
for i in range(0,100):
threads.append(threading.Thread(target= Test))
for t in threads:
t.start()
for t in threads:
t.join()
print("test Done") Output:
|
There's two possibilities here, as far as I can tell:
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
interpreter-core
(Objects, Python, Grammar, and Parser dirs)
topic-free-threading
type-bug
An unexpected behavior, bug, or error
Bug report
Bug description:
Hi,
We're a research group focused on testing concurrent runtimes. Our work-in-progress prototype found a violation of atomicity on the current nogil build when using set
__len__
or__eq__
with other concurrent operations on the same set/dict. The program below shows the wrong behavior for__len__
and__ixor__
:Operation
__len__
should see the initial set and return7
or the set after the__ixor__
completes and return32
. However, it can see internal changes made by__ixor__
and return different numbers. Here's an example execution:We have observed the same problem when
__len__
runs concurrently with other operations:symmetric_difference_update
,__isub__
, andupdate
. We'll share these test below in a comment.We observed the same problem when dict's
__len__
runs concurrently with__ior__
:Again, the length of the dict should be either 4 or 9, depending on whether
__ior__
took place. However,__len__
can see internal changes from__ior__
as shown by the output:Finally, we observed set's
__eq__
to be able to access intermediate results of__isub__
, as shown by the program below:The original and resulting sets are never equal to the argument passed to
__eq__
, however the operation can see internal changes from__isub__
as shown by the ouput:@flypoodles and @overlorde are part of the team, adding them so they get notified about further discussion.
We note that we observed these outputs in several x86_64 machines and one ARM machine.
CPython versions tested on:
CPython main branch, 3.14
Operating systems tested on:
Linux
The text was updated successfully, but these errors were encountered: