Signals.c 14.1 KB
Newer Older
1
/* -----------------------------------------------------------------------------
2
 *
3
 * (c) The GHC Team, 1998-2005
4 5 6 7 8
 *
 * Signal processing / handling.
 *
 * ---------------------------------------------------------------------------*/

Simon Marlow's avatar
Simon Marlow committed
9
#include "PosixSource.h" 
10
#include "Rts.h"
Simon Marlow's avatar
Simon Marlow committed
11

12
#include "Schedule.h"
13
#include "RtsSignals.h"
Simon Marlow's avatar
Simon Marlow committed
14
#include "Signals.h"
15
#include "RtsUtils.h"
16
#include "Prelude.h"
Simon Marlow's avatar
Simon Marlow committed
17
#include "Stable.h"
18

19 20
#ifdef alpha_HOST_ARCH
# if defined(linux_HOST_OS)
21 22 23 24
#  include <asm/fpu.h>
# else
#  include <machine/fpu.h>
# endif
ken's avatar
ken committed
25 26
#endif

27 28 29 30 31 32 33 34
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif

35 36 37 38
#ifdef HAVE_ERRNO_H
# include <errno.h>
#endif

39
#include <stdlib.h>
40
#include <string.h>
41

sof's avatar
sof committed
42 43 44 45
/* This curious flag is provided for the benefit of the Haskell binding
 * to POSIX.1 to control whether or not to include SA_NOCLDSTOP when
 * installing a SIGCHLD handler. 
 */
Ian Lynagh's avatar
Ian Lynagh committed
46
HsInt nocldstop = 0;
sof's avatar
sof committed
47

48 49 50 51
/* -----------------------------------------------------------------------------
 * The table of signal handlers
 * -------------------------------------------------------------------------- */

sof's avatar
sof committed
52
#if defined(RTS_USER_SIGNALS)
53

54
/* SUP: The type of handlers is a little bit, well, doubtful... */
55
StgInt *signal_handlers = NULL; /* Dynamically grown array of signal handlers */
56 57
static StgInt nHandlers = 0;    /* Size of handlers array */

58 59
static nat n_haskell_handlers = 0;

60
/* -----------------------------------------------------------------------------
61 62
 * Allocate/resize the table of signal handlers.
 * -------------------------------------------------------------------------- */
63 64

static void
65
more_handlers(int sig)
66
{
67
    StgInt i;
68 69

    if (sig < nHandlers)
70
	return;
71

72 73
    if (signal_handlers == NULL)
	signal_handlers = (StgInt *)stgMallocBytes((sig + 1) * sizeof(StgInt), "more_handlers");
74
    else
75
	signal_handlers = (StgInt *)stgReallocBytes(signal_handlers, (sig + 1) * sizeof(StgInt), "more_handlers");
76 77

    for(i = nHandlers; i <= sig; i++)
78
	// Fill in the new slots with default actions
79
	signal_handlers[i] = STG_SIG_DFL;
80 81 82 83

    nHandlers = sig + 1;
}

84 85 86
// Here's the pipe into which we will send our signals
static int io_manager_pipe = -1;

87 88
#define IO_MANAGER_WAKEUP 0xff
#define IO_MANAGER_DIE    0xfe
89
#define IO_MANAGER_SYNC   0xfd
90

91 92 93
void
setIOManagerPipe (int fd)
{
94
    // only called when THREADED_RTS, but unconditionally
95
    // compiled here because GHC.Conc depends on it.
96
    io_manager_pipe = fd;
97 98
}

99 100 101
void
ioManagerWakeup (void)
{
102
    int r;
103 104 105
    // Wake up the IO Manager thread by sending a byte down its pipe
    if (io_manager_pipe >= 0) {
	StgWord8 byte = (StgWord8)IO_MANAGER_WAKEUP;
106 107
	r = write(io_manager_pipe, &byte, 1);
        if (r == -1) { sysErrorBelch("ioManagerWakeup: write"); }
108 109 110
    }
}

111 112 113 114 115 116 117 118 119 120 121 122 123
void
ioManagerSync (void)
{
    int r;
    // Wake up the IO Manager thread by sending a byte down its pipe
    if (io_manager_pipe >= 0) {
	StgWord8 byte = (StgWord8)IO_MANAGER_SYNC;
	r = write(io_manager_pipe, &byte, 1);
        if (r == -1) { sysErrorBelch("ioManagerSync: write"); }
    }
}

#if defined(THREADED_RTS)
124 125 126
void
ioManagerDie (void)
{
127
    int r;
128 129 130
    // Ask the IO Manager thread to exit
    if (io_manager_pipe >= 0) {
	StgWord8 byte = (StgWord8)IO_MANAGER_DIE;
131 132
	r = write(io_manager_pipe, &byte, 1);
        if (r == -1) { sysErrorBelch("ioManagerDie: write"); }
133 134
        close(io_manager_pipe);
        io_manager_pipe = -1;
135 136 137 138 139 140 141 142 143 144
    }
}

void
ioManagerStart (void)
{
    // Make sure the IO manager thread is running
    Capability *cap;
    if (io_manager_pipe < 0) {
	cap = rts_lock();
Simon Marlow's avatar
Simon Marlow committed
145
	cap = rts_evalIO(cap,&base_GHCziConc_ensureIOManagerIsRunning_closure,NULL);
146 147 148 149 150
	rts_unlock(cap);
    }
}
#endif

151
#if !defined(THREADED_RTS)
152 153 154

#define N_PENDING_HANDLERS 16

155 156
siginfo_t pending_handler_buf[N_PENDING_HANDLERS];
siginfo_t *next_pending_handler = pending_handler_buf;
157

158
#endif /* THREADED_RTS */
159

160 161 162 163 164 165
/* -----------------------------------------------------------------------------
 * Low-level signal handler
 *
 * Places the requested handler on a stack of pending handlers to be
 * started up at the next context switch.
 * -------------------------------------------------------------------------- */
166 167

static void
168 169 170
generic_handler(int sig USED_IF_THREADS,
                siginfo_t *info,
                void *p STG_UNUSED)
171
{
172
#if defined(THREADED_RTS)
173

174 175
    if (io_manager_pipe != -1)
    {
176 177 178 179 180 181 182 183 184 185
        StgWord8 buf[sizeof(siginfo_t) + 1];
        int r;

        buf[0] = sig;
        memcpy(buf+1, info, sizeof(siginfo_t));
	r = write(io_manager_pipe, buf, sizeof(siginfo_t)+1);
        if (r == -1 && errno == EAGAIN)
        {
            errorBelch("lost signal due to full pipe: %d\n", sig);
        }
186
    }
187 188 189 190
    // If the IO manager hasn't told us what the FD of the write end
    // of its pipe is, there's not much we can do here, so just ignore
    // the signal..

191
#else /* not THREADED_RTS */
192

193 194 195 196
    /* Can't call allocate from here.  Probably can't call malloc
       either.  However, we have to schedule a new thread somehow.

       It's probably ok to request a context switch and allow the
197
       scheduler to  start the handler thread, but how do we
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
       communicate this to the scheduler?

       We need some kind of locking, but with low overhead (i.e. no
       blocking signals every time around the scheduler).
       
       Signal Handlers are atomic (i.e. they can't be interrupted), and
       we can make use of this.  We just need to make sure the
       critical section of the scheduler can't be interrupted - the
       only way to do this is to block signals.  However, we can lower
       the overhead by only blocking signals when there are any
       handlers to run, i.e. the set of pending handlers is
       non-empty.
    */
       
    /* We use a stack to store the pending signals.  We can't
       dynamically grow this since we can't allocate any memory from
       within a signal handler.

       Hence unfortunately we have to bomb out if the buffer
       overflows.  It might be acceptable to carry on in certain
       circumstances, depending on the signal.  
    */

221 222 223
    memcpy(next_pending_handler, info, sizeof(siginfo_t));

    next_pending_handler++;
224

225
    // stack full?
226
    if (next_pending_handler == &pending_handler_buf[N_PENDING_HANDLERS]) {
227
	errorBelch("too many pending signals");
228
	stg_exit(EXIT_FAILURE);
229 230
    }
    
231
    contextSwitchCapability(&MainCapability);
232

233
#endif /* THREADED_RTS */
234 235 236
}

/* -----------------------------------------------------------------------------
237 238
 * Blocking/Unblocking of the user signals
 * -------------------------------------------------------------------------- */
239 240 241 242 243 244 245 246

static sigset_t userSignals;
static sigset_t savedSignals;

void
initUserSignals(void)
{
    sigemptyset(&userSignals);
247 248 249 250
#ifndef THREADED_RTS
    getStablePtr((StgPtr)&base_GHCziConc_runHandlers_closure); 
    // needed to keep runHandler alive
#endif
251 252 253 254 255
}

void
blockUserSignals(void)
{
256
    sigprocmask(SIG_BLOCK, &userSignals, &savedSignals);
257 258 259 260 261 262 263 264
}

void
unblockUserSignals(void)
{
    sigprocmask(SIG_SETMASK, &savedSignals, NULL);
}

265 266 267 268 269 270
rtsBool
anyUserHandlers(void)
{
    return n_haskell_handlers != 0;
}

271
#if !defined(THREADED_RTS)
272 273 274
void
awaitUserSignals(void)
{
275
    while (!signals_pending() && sched_state == SCHED_RUNNING) {
276 277 278
	pause();
    }
}
279
#endif
280 281

/* -----------------------------------------------------------------------------
282
 * Install a Haskell signal handler.
283 284 285 286
 *
 * We should really do this in Haskell in GHC.Conc, and share the
 * signal_handlers array with the one there.
 *
287
 * -------------------------------------------------------------------------- */
288

289
int
290
stg_sig_install(int sig, int spi, void *mask)
291
{
292
    sigset_t signals, osignals;
293 294 295
    struct sigaction action;
    StgInt previous_spi;

296 297 298
    // Block the signal until we figure out what to do
    // Count on this to fail if the signal number is invalid
    if (sig < 0 || sigemptyset(&signals) ||
299
	sigaddset(&signals, sig) || sigprocmask(SIG_BLOCK, &signals, &osignals)) {
300 301 302
	return STG_SIG_ERR;
    }
    
303 304
    more_handlers(sig);

305
    previous_spi = signal_handlers[sig];
306

307 308
    action.sa_flags = 0;
    
309 310 311 312
    switch(spi) {
    case STG_SIG_IGN:
        action.sa_handler = SIG_IGN;
    	break;
313

314 315 316
    case STG_SIG_DFL:
        action.sa_handler = SIG_DFL;
    	break;
317

318
    case STG_SIG_RST:
319 320 321 322 323
        action.sa_flags |= SA_RESETHAND;
        /* fall through */
    case STG_SIG_HAN:
    	action.sa_sigaction = generic_handler;
        action.sa_flags |= SA_SIGINFO;
324
    	break;
325

326
    default:
327
        barf("stg_sig_install: bad spi");
328 329
    }

330 331
    if (mask != NULL)
        action.sa_mask = *(sigset_t *)mask;
332 333 334
    else
	sigemptyset(&action.sa_mask);

335
    action.sa_flags |= sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
336

337
    if (sigaction(sig, &action, NULL))
338
    {
339
        errorBelch("sigaction");
340
	return STG_SIG_ERR;
341
    }
sof's avatar
sof committed
342

343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
    signal_handlers[sig] = spi;

    switch(spi) {
    case STG_SIG_RST:
    case STG_SIG_HAN:
	sigaddset(&userSignals, sig);
        if (previous_spi != STG_SIG_HAN && previous_spi != STG_SIG_RST) {
            n_haskell_handlers++;
        }
    	break;

    default:
	sigdelset(&userSignals, sig);
        if (previous_spi == STG_SIG_HAN || previous_spi == STG_SIG_RST) {
            n_haskell_handlers--;
        }
        break;
360
    }
361 362 363 364 365 366 367 368

    if (sigprocmask(SIG_SETMASK, &osignals, NULL))
    {
        errorBelch("sigprocmask");
	return STG_SIG_ERR;
    }

    return previous_spi;
369 370 371
}

/* -----------------------------------------------------------------------------
372
 * Creating new threads for signal handlers.
373
 * -------------------------------------------------------------------------- */
374

375
#if !defined(THREADED_RTS)
376
void
377
startSignalHandlers(Capability *cap)
378
{
379 380 381
  siginfo_t *info;
  int sig;

382 383 384 385 386 387
  blockUserSignals();
  
  while (next_pending_handler != pending_handler_buf) {

    next_pending_handler--;

388 389 390 391 392 393 394 395 396
    sig = next_pending_handler->si_signo;
    if (signal_handlers[sig] == STG_SIG_DFL) {
        continue; // handler has been changed.
    }

    info = stgMallocBytes(sizeof(siginfo_t), "startSignalHandlers"); 
           // freed by runHandler
    memcpy(info, next_pending_handler, sizeof(siginfo_t));

397 398
    scheduleThread (cap,
	createIOThread(cap,
399
		       RtsFlags.GcFlags.initialStkSize, 
400 401 402 403 404
                       rts_apply(cap,
                                 rts_apply(cap,
                                           &base_GHCziConc_runHandlers_closure,
                                           rts_mkPtr(cap, info)),
                                 rts_mkInt(cap, info->si_signo))));
405 406 407 408
  }

  unblockUserSignals();
}
409
#endif
410

411 412 413 414
/* ----------------------------------------------------------------------------
 * Mark signal handlers during GC.
 * -------------------------------------------------------------------------- */

415
void
416
markSignalHandlers (evac_fn evac STG_UNUSED, void *user STG_UNUSED)
417
{
418
    // nothing to do
419
}
420

sof's avatar
sof committed
421
#else /* !RTS_USER_SIGNALS */
422
StgInt 
sof's avatar
sof committed
423 424 425
stg_sig_install(StgInt sig STG_UNUSED,
		StgInt spi STG_UNUSED,
		void* mask STG_UNUSED)
426
{
sof's avatar
sof committed
427 428
  //barf("User signals not supported");
  return STG_SIG_DFL;
429 430 431
}

#endif
sof's avatar
sof committed
432

sof's avatar
sof committed
433
#if defined(RTS_USER_SIGNALS)
434
/* -----------------------------------------------------------------------------
435 436 437 438 439
 * SIGINT handler.
 *
 * We like to shutdown nicely after receiving a SIGINT, write out the
 * stats, write profiling info, close open files and flush buffers etc.
 * -------------------------------------------------------------------------- */
sof's avatar
sof committed
440
static void
441
shutdown_handler(int sig STG_UNUSED)
sof's avatar
sof committed
442
{
443 444 445
    // If we're already trying to interrupt the RTS, terminate with
    // extreme prejudice.  So the first ^C tries to exit the program
    // cleanly, and the second one just kills it.
446
    if (sched_state >= SCHED_INTERRUPTING) {
sof's avatar
sof committed
447
	stg_exit(EXIT_INTERRUPTED);
448 449 450
    } else {
	interruptStgRts();
    }
sof's avatar
sof committed
451 452
}

453 454 455
/* -----------------------------------------------------------------------------
 * Install default signal handlers.
 *
sof's avatar
sof committed
456
 * The RTS installs a default signal handler for catching
457
 * SIGINT, so that we can perform an orderly shutdown.
sof's avatar
sof committed
458 459 460 461
 *
 * Haskell code may install their own SIGINT handler, which is
 * fine, provided they're so kind as to put back the old one
 * when they de-install.
ken's avatar
ken committed
462 463 464 465 466
 *
 * In addition to handling SIGINT, the RTS also handles SIGFPE
 * by ignoring it.  Apparently IEEE requires floating-point
 * exceptions to be ignored by default, but alpha-dec-osf3
 * doesn't seem to do so.
467
 * -------------------------------------------------------------------------- */
sof's avatar
sof committed
468
void
Simon Marlow's avatar
Simon Marlow committed
469
initDefaultHandlers(void)
sof's avatar
sof committed
470 471 472
{
    struct sigaction action,oact;

473
    // install the SIGINT handler
sof's avatar
sof committed
474 475 476 477
    action.sa_handler = shutdown_handler;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    if (sigaction(SIGINT, &action, &oact) != 0) {
478
	sysErrorBelch("warning: failed to install SIGINT handler");
sof's avatar
sof committed
479
    }
480

sof's avatar
sof committed
481
#if defined(HAVE_SIGINTERRUPT)
482
    siginterrupt(SIGINT, 1);	// isn't this the default? --SDM
sof's avatar
sof committed
483
#endif
484 485

    // install the SIGFPE handler
486 487 488 489 490 491 492 493 494 495

    // In addition to handling SIGINT, also handle SIGFPE by ignoring it.
    // Apparently IEEE requires floating-point exceptions to be ignored by
    // default, but alpha-dec-osf3 doesn't seem to do so.

    // Commented out by SDM 2/7/2002: this causes an infinite loop on
    // some architectures when an integer division by zero occurs: we
    // don't recover from the floating point exception, and the
    // program just generates another one immediately.
#if 0
ken's avatar
ken committed
496 497 498 499
    action.sa_handler = SIG_IGN;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    if (sigaction(SIGFPE, &action, &oact) != 0) {
500
	sysErrorBelch("warning: failed to install SIGFPE handler");
ken's avatar
ken committed
501
    }
502 503
#endif

504
#ifdef alpha_HOST_ARCH
ken's avatar
ken committed
505 506
    ieee_set_fp_control(0);
#endif
Simon Marlow's avatar
Simon Marlow committed
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533

    // ignore SIGPIPE; see #1619
    action.sa_handler = SIG_IGN;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    if (sigaction(SIGPIPE, &action, &oact) != 0) {
	sysErrorBelch("warning: failed to install SIGPIPE handler");
    }
}

void
resetDefaultHandlers(void)
{
    struct sigaction action;

    action.sa_handler = SIG_DFL;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;

    // restore SIGINT
    if (sigaction(SIGINT, &action, NULL) != 0) {
	sysErrorBelch("warning: failed to uninstall SIGINT handler");
    }
    // restore SIGPIPE
    if (sigaction(SIGPIPE, &action, NULL) != 0) {
	sysErrorBelch("warning: failed to uninstall SIGPIPE handler");
    }
534
}
sof's avatar
sof committed
535

Ian Lynagh's avatar
Ian Lynagh committed
536 537 538 539 540 541 542
void
freeSignalHandlers(void) {
    if (signal_handlers != NULL) {
        stgFree(signal_handlers);
    }
}

sof's avatar
sof committed
543
#endif /* RTS_USER_SIGNALS */