| /* Copyright (C) 1996-2014 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <errno.h> |
| #include <hurd.h> |
| #include <hurd/signal.h> |
| #include <hurd/msg.h> |
| #include <hurd/sigpreempt.h> |
| #include <assert.h> |
| |
| /* Select any of pending signals from SET or wait for any to arrive. */ |
| int |
| __sigwait (const sigset_t *set, int *sig) |
| { |
| struct hurd_sigstate *ss; |
| sigset_t mask, ready; |
| int signo = 0; |
| struct hurd_signal_preemptor preemptor; |
| jmp_buf buf; |
| mach_port_t wait; |
| mach_msg_header_t msg; |
| |
| sighandler_t |
| preempt_fun (struct hurd_signal_preemptor *pe, |
| struct hurd_sigstate *ss, |
| int *sigp, |
| struct hurd_signal_detail *detail) |
| { |
| if (signo) |
| /* We've already been run; don't interfere. */ |
| return SIG_ERR; |
| |
| signo = *sigp; |
| |
| /* Make sure this is all kosher */ |
| assert (__sigismember (&mask, signo)); |
| |
| /* Make sure this signal is unblocked */ |
| __sigdelset (&ss->blocked, signo); |
| |
| return pe->handler; |
| } |
| |
| void |
| handler (int sig) |
| { |
| assert (sig == signo); |
| longjmp (buf, 1); |
| } |
| |
| wait = __mach_reply_port (); |
| |
| if (set != NULL) |
| /* Crash before locking */ |
| mask = *set; |
| else |
| __sigemptyset (&mask); |
| |
| ss = _hurd_self_sigstate (); |
| __spin_lock (&ss->lock); |
| |
| /* See if one of these signals is currently pending. */ |
| __sigandset (&ready, &ss->pending, &mask); |
| if (! __sigisemptyset (&ready)) |
| { |
| for (signo = 1; signo < NSIG; signo++) |
| if (__sigismember (&ready, signo)) |
| { |
| __sigdelset (&ready, signo); |
| goto all_done; |
| } |
| /* Huh? Where'd it go? */ |
| abort (); |
| } |
| |
| /* Wait for one of them to show up. */ |
| |
| if (!setjmp (buf)) |
| { |
| /* Make the preemptor */ |
| preemptor.signals = mask; |
| preemptor.first = 0; |
| preemptor.last = -1; |
| preemptor.preemptor = preempt_fun; |
| preemptor.handler = handler; |
| |
| /* Install this preemptor */ |
| preemptor.next = ss->preemptors; |
| ss->preemptors = &preemptor; |
| |
| __spin_unlock (&ss->lock); |
| |
| /* Wait. */ |
| __mach_msg (&msg, MACH_RCV_MSG, 0, sizeof (msg), wait, |
| MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
| abort (); |
| } |
| else |
| { |
| assert (signo); |
| |
| __spin_lock (&ss->lock); |
| |
| /* Delete our preemptor. */ |
| assert (ss->preemptors == &preemptor); |
| ss->preemptors = preemptor.next; |
| } |
| |
| |
| all_done: |
| spin_unlock (&ss->lock); |
| |
| __mach_port_destroy (__mach_task_self (), wait); |
| *sig = signo; |
| return 0; |
| } |
| |
| weak_alias (__sigwait, sigwait) |