@@ -473,54 +473,77 @@ int enif_demonitor_process(ErlNifEnv *env, void *obj, const ErlNifMonitor *mon)
473473 return -1 ;
474474 }
475475
476+ int32_t target_process_id = INVALID_PROCESS_ID ;
477+ uint64_t ref_ticks = 0 ;
478+
479+ // Phase 1: Find and remove from monitors list while holding monitors lock
476480 struct ListHead * monitors = synclist_wrlock (& resource_type -> monitors );
477481 struct ListHead * item ;
478482 LIST_FOR_EACH (item , monitors ) {
479483 struct ResourceMonitor * monitor = GET_LIST_ENTRY (item , struct ResourceMonitor , resource_list_head );
480484 if (monitor -> ref_ticks == mon -> ref_ticks ) {
481485 struct RefcBinary * resource = refc_binary_from_data (obj );
482486 if (resource -> resource_type != mon -> resource_type ) {
487+ synclist_unlock (& resource_type -> monitors );
483488 return -1 ;
484489 }
485490
486- Context * target = globalcontext_get_process_lock (global , monitor -> process_id );
487- if (target ) {
488- mailbox_send_ref_signal (target , DemonitorSignal , monitor -> ref_ticks );
489- globalcontext_get_process_unlock (global , target );
490- }
491-
491+ target_process_id = monitor -> process_id ;
492+ ref_ticks = monitor -> ref_ticks ;
492493 list_remove (& monitor -> resource_list_head );
493494 free (monitor );
494- synclist_unlock (& resource_type -> monitors );
495- return 0 ;
495+ break ;
496496 }
497497 }
498-
499498 synclist_unlock (& resource_type -> monitors );
500499
501- return -1 ;
500+ if (target_process_id == INVALID_PROCESS_ID ) {
501+ return -1 ;
502+ }
503+
504+ // Phase 2: Send demonitor signal without holding monitors lock
505+ // This avoids lock order inversion with processes_table
506+ Context * target = globalcontext_get_process_lock (global , target_process_id );
507+ if (target ) {
508+ mailbox_send_ref_signal (target , DemonitorSignal , ref_ticks );
509+ globalcontext_get_process_unlock (global , target );
510+ }
511+
512+ return 0 ;
502513}
503514
504515void destroy_resource_monitors (struct RefcBinary * resource , GlobalContext * global )
505516{
506517 struct ResourceType * resource_type = resource -> resource_type ;
518+
519+ // Phase 1: Collect monitors to destroy while holding monitors lock
520+ struct ListHead to_signal ;
521+ list_init (& to_signal );
522+
507523 struct ListHead * monitors = synclist_wrlock (& resource_type -> monitors );
508524 struct ListHead * item ;
509525 struct ListHead * tmp ;
510526 MUTABLE_LIST_FOR_EACH (item , tmp , monitors ) {
511527 struct ResourceMonitor * monitor = GET_LIST_ENTRY (item , struct ResourceMonitor , resource_list_head );
512528 if (monitor -> resource == resource ) {
513- Context * target = globalcontext_get_process_lock (global , monitor -> process_id );
514- if (target ) {
515- mailbox_send_ref_signal (target , DemonitorSignal , monitor -> ref_ticks );
516- globalcontext_get_process_unlock (global , target );
517- }
518529 list_remove (& monitor -> resource_list_head );
519- free ( monitor );
530+ list_append ( & to_signal , & monitor -> resource_list_head );
520531 }
521532 }
522-
523533 synclist_unlock (& resource_type -> monitors );
534+
535+ // Phase 2: Send demonitor signals without holding monitors lock
536+ // This avoids lock order inversion with processes_table
537+ MUTABLE_LIST_FOR_EACH (item , tmp , & to_signal ) {
538+ struct ResourceMonitor * monitor = GET_LIST_ENTRY (item , struct ResourceMonitor , resource_list_head );
539+ Context * target = globalcontext_get_process_lock (global , monitor -> process_id );
540+ if (target ) {
541+ mailbox_send_ref_signal (target , DemonitorSignal , monitor -> ref_ticks );
542+ globalcontext_get_process_unlock (global , target );
543+ }
544+ list_remove (& monitor -> resource_list_head );
545+ free (monitor );
546+ }
524547}
525548
526549int enif_compare_monitors (const ErlNifMonitor * monitor1 , const ErlNifMonitor * monitor2 )
0 commit comments