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