[ruby-oci8-commit] [566] trunk/ruby-oci8: fix potential SEGV when one connection is used by more than

nobody at rubyforge.org nobody at rubyforge.org
Sat Mar 30 09:45:15 UTC 2013


Revision: 566
Author:   kubo
Date:     2013-03-30 09:45:14 +0000 (Sat, 30 Mar 2013)
Log Message:
-----------
fix potential SEGV when one connection is used by more than
two threads and temporary lobs are freed by GC.

Modified Paths:
--------------
    trunk/ruby-oci8/ChangeLog
    trunk/ruby-oci8/ext/oci8/oci8lib.c

Modified: trunk/ruby-oci8/ChangeLog
===================================================================
--- trunk/ruby-oci8/ChangeLog	2013-03-16 14:43:39 UTC (rev 565)
+++ trunk/ruby-oci8/ChangeLog	2013-03-30 09:45:14 UTC (rev 566)
@@ -1,3 +1,8 @@
+2013-03-30  KUBO Takehiro  <kubo at jiubao.org>
+	* ext/oci8/oci8lib.c: fix potential SEGV when one connection
+	    is used by more than two threads and temporary lobs are
+	    freed by GC.
+
 2013-03-16  KUBO Takehiro  <kubo at jiubao.org>
 	* ext/oci8/apiwrap.yml, ext/oci8/stmt.c: call OCIStmtPrepare2()
 	    without GVL to prevent OCI8#parse from blocking ruby itself

Modified: trunk/ruby-oci8/ext/oci8/oci8lib.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/oci8lib.c	2013-03-16 14:43:39 UTC (rev 565)
+++ trunk/ruby-oci8/ext/oci8/oci8lib.c	2013-03-30 09:45:14 UTC (rev 566)
@@ -234,64 +234,83 @@
     return (void*)(VALUE)rv;
 }
 
+typedef struct protected_call_arg {
+    void *(*func)(void *);
+    void *data;
+    oci8_svcctx_t *svcctx;
+} protected_call_arg_t;
+
+static VALUE protected_call(VALUE data)
+{
+    struct protected_call_arg *parg = (struct protected_call_arg*)data;
+    VALUE rv;
+
+    if (!NIL_P(parg->svcctx->executing_thread)) {
+        rb_raise(rb_eRuntimeError, "executing in another thread");
+    }
+    parg->svcctx->executing_thread = rb_thread_current();
+#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
+    rv = (VALUE)rb_thread_call_without_gvl(parg->func, parg->data, oci8_unblock_func, parg->svcctx);
+#else
+    rv = rb_thread_blocking_region((VALUE(*)(void*))parg->func, parg->data, oci8_unblock_func, parg->svcctx);
+#endif
+    if ((sword)rv == OCI_ERROR) {
+        if (oci8_get_error_code(oci8_errhp) == 1013) {
+            rb_raise(eOCIBreak, "Canceled by user request.");
+        }
+    }
+    return rv;
+}
+
 /* ruby 1.9 */
 sword oci8_call_without_gvl(oci8_svcctx_t *svcctx, void *(*func)(void *), void *data)
 {
     OCIError *errhp = oci8_errhp;
+    protected_call_arg_t parg;
+    sword rv;
+    int state;
 
     if (!NIL_P(svcctx->executing_thread)) {
-        rb_raise(rb_eRuntimeError /* FIXME */, "executing in another thread");
+        rb_raise(rb_eRuntimeError, "executing in another thread");
     }
-
     if (!svcctx->suppress_free_temp_lobs) {
-        oci8_temp_lob_t *lob = svcctx->temp_lobs;
-        while (lob != NULL) {
-            oci8_temp_lob_t *lob_next = lob->next;
+        oci8_temp_lob_t *lob;
+        while ((lob = svcctx->temp_lobs) != NULL) {
+            svcctx->temp_lobs = lob->next;
 
             if (svcctx->non_blocking) {
                 free_temp_lob_arg_t arg;
-                sword rv;
 
                 arg.svcctx = svcctx;
                 arg.svchp = svcctx->base.hp.svc;
                 arg.errhp = errhp;
                 arg.lob = lob->lob;
 
-                svcctx->executing_thread = rb_thread_current();
-#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
-                rv = (sword)(VALUE)rb_thread_call_without_gvl(free_temp_lob, &arg, oci8_unblock_func, svcctx);
-#else
-                rv = (sword)rb_thread_blocking_region((VALUE(*)(void*))free_temp_lob, &arg, oci8_unblock_func, svcctx);
-#endif
-                if (rv == OCI_ERROR) {
-                    if (oci8_get_error_code(errhp) == 1013) {
-                        rb_raise(eOCIBreak, "Canceled by user request.");
-                    }
+                parg.svcctx = svcctx;
+                parg.func = free_temp_lob;
+                parg.data = &arg;
+
+                rb_protect(protected_call, (VALUE)&parg, &state);
+                if (state) {
+                    lob->next = svcctx->temp_lobs;
+                    svcctx->temp_lobs = lob;
+                    rb_jump_tag(state);
                 }
             } else {
                 OCILobFreeTemporary(svcctx->base.hp.svc, errhp, lob->lob);
             }
             OCIDescriptorFree(lob->lob, OCI_DTYPE_LOB);
-
             xfree(lob);
-            svcctx->temp_lobs = lob = lob_next;
         }
     }
 
     if (svcctx->non_blocking) {
-        sword rv;
-
-        svcctx->executing_thread = rb_thread_current();
-        /* Note: executing_thread is cleard at the end of the blocking function. */
-#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
-        rv = (sword)(VALUE)rb_thread_call_without_gvl(func, data, oci8_unblock_func, svcctx);
-#else
-        rv = (sword)rb_thread_blocking_region((VALUE(*)(void*))func, data, oci8_unblock_func, svcctx);
-#endif
-        if (rv == OCI_ERROR) {
-            if (oci8_get_error_code(errhp) == 1013) {
-                rb_raise(eOCIBreak, "Canceled by user request.");
-            }
+        parg.svcctx = svcctx;
+        parg.func = func;
+        parg.data = data;
+        rv = (sword)rb_protect(protected_call, (VALUE)&parg, &state);
+        if (state) {
+            rb_jump_tag(state);
         }
         return rv;
     } else {



More information about the ruby-oci8-commit mailing list