1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
/* npth.h - a lightweight implementation of pth over pthread.
This file is part of NPTH.
NPTH is free software; you can redistribute it and/or modify it
under the terms of either
- the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at
your option) any later version.
or
- the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at
your option) any later version.
or both in parallel, as here.
NPTH 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 General Public
License for more details.
You should have received a copies of the GNU General Public License
and the GNU Lesser General Public License along with this program;
if not, see <http://www.gnu.org/licenses/>. */
/* This is a support interface to make it easier to handle signals.
The interfaces here support one (and only one) thread (here called
"main thread") in the application to monitor several signals while
selecting on filedescriptors.
First, the main thread should call npth_sigev_init. This
initializes some global data structures used to record interesting
and pending signals.
Then, the main thread should call npth_sigev_add for every signal
it is interested in observing, and finally npth_sigev_fini. This
will block the signal in the main threads sigmask. Note that these
signals should also be blocked in all other threads. Since they
are blocked in the main thread after calling npth_sigev_add, it is
recommended to call npth_sigev_add in the main thread before
creating any threads.
The function npth_sigev_sigmask is a convenient function that
returns the sigmask of the thread at time of npth_sigev_init, but
with all registered signals unblocked. It is recommended to do all
other changes to the main thread's sigmask before calling
npth_sigev_init, so that the return value of npth_sigev_sigmask can
be used in the npth_pselect invocation.
In any case, the main thread should invoke npth_pselect with a
sigmask that has all signals that should be monitored unblocked.
After npth_pselect returns, npth_sigev_get_pending can be called in
a loop until it returns 0 to iterate over the list of pending
signals. Each time a signal is returned by that function, its
status is reset to non-pending. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <signal.h>
#include <assert.h>
#include "npth.h"
/* Record events that have been noticed. */
static sigset_t sigev_pending;
/* The signal mask during normal operation. */
static sigset_t sigev_block;
/* The signal mask during pselect. */
static sigset_t sigev_unblock;
/* Registered signal numbers. Needed to iterate over sigset_t.
Bah. */
#define SIGEV_MAX 32
static int sigev_signum[SIGEV_MAX];
static int sigev_signum_cnt;
/* The internal handler which just sets a global flag. */
static void
_sigev_handler (int signum)
{
sigaddset (&sigev_pending, signum);
}
/* Start setting up signal event handling. */
void
npth_sigev_init (void)
{
sigemptyset (&sigev_pending);
pthread_sigmask (SIG_SETMASK, NULL, &sigev_block);
pthread_sigmask (SIG_SETMASK, NULL, &sigev_unblock);
}
/* Add signal SIGNUM to the list of watched signals. */
void
npth_sigev_add (int signum)
{
struct sigaction sa;
sigset_t ss;
sigemptyset(&ss);
assert (sigev_signum_cnt < SIGEV_MAX);
sigev_signum[sigev_signum_cnt++] = signum;
/* Make sure we can receive it. */
sigdelset (&sigev_unblock, signum);
sigaddset (&sigev_block, signum);
sa.sa_handler = _sigev_handler;
sa.sa_mask = ss;
sa.sa_flags = 0; /* NOT setting SA_RESTART! */
sigaction (signum, &sa, NULL);
}
/* Finish the list of watched signals. This starts to block them,
too. */
void
npth_sigev_fini (void)
{
/* Block the interesting signals. */
pthread_sigmask (SIG_SETMASK, &sigev_block, NULL);
}
/* Get the sigmask as needed for pselect. */
sigset_t *
npth_sigev_sigmask (void)
{
return &sigev_unblock;
}
/* Return the next signal event that occured. Returns if none are
left, 1 on success. */
int
npth_sigev_get_pending (int *r_signum)
{
int i;
for (i = 0; i < sigev_signum_cnt; i++)
{
int signum = sigev_signum[i];
if (sigismember (&sigev_pending, signum))
{
sigdelset (&sigev_pending, signum);
*r_signum = signum;
return 1;
}
}
return 0;
}
|