It may work for your codebase, but the GIL has deep implications for any multithreaded code. For a long time now we've relied on the GIL for some level of thread safety. I'm afraid removing it would cause quite a few libraries to fail in unexpected ways.
The statement that "this work is very carefully designed not to break multithreaded code" is difficult to reconcile with some of the ways it potentially breaks multithreaded code that are explicitly called out down in the "compatibility" section.
In particular, the design document's observation that "some C API extensions need modifications to protect global data structures that were previously protected by the GIL" serves as a direct confirmation that this change was not able to avoid breaking at least some multithreaded code that relies on the GIL to provide thread safety.
Which is why the proposal was always to have it behind either a runtime of compile time flag. It has never been their plan to unilaterally change the way this works (py2>3 style).
> To mitigate compatibility issues and improve debugging, the proof of concept can run with the GIL enabled or disabled controlled at runtime by an environment variable (or command-line option). If CPython adopts some form of the GIL changes, I’d expect this runtime control to be useful for at least a few releases to address flag day issues.
This doesn't say "GILectomy will forever remain an opt-in".
One example off the top of my head is socketserver in the standard library, which is used by http.server, xmlrpc, IDLE and all kinds of things.
The socketserver.BaseServer class sets self.__shutdown_request in one thread and expects it to be picked up by another. In the Java memory model this variable would have to be marked as volatile (or the methods involved as synchronized) to make sure that the other thread will actually see the change, so unless Python implicitly make every variable volatile (does it?) this wouldn't be guaranteed to work (although it would probably mostly still work most of the time except for when it mysteriously doesn't).
I'm sure there's something I'm missing here, since no-one seems to be talking about it, but how does the nogil version ensure that changes are made visible to other threads, i.e. what Java calls "safely published"?
As I understand it, a different thread running on a different core could have a cached version of a value which wouldn't necessarily be updated unless some instruction is issued to synchronize with main memory, which in Java is done with volatile or synchronized.
Also, if some optimization is implemented that reorders instructions or eliminates the variable update entirely, that could also prevent the other thread from ever seeing the updated value. This is also solved by using volatile or sychronized in Java.
Is every variable implicitly volatile in nogil Python? Or only object attributes? Or have I completely misunderstood some important aspect?
Edit: I suppose modifying the reference count might cause implicit synchronization similar to the piggybacking technique [1] in Java, making this a non-issue?
Yes, I believe every variable is implicitly volatile in nogil Python.
That said, I am not a good source for truth on this. But it feels like so much code would break if this weren't true that I don't think it would have gotten to this stage.
Maybe that's the only one? Maybe it isn't? But I think the point still stands that people saying this has the potential to break existing Python packages in subtle ways are not just being hyperbolic.
I would argue from a theoretic point of view no, but because the GIL causes execution of Python threads to be "batched", there surely will be racy code that seems to work now, and will start experiencing failures or more of them without the GIL batching.
It worked! I was stunned at how easy and effective it was. My page load time dropped from 77ms to 47ms when I ran some of the code in parallel.
I wrote some notes on that here: https://simonwillison.net/2022/May/6/weeknotes/#nogil