diff --git a/pkg/c3/motes.h b/pkg/c3/motes.h index 975554e94b..5881f5f38e 100644 --- a/pkg/c3/motes.h +++ b/pkg/c3/motes.h @@ -618,6 +618,7 @@ # define c3__jato c3_s4('j','a','t','o') # define c3__jet c3_s3('j','e','t') # define c3__jetd c3_s4('j','e','t','d') +# define c3__jinx c3_s4('j','i','n','x') # define c3__just c3_s4('j','u','s','t') # define c3__k c3_s1('k') # define c3__khan c3_s4('k','h','a','n') diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index d880a42032..8da667d060 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -30,6 +30,10 @@ // #undef NO_OVERFLOW +/** Global variables. +**/ + static timer_stack *stk_u; + /* (u3_noun)setjmp(u3R->esc.buf): setjmp within road. */ #if 0 @@ -197,6 +201,8 @@ _cm_signal_handle_intr(int x) static void _cm_signal_handle_alrm(int x) { + stk_u->top = (timer*)NULL; // clear the timer stack + u3l_log("\%jinx timer expired\r\n"); _cm_signal_handle(c3__alrm); } @@ -367,9 +373,60 @@ _cm_signal_deep(c3_w mil_w) ); } + u3m_timer_set(mil_w); + + // factor into own function, call here and in _n_hint_fore() TODO + // _n_hint_hind() will look like this w/ deinstall handler instead + // return void b/c just clearing timer, no args + + u3t_boot(); +} + +// Function to add an `itimerval` to a `timeval` +struct timeval __add_itimer(struct timeval base_time, struct itimerval timer) { + struct timeval result; + + // Add seconds + result.tv_sec = base_time.tv_sec + timer.it_value.tv_sec; + + // Add microseconds + result.tv_usec = base_time.tv_usec + timer.it_value.tv_usec; + + // Handle microsecond overflow + if (result.tv_usec >= 1000000) { + result.tv_sec += 1; + result.tv_usec -= 1000000; + } + + return result; +} + +// Function to calculate the interval between two `timeval` structs +struct itimerval __get_interval(struct timeval start, struct timeval end) { + struct itimerval interval; + + // Calculate the difference in seconds and microseconds + interval.it_value.tv_sec = end.tv_sec - start.tv_sec; + interval.it_value.tv_usec = end.tv_usec - start.tv_usec; + + // Handle underflow of microseconds (i.e., if end's microseconds < start's microseconds) + if (interval.it_value.tv_usec < 0) { + interval.it_value.tv_sec -= 1; + interval.it_value.tv_usec += 1000000; + } + + timerclear(&interval.it_interval); + + return interval; +} + +/* u3m_timer_set: set interval timer for mil_w milliseconds +*/ +void +u3m_timer_set(c3_w mil_w) +{ if ( mil_w ) { struct itimerval itm_u; - timerclear(&itm_u.it_interval); itm_u.it_value.tv_sec = (mil_w / 1000); itm_u.it_value.tv_usec = 1000 * (mil_w % 1000); @@ -381,8 +438,128 @@ _cm_signal_deep(c3_w mil_w) rsignal_install_handler(SIGVTALRM, _cm_signal_handle_alrm); } } +} - u3t_boot(); +/* u3m_timer_clear +*/ +void +u3m_timer_clear() +{ + rsignal_deinstall_handler(SIGVTALRM); +} + +/* u3m_timer_push: set interval timer against walltime +*/ +void +u3m_timer_push(c3_w mil_w) +{ + // get the request timer interval + struct itimerval itm_u; + timerclear(&itm_u.it_interval); + itm_u.it_value.tv_sec = (mil_w / 1000); + itm_u.it_value.tv_usec = 1000 * (mil_w % 1000); + + fprintf(stderr, "\r\npushing timer for %lu ms at 0x%lx\r\n", mil_w, (c3_d)stk_u->top); + + // does the stack have anything on it? if it's clean, this is easy: + if (stk_u->top == NULL) { + // if not, set the timer and return + if ( rsignal_setitimer(ITIMER_VIRTUAL, &itm_u, 0) ) { + u3l_log("loom: push timer failed %s", strerror(errno)); + } + else { + // keep the expiry walltime in the stack + struct timeval tim_u; + gettimeofday(&tim_u, 0); + + struct timer* new_u = (timer*)c3_malloc(sizeof(timer)); + new_u->wal_u = tim_u; + new_u->nex_u = stk_u->top; + stk_u->top = new_u; + fprintf(stderr, "\r\npushed timer for %lu ms at 0x%lx\r\n", tim_u.tv_sec*1000+tim_u.tv_usec, (c3_d)stk_u->top); + + rsignal_install_handler(SIGVTALRM, _cm_signal_handle_alrm); + } + return; + } + + // check that it is less than the current remaining interval + struct itimerval cur_u; + + // if no timer is set this is zero but we shouldn't be here if that's the case + rsignal_getitimer(ITIMER_VIRTUAL, &cur_u); + fprintf(stderr, "current interval: %d s %d us\r\n", cur_u.it_value.tv_sec, cur_u.it_value.tv_usec); + + if (timercmp(&cur_u.it_value, &itm_u.it_value, <)) { + u3l_log("loom: nest timer failed, too large for remaining time %s", + strerror(errno)); + return; + } + + // otherwise set the timer + struct itimerval rem_u; + timersub(&cur_u.it_value, &itm_u.it_value, &rem_u.it_value); + + if ( rsignal_setitimer(ITIMER_VIRTUAL, &itm_u, 0) ) { + u3l_log("loom: nest timer failed %s", strerror(errno)); + } + else { + // keep the expiry walltime in the stack + struct timeval tim_u; + gettimeofday(&tim_u, 0); + + // debugging, TODO remove + c3_d tim_d = 1000000ULL * tim_u.tv_sec + tim_u.tv_usec; + fprintf(stderr, "expiry: %lu us\r\n", tim_d); + c3_d cur_d = cur_u.it_value.tv_sec * 1000 + cur_u.it_value.tv_usec / 1000; + fprintf(stderr, "remaining: %lu ms\r\n", cur_d); + + struct timer* new_u = (timer*)u3a_malloc(sizeof(timer)); + new_u->wal_u = __add_itimer(tim_u, cur_u); + new_u->nex_u = stk_u->top; + stk_u->top = new_u; + + rsignal_install_handler(SIGVTALRM, _cm_signal_handle_alrm); + } +} + +/* u3m_timer_pop +*/ +void +u3m_timer_pop() +{ + fprintf(stderr, "popping timer at 0x%lx\r\n", (c3_d)stk_u->top); + if (stk_u->top == NULL) { + u3m_timer_clear(); + return; + } + else { + timer *old_u = stk_u->top; + stk_u->top = old_u->nex_u; + + if (stk_u->top == NULL) { + // if the stack is empty, simply clear the timer + fprintf(stderr, "no more timers to pop\r\n"); + u3m_timer_clear(); + c3_free(old_u); + return; + } + struct timeval nex_u = stk_u->top->wal_u; + struct timeval tim_u; + gettimeofday(&tim_u, 0); + struct itimerval itm_u; + itm_u = __get_interval(nex_u, tim_u); + fprintf(stderr, "remaining interval: %d s %d us\r\n", itm_u.it_value.tv_sec, itm_u.it_value.tv_usec); + + if ( rsignal_setitimer(ITIMER_VIRTUAL, &itm_u, 0) ) { + u3l_log("loom: pop timer failed %s", strerror(errno)); + } + else { + rsignal_install_handler(SIGVTALRM, _cm_signal_handle_alrm); + } + + c3_free(old_u); + } } /* _cm_signal_done(): @@ -2077,6 +2254,11 @@ u3m_init(size_t len_i) u3C.wor_i = len_i >> 2; u3l_log("loom: mapped %zuMB", len_i >> 20); } + + // start %jinx timer stack + // + stk_u = (struct timer_stack*)c3_malloc(sizeof(struct timer_stack)); + stk_u->top = (timer*)NULL; } extern void u3je_secp_stop(void); @@ -2183,6 +2365,7 @@ u3m_boot(c3_c* dir_c, size_t len_i) memset(u3A, 0, sizeof(*u3A)); return 0; } + } /* u3m_boot_lite(): start without checkpointing. diff --git a/pkg/noun/manage.h b/pkg/noun/manage.h index 14f9cc503d..c6ae5ddeec 100644 --- a/pkg/noun/manage.h +++ b/pkg/noun/manage.h @@ -198,4 +198,33 @@ c3_w u3m_pack(void); + /* u3m_timer_set: set the timer. + */ + void + u3m_timer_set(c3_w mil_w); + + /* u3m_timer_clear: clear the timer. + */ + void + u3m_timer_clear(void); + + /* u3m_timer_push: push a timer on the stack. + */ + void + u3m_timer_push(c3_w mil_w); + + /* u3m_timer_pop: pop a timer from the stack. + */ + void + u3m_timer_pop(void); + + typedef struct timer { + struct timeval wal_u; + struct timer* nex_u; + } timer; + + typedef struct timer_stack { + timer* top; + } timer_stack; + #endif /* ifndef U3_MANAGE_H */ diff --git a/pkg/noun/nock.c b/pkg/noun/nock.c index 452b04c309..dc4f64159a 100644 --- a/pkg/noun/nock.c +++ b/pkg/noun/nock.c @@ -16,6 +16,8 @@ #include "xtract.h" #include "zave.h" +#include + // define to have each opcode printed as it executes, // along with some other debugging info # undef VERBOSE_BYTECODE @@ -1060,6 +1062,7 @@ _n_bint(u3_noun* ops, u3_noun hif, u3_noun nef, c3_o los_o, c3_o tel_o) case c3__meme: case c3__nara: case c3__hela: + case c3__jinx: case c3__bout: { u3_noun fen = u3_nul; c3_w nef_w = _n_comp(&fen, nef, los_o, c3n); @@ -1924,6 +1927,19 @@ _n_hint_fore(u3_cell hin, u3_noun bus, u3_noun* clu) *clu = u3nt(u3k(tag), *clu, now); } break; + case c3__jinx: { + if (c3y == u3a_is_atom(*clu)) { + // clu is in Urbit time, but we need Unix time + c3_d tim_d[2]; + u3r_chubs(0, 2, &tim_d, *clu); + c3_w sec_w = tim_d[1]; + c3_w mil_w = u3_time_msc_out(tim_d[0]); + u3m_timer_push(sec_w * 1000 + mil_w); + } + u3z(*clu); + *clu = c3__jinx; + } break; + case c3__nara: { u3_noun pri, tan; if ( c3y == u3r_cell(*clu, &pri, &tan) ) { @@ -1985,7 +2001,10 @@ static void _n_hint_hind(u3_noun tok, u3_noun pro) { u3_noun p_tok, q_tok, r_tok; - if ( (c3y == u3r_trel(tok, &p_tok, &q_tok, &r_tok)) && (c3__bout == p_tok) ) { + if (c3__jinx == tok) { + u3m_timer_pop(); + } + else if ( (c3y == u3r_trel(tok, &p_tok, &q_tok, &r_tok)) && (c3__bout == p_tok) ) { // get the microseconds elapsed u3_atom delta = u3ka_sub(u3i_chub(u3t_trace_time()), u3k(r_tok)); diff --git a/pkg/noun/platform/darwin/rsignal.h b/pkg/noun/platform/darwin/rsignal.h index a14670e19a..bdf75286e6 100644 --- a/pkg/noun/platform/darwin/rsignal.h +++ b/pkg/noun/platform/darwin/rsignal.h @@ -9,5 +9,6 @@ #define rsignal_install_handler signal #define rsignal_deinstall_handler(sig) signal((sig), SIG_IGN) #define rsignal_setitimer setitimer +#define rsignal_getitimer getitimer #endif /* ifndef NOUN_PLATFORM_DARWIN_RSIGNAL_H */ diff --git a/pkg/noun/platform/linux/rsignal.h b/pkg/noun/platform/linux/rsignal.h index 9c55cee945..0507751f1c 100644 --- a/pkg/noun/platform/linux/rsignal.h +++ b/pkg/noun/platform/linux/rsignal.h @@ -9,5 +9,6 @@ #define rsignal_install_handler signal #define rsignal_deinstall_handler(sig) signal((sig), SIG_IGN) #define rsignal_setitimer setitimer +#define rsignal_getitimer getitimer #endif /* ifndef NOUN_PLATFORM_LINUX_RSIGNAL_H */