Mon 16 Mar 11:09:06 CET 2026
This commit is contained in:
parent
ab7820fda5
commit
1d76b0bad0
341
src/event.c
Normal file
341
src/event.c
Normal file
|
|
@ -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;i<events.top;i++) {
|
||||
events.table[i].listeners=0;
|
||||
}
|
||||
} else {
|
||||
// coldstart; then AddEvent
|
||||
memset(&events,0,sizeof(events));
|
||||
}
|
||||
events.last=-1;
|
||||
}
|
||||
|
||||
void EventCleanUp(context_t *C) {
|
||||
for(int i=0;i<events.top;i++) {
|
||||
event_t *ev=&events.table[i];
|
||||
OffEvent(ev->name,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;i<events.top;i++) {
|
||||
if (EVNAMECMP(&events.table[i].name,name) && events.table[i].index==index) return -1;
|
||||
}
|
||||
memset(&events.table[events.top],0,sizeof(event_t));
|
||||
// only 4 letters are relevant
|
||||
EVNAMECOPY(&events.table[events.top].name,name);
|
||||
events.table[events.top].index = index;
|
||||
events.table[events.top].type = type;
|
||||
events.top++;
|
||||
if (type!=EVUSER) events.user=events.top;
|
||||
return events.top-1;
|
||||
}
|
||||
|
||||
/*
|
||||
Add a signal event sender function (provided by host application)
|
||||
*/
|
||||
int AddEventSender(char *name, index_t index, int (*send)()) {
|
||||
int evindex=FindEvent(name,index);
|
||||
if (evindex==-1 || events.table[evindex].type!=EVSIGNAL) return -1;
|
||||
events.table[evindex].send=send;
|
||||
return evindex;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
int FindEvent(char *name, index_t index) {
|
||||
int i;
|
||||
if (events.last!=-1 && EVNAMECMP(&events.table[events.last].name,name)) return events.last;
|
||||
for(i=0;i<events.top;i++) {
|
||||
// print_format("FindEvent[%d] %s?=%s %d=?%d\n",i,events.table[i].name,name,events.table[i].index,index);
|
||||
if (EVNAMECMP(&events.table[i].name,name) && events.table[i].index==index) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
Called externally by device drivers or timer interrupt, except for user events
|
||||
*/
|
||||
int RaiseEventIndex(index_t evindex) {
|
||||
int i,j,h=0;
|
||||
event_t *ev;
|
||||
context_t *C;
|
||||
ev=&events.table[evindex];
|
||||
|
||||
for(i=0,j=0;i<ev->listeners;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;i<ev->listeners;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;j<MAXTASKS;j++,i++) {
|
||||
ev->context[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<<events.last))) {
|
||||
first=events.last;
|
||||
// TODO TBC mask adjustment
|
||||
mask=mask>>first;
|
||||
}
|
||||
for(i=first;(i<events.top) && mask;i++) {
|
||||
if (mask&1) {
|
||||
// handle event
|
||||
ev=&events.table[i];
|
||||
#if DEBUG>0
|
||||
log(LOGDEBUG2,"HandleEvents: Activated, evindex=%d mask=%x\n",i,mask);
|
||||
#endif
|
||||
// prepare event handling
|
||||
// find our context
|
||||
for(j=0;j<ev->listeners;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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user