<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: threads</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/threads.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2025-09-11T03:07:16+00:00</updated><author><name>Simon Willison</name></author><entry><title>Quoting Kumar Aditya</title><link href="https://simonwillison.net/2025/Sep/11/kumar-aditya/#atom-tag" rel="alternate"/><published>2025-09-11T03:07:16+00:00</published><updated>2025-09-11T03:07:16+00:00</updated><id>https://simonwillison.net/2025/Sep/11/kumar-aditya/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://labs.quansight.org/blog/scaling-asyncio-on-free-threaded-python"&gt;&lt;p&gt;In Python 3.14, I have implemented several changes to fix thread safety of &lt;code&gt;asyncio&lt;/code&gt; and enable it to scale effectively on the free-threaded build of CPython. It is now implemented using lock-free data structures and per-thread state, allowing for highly efficient task management and execution across multiple threads. In the general case of multiple event loops running in parallel, there is no lock contention and performance scales linearly with the number of threads. [...]&lt;/p&gt;
&lt;p&gt;For a deeper dive into the implementation, check out the &lt;a href="https://github.com/python/cpython/blob/main/InternalDocs/asyncio.md#python-314-implementation"&gt;internal docs for asyncio&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://labs.quansight.org/blog/scaling-asyncio-on-free-threaded-python"&gt;Kumar Aditya&lt;/a&gt;, Scaling asyncio on Free-Threaded Python&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/async"&gt;async&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gil"&gt;gil&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;&lt;/p&gt;



</summary><category term="async"/><category term="gil"/><category term="python"/><category term="scaling"/><category term="threads"/></entry><entry><title>Hypothesis is now thread-safe</title><link href="https://simonwillison.net/2025/Aug/8/hypothesis/#atom-tag" rel="alternate"/><published>2025-08-08T22:08:55+00:00</published><updated>2025-08-08T22:08:55+00:00</updated><id>https://simonwillison.net/2025/Aug/8/hypothesis/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://hypothesis.works/articles/thread-safe/"&gt;Hypothesis is now thread-safe&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Hypothesis is a property-based testing library for Python. It lets you write tests like this one:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s1"&gt;hypothesis&lt;/span&gt; &lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-s1"&gt;given&lt;/span&gt;, &lt;span class="pl-s1"&gt;strategies&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-s1"&gt;st&lt;/span&gt;

&lt;span class="pl-en"&gt;@&lt;span class="pl-en"&gt;given&lt;/span&gt;(&lt;span class="pl-s1"&gt;st&lt;/span&gt;.&lt;span class="pl-c1"&gt;lists&lt;/span&gt;(&lt;span class="pl-s1"&gt;st&lt;/span&gt;.&lt;span class="pl-c1"&gt;integers&lt;/span&gt;()))&lt;/span&gt;
&lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;test_matches_builtin&lt;/span&gt;(&lt;span class="pl-s1"&gt;ls&lt;/span&gt;):
    &lt;span class="pl-k"&gt;assert&lt;/span&gt; &lt;span class="pl-en"&gt;sorted&lt;/span&gt;(&lt;span class="pl-s1"&gt;ls&lt;/span&gt;) &lt;span class="pl-c1"&gt;==&lt;/span&gt; &lt;span class="pl-en"&gt;my_sort&lt;/span&gt;(&lt;span class="pl-s1"&gt;ls&lt;/span&gt;)&lt;/pre&gt;

&lt;p&gt;This will automatically create a collection of test fixtures that exercise a large array of expected list and integer shapes. Here's &lt;a href="https://gist.github.com/simonw/74014071af1553921e0307efd2280168"&gt;a Gist&lt;/a&gt; demonstrating the tests the above code will run, which include things like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[]
[0]
[-62, 13194]
[44, -19562, 44, -12803, -24012]
[-7531692443171623764, -109369043848442345045856489093298649615]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hypothesis contributor Liam DeVoe was recently sponsored by Quansight to add thread safety to Hypothesis, which has become important recently due to Python free threading:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;While we of course would always have loved for Hypothesis to be thread-safe, thread-safety has historically not been a priority, because running Hypothesis tests under multiple threads is not something we see often.&lt;/p&gt;
&lt;p&gt;That changed recently. Python---as both a language, and a community---is gearing up to &lt;a href="https://peps.python.org/pep-0703/"&gt;remove the global interpreter lock (GIL)&lt;/a&gt;, in a build called &lt;a href="https://docs.python.org/3/howto/free-threading-python.html"&gt;free threading&lt;/a&gt;. Python packages, especially those that interact with the C API, will need to test that their code still works under the free threaded build. A great way to do this is to run each test in the suite in two or more threads simultaneously. [...]&lt;/p&gt;
&lt;p&gt;Nathan mentioned that because Hypothesis is not thread-safe, Hypothesis tests in community packages have to be skipped when testing free threaded compatibility, which removes a substantial battery of coverage.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now that Hypothesis is thread-safe another blocker to increased Python ecosystem support for free threading has been removed!

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://lobste.rs/s/zrbpds/hypothesis_is_now_thread_safe"&gt;lobste.rs&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/gil"&gt;gil&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/testing"&gt;testing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;&lt;/p&gt;



</summary><category term="gil"/><category term="python"/><category term="testing"/><category term="threads"/></entry><entry><title>From Async/Await to Virtual Threads</title><link href="https://simonwillison.net/2025/Aug/3/virtual-threads/#atom-tag" rel="alternate"/><published>2025-08-03T18:57:56+00:00</published><updated>2025-08-03T18:57:56+00:00</updated><id>https://simonwillison.net/2025/Aug/3/virtual-threads/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://lucumr.pocoo.org/2025/7/26/virtual-threads/"&gt;From Async/Await to Virtual Threads&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Armin Ronacher has long been critical of async/await in Python, both for necessitating &lt;a href="https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/"&gt;colored functions&lt;/a&gt; and because of the more subtle challenges they introduce like &lt;a href="https://lucumr.pocoo.org/2020/1/1/async-pressure/"&gt;managing back pressure&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Armin &lt;a href="https://lucumr.pocoo.org/2024/11/18/threads-beat-async-await/"&gt;argued convincingly&lt;/a&gt; for the threaded programming model back in December. Now he's expanded upon that with a description of how virtual threads might make sense in Python.&lt;/p&gt;
&lt;p&gt;Virtual threads behave like real system threads but can vastly outnumber them, since they can be paused and scheduled to run on a real thread when needed. Go uses this trick to implement goroutines which can then support millions of virtual threads on a single system.&lt;/p&gt;
&lt;p&gt;Python core developer Mark Shannon &lt;a href="https://discuss.python.org/t/add-virtual-threads-to-python/91403"&gt;started a conversation&lt;/a&gt; about the potential for seeing virtual threads to Python back in May.&lt;/p&gt;
&lt;p&gt;Assuming this proposal turns into something concrete I don't expect we will see it in a production Python release for a few more years. In the meantime there are some exciting improvements to the Python concurrency story - most notably &lt;a href="https://docs.python.org/3.14/whatsnew/3.14.html#whatsnew314-pep734"&gt;around sub-interpreters&lt;/a&gt; - coming up this year in Python 3.14.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/armin-ronacher"&gt;armin-ronacher&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/concurrency"&gt;concurrency&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gil"&gt;gil&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/go"&gt;go&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;&lt;/p&gt;



</summary><category term="armin-ronacher"/><category term="concurrency"/><category term="gil"/><category term="go"/><category term="python"/><category term="threads"/></entry><entry><title>How we think about Threads’ iOS performance</title><link href="https://simonwillison.net/2024/Dec/29/threads-ios-performance/#atom-tag" rel="alternate"/><published>2024-12-29T21:45:14+00:00</published><updated>2024-12-29T21:45:14+00:00</updated><id>https://simonwillison.net/2024/Dec/29/threads-ios-performance/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://engineering.fb.com/2024/12/18/ios/how-we-think-about-threads-ios-performance/"&gt;How we think about Threads’ iOS performance&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This article by Dave LaMacchia and Jason Patterson provides an incredibly deep insight into what effective performance engineering looks like for an app with 100s of millions of users.&lt;/p&gt;
&lt;p&gt;I always like hearing about custom performance metrics with their own acronyms. Here we are introduced to &lt;strong&gt;%FIRE&lt;/strong&gt; - the portion of people who experience a &lt;em&gt;frustrating image-render experience&lt;/em&gt; (based on how long an image takes to load after the user scrolls it into the viewport), &lt;strong&gt;TTNC&lt;/strong&gt; (&lt;em&gt;time-to-network content&lt;/em&gt;) measuring time from app launch to fresh content visible in the feed and &lt;strong&gt;cPSR&lt;/strong&gt; (&lt;em&gt;creation-publish success rate&lt;/em&gt;) for how often a user manages to post content that they started to create.&lt;/p&gt;
&lt;p&gt;This article introduced me to the concept of a &lt;strong&gt;boundary test&lt;/strong&gt;, described like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A boundary test is one where we measure extreme ends of a boundary to learn what the effect is. In our case, we introduced a slight bit of latency when a small percentage of our users would navigate to a user profile, to the conversion view for a post, or to their activity feed. &lt;/p&gt;
&lt;p&gt;This latency would allow us to extrapolate what the effect would be if we similarly &lt;em&gt;improved&lt;/em&gt; how we delivered content to those views.&lt;/p&gt;
&lt;p&gt;[...]&lt;/p&gt;
&lt;p&gt;We learned that iOS users don’t tolerate a lot of latency. The more we added, the less often they would launch the app and the less time they would stay in it. With the smallest latency injection, the impact was small or negligible for some views, but the largest injections had negative effects across the board. People would read fewer posts, post less often themselves, and in general interact less with the app. Remember, we weren’t injecting latency into the core feed, either; just into the profile, permalink, and activity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There's a whole lot more in there, including details of their custom internal performance logger (SLATE, the “Systemic LATEncy” logger) and several case studies of surprising performance improvements made with the assistance of their metrics and tools, plus some closing notes on how Swift concurrency is being adopted throughout Meta.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://bsky.app/profile/raf.eco/post/3lehpzyipic2c"&gt;Rafe Colburn&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/performance"&gt;performance&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ios"&gt;ios&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/meta"&gt;meta&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/swift"&gt;swift&lt;/a&gt;&lt;/p&gt;



</summary><category term="performance"/><category term="threads"/><category term="ios"/><category term="meta"/><category term="swift"/></entry><entry><title>Free-threaded CPython is ready to experiment with!</title><link href="https://simonwillison.net/2024/Jul/12/free-threaded-cpython/#atom-tag" rel="alternate"/><published>2024-07-12T23:42:46+00:00</published><updated>2024-07-12T23:42:46+00:00</updated><id>https://simonwillison.net/2024/Jul/12/free-threaded-cpython/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://labs.quansight.org/blog/free-threaded-python-rollout"&gt;Free-threaded CPython is ready to experiment with!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The Python 3.13 beta releases that include a "free-threaded" version that removes the GIL are now available to test! A team from Quansight Labs, home of the PyData core team, just launched &lt;a href="https://py-free-threading.github.io/"&gt;py-free-threading.github.io&lt;/a&gt; to help document the new builds and track compatibility with Python's larger ecosystem.&lt;/p&gt;
&lt;p&gt;Free-threading mode will not be enabled in Python installations by default. You can install special builds that have the option enabled today - I used the macOS installer and, after enabling the new build in the "Customize" panel in the installer, ended up with a &lt;code&gt;/usr/local/bin/python3.13t&lt;/code&gt; binary which shows "Python 3.13.0b3 experimental free-threading build" when I run it.&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://til.simonwillison.net/python/trying-free-threaded-python"&gt;my TIL describing my experiments so far&lt;/a&gt; installing and running the 3.13 beta on macOS, which also includes a correction to an embarrassing bug that Claude introduced but I failed to catch!


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/concurrency"&gt;concurrency&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gil"&gt;gil&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;&lt;/p&gt;



</summary><category term="concurrency"/><category term="gil"/><category term="python"/><category term="threads"/></entry><entry><title>Threads has entered the fediverse</title><link href="https://simonwillison.net/2024/Mar/22/threads-has-entered-the-fediverse/#atom-tag" rel="alternate"/><published>2024-03-22T20:15:20+00:00</published><updated>2024-03-22T20:15:20+00:00</updated><id>https://simonwillison.net/2024/Mar/22/threads-has-entered-the-fediverse/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://engineering.fb.com/2024/03/21/networking-traffic/threads-has-entered-the-fediverse/"&gt;Threads has entered the fediverse&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Threads users with public profiles in certain countries can now turn on a setting which makes their posts available in the fediverse—so users of ActivityPub systems such as Mastodon can follow their accounts to subscribe to their posts.&lt;/p&gt;

&lt;p&gt;It’s only a partial integration at the moment: Threads users can’t themselves follow accounts from other providers yet, and their notifications will show them likes but not boosts or replies: “For now, people who want to see replies on their posts on other fediverse servers will have to visit those servers directly.”&lt;/p&gt;

&lt;p&gt;Depending on how you count, Mastodon has around 9m user accounts of which 1m are active. Threads claims more than 130m active monthly users. The Threads team are developing these features cautiously which is reassuring to see—a clumsy or thoughtless integration could cause all sorts of damage just from the sheer scale of their service.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://anderegg.ca/2024/03/22/poking-at-threads-in-the-fediverse"&gt;Gavin Anderegg&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/facebook"&gt;facebook&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mastodon"&gt;mastodon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/activitypub"&gt;activitypub&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/fediverse"&gt;fediverse&lt;/a&gt;&lt;/p&gt;



</summary><category term="facebook"/><category term="threads"/><category term="mastodon"/><category term="activitypub"/><category term="fediverse"/></entry><entry><title>The GIL and its effects on Python multithreading</title><link href="https://simonwillison.net/2021/Sep/29/the-gil-and-its-effects-on-python-multithreading/#atom-tag" rel="alternate"/><published>2021-09-29T17:23:15+00:00</published><updated>2021-09-29T17:23:15+00:00</updated><id>https://simonwillison.net/2021/Sep/29/the-gil-and-its-effects-on-python-multithreading/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://tenthousandmeters.com/blog/python-behind-the-scenes-13-the-gil-and-its-effects-on-python-multithreading/"&gt;The GIL and its effects on Python multithreading&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Victor Skvortsov presents the most in-depth explanation of the Python Global Interpreter Lock I’ve seen anywhere. I learned a ton from reading this.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=28690560"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/concurrency"&gt;concurrency&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gil"&gt;gil&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;&lt;/p&gt;



</summary><category term="concurrency"/><category term="gil"/><category term="python"/><category term="threads"/></entry><entry><title>How we found and fixed a rare race condition in our session handling</title><link href="https://simonwillison.net/2021/Mar/18/how-we-found-and-fixed-a-rare-race-condition-in-our-session-hand/#atom-tag" rel="alternate"/><published>2021-03-18T23:06:00+00:00</published><updated>2021-03-18T23:06:00+00:00</updated><id>https://simonwillison.net/2021/Mar/18/how-we-found-and-fixed-a-rare-race-condition-in-our-session-hand/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.blog/2021-03-18-how-we-found-and-fixed-a-rare-race-condition-in-our-session-handling/"&gt;How we found and fixed a rare race condition in our session handling&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
GitHub had a terrifying bug this month where a user reported suddenly being signed in as another user. This is a particularly great example of a security incident report, explaining how GitHub identified the underlying bug, what caused it and the steps they are taking to ensure bugs like that never happen in the future. The root cause was a convoluted sequence of events which could cause a Ruby Hash to be accidentally shared between two requests, caused as a result of a new background thread that was introduced as a performance optimization.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;&lt;/p&gt;



</summary><category term="github"/><category term="security"/><category term="threads"/></entry><entry><title>Weeknotes: Datasette Writes</title><link href="https://simonwillison.net/2020/Feb/26/weeknotes-datasette-writes/#atom-tag" rel="alternate"/><published>2020-02-26T06:34:46+00:00</published><updated>2020-02-26T06:34:46+00:00</updated><id>https://simonwillison.net/2020/Feb/26/weeknotes-datasette-writes/#atom-tag</id><summary type="html">
    &lt;p&gt;As &lt;a href="https://simonwillison.net/2020/Jan/21/weeknotes-datasette-cloud-and-zero-downtime-deployments/#datasette-upload-csvs"&gt;discussed previously&lt;/a&gt;, the biggest hole in Datasette's feature set at the moment involves writing to the database.&lt;/p&gt;

&lt;p&gt;Datasette was born as a hack to abuse serverless, stateless hosting by bundling a static, immutable database as part of the deployment. The key idea was that for some use-cases - such as data journalism - you don't need to be able to continually update your data. It's just the facts that support the story you are trying to tell.&lt;/p&gt;

&lt;p&gt;I also believed the conventional wisdom that SQLite is fine for reads but shouldn't be trusted to handle web application writes. I no longer believe this to be the case: SQLite is &lt;em&gt;great&lt;/em&gt; at handling writes, as millions of iPhone and Android apps will attest.&lt;/p&gt;

&lt;p&gt;Meanwhile, the biggest blocker to people trying out Datasette is that they would need to convert their data to SQLite somehow in order to use it. I've been building &lt;a href="https://datasette.readthedocs.io/en/stable/ecosystem.html#tools-for-creating-sqlite-databases"&gt;a family of CLI tools for this&lt;/a&gt;, but that requires users to both be familiar with the command-line and to &lt;em&gt;install software on their computers&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So: Datasette needs to grow web-based tools for loading data into the database.&lt;/p&gt;

&lt;p&gt;Datasette's &lt;a href="https://datasette.readthedocs.io/en/stable/plugins.html"&gt;plugin system&lt;/a&gt; is the ideal space for experimenting with ways of doing this, without needing to try out crazy new features on Datasette's own core.&lt;/p&gt;

&lt;p&gt;There's just one big problem: SQLite may be great at fast, reliable writes but it still doesn't like concurrent writes: it's important to only ever have one connection writing to a SQLite database at a time.&lt;/p&gt;

&lt;p&gt;I've been mulling over the best way to handle this for &lt;a href="https://github.com/simonw/datasette/issues/567"&gt;the best part of a year&lt;/a&gt;... and then a couple of days ago I had a breakthrough: with a dedicated write thread for a database file, I could use a Python queue to ensure only one write could access the database at a time.&lt;/p&gt;

&lt;p&gt;There's prior art for this: SQLite wizard Charles Leifer &lt;a href="https://charlesleifer.com/blog/multi-threaded-sqlite-without-the-operationalerrors/"&gt;released code plus a beautiful explanation&lt;/a&gt; of how to queue writes to SQLite back in 2017. I'm not sure why I didn't settle on his approach sooner.&lt;/p&gt;

&lt;p&gt;So... &lt;a href="https://datasette.readthedocs.io/en/stable/changelog.html#v0-37"&gt;Datasette 0.37&lt;/a&gt;, released this evening, has a new capability exposed to plugins: they can now request that an operation (either a SQL statement or a full custom Python function) be &lt;a href="https://github.com/simonw/datasette/issues/682"&gt;queued up to execute inside a thread&lt;/a&gt; that posesses an exclusive write connection to a SQLite database.&lt;/p&gt;

&lt;p&gt;I've documented how plugins can use this in the new plugin internals documentation: &lt;a href="https://datasette.readthedocs.io/en/latest/internals.html#database-class"&gt;execute_write() and execute_write_fn()&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So far there's only one public plugin that takes advantage of this: &lt;a href="https://github.com/simonw/datasette-upload-csvs"&gt;datasette-upload-csvs&lt;/a&gt;, which previously used &lt;a href="https://github.com/simonw/datasette-upload-csvs/blob/699e6ca591f36264bfc8e590d877e6852f274beb/datasette_upload_csvs/app.py#L43-L46"&gt;a dirty hack&lt;/a&gt; but has now been upgraded to use the new &lt;code&gt;execute_write_fn()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;I'm really excited about the potential plugins this unlocks though. I experimented with a logging plugin and a plugin for deleting tables while I was building the hooks (full implementations of those are posted as comments in &lt;a href="https://github.com/simonw/datasette/pull/683"&gt;the pull request&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Other use-cases I'm interested to explore include:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;Plugins that import data from other APIs or services. Imagine web UI interfaces to some of my &lt;a href="https://github.com/dogsheep"&gt;Dogsheep tools&lt;/a&gt; for example.&lt;/li&gt;&lt;li&gt;Plugins that periodically update data - pulling the latest CSV updates from government open data portals (like &lt;a href="https://simonwillison.net/2019/Mar/13/tree-history/"&gt;San Francisco's trees&lt;/a&gt;).&lt;/li&gt;&lt;li&gt;Tools for enhancing tables with additional data derived from their values - geocoding or reverse geocoding columns, resolving identifiers and so on.&lt;/li&gt;&lt;li&gt;Now that plugins have a tool for maintaining their own state, plugins could use SQLite tables to track things like which saved searches have been executed.&lt;/li&gt;&lt;li&gt;A plugin that lets you attach annotations to rows and columns in other tables, storing those annotations in its own SQLite database.&lt;/li&gt;&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/csv"&gt;csv&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/plugins"&gt;plugins&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="csv"/><category term="plugins"/><category term="sqlite"/><category term="threads"/><category term="datasette"/><category term="weeknotes"/></entry><entry><title>An Intro to Threading in Python</title><link href="https://simonwillison.net/2019/Apr/18/intro-threading-python/#atom-tag" rel="alternate"/><published>2019-04-18T05:24:24+00:00</published><updated>2019-04-18T05:24:24+00:00</updated><id>https://simonwillison.net/2019/Apr/18/intro-threading-python/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://realpython.com/intro-to-python-threading/"&gt;An Intro to Threading in Python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Real Python consistently produces really comprehensive, high quality articles and tutorials. This is an excellent introduction to threading in Python, covering threads, locks, queues, ThreadPoolExecutor and more.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/realpython/status/1118522943246671880"&gt;@realpython&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;&lt;/p&gt;



</summary><category term="python"/><category term="threads"/></entry><entry><title>Intro to Threads and Processes in Python</title><link href="https://simonwillison.net/2018/Apr/19/intro-threads-and-processes-python/#atom-tag" rel="alternate"/><published>2018-04-19T18:32:24+00:00</published><updated>2018-04-19T18:32:24+00:00</updated><id>https://simonwillison.net/2018/Apr/19/intro-threads-and-processes-python/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/@bfortuner/python-multithreading-vs-multiprocessing-73072ce5600b"&gt;Intro to Threads and Processes in Python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I really like the diagrams in this article which compares the performance of Python threads and processes for different types of task via the excellent concurrent.futures library.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://medium.freecodecamp.org/python-collection-of-my-favorite-articles-8469b8455939"&gt;Gergely Szerovay&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/multiprocessing"&gt;multiprocessing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;&lt;/p&gt;



</summary><category term="multiprocessing"/><category term="python"/><category term="threads"/></entry><entry><title>Testing Django Views for Concurrency Issues</title><link href="https://simonwillison.net/2009/May/27/testing/#atom-tag" rel="alternate"/><published>2009-05-27T10:01:08+00:00</published><updated>2009-05-27T10:01:08+00:00</updated><id>https://simonwillison.net/2009/May/27/testing/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.caktusgroup.com/blog/2009/05/26/testing-django-views-for-concurrency-issues/"&gt;Testing Django Views for Concurrency Issues&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Neat decorator for executing a Django view under high concurrency in your unit tests, to help spot errors caused by database race conditions that should be executed inside a transaction.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/concurrency"&gt;concurrency&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/raceconditions"&gt;raceconditions&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/testing"&gt;testing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;&lt;/p&gt;



</summary><category term="concurrency"/><category term="django"/><category term="python"/><category term="raceconditions"/><category term="testing"/><category term="threads"/></entry><entry><title>Introducing Yardbird</title><link href="https://simonwillison.net/2009/May/22/yardbird/#atom-tag" rel="alternate"/><published>2009-05-22T23:13:39+00:00</published><updated>2009-05-22T23:13:39+00:00</updated><id>https://simonwillison.net/2009/May/22/yardbird/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://zork.net/motd/nick/django/introducing-yardbird.html"&gt;Introducing Yardbird&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I absolutely love it—an IRC bot built on top of Twisted that passes incoming messages off to Django code running in a separate thread. Requests and Response objects are used to represent incoming and outgoing messages, and Django’s regex-based URL routing is used to dispatch messages to different handling functions based on their content.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/irc"&gt;irc&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/regular-expressions"&gt;regular-expressions&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/twisted"&gt;twisted&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/yardbird"&gt;yardbird&lt;/a&gt;&lt;/p&gt;



</summary><category term="django"/><category term="irc"/><category term="regular-expressions"/><category term="threads"/><category term="twisted"/><category term="yardbird"/></entry><entry><title>Using Scala with Google App Engine</title><link href="https://simonwillison.net/2009/Apr/11/print/#atom-tag" rel="alternate"/><published>2009-04-11T15:28:48+00:00</published><updated>2009-04-11T15:28:48+00:00</updated><id>https://simonwillison.net/2009/Apr/11/print/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.riffraff.info/2009/4/9/using-scala-with-google-app-engine"&gt;Using Scala with Google App Engine&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Scala works, but I haven’t seen confirmation on actors yet (which are likely to break due to their dependency on threads).


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/google-app-engine"&gt;google-app-engine&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/java"&gt;java&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scala"&gt;scala&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;&lt;/p&gt;



</summary><category term="google"/><category term="google-app-engine"/><category term="java"/><category term="scala"/><category term="threads"/></entry><entry><title>django-springsteen and Distributed Search</title><link href="https://simonwillison.net/2009/Feb/25/springsteen/#atom-tag" rel="alternate"/><published>2009-02-25T22:28:25+00:00</published><updated>2009-02-25T22:28:25+00:00</updated><id>https://simonwillison.net/2009/Feb/25/springsteen/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://lethain.com/entry/2009/feb/25/django-springsteen-and-distributed-search/"&gt;django-springsteen and Distributed Search&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Will Larson’s Django search library currently just talks to Yahoo! BOSS, but is designed to be extensible for other external search services. Interestingly, it uses threads to fire off several HTTP requests in parallel from within the Django view.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/concurrency"&gt;concurrency&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/djangospringsteen"&gt;djangospringsteen&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/http"&gt;http&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/search"&gt;search&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/will-larson"&gt;will-larson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/yahoo-boss"&gt;yahoo-boss&lt;/a&gt;&lt;/p&gt;



</summary><category term="concurrency"/><category term="django"/><category term="djangospringsteen"/><category term="http"/><category term="python"/><category term="search"/><category term="threads"/><category term="will-larson"/><category term="yahoo-boss"/></entry><entry><title>jessenoller.com - python magazine</title><link href="https://simonwillison.net/2009/Feb/5/jessenollercom/#atom-tag" rel="alternate"/><published>2009-02-05T23:10:43+00:00</published><updated>2009-02-05T23:10:43+00:00</updated><id>https://simonwillison.net/2009/Feb/5/jessenollercom/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://jessenoller.com/category/python-magazine/"&gt;jessenoller.com - python magazine&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Jesse Noller has been sharing his articles originally published in Python Magazine. Topics include SSH programming with Paramiko, context managers and the with statement and an excellent explanation of Python’s threading support and the global interpreter lock.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/contextmanagers"&gt;contextmanagers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gil"&gt;gil&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jesse-noller"&gt;jesse-noller&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/paramiko"&gt;paramiko&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pythonmagazine"&gt;pythonmagazine&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ssh"&gt;ssh&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;&lt;/p&gt;



</summary><category term="contextmanagers"/><category term="gil"/><category term="jesse-noller"/><category term="paramiko"/><category term="python"/><category term="pythonmagazine"/><category term="ssh"/><category term="threads"/></entry><entry><title>Thread Synchronization Mechanisms in Python</title><link href="https://simonwillison.net/2007/Jul/29/thread/#atom-tag" rel="alternate"/><published>2007-07-29T21:32:01+00:00</published><updated>2007-07-29T21:32:01+00:00</updated><id>https://simonwillison.net/2007/Jul/29/thread/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://effbot.org/zone/thread-synchronization.htm"&gt;Thread Synchronization Mechanisms in Python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Locks, RLocks, Semaphores, Events and Conditions as explained by Fredrik Lundh.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/conditions"&gt;conditions&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/effbot"&gt;effbot&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/events"&gt;events&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/fredrik-lundh"&gt;fredrik-lundh&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/locks"&gt;locks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rlocks"&gt;rlocks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/semaphores"&gt;semaphores&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tutorial"&gt;tutorial&lt;/a&gt;&lt;/p&gt;



</summary><category term="conditions"/><category term="effbot"/><category term="events"/><category term="fredrik-lundh"/><category term="locks"/><category term="python"/><category term="rlocks"/><category term="semaphores"/><category term="threads"/><category term="tutorial"/></entry><entry><title>Quoting Guido van Rossum</title><link href="https://simonwillison.net/2007/May/8/guido/#atom-tag" rel="alternate"/><published>2007-05-08T21:21:30+00:00</published><updated>2007-05-08T21:21:30+00:00</updated><id>https://simonwillison.net/2007/May/8/guido/#atom-tag</id><summary type="html">
    &lt;blockquote cite="http://mail.python.org/pipermail/python-3000/2007-May/007414.html"&gt;&lt;p&gt;Just because Java was once aimed at a set-top box OS that didn't support multiple address spaces, and just because process creation in Windows used to be slow as a dog, doesn't mean that multiple processes (with judicious use of IPC) aren't a much better approach to writing apps for multi-CPU boxes than threads.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="http://mail.python.org/pipermail/python-3000/2007-May/007414.html"&gt;Guido van Rossum&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/guido-van-rossum"&gt;guido-van-rossum&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ipc"&gt;ipc&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/java"&gt;java&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/threads"&gt;threads&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/windows"&gt;windows&lt;/a&gt;&lt;/p&gt;



</summary><category term="guido-van-rossum"/><category term="ipc"/><category term="java"/><category term="python"/><category term="threads"/><category term="windows"/></entry></feed>