Mon 16 Mar 11:09:06 CET 2026

This commit is contained in:
sbosse 2026-03-16 11:12:59 +01:00
parent ab7820fda5
commit 1d76b0bad0

341
src/event.c Normal file
View 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;
}