Skip to content

BUG: Data race reading and writing np.random.seed #32059

Closed

Description

@hawkinsp

Describe the issue:

This is not terribly surprising, but the attached example is a TSAN data race under NumPy 2.5.1.

While you can certainly say "you shouldn't be setting the seed concurrently from multiple threads", doing so shouldn't be a C-level race because that is UB.

Reproduce the code example:

import threading
import time

from absl.testing import absltest
import numpy as np


class TsanFailureTest(absltest.TestCase):

  def test_mt19937_seed_get_state_race(self):
    stop = False

    def seeder():
      while not stop:
        np.random.seed(12345)

    def getter():
      while not stop:
        _ = np.random.get_state()

    threads = [
        threading.Thread(target=seeder) for _ in range(4)
    ] + [
        threading.Thread(target=getter) for _ in range(4)
    ]

    for t in threads:
      t.start()

    time.sleep(0.5)
    stop = True

    for t in threads:
      t.join()


if __name__ == "__main__":
  absltest.main()

Error message:

WARNING: ThreadSanitizer: data race (pid=8797)
  Read of size 4 at 0x7f6c3c840c78 by thread T6:
    #0 __pyx_pf_5numpy_6random_8_mt19937_7MT19937_5state___get__ third_party/py/numpy/_cython/random/_mt19937.c:7153:45 (_mt19937.abi3.so+0x1af2d) (BuildId: 84033ccc726a8096efee455b3a93cd98)
    #1 __pyx_pw_5numpy_6random_8_mt19937_7MT19937_5state_1__get__ third_party/py/numpy/_cython/random/_mt19937.c:7066:13 (_mt19937.abi3.so+0x1af2d)
    #2 __pyx_getprop_5numpy_6random_8_mt19937_7MT19937_state third_party/py/numpy/_cython/random/_mt19937.c:7838:10 (_mt19937.abi3.so+0x1af2d)
    #3 getset_get third_party/python_runtime/v3_14_unstable/Objects/descrobject.c:194:16 
    #4 _PyObject_GenericGetAttrWithDict third_party/python_runtime/v3_14_unstable/Objects/object.c 
...

  Previous write of size 4 at 0x7f6c3c840c78 by thread T3:
    #0 mt19937_seed third_party/py/numpy/random/src/mt19937/mt19937.c:10:21 
    #1 __pyx_pf_5numpy_6random_8_mt19937_7MT19937_2_legacy_seeding third_party/py/numpy/_cython/random/_mt19937.c:6356:17 (_mt19937.abi3.so+0x152fa) 
    #2 __pyx_pw_5numpy_6random_8_mt19937_7MT19937_3_legacy_seeding third_party/py/numpy/_cython/random/_mt19937.c:6015:13 (_mt19937.abi3.so+0x12b05) 
    #3 __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS third_party/py/numpy/_cython/random/_mt19937.c:13156:12 (_mt19937.abi3.so+0x1e5a8) 
    #4 _PyObject_VectorcallTstate third_party/python_runtime/v3_14_unstable/Include/internal/pycore_call.h:169:11
...

Python and NumPy Versions:

Python 3.14t
Numpy 2.5.1

Runtime Environment:

Not super easy to collect from CI.

How does this issue affect you or how did you find it:

Observed in JAX TSAN CI: https://github.com/jax-ml/jax/actions/runs/29895407638/job/88844401276

Activity

  1. ngoldbaum commented on Jul 22, 2026

    @ngoldbaum
    Member

    I wonder if we should just go ahead and make the global np.random random state be stored in a context variable or a thread-local. I doubt it's ever useful for np.random's state to be truly process-wide global given that we already have locking and it's probably a performance bottleneck to use instead of a per-thread RNG stream. The global RNG is truly global now out of a historical accident IMO.

    I don't know if that idea has come up before so I'm going to ping @rkern to get his opinion. FWIW I also think random.random RNG state in CPython should probably become thread-local because it's a performance footgun for a naive monte carlo algorithm implementation.

  2. ngoldbaum commented on Jul 22, 2026

    @ngoldbaum
    Member

    The global RNG state is supposed to be thread-safe though and I think I see the issue, we're accessing the RNG state in get_state outside the lock. Patch incoming...

  3. rkern commented on Jul 22, 2026

    @rkern
    Member

    Yes, we have locks for this purpose. I suspect we looked at the the get path and assumed the GIL would handle it.

  4. ngoldbaum commented on Jul 22, 2026

    @ngoldbaum
    Member

    I opened #32061

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions