summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDon Anderson <dda@ddanderson.com>2015-06-26 15:52:35 -0400
committerDon Anderson <dda@ddanderson.com>2015-06-26 15:52:35 -0400
commitdf854884079e1839d3353483c4988dd74fb7ecd3 (patch)
treeb5f070cdfbd1ca1c79c8c19c92a0b7a1da31c616
parent74271fa733f4381084702d94ae5ef2f600dcb8b6 (diff)
downloadmongo-df854884079e1839d3353483c4988dd74fb7ecd3.tar.gz
WT-1964. For Java, handle cases where a session or cursor is closed
when the thread that open it no longer exists. Add tests to kill threads that have open cursors and sessions.
-rw-r--r--lang/java/wiredtiger.i69
-rw-r--r--test/java/com/wiredtiger/test/AutoCloseTest.java87
2 files changed, 138 insertions, 18 deletions
diff --git a/lang/java/wiredtiger.i b/lang/java/wiredtiger.i
index fbdfbb32212..0cbf154eaa0 100644
--- a/lang/java/wiredtiger.i
+++ b/lang/java/wiredtiger.i
@@ -177,6 +177,9 @@ static void throwWiredTigerException(JNIEnv *jenv, int err) {
}
%enddef
+/*
+ * 'Declare' a WiredTiger class. This sets up boilerplate typemaps.
+ */
%define WT_CLASS(type, class, name)
/*
* Extra 'self' elimination.
@@ -210,17 +213,32 @@ static void throwWiredTigerException(JNIEnv *jenv, int err) {
*/"
%enddef
-%define WT_CLASS_WITH_CLOSE_HANDLER(type, class, name, closeHandler, priv)
+/*
+ * Declare a WT_CLASS so that close methods call a specified closeHandler,
+ * after the WT core close function has completed. Arguments to the
+ * closeHandler are saved in advance since, as macro args, they may refer to
+ * values that are freed/zeroed by the close.
+ */
+%define WT_CLASS_WITH_CLOSE_HANDLER(type, class, name, closeHandler,
+ sess, priv)
WT_CLASS(type, class, name)
-%typemap(in, numinputs=0) class ## _CLOSED *name (JAVA_CALLBACK *jcb) {
+/*
+ * This typemap recognizes a close function via a special declaration on its
+ * first argument. See WT_HANDLE_CLOSED in wiredtiger.h . Like
+ * WT_CURSOR_NULLABLE, the WT_{CURSOR,SESSION,CONNECTION}_CLOSED typedefs
+ * are only visible to the SWIG parser.
+ */
+%typemap(in, numinputs=0) class ## _CLOSED *name (
+ WT_SESSION *savesess, JAVA_CALLBACK *jcb) {
$1 = *(type **)&jarg1;
NULL_CHECK($1, $1_name)
+ savesess = sess;
jcb = (JAVA_CALLBACK *)(priv);
}
%typemap(freearg, numinputs=0) class ## _CLOSED *name {
- closeHandler(jcb2);
+ closeHandler(jenv, savesess2, jcb2);
priv = NULL;
}
@@ -239,11 +257,11 @@ WT_CLASS(type, class, name)
%}
WT_CLASS_WITH_CLOSE_HANDLER(struct __wt_connection, WT_CONNECTION, connection,
- closeHandler, ((WT_CONNECTION_IMPL *)$1)->lang_private)
+ closeHandler, NULL, ((WT_CONNECTION_IMPL *)$1)->lang_private)
WT_CLASS_WITH_CLOSE_HANDLER(struct __wt_session, WT_SESSION, session,
- closeHandler, ((WT_SESSION_IMPL *)$1)->lang_private)
+ closeHandler, $1, ((WT_SESSION_IMPL *)$1)->lang_private)
WT_CLASS_WITH_CLOSE_HANDLER(struct __wt_cursor, WT_CURSOR, cursor,
- cursorCloseHandler, ((WT_CURSOR *)$1)->lang_private)
+ cursorCloseHandler, $1->session, ((WT_CURSOR *)$1)->lang_private)
WT_CLASS(struct __wt_async_op, WT_ASYNC_OP, op)
%define COPYDOC(SIGNATURE_CLASS, CLASS, METHOD)
@@ -312,21 +330,35 @@ enum SearchStatus { FOUND, NOTFOUND, SMALLER, LARGER };
%wrapper %{
/* Zero out SWIG's pointer to the C object,
* equivalent to 'jobj.swigCPtr = 0;' in java.
+ * We expect that either env in non-null (if called
+ * via an explicit session/cursor close() call), or
+ * that session is non-null (if called implicitly
+ * as part of connection/session close).
*/
static int
-javaClose(JNIEnv *env, JAVA_CALLBACK *jcb, jfieldID *pfid)
+javaClose(JNIEnv *env, WT_SESSION *session, JAVA_CALLBACK *jcb, jfieldID *pfid)
{
jclass cls;
jfieldID fid;
+ WT_CONNECTION_IMPL *conn;
+ /* If we were not called via an implicit close call,
+ * we won't have a JNIEnv yet. Get one from the connection,
+ * since the thread that started the session may have
+ * terminated.
+ */
+ if (env == NULL) {
+ conn = (WT_CONNECTION_IMPL *)session->connection;
+ env = ((JAVA_CALLBACK *)conn->lang_private)->jnienv;
+ }
if (pfid == NULL || *pfid == NULL) {
cls = (*env)->GetObjectClass(env, jcb->jobj);
fid = (*env)->GetFieldID(env, cls, "swigCPtr", "J");
if (pfid != NULL)
*pfid = fid;
- } else {
+ } else
fid = *pfid;
- }
+
(*env)->SetLongField(env, jcb->jobj, fid, 0L);
(*env)->DeleteGlobalRef(env, jcb->jobj);
__wt_free(jcb->session, jcb);
@@ -335,20 +367,22 @@ javaClose(JNIEnv *env, JAVA_CALLBACK *jcb, jfieldID *pfid)
/* Connection and Session close handler. */
static int
-closeHandler(JAVA_CALLBACK *jcb)
+closeHandler(JNIEnv *env, WT_SESSION *session, JAVA_CALLBACK *jcb)
{
- return (javaClose(jcb->jnienv, jcb, NULL));
+ return (javaClose(env, session, jcb, NULL));
}
/* Cursor specific close handler. */
static int
-cursorCloseHandler(JAVA_CALLBACK *jcb)
+cursorCloseHandler(JNIEnv *env, WT_SESSION *wt_session, JAVA_CALLBACK *jcb)
{
int ret;
JAVA_CALLBACK *sess_jcb;
+ WT_SESSION_IMPL *session;
- sess_jcb = (JAVA_CALLBACK *)jcb->session->lang_private;
- ret = javaClose(jcb->jnienv, jcb,
+ session = (WT_SESSION_IMPL *)wt_session;
+ sess_jcb = (JAVA_CALLBACK *)session->lang_private;
+ ret = javaClose(env, wt_session, jcb,
sess_jcb ? &sess_jcb->cptr_fid : NULL);
return (ret);
@@ -364,9 +398,10 @@ javaCloseHandler(WT_EVENT_HANDLER *handler, WT_SESSION *session,
WT_UNUSED(handler);
if (cursor != NULL)
- ret = cursorCloseHandler((JAVA_CALLBACK *)cursor->lang_private);
+ ret = cursorCloseHandler(NULL, session, (JAVA_CALLBACK *)
+ cursor->lang_private);
else
- ret = closeHandler((JAVA_CALLBACK *)
+ ret = closeHandler(NULL, session, (JAVA_CALLBACK *)
((WT_SESSION_IMPL *)session)->lang_private);
return (ret);
}
@@ -474,7 +509,7 @@ err: __wt_err(session, ret, "Java async callback error");
}
/* Invalidate the AsyncOp, further use throws NullPointerException. */
- ret = javaClose(jenv, jcb, &conn_jcb->asynccptr_fid);
+ ret = javaClose(jenv, NULL, jcb, &conn_jcb->asynccptr_fid);
(*jenv)->DeleteGlobalRef(jenv, jcallback);
diff --git a/test/java/com/wiredtiger/test/AutoCloseTest.java b/test/java/com/wiredtiger/test/AutoCloseTest.java
index 22f0ade7452..b55dbb0df03 100644
--- a/test/java/com/wiredtiger/test/AutoCloseTest.java
+++ b/test/java/com/wiredtiger/test/AutoCloseTest.java
@@ -44,6 +44,35 @@ import org.junit.runners.JUnit4;
public class AutoCloseTest {
/*
+ * Inner class to hold a session and cursor created in
+ * in a thread that may be closed in another thread.
+ */
+ static class OpenSession extends Thread {
+ private final Connection conn;
+ private Session sess;
+ private Cursor cur;
+ private int threadnum;
+ public OpenSession(Connection conn, int threadnum) {
+ this.conn = conn;
+ this.threadnum = threadnum;
+ }
+
+ public void run() {
+ sess = conn.open_session(null);
+ sess.create("table:autoclose", "key_format=S,value_format=S");
+ cur = sess.open_cursor("table:autoclose", null, null);
+ cur.putKeyString("key" + threadnum);
+ cur.putValueString("value" + threadnum);
+ cur.insert();
+ }
+
+ public void close() {
+ cur.close();
+ sess.close(null);
+ }
+ }
+
+ /*
* Connvalid tells us that we really closed the connection.
* That allows teardown to reliably clean up so that
* a single failure in one test does not cascade.
@@ -232,6 +261,7 @@ public class AutoCloseTest {
assertEquals(caught, true);
}
+ @Test
public void autoCloseSession03()
throws WiredTigerPackingException {
Session s = sessionSetup();
@@ -245,7 +275,8 @@ public class AutoCloseTest {
s.open_cursor("table:t", null, null);
}
catch (NullPointerException iae) {
- assertEquals(iae.toString().contains("session is null"), true);
+ // The exception message is different, but still informative.
+ assertEquals(iae.toString().contains("self is null"), true);
caught = true;
}
assertEquals(caught, true);
@@ -275,6 +306,60 @@ public class AutoCloseTest {
assertEquals(caught, true);
}
+ public void multithreadedHelper(boolean explicitClose, int nthreads)
+ throws WiredTigerPackingException {
+ Session s = sessionSetup();
+ Cursor c = populate(s);
+
+ OpenSession[] threads = new OpenSession[nthreads];
+ for (int i = 0; i < nthreads; i++) {
+ threads[i] = new OpenSession(conn, i);
+ threads[i].start();
+ }
+
+ for (int i = 0; i < nthreads; i++) {
+ try {
+ threads[i].join();
+ if (explicitClose)
+ threads[i].close();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ try {
+ conn.close("");
+ connvalid = false;
+ }
+ catch (Exception e) {
+ // nothing
+ }
+ assertEquals(connvalid, false);
+ }
+
+ @Test
+ public void autoCloseConnection02()
+ throws WiredTigerPackingException {
+ multithreadedHelper(false, 2);
+ }
+
+ @Test
+ public void autoCloseConnection03()
+ throws WiredTigerPackingException {
+ multithreadedHelper(true, 2);
+ }
+
+ @Test
+ public void autoCloseConnection04()
+ throws WiredTigerPackingException {
+ multithreadedHelper(false, 10);
+ }
+
+ @Test
+ public void autoCloseConnection05()
+ throws WiredTigerPackingException {
+ multithreadedHelper(true, 10);
+ }
+
@After
public void teardown() {
if (connvalid) {