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:
Corinna Vinschen 2019-01-13 23:13:33 +01:00
parent 8ae26f96ae
commit 9d13a2995c
17 changed files with 397 additions and 4 deletions

View File

@ -298,6 +298,7 @@ DLL_OFILES:= \
fhandler_raw.o \ fhandler_raw.o \
fhandler_registry.o \ fhandler_registry.o \
fhandler_serial.o \ fhandler_serial.o \
fhandler_signalfd.o \
fhandler_socket.o \ fhandler_socket.o \
fhandler_socket_inet.o \ fhandler_socket_inet.o \
fhandler_socket_local.o \ fhandler_socket_local.o \

View File

@ -1333,6 +1333,7 @@ siginterrupt SIGFE
sigismember SIGFE sigismember SIGFE
siglongjmp NOSIGFE siglongjmp NOSIGFE
signal SIGFE signal SIGFE
signalfd SIGFE
significand NOSIGFE significand NOSIGFE
significandf NOSIGFE significandf NOSIGFE
sigpause SIGFE sigpause SIGFE

View File

@ -120,6 +120,9 @@ const _device dev_piper_storage =
const _device dev_pipew_storage = const _device dev_pipew_storage =
{"", {FH_PIPEW}, "", exists_internal}; {"", {FH_PIPEW}, "", exists_internal};
const _device dev_signalfd_storage =
{"", {FH_SIGNALFD}, "", exists_internal};
const _device dev_socket_storage = const _device dev_socket_storage =
{"", {FH_SOCKET}, "", exists_internal}; {"", {FH_SOCKET}, "", exists_internal};

View File

@ -72,6 +72,8 @@ enum fh_devices
FH_DEV = FHDEV (DEV_VIRTFS_MAJOR, 193), FH_DEV = FHDEV (DEV_VIRTFS_MAJOR, 193),
FH_CYGDRIVE= FHDEV (DEV_VIRTFS_MAJOR, 192), FH_CYGDRIVE= FHDEV (DEV_VIRTFS_MAJOR, 192),
FH_SIGNALFD= FHDEV (DEV_VIRTFS_MAJOR, 13),
DEV_FLOPPY_MAJOR = 2, DEV_FLOPPY_MAJOR = 2,
FH_FLOPPY = FHDEV (DEV_FLOPPY_MAJOR, 0), 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; extern const _device dev_af_unix_storage;
#define af_unix_dev ((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; extern const _device dev_piper_storage;
#define piper_dev ((device *) &dev_piper_storage) #define piper_dev ((device *) &dev_piper_storage)
extern const _device dev_pipew_storage; extern const _device dev_pipew_storage;

View File

@ -116,6 +116,9 @@ const _device dev_piper_storage =
const _device dev_pipew_storage = const _device dev_pipew_storage =
{"", {FH_PIPEW}, "", exists_internal}; {"", {FH_PIPEW}, "", exists_internal};
const _device dev_signalfd_storage =
{"", {FH_SIGNALFD}, "", exists_internal};
const _device dev_socket_storage = const _device dev_socket_storage =
{"", {FH_SOCKET}, "", exists_internal}; {"", {FH_SOCKET}, "", exists_internal};

View File

@ -575,6 +575,9 @@ fh_alloc (path_conv& pc)
case FH_CYGDRIVE: case FH_CYGDRIVE:
fh = cnew (fhandler_cygdrive); fh = cnew (fhandler_cygdrive);
break; break;
case FH_SIGNALFD:
fh = cnew (fhandler_signalfd);
break;
case FH_TTY: case FH_TTY:
if (!pc.isopen ()) if (!pc.isopen ())
{ {

View File

@ -1345,6 +1345,9 @@ fhandler_base::fstat (struct stat *buf)
case FH_PIPER: case FH_PIPER:
buf->st_mode = S_IFIFO | S_IRUSR; buf->st_mode = S_IFIFO | S_IRUSR;
break; break;
case FH_SIGNALFD:
buf->st_mode = S_IRUSR | S_IWUSR;
break;
default: default:
buf->st_mode = S_IFCHR | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH; buf->st_mode = S_IFCHR | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH;
break; break;

View File

@ -420,6 +420,7 @@ public:
virtual class fhandler_socket *is_socket () { return NULL; } virtual class fhandler_socket *is_socket () { return NULL; }
virtual class fhandler_socket_wsock *is_wsock_socket () { return NULL; } virtual class fhandler_socket_wsock *is_wsock_socket () { return NULL; }
virtual class fhandler_console *is_console () { return 0; } virtual class fhandler_console *is_console () { return 0; }
virtual class fhandler_signalfd *is_signalfd () { return NULL; }
virtual int is_windows () {return 0; } virtual int is_windows () {return 0; }
virtual void __reg3 raw_read (void *ptr, size_t& ulen); 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 struct fhandler_nodevice: public fhandler_base
{ {
fhandler_nodevice (); fhandler_nodevice ();
@ -2672,6 +2711,7 @@ typedef union
char __pty_master[sizeof (fhandler_pty_master)]; char __pty_master[sizeof (fhandler_pty_master)];
char __registry[sizeof (fhandler_registry)]; char __registry[sizeof (fhandler_registry)];
char __serial[sizeof (fhandler_serial)]; char __serial[sizeof (fhandler_serial)];
char __signalfd[sizeof (fhandler_signalfd)];
char __socket_inet[sizeof (fhandler_socket_inet)]; char __socket_inet[sizeof (fhandler_socket_inet)];
char __socket_local[sizeof (fhandler_socket_local)]; char __socket_local[sizeof (fhandler_socket_local)];
#ifdef __WITH_AF_UNIX #ifdef __WITH_AF_UNIX

View File

@ -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;
}

View File

@ -502,12 +502,13 @@ details. */
330: Add CLOCK_REALTIME_COARSE, CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_COARSE, 330: Add CLOCK_REALTIME_COARSE, CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_COARSE,
CLOCK_BOOTTIME. CLOCK_BOOTTIME.
331: Add timer_getoverrun, DELAYTIMER_MAX. 331: Add timer_getoverrun, DELAYTIMER_MAX.
332: Add signalfd.
Note that we forgot to bump the api for ualarm, strtoll, strtoull, Note that we forgot to bump the api for ualarm, strtoll, strtoull,
sigaltstack, sethostname. */ sigaltstack, sethostname. */
#define CYGWIN_VERSION_API_MAJOR 0 #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 /* There is also a compatibity version number associated with the shared memory
regions. It is incremented when incompatible changes are made to the shared regions. It is incremented when incompatible changes are made to the shared

View File

@ -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 */

View File

@ -27,7 +27,7 @@ What's new:
- Support overrun counter for posix timers (via timer_getoverrun() or - Support overrun counter for posix timers (via timer_getoverrun() or
siginfo_t::si_overrun). siginfo_t::si_overrun).
- New API: timer_getoverrun. - New API: signalfd, timer_getoverrun.
What changed: What changed:

View File

@ -1731,3 +1731,68 @@ fhandler_windows::select_except (select_stuff *ss)
s->windows_handle = true; s->windows_handle = true;
return s; 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;
}

View File

@ -12,6 +12,7 @@ details. */
#include "winsup.h" #include "winsup.h"
#include <stdlib.h> #include <stdlib.h>
#include <sys/cygwin.h> #include <sys/cygwin.h>
#include <sys/signalfd.h>
#include "pinfo.h" #include "pinfo.h"
#include "sigproc.h" #include "sigproc.h"
#include "cygtls.h" #include "cygtls.h"
@ -592,7 +593,7 @@ siginterrupt (int sig, int flag)
return res; return res;
} }
static inline int int
sigwait_common (const sigset_t *set, siginfo_t *info, PLARGE_INTEGER waittime) sigwait_common (const sigset_t *set, siginfo_t *info, PLARGE_INTEGER waittime)
{ {
int res = -1; int res = -1;
@ -781,3 +782,62 @@ sigaltstack (const stack_t *ss, stack_t *oss)
__endtry __endtry
return 0; 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;
}

View File

@ -79,6 +79,7 @@ void __stdcall sigalloc ();
int kill_pgrp (pid_t, siginfo_t&); int kill_pgrp (pid_t, siginfo_t&);
void __reg1 exit_thread (DWORD) __attribute__ ((noreturn)); void __reg1 exit_thread (DWORD) __attribute__ ((noreturn));
void __reg1 setup_signal_exit (int); void __reg1 setup_signal_exit (int);
int sigwait_common (const sigset_t *, siginfo_t *, PLARGE_INTEGER);
class no_thread_exit_protect class no_thread_exit_protect
{ {

View File

@ -51,7 +51,7 @@ siginfo_t::si_overrun).
</para></listitem> </para></listitem>
<listitem><para> <listitem><para>
New API: timer_getoverrun. New API: signalfd, timer_getoverrun.
</para></listitem> </para></listitem>
<listitem><para> <listitem><para>

View File

@ -1378,6 +1378,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
scandirat scandirat
sched_getcpu sched_getcpu
setxattr setxattr
signalfd
sincos sincos
sincosf sincosf
sincosl sincosl