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

aio-libs / multidict / 36155852940
87%

Build:
DEFAULT BRANCH: master
Ran 25 Sep 2026 03:42PM 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 03:41PM UTC coverage: 87.004%. Remained the same
36155852940

push

github

web-flow
Reserve version numbers in per-thread batches on free-threaded builds (#1587)

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

## What do these changes do?

On the free-threaded build every mutation bumped one counter shared by
every multidict with an atomic fetch-add. Threads mutating their own,
unrelated multidicts still fought over that one cache line, and with six
threads a `d[key] = value` spent about three quarters of its time there.

Each thread now reserves 256 versions at a time from a process-wide
counter and hands them out from thread-local storage, the same scheme
free-threaded CPython uses for its dict version tags. Versions stay
unique across all multidicts and keep increasing within a thread, but
are no longer ordered across threads; `getversion()` is documented as
equality-only and the C code only ever compares with `!=`. The counter
moved out of module state so a batch left over in a thread can never
overlap a newer module state's range. The thread-locals use the default
TLS model: initial-exec would save a few ns per mutation, but it draws
on glibc's small static TLS reserve for `dlopen()`, and when that is
exhausted the import fails and multidict silently falls back to pure
Python. The GIL build is unchanged, and the pure-Python implementation
has no equivalent contention to fix.

Wall clock, 3.14.7t, ns per mutation, each thread on its own dict and
keys, pinned to separate P-cores, median of 5 interleaved rounds:

| op | threads | master | this PR |
|---|---:|---:|---:|
| `d[k] = v` | 1 | 46.1 | 46.2 |
| `d[k] = v` | 2 | 90.7 | 49.5 |
| `d[k] = v` | 4 | 162.3 | 55.9 |
| `d[k] = v` | 6 | 266.4 | 61.7 |
| `add` + `del` | 1 | 53.3 | 51.0 |
| `add` + `del` | 6 | 288.5 | 74.1 |
| `get()` (control) | 6 | 59.1 | 58.0 |

## Are there changes in behavior for the user?

Faster concurrent mutation on free-threaded builds. Versions from
different threads are no longer ordered relative to each other, which
was never part of the documented contract.
... (continued)

780 of 1560 branches covered (50.0%)

Branch coverage included in aggregate %.

8104 of 8651 relevant lines covered (93.68%)

1.87 hits per line

Jobs
ID Job ID Ran Files Coverage
1 MyPy - 36155852940.1 25 Sep 2026 03:42PM UTC 86
87.0
GitHub Action Run
Source Files on build 36155852940
  • 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 #36155852940
  • 8aee2a59 on github
  • Prev Build on master (#36150606046)
  • Next Build on master (#36156013899)
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