Add user interface to #eventfd (XCC) [2/2]
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 22 Sep 2015 15:28:18 +0000 (11:28 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 28 Sep 2015 19:14:00 +0000 (15:14 -0400)
The glibc eventfd headers are from the Linux port.

Rebuild glibc.

tests/eventfd.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/Makefile
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/Versions
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/bits/eventfd.h [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/eventfd.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/sys/eventfd.h [new file with mode: 0644]

diff --git a/tests/eventfd.c b/tests/eventfd.c
new file mode 100644 (file)
index 0000000..99c6ca7
--- /dev/null
@@ -0,0 +1,122 @@
+/* Copyright (c) 2015 Google Inc
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * #eventfd test, using the glibc interface mostly. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <parlib/parlib.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/eventfd.h>
+#include <sys/epoll.h>
+
+
+#define handle_error(msg) \
+        do { perror(msg); exit(-1); } while (0)
+
+static void epoll_on_efd(int efd)
+{
+       #define EP_SET_SZ 10    /* this is actually the ID of the largest FD */
+       int epfd = epoll_create(EP_SET_SZ);
+       struct epoll_event ep_ev;
+       struct epoll_event results[EP_SET_SZ];
+
+       if (epfd < 0)
+               handle_error("epoll_create");
+       ep_ev.events = EPOLLIN | EPOLLET;
+       ep_ev.data.fd = efd;
+       if (epoll_ctl(epfd, EPOLL_CTL_ADD, efd, &ep_ev))
+               handle_error("epoll_ctl_add eventfd");
+       if (epoll_wait(epfd, results, EP_SET_SZ, -1) != 1)
+               handle_error("epoll_wait");
+       close(epfd);
+}
+
+static pthread_attr_t pth_attrs;
+static bool upped;
+
+static void *upper_thread(void *arg)
+{
+       int efd = (int)(long)arg;
+       uthread_sleep(1);
+       if (eventfd_write(efd, 1))
+               handle_error("upper write");
+       upped = TRUE;
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       int ret;
+       eventfd_t efd_val = 0;
+       int efd;
+       pthread_t child;
+
+       parlib_wants_to_be_mcp = FALSE; /* Make us an SCP with a 2LS */
+       pthread_attr_init(&pth_attrs);
+       if (pthread_attr_setdetachstate(&pth_attrs, PTHREAD_CREATE_DETACHED))
+               handle_error("pth attrs");
+
+       /* Semaphore counter, nonblocking */
+       efd = eventfd(2, EFD_SEMAPHORE | EFD_NONBLOCK);
+       if (efd < 0)
+               handle_error("open sem");
+       if (eventfd_read(efd, &efd_val))
+               handle_error("first read");
+       assert(efd_val == 1);
+       if (eventfd_read(efd, &efd_val))
+               handle_error("second read");
+       assert(efd_val == 1);   /* always get 1 back from the SEM */
+       ret = eventfd_read(efd, &efd_val);
+       if ((ret != -1) && (errno != EAGAIN))
+               handle_error("third read should be EAGAIN");
+
+       if (pthread_create(&child, &pth_attrs, &upper_thread, (void*)(long)efd))
+               handle_error("pth_create failed");
+       epoll_on_efd(efd);
+       if (eventfd_read(efd, &efd_val))
+               handle_error("final read");
+       assert(efd_val == 1);
+       close(efd);
+
+       /* Regular counter */
+       efd = eventfd(2, 0);
+       if (efd < 0)
+               handle_error("open nonsem");
+       if (eventfd_read(efd, &efd_val))
+               handle_error("first read nonsem");
+       assert(efd_val == 2);
+
+       /* Will try to block in the kernel.  Using 'upped' to catch any quick
+        * returns.  It's not full-proof, but it can catch an O_NONBLOCK */
+       if (pthread_create(&child, &pth_attrs, &upper_thread, (void*)(long)efd))
+               handle_error("pth_create failed");
+       upped = FALSE;
+       if (eventfd_read(efd, &efd_val))
+               handle_error("blocking read nonsem");
+       cmb();
+       assert(upped && efd_val == 1);
+
+       /* Should still be 0.  Add 1 and then extract to see if it was. */
+       if (eventfd_write(efd, 1))
+               handle_error("write nonsem +1");
+       if (eventfd_read(efd, &efd_val))
+               handle_error("final read nonsem");
+       /* 1 means it was 0 before we added 1.  it's 0 again, since we read. */
+       assert(efd_val == 1);
+       /* Test the max_val + 1 write */
+       ret = eventfd_write(efd, -1);
+       if ((ret != -1) && (errno != EINVAL))
+               handle_error("write nonsem should have failed");
+       if (eventfd_write(efd, 0xfffffffffffffffe))
+               handle_error("largest legal write");
+       close(efd);
+
+       return 0;
+}
index 4e35f97..5f3b844 100644 (file)
@@ -54,3 +54,9 @@ endif
 sysdep_headers += sys/user_fd.h
 # Epoll: uses User FDs, implemented in iplib
 sysdep_headers += sys/epoll.h bits/epoll.h
+
+# Eventfd, implemented in glibc
+ifeq ($(subdir),stdlib)
+sysdep_routines += eventfd
+endif
+sysdep_headers += sys/eventfd.h bits/eventfd.h
index c9a2b78..dc82952 100644 (file)
@@ -48,5 +48,9 @@ libc {
 
     _sock_get_listen_fd;
     _sock_lookup_listen_fd;
+
+    eventfd;
+    eventfd_read;
+    eventfd_write;
   }
 }
diff --git a/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/bits/eventfd.h b/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/bits/eventfd.h
new file mode 100644 (file)
index 0000000..aedcda0
--- /dev/null
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-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/>.  */
+
+#ifndef        _SYS_EVENTFD_H
+# error "Never use <bits/eventfd.h> directly; include <sys/eventfd.h> instead."
+#endif
+
+/* Flags for eventfd.  */
+enum
+  {
+    EFD_SEMAPHORE = 00000001,
+#define EFD_SEMAPHORE EFD_SEMAPHORE
+    EFD_CLOEXEC = 02000000,
+#define EFD_CLOEXEC EFD_CLOEXEC
+    EFD_NONBLOCK = 00004000
+#define EFD_NONBLOCK EFD_NONBLOCK
+  };
diff --git a/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/eventfd.c b/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/eventfd.c
new file mode 100644 (file)
index 0000000..372447e
--- /dev/null
@@ -0,0 +1,64 @@
+/* Copyright (c) 2015 Google Inc.
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Implementation of glibc's eventfd interface, hooking in to #eventfd */
+
+#include <sys/eventfd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Gets a new EFD instance, returning the FD on success. */
+int eventfd(int initval, int flags)
+{
+       const char *pathname = "#eventfd/efd";
+       int oflags = 0;
+       int efd;
+
+       /* we choose semaphore mode by passing 'sem' as the device spec */
+       if (flags & EFD_SEMAPHORE)
+               pathname = "#eventfd.sem/efd";
+       if (flags & EFD_CLOEXEC)
+               oflags |= O_CLOEXEC;
+       if (flags & EFD_NONBLOCK)
+               oflags |= O_NONBLOCK;
+       efd = open(pathname, O_READ | O_WRITE | oflags);
+       if (efd < 0)
+               return efd;
+       /* default initval for an #eventfd is 0. */
+       if (initval) {
+               if (eventfd_write(efd, initval)) {
+                       close(efd);
+                       return -1;
+               }
+       }
+       return efd;
+}
+
+/* Reads into *value, in host-endian format, from the efd.  Returns 0 on
+ * success.  */
+int eventfd_read(int efd, eventfd_t *value)
+{
+       int ret;
+       char num64[32];
+       ret = read(efd, num64, sizeof(num64));
+       if (ret <= 0)
+               return -1;
+       *value = strtoul(num64, 0, 0);
+       return 0;
+}
+
+/* Writes value, in host-endian format, into the efd.  Returns 0 on success. */
+int eventfd_write(int efd, eventfd_t value)
+{
+       int ret;
+       char num64[32];
+       ret = snprintf(num64, sizeof(num64), "0x%llx", value);
+       if (write(efd, num64, ret) == ret)
+               return 0;
+       else
+               return -1;
+}
diff --git a/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/sys/eventfd.h b/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/sys/eventfd.h
new file mode 100644 (file)
index 0000000..7f977ed
--- /dev/null
@@ -0,0 +1,44 @@
+/* Copyright (C) 2007-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/>.  */
+
+#ifndef        _SYS_EVENTFD_H
+#define        _SYS_EVENTFD_H  1
+
+#include <stdint.h>
+
+/* Get the platform-dependent flags.  */
+#include <bits/eventfd.h>
+
+/* Type for event counter.  */
+typedef uint64_t eventfd_t;
+
+
+__BEGIN_DECLS
+
+/* Return file descriptor for generic event channel.  Set initial
+   value to COUNT.  */
+extern int eventfd (int __count, int __flags) __THROW;
+
+/* Read event counter and possibly wait for events.  */
+extern int eventfd_read (int __fd, eventfd_t *__value);
+
+/* Increment event counter.  */
+extern int eventfd_write (int __fd, eventfd_t __value);
+
+__END_DECLS
+
+#endif /* sys/eventfd.h */