python / python/cpython

data races in decimal module with global context

Open
#141,148 16 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

docs extension-modules topic-free-threading type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Reproducer:

import decimal
from threading import Thread, Barrier

context = decimal.Context()
context.prec = 28

barrier = Barrier(10)

def race():
    barrier.wait()
    decimal.Decimal('1.0', context=context)

if __name__ == "__main__":
    while True:
        threads = [Thread(target=race) for _ in range(10)]
        for thread in threads:
            thread.start()
        for thread in threads:
            thread.join()

Data race:

==================
WARNING: ThreadSanitizer: data race (pid=58514)
  Write of size 4 at 0x00010d315cbc by thread T5808:
    #0 dec_addstatus _decimal.c:612 (_decimal.cpython-315t-darwin.so:arm64+0x9a88)
    #1 PyDecType_FromCStringExact _decimal.c:2323 (_decimal.cpython-315t-darwin.so:arm64+0x1a4a4)
    #2 dec_new _decimal.c.h:572 (_decimal.cpython-315t-darwin.so:arm64+0x506c)
    #3 type_call typeobject.c:2432 (python.exe:arm64+0x10017e768)
    #4 _PyObject_MakeTpCall call.c:242 (python.exe:arm64+0x10007eb64)
    #5 PyObject_Vectorcall call.c:327 (python.exe:arm64+0x10007f750)
    #6 _PyEval_EvalFrameDefault generated_cases.c.h:1620 (python.exe:arm64+0x100277dec)
    #7 _PyEval_Vector ceval.c:1989 (python.exe:arm64+0x10026ad88)
    #8 _PyFunction_Vectorcall call.c (python.exe:arm64+0x10007fcec)
    #9 method_vectorcall classobject.c:65 (python.exe:arm64+0x100084190)
    #10 _PyObject_Call call.c:348 (python.exe:arm64+0x10007f8b0)
    #11 PyObject_Call call.c:373 (python.exe:arm64+0x10007f9d8)
    #12 _PyEval_EvalFrameDefault generated_cases.c.h:2616 (python.exe:arm64+0x10027bf64)
    #13 _PyEval_Vector ceval.c:1989 (python.exe:arm64+0x10026ad88)
    #14 _PyFunction_Vectorcall call.c (python.exe:arm64+0x10007fcec)
    #15 method_vectorcall classobject.c:73 (python.exe:arm64+0x10008422c)
    #16 context_run context.c:728 (python.exe:arm64+0x1002b53dc)
    #17 _PyEval_EvalFrameDefault generated_cases.c.h:3710 (python.exe:arm64+0x10027ffbc)
    #18 _PyEval_Vector ceval.c:1989 (python.exe:arm64+0x10026ad88)
    #19 _PyFunction_Vectorcall call.c (python.exe:arm64+0x10007fcec)
    #20 method_vectorcall classobject.c:73 (python.exe:arm64+0x10008422c)
    #21 _PyObject_Call call.c:348 (python.exe:arm64+0x10007f964)
    #22 PyObject_Call call.c:373 (python.exe:arm64+0x10007f9d8)
    #23 thread_run _threadmodule.c:387 (python.exe:arm64+0x10041b338)
    #24 pythread_wrapper thread_pthread.h:234 (python.exe:arm64+0x100358824)

  Previous write of size 4 at 0x00010d315cbc by thread T5806:
    #0 dec_addstatus _decimal.c:612 (_decimal.cpython-315t-darwin.so:arm64+0x9a88)
    #1 PyDecType_FromCStringExact _decimal.c:2323 (_decimal.cpython-315t-darwin.so:arm64+0x1a4a4)
    #2 dec_new _decimal.c.h:572 (_decimal.cpython-315t-darwin.so:arm64+0x506c)
    #3 type_call typeobject.c:2432 (python.exe:arm64+0x10017e768)
    #4 _PyObject_MakeTpCall call.c:242 (python.exe:arm64+0x10007eb64)
    #5 PyObject_Vectorcall call.c:327 (python.exe:arm64+0x10007f750)
    #6 _PyEval_EvalFrameDefault generated_cases.c.h:1620 (python.exe:arm64+0x100277dec)
    #7 _PyEval_Vector ceval.c:1989 (python.exe:arm64+0x10026ad88)
    #8 _PyFunction_Vectorcall call.c (python.exe:arm64+0x10007fcec)
    #9 method_vectorcall classobject.c:65 (python.exe:arm64+0x100084190)
    #10 _PyObject_Call call.c:348 (python.exe:arm64+0x10007f8b0)
    #11 PyObject_Call call.c:373 (python.exe:arm64+0x10007f9d8)
    #12 _PyEval_EvalFrameDefault generated_cases.c.h:2616 (python.exe:arm64+0x10027bf64)
    #13 _PyEval_Vector ceval.c:1989 (python.exe:arm64+0x10026ad88)
    #14 _PyFunction_Vectorcall call.c (python.exe:arm64+0x10007fcec)
    #15 method_vectorcall classobject.c:73 (python.exe:arm64+0x10008422c)
    #16 context_run context.c:728 (python.exe:arm64+0x1002b53dc)
    #17 _PyEval_EvalFrameDefault generated_cases.c.h:3710 (python.exe:arm64+0x10027ffbc)
    #18 _PyEval_Vector ceval.c:1989 (python.exe:arm64+0x10026ad88)
    #19 _PyFunction_Vectorcall call.c (python.exe:arm64+0x10007fcec)
    #20 method_vectorcall classobject.c:73 (python.exe:arm64+0x10008422c)
    #21 _PyObject_Call call.c:348 (python.exe:arm64+0x10007f964)
    #22 PyObject_Call call.c:373 (python.exe:arm64+0x10007f9d8)
    #23 thread_run _threadmodule.c:387 (python.exe:arm64+0x10041b338)
    #24 pythread_wrapper thread_pthread.h:234 (python.exe:arm64+0x100358824)
SUMMARY: ThreadSanitizer: data race _decimal.c:615 in dec_addstatus
==================

I discovered this while adding thread safety support to msgspec. Running its test suite under pytest-run-parallel leads to similar data races as present above from the minimal reproducer.

See https://gist.github.com/kumaraditya303/3c26f31eaf0beea39d7eeb539b662846 for full tsan report

Linked PRs
  • gh-146482
  • gh-151953

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the reported call paths in _decimal.c, _decimal.c.h, and context.c, then run the minimal reproducer under ThreadSanitizer. Compare the behavior with the linked PRs gh-146482 and gh-151953. Done means the reproducer and comparable parallel workloads no longer report this data race, with regression coverage added where appropriate.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, python
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.