• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aio-libs / multidict / 36147951162
87%

Build:
DEFAULT BRANCH: master
Ran 25 Sep 2026 02:31PM UTC
Jobs 1
Files 43
Run time 1min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

25 Sep 2026 02:30PM UTC coverage: 87.058% (-0.04%) from 87.098%
36147951162

push

github

web-flow
Make watcher registration thread safe and retire stale watch bits (#1583)

<!-- Thank you for your contribution! -->

## What do these changes do?

`MultiDict_AddWatcher()` and `MultiDict_ClearWatcher()` wrote the
module-wide slot table with plain stores, while event delivery on
another thread read it with no lock. On the free-threaded build that is
a data race, and TSan reports it at `watch.h` against both functions.
Registration now serializes on a `PyMutex` and publishes through new
`freethreading.h` accessors, following CPython's `PyDict_AddWatcher()`.
Delivery stays lock-free.

`ClearWatcher()` also bumps a per-slot generation, and each watch
records the generation it was attached under. A multidict left watched
through a cleared ID used to report to the next registration under that
ID, passing the old registration's `user_data`. That bit is now ignored
until the multidict is watched again. CPython's dict watchers still have
the same reuse hazard; the heap-allocated per-multidict record makes it
cheap to close here. This only affects the C extension: watchers are a C
API feature and have no pure-Python counterpart.

## Are there changes in behavior for the user?

Yes, for C API clients only. Both registration functions are now safe to
call from any thread, and a cleared watcher's leftover watches no longer
reach a later registration that reuses its ID. The docs drop the warning
about that reuse.

## Is it a substantial burden for the maintainers to support this?

No. The mutation hot path is unchanged: an unwatched multidict still
pays one pointer test per mutation.

## Related issue number

Follow-up to #1551 and #1574.

## Checklist

- [x] I think the code is well written
- [x] Unit tests for the changes exist
- [x] Documentation reflects the changes
- [ ] If you provide code modification, please add yourself to
`CONTRIBUTORS.txt` (N/A)
- [x] Add a new news fragment into the `CHANGES/` folder
- [x] `make doc-spelling` passes and any new technic... (continued)

780 of 1560 branches covered (50.0%)

Branch coverage included in aggregate %.

28 of 36 new or added lines in 1 file covered. (77.78%)

8086 of 8624 relevant lines covered (93.76%)

1.88 hits per line

Uncovered Changes

Lines Coverage ∆ File
8
74.65
0.08% tests/test_capi.py
Jobs
ID Job ID Ran Files Coverage
1 MyPy - 36147951162.1 25 Sep 2026 02:31PM UTC 86
87.06
GitHub Action Run
Source Files on build 36147951162
  • Tree
  • List 43
  • Changed 3
  • Source Changed 1
  • Coverage Changed 3
Coverage ∆ File Lines Relevant Covered Missed Hits/Line Branch Hits Branch Misses
  • Back to Repo
  • Github Actions Build #36147951162
  • 0f245d08 on github
  • Prev Build on master (#36144372153)
  • Next Build on master (#36149088889)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc