ruby-changes:51008
From: normal <ko1@a...>
Date: Sat, 21 Apr 2018 07:53:42 +0900 (JST)
Subject: [ruby-changes:51008] normal:r63215 (trunk): thread_sync: redo r62934 to use fork_gen
normal 2018-04-21 07:53:37 +0900 (Sat, 21 Apr 2018) New Revision: 63215 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=63215 Log: thread_sync: redo r62934 to use fork_gen Instead of maintaining linked-lists to store all rb_queue/rb_szqueue/rb_condvar structs; store only a fork_gen serial number to simplify management of these items. This reduces initialization costs and avoids the up-front cost of resetting all Queue/SizedQueue/ConditionVariable objects at fork while saving 8 bytes per-structure on 64-bit. There are no savings on 32-bit. * thread.c (rb_thread_atfork_internal): remove rb_thread_sync_reset_all call * thread_sync.c (rb_thread_sync_reset_all): remove * thread_sync.c (queue_live): remove * thread_sync.c (queue_free): remove * thread_sync.c (struct rb_queue): s/live/fork_gen/ * thread_sync.c (queue_data_type): use default free * thread_sync.c (queue_alloc): remove list_add * thread_sync.c (queue_fork_check): new function * thread_sync.c (queue_ptr): call queue_fork_check * thread_sync.c (szqueue_free): remove * thread_sync.c (szqueue_data_type): use default free * thread_sync.c (szqueue_alloc): remove list_add * thread_sync.c (szqueue_ptr): check fork_gen via queue_fork_check * thread_sync.c (struct rb_condvar): s/live/fork_gen/ * thread_sync.c (condvar_free): remove * thread_sync.c (cv_data_type): use default free * thread_sync.c (condvar_ptr): check fork_gen * thread_sync.c (condvar_alloc): remove list_add [ruby-core:86316] [Bug #14634] Modified files: trunk/thread.c trunk/thread_sync.c Index: thread.c =================================================================== --- thread.c (revision 63214) +++ thread.c (revision 63215) @@ -4212,7 +4212,6 @@ rb_thread_atfork_internal(rb_thread_t *t https://github.com/ruby/ruby/blob/trunk/thread.c#L4212 rb_vm_living_threads_init(vm); rb_vm_living_threads_insert(vm, th); vm->fork_gen++; - rb_thread_sync_reset_all(); vm->sleeper = 0; clear_coverage(); Index: thread_sync.c =================================================================== --- thread_sync.c (revision 63214) +++ thread_sync.c (revision 63215) @@ -62,7 +62,6 @@ typedef struct rb_mutex_struct { https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L62 static void rb_mutex_abandon_all(rb_mutex_t *mutexes); static void rb_mutex_abandon_keeping_mutexes(rb_thread_t *th); static void rb_mutex_abandon_locking_mutex(rb_thread_t *th); -static void rb_thread_sync_reset_all(void); #endif static const char* rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t volatile *th); @@ -547,17 +546,15 @@ void rb_mutex_allow_trap(VALUE self, int https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L546 /* Queue */ #define queue_waitq(q) UNALIGNED_MEMBER_PTR(q, waitq) -#define queue_live(q) UNALIGNED_MEMBER_PTR(q, live) PACKED_STRUCT_UNALIGNED(struct rb_queue { - struct list_node live; struct list_head waitq; + rb_serial_t fork_gen; const VALUE que; int num_waiting; }); #define szqueue_waitq(sq) UNALIGNED_MEMBER_PTR(sq, q.waitq) #define szqueue_pushq(sq) UNALIGNED_MEMBER_PTR(sq, pushq) -#define szqueue_live(sq) UNALIGNED_MEMBER_PTR(sq, q.live) PACKED_STRUCT_UNALIGNED(struct rb_szqueue { struct rb_queue q; int num_waiting_push; @@ -574,14 +571,6 @@ queue_mark(void *ptr) https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L571 rb_gc_mark(q->que); } -static void -queue_free(void *ptr) -{ - struct rb_queue *q = ptr; - list_del(queue_live(q)); - ruby_xfree(ptr); -} - static size_t queue_memsize(const void *ptr) { @@ -590,7 +579,7 @@ queue_memsize(const void *ptr) https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L579 static const rb_data_type_t queue_data_type = { "queue", - {queue_mark, queue_free, queue_memsize,}, + {queue_mark, RUBY_TYPED_DEFAULT_FREE, queue_memsize,}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED }; @@ -602,16 +591,32 @@ queue_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L591 obj = TypedData_Make_Struct(klass, struct rb_queue, &queue_data_type, q); list_head_init(queue_waitq(q)); - list_add(&queue_list, queue_live(q)); return obj; } +static int +queue_fork_check(struct rb_queue *q) +{ + rb_serial_t fork_gen = GET_VM()->fork_gen; + + if (q->fork_gen == fork_gen) { + return 0; + } + /* forked children can't reach into parent thread stacks */ + q->fork_gen = fork_gen; + list_head_init(queue_waitq(q)); + q->num_waiting = 0; + return 1; +} + static struct rb_queue * queue_ptr(VALUE obj) { struct rb_queue *q; TypedData_Get_Struct(obj, struct rb_queue, &queue_data_type, q); + queue_fork_check(q); + return q; } @@ -625,14 +630,6 @@ szqueue_mark(void *ptr) https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L630 queue_mark(&sq->q); } -static void -szqueue_free(void *ptr) -{ - struct rb_szqueue *sq = ptr; - list_del(szqueue_live(sq)); - ruby_xfree(ptr); -} - static size_t szqueue_memsize(const void *ptr) { @@ -641,7 +638,7 @@ szqueue_memsize(const void *ptr) https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L638 static const rb_data_type_t szqueue_data_type = { "sized_queue", - {szqueue_mark, szqueue_free, szqueue_memsize,}, + {szqueue_mark, RUBY_TYPED_DEFAULT_FREE, szqueue_memsize,}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED }; @@ -653,7 +650,6 @@ szqueue_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L650 &szqueue_data_type, sq); list_head_init(szqueue_waitq(sq)); list_head_init(szqueue_pushq(sq)); - list_add(&szqueue_list, szqueue_live(sq)); return obj; } @@ -663,6 +659,11 @@ szqueue_ptr(VALUE obj) https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L659 struct rb_szqueue *sq; TypedData_Get_Struct(obj, struct rb_szqueue, &szqueue_data_type, sq); + if (queue_fork_check(&sq->q)) { + list_head_init(szqueue_pushq(sq)); + sq->num_waiting_push = 0; + } + return sq; } @@ -1260,10 +1261,9 @@ rb_szqueue_empty_p(VALUE self) https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L1261 /* ConditionalVariable */ -/* TODO: maybe this can be IMEMO */ struct rb_condvar { struct list_head waitq; - struct list_node live; + rb_serial_t fork_gen; }; /* @@ -1294,14 +1294,6 @@ struct rb_condvar { https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L1294 * } */ -static void -condvar_free(void *ptr) -{ - struct rb_condvar *cv = ptr; - list_del(&cv->live); - ruby_xfree(ptr); -} - static size_t condvar_memsize(const void *ptr) { @@ -1310,7 +1302,7 @@ condvar_memsize(const void *ptr) https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L1302 static const rb_data_type_t cv_data_type = { "condvar", - {0, condvar_free, condvar_memsize,}, + {0, RUBY_TYPED_DEFAULT_FREE, condvar_memsize,}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED }; @@ -1318,9 +1310,15 @@ static struct rb_condvar * https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L1310 condvar_ptr(VALUE self) { struct rb_condvar *cv; + rb_serial_t fork_gen = GET_VM()->fork_gen; TypedData_Get_Struct(self, struct rb_condvar, &cv_data_type, cv); + /* forked children can't reach into parent thread stacks */ + if (cv->fork_gen != fork_gen) { + list_head_init(&cv->waitq); + } + return cv; } @@ -1332,7 +1330,6 @@ condvar_alloc(VALUE klass) https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L1330 obj = TypedData_Make_Struct(klass, struct rb_condvar, &cv_data_type, cv); list_head_init(&cv->waitq); - list_add(&condvar_list, &cv->live); return obj; } @@ -1446,32 +1443,6 @@ define_thread_class(VALUE outer, const c https://github.com/ruby/ruby/blob/trunk/thread_sync.c#L1443 return klass; } -#if defined(HAVE_WORKING_FORK) -/* we must not reference stacks of dead threads in a forked child */ -static void -rb_thread_sync_reset_all(void) -{ - struct rb_queue *q = 0; - struct rb_szqueue *sq = 0; - struct rb_condvar *cv = 0; - - list_for_each(&queue_list, q, live) { - list_head_init(queue_waitq(q)); - q->num_waiting = 0; - } - list_for_each(&szqueue_list, q, live) { - sq = container_of(q, struct rb_szqueue, q); - list_head_init(szqueue_waitq(sq)); - list_head_init(szqueue_pushq(sq)); - sq->num_waiting_push = 0; - sq->q.num_waiting = 0; - } - list_for_each(&condvar_list, cv, live) { - list_head_init(&cv->waitq); - } -} -#endif - static void Init_thread_sync(void) { -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/