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

aio-libs / multidict / 35619716719
87%

Build:
DEFAULT BRANCH: master
Ran 21 Sep 2026 03:34PM UTC
Jobs 1
Files 39
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

21 Sep 2026 03:33PM UTC coverage: 87.557% (+0.06%) from 87.498%
35619716719

push

github

web-flow
Stop a GIL-releasing __del__ from segfaulting the standard C extension build (#1491)

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

## What do these changes do?

Extends the deferred-decref technique from #1487 (free-threaded builds
only) to the standard, GIL-enabled C extension build. On that build
`Py_BEGIN_CRITICAL_SECTION` compiles to a no-op, so a decref whose
`__del__` (or a weakref callback) itself releases the GIL was the only
thing standing between a mutation and a second thread running
concurrently
on the very same `MultiDict`; nothing caught that. `update()`,
`merge()`,
`__setitem__()`, `__delitem__()`, `pop()`, `popone()`, and `popall()`
now
defer every such decref until the mutation has fully finished, the same
way the free-threaded build already does. `md_clear()` needed no change:
it already swaps `md->keys` to the empty table before releasing any
reference, so a suspended decref there can only ever be observed against
a fully detached old table.

Also bumped the deferred-decref accumulator's inline buffer from 8 to
1024 entries so the allocator is essentially never reached in practice.

A related, pre-existing gap was found but is intentionally **not** fixed
here (see the follow-up note below): `md_del()`/`popall()`'s own
multi-entry loops cache the entries array and iterator once at loop
start
and never refresh them the way `_md_replace()`/`_md_update()` do, so a
decref-triggered suspension mid-loop can leave them pointing at a table
a
concurrent resize already freed. This reproduces identically on
unmodified upstream `master`, on both builds, so it predates this PR.

C extension only; the pure-Python implementation has no locking of its
own to regress.

## Are there changes in behavior for the user?

Yes: a value type whose `__del__` (or a weakref callback) releases the
GIL while another thread concurrently mutates the same `MultiDict` no
longer risks a segfault or table corruption on the standard build.
Single-threaded behavior is unch... (continued)

762 of 1524 branches covered (50.0%)

Branch coverage included in aggregate %.

45 of 45 new or added lines in 1 file covered. (100.0%)

6514 of 6786 relevant lines covered (95.99%)

1.92 hits per line

Jobs
ID Job ID Ran Files Coverage
1 MyPy - 35619716719.1 21 Sep 2026 03:34PM UTC 78
87.55
GitHub Action Run
Source Files on build 35619716719
  • Tree
  • List 39
  • 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 #35619716719
  • 30cd596f on github
  • Prev Build on master (#35610986105)
  • Next Build on master (#35621094685)
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