Skip to content

Data race in Decimal.__hash__() #154037

Description

@skirpichev

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions