type suspendGState … // suspendG suspends goroutine gp at a safe-point and returns the // state of the suspended goroutine. The caller gets read access to // the goroutine until it calls resumeG. // // It is safe for multiple callers to attempt to suspend the same // goroutine at the same time. The goroutine may execute between // subsequent successful suspend operations. The current // implementation grants exclusive access to the goroutine, and hence // multiple callers will serialize. However, the intent is to grant // shared read access, so please don't depend on exclusive access. // // This must be called from the system stack and the user goroutine on // the current M (if any) must be in a preemptible state. This // prevents deadlocks where two goroutines attempt to suspend each // other and both are in non-preemptible states. There are other ways // to resolve this deadlock, but this seems simplest. // // TODO(austin): What if we instead required this to be called from a // user goroutine? Then we could deschedule the goroutine while // waiting instead of blocking the thread. If two goroutines tried to // suspend each other, one of them would win and the other wouldn't // complete the suspend until it was resumed. We would have to be // careful that they couldn't actually queue up suspend for each other // and then both be suspended. This would also avoid the need for a // kernel context switch in the synchronous case because we could just // directly schedule the waiter. The context switch is unavoidable in // the signal case. // //go:systemstack func suspendG(gp *g) suspendGState { … } // resumeG undoes the effects of suspendG, allowing the suspended // goroutine to continue from its current safe-point. func resumeG(state suspendGState) { … } // canPreemptM reports whether mp is in a state that is safe to preempt. // // It is nosplit because it has nosplit callers. // //go:nosplit func canPreemptM(mp *m) bool { … } // asyncPreempt saves all user registers and calls asyncPreempt2. // // When stack scanning encounters an asyncPreempt frame, it scans that // frame and its parent frame conservatively. // // asyncPreempt is implemented in assembly. func asyncPreempt() //go:nosplit func asyncPreempt2() { … } var asyncPreemptStack … func init() { … } // wantAsyncPreempt returns whether an asynchronous preemption is // queued for gp. func wantAsyncPreempt(gp *g) bool { … } // isAsyncSafePoint reports whether gp at instruction PC is an // asynchronous safe point. This indicates that: // // 1. It's safe to suspend gp and conservatively scan its stack and // registers. There are no potentially hidden pointer values and it's // not in the middle of an atomic sequence like a write barrier. // // 2. gp has enough stack space to inject the asyncPreempt call. // // 3. It's generally safe to interact with the runtime, even if we're // in a signal handler stopped here. For example, there are no runtime // locks held, so acquiring a runtime lock won't self-deadlock. // // In some cases the PC is safe for asynchronous preemption but it // also needs to adjust the resumption PC. The new PC is returned in // the second result. func isAsyncSafePoint(gp *g, pc, sp, lr uintptr) (bool, uintptr) { … }