| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // This file implements runtime support for signal handling. |
| // |
| // Most synchronization primitives are not available from |
| // the signal handler (it cannot block, allocate memory, or use locks) |
| // so the handler communicates with a processing goroutine |
| // via struct sig, below. |
| // |
| // sigsend() is called by the signal handler to queue a new signal. |
| // signal_recv() is called by the Go program to receive a newly queued signal. |
| // Synchronization between sigsend() and signal_recv() is based on the sig.state |
| // variable. It can be in 3 states: 0, HASWAITER and HASSIGNAL. |
| // HASWAITER means that signal_recv() is blocked on sig.Note and there are no |
| // new pending signals. |
| // HASSIGNAL means that sig.mask *may* contain new pending signals, |
| // signal_recv() can't be blocked in this state. |
| // 0 means that there are no new pending signals and signal_recv() is not blocked. |
| // Transitions between states are done atomically with CAS. |
| // When signal_recv() is unblocked, it resets sig.Note and rechecks sig.mask. |
| // If several sigsend()'s and signal_recv() execute concurrently, it can lead to |
| // unnecessary rechecks of sig.mask, but must not lead to missed signals |
| // nor deadlocks. |
| |
| package runtime |
| #include "runtime.h" |
| #include "defs_GOOS_GOARCH.h" |
| #include "os_GOOS.h" |
| #include "cgocall.h" |
| #include "../../cmd/ld/textflag.h" |
| |
| static struct { |
| Note; |
| uint32 mask[(NSIG+31)/32]; |
| uint32 wanted[(NSIG+31)/32]; |
| uint32 state; |
| bool inuse; |
| } sig; |
| |
| enum { |
| HASWAITER = 1, |
| HASSIGNAL = 2, |
| }; |
| |
| // Called from sighandler to send a signal back out of the signal handling thread. |
| bool |
| runtime·sigsend(int32 s) |
| { |
| uint32 bit, mask, old, new; |
| |
| if(!sig.inuse || s < 0 || s >= 32*nelem(sig.wanted) || !(sig.wanted[s/32]&(1U<<(s&31)))) |
| return false; |
| bit = 1 << (s&31); |
| for(;;) { |
| mask = sig.mask[s/32]; |
| if(mask & bit) |
| break; // signal already in queue |
| if(runtime·cas(&sig.mask[s/32], mask, mask|bit)) { |
| // Added to queue. |
| // Only send a wakeup if the receiver needs a kick. |
| for(;;) { |
| old = runtime·atomicload(&sig.state); |
| if(old == HASSIGNAL) |
| break; |
| if(old == HASWAITER) |
| new = 0; |
| else // if(old == 0) |
| new = HASSIGNAL; |
| if(runtime·cas(&sig.state, old, new)) { |
| if (old == HASWAITER) |
| runtime·notewakeup(&sig); |
| break; |
| } |
| } |
| break; |
| } |
| } |
| return true; |
| } |
| |
| // Called to receive the next queued signal. |
| // Must only be called from a single goroutine at a time. |
| func signal_recv() (m uint32) { |
| static uint32 recv[nelem(sig.mask)]; |
| uint32 i, old, new; |
| |
| for(;;) { |
| // Serve from local copy if there are bits left. |
| for(i=0; i<NSIG; i++) { |
| if(recv[i/32]&(1U<<(i&31))) { |
| recv[i/32] ^= 1U<<(i&31); |
| m = i; |
| goto done; |
| } |
| } |
| |
| // Check and update sig.state. |
| for(;;) { |
| old = runtime·atomicload(&sig.state); |
| if(old == HASWAITER) |
| runtime·throw("inconsistent state in signal_recv"); |
| if(old == HASSIGNAL) |
| new = 0; |
| else // if(old == 0) |
| new = HASWAITER; |
| if(runtime·cas(&sig.state, old, new)) { |
| if (new == HASWAITER) { |
| runtime·notetsleepg(&sig, -1); |
| runtime·noteclear(&sig); |
| } |
| break; |
| } |
| } |
| |
| // Get a new local copy. |
| for(i=0; i<nelem(sig.mask); i++) { |
| for(;;) { |
| m = sig.mask[i]; |
| if(runtime·cas(&sig.mask[i], m, 0)) |
| break; |
| } |
| recv[i] = m; |
| } |
| } |
| |
| done:; |
| // goc requires that we fall off the end of functions |
| // that return values instead of using our own return |
| // statements. |
| } |
| |
| // Must only be called from a single goroutine at a time. |
| func signal_enable(s uint32) { |
| if(!sig.inuse) { |
| // The first call to signal_enable is for us |
| // to use for initialization. It does not pass |
| // signal information in m. |
| sig.inuse = true; // enable reception of signals; cannot disable |
| runtime·noteclear(&sig); |
| return; |
| } |
| |
| if(s >= nelem(sig.wanted)*32) |
| return; |
| sig.wanted[s/32] |= 1U<<(s&31); |
| runtime·sigenable(s); |
| } |
| |
| // Must only be called from a single goroutine at a time. |
| func signal_disable(s uint32) { |
| if(s >= nelem(sig.wanted)*32) |
| return; |
| sig.wanted[s/32] &= ~(1U<<(s&31)); |
| runtime·sigdisable(s); |
| } |
| |
| // This runs on a foreign stack, without an m or a g. No stack split. |
| #pragma textflag NOSPLIT |
| void |
| runtime·badsignal(uintptr sig) |
| { |
| runtime·cgocallback((void (*)(void))runtime·sigsend, &sig, sizeof(sig)); |
| } |