summaryrefslogtreecommitdiffstats
path: root/src/shared
diff options
context:
space:
mode:
authorSimon Rettberg2016-02-05 21:14:23 +0100
committerSimon Rettberg2016-02-05 21:14:23 +0100
commit415e28e43bf0538ced45d6f0a2627448d7360ba0 (patch)
tree9cc6404f083b5d8bb3da0bcdb816872c934aface /src/shared
parentFirst steps in make signals more abstract from the underlying mechanism; repl... (diff)
downloaddnbd3-415e28e43bf0538ced45d6f0a2627448d7360ba0.tar.gz
dnbd3-415e28e43bf0538ced45d6f0a2627448d7360ba0.tar.xz
dnbd3-415e28e43bf0538ced45d6f0a2627448d7360ba0.zip
[SHARED] Implement pipe() based signalling
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/signal.c81
-rw-r--r--src/shared/signal.inc/eventfd.c74
-rw-r--r--src/shared/signal.inc/pipe64.c88
-rw-r--r--src/shared/signal.inc/pipe_malloc.c89
4 files changed, 261 insertions, 71 deletions
diff --git a/src/shared/signal.c b/src/shared/signal.c
index e023c49..051f785 100644
--- a/src/shared/signal.c
+++ b/src/shared/signal.c
@@ -1,74 +1,13 @@
#include "signal.h"
-#include <sys/eventfd.h>
-#include <poll.h>
-#include <inttypes.h>
-#include <errno.h>
-#include <unistd.h>
-/*
- * Linux implementation of signals.
- * Internally, eventfds are used for signalling, as they
- * provide the least overhead. We don't allocate any struct
- * ever, but cast the event fd+1 to dnbd3_signal_t*
- * to save all the malloc() and free() calls.
- */
-
-dnbd3_signal_t* signal_new()
-{
- // On error, eventfd() returns -1, so essentially we return NULL on error.
- // (Yes, NULL doesn't have to be 0 everywhere, but cmon)
- return (dnbd3_signal_t*)(intptr_t)( eventfd( 0, EFD_NONBLOCK ) + 1 );
-}
-
-dnbd3_signal_t* signal_newBlocking()
-{
- return (dnbd3_signal_t*)(intptr_t)( eventfd( 0, 0 ) + 1 );
-}
-
-int signal_call(const dnbd3_signal_t* const signal)
-{
- if ( signal == NULL ) return SIGNAL_ERROR;
- static uint64_t one = 1;
- const int signalFd = ( (int)(intptr_t)signal ) - 1;
- return write( signalFd, &one, sizeof one ) == sizeof one;
-}
-
-int signal_wait(const dnbd3_signal_t* const signal, int timeoutMs)
-{
- if ( signal == NULL ) return SIGNAL_ERROR;
- const int signalFd = ( (int)(intptr_t)signal ) - 1;
- struct pollfd ps = {
- .fd = signalFd,
- .events = POLLIN
- };
- int ret = poll( &ps, 1, timeoutMs );
- if ( ret == 0 ) return SIGNAL_TIMEOUT;
- if ( ret == -1 ) return SIGNAL_ERROR;
- if ( ps.revents & ( POLLERR | POLLNVAL ) ) return SIGNAL_ERROR;
- return signal_clear( signal );
-}
-
-int signal_clear(const dnbd3_signal_t* const signal)
-{
- if ( signal == NULL ) return SIGNAL_ERROR;
- uint64_t ret;
- const int signalFd = ( (int)(intptr_t)signal ) - 1;
- if ( read( signalFd, &ret, sizeof ret ) != sizeof ret ) {
- if ( errno == EAGAIN ) return 0;
- return SIGNAL_ERROR;
- }
- return (int)ret;
-}
-
-void signal_close(const dnbd3_signal_t* const signal)
-{
- const int signalFd = ( (int)(intptr_t)signal ) - 1;
- close( signalFd );
-}
-
-int signal_getWaitFd(const dnbd3_signal_t* const signal)
-{
- const int signalFd = ( (int)(intptr_t)signal ) - 1;
- return signalFd;
-}
+#if defined(linux)
+//#warning "Using eventfd based signalling"
+#include "signal.inc/eventfd.c"
+#elif __SIZEOF_INT__ == 4 && __SIZEOF_POINTER__ == 8
+//#warning "Using pointer-packing pipe based signalling"
+#include "signal.inc/pipe64.c"
+#else
+//#warning "Using fallback pipe based signalling"
+#include "signal.inc/pipe_malloc.c"
+#endif
diff --git a/src/shared/signal.inc/eventfd.c b/src/shared/signal.inc/eventfd.c
new file mode 100644
index 0000000..b4d4d6e
--- /dev/null
+++ b/src/shared/signal.inc/eventfd.c
@@ -0,0 +1,74 @@
+#include <sys/eventfd.h>
+#include <poll.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <unistd.h>
+
+/*
+ * Linux implementation of signals.
+ * Internally, eventfds are used for signalling, as they
+ * provide the least overhead. We don't allocate any struct
+ * ever, but cast the event fd+1 to dnbd3_signal_t*
+ * to save all the malloc() and free() calls.
+ */
+
+dnbd3_signal_t* signal_new()
+{
+ // On error, eventfd() returns -1, so essentially we return NULL on error.
+ // (Yes, NULL doesn't have to be 0 everywhere, but cmon)
+ return (dnbd3_signal_t*)(intptr_t)( eventfd( 0, EFD_NONBLOCK ) + 1 );
+}
+
+dnbd3_signal_t* signal_newBlocking()
+{
+ return (dnbd3_signal_t*)(intptr_t)( eventfd( 0, 0 ) + 1 );
+}
+
+int signal_call(const dnbd3_signal_t* const signal)
+{
+ if ( signal == NULL ) return SIGNAL_ERROR;
+ static uint64_t one = 1;
+ const int signalFd = ( (int)(intptr_t)signal ) - 1;
+ return write( signalFd, &one, sizeof one ) == sizeof one ? SIGNAL_OK : SIGNAL_ERROR;
+}
+
+int signal_wait(const dnbd3_signal_t* const signal, int timeoutMs)
+{
+ if ( signal == NULL ) return SIGNAL_ERROR;
+ const int signalFd = ( (int)(intptr_t)signal ) - 1;
+ struct pollfd ps = {
+ .fd = signalFd,
+ .events = POLLIN
+ };
+ int ret = poll( &ps, 1, timeoutMs );
+ if ( ret == 0 ) return SIGNAL_TIMEOUT;
+ if ( ret == -1 ) return SIGNAL_ERROR;
+ if ( ps.revents & ( POLLERR | POLLNVAL ) ) return SIGNAL_ERROR;
+ return signal_clear( signal );
+}
+
+int signal_clear(const dnbd3_signal_t* const signal)
+{
+ if ( signal == NULL ) return SIGNAL_ERROR;
+ uint64_t ret;
+ const int signalFd = ( (int)(intptr_t)signal ) - 1;
+ if ( read( signalFd, &ret, sizeof ret ) != sizeof ret ) {
+ if ( errno == EAGAIN ) return 0;
+ return SIGNAL_ERROR;
+ }
+ return (int)ret;
+}
+
+void signal_close(const dnbd3_signal_t* const signal)
+{
+ const int signalFd = ( (int)(intptr_t)signal ) - 1;
+ close( signalFd );
+}
+
+int signal_getWaitFd(const dnbd3_signal_t* const signal)
+{
+ if ( signal == NULL ) return -1;
+ const int signalFd = ( (int)(intptr_t)signal ) - 1;
+ return signalFd;
+}
+
diff --git a/src/shared/signal.inc/pipe64.c b/src/shared/signal.inc/pipe64.c
new file mode 100644
index 0000000..4f0614b
--- /dev/null
+++ b/src/shared/signal.inc/pipe64.c
@@ -0,0 +1,88 @@
+#include <poll.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define P_READ (0)
+#define P_WRITE (1)
+
+/*
+ * Generic (posix) implelentation of signals, using pipes.
+ * 64bit version, packing two ints into a pointer.
+ * This version requires that you use -fno-strict-aliasing
+ * since it's doing evil pointer casting.
+ */
+
+dnbd3_signal_t* signal_new()
+{
+ int fds[2];
+ if ( pipe( fds ) == -1 ) return NULL;
+ fcntl( fds[P_READ], F_SETFL, O_NONBLOCK );
+ fcntl( fds[P_WRITE], F_SETFL, O_NONBLOCK );
+ return (dnbd3_signal_t*)*((uintptr_t*)fds);
+}
+
+dnbd3_signal_t* signal_newBlocking()
+{
+ int fds[2];
+ if ( pipe( fds ) == -1 ) return NULL;
+ return (dnbd3_signal_t*)*((uintptr_t*)fds);
+}
+
+int signal_call(const dnbd3_signal_t* const signal)
+{
+ if ( signal == NULL ) return SIGNAL_ERROR;
+ static char one = 1;
+ const int* fds = (int*)&signal;
+ // Write one byte on every call, so the number of bytes read will
+ // match the number of events
+ return write( fds[P_WRITE], &one, 1 ) > 0 ? SIGNAL_OK : SIGNAL_ERROR;
+}
+
+int signal_wait(const dnbd3_signal_t* const signal, int timeoutMs)
+{
+ if ( signal == NULL ) return SIGNAL_ERROR;
+ const int* fds = (int*)&signal;
+ struct pollfd ps = {
+ .fd = fds[P_READ],
+ .events = POLLIN
+ };
+ int ret = poll( &ps, 1, timeoutMs );
+ if ( ret == 0 ) return SIGNAL_TIMEOUT;
+ if ( ret == -1 ) return SIGNAL_ERROR;
+ if ( ps.revents & ( POLLERR | POLLNVAL ) ) return SIGNAL_ERROR;
+ return signal_clear( signal );
+}
+
+int signal_clear(const dnbd3_signal_t* const signal)
+{
+ if ( signal == NULL ) return SIGNAL_ERROR;
+ char throwaway[100];
+ const int* fds = (int*)&signal;
+ ssize_t ret, total = 0;
+ do {
+ ret = read( fds[P_READ], throwaway, sizeof throwaway );
+ if ( ret < 0 ) {
+ if ( errno == EAGAIN ) return total;
+ return SIGNAL_ERROR;
+ }
+ total += ret;
+ } while ( (size_t)ret == sizeof throwaway );
+ return (int)total;
+}
+
+void signal_close(const dnbd3_signal_t* const signal)
+{
+ const int* fds = (int*)&signal;
+ close( fds[P_READ] );
+ close( fds[P_WRITE] );
+}
+
+int signal_getWaitFd(const dnbd3_signal_t* const signal)
+{
+ if ( signal == NULL ) return -1;
+ const int* fds = (int*)&signal;
+ return fds[P_READ];
+}
+
diff --git a/src/shared/signal.inc/pipe_malloc.c b/src/shared/signal.inc/pipe_malloc.c
new file mode 100644
index 0000000..b23ddcd
--- /dev/null
+++ b/src/shared/signal.inc/pipe_malloc.c
@@ -0,0 +1,89 @@
+#include <poll.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+struct _dnbd3_signal {
+ int read;
+ int write;
+};
+
+/*
+ * Generic (posix) implelentation of signals, using pipes.
+ * A struct containing both fds will be malloc()ed for each
+ * signal.
+ */
+
+dnbd3_signal_t* signal_new()
+{
+ dnbd3_signal_t *ret = signal_newBlocking();
+ if ( ret == NULL ) return NULL;
+ fcntl( ret->read, F_SETFL, O_NONBLOCK );
+ fcntl( ret->write, F_SETFL, O_NONBLOCK );
+ return ret;
+}
+
+dnbd3_signal_t* signal_newBlocking()
+{
+ int fds[2];
+ if ( pipe( fds ) == -1 ) return NULL;
+ dnbd3_signal_t* ret = malloc( sizeof(dnbd3_signal_t) );
+ ret->read = fds[0];
+ ret->write = fds[1];
+ return ret;
+}
+
+int signal_call(const dnbd3_signal_t* const signal)
+{
+ if ( signal == NULL ) return SIGNAL_ERROR;
+ static char one = 1;
+ // Write one byte on every call, so the number of bytes read will
+ // match the number of events
+ return write( signal->write, &one, 1 ) > 0 ? SIGNAL_OK : SIGNAL_ERROR;
+}
+
+int signal_wait(const dnbd3_signal_t* const signal, int timeoutMs)
+{
+ if ( signal == NULL ) return SIGNAL_ERROR;
+ struct pollfd ps = {
+ .fd = signal->read,
+ .events = POLLIN
+ };
+ int ret = poll( &ps, 1, timeoutMs );
+ if ( ret == 0 ) return SIGNAL_TIMEOUT;
+ if ( ret == -1 ) return SIGNAL_ERROR;
+ if ( ps.revents & ( POLLERR | POLLNVAL ) ) return SIGNAL_ERROR;
+ return signal_clear( signal );
+}
+
+int signal_clear(const dnbd3_signal_t* const signal)
+{
+ if ( signal == NULL ) return SIGNAL_ERROR;
+ char throwaway[100];
+ ssize_t ret, total = 0;
+ do {
+ ret = read( signal->read, throwaway, sizeof throwaway );
+ if ( ret < 0 ) {
+ if ( errno == EAGAIN ) return (int)total;
+ return SIGNAL_ERROR;
+ }
+ total += ret;
+ } while ( (size_t)ret == sizeof throwaway );
+ return (int)total;
+}
+
+void signal_close(const dnbd3_signal_t* const signal)
+{
+ close( signal->read );
+ close( signal->write );
+ free( (void*)signal );
+}
+
+int signal_getWaitFd(const dnbd3_signal_t* const signal)
+{
+ if ( signal == NULL ) return -1;
+ return signal->read;
+}
+