diff options
author | Don Anderson <dda@ddanderson.com> | 2015-06-26 15:52:35 -0400 |
---|---|---|
committer | Don Anderson <dda@ddanderson.com> | 2015-06-26 15:52:35 -0400 |
commit | df854884079e1839d3353483c4988dd74fb7ecd3 (patch) | |
tree | b5f070cdfbd1ca1c79c8c19c92a0b7a1da31c616 | |
parent | 74271fa733f4381084702d94ae5ef2f600dcb8b6 (diff) | |
download | mongo-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.i | 69 | ||||
-rw-r--r-- | test/java/com/wiredtiger/test/AutoCloseTest.java | 87 |
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) { |