diff --git a/src/event.c b/src/event.c new file mode 100644 index 0000000..0b396e3 --- /dev/null +++ b/src/event.c @@ -0,0 +1,341 @@ +#include "config.h" +#include "types.h" +#include "error.h" +#include "lexer.h" +#include "ops.h" +#include "mem.h" +#include "stack.h" +#include "reg.h" +#include "utils.h" +#include "vm.h" +#include "printf.h" +#include "ccall.h" +#include "context.h" +#include "event.h" +#include "tokens.h" +#include "debug.h" +#include "str.h" +#include "log.h" + +/* + All tasks/VMs (must) share same event logic and data structures! +*/ +events_t events; + +void EventInit(char reset) { + int i; + if (reset) { + // don't delete host events, only reset user events + events.mask=0; + events.user=events.top; + for(i=0;iname,ev->index,C); + } +} + +/* + Event names must be at least 4 letters longm but only first 4 letters are (case-sensitive for speed) considered. +*/ +int AddEvent(char *name, index_t index, evtype_t type) { + int i; + for(i=0;ilisteners;i++) { + if (ev->handler[i]) { + // handler proc must be called by VM +#if DEBUG > 0 + log(LOGDEBUG3,"RaiseEventIndex: C%d[state=%x] -> PEVENTPEND\n",CONTEXTID(ev->context[i]),ev->context[i]->R->state,sizeof(vm_state_t)); +#endif + ev->context[i]->R->state |= PEVENTPEND; // notify task for pending event + j++; h++; + } else { + // await event, wake-up task + C=ev->context[i]; + if (C && C->R->state & PINTERRUPT) { +#if DEBUG > 0 + log(LOGDEBUG3,"RaiseEventIndex: C#%d -> PRUNNING\n",CONTEXTID(C)); +#endif + C->R->state &= ~(PINTERRUPT|PTIMEOUT|PAWAIT); + C->R->state |= PRUNNING; + C->R->timeout = 0; + if (ev->type==EVUSER) C->R->event = i; + else C->R->event = ev->index; + h++; + } + ev->context[i]=NULL; + } + } + if (j) { + // only pending interrupts must be handled by VM loop + EVSET(events.mask,evindex); + ev->pending=j; + } + ev->listeners=j; + if (h==0) ev->latch=1; + return h; +} + +int RaiseEvent(char *name, index_t index) { + int i,j; + index_t evindex=FindEvent(name,index); +#if DEBUG>0 + log(LOGDEBUG2,"RaiseEvent (%s:%d) -> evindex=%d\n",name,index,evindex); +#endif + if (evindex!=-1) { + RaiseEventIndex(evindex); + events.last=evindex; + } + return evindex; +} + +int SendEventIndex(index_t evindex) { + int i,j; + event_t *ev; + ev=&events.table[evindex]; + if (ev->send!=NULL) { + ev->send(ev,evindex); + } +} + +int SendEvent(char *name, index_t index) { + index_t evindex=FindEvent(name,index); + if (evindex!=-1) { + SendEventIndex(evindex); + } + return evindex; +} +/* + Install event handler, find free slot + There may be only one event handler per task and event(devid) +*/ +int OnEvent(char *name, index_t index, address_t call, context_t *C) { + index_t evindex=FindEvent(name,index); +#if DEBUG>0 + log(LOGDEBUG1,"OnEvent(%s,%d,%d,C%d):%d\n",name,index,call,CONTEXTID(C),evindex); +#endif + return OnEventIndex(evindex,call,C); +} + +int OnEventIndex(index_t evindex, address_t call, context_t *C) { + int i; + event_t *ev; + +#if DEBUG>0 + log(LOGDEBUG1,"OnEventIndex(%d,C%d):%d\n",call,CONTEXTID(C),evindex); +#endif + + if (evindex==-1) return -1; + ev=&events.table[evindex]; + + if ((ev->listeners+1)==MAXTASKS) { + error(C->R,EEVENT); + return -1; + } + i=ev->listeners++; + ev->context[i]=C; + ev->handler[i]=call; // 0: await + EVSET(C->evmask,evindex); + + if (ev->latch) { + // consume earlier event + ev->latch=0; + RaiseEventIndex(evindex); + } + + return evindex; +} + +/* + Remove event handler +*/ +int OffEvent(char *name, index_t index, context_t *C) { + index_t evindex=FindEvent(name,index); +#if DEBUG>0 + log(LOGDEBUG2,"OffEvent('%s',%d):%d C#%d \n",name,index,evindex,CONTEXTID(C)); +#endif + if (evindex==-1) return -1; + return OffEventIndex(evindex,C); +} + +int OffEventIndex(index_t evindex, context_t *C) { + event_t *ev=&events.table[evindex]; + for(int i=0;ilisteners;i++) { + if (ev->context[i]==C) { + ev->context[i]=NULL; + ev->handler[i]=0; + EVCLEAR(C->evmask,evindex); + // if there are handlers after this entry they must be shifted + if ((i+1)!=MAXTASKS && ev->context[i+1]) { + for(int j=i+1;jcontext[i]=ev->context[j]; + ev->handler[i]=ev->handler[j]; + } + ev->context[i]=NULL; + ev->handler[i]=0; + } + ev->listeners--; + return evindex; + } + } + return -1; +} +/* + Event handling called from VM loop +*/ +int HandleEvents(context_t *C) { + int i,j,first=0; + int32_t mask=events.mask & C->evmask; // handle ready events for this task context + event_t *ev; +#if DEBUG>0 + log(LOGDEBUG2,"HandleEvents: Entry mask=%x\n",mask); +#endif + if (!mask) return 0; + // speed up event handling: find start index, maybe the only pending and handled event here + if (events.last!=-1 && (mask>= (1<>first; + } + for(i=first;(i0 + log(LOGDEBUG2,"HandleEvents: Activated, evindex=%d mask=%x\n",i,mask); +#endif + // prepare event handling + // find our context + for(j=0;jlisteners;j++) { + if (!ev->context[j]) return 0; + if (ev->context[j]==C) { + // TODO TBC ?? mask=mask>>1; +#if DEBUG>0 + log(LOGDEBUG2,"HandleEvents: Context found, evindex=%d contextid=%d\n",i,j); +#endif + ev->pending--; + if (ev->handler[j]) { +#if DEBUG>0 + log(LOGDEBUG2,"HandleEvents: Handler found, evindex=%d contextid=%d\n",i,j); +#endif + // call handler, prepare stack frame +#if HAS_FUNC > 0 + PUSH(C->FS,(number_t)C->DS->sp); + PUSH(C->FS,(number_t)C->R->fp); +#endif + PUSH(C->FS,(number_t)C->R->pc); + PUSH(C->FS,(number_t)TEVENT); + C->R->state |= PHANDLER; + C->R->pc = ev->handler[j]; + if (ev->type==EVUSER) C->R->event = i; + else C->R->event = ev->index; + } else if (!(C->R->state & PRUNNING)) { + // go! + C->R->state |= PRUNNING; + } + if (!mask) { + C->R->state &= ~PEVENTPEND; + } + if (!ev->pending) EVCLEAR(events.mask,i); + return 1; + } + } + } + mask=mask>>1; + } + return 0; +} + +/* + Handle pending timeout in this context, if any, called from IO loop + If timeout happened here, return 1, else 0. +*/ +int HandleTimeout(context_t *C) { + reg_t *R=C->R; + time_t t=MILLIS(); + int status=0; +#if DEBUG > 0 + log(LOGDEBUG2,"HandleTimeout timeout=%d t=%d\n",R->timeout,t); +#endif + if ((R->state&PTIMEOUT) && R->timeout<=t) { + R->state &= ~(PTIMEOUT|PINTERRUPT); + R->state |= PRUNNING; + R->timeout = 0; + if (R->event!=-1) { + // clean-up pending event entry + OffEventIndex(R->event, C); + } + status=1; + } + return status; +} + + +/* + Host platform notification +*/ +static void (*notify_callback)(notify_t cls, int arg, void *data)=0; +void Notify(notify_t cls, int arg, void *data) { + if (notify_callback) notify_callback(cls,arg,data); +} +void OnNotify(void (*callback)(notify_t cls, int arg, void *data)) { + notify_callback=callback; +} +