Basically, issue a syscall that acquires the PC of all threads. When none of the PCs are in the critical section, then you can garbage collect old objects.
Hmm, if I'm understanding right, rather than passively checking whether any of the other threads are in the critical section, it's actually forcing all the threads to undergo a context switch so that the PC (instruction pointer) can be read. So long as the context switch is prevented until the thread is out of the critical section, the actual value of the PC is irrelevant so long as the call succeeds:
If you have a lot of threads running, this could get really expensive. I presume it works, but this doesn't seem like an efficient approach unless your "writes" are extremely rare relative to the number reads and number of threads.
I haven't been able to find source for thread_get_state(), though. I don't think there is any way to get the current PC from a core without an interrupt? I presume it at least reads the PC for sleeping threads without needing to wake them?
It absolutely can get expensive with lots of threads running. And you are also right that this code ends up making each thread reach a synchronization point. Luckily Objective-C is targeted towards clients which generally don't have a bazillion threads running so it's generally not a problem.
There was talk at some point of adding a single syscall that got all the PCs of all the threads at once to cut down on the overhead, but it looks like that still hasn't happened.
What's going on here is something like this:
1. objc_msgSend is Obj-C's method dispatcher, and it depends on method caches to make method lookup faster.
2. Sometimes the method caches become out of date. For instance, maybe the method cache filled up and the Obj-C runtime needs to allocate a larger method cache.
3. This old method cache has to be GC'd at some point, so it's added to a freelist.
4. This GC function (the "write" you're talking about) for these caches runs once the size of outdated method caches grows beyond a certain threshold (garbage_threshold in the code). So it shouldn't run too often. The GC function works by checking if any thread is currently within objc_msgSend. If no function is in objc_msgSend, then the runtime is sure that none of the method caches on the free list is in use.
5. It is definitely optimized for reads. The "read" in this case is literally a method dispatch so it happens all the time, and it's important for the read to be lock-free.
https://github.com/opensource-apple/objc4/blob/master/runtim...
Basically, issue a syscall that acquires the PC of all threads. When none of the PCs are in the critical section, then you can garbage collect old objects.