bug-glibc
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

pselect() and signal safety


From: Serge Victorov
Subject: pselect() and signal safety
Date: Fri, 01 Dec 2000 14:05:47 +0300

Hi,

  While developing some heavily multithreaded app for Linux
I was faced with a problem of interrupting select() before
it times out. Among other solutions I considered using
pselect() and sending SIGINT, but as it turned out pselect()
is not signal safe on Linux. I think there is a solution to
add atimicity to pselect() without kernel change. Read on the
details if you're interested.

  Generic pselect() looks like this (pseudocode):

pselect()
{
  sigprocmask(); /* enable some signal*/
  /**
   * The bad thing can happen here if we receive
   * a signal: select will still start. This is
   * because there's a gap between return from
   * previous syscall and entry to select syscall.
   */
  select();
  /* Receiving signal here is of much less danger */
  sigprocmask(); /* restore signal mask */
}

  We can prevent select() syscall from starting if signal
handler substitutes machine command that switches to kernel
for branch command that leads to syscall undo code and sets
syscall result to EINTR. Of course the code that actually
does the select() syscall in pselect() should be placed
into some per-thread structure.

internal_sighandler()
{
  ctx = get_thrcontext();
  if (ctx->pselect_started) {
    /**
     * Since invoking signal handler involves quite
     * some kernel work pselect() machine code should
     * have been flushed from the pipeline and instruction
     * cache (hasn't it?). So that patched code is going
     * to execute when signal is processed.
     */
    ctx->pselect_wrapper[ASMSYSCALL_OFFSET] = ASMBRANCH;
  }
  /* do everything else */
}

pselect()
{
  select_prevented = FALSE;
  ctx = get_thrcontext();
  ctx->pselect_started = TRUE;
  sigprocmask();
  if (call_select_wrapper(ctx->pselect_wrapper)) {
    /**
     * The wrapper has been modified by signal
     * handler -- return EINTR to caller
     */
    select_prevented = TRUE;
  }
  sigprocmask();
  ctx->pselect_started = FALSE;
  if (!select_prevented &&
      ctx->pselect_wrapper[ASMSYSCALL_OFFSET] != ASMSYSCALL) {
    /**
     * The signal has been received in select() or after
     * select() but before restoring signal mask. Check
     * actual select() return to decide.
     */
  }
  ctx->pselect_wrapper[ASMSYSCALL_OFFSET] = ASMSYSCALL;
}


-- 
Best regards
Serge Victorov



reply via email to

[Prev in Thread] Current Thread [Next in Thread]