Cygwin: signal: implement signalfd
First cut of a signalfd implementation. Still TODO: Non-polling select. This should mostly work as on Linux except for missing support for some members of struct signalfd_siginfo, namely ssi_fd, ssi_band (both SIGIO/SIGPOLL, not fully implemented) and ssi_trapno (HW exception, required HW support). Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
parent
8ae26f96ae
commit
9d13a2995c
|
@ -298,6 +298,7 @@ DLL_OFILES:= \
|
|||
fhandler_raw.o \
|
||||
fhandler_registry.o \
|
||||
fhandler_serial.o \
|
||||
fhandler_signalfd.o \
|
||||
fhandler_socket.o \
|
||||
fhandler_socket_inet.o \
|
||||
fhandler_socket_local.o \
|
||||
|
|
|
@ -1333,6 +1333,7 @@ siginterrupt SIGFE
|
|||
sigismember SIGFE
|
||||
siglongjmp NOSIGFE
|
||||
signal SIGFE
|
||||
signalfd SIGFE
|
||||
significand NOSIGFE
|
||||
significandf NOSIGFE
|
||||
sigpause SIGFE
|
||||
|
|
|
@ -120,6 +120,9 @@ const _device dev_piper_storage =
|
|||
const _device dev_pipew_storage =
|
||||
{"", {FH_PIPEW}, "", exists_internal};
|
||||
|
||||
const _device dev_signalfd_storage =
|
||||
{"", {FH_SIGNALFD}, "", exists_internal};
|
||||
|
||||
const _device dev_socket_storage =
|
||||
{"", {FH_SOCKET}, "", exists_internal};
|
||||
|
||||
|
|
|
@ -72,6 +72,8 @@ enum fh_devices
|
|||
FH_DEV = FHDEV (DEV_VIRTFS_MAJOR, 193),
|
||||
FH_CYGDRIVE= FHDEV (DEV_VIRTFS_MAJOR, 192),
|
||||
|
||||
FH_SIGNALFD= FHDEV (DEV_VIRTFS_MAJOR, 13),
|
||||
|
||||
DEV_FLOPPY_MAJOR = 2,
|
||||
FH_FLOPPY = FHDEV (DEV_FLOPPY_MAJOR, 0),
|
||||
|
||||
|
@ -400,6 +402,8 @@ extern const _device dev_af_local_storage;
|
|||
extern const _device dev_af_unix_storage;
|
||||
#define af_unix_dev ((device *) &dev_af_unix_storage)
|
||||
|
||||
extern const _device dev_signalfd_storage;
|
||||
#define signalfd_dev ((device *) &dev_signalfd_storage)
|
||||
extern const _device dev_piper_storage;
|
||||
#define piper_dev ((device *) &dev_piper_storage)
|
||||
extern const _device dev_pipew_storage;
|
||||
|
|
|
@ -116,6 +116,9 @@ const _device dev_piper_storage =
|
|||
const _device dev_pipew_storage =
|
||||
{"", {FH_PIPEW}, "", exists_internal};
|
||||
|
||||
const _device dev_signalfd_storage =
|
||||
{"", {FH_SIGNALFD}, "", exists_internal};
|
||||
|
||||
const _device dev_socket_storage =
|
||||
{"", {FH_SOCKET}, "", exists_internal};
|
||||
|
||||
|
|
|
@ -575,6 +575,9 @@ fh_alloc (path_conv& pc)
|
|||
case FH_CYGDRIVE:
|
||||
fh = cnew (fhandler_cygdrive);
|
||||
break;
|
||||
case FH_SIGNALFD:
|
||||
fh = cnew (fhandler_signalfd);
|
||||
break;
|
||||
case FH_TTY:
|
||||
if (!pc.isopen ())
|
||||
{
|
||||
|
|
|
@ -1345,6 +1345,9 @@ fhandler_base::fstat (struct stat *buf)
|
|||
case FH_PIPER:
|
||||
buf->st_mode = S_IFIFO | S_IRUSR;
|
||||
break;
|
||||
case FH_SIGNALFD:
|
||||
buf->st_mode = S_IRUSR | S_IWUSR;
|
||||
break;
|
||||
default:
|
||||
buf->st_mode = S_IFCHR | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH;
|
||||
break;
|
||||
|
|
|
@ -420,6 +420,7 @@ public:
|
|||
virtual class fhandler_socket *is_socket () { return NULL; }
|
||||
virtual class fhandler_socket_wsock *is_wsock_socket () { return NULL; }
|
||||
virtual class fhandler_console *is_console () { return 0; }
|
||||
virtual class fhandler_signalfd *is_signalfd () { return NULL; }
|
||||
virtual int is_windows () {return 0; }
|
||||
|
||||
virtual void __reg3 raw_read (void *ptr, size_t& ulen);
|
||||
|
@ -2633,6 +2634,44 @@ class fhandler_procnet: public fhandler_proc
|
|||
}
|
||||
};
|
||||
|
||||
class fhandler_signalfd : public fhandler_base
|
||||
{
|
||||
sigset_t sigset;
|
||||
|
||||
public:
|
||||
fhandler_signalfd ();
|
||||
fhandler_signalfd (void *) {}
|
||||
|
||||
fhandler_signalfd *is_signalfd () { return this; }
|
||||
|
||||
char *get_proc_fd_name (char *buf);
|
||||
|
||||
int signalfd (const sigset_t *mask, int flags);
|
||||
int __reg2 fstat (struct stat *buf);
|
||||
void __reg3 read (void *ptr, size_t& len);
|
||||
|
||||
int poll ();
|
||||
|
||||
select_record *select_read (select_stuff *);
|
||||
select_record *select_write (select_stuff *);
|
||||
select_record *select_except (select_stuff *);
|
||||
|
||||
void copyto (fhandler_base *x)
|
||||
{
|
||||
x->pc.free_strings ();
|
||||
*reinterpret_cast<fhandler_signalfd *> (x) = *this;
|
||||
x->reset (this);
|
||||
}
|
||||
|
||||
fhandler_signalfd *clone (cygheap_types malloc_type = HEAP_FHANDLER)
|
||||
{
|
||||
void *ptr = (void *) ccalloc (malloc_type, 1, sizeof (fhandler_signalfd));
|
||||
fhandler_signalfd *fh = new (ptr) fhandler_signalfd (ptr);
|
||||
copyto (fh);
|
||||
return fh;
|
||||
}
|
||||
};
|
||||
|
||||
struct fhandler_nodevice: public fhandler_base
|
||||
{
|
||||
fhandler_nodevice ();
|
||||
|
@ -2672,6 +2711,7 @@ typedef union
|
|||
char __pty_master[sizeof (fhandler_pty_master)];
|
||||
char __registry[sizeof (fhandler_registry)];
|
||||
char __serial[sizeof (fhandler_serial)];
|
||||
char __signalfd[sizeof (fhandler_signalfd)];
|
||||
char __socket_inet[sizeof (fhandler_socket_inet)];
|
||||
char __socket_local[sizeof (fhandler_socket_local)];
|
||||
#ifdef __WITH_AF_UNIX
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
/* fhandler_signalfd.cc: fhandler for /proc/<pid>/fd/<desc> operations
|
||||
|
||||
This file is part of Cygwin.
|
||||
|
||||
This software is a copyrighted work licensed under the terms of the
|
||||
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
||||
details. */
|
||||
|
||||
#include "winsup.h"
|
||||
#include "path.h"
|
||||
#include "fhandler.h"
|
||||
#include "pinfo.h"
|
||||
#include "dtable.h"
|
||||
#include "cygheap.h"
|
||||
#include "sigproc.h"
|
||||
#include <cygwin/signal.h>
|
||||
#include <sys/signalfd.h>
|
||||
|
||||
fhandler_signalfd::fhandler_signalfd () :
|
||||
fhandler_base (),
|
||||
sigset (0)
|
||||
{
|
||||
}
|
||||
|
||||
char *
|
||||
fhandler_signalfd::get_proc_fd_name (char *buf)
|
||||
{
|
||||
return strcpy (buf, "anon_inode:[signalfd]");
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_signalfd::signalfd (const sigset_t *mask, int flags)
|
||||
{
|
||||
__try
|
||||
{
|
||||
sigset = *mask & ~(SIGKILL | SIGSTOP);
|
||||
}
|
||||
__except (EINVAL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
__endtry
|
||||
if (flags & SFD_NONBLOCK)
|
||||
set_nonblocking (true);
|
||||
if (flags & SFD_CLOEXEC)
|
||||
set_close_on_exec (true);
|
||||
if (get_unique_id () == 0)
|
||||
{
|
||||
nohandle (true);
|
||||
set_unique_id ();
|
||||
set_ino (get_unique_id ());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __reg2
|
||||
fhandler_signalfd::fstat (struct stat *buf)
|
||||
{
|
||||
int ret = fhandler_base::fstat (buf);
|
||||
if (!ret)
|
||||
{
|
||||
buf->st_dev = FH_SIGNALFD;
|
||||
buf->st_ino = get_unique_id ();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void
|
||||
copy_siginfo_to_signalfd (struct signalfd_siginfo *sfd,
|
||||
const siginfo_t * const si)
|
||||
{
|
||||
sfd->ssi_signo = si->si_signo;
|
||||
sfd->ssi_errno = si->si_errno;
|
||||
sfd->ssi_code = si->si_code;
|
||||
sfd->ssi_pid = si->si_pid;
|
||||
sfd->ssi_uid = si->si_uid;
|
||||
sfd->ssi_fd = -1;
|
||||
sfd->ssi_tid = si->si_tid;
|
||||
sfd->ssi_band = 0;
|
||||
sfd->ssi_overrun = si->si_overrun;
|
||||
sfd->ssi_trapno = 0;
|
||||
sfd->ssi_status = si->si_status;
|
||||
sfd->ssi_int = si->si_value.sival_int;
|
||||
sfd->ssi_ptr = (uint64_t) si->si_value.sival_ptr;
|
||||
sfd->ssi_utime = si->si_utime;
|
||||
sfd->ssi_stime = si->si_stime;
|
||||
sfd->ssi_addr = (uint64_t) si->si_addr;
|
||||
}
|
||||
|
||||
void __reg3
|
||||
fhandler_signalfd::read (void *ptr, size_t& len)
|
||||
{
|
||||
const LARGE_INTEGER poll = { QuadPart : 0 };
|
||||
siginfo_t si;
|
||||
int ret, old_errno;
|
||||
size_t curlen = 0;
|
||||
signalfd_siginfo *sfd_ptr = (signalfd_siginfo *) ptr;
|
||||
|
||||
if (len < sizeof (struct signalfd_siginfo))
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
len = (size_t) -1;
|
||||
return;
|
||||
}
|
||||
old_errno = get_errno ();
|
||||
do
|
||||
{
|
||||
/* Even when read is blocking, only one pending signal is actually
|
||||
required to return. Subsequently use sigtimedwait to just poll
|
||||
if some more signal is available. */
|
||||
ret = sigwait_common (&sigset, &si, (is_nonblocking () || curlen)
|
||||
? (PLARGE_INTEGER) &poll : NULL);
|
||||
if (ret == -1)
|
||||
{
|
||||
if (curlen == 0)
|
||||
{
|
||||
if (get_errno () == EINTR && curlen == 0)
|
||||
continue;
|
||||
set_errno (old_errno);
|
||||
}
|
||||
len = curlen ?: (size_t) -1;
|
||||
return;
|
||||
}
|
||||
__try
|
||||
{
|
||||
copy_siginfo_to_signalfd (sfd_ptr, &si);
|
||||
}
|
||||
__except (EFAULT)
|
||||
{
|
||||
len = (size_t) -1;
|
||||
return;
|
||||
}
|
||||
__endtry
|
||||
sfd_ptr++;
|
||||
curlen += sizeof (*sfd_ptr);
|
||||
}
|
||||
while ((len - curlen >= sizeof (struct signalfd_siginfo)));
|
||||
set_errno (old_errno);
|
||||
len = curlen;
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_signalfd::poll ()
|
||||
{
|
||||
Sleep (1L); /* BAD HACK, FIXME, need a non-polling technique. */
|
||||
sigset_t outset = (sigset_t) sig_send (myself, __SIGPENDING, &_my_tls);
|
||||
if (outset == SIG_BAD_MASK)
|
||||
return -1;
|
||||
if ((outset & sigset) != 0)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
|
@ -502,12 +502,13 @@ details. */
|
|||
330: Add CLOCK_REALTIME_COARSE, CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_COARSE,
|
||||
CLOCK_BOOTTIME.
|
||||
331: Add timer_getoverrun, DELAYTIMER_MAX.
|
||||
332: Add signalfd.
|
||||
|
||||
Note that we forgot to bump the api for ualarm, strtoll, strtoull,
|
||||
sigaltstack, sethostname. */
|
||||
|
||||
#define CYGWIN_VERSION_API_MAJOR 0
|
||||
#define CYGWIN_VERSION_API_MINOR 331
|
||||
#define CYGWIN_VERSION_API_MINOR 332
|
||||
|
||||
/* There is also a compatibity version number associated with the shared memory
|
||||
regions. It is incremented when incompatible changes are made to the shared
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/* sys/signalfd.h: define signalfd(2) and struct signalfd_siginfo
|
||||
|
||||
This file is part of Cygwin.
|
||||
|
||||
This software is a copyrighted work licensed under the terms of the
|
||||
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
||||
details. */
|
||||
|
||||
#ifndef _SYS_SIGNALFD_H
|
||||
#define _SYS_SIGNALFD_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/_default_fcntl.h>
|
||||
|
||||
enum
|
||||
{
|
||||
SFD_CLOEXEC = O_CLOEXEC,
|
||||
SFD_NONBLOCK = O_NONBLOCK
|
||||
};
|
||||
#define SFD_CLOEXEC SFD_CLOEXEC
|
||||
#define SFD_NONBLOCK SFD_NONBLOCK
|
||||
|
||||
struct signalfd_siginfo
|
||||
{
|
||||
uint32_t ssi_signo;
|
||||
int32_t ssi_errno;
|
||||
int32_t ssi_code;
|
||||
uint32_t ssi_pid;
|
||||
uint32_t ssi_uid;
|
||||
int32_t ssi_fd;
|
||||
uint32_t ssi_tid;
|
||||
uint32_t ssi_band;
|
||||
uint32_t ssi_overrun;
|
||||
uint32_t ssi_trapno;
|
||||
int32_t ssi_status;
|
||||
int32_t ssi_int;
|
||||
uint64_t ssi_ptr;
|
||||
uint64_t ssi_utime;
|
||||
uint64_t ssi_stime;
|
||||
uint64_t ssi_addr;
|
||||
uint8_t pad[48];
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int signalfd (int, const sigset_t *, int);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_SIGNALFD_H */
|
|
@ -27,7 +27,7 @@ What's new:
|
|||
- Support overrun counter for posix timers (via timer_getoverrun() or
|
||||
siginfo_t::si_overrun).
|
||||
|
||||
- New API: timer_getoverrun.
|
||||
- New API: signalfd, timer_getoverrun.
|
||||
|
||||
|
||||
What changed:
|
||||
|
|
|
@ -1731,3 +1731,68 @@ fhandler_windows::select_except (select_stuff *ss)
|
|||
s->windows_handle = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
static int
|
||||
peek_signalfd (select_record *me, bool)
|
||||
{
|
||||
if (((fhandler_signalfd *) me->fh)->poll () == 0)
|
||||
{
|
||||
select_printf ("signalfd %d ready", me->fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
select_printf ("signalfd %d not ready", me->fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
verify_signalfd (select_record *me, fd_set *rfds, fd_set *wfds,
|
||||
fd_set *efds)
|
||||
{
|
||||
return peek_signalfd (me, true);
|
||||
}
|
||||
|
||||
select_record *
|
||||
fhandler_signalfd::select_read (select_stuff *ss)
|
||||
{
|
||||
select_record *s = ss->start.next;
|
||||
if (!s->startup)
|
||||
{
|
||||
s->startup = no_startup;
|
||||
}
|
||||
s->verify = verify_signalfd;
|
||||
s->peek = peek_signalfd;
|
||||
s->read_selected = true;
|
||||
s->read_ready = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
select_record *
|
||||
fhandler_signalfd::select_write (select_stuff *ss)
|
||||
{
|
||||
select_record *s = ss->start.next;
|
||||
if (!s->startup)
|
||||
{
|
||||
s->startup = no_startup;
|
||||
s->verify = no_verify;
|
||||
}
|
||||
s->peek = NULL;
|
||||
s->write_selected = false;
|
||||
s->write_ready = false;
|
||||
return s;
|
||||
}
|
||||
|
||||
select_record *
|
||||
fhandler_signalfd::select_except (select_stuff *ss)
|
||||
{
|
||||
select_record *s = ss->start.next;
|
||||
if (!s->startup)
|
||||
{
|
||||
s->startup = no_startup;
|
||||
s->verify = no_verify;
|
||||
}
|
||||
s->peek = NULL;
|
||||
s->except_selected = false;
|
||||
s->except_ready = false;
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ details. */
|
|||
#include "winsup.h"
|
||||
#include <stdlib.h>
|
||||
#include <sys/cygwin.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include "pinfo.h"
|
||||
#include "sigproc.h"
|
||||
#include "cygtls.h"
|
||||
|
@ -592,7 +593,7 @@ siginterrupt (int sig, int flag)
|
|||
return res;
|
||||
}
|
||||
|
||||
static inline int
|
||||
int
|
||||
sigwait_common (const sigset_t *set, siginfo_t *info, PLARGE_INTEGER waittime)
|
||||
{
|
||||
int res = -1;
|
||||
|
@ -781,3 +782,62 @@ sigaltstack (const stack_t *ss, stack_t *oss)
|
|||
__endtry
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
signalfd (int fd_in, const sigset_t *mask, int flags)
|
||||
{
|
||||
int ret = -1;
|
||||
fhandler_signalfd *fh;
|
||||
|
||||
debug_printf ("signalfd (%d, %p, %y)", fd_in, mask, flags);
|
||||
|
||||
if ((flags & ~(SFD_NONBLOCK | SFD_CLOEXEC)) != 0)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (fd_in != -1)
|
||||
{
|
||||
/* Change signal mask. */
|
||||
cygheap_fdget fd (fd_in);
|
||||
|
||||
if (fd < 0)
|
||||
goto done;
|
||||
fh = fd->is_signalfd ();
|
||||
if (!fh)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
goto done;
|
||||
}
|
||||
__try
|
||||
{
|
||||
if (fh->signalfd (mask, flags) == 0)
|
||||
ret = fd_in;
|
||||
}
|
||||
__except (EINVAL) {}
|
||||
__endtry
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Create new signalfd descriptor. */
|
||||
cygheap_fdnew fd;
|
||||
|
||||
if (fd < 0)
|
||||
goto done;
|
||||
fh = (fhandler_signalfd *) build_fh_dev (*signalfd_dev);
|
||||
if (fh && fh->signalfd (mask, flags) == 0)
|
||||
{
|
||||
fd = fh;
|
||||
if (fd <= 2)
|
||||
set_std_handle (fd);
|
||||
ret = fd;
|
||||
}
|
||||
else
|
||||
delete fh;
|
||||
}
|
||||
|
||||
done:
|
||||
syscall_printf ("%R = signalfd (%d, %p, %y)", ret, fd_in, mask, flags);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@ void __stdcall sigalloc ();
|
|||
int kill_pgrp (pid_t, siginfo_t&);
|
||||
void __reg1 exit_thread (DWORD) __attribute__ ((noreturn));
|
||||
void __reg1 setup_signal_exit (int);
|
||||
int sigwait_common (const sigset_t *, siginfo_t *, PLARGE_INTEGER);
|
||||
|
||||
class no_thread_exit_protect
|
||||
{
|
||||
|
|
|
@ -51,7 +51,7 @@ siginfo_t::si_overrun).
|
|||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
New API: timer_getoverrun.
|
||||
New API: signalfd, timer_getoverrun.
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
|
|
|
@ -1378,6 +1378,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
|
|||
scandirat
|
||||
sched_getcpu
|
||||
setxattr
|
||||
signalfd
|
||||
sincos
|
||||
sincosf
|
||||
sincosl
|
||||
|
|
Loading…
Reference in New Issue