@@ -8,6 +8,7 @@ static VALUE rb_eClosedQueueError;
88/* Mutex */
99typedef struct rb_mutex_struct {
1010 rb_fiber_t * fiber ;
11+ VALUE thread ; // even if the fiber is collected, we might need access to the thread in mutex_free
1112 struct rb_mutex_struct * next_mutex ;
1213 struct ccan_list_head waitq ; /* protected by GVL */
1314} rb_mutex_t ;
@@ -106,8 +107,6 @@ static const char* rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t *th, rb_fib
106107 *
107108 */
108109
109- #define mutex_mark ((void(*)(void*))0)
110-
111110static size_t
112111rb_mutex_num_waiting (rb_mutex_t * mutex )
113112{
@@ -123,13 +122,39 @@ rb_mutex_num_waiting(rb_mutex_t *mutex)
123122
124123rb_thread_t * rb_fiber_threadptr (const rb_fiber_t * fiber );
125124
125+ static bool
126+ locked_p (rb_mutex_t * mutex )
127+ {
128+ return mutex -> fiber != 0 ;
129+ }
130+
131+ static void
132+ mutex_mark (void * ptr )
133+ {
134+ rb_mutex_t * mutex = ptr ;
135+ VALUE fiber ;
136+ if (locked_p (mutex )) {
137+ fiber = rb_fiberptr_self (mutex -> fiber ); // rb_fiber_t* doesn't move along with fiber object
138+ if (fiber ) rb_gc_mark_movable (fiber );
139+ rb_gc_mark_movable (mutex -> thread );
140+ }
141+ }
142+
143+ static void
144+ mutex_compact (void * ptr )
145+ {
146+ rb_mutex_t * mutex = ptr ;
147+ if (locked_p (mutex )) {
148+ mutex -> thread = rb_gc_location (mutex -> thread );
149+ }
150+ }
151+
126152static void
127153mutex_free (void * ptr )
128154{
129155 rb_mutex_t * mutex = ptr ;
130- if (mutex -> fiber ) {
131- /* rb_warn("free locked mutex"); */
132- const char * err = rb_mutex_unlock_th (mutex , rb_fiber_threadptr (mutex -> fiber ), mutex -> fiber );
156+ if (locked_p (mutex )) {
157+ const char * err = rb_mutex_unlock_th (mutex , rb_thread_ptr (mutex -> thread ), mutex -> fiber );
133158 if (err ) rb_bug ("%s" , err );
134159 }
135160 ruby_xfree (ptr );
@@ -143,7 +168,7 @@ mutex_memsize(const void *ptr)
143168
144169static const rb_data_type_t mutex_data_type = {
145170 "mutex" ,
146- {mutex_mark , mutex_free , mutex_memsize ,},
171+ {mutex_mark , mutex_free , mutex_memsize , mutex_compact , },
147172 0 , 0 , RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
148173};
149174
@@ -204,12 +229,13 @@ rb_mutex_locked_p(VALUE self)
204229{
205230 rb_mutex_t * mutex = mutex_ptr (self );
206231
207- return RBOOL (mutex -> fiber );
232+ return RBOOL (locked_p ( mutex ) );
208233}
209234
210235static void
211236thread_mutex_insert (rb_thread_t * thread , rb_mutex_t * mutex )
212237{
238+ RUBY_ASSERT (!mutex -> next_mutex );
213239 if (thread -> keeping_mutexes ) {
214240 mutex -> next_mutex = thread -> keeping_mutexes ;
215241 }
@@ -234,10 +260,24 @@ thread_mutex_remove(rb_thread_t *thread, rb_mutex_t *mutex)
234260}
235261
236262static void
237- mutex_locked (rb_thread_t * th , VALUE self )
263+ mutex_set_owner (VALUE self , rb_thread_t * th , rb_fiber_t * fiber )
264+ {
265+ rb_mutex_t * mutex = mutex_ptr (self );
266+
267+ mutex -> thread = th -> self ;
268+ mutex -> fiber = fiber ;
269+ RB_OBJ_WRITTEN (self , Qundef , th -> self );
270+ if (fiber ) {
271+ RB_OBJ_WRITTEN (self , Qundef , rb_fiberptr_self (fiber ));
272+ }
273+ }
274+
275+ static void
276+ mutex_locked (rb_thread_t * th , rb_fiber_t * fiber , VALUE self )
238277{
239278 rb_mutex_t * mutex = mutex_ptr (self );
240279
280+ mutex_set_owner (self , th , fiber );
241281 thread_mutex_insert (th , mutex );
242282}
243283
@@ -258,9 +298,8 @@ rb_mutex_trylock(VALUE self)
258298
259299 rb_fiber_t * fiber = GET_EC ()-> fiber_ptr ;
260300 rb_thread_t * th = GET_THREAD ();
261- mutex -> fiber = fiber ;
262301
263- mutex_locked (th , self );
302+ mutex_locked (th , fiber , self );
264303 return Qtrue ;
265304 }
266305 else {
@@ -328,7 +367,7 @@ do_mutex_lock(VALUE self, int interruptible_p)
328367 rb_ensure (call_rb_fiber_scheduler_block , self , delete_from_waitq , (VALUE )& sync_waiter );
329368
330369 if (!mutex -> fiber ) {
331- mutex -> fiber = fiber ;
370+ mutex_set_owner ( self , th , fiber ) ;
332371 }
333372 }
334373 else {
@@ -358,6 +397,7 @@ do_mutex_lock(VALUE self, int interruptible_p)
358397 rb_ractor_sleeper_threads_inc (th -> ractor );
359398 rb_check_deadlock (th -> ractor );
360399
400+ RUBY_ASSERT (!th -> locking_mutex );
361401 th -> locking_mutex = self ;
362402
363403 ccan_list_add_tail (& mutex -> waitq , & sync_waiter .node );
@@ -368,7 +408,7 @@ do_mutex_lock(VALUE self, int interruptible_p)
368408
369409 // unlocked by another thread while sleeping
370410 if (!mutex -> fiber ) {
371- mutex -> fiber = fiber ;
411+ mutex_set_owner ( self , th , fiber ) ;
372412 }
373413
374414 rb_ractor_sleeper_threads_dec (th -> ractor );
@@ -382,10 +422,13 @@ do_mutex_lock(VALUE self, int interruptible_p)
382422 if (interruptible_p ) {
383423 /* release mutex before checking for interrupts...as interrupt checking
384424 * code might call rb_raise() */
385- if (mutex -> fiber == fiber ) mutex -> fiber = 0 ;
425+ if (mutex -> fiber == fiber ) {
426+ mutex -> thread = Qfalse ;
427+ mutex -> fiber = NULL ;
428+ }
386429 RUBY_VM_CHECK_INTS_BLOCKING (th -> ec ); /* may release mutex */
387430 if (!mutex -> fiber ) {
388- mutex -> fiber = fiber ;
431+ mutex_set_owner ( self , th , fiber ) ;
389432 }
390433 }
391434 else {
@@ -404,7 +447,7 @@ do_mutex_lock(VALUE self, int interruptible_p)
404447 }
405448
406449 if (saved_ints ) th -> ec -> interrupt_flag = saved_ints ;
407- if (mutex -> fiber == fiber ) mutex_locked (th , self );
450+ if (mutex -> fiber == fiber ) mutex_locked (th , fiber , self );
408451 }
409452
410453 RUBY_DEBUG_LOG ("%p locked" , mutex );
0 commit comments