diff options
Diffstat (limited to 'libgo/runtime/sigqueue.goc')
-rw-r--r-- | libgo/runtime/sigqueue.goc | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/libgo/runtime/sigqueue.goc b/libgo/runtime/sigqueue.goc new file mode 100644 index 000000000..b5f2954bc --- /dev/null +++ b/libgo/runtime/sigqueue.goc @@ -0,0 +1,113 @@ +// 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 and cannot use locks) +// so the handler communicates with a processing goroutine +// via struct sig, below. +// +// Ownership for sig.Note passes back and forth between +// the signal handler and the signal goroutine in rounds. +// The initial state is that sig.note is cleared (setup by siginit). +// At the beginning of each round, mask == 0. +// The round goes through three stages: +// +// (In parallel) +// 1a) One or more signals arrive and are handled +// by sigsend using cas to set bits in sig.mask. +// The handler that changes sig.mask from zero to non-zero +// calls notewakeup(&sig). +// 1b) Sigrecv calls notesleep(&sig) to wait for the wakeup. +// +// 2) Having received the wakeup, sigrecv knows that sigsend +// will not send another wakeup, so it can noteclear(&sig) +// to prepare for the next round. (Sigsend may still be adding +// signals to sig.mask at this point, which is fine.) +// +// 3) Sigrecv uses cas to grab the current sig.mask and zero it, +// triggering the next round. +// +// The signal handler takes ownership of the note by atomically +// changing mask from a zero to non-zero value. It gives up +// ownership by calling notewakeup. The signal goroutine takes +// ownership by returning from notesleep (caused by the notewakeup) +// and gives up ownership by clearing mask. + +package runtime +#include "config.h" +#include "runtime.h" +#include "malloc.h" +#include "defs.h" + +static struct { + Note; + uint32 mask; + bool inuse; +} sig; + +void +siginit(void) +{ + noteclear(&sig); +} + +// Called from sighandler to send a signal back out of the signal handling thread. +bool +__go_sigsend(int32 s) +{ + uint32 bit, mask; + + if(!sig.inuse) + return false; + bit = 1 << s; + for(;;) { + mask = sig.mask; + if(mask & bit) + break; // signal already in queue + if(runtime_cas(&sig.mask, mask, mask|bit)) { + // Added to queue. + // Only send a wakeup for the first signal in each round. + if(mask == 0) + notewakeup(&sig); + break; + } + } + return true; +} + +// Called to receive a bitmask of queued signals. +func Sigrecv() (m uint32) { + // runtime·entersyscall(); + notesleep(&sig); + // runtime·exitsyscall(); + noteclear(&sig); + for(;;) { + m = sig.mask; + if(runtime_cas(&sig.mask, m, 0)) + break; + } +} + +func Signame(sig int32) (name String) { + const char* s = NULL; + char buf[100]; +#if defined(HAVE_STRSIGNAL) + s = strsignal(sig); +#endif + if (s == NULL) { + snprintf(buf, sizeof buf, "signal %d", sig); + s = buf; + } + int32 len = __builtin_strlen(s); + unsigned char *data = runtime_mallocgc(len, RefNoPointers, 0, 0); + __builtin_memcpy(data, s, len); + name.__data = data; + name.__length = len; +} + +func Siginit() { + sig.inuse = true; // enable reception of signals; cannot disable +} |