@@ -19,98 +19,115 @@ namespace eosio::chain {
1919static_assert (std::atomic_bool::is_always_lock_free, " Only lock-free atomics AS-safe." );
2020
2121struct platform_timer ::impl {
22- timer_t timerid;
22+ constexpr static unsigned num_timers = 8 ;
23+ static_assert (std::has_single_bit(num_timers), " num_timers must be a power of two" );
24+
25+ constexpr static unsigned tag_ptr_shift = 57 ; // safe for x64 w/ 5-level paging; RISC-V w/ Sv57; POWER10; ARM8.2's LVA is only 52
26+ constexpr static uint64_t tag_ptr_mask = -1ull << tag_ptr_shift;
27+
28+ static_assert (std::cmp_less_equal(std::popcount(num_timers-1 ), sizeof (uintptr_t )*8 -tag_ptr_shift), " generation count won't fit in pointer tag" );
29+
30+ std::array<timer_t , num_timers> timerid;
31+
32+ struct signal_initer {
33+ signal_initer () {
34+ struct sigaction act;
35+ sigemptyset (&act.sa_mask );
36+ act.sa_sigaction = impl::sig_handler;
37+ act.sa_flags = SA_SIGINFO | SA_RESTART;
38+ FC_ASSERT (sigaction (SIGRTMIN, &act, NULL ) == 0 , " failed to acquire SIGRTMIN signal" );
39+ }
40+ };
2341
2442 static void sig_handler (int , siginfo_t * si, void *) {
25- platform_timer* self = (platform_timer*)si->si_value .sival_ptr ;
26- self->expire_now ();
43+ const uintptr_t sival_ptr_as_uint = reinterpret_cast <uintptr_t >(si->si_value .sival_ptr );
44+ const generation_t expiry_gen = sival_ptr_as_uint >> tag_ptr_shift;
45+ platform_timer* self = reinterpret_cast <platform_timer*>(sival_ptr_as_uint & ~tag_ptr_mask);
46+
47+ self->expire_now (expiry_gen);
2748 }
2849};
2950
3051platform_timer::platform_timer () {
3152 static_assert (std::atomic<timer_state_t >::is_always_lock_free, " Only lock-free atomics AS-safe." );
3253 static_assert (sizeof (impl) <= fwd_size);
54+ static_assert (std::numeric_limits<generation_t >::max () > impl::num_timers-1 , " generation_t rolls over before timer count does" );
3355
34- static bool initialized;
35- static std::mutex initialized_mutex;
36-
37- if (std::lock_guard guard (initialized_mutex); !initialized) {
38- struct sigaction act;
39- sigemptyset (&act.sa_mask );
40- act.sa_sigaction = impl::sig_handler;
41- act.sa_flags = SA_SIGINFO | SA_RESTART;
42- FC_ASSERT (sigaction (SIGRTMIN, &act, NULL ) == 0 , " failed to aquire SIGRTMIN signal" );
43- initialized = true ;
44- }
56+ static impl::signal_initer the_signal_initer;
4557
46- struct sigevent se;
47- se.sigev_notify = SIGEV_SIGNAL;
48- se.sigev_signo = SIGRTMIN;
49- se.sigev_value .sival_ptr = (void *)this ;
58+ for (uint64_t i = 0 ; i < impl::num_timers; ++i) {
59+ struct sigevent se;
60+ se.sigev_notify = SIGEV_SIGNAL;
61+ se.sigev_signo = SIGRTMIN;
62+ se.sigev_value .sival_ptr = (void *)(reinterpret_cast <uintptr_t >(this ) | i<<impl::tag_ptr_shift);
5063
51- FC_ASSERT (timer_create (CLOCK_REALTIME, &se, &my->timerid ) == 0 , " failed to create timer" );
64+ FC_ASSERT (timer_create (CLOCK_REALTIME, &se, &my->timerid [i]) == 0 , " failed to create timer" );
65+ }
5266
5367 compute_and_print_timer_accuracy (*this );
5468}
5569
5670platform_timer::~platform_timer () {
57- timer_delete (my->timerid );
71+ for (unsigned i = 0 ; i < impl::num_timers; ++i)
72+ timer_delete (my->timerid [i]);
5873}
5974
6075void platform_timer::start (fc::time_point tp) {
6176 assert (timer_state () == state_t ::stopped);
77+ generation = (generation + 1 ) % impl::num_timers;
6278 timer_running_forever = tp == fc::time_point::maximum ();
6379 if (timer_running_forever) {
64- _state.store (timer_state_t {.state = state_t ::running, .callback_in_flight = false });
80+ _state.store (timer_state_t {.state = state_t ::running, .callback_in_flight = false , . generation_running = generation });
6581 return ;
6682 }
6783 fc::microseconds x = tp.time_since_epoch () - fc::time_point::now ().time_since_epoch ();
6884 if (x.count () <= 0 ) {
69- _state.store (timer_state_t {.state = state_t ::timed_out, .callback_in_flight = false });
85+ _state.store (timer_state_t {.state = state_t ::timed_out, .callback_in_flight = false , . generation_running = generation });
7086 } else {
7187 time_t secs = x.count () / 1000000 ;
7288 long nsec = (x.count () - (secs*1000000 )) * 1000 ;
7389 struct itimerspec enable = {{0 , 0 }, {secs, nsec}};
74- _state.store (timer_state_t {.state = state_t ::running, .callback_in_flight = false });
75- if (timer_settime (my->timerid , 0 , &enable, NULL ) != 0 ) {
76- _state.store (timer_state_t {.state = state_t ::timed_out, .callback_in_flight = false });
90+ _state.store (timer_state_t {.state = state_t ::running, .callback_in_flight = false , . generation_running = generation });
91+ if (timer_settime (my->timerid [generation] , 0 , &enable, NULL ) != 0 ) {
92+ _state.store (timer_state_t {.state = state_t ::timed_out, .callback_in_flight = false , . generation_running = generation });
7793 }
7894 }
7995}
8096
81- void platform_timer::expire_now () {
82- timer_state_t expected{.state = state_t ::running, .callback_in_flight = false };
83- if (_state.compare_exchange_strong (expected, timer_state_t {state_t ::timed_out, true })) {
97+ void platform_timer::expire_now (generation_t expired_generation ) {
98+ timer_state_t expected{.state = state_t ::running, .callback_in_flight = false , . generation_running = expired_generation };
99+ if (_state.compare_exchange_strong (expected, timer_state_t {state_t ::timed_out, true , expired_generation })) {
84100 call_expiration_callback ();
85- _state.store (timer_state_t {state_t ::timed_out, false });
101+ _state.store (timer_state_t {state_t ::timed_out, false , expired_generation });
86102 }
87103}
88104
89105void platform_timer::interrupt_timer () {
90- timer_state_t expected{.state = state_t ::running, .callback_in_flight = false };
91- if (_state.compare_exchange_strong (expected, timer_state_t {state_t ::interrupted, true })) {
106+ const generation_t generation_running = _state.load ().generation_running ;
107+ timer_state_t expected = {.state = state_t ::running, .callback_in_flight = false , .generation_running = generation_running};
108+ if (_state.compare_exchange_strong (expected, timer_state_t {state_t ::interrupted, true , generation_running})) {
92109 call_expiration_callback ();
93- _state.store (timer_state_t {state_t ::interrupted, false });
110+ _state.store (timer_state_t {state_t ::interrupted, false , generation_running });
94111 }
95112}
96113
97114void platform_timer::stop () {
98115 // if still running, then interrupt so expire_now() and interrupt_timer() can't start a callback call
99- timer_state_t prior_state{.state = state_t ::running, .callback_in_flight = false };
100- if (_state.compare_exchange_strong (prior_state, timer_state_t {state_t ::interrupted, false })) {
101- prior_state = timer_state_t {state_t ::interrupted, false };
116+ timer_state_t prior_state{.state = state_t ::running, .callback_in_flight = false , . generation_running = generation };
117+ if (_state.compare_exchange_strong (prior_state, timer_state_t {state_t ::interrupted, false , generation })) {
118+ prior_state = timer_state_t {state_t ::interrupted, false , generation };
102119 }
103120
104121 for (; prior_state.callback_in_flight ; prior_state = _state.load ())
105122 boost::core::sp_thread_pause ();
106123
107124 if (prior_state.state == state_t ::stopped)
108125 return ;
109- _state.store (timer_state_t {.state = state_t ::stopped, .callback_in_flight = false });
126+ _state.store (timer_state_t {.state = state_t ::stopped, .callback_in_flight = false , . generation_running = generation });
110127 if (prior_state.state == state_t ::timed_out || timer_running_forever)
111128 return ;
112129 struct itimerspec disable = {{0 , 0 }, {0 , 0 }};
113- timer_settime (my->timerid , 0 , &disable, NULL );
130+ timer_settime (my->timerid [generation] , 0 , &disable, NULL );
114131}
115132
116133}
0 commit comments