|
| 1 | +# Concurrency Guide |
| 2 | + |
| 3 | +This is a guide to thinking about concurrency in the native cruby source code, whether that's |
| 4 | +contributing to Ruby by writing C or Rust. This doesn't touch on native extensions, only the core |
| 5 | +language. It will go over: |
| 6 | + |
| 7 | +* How to use the VM lock, and what you can and can't do when you've acquired this lock. |
| 8 | +* What you can and can't do when you've acquired other native locks. |
| 9 | +* The difference between the VM lock and the GVL. |
| 10 | +* How to write code that is ractor safe. |
| 11 | +* What a VM barrier is and when to use it. |
| 12 | +* The lock hierarchy of some important locks. |
| 13 | +* How ruby interrupt handling works. |
| 14 | +* What happens when IO is performed through ruby. |
| 15 | +* The timer thread and what it's responsible for. |
| 16 | + |
| 17 | + |
| 18 | +## The VM Lock |
| 19 | + |
| 20 | +There's only one VM lock and its for critical sections that can only be entered by one ractor at a time. |
| 21 | +Without ractors, the VM lock is useless. It does not stop all ractors from running, as ractors can run |
| 22 | +without trying to acquire this lock. If you're updating global (shared) data between ractors and aren't using |
| 23 | +atomics, you need to use this lock. When you take the VM lock, there are things you can and can't do during |
| 24 | +your (hopefully brief) critical section: |
| 25 | + |
| 26 | +You can (as long as no other locks are also held): |
| 27 | + |
| 28 | +* Create ruby objects, call ruby_xmalloc, etc. |
| 29 | + |
| 30 | +* Raise exceptions or use `EC_JUMP_TAG`. The lock will automatically be unlocked depending on how far up the call stack you locked it, and how far you're jumping to. |
| 31 | + |
| 32 | +* Check ruby interrupts. Since raising exceptions can pop ruby frames and popping ruby frames checks interrupts, you are allowed. |
| 33 | + |
| 34 | +You can't: |
| 35 | + |
| 36 | +* Context switch to another thread or ractor. This is important, as many things can cause context switches including: |
| 37 | + |
| 38 | + * Calling any ruby method through, for example, `rb_funcall`. If you execute ruby code with the VM lock, a context switch |
| 39 | + could happen. This also applies to ruby methods defined in C. You also can't check if objects respond to methods or set |
| 40 | + constants (these can both call ruby methods). |
| 41 | + |
| 42 | + * Calling `rb_nogvl` |
| 43 | + |
| 44 | + * Enter any blocking operation managed by ruby. This will context switch to another ruby thread. |
| 45 | + |
| 46 | + * Calling a ruby-level mechanism that can context switch, like `rb_mutex_lock`. |
| 47 | + |
| 48 | +Internally, this is the `vm->ractor.sync.lock`. |
| 49 | + |
| 50 | +## Other Locks |
| 51 | + |
| 52 | +All native locks that aren't the VM lock share a more strict set of rules for what's allowed during the critical section. By native locks, we mean |
| 53 | +anything that uses `rb_native_mutex_lock`. Some important examples are the `interrupt_lock` and the ractor scheduling lock. |
| 54 | + |
| 55 | +You can't: |
| 56 | + |
| 57 | +* Raise exceptions or use `EC_JUMP_TAG` if it jumps out of the critical section. |
| 58 | + |
| 59 | +* Context switch. See the `VM Lock` section for more info. |
| 60 | + |
| 61 | +* Check interrupts. Doing so can raise exceptions or context switch. |
| 62 | + |
| 63 | +* Allocate memory through ruby. This includes creating ruby objects or using `ruby_xmalloc` or `st_insert`. |
| 64 | + |
| 65 | + |
| 66 | + |
0 commit comments