<p>CPython is known for having a Global Interpreter Lock (GIL), which many users consider to be detrimental. What does this mean, and why would an intepreter be designed to have this feature?</p>

<interpreters>
What is a Global Interpreter Lock, and why would an interpreter have it?
CC BY-SA 4.0
2023-07-02T09:19:24.573Z
**When you vote down, please specify in comments what is wrong.**
чистов_n
CC BY-SA 4.0
2023-07-02T10:06:01.290Z
I would imagine top reason for the downvotes is that the top internet search results easily answer your question - suggesting lack of research on your part.
Your question does not add any context to explain why those explanations (if you found them) are not sufficient for you.
Bruce Adams
CC BY-SA 4.0
2023-07-02T12:11:26.510Z
Shows no research effort is why I downvoted;read the tooltip @nchistov
Starship
CC BY-SA 4.0
2023-07-02T12:11:44.993Z
I’m voting to close this question because its easily look up able
Starship
CC BY-SA 4.0
2023-07-02T13:22:57.017Z
@Starship-OnStrike That's not a reason to close a question
mousetail 'he-him'
CC BY-SA 4.0
2023-07-02T13:31:57.297Z
Its a common custom reason on space SE at least;enough that I feel like they talked about making it a community specific reason @mousetail
Starship
CC BY-SA 4.0
2023-07-02T13:39:37.113Z
@nchistov [Stack Exchange sites have never required downvoters to leave comments explaining their downvotes](https://meta.stackexchange.com/q/325416/1135948), and this site is no exception.
kaya3
CC BY-SA 4.0
2023-07-02T13:45:43.957Z
@Starship-OnStrike I'm sure you haven't confused PLDI SE with Space SE, so why bring up what their close reasons are (or what you "feel like they talked about")?
kaya3
CC BY-SA 4.0
2023-07-02T13:47:05.950Z
No I haven't but what im saying is that at least where im familiar with its a fine close reason @kaya3-supportthestrike
Starship
CC BY-SA 4.0
2023-07-02T14:12:54.237Z
@kaya3-supportthestrike to be fair nchstiov said please, not you must or die
Starship
CC BY-SA 4.0
2023-07-02T14:25:11.437Z
@kaya3-supportthestrike It's still a good idea to explain why you're downvoting the question, if not so the OP can improve it, then so that other viewers know what's wrong with it.
user
CC BY-SA 4.0
2023-07-02T16:15:35.700Z
I think this question is fine; I’ve voted to reopen. It is clearly about programming language implementation, and it has a precise and well-defined answer (which cannot be said of many other questions asked recently with a much higher score). It is true that information can be found easily via an internet search, but having information on essential concepts here is useful as a repository of knowledge.
Alexis King
CC BY-SA 4.0
2023-07-02T17:13:06.480Z
8 downvotes? Y'all, that's excessive. I've seen spam with better ratios than this.
rydwolf
CC BY-SA 4.0
2023-07-02T20:55:30.610Z
And thats a problem that spam has better ratios @RydwolfPrograms
Starship
CC BY-SA 4.0
2023-07-02T20:56:02.527Z
Really, so on Math SE "whats 1+1?" is a good question @AlexisKing
Starship
CC BY-SA 4.0
2023-07-02T20:57:21.513Z
@Starship-OnStrike Students are not taught about GILs in grade school.
Alexis King
CC BY-SA 4.0
2023-07-02T20:58:04.077Z
Becuase math is a more essential subject than language design @AlexisKing
Starship
CC BY-SA 4.0
2023-07-02T20:59:12.977Z
@Starship-OnStrike Indeed. So there is more utility to having the fundamentals available on this website since most people haven’t already learned them elsewhere.
Alexis King
<p>Needing a global lock is a consequence of an ad hoc approach to implementing an interpreter, and is perfectly avoidable.</p>
<p>If your language have, say, dynamic scoping, any thread of the interpreter may want to query a global identifier table at any moment. GIL is an easy way to ensure there are no race conditions in accessing the table. This could have been avoided, firstly, by not designing a language with dynamic scoping (really, why would anyone do it?), and, secondly, if you're really hell-bent on having dynamic name lookup in runtime, by using discrete lock-free data structures for such tables.</p>
<p>And dynamic name lookup is just one of the parts of the CPython implementation that needs to be protected from race conditions.</p>
<p>To summarise: needing a GIL is pretty much a consequence of poor choices in both language design and in interpreter implementation.</p>

CC BY-SA 4.0
2023-07-02T09:57:13.960Z
2023-07-03T23:36:14.593Z
2023-07-03T23:36:14.593Z
SK-logic
CC BY-SA 4.0
2023-07-02T17:24:06.550Z
I think this answer is using the term "[dynamic scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Dynamic_scope)" to mean something different to what it is normally understood as meaning. I would think that dynamic scoping, i.e. resolving names by traversing the call stack instead of lexically, would mean different threads (each having their own call stack) need not contend for a any shared resource when resolving names.
kaya3
CC BY-SA 4.0
2023-07-02T17:42:17.380Z
@kaya3-supportthestrike Python, despite claims of the opposite, use dynamic scoping and late binding in this very sense. Any thread can look.up outer definitions of the same binding. Not that different from elisp and its ilk.
SK-logic
CC BY-SA 4.0
2023-07-02T17:58:53.130Z
Python has lexical scoping for most variables, the exceptions being global variables defined dynamically through the `globals()` dictionary, and arguably the ability to walk the call-stack at runtime using the `inspect` module. If either of those is what you mean by "dynamic scope" then you should make this clear (though I don't see how either lead to a resource contention between threads that lexical scoping wouldn't have). Python of course has late binding, but that is a different thing.
kaya3
CC BY-SA 4.0
2023-07-02T18:17:39.043Z
I don’t think this answer accurately describes why CPython has retained its GIL, or at least I think it paints a misleadingly incomplete picture. If the GIL were purely an implementation problem, it would be removed by now, but the GIL has become a part of CPython’s interface.
Alexis King
CC BY-SA 4.0
2023-07-02T19:00:43.627Z
Yes, I said that name lookup is just one of the things GIL was needed for, CPython grew dependent on it in many other aspects to an extend that removing GIL will be a breaking change in an interface, so now it is virtually impossible to fo. As I said, bad design choices early on, doomed to linger forever now.
SK-logic
CC BY-SA 4.0
2023-07-04T11:24:03.013Z
"Any thread can look.up outer definitions of the same binding." - No, this conflates different meanings of "outer", and thus gets the meaning of "dynamic scoping" wrong. Python enables late binding to names defined in enclosing *scopes*, which is *lexical* scoping. *Dynamic* scoping is late binding to names defined in the *calling context*.
Karl Knechtel
<p>As the question notes, the specific term “global interpreter lock” <a href="https://stackoverflow.com/questions/1294382/what-is-the-global-interpreter-lock-gil-in-cpython">is generally associated with CPython</a>, which infamously includes a global mutex that must be acquired whenever any thread interprets Python code. CPython was designed before multicore processors were a common occurrence on commodity hardware, so support for parallelism was not engineered into the runtime from the start. This decision turned out to be difficult to undo:</p>
<ul>
<li><p>CPython’s interpreter includes global data structures shared between threads. Some of these data structures are heavily accessed and modified during execution, so fine-grained locking of each access would be prohibitively expensive.</p>
</li>
<li><p>CPython provides a relatively strong <a href="https://en.wikipedia.org/wiki/Memory_model_(programming)" rel="noreferrer">memory model</a> to Python programs, so explicit barriers are not necessary for mutation to be well-ordered from the perspective of other Python threads, which would be difficult and expensive to guarantee without the GIL.</p>
</li>
<li><p>Much of CPython’s popularity stems from its easy interoperation with C libraries, many of which are not themselves thread-safe, so elimination of the GIL would create a compatibility headache for those libraries.</p>
</li>
</ul>
<p>Some other interpreted languages include a GIL or something like it, most notably the primary Ruby implementation, MRI. However, even among languages that don’t provide parallel threads, most implementations don’t include a GIL <em>per se</em>.</p>
<p>Racket, for example, is essentially a single-core language in much the same way CPython is: it provides preemptively multitasked green threads, but these only run concurrently, not in parallel. However, Racket does not have a global mutex that locks the interpreter, and there is relatively little global interpreter state. Instead, Racket’s thread scheduler simply chooses to run a single thread at a time, so all program execution is still serialized. Even in the absence of a GIL, there are reasons why Racket chooses to serialize execution:</p>
<ul>
<li><p>Like CPython, Racket provides a strong memory model to programs, so mutations on one thread are always visible in the same order from the perspective of another.</p>
</li>
<li><p>Like CPython, Racket provides an FFI, and many programs use C libraries that are not completely thread-safe.</p>
</li>
<li><p>Even though Racket doesn’t have a GIL, it provides an unsafe primitive to enter “atomic mode”, during which the thread scheduler will never switch threads, which is used to implement some low-level functionality. This would be impossible to implement efficiently with parallel threads.</p>
</li>
</ul>
<p>In practice, this means that Racket is limited in much the same way as CPython from the perspective of most programmers, so saying that Racket “doesn’t have a GIL” might seem like a technicality. However, there <em>is</em> a real difference: the obstacles to providing parallel threads in Racket are essentially just backwards compatibility constraints, while a GIL is a fundamental technical limitation. Indeed, <a href="https://docs.racket-lang.org/reference/futures.html" rel="noreferrer">safe parallelism is available in a restricted form</a>, and recent versions of Racket provide <a href="https://docs.racket-lang.org/foreign/Operating_System_Threads.html" rel="noreferrer">unsafe access to OS threads</a> without issue, which would not be possible if Racket had a GIL.</p>
<hr />
<p>As this answer hopefully illustrates, the main reason for a language to have a GIL is one of backwards compatibility. Adding support for parallel execution to an existing language can be quite challenging; the best way to avoid falling into a similar trap is to design a language and its runtime to support parallelism from the start. Many older languages did not do this because shared-memory parallelism was simply not widely available when they were designed.</p>
<p>Personally, I think it is worth taking the time and effort to ensure new languages can support fine-grained parallelism eventually, rather than backing oneself into a corner. Multicore systems are more or less ubiquitous now, and programmers wish to take advantage of them. However, there is undoubtedly work involved in doing this, so whether it is worth it to you will always be a matter of opinion.</p>

CC BY-SA 4.0
2023-07-02T18:10:54.927Z
2023-07-02T20:42:06.057Z
2023-07-02T20:42:06.057Z
Alexis King
CC BY-SA 4.0
2023-07-02T20:09:06.957Z
*so fine-grained locking of each access would be prohibitively expensive.* - And might lead to inconsistent results if two threads were accessing the same data structure and parts of the changes from one Python statement became visible to other threads while others didn't yet.  (I guess that would require Python programs to do their own locking around any modifications to shared state, which is probably necessary anyway in a lot of cases, so IDK how much they benefit from whatever atomicity the GIL gives.)
Peter Cordes
CC BY-SA 4.0
2023-07-02T20:14:37.720Z
*explicit barriers are not necessary for mutation to be visible from other Python threads* - Note that [hardware has coherent caches](https://stackoverflow.com/questions/4557979/when-to-use-volatile-with-multi-threading/58535118#58535118) between cores that run threads of the same program, so visibility isn't much of a problem; as long as data gets stored to memory (not kept in a register by a compiler) it will be visible to other threads.
Peter Cordes
CC BY-SA 4.0
2023-07-02T20:16:31.180Z
Memory barriers are needed for *ordering*, to make operations on data structures visible in a consistent order, e.g. as part locking to give the acquire and release semantics a lock needs to serialize the critical sections of different threads.  And for a non-trivial data structure like a hash table or Python List or integer, the different parts of the C data structures need to be consistent with each other, so what you said is true about making mutations visible in a *safe* way, that other threads can read without seeing objects in mid modification. (Or having C data-race undefined behavior)
Peter Cordes
CC BY-SA 4.0
2023-07-02T20:25:04.883Z
The way you put it does basically give the right idea, that an interpreter designed around serial execution of its functions that modify data structures can use a GIL to preserve that and still be able to use threads for I/O parallelism (e.g. so they can block in a system call without holding the lock).  It's non-obvious why Racket would be able to lets its interpreter functions run in parallel, other than I guess having "relatively little global interpreter state".
Peter Cordes
CC BY-SA 4.0
2023-07-02T20:44:24.840Z
@PeterCordes Yes, you’re right, my wording was sloppy—I’ve updated the answer to be a little more explicit. As for why Racket is able to let its interpreter run in parallel—why do you think there must be some obstacle? There is no trouble to running multiple parallel mutators as long as they have some simple protocol to yield for garbage collection.
Alexis King
CC BY-SA 4.0
2023-07-02T21:45:02.913Z
In a program originally designed to run in a single thread (just doing user-space context-switches for green threads or coroutines or whatever) you'd expect the data structures to not be thread-safe, like C++ `std::vector` (or the equivalent in languages like Java or C# which don't have undefined behaviour for data races).  If two threads append an element, in practice the way they compile to machine code, you might end up with only one total increment to the in-use size, and/or both stores might or might not be to the same element. You'd need an atomic RMW to make it safe even without realloc
Peter Cordes
CC BY-SA 4.0
2023-07-02T21:50:36.560Z
I don't know Racket, so maybe the restrictions on mutating objects in https://docs.racket-lang.org/foreign/Operating_System_Threads.html mean you can only update a reference, so it can be like Linux RCU (Read, Copy, Update), or something else that's not terrible for frequent small changes to large arrays.  Internal consistency between parts of one data structure, like `std::vector`'s data pointer, and its size and capacity (actually pointers to the ends), can't be maintained just by ordering of operations; lock-free data structures require careful design and are slower with just 1 thread.
Peter Cordes
CC BY-SA 4.0
2023-07-02T21:53:41.433Z
@PeterCordes Yes, as I said in the answer, OS threads are unsafe, and you must stick to a restricted subset for it to be safe. But those restrictions only come into play when two OS threads *interact*; there is no issue with simply interpreting code in parallel if they avoid mutating the same memory. And as the docs say, you can synchronize across OS threads with explicit OS-level locks. This is all quite different from a system with a GIL.
Alexis King
CC BY-SA 4.0
2023-07-02T22:12:41.100Z
Oh, yeah parallel mutators on *separate* objects is fairly easy, I thought you were asking what obstacles I thought would exist for threads mutating shared data.  The doc says "but the visibility of mutations to other threads is unspecified except as synchronized ...", which implies it won't crash the interpreter by corrupting objects, they just might not be visible in any order you expect.  But yes, if the threading model fully forbids data races in Ratchet programs, that leaves locking up to the Ratchet code, no need for a GIL built-in to the interpreter.
Peter Cordes
CC BY-SA 4.0
2023-07-02T22:15:49.327Z
@PeterCordes Yes, that was the distinction I was intending to convey. Feel free to reword my answer slightly if you think that part could be made clearer somehow!
Alexis King
CC BY-SA 4.0
2023-07-02T22:21:01.677Z
I don't know Ratchet at all, so I don't want to say anything incorrect about it.  And it still sounds to me like https://docs.racket-lang.org/foreign/Operating_System_Threads.html doesn't strongly forbid different threads mutating the same object, you just aren't guaranteed what the final result will be.  If that's allowed to crash the interpreter (via C data-race UB for an interpreter written in C), then yeah your answer makes sense.  So maybe say explicitly that it's not just about data visibility across threads for the Ratchet code, it's exposing the possibility of interpreter data-race UB.
Peter Cordes
# What is a Global Interpreter Lock, and why would an interpreter have it?
Posted by: username_0 on 2023-07-02
CPython is known for having a Global Interpreter Lock (GIL), which many users consider to be detrimental. What does this mean, and why would an intepreter be designed to have this feature?
**Tags:** interpreters
> **Comments:**
> **username_0**: **When you vote down, please specify in comments what is wrong.**  
> **username_1**: I would imagine top reason for the downvotes is that the top internet search results easily answer your question - suggesting lack of research on your part.
Your question does not add any context to explain why those explanations (if you found them) are not sufficient for you.  
> **username_2**: Shows no research effort is why I downvoted;read the tooltip @nchistov  
> **username_2**: I’m voting to close this question because its easily look up able  
> **username_3**: @Starship-OnStrike That's not a reason to close a question  
> **username_2**: Its a common custom reason on space SE at least;enough that I feel like they talked about making it a community specific reason @mousetail  
> **username_4**: @nchistov [Stack Exchange sites have never required downvoters to leave comments explaining their downvotes](https://meta.stackexchange.com/q/325416/1135948), and this site is no exception.  
> **username_4**: @Starship-OnStrike I'm sure you haven't confused PLDI SE with Space SE, so why bring up what their close reasons are (or what you "feel like they talked about")?  
> **username_2**: No I haven't but what im saying is that at least where im familiar with its a fine close reason @kaya3-supportthestrike  
> **username_2**: @kaya3-supportthestrike to be fair nchstiov said please, not you must or die  
> **username_5**: @kaya3-supportthestrike It's still a good idea to explain why you're downvoting the question, if not so the OP can improve it, then so that other viewers know what's wrong with it.  
> **username_6**: I think this question is fine; I’ve voted to reopen. It is clearly about programming language implementation, and it has a precise and well-defined answer (which cannot be said of many other questions asked recently with a much higher score). It is true that information can be found easily via an internet search, but having information on essential concepts here is useful as a repository of knowledge.  
> **username_7**: 8 downvotes? Y'all, that's excessive. I've seen spam with better ratios than this.  
> **username_2**: And thats a problem that spam has better ratios @RydwolfPrograms  
> **username_2**: Really, so on Math SE "whats 1+1?" is a good question @AlexisKing  
> **username_6**: @Starship-OnStrike Students are not taught about GILs in grade school.  
> **username_2**: Becuase math is a more essential subject than language design @AlexisKing  
> **username_6**: @Starship-OnStrike Indeed. So there is more utility to having the fundamentals available on this website since most people haven’t already learned them elsewhere.  
---
## 2 answers
### Answer by username_6 on 2023-07-02. Score: 22 | Accepted answer
As the question notes, the specific term “global interpreter lock” [is generally associated with CPython](https://stackoverflow.com/questions/1294382/what-is-the-global-interpreter-lock-gil-in-cpython), which infamously includes a global mutex that must be acquired whenever any thread interprets Python code. CPython was designed before multicore processors were a common occurrence on commodity hardware, so support for parallelism was not engineered into the runtime from the start. This decision turned out to be difficult to undo:

* CPython’s interpreter includes global data structures shared between threads. Some of these data structures are heavily accessed and modified during execution, so fine-grained locking of each access would be prohibitively expensive.
* CPython provides a relatively strong [memory model](https://en.wikipedia.org/wiki/Memory_model_(programming)) to Python programs, so explicit barriers are not necessary for mutation to be well-ordered from the perspective of other Python threads, which would be difficult and expensive to guarantee without the GIL.
* Much of CPython’s popularity stems from its easy interoperation with C libraries, many of which are not themselves thread-safe, so elimination of the GIL would create a compatibility headache for those libraries.

Some other interpreted languages include a GIL or something like it, most notably the primary Ruby implementation, MRI. However, even among languages that don’t provide parallel threads, most implementations don’t include a GIL *per se*.

Racket, for example, is essentially a single-core language in much the same way CPython is: it provides preemptively multitasked green threads, but these only run concurrently, not in parallel. However, Racket does not have a global mutex that locks the interpreter, and there is relatively little global interpreter state. Instead, Racket’s thread scheduler simply chooses to run a single thread at a time, so all program execution is still serialized. Even in the absence of a GIL, there are reasons why Racket chooses to serialize execution:

* Like CPython, Racket provides a strong memory model to programs, so mutations on one thread are always visible in the same order from the perspective of another.
* Like CPython, Racket provides an FFI, and many programs use C libraries that are not completely thread-safe.
* Even though Racket doesn’t have a GIL, it provides an unsafe primitive to enter “atomic mode”, during which the thread scheduler will never switch threads, which is used to implement some low-level functionality. This would be impossible to implement efficiently with parallel threads.

In practice, this means that Racket is limited in much the same way as CPython from the perspective of most programmers, so saying that Racket “doesn’t have a GIL” might seem like a technicality. However, there *is* a real difference: the obstacles to providing parallel threads in Racket are essentially just backwards compatibility constraints, while a GIL is a fundamental technical limitation. Indeed, [safe parallelism is available in a restricted form](https://docs.racket-lang.org/reference/futures.html), and recent versions of Racket provide [unsafe access to OS threads](https://docs.racket-lang.org/foreign/Operating_System_Threads.html) without issue, which would not be possible if Racket had a GIL.

---

As this answer hopefully illustrates, the main reason for a language to have a GIL is one of backwards compatibility. Adding support for parallel execution to an existing language can be quite challenging; the best way to avoid falling into a similar trap is to design a language and its runtime to support parallelism from the start. Many older languages did not do this because shared-memory parallelism was simply not widely available when they were designed.

Personally, I think it is worth taking the time and effort to ensure new languages can support fine-grained parallelism eventually, rather than backing oneself into a corner. Multicore systems are more or less ubiquitous now, and programmers wish to take advantage of them. However, there is undoubtedly work involved in doing this, so whether it is worth it to you will always be a matter of opinion.
> **Comments:**
> **username_10** (2023-07-02): *so fine-grained locking of each access would be prohibitively expensive.* - And might lead to inconsistent results if two threads were accessing the same data structure and parts of the changes from one Python statement became visible to other threads while others didn't yet.  (I guess that would require Python programs to do their own locking around any modifications to shared state, which is probably necessary anyway in a lot of cases, so IDK how much they benefit from whatever atomicity the GIL gives.)  
> **username_10** (2023-07-02): *explicit barriers are not necessary for mutation to be visible from other Python threads* - Note that [hardware has coherent caches](https://stackoverflow.com/questions/4557979/when-to-use-volatile-with-multi-threading/58535118#58535118) between cores that run threads of the same program, so visibility isn't much of a problem; as long as data gets stored to memory (not kept in a register by a compiler) it will be visible to other threads.  
> **username_10** (2023-07-02): Memory barriers are needed for *ordering*, to make operations on data structures visible in a consistent order, e.g. as part locking to give the acquire and release semantics a lock needs to serialize the critical sections of different threads.  And for a non-trivial data structure like a hash table or Python List or integer, the different parts of the C data structures need to be consistent with each other, so what you said is true about making mutations visible in a *safe* way, that other threads can read without seeing objects in mid modification. (Or having C data-race undefined behavior)  
> **username_10** (2023-07-02): The way you put it does basically give the right idea, that an interpreter designed around serial execution of its functions that modify data structures can use a GIL to preserve that and still be able to use threads for I/O parallelism (e.g. so they can block in a system call without holding the lock).  It's non-obvious why Racket would be able to lets its interpreter functions run in parallel, other than I guess having "relatively little global interpreter state".  
> **username_6** (2023-07-02): @PeterCordes Yes, you’re right, my wording was sloppy—I’ve updated the answer to be a little more explicit. As for why Racket is able to let its interpreter run in parallel—why do you think there must be some obstacle? There is no trouble to running multiple parallel mutators as long as they have some simple protocol to yield for garbage collection.  
> **username_10** (2023-07-02): In a program originally designed to run in a single thread (just doing user-space context-switches for green threads or coroutines or whatever) you'd expect the data structures to not be thread-safe, like C++ `std::vector` (or the equivalent in languages like Java or C# which don't have undefined behaviour for data races).  If two threads append an element, in practice the way they compile to machine code, you might end up with only one total increment to the in-use size, and/or both stores might or might not be to the same element. You'd need an atomic RMW to make it safe even without realloc  
> **username_10** (2023-07-02): I don't know Racket, so maybe the restrictions on mutating objects in https://docs.racket-lang.org/foreign/Operating_System_Threads.html mean you can only update a reference, so it can be like Linux RCU (Read, Copy, Update), or something else that's not terrible for frequent small changes to large arrays.  Internal consistency between parts of one data structure, like `std::vector`'s data pointer, and its size and capacity (actually pointers to the ends), can't be maintained just by ordering of operations; lock-free data structures require careful design and are slower with just 1 thread.  
> **username_6** (2023-07-02): @PeterCordes Yes, as I said in the answer, OS threads are unsafe, and you must stick to a restricted subset for it to be safe. But those restrictions only come into play when two OS threads *interact*; there is no issue with simply interpreting code in parallel if they avoid mutating the same memory. And as the docs say, you can synchronize across OS threads with explicit OS-level locks. This is all quite different from a system with a GIL.  
> **username_10** (2023-07-02): Oh, yeah parallel mutators on *separate* objects is fairly easy, I thought you were asking what obstacles I thought would exist for threads mutating shared data.  The doc says "but the visibility of mutations to other threads is unspecified except as synchronized ...", which implies it won't crash the interpreter by corrupting objects, they just might not be visible in any order you expect.  But yes, if the threading model fully forbids data races in Ratchet programs, that leaves locking up to the Ratchet code, no need for a GIL built-in to the interpreter.  
> **username_6** (2023-07-02): @PeterCordes Yes, that was the distinction I was intending to convey. Feel free to reword my answer slightly if you think that part could be made clearer somehow!  
> **username_10** (2023-07-02): I don't know Ratchet at all, so I don't want to say anything incorrect about it.  And it still sounds to me like https://docs.racket-lang.org/foreign/Operating_System_Threads.html doesn't strongly forbid different threads mutating the same object, you just aren't guaranteed what the final result will be.  If that's allowed to crash the interpreter (via C data-race UB for an interpreter written in C), then yeah your answer makes sense.  So maybe say explicitly that it's not just about data visibility across threads for the Ratchet code, it's exposing the possibility of interpreter data-race UB.  
---
### Answer by username_8 on 2023-07-02. Score: 3
Needing a global lock is a consequence of an ad hoc approach to implementing an interpreter, and is perfectly avoidable.

If your language have, say, dynamic scoping, any thread of the interpreter may want to query a global identifier table at any moment. GIL is an easy way to ensure there are no race conditions in accessing the table. This could have been avoided, firstly, by not designing a language with dynamic scoping (really, why would anyone do it?), and, secondly, if you're really hell-bent on having dynamic name lookup in runtime, by using discrete lock-free data structures for such tables.

And dynamic name lookup is just one of the parts of the CPython implementation that needs to be protected from race conditions.

To summarise: needing a GIL is pretty much a consequence of poor choices in both language design and in interpreter implementation.
> **Comments:**
> **username_4** (2023-07-02): I think this answer is using the term "[dynamic scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Dynamic_scope)" to mean something different to what it is normally understood as meaning. I would think that dynamic scoping, i.e. resolving names by traversing the call stack instead of lexically, would mean different threads (each having their own call stack) need not contend for a any shared resource when resolving names.  
> **username_8** (2023-07-02): @kaya3-supportthestrike Python, despite claims of the opposite, use dynamic scoping and late binding in this very sense. Any thread can look.up outer definitions of the same binding. Not that different from elisp and its ilk.  
> **username_4** (2023-07-02): Python has lexical scoping for most variables, the exceptions being global variables defined dynamically through the `globals()` dictionary, and arguably the ability to walk the call-stack at runtime using the `inspect` module. If either of those is what you mean by "dynamic scope" then you should make this clear (though I don't see how either lead to a resource contention between threads that lexical scoping wouldn't have). Python of course has late binding, but that is a different thing.  
> **username_6** (2023-07-02): I don’t think this answer accurately describes why CPython has retained its GIL, or at least I think it paints a misleadingly incomplete picture. If the GIL were purely an implementation problem, it would be removed by now, but the GIL has become a part of CPython’s interface.  
> **username_8** (2023-07-02): Yes, I said that name lookup is just one of the things GIL was needed for, CPython grew dependent on it in many other aspects to an extend that removing GIL will be a breaking change in an interface, so now it is virtually impossible to fo. As I said, bad design choices early on, doomed to linger forever now.  
> **username_9** (2023-07-04): "Any thread can look.up outer definitions of the same binding." - No, this conflates different meanings of "outer", and thus gets the meaning of "dynamic scoping" wrong. Python enables late binding to names defined in enclosing *scopes*, which is *lexical* scoping. *Dynamic* scoping is late binding to names defined in the *calling context*.  
---
