@@ -214,6 +214,17 @@ typedef struct {
214214 Sint count ;
215215} ErtsPausedProcTimer ;
216216
217+ typedef struct ErtsPausedBifTimer_ {
218+ ErtsBifTimer tmr ;
219+ Sint64 time_left_in_msec ;
220+ struct ErtsPausedBifTimer_ * next ;
221+ } ErtsPausedBifTimer ;
222+
223+ struct ErtsPausedBifTimers_ {
224+ ErtsPausedBifTimer * list ;
225+ Sint count ;
226+ };
227+
217228typedef ErtsTimer * (* ErtsCreateTimerFunc )(ErtsSchedulerData * esdp ,
218229 ErtsMonotonicTime timeout_pos ,
219230 int short_time , ErtsTmrType type ,
@@ -966,7 +977,7 @@ create_tw_timer(ErtsSchedulerData *esdp,
966977}
967978
968979/*
969- * Paused proc timers
980+ * Paused bif timers
970981 */
971982
972983static ERTS_INLINE Sint64
@@ -980,6 +991,31 @@ time_left_for_timer_in_msec(ErtsTimer* tmr, ErtsSchedulerData *esdp)
980991 return get_time_left (esdp , timeout_pos );
981992}
982993
994+ static ERTS_INLINE void
995+ create_paused_bif_timer (ErtsBifTimer * tmr , Process * c_p , ErtsSchedulerData * esdp )
996+ {
997+ ErtsPausedBifTimer * pbtmr = erts_alloc (ERTS_ALC_T_PAUSED_TIMER ,
998+ sizeof (ErtsPausedBifTimer ));
999+
1000+ ASSERT (c_p -> paused_bif_timers -> count > 0 );
1001+
1002+ ASSERT (!(tmr -> type .head .roflgs & ERTS_TMR_ROFLG_PAUSED ));
1003+
1004+ init_btm_message (& pbtmr -> tmr , tmr -> btm .message );
1005+
1006+ pbtmr -> tmr .type .head .roflgs = tmr -> type .head .roflgs | ERTS_TMR_ROFLG_PAUSED ;
1007+ erts_atomic32_init_nob (& pbtmr -> tmr .type .head .refc , 1 );
1008+ pbtmr -> tmr .type .head .receiver .proc = tmr -> type .head .receiver .proc ;
1009+
1010+ pbtmr -> time_left_in_msec = time_left_for_timer_in_msec ((ErtsTimer * ) tmr , esdp );
1011+
1012+ pbtmr -> next = c_p -> paused_bif_timers -> list ;
1013+ c_p -> paused_bif_timers -> list = pbtmr ;
1014+ }
1015+
1016+ /*
1017+ * Paused proc timers
1018+ */
9831019static ERTS_INLINE ErtsPausedProcTimer *
9841020create_paused_proc_timer (Process * c_p )
9851021{
@@ -1739,7 +1775,7 @@ continue_cancel_ptimer(ErtsSchedulerData *esdp, ErtsTimer *tmr)
17391775 return ;
17401776 }
17411777
1742- if (esdp -> no != sid )
1778+ if (esdp -> no != sid )
17431779 queue_canceled_timer (esdp , sid , tmr );
17441780 else
17451781 cleanup_sched_local_canceled_timer (esdp , tmr );
@@ -1789,7 +1825,16 @@ setup_bif_timer(Process *c_p, int twheel, ErtsMonotonicTime timeout_pos,
17891825 Process * proc = erts_pid2proc_opt (c_p , ERTS_PROC_LOCK_MAIN ,
17901826 rcvr , ERTS_PROC_LOCK_BTM ,
17911827 ERTS_P2P_FLG_INC_REFC );
1792- if (!proc ) {
1828+ if (proc ) {
1829+ tmr -> type .head .receiver .proc = proc ;
1830+ if (proc -> paused_bif_timers ) {
1831+ create_paused_bif_timer (tmr , proc , esdp );
1832+ } else {
1833+ proc_btm_rbt_insert (& proc -> bif_timers , tmr );
1834+ }
1835+ erts_proc_unlock (proc , ERTS_PROC_LOCK_BTM );
1836+ }
1837+ if (!proc || proc -> paused_bif_timers ) {
17931838 if (tmr -> btm .tree .parent != ERTS_HLT_PFIELD_NOT_IN_TABLE ) {
17941839 btm_rbt_delete (& esdp -> timer_service -> btm_tree , tmr );
17951840 tmr -> btm .tree .parent = ERTS_HLT_PFIELD_NOT_IN_TABLE ;
@@ -1802,11 +1847,6 @@ setup_bif_timer(Process *c_p, int twheel, ErtsMonotonicTime timeout_pos,
18021847 hlt_delete_timer (esdp , & tmr -> type .hlt );
18031848 timer_destroy ((ErtsTimer * ) tmr , twheel , 1 );
18041849 }
1805- else {
1806- proc_btm_rbt_insert (& proc -> bif_timers , tmr );
1807- erts_proc_unlock (proc , ERTS_PROC_LOCK_BTM );
1808- tmr -> type .head .receiver .proc = proc ;
1809- }
18101850 }
18111851
18121852 ERTS_BIF_PREP_RET (ret , ref );
@@ -1819,11 +1859,12 @@ setup_bif_timer(Process *c_p, int twheel, ErtsMonotonicTime timeout_pos,
18191859}
18201860
18211861static int
1822- cancel_bif_timer (ErtsBifTimer * tmr )
1862+ cancel_bif_timer (ErtsBifTimer * tmr , ErtsProcLocks c_p_locks )
18231863{
18241864 erts_aint_t state ;
18251865 Uint32 roflgs ;
18261866 int res ;
1867+ int proc_lock_btm_held = c_p_locks & ERTS_PROC_LOCK_BTM ;
18271868
18281869 state = erts_atomic32_cmpxchg_acqb (& tmr -> btm .state ,
18291870 ERTS_TMR_STATE_CANCELED ,
@@ -1843,7 +1884,9 @@ cancel_bif_timer(ErtsBifTimer *tmr)
18431884 proc = tmr -> type .head .receiver .proc ;
18441885 ERTS_HLT_ASSERT (!(tmr -> type .head .roflgs & ERTS_TMR_ROFLG_REG_NAME ));
18451886
1846- erts_proc_lock (proc , ERTS_PROC_LOCK_BTM );
1887+ if (!proc_lock_btm_held ) {
1888+ erts_proc_lock (proc , ERTS_PROC_LOCK_BTM );
1889+ }
18471890 /*
18481891 * If process is exiting, let it clean up
18491892 * the btm tree by itself (it may be in
@@ -1855,14 +1898,16 @@ cancel_bif_timer(ErtsBifTimer *tmr)
18551898 tmr -> btm .proc_tree .parent = ERTS_HLT_PFIELD_NOT_IN_TABLE ;
18561899 res = 1 ;
18571900 }
1858- erts_proc_unlock (proc , ERTS_PROC_LOCK_BTM );
1901+ if (!proc_lock_btm_held ) {
1902+ erts_proc_unlock (proc , ERTS_PROC_LOCK_BTM );
1903+ }
18591904 }
18601905
18611906 return res ;
18621907}
18631908
18641909static ERTS_INLINE Sint64
1865- access_btm (ErtsBifTimer * tmr , Uint32 sid , ErtsSchedulerData * esdp , int cancel )
1910+ access_btm (ErtsBifTimer * tmr , Uint32 sid , ErtsSchedulerData * esdp , int cancel , ErtsProcLocks c_p_locks )
18661911{
18671912 int cncl_res ;
18681913 Sint64 time_left ;
@@ -1884,7 +1929,7 @@ access_btm(ErtsBifTimer *tmr, Uint32 sid, ErtsSchedulerData *esdp, int cancel)
18841929 return -1 ;
18851930 }
18861931
1887- cncl_res = cancel_bif_timer (tmr );
1932+ cncl_res = cancel_bif_timer (tmr , c_p_locks );
18881933 if (!cncl_res )
18891934 return -1 ;
18901935
@@ -2048,11 +2093,6 @@ access_sched_local_btm(Process *c_p, Eterm pid,
20482093
20492094 tmr = btm_rbt_lookup (srv -> btm_tree , trefn );
20502095
2051- time_left = access_btm (tmr , (Uint32 ) esdp -> no , esdp , cancel );
2052-
2053- if (async && !info )
2054- return am_ok ;
2055-
20562096 if (c_p ) {
20572097 proc = c_p ;
20582098 proc_locks = ERTS_PROC_LOCK_MAIN ;
@@ -2062,6 +2102,11 @@ access_sched_local_btm(Process *c_p, Eterm pid,
20622102 proc_locks = 0 ;
20632103 }
20642104
2105+ time_left = access_btm (tmr , (Uint32 ) esdp -> no , esdp , cancel , proc_locks );
2106+
2107+ if (async && !info )
2108+ return am_ok ;
2109+
20652110 if (!async ) {
20662111 if (c_p ) {
20672112 if (!info )
@@ -2135,7 +2180,7 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp,
21352180 if (!tmr )
21362181 return 0 ;
21372182
2138- time_left = access_btm (tmr , sid , esdp , cancel );
2183+ time_left = access_btm (tmr , sid , esdp , cancel , ERTS_PROC_LOCK_MAIN );
21392184
21402185 if (!info )
21412186 * resp = am_ok ;
@@ -2805,6 +2850,59 @@ erts_pause_proc_timer(Process *c_p)
28052850 erts_atomic_set_nob (& c_p -> common .timer , (erts_aint_t ) pptmr );
28062851}
28072852
2853+ static ERTS_INLINE int
2854+ add_bif_timer_to_worklist (ErtsBifTimer * tmr , void * arg , Sint reds )
2855+ {
2856+ ErtsWStack * s = (ErtsWStack * ) arg ;
2857+ WSTACK_PUSH ((* s ), (UWord ) tmr );
2858+ return 1 ;
2859+ }
2860+
2861+ void
2862+ erts_pause_bif_timers (Process * c_p , ErtsProcLocks c_p_locks )
2863+ {
2864+ ErtsSchedulerData * esdp = erts_proc_sched_data (c_p );
2865+ int must_release_btm_lock = 0 ;
2866+
2867+ ASSERT (c_p_locks & ERTS_PROC_LOCK_MAIN );
2868+ ERTS_LC_ASSERT (ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks (c_p ));
2869+
2870+ if (!(c_p_locks & ERTS_PROC_LOCK_BTM )) {
2871+ erts_proc_lock (c_p , ERTS_PROC_LOCK_BTM );
2872+ c_p_locks |= ERTS_PROC_LOCK_BTM ;
2873+ must_release_btm_lock = 1 ;
2874+ }
2875+ ERTS_LC_ASSERT (ERTS_PROC_LOCK_BTM & erts_proc_lc_my_proc_locks (c_p ));
2876+
2877+ if (c_p -> paused_bif_timers ) {
2878+ ASSERT (c_p -> paused_bif_timers -> count > 0 );
2879+ c_p -> paused_bif_timers -> count ++ ;
2880+ }
2881+ else {
2882+ WSTACK_DECLARE (bif_timers_worklist );
2883+ c_p -> paused_bif_timers = erts_alloc (ERTS_ALC_T_PAUSED_TIMER ,
2884+ sizeof (ErtsPausedBifTimers ));
2885+ c_p -> paused_bif_timers -> list = NULL ;
2886+ c_p -> paused_bif_timers -> count = 1 ;
2887+ /*
2888+ * It would be better in theory to use a yielding version of foreach here,
2889+ * but it would be a lot more complex, and since this function is only
2890+ * used by the debugger potential loss in responsiveness is acceptable.
2891+ */
2892+ proc_btm_rbt_foreach (c_p -> bif_timers , add_bif_timer_to_worklist , & bif_timers_worklist );
2893+ while (!WSTACK_ISEMPTY (bif_timers_worklist )) {
2894+ ErtsBifTimer * tmr = (ErtsBifTimer * ) WSTACK_POP (bif_timers_worklist );
2895+ Uint32 sid = (tmr -> type .head .roflgs & ERTS_TMR_ROFLG_SID_MASK );
2896+ create_paused_bif_timer (tmr , c_p , esdp );
2897+ access_btm (tmr , sid , esdp , /* cancel = */ 1 , c_p_locks );
2898+ }
2899+ WSTACK_DESTROY (bif_timers_worklist );
2900+ }
2901+ if (must_release_btm_lock ) {
2902+ erts_proc_unlock (c_p , ERTS_PROC_LOCK_BTM );
2903+ }
2904+ }
2905+
28082906void
28092907erts_resume_paused_proc_timer (Process * c_p )
28102908{
@@ -2835,6 +2933,90 @@ erts_resume_paused_proc_timer(Process *c_p)
28352933 }
28362934}
28372935
2936+ void
2937+ erts_resume_paused_bif_timers (Process * c_p )
2938+ {
2939+ ErtsSchedulerData * esdp = erts_proc_sched_data (c_p );
2940+ ErtsPausedBifTimer * paused_bif_timer ;
2941+
2942+ ERTS_LC_ASSERT (ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks (c_p ));
2943+ ERTS_LC_ASSERT (!(ERTS_PROC_LOCK_BTM & erts_proc_lc_my_proc_locks (c_p )));
2944+ erts_proc_lock (c_p , ERTS_PROC_LOCK_BTM );
2945+
2946+ ASSERT (c_p -> paused_bif_timers );
2947+ ASSERT (c_p -> paused_bif_timers -> count > 0 );
2948+
2949+ if (-- c_p -> paused_bif_timers -> count > 0 ) {
2950+ /* Other suspends still active; leave them paused... */
2951+ erts_proc_unlock (c_p , ERTS_PROC_LOCK_BTM );
2952+ return ;
2953+ }
2954+
2955+ paused_bif_timer = c_p -> paused_bif_timers -> list ;
2956+
2957+ while (paused_bif_timer != NULL ) {
2958+ ErtsPausedBifTimer * old_timer = paused_bif_timer ;
2959+ ErtsMonotonicTime timeout_pos ;
2960+ ErtsBifTimer * tmr ;
2961+ Eterm ref ;
2962+ UWord tmo ;
2963+ ErtsCreateTimerFunc create_timer ;
2964+ void * hp ;
2965+
2966+ tmo = (UWord ) paused_bif_timer -> time_left_in_msec ;
2967+ timeout_pos = get_timeout_pos (erts_get_monotonic_time (esdp ), (ErtsMonotonicTime ) tmo );
2968+
2969+ /* Lifted from setup_bif_timer */
2970+ hp = HAlloc (c_p , ERTS_REF_THING_SIZE );
2971+ ref = erts_sched_make_ref_in_buffer (esdp , hp );
2972+ create_timer = (tmo < ERTS_TIMER_WHEEL_MSEC
2973+ ? create_tw_timer
2974+ : create_hl_timer );
2975+ tmr = (ErtsBifTimer * ) create_timer (esdp , timeout_pos ,
2976+ tmo < ERTS_BIF_TIMER_SHORT_TIME , ERTS_TMR_BIF ,
2977+ NULL , c_p -> common .id , paused_bif_timer -> tmr .btm .message ,
2978+ internal_ordinary_ref_numbers (ref ),
2979+ NULL , NULL );
2980+ proc_btm_rbt_insert (& c_p -> bif_timers , tmr );
2981+ tmr -> type .head .receiver .proc = c_p ;
2982+
2983+ check_canceled_queue (esdp , esdp -> timer_service );
2984+
2985+ paused_bif_timer = paused_bif_timer -> next ;
2986+ erts_free (ERTS_ALC_T_PAUSED_TIMER , old_timer );
2987+
2988+ /* We correctly decrement the refc in the pausing of the bif timer, but for some reason recreating it does not increment it */
2989+ /* So we do it manually here */
2990+ erts_proc_inc_refc (c_p );
2991+ }
2992+
2993+ erts_free (ERTS_ALC_T_PAUSED_TIMER , c_p -> paused_bif_timers );
2994+ c_p -> paused_bif_timers = NULL ;
2995+
2996+ erts_proc_unlock (c_p , ERTS_PROC_LOCK_BTM );
2997+ }
2998+
2999+ void
3000+ erts_destroy_paused_bif_timers (Process * c_p )
3001+ {
3002+ ErtsPausedBifTimer * ptmr , * free_ptmr ;
3003+
3004+ erts_proc_lock (c_p , ERTS_PROC_LOCK_BTM );
3005+
3006+ ptmr = c_p -> paused_bif_timers -> list ;
3007+
3008+ while (ptmr ) {
3009+ free_ptmr = ptmr ;
3010+ ptmr = ptmr -> next ;
3011+ erts_free (ERTS_ALC_T_PAUSED_TIMER , free_ptmr );
3012+ }
3013+
3014+ erts_free (ERTS_ALC_T_PAUSED_TIMER , c_p -> paused_bif_timers );
3015+
3016+ c_p -> paused_bif_timers = NULL ;
3017+ erts_proc_unlock (c_p , ERTS_PROC_LOCK_BTM );
3018+ }
3019+
28383020void
28393021erts_set_port_timer (Port * c_prt , Sint64 tmo )
28403022{
0 commit comments