github.com/sitano/gsysint@v0.0.0-20190607084937-69a4f3233e4e/README.md (about) 1 # gsysint 2 3 Golang (as of 1.12.5) runtime internals that gives you an access to internal scheduling primitives. 4 (for learning purposes) 5 6 Features 7 ======== 8 9 * `g` and `m` internal structures access (read goroutine id) 10 * goroutines native parking / unparking 11 * internal spin lock 12 13 Examples 14 ======= 15 16 Get goroutine id: 17 18 g.CurG().GoID 19 20 or 21 22 GIDFromStackTrace() 23 24 Park goroutine the simple way: 25 26 var p Park 27 p.Set() 28 p.Park(nil) 29 30 Park goroutine detailed (simple): 31 32 w.Add(1) 33 go func() { 34 p.Set() 35 p.Park(nil) 36 w.Done() 37 }() 38 runtime.Gosched() 39 // unpark goroutine and mark as ready 40 p.Ready() 41 w.Wait() 42 43 Park goroutine harder with mutex release on park: 44 45 var gp unsafe.Pointer 46 47 w := sync.WaitGroup{} 48 w.Add(1) 49 50 l := &g.Mutex{} 51 go func() { 52 atomic.StorePointer(&gp, g.GetG()) 53 Lock(l) 54 // park 55 GoParkUnlock(l, g.WaitReasonZero, trace.TraceEvNone, 1) // actual park 56 w.Done() 57 }() 58 59 runtime.Gosched() 60 61 if gp == nil { 62 t.Fatalf("GetG() returned nil pointer to the g structure") 63 } 64 65 Lock(l) 66 // unpark goroutine and mark as ready 67 GoReady((*g.G)(gp), 1) 68 Unlock(l) 69 70 w.Wait() 71 72 Scheduling details 73 ================== 74 75 I am not going to cover go [scheduler]( 76 https://github.com/golang/go/blob/7bc40ffb05d8813bf9b41a331b45d37216f9e747/src/runtime/proc.go#L2022) 77 in [details](https://golang.org/s/go11sched) here. 78 79 The scheduler's job is to distribute ready-to-run goroutines 80 over worker threads. Main concepts: 81 82 - G - goroutine. 83 - M - worker thread, or machine. 84 - P - processor, a resource that is required to execute Go code. 85 M must have an associated P to execute Go code, however it can be 86 blocked or in a syscall w/o an associated P. 87 88 Runtime defined as a tuple of (m0, g0). Almost everything interested is happening in 89 the context of g0 (like scheduling, gc setup, etc). Usually switch from an arbitrary 90 goroutine to the g0 can happen in the case of: resceduling, goroutine parking, exiting / 91 finishing, syscalling, recovery from panic and maybe other cases I did not managed 92 to find with grep. In order to do a switch runtime calls [mcall]( 93 https://github.com/golang/go/blob/7bc40ffb05d8813bf9b41a331b45d37216f9e747/src/runtime/stubs.go#L34) 94 function. 95 96 `mcall` switches from the g to the g0 stack and invokes fn(g), where g is the 97 goroutine that made the call. mcall can only be called from g stacks (not g0, not gsignal). 98 99 Parking 100 ======= 101 102 A goroutine can be took off the scheduling for a while to keep resources free until 103 some condition met. It called `parking`. Often and almost always go runtime uses 104 this method to implement various synchronisation primitives behavior and implementation. 105 106 * `gopark` puts the current goroutine into a waiting state and calls unlockf. 107 If unlockf returns false, the goroutine is resumed. Implementation execute scheduling 108 of the next goroutine forgetting about existence of current one, until it will 109 be brought back by `goready`. Thus, schedule do not waste resources for goroutines 110 waiting some external event to continue its execution. This used exactly instead 111 of spinning cpu; 112 * `goparkunlock` puts the current goroutine into a waiting state and unlocks the lock 113 by calling `parkunlock_c` over internal `mutex` object. If unlockf returns false, 114 the goroutine is resumed. Implemented via; 115 * `goready / ready` mark gp ready to run. Naturally `unpark`. Places a goroutine 116 into the next run slot (via `runqput`) or to the local run queue (size 256) if 117 its contended. If the local run queue is full, runnext puts g on the global queue. 118 119 Parking used in implementations of io, gc, timers, finalizers, channels, panics, tracer, 120 semaphore and select. 121 122 It effectively used for implementations of sync primitives when the moment of acquisition 123 or releasing the lock is known in advance (instead of blind spinning) (by some external 124 event i.e.). 125 126 If there was no contention on next run slot on the `p`, `goready` can effectively 127 bring goroutine back to life omitting long passing through the run queues what 128 intended to minimize latency. 129 130 Author 131 === 132 133 Ivan Prisyazhnyy, @john.koepi, 2019