Bug report
Bug description:
Decimal.__hash__() writes its lazy hash cache (self->hash) with plain stores (-1 default). On a free-threaded build, two threads calling hash() on the same shared Decimal race on the cache field - one reads the -1 sentinel while another stores the computed hash. hash() looks read-only to callers, so a shared Decimal is not safe to hash concurrently.
This is a sub-issue of #153852, see gist here.
Reproducer:
importsys, threadingfromdecimalimportDecimalassertnotsys._is_gil_enabled(), "run free-threaded: PYTHON_GIL=0"NT=4ROUNDS=4000pool= [None]
enter=threading.Barrier(NT+1)
leave=threading.Barrier(NT+1)
defworker():
for_inrange(ROUNDS):
enter.wait()
fordinpool[0]:
hash(d) # dec_hash: read self->hash==-1, then write itleave.wait()
ts= [threading.Thread(target=worker) for_inrange(NT)]
fortints: t.start()
forrinrange(ROUNDS):
pool[0] = [Decimal(f"{r}.{i}") foriinrange(64)] # fresh, unhashed each roundenter.wait(); leave.wait()
fortints: t.join()
print("done, no crash")CPython versions tested on:
CPython main branch
Operating systems tested on:
No response
Linked PRs
Bug report
Bug description:
Decimal.__hash__()writes its lazy hash cache (self->hash) with plain stores (-1 default). On a free-threaded build, two threads calling hash() on the same shared Decimal race on the cache field - one reads the -1 sentinel while another stores the computed hash. hash() looks read-only to callers, so a shared Decimal is not safe to hash concurrently.This is a sub-issue of #153852, see gist here.
Reproducer:
CPython versions tested on:
CPython main branch
Operating systems tested on:
No response
Linked PRs