summaryrefslogtreecommitdiffstats
path: root/src/3rdparty
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty')
-rw-r--r--src/3rdparty/forkfd/forkfd.c495
-rw-r--r--src/3rdparty/forkfd/forkfd.h58
-rw-r--r--src/3rdparty/forkfd/forkfd_gcc.h76
3 files changed, 629 insertions, 0 deletions
diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c
new file mode 100644
index 0000000000..b781b517a6
--- /dev/null
+++ b/src/3rdparty/forkfd/forkfd.c
@@ -0,0 +1,495 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Intel Corporation
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+# define _POSIX_C_SOURCE 200809L
+# define _XOPEN_SOURCE 500
+#endif
+#include "forkfd.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __linux__
+# define HAVE_PIPE2 1
+# define HAVE_EVENTFD 1
+# include <sys/eventfd.h>
+#endif
+
+#if _POSIX_VERSION-0 >= 200809L || _XOPEN_VERSION-0 >= 500
+# define HAVE_WAITID 1
+#endif
+
+#ifndef FFD_ATOMIC_RELAXED
+# include "forkfd_gcc.h"
+#endif
+
+#define CHILDREN_IN_SMALL_ARRAY 16
+#define CHILDREN_IN_BIG_ARRAY 256
+#define sizeofarray(array) (sizeof(array)/sizeof(array[0]))
+#define EINTR_LOOP(ret, call) \
+ do { \
+ ret = call; \
+ } while (ret == -1 && errno == EINTR)
+
+typedef struct process_info
+{
+ ffd_atomic_int pid;
+ int deathPipe;
+} ProcessInfo;
+
+struct BigArray;
+typedef struct Header
+{
+ ffd_atomic_pointer(struct BigArray) nextArray;
+ ffd_atomic_int busyCount;
+} Header;
+
+typedef struct BigArray
+{
+ Header header;
+ ProcessInfo entries[CHILDREN_IN_BIG_ARRAY];
+} BigArray;
+
+typedef struct SmallArray
+{
+ Header header;
+ ProcessInfo entries[CHILDREN_IN_SMALL_ARRAY];
+} SmallArray;
+static SmallArray children;
+
+static struct sigaction old_sigaction;
+static pthread_once_t forkfd_initialization = PTHREAD_ONCE_INIT;
+static ffd_atomic_int forkfd_status = FFD_ATOMIC_INIT(0);
+
+static ProcessInfo *tryAllocateInSection(Header *header, ProcessInfo entries[], int maxCount)
+{
+ /* we use ACQUIRE here because the signal handler might have released the PID */
+ int busyCount = ffd_atomic_add_fetch(&header->busyCount, 1, FFD_ATOMIC_ACQUIRE);
+ if (busyCount <= maxCount) {
+ /* there's an available entry in this section, find it and take it */
+ int i;
+ for (i = 0; i < maxCount; ++i) {
+ /* if the PID is 0, it's free; mark it as used by swapping it with -1 */
+ int expected_pid = 0;
+ if (ffd_atomic_compare_exchange(&entries[i].pid, &expected_pid,
+ -1, FFD_ATOMIC_RELAXED, FFD_ATOMIC_RELAXED))
+ return &entries[i];
+ }
+ }
+
+ /* there isn't an available entry, undo our increment */
+ ffd_atomic_add_fetch(&header->busyCount, -1, FFD_ATOMIC_RELAXED);
+ return NULL;
+}
+
+static ProcessInfo *allocateInfo(Header **header)
+{
+ Header *currentHeader = &children.header;
+
+ /* try to find an available entry in the small array first */
+ ProcessInfo *info =
+ tryAllocateInSection(currentHeader, children.entries, sizeofarray(children.entries));
+
+ /* go on to the next arrays */
+ while (info == NULL) {
+ BigArray *array = ffd_atomic_load(&currentHeader->nextArray, FFD_ATOMIC_ACQUIRE);
+ if (array == NULL) {
+ /* allocate an array and try to use it */
+ BigArray *allocatedArray = (BigArray *)calloc(1, sizeof(BigArray));
+ if (allocatedArray == NULL)
+ return NULL;
+
+ if (ffd_atomic_compare_exchange(&currentHeader->nextArray, &array, allocatedArray,
+ FFD_ATOMIC_RELEASE, FFD_ATOMIC_ACQUIRE)) {
+ /* success */
+ array = allocatedArray;
+ } else {
+ /* failed, the atomic updated 'array' */
+ free(allocatedArray);
+ }
+ }
+
+ currentHeader = &array->header;
+ info = tryAllocateInSection(currentHeader, array->entries, sizeofarray(array->entries));
+ }
+
+ *header = currentHeader;
+ return info;
+}
+
+static int tryReaping(pid_t pid, siginfo_t *info)
+{
+ /* reap the child */
+#ifdef HAVE_WAITID
+ // we have waitid(2), which fills in siginfo_t for us
+ info->si_pid = 0;
+ return waitid(P_PID, pid, info, WEXITED | WNOHANG) == 0 && info->si_pid == pid;
+#else
+ int status;
+ if (waitpid(pid, &status, WNOHANG) <= 0)
+ return 0; // child did not change state
+
+ info->si_signo = SIGCHLD;
+ info->si_utime = 0;
+ info->si_stime = 0;
+ info->si_pid = pid;
+ if (WIFEXITED(status)) {
+ info->si_code = CLD_EXITED;
+ info->si_status = WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ info->si_code = CLD_KILLED;
+# ifdef WCOREDUMP
+ if (WCOREDUMP(status))
+ info->si_code = CLD_DUMPED;
+# endif
+ info->si_status = WTERMSIG(status);
+ }
+
+ return 1;
+#endif
+}
+
+static void freeInfo(Header *header, ProcessInfo *entry)
+{
+ entry->deathPipe = -1;
+ entry->pid = 0;
+
+ ffd_atomic_add_fetch(&header->busyCount, -1, FFD_ATOMIC_RELEASE);
+ assert(header->busyCount >= 0);
+}
+
+static void notifyAndFreeInfo(Header *header, ProcessInfo *entry, siginfo_t *info)
+{
+ ssize_t ret;
+ EINTR_LOOP(ret, write(entry->deathPipe, info, sizeof(*info)));
+ EINTR_LOOP(ret, close(entry->deathPipe));
+
+ freeInfo(header, entry);
+}
+
+static void sigchld_handler(int signum)
+{
+ /*
+ * This is a signal handler, so we need to be careful about which functions
+ * we can call. See the full, official listing in the POSIX.1-2008
+ * specification at:
+ * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03
+ *
+ */
+
+ if (ffd_atomic_load(&forkfd_status, FFD_ATOMIC_RELAXED) == 1) {
+ /* is this one of our children? */
+ BigArray *array;
+ siginfo_t info;
+ int i;
+
+ for (i = 0; i < (int)sizeofarray(children.entries); ++i) {
+ int pid = ffd_atomic_load(&children.entries[i].pid, FFD_ATOMIC_ACQUIRE);
+ if (pid > 0 && tryReaping(pid, &info)) {
+ /* this is our child, send notification and free up this entry */
+ notifyAndFreeInfo(&children.header, &children.entries[i], &info);
+ }
+ }
+
+ /* try the arrays */
+ array = ffd_atomic_load(&children.header.nextArray, FFD_ATOMIC_ACQUIRE);
+ while (array != NULL) {
+ for (i = 0; i < (int)sizeofarray(array->entries); ++i) {
+ int pid = ffd_atomic_load(&array->entries[i].pid, FFD_ATOMIC_ACQUIRE);
+ if (pid > 0 && tryReaping(pid, &info)) {
+ /* this is our child, send notification and free up this entry */
+ notifyAndFreeInfo(&array->header, &array->entries[i], &info);
+ }
+ }
+
+ array = ffd_atomic_load(&array->header.nextArray, FFD_ATOMIC_ACQUIRE);
+ }
+ }
+
+ if (old_sigaction.sa_handler != SIG_IGN && old_sigaction.sa_handler != SIG_DFL)
+ old_sigaction.sa_handler(signum);
+}
+
+static void forkfd_initialize()
+{
+ /* install our signal handler */
+ struct sigaction action;
+ memset(&action, 0, sizeof action);
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = SA_NOCLDSTOP;
+ action.sa_handler = sigchld_handler;
+
+ /* ### RACE CONDITION
+ * The sigaction function does a memcpy from an internal buffer
+ * to old_sigaction, which we use in the SIGCHLD handler. If a
+ * SIGCHLD is delivered before or during that memcpy, the handler will
+ * see an inconsistent state.
+ *
+ * There is no solution. pthread_sigmask doesn't work here because the
+ * signal could be delivered to another thread.
+ */
+ sigaction(SIGCHLD, &action, &old_sigaction);
+
+#ifndef __GNUC__
+ atexit(cleanup);
+#endif
+
+ ffd_atomic_store(&forkfd_status, 1, FFD_ATOMIC_RELAXED);
+}
+
+#ifdef __GNUC__
+__attribute((destructor, unused)) static void cleanup();
+#endif
+
+static void cleanup()
+{
+ BigArray *array;
+ /* This function is not thread-safe!
+ * It must only be called when the process is shutting down.
+ * At shutdown, we expect no one to be calling forkfd(), so we don't
+ * need to be thread-safe with what is done there.
+ *
+ * But SIGCHLD might be delivered to any thread, including this one.
+ * There's no way to prevent that. The correct solution would be to
+ * cooperatively delete. We don't do that.
+ */
+ if (ffd_atomic_load(&forkfd_status, FFD_ATOMIC_RELAXED) == 0)
+ return;
+
+ /* notify the handler that we're no longer in operation */
+ ffd_atomic_store(&forkfd_status, 0, FFD_ATOMIC_RELAXED);
+
+ /* free any arrays we might have */
+ array = children.header.nextArray;
+ while (array != NULL) {
+ BigArray *next = array->header.nextArray;
+ free(array);
+ array = next;
+ }
+}
+
+static int create_pipe(int filedes[], int flags)
+{
+ int ret;
+#ifdef HAVE_PIPE2
+ /* use pipe2(2) whenever possible, since it can thread-safely create a
+ * cloexec pair of pipes. Without it, we have a race condition setting
+ * FD_CLOEXEC
+ */
+ ret = pipe2(filedes, O_CLOEXEC);
+ if (ret == -1)
+ return ret;
+
+ if ((flags & FFD_CLOEXEC) == 0)
+ fcntl(filedes[0], F_SETFD, 0);
+#else
+ ret = pipe(filedes);
+ if (ret == -1)
+ return ret;
+
+ fcntl(filedes[1], F_SETFD, FD_CLOEXEC);
+ if (flags & FFD_CLOEXEC)
+ fcntl(filedes[0], F_SETFD, FD_CLOEXEC);
+#endif
+ if (flags & FFD_NONBLOCK)
+ fcntl(filedes[0], F_SETFL, fcntl(filedes[0], F_GETFL) | O_NONBLOCK);
+ return ret;
+}
+
+/**
+ * @brief forkfd returns a file descriptor representing a child process
+ * @return a file descriptor, or -1 in case of failure
+ *
+ * forkfd() creates a file descriptor that can be used to be notified of when a
+ * child process exits. This file descriptor can be monitored using select(2),
+ * poll(2) or similar mechanisms.
+ *
+ * The @a flags parameter can contain the following values ORed to change the
+ * behaviour of forkfd():
+ *
+ * @li @c FFD_NONBLOCK Set the O_NONBLOCK file status flag on the new open file
+ * descriptor. Using this flag saves extra calls to fnctl(2) to achieve the same
+ * result.
+ *
+ * @li @c FFD_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the new file
+ * descriptor. You probably want to set this flag, since forkfd() does not work
+ * if the original parent process dies.
+ *
+ * The file descriptor returned by forkfd() supports the following operations:
+ *
+ * @li read(2) When the child process exits, then the buffer supplied to
+ * read(2) is used to return information about the status of the child in the
+ * form of one @c siginfo_t structure. The buffer must be at least
+ * sizeof(siginfo_t) bytes. The return value of read(2) is the total number of
+ * bytes read.
+ *
+ * @li poll(2), select(2) (and similar) The file descriptor is readable (the
+ * select(2) readfds argument; the poll(2) POLLIN flag) if the child has exited
+ * or signalled via SIGCHLD.
+ *
+ * @li close(2) When the file descriptor is no longer required it should be closed.
+ */
+int forkfd(int flags, pid_t *ppid)
+{
+ Header *header;
+ ProcessInfo *info;
+ pid_t pid;
+ int fd = -1;
+ int death_pipe[2];
+ int sync_pipe[2];
+ int ret;
+#ifdef __linux__
+ int efd;
+#endif
+
+ (void) pthread_once(&forkfd_initialization, forkfd_initialize);
+
+ info = allocateInfo(&header);
+ if (info == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* create the pipes before we fork */
+ if (create_pipe(death_pipe, flags) == -1)
+ goto err_free; /* failed to create the pipes, pass errno */
+
+#ifdef HAVE_EVENTFD
+ /* try using an eventfd, which consumes less resources */
+ efd = eventfd(0, EFD_CLOEXEC);
+ if (efd == -1)
+#endif
+ {
+ /* try a pipe */
+ if (create_pipe(sync_pipe, O_CLOEXEC) == -1) {
+ /* failed both at eventfd and pipe; fail and pass errno */
+ goto err_close;
+ }
+ }
+
+ /* now fork */
+ pid = fork();
+ if (pid == -1)
+ goto err_close2; /* failed to fork, pass errno */
+ if (ppid)
+ *ppid = pid;
+
+ /*
+ * We need to store the child's PID in the info structure, so
+ * the SIGCHLD handler knows that this child is present and it
+ * knows the writing end of the pipe to pass information on.
+ * However, the child process could exit before we stored the
+ * information (or the handler could run for other children exiting).
+ * We prevent that from happening by blocking the child process in
+ * a read(2) until we're finished storing the information.
+ */
+ if (pid == 0) {
+ /* this is the child process */
+ /* first, wait for the all clear */
+#ifdef HAVE_EVENTFD
+ if (efd != -1) {
+ eventfd_t val64;
+ EINTR_LOOP(ret, eventfd_read(efd, &val64));
+ EINTR_LOOP(ret, close(efd));
+ } else
+#endif
+ {
+ char c;
+ EINTR_LOOP(ret, close(sync_pipe[1]));
+ EINTR_LOOP(ret, read(sync_pipe[0], &c, sizeof c));
+ EINTR_LOOP(ret, close(sync_pipe[0]));
+ }
+
+ /* now close the pipes and return to the caller */
+ EINTR_LOOP(ret, close(death_pipe[0]));
+ EINTR_LOOP(ret, close(death_pipe[1]));
+ fd = FFD_CHILD_PROCESS;
+ } else {
+ /* parent process */
+ info->deathPipe = death_pipe[1];
+ fd = death_pipe[0];
+ ffd_atomic_store(&info->pid, pid, FFD_ATOMIC_RELEASE);
+
+ /* release the child */
+#ifdef HAVE_EVENTFD
+ if (efd != -1) {
+ eventfd_t val64 = 42;
+ EINTR_LOOP(ret, eventfd_write(efd, val64));
+ EINTR_LOOP(ret, close(efd));
+ } else
+#endif
+ {
+ /*
+ * Usually, closing would be enough to make read(2) return and the child process
+ * continue. We need to write here: another thread could be calling forkfd at the
+ * same time, which means auxpipe[1] might be open in another child process.
+ */
+ EINTR_LOOP(ret, close(sync_pipe[0]));
+ EINTR_LOOP(ret, write(sync_pipe[1], "", 1));
+ EINTR_LOOP(ret, close(sync_pipe[1]));
+ }
+ }
+
+ return fd;
+
+err_close2:
+#ifdef HAVE_EVENTFD
+ if (efd != -1) {
+ EINTR_LOOP(ret, close(efd));
+ } else
+#endif
+ {
+ EINTR_LOOP(ret, close(sync_pipe[0]));
+ EINTR_LOOP(ret, close(sync_pipe[1]));
+ }
+err_close:
+ EINTR_LOOP(ret, close(death_pipe[0]));
+ EINTR_LOOP(ret, close(death_pipe[1]));
+err_free:
+ /* free the info pointer */
+ freeInfo(header, info);
+ return -1;
+}
diff --git a/src/3rdparty/forkfd/forkfd.h b/src/3rdparty/forkfd/forkfd.h
new file mode 100644
index 0000000000..de75f84bc0
--- /dev/null
+++ b/src/3rdparty/forkfd/forkfd.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Intel Corporation
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FORKFD_H
+#define FORKFD_H
+
+#include <fcntl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FFD_CLOEXEC O_CLOEXEC
+#define FFD_NONBLOCK O_NONBLOCK
+
+#define FFD_CHILD_PROCESS (-2)
+
+int forkfd(int flags, pid_t *ppid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // FORKFD_H
diff --git a/src/3rdparty/forkfd/forkfd_gcc.h b/src/3rdparty/forkfd/forkfd_gcc.h
new file mode 100644
index 0000000000..e5cbbe60e0
--- /dev/null
+++ b/src/3rdparty/forkfd/forkfd_gcc.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Intel Corporation
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FFD_ATOMIC_GCC_H
+#define FFD_ATOMIC_GCC_H
+
+/* atomics */
+/* we'll use the GCC 4.7 atomic builtins
+ * See http://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html#_005f_005fatomic-Builtins
+ * Or in texinfo: C Extensions > __atomic Builtins
+ */
+typedef int ffd_atomic_int;
+#define ffd_atomic_pointer(type) type*
+
+#define FFD_ATOMIC_INIT(val) (val)
+
+#define FFD_ATOMIC_RELAXED __ATOMIC_RELAXED
+#define FFD_ATOMIC_ACQUIRE __ATOMIC_ACQUIRE
+#define FFD_ATOMIC_RELEASE __ATOMIC_RELEASE
+// acq_rel & cst not necessary
+
+#if !defined(__GNUC__) || \
+ ((__GNUC__ - 0) * 100 + (__GNUC_MINOR__ - 0)) < 407 || \
+ (defined(__INTEL_COMPILER) && __INTEL_COMPILER-0 < 1310) || \
+ (defined(__clang__) && ((__clang_major__-0) * 100 + (__clang_minor-0)) < 303)
+#define ffd_atomic_load_n(ptr,order) *(ptr)
+#define ffd_atomic_store_n(ptr,val,order) (*(ptr) = (val), (void)0)
+#define ffd_atomic_exchange_n(ptr,val,order) __sync_lock_test_and_set(ptr, val)
+#define ffd_atomic_compare_exchange_n(ptr,expected,desired,weak,order1,order2) \
+ __sync_bool_compare_and_swap(ptr, *(expected), desired) ? 1 : \
+ (*(expected) = *(ptr), 0)
+#define ffd_atomic_add_fetch(ptr,val,order) __sync_add_and_fetch(ptr, val)
+#else
+#define ffd_atomic_load(ptr,order) __atomic_load_n(ptr, order)
+#define ffd_atomic_store(ptr,val,order) __atomic_store_n(ptr, val, order)
+#define ffd_atomic_exchange(ptr,val,order) __atomic_exchange_n(ptr, val, order)
+#define ffd_atomic_compare_exchange(ptr,expected,desired,order1,order2) \
+ __atomic_compare_exchange_n(ptr, expected, desired, 1, order1, order2)
+#define ffd_atomic_add_fetch(ptr,val,order) __atomic_add_fetch(ptr, val, order)
+#endif
+
+#endif