Skip to content

Instantly share code, notes, and snippets.

@lmb
Last active May 31, 2017 10:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lmb/a35e7b9566fa79e7bf971ac21bbb9efb to your computer and use it in GitHub Desktop.
Save lmb/a35e7b9566fa79e7bf971ac21bbb9efb to your computer and use it in GitHub Desktop.
[PATCH] LMDB: Suppress SIGPIPE in mdb_env_copythr on OS X
From 5092fd69abc0d9fd81b62a0a00553ace556cbc0a Mon Sep 17 00:00:00 2001
From: Lorenz Bauer <lmb@xxx>
Date: Wed, 15 Feb 2017 18:58:05 +0000
Subject: [PATCH] Use F_SETNOSIGPIPE on OS X
It seems like OS X delivers SIGPIPE to the whole process, instead of the generating thread, as on other UNIXes. Therefore the current code sometimes works, but mostly doesn't, since the call to sigwait doesn't happen quickly enough.
Instead of masking SIGPIPE, we use an OS X specific fcntl to disable SIGPIPE for that particular fd.
---
libraries/liblmdb/mdb.c | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c
index ac381d9..cb08d4f 100644
--- a/libraries/liblmdb/mdb.c
+++ b/libraries/liblmdb/mdb.c
@@ -9815,7 +9815,14 @@ mdb_env_copythr(void *arg)
#else
int len;
#define DO_WRITE(rc, fd, ptr, w2, len) len = write(fd, ptr, w2); rc = (len >= 0)
-#ifdef SIGPIPE
+#ifdef F_SETNOSIGPIPE
+ /* OS X delivers SIGPIPE to the whole process, not the thread that caused it.
+ * Disable SIGPIPE using platform specific fcntl.
+ */
+ int enabled = 1;
+ if ((rc = fcntl(my->mc_fd, F_SETNOSIGPIPE, &enabled)) != 0)
+ my->mc_error = errno;
+#elif defined SIGPIPE
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGPIPE);
@@ -9838,15 +9845,6 @@ again:
DO_WRITE(rc, my->mc_fd, ptr, wsize, len);
if (!rc) {
rc = ErrCode();
-#if defined(SIGPIPE) && !defined(_WIN32)
- if (rc == EPIPE) {
- /* Collect the pending SIGPIPE, otherwise at least OS X
- * gives it to the process on thread-exit (ITS#8504).
- */
- int tmp;
- sigwait(&set, &tmp);
- }
-#endif
break;
} else if (len > 0) {
rc = MDB_SUCCESS;
--
2.9.0
From ee1f53872db5d60c612ada95695b3f1bb0d49250 Mon Sep 17 00:00:00 2001
From: Lorenz Bauer <lmb@cloudflare.com>
Date: Wed, 31 May 2017 11:07:38 +0100
Subject: [PATCH 2/2] Add tests for SIGPIPE behaviour
---
libraries/liblmdb/Makefile | 1 +
libraries/liblmdb/mtest7.c | 101 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 102 insertions(+)
create mode 100644 libraries/liblmdb/mtest7.c
diff --git a/libraries/liblmdb/Makefile b/libraries/liblmdb/Makefile
index 72d0984..1b4567a 100644
--- a/libraries/liblmdb/Makefile
+++ b/libraries/liblmdb/Makefile
@@ -78,6 +78,7 @@ mtest3: mtest3.o liblmdb.a
mtest4: mtest4.o liblmdb.a
mtest5: mtest5.o liblmdb.a
mtest6: mtest6.o liblmdb.a
+mtest7: mtest7.o liblmdb.a
mdb.o: mdb.c lmdb.h midl.h
$(CC) $(CFLAGS) $(CPPFLAGS) -c mdb.c
diff --git a/libraries/liblmdb/mtest7.c b/libraries/liblmdb/mtest7.c
new file mode 100644
index 0000000..dc84dd9
--- /dev/null
+++ b/libraries/liblmdb/mtest7.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2011-2017 Howard Chu, Symas Corp.
+ * Copyright 2017 Lorenz Bauer, Cloudflare, Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/*
+ * Tests conditions under which SIGPIPE can be generated, and verifies we return EPIPE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include "lmdb.h"
+
+#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
+#define E_ERRNO(expr) CHECK((expr) != -1 || (rc = errno) != 0, #expr)
+#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
+ "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
+
+/* Has to be larger than socketpair send / receive buffer */
+#define VALSIZE (10*1024*1024)
+
+void *threadfn(void *arg)
+{
+ char buf[4096];
+ int fd = (intptr_t)arg;
+ read(fd, (void*)buf, sizeof(buf));
+ close(fd);
+ return NULL;
+}
+
+int main(int argc,char * argv[])
+{
+ int rc, nfail = 0;
+ MDB_env *env;
+ MDB_txn *txn;
+ MDB_dbi dbi;
+ MDB_val key, val;
+ int pipefd[2];
+ int socketfd[2];
+ int filefd;
+ pthread_t thr;
+
+ key.mv_size = 7;
+ key.mv_data = "testing";
+ val.mv_size = VALSIZE;
+ val.mv_data = (void*)malloc(VALSIZE);
+
+ E(mdb_env_create(&env));
+ E(mdb_env_set_mapsize(env, VALSIZE*3));
+ E(mdb_env_open(env, "./testdb", 0, 0664));
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ E(mdb_dbi_open(txn, NULL, 0, &dbi));
+ E(mdb_put(txn, dbi, &key, &val, 0));
+ E(mdb_txn_commit(txn));
+ txn = NULL;
+
+ // pipe
+ E_ERRNO(pipe(pipefd));
+ E_ERRNO(close(pipefd[0]));
+ rc = mdb_env_copyfd2(env, pipefd[1], MDB_CP_COMPACT);
+ if (rc != EPIPE) {
+ nfail++;
+ fprintf(stderr, "invalid pipe return code: %s\n", mdb_strerror(rc));
+ }
+
+ // socketpair
+ E_ERRNO(socketpair(AF_UNIX, SOCK_STREAM, 0, socketfd));
+ E(pthread_create(&thr, NULL, threadfn, (void *)(intptr_t)socketfd[0]));
+ rc = mdb_env_copyfd2(env, socketfd[1], MDB_CP_COMPACT);
+ if (rc != EPIPE && rc != ENOTCONN) {
+ nfail++;
+ fprintf(stderr, "invalid socketpair return code: %d %s\n", rc, mdb_strerror(rc));
+ }
+
+ // file, sanity check
+ E_ERRNO(filefd = open("./testdb/temp.out", O_WRONLY|O_CREAT|O_TRUNC, 0644));
+ rc = mdb_env_copyfd2(env, filefd, MDB_CP_COMPACT);
+ if (rc) {
+ nfail++;
+ fprintf(stderr, "invalid file return code: %s\n", mdb_strerror(rc));
+ }
+ close(filefd);
+
+ mdb_dbi_close(env, dbi);
+ mdb_env_close(env);
+ free(val.mv_data);
+ return nfail;
+}
--
2.9.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment