44#include <stdlib.h>
55#include <string.h>
66#include <pthread.h>
7+ #include <unistd.h>
78#include <math.h>
89
910#if defined(__linux__ ) && !defined(__GLIBC__ )
@@ -136,6 +137,9 @@ typedef struct Context
136137 VALUE exception ; // pending exception or Qnil
137138 Buf req , res ; // ruby->v8 request/response, mediated by |mtx| and |cv|
138139 Buf snapshot ;
140+ pthread_t single_threaded_thr ;
141+ pid_t single_threaded_pid ;
142+ int single_threaded_thr_started ;
139143 // |rr_mtx| stands for "recursive ruby mutex"; it's used to exclude
140144 // other ruby threads but allow reentrancy from the same ruby thread
141145 // (think ruby->js->ruby->js calls)
@@ -868,18 +872,10 @@ void v8_dispatch(Context *c)
868872// only called when inside v8_call, v8_eval, or v8_pump_message_loop
869873void v8_roundtrip (Context * c , const uint8_t * * p , size_t * n )
870874{
871- struct rendezvous_nogvl * args ;
872-
873875 buf_reset (& c -> req );
874- if (single_threaded ) {
875- assert (* c -> res .buf == 'c' ); // js -> ruby callback
876- args = & (struct rendezvous_nogvl ){c , & c -> req , & c -> res };
877- rb_thread_call_with_gvl (rendezvous_callback , args );
878- } else {
879- pthread_cond_signal (& c -> cv );
880- while (!c -> req .len )
881- pthread_cond_wait (& c -> cv , & c -> mtx );
882- }
876+ pthread_cond_signal (& c -> cv );
877+ while (!c -> req .len )
878+ pthread_cond_wait (& c -> cv , & c -> mtx );
883879 buf_reset (& c -> res );
884880 * p = c -> req .buf ;
885881 * n = c -> req .len ;
@@ -991,10 +987,45 @@ static void *rendezvous_callback(void *arg)
991987 goto out ;
992988}
993989
990+ static void * single_threaded_runner (void * arg )
991+ {
992+ Context * c ;
993+
994+ c = arg ;
995+ pthread_mutex_lock (& c -> mtx );
996+ for (;;) {
997+ while (!c -> req .len && atomic_load (& c -> quit ) < 1 )
998+ pthread_cond_wait (& c -> cv , & c -> mtx );
999+ if (atomic_load (& c -> quit ) >= 1 )
1000+ break ;
1001+ v8_single_threaded_enter (c -> pst , c , dispatch );
1002+ pthread_cond_signal (& c -> cv );
1003+ }
1004+ pthread_mutex_unlock (& c -> mtx );
1005+ return NULL ;
1006+ }
1007+
1008+ static int single_threaded_runner_start (Context * c )
1009+ {
1010+ pid_t pid ;
1011+ int r ;
1012+
1013+ pid = getpid ();
1014+ if (c -> single_threaded_thr_started && c -> single_threaded_pid == pid )
1015+ return 0 ;
1016+ c -> single_threaded_thr_started = 0 ;
1017+ c -> single_threaded_pid = pid ;
1018+ r = pthread_create (& c -> single_threaded_thr , NULL , single_threaded_runner , c );
1019+ if (!r )
1020+ c -> single_threaded_thr_started = 1 ;
1021+ return r ;
1022+ }
1023+
9941024static inline void * rendezvous_nogvl (void * arg )
9951025{
9961026 struct rendezvous_nogvl * a ;
9971027 Context * c ;
1028+ int r ;
9981029
9991030 a = arg ;
10001031 c = a -> context ;
@@ -1010,7 +1041,16 @@ static inline void *rendezvous_nogvl(void *arg)
10101041 assert (c -> res .len == 0 );
10111042 buf_move (a -> req , & c -> req ); // v8 thread takes ownership of req
10121043 if (single_threaded ) {
1013- v8_single_threaded_enter (c -> pst , c , dispatch );
1044+ r = single_threaded_runner_start (c );
1045+ if (r ) {
1046+ buf_move (& c -> req , a -> req );
1047+ pthread_mutex_unlock (& c -> mtx );
1048+ c -> depth -- ;
1049+ pthread_mutex_unlock (& c -> rr_mtx );
1050+ return (void * )(intptr_t )r ;
1051+ }
1052+ pthread_cond_signal (& c -> cv );
1053+ do pthread_cond_wait (& c -> cv , & c -> mtx ); while (!c -> res .len );
10141054 } else {
10151055 pthread_cond_signal (& c -> cv );
10161056 do pthread_cond_wait (& c -> cv , & c -> mtx ); while (!c -> res .len );
@@ -1019,6 +1059,7 @@ static inline void *rendezvous_nogvl(void *arg)
10191059 pthread_mutex_unlock (& c -> mtx );
10201060 if (* a -> res -> buf == 'c' ) { // js -> ruby callback?
10211061 rb_thread_call_with_gvl (rendezvous_callback , a );
1062+ buf_reset (a -> res );
10221063 goto next ;
10231064 }
10241065 c -> depth -- ;
@@ -1028,12 +1069,16 @@ static inline void *rendezvous_nogvl(void *arg)
10281069
10291070static void rendezvous_no_des (Context * c , Buf * req , Buf * res )
10301071{
1072+ void * r ;
1073+
10311074 if (atomic_load (& c -> quit )) {
10321075 buf_reset (req );
10331076 rb_raise (context_disposed_error , "disposed context" );
10341077 }
1035- rb_nogvl (rendezvous_nogvl , & (struct rendezvous_nogvl ){c , req , res },
1036- NULL , NULL , 0 );
1078+ r = rb_nogvl (rendezvous_nogvl , & (struct rendezvous_nogvl ){c , req , res },
1079+ NULL , NULL , 0 );
1080+ if (r )
1081+ rb_raise (runtime_error , "pthread_create: %s" , strerror ((int )(intptr_t )r ));
10371082}
10381083
10391084// send request to & receive reply from v8 thread; takes ownership of |req|
@@ -1190,7 +1235,16 @@ static void *context_free_thread_do(void *arg)
11901235 Context * c ;
11911236
11921237 c = arg ;
1193- v8_single_threaded_dispose (c -> pst );
1238+ if (single_threaded && c -> single_threaded_thr_started && c -> single_threaded_pid == getpid ()) {
1239+ pthread_mutex_lock (& c -> mtx );
1240+ atomic_store (& c -> quit , 2 );
1241+ pthread_cond_signal (& c -> cv );
1242+ pthread_mutex_unlock (& c -> mtx );
1243+ pthread_join (c -> single_threaded_thr , NULL );
1244+ }
1245+ if (c -> pst )
1246+ v8_single_threaded_dispose (c -> pst );
1247+ pthread_mutex_lock (& c -> mtx );
11941248 context_destroy (c );
11951249 return NULL ;
11961250}
@@ -1284,8 +1338,18 @@ static void *context_dispose_do(void *arg)
12841338
12851339 c = arg ;
12861340 if (single_threaded ) {
1341+ pthread_mutex_lock (& c -> mtx );
1342+ while (c -> req .len || c -> res .len )
1343+ pthread_cond_wait (& c -> cv , & c -> mtx );
12871344 atomic_store (& c -> quit , 1 ); // disposed
1288- // intentionally a no-op for now
1345+ if (c -> single_threaded_thr_started && c -> single_threaded_pid == getpid ()) {
1346+ pthread_cond_signal (& c -> cv );
1347+ pthread_mutex_unlock (& c -> mtx );
1348+ pthread_join (c -> single_threaded_thr , NULL );
1349+ pthread_mutex_lock (& c -> mtx );
1350+ c -> single_threaded_thr_started = 0 ;
1351+ }
1352+ pthread_mutex_unlock (& c -> mtx );
12891353 } else {
12901354 pthread_mutex_lock (& c -> mtx );
12911355 while (c -> req .len || c -> res .len )
0 commit comments