The GIL comes with great convenience as you don't have to worry about a whole host of data races. It's no silver bullet but it's mighty convenient if you only do some multi-threading like in a web-server.
Many libraries are not prepared for the disappearance of the GIL and while it's not a general problem for python per se it will be a great amount of work to make every library compatible with GILless python.
Therefore I think that you must always provide an option for the GIL that is enabled by default in order to provide backward compatibility.
> The GIL comes with great convenience as you don't have to worry about a whole host of data races.
This is true, but it doesn't mean that a GIL-less Python would need to have an option for "enable GIL so I don't have to worry about data races". It means that a GIL-less Python would have to ensure that there are no data races, without having to have a GIL.
> it will be a great amount of work to make every library compatible with GILless python
No, it won't; libraries won't have to change at all. The current interpreter makes a guarantee that those libraries rely on: "you don't have to worry about data races". A GIL-less interpreter would have to make the same guarantee; it just wouldn't have to have a GIL to do it. That requirement is what makes a GIL-less Python interpreter hard to do.
Right now you have one mutex for everything (the GIL itself) and everything else doesn't need locking. In order to achieve similar convenience without the GIL you would have to trade this for one mutex for every single data structure. Because the data structures in python are so flexible, every single variable needs its own mutex then. Locking every single variable access would be enormously costly.
Other languages achieve a good compromise by clustering data structures into fewer parts with only a handful of mutexes that are locked while other threads work on different datasets. This is usually done manually and with great care as it is the heart of both safety and performance. I don't know if there is an automatic solution to this problem that is compatible with the way python programs are structured.
The libraries basically assume that, while you call them, nothing else changes. In order to ensure that you need to lock everything down. Because you don't know what these libraries do and what data they access it needs to be everything (like it is today). It should be possible to only lock the GIL when such a library is called, so there should be kind of a middle way forward.
> Right now you have one mutex for everything (the GIL itself) and everything else doesn't need locking.
If this were true, all of the explicit locking mechanisms in Python's threading module would be pointless. But in fact the GIL's "mutex" is quite a bit more limited than you are saying. It does not prevent all concurrent code from running. It only prevents Python bytecode from running concurrently in more than one thread. But the GIL allows switching between threads in between individual bytecodes, and "one Python bytecode" does not correspond to "one Python statement that performs an operation that you want to be atomic"; plenty of Python expressions and statements are implemented by multiple bytecodes, so it is perfectly possible for multiple threads executing concurrently to modify the same data structures with such statements, creating race conditions if explicit locking mechanisms are not used to prevent it. That's why Python's standard library provides such explicit mechanisms.
> If this were true, all of the explicit locking mechanisms in Python's threading module would be pointless.
Not true. You can have serialized access to the same data structure that still have data race.
But as long as each Python process doesn't keep its local copies for those shared data structures, like free lists, no explicit locking is required if GIL is presented.
> You can have serialized access to the same data structure that still have data race
How?
> as long as each Python process doesn't keep its local copies for those shared data structures, like free lists, no explicit locking is required if GIL is presented.
I have no idea what you're talking about. Different Python processes each have their own GIL, and they don't share data at all (except by explicit mechanisms like communicating through sockets or pipes). Different Python threads share the GIL for their interpreter process, and if each thread doesn't keep its own local copy of data, there is explicit locking required if you don't want data races.
Simplest scenario, the read-increment-write cycle with 2 threads. Even with a mutex, it is still possible to have data race, if the lock is on per operation level.
For the second part, yep, it is a mistake, not processes, but threads.
With GIL, the thread is given the permission to operate on certain interpreter-related data-structures, like reference counts, or free_list like in PyIntObject. What I mean the active thread is free to modify those data structures without fear of data races, and there is no explicit locking required, if it doesn't hold its own copies of those interpreter internal states.
But GIL can only guard the interpreter's own states, not any user program's states. And yes, explicit locking for operating on your own data is still required.
> Simplest scenario, the read-increment-write cycle with 2 threads. Even with a mutex, it is still possible to have data race, if the lock is on per operation level.
What you're describing is not "serialized access with a data race"; it's "multi-thread access that you didn't explicitly control properly".
> For the second part, yep, it is a mistake, not processes, but threads.
Ok, that clarifies things.
> the active thread is free to modify those data structures without fear of data races, and there is no explicit locking required, if it doesn't hold its own copies of those interpreter internal states.
I'm not sure I see why a thread would want to hold copies of those interpreter internal states, since if it did the issue would not be modifying them properly but having the local copies get out of sync with the interpreter's copies, since other threads can also mutate the latter.
The problem with GIL is that it's a mutex that you don't control. So you can't use it to do atomic updates, if that involves more than one low-level operation that is atomic.
So in practice I don't think it simplifies things all that much. If anything, it creates a false sense of security - first developers get used to the fact that they can just assign to variables without synchronization, and then they forget that they still need to synchronize when they need to assign to more than one atomically.
I'm pretty rusty on Python but my impression wasn't that the GIL meant just "no data races" but that it also meant "data can't change out from under you in the middle of executing a statement". You could write a Python interpreter that ensured no data races and yet still had divergent behavior by allowing shared data to be mutated by another thread halfway through executing a statement.
> my impression wasn't that the GIL meant just "no data races" but that it also meant "data can't change out from under you in the middle of executing a statement".
That's not quite what the GIL guarantees. It guarantees that data can't change out from under you in the middle of executing a bytecode. But many Python statements (and expressions) do not correspond to single bytecodes.
Many libraries are not prepared for the disappearance of the GIL and while it's not a general problem for python per se it will be a great amount of work to make every library compatible with GILless python.
Therefore I think that you must always provide an option for the GIL that is enabled by default in order to provide backward compatibility.