I see the terms 'g' and 'p' used a lot, never really 'm' but I may as well get it out of the way now. These structures are defined in
runtime2.go. The main reason I'm asking specifically about these structures is that they are used almost everywhere in the runtime, and I need to know exactly how they work before I can proceed.
What is the 'g' structure?
From what I've heard before, the 'g' is a structure used to keep track of a Goroutine's stack (and StackGuard to prevent buffer overflows) and state, kept in TLS. So I'm assuming this structure, pretty much is how a Goroutine is implemented in Go.
However, I can tell whatever this structure does is MASSIVE, and I want to know precisely how it works. It's not like there's an article describing how the runtime works at a low-level (... or is there?), hence I have to ask it here.
1) How precisely does the 'g' interact with the Garbage Collector? From what I have read (and its been a lot so bear with me if I miss a few details), it has stack barriers in place to help during the _GCmark and _GCtermination phase. These stack barriers effectively overwrite the original return address to a "stack barrier trampoline", which after researching what a trampoline is and looking at further documentation, it basically jumps to a function, or a 'thunk' (of which the actual details I am very iffy on) which effectively keeps track of what stack barriers have been hit. This is so the GC knows precisely what areas of the stack to re-scan, for efficiency reasons of course.
Now, all of this revolves around "GC Safe Points", which is pretty much where the GC deems it safe to analyze the "true roots", before allowing the mutators (I.E Goroutines) to continue. However, these "Safe Points" apparently, as described in the synchronization section of runtime/mstkbar.go, are visited only when installing stack barriers(?). Am I correct in that these safe-points are visited from transition to phase GCoff to GCscan only? It seems that "Stop-The-World" is done this way? (Making all 'g' Goroutines stop at safe-points until it is ready to proceed past GCscan phase)
2) Am I right in assuming that the 'g' is quite literally the only thread structure available to Go? In the introduction for the GC, it states "The GC runs concurrently with mutator threads", and the 'g' structures holds data relevant to the GC mutator assist algorithm.
3) How exactly does the 'g' help in race detection? How can you detect race conditions based on the 'g', or with the help of the 'g'? It has a field called 'raceignore', which if true, ignores race detection events. Am I correct in assuming that if a race condition has been detected, it will throw a signal to the specific thread (The 'g')?
4) How are program counters used in general? They are everywhere, but I've no idea how they work. My limited understanding, the PC can be used as a direct offset into the PC-Value table to receive meta-data that makes stack-unwinding easier/possible. However, this seems to only apply to a base pointer (or frame pointer), so I assume that the other PC is used as the return address, or for debugging purposes? I.E, 'gopc' field is the PC of the Go statement that created the goroutine, hence I'm assuming that is just used to debug where it was created. 'startpc' field is PC of the actual function to call, which can be looked up in the PC-Value table.
What is the 'p' structure?
Now its beginning to get a bit more difficult to understand. 'g' could stand for goroutine, but 'p'? Does 'p' stand for 'process'? It contains a lot of information a process should have, so maybe. It has with it a '
p.id' => 'pid', so it starts to click a bit more.
Oh whats this? The 'p' has a run-queue for it's goroutines, so maybe it is a process scheduling it's threads... except on some systems, I.E Linux, there is no difference between a Thread and a Process. Hence would it be a thread scheduling other threads then? Should I have a look further at the scheduler Go uses?
1) Is 'p' a process with it's own scheduler for the threads/Goroutines it spawns?
2) How does this work on systems where there is no difference between a process and a thread besides for whether memory is shared or how "lightweight" it is?
3) What precisely does it cache with mcache? Which leads to my next question...
What is the 'm' structure?
Does the 'm' stand for... memory? It would kind of make sense since ti consists of a lot of buffers, but that's not right... it has 'procid'... hm, maybe this is the actual process? It can block, has directly access to TLS, and... what's this? A 'thread handle'? Field 'thread' is a pointer to a thread... so 'g' can't be a thread in and of itself, right? Otherwise it would use a guintptr...
1) Is the 'm' an actual process/thread? What relation does it have to everything else?