#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#define SIGNAL(name) signal_event(name, 1)
typedef int8_t prio_t;
#define PRIO_IDLE INT8_MAX
typedef uint8_t sched_idx;
typedef int_fast8_t (*event_func_t)(uintptr_t);
typedef struct s_event_handler {
struct s_event_handler *next;
event_func_t func;
uintptr_t arg;
prio_t prio;
#ifdef EVENT_CKSUM
uint8_t cksum;
#endif
} event_handler_t;
typedef struct s_event {
event_handler_t
*first,
*current;
sched_idx sq_idx;
#ifdef EVENT_CKSUM
uint8_t cksum;
#endif
uint8_t bcast;
struct s_event *next;
} event_t;
#define BIND_EVENT(_event, _prio, _func, _arg) do { \
static event_handler_t _anon_event_handler = { \
.prio = (_prio), .func = (_func), .arg=(_arg) \
}; \
bind_event((_event), &_anon_event_handler); \
} while(0)
#define EVENT_HANDLER(_func, _ename, _prio) \
static event_handler_t _ename = { \
.prio=(_prio), .func=(_func), .arg=(uintptr_t)&_ename \
}
void signal_event(event_t *, uint_fast8_t bcast);
void unbind_event(event_t *, event_handler_t *);
void unbind_all(event_t *);
void bind_event(event_t *, event_handler_t *);
void eh_update(event_handler_t *, prio_t, event_func_t, uintptr_t);
uint_fast8_t process_event(prio_t);
void scheduler(void);
static event_handler_t last_handler;
static event_t last_pending;
static event_t *pend_events = &last_pending;
#define SCHED_INC_SIZE 32
static struct s_event **sched_queue=NULL;
static sched_idx sched_len=0;
static sched_idx sched_size=0;
static sched_idx sched_maxlen=0;
#if 1
void sched_debug(const char *s)
{
sched_idx n;
const char *d="";
printf("%s", s);
for (n=0; n<sched_len; n++) {
assert(sched_queue[n]!=NULL);
assert(sched_queue[n]->current!=NULL);
printf("%s[%d] %8.8lx", d, sched_queue[n]->current->prio, (long)sched_queue[n]);
d=", ";
}
puts("");
}
#else
#define sched_debug(s)
#endif
#ifdef EVENT_CKSUM
static uint_fast8_t cksum(const void *data, size_t size)
{
const char *p=data;
uint_fast8_t sum=0;
while (size--) {
sum+=*p, sum^=*p, p++;
}
return sum&0xFF;
}
#define EVENT_CHECK(e) \
assert(cksum((e), offsetof(event_t, cksum)==(e)->cksum)
#define HANDLER_CHECK(h) \
assert(cksum((h), offsetof(event_handler_t, cksum)==(h)->cksum))
#define EVENT_CKSUM(e) do { \
(e)->cksum=cksum((e), offsetof(event_t, cksum)); \
} while(0)
#define HANDLER_CKSUM(h) do { \
(h)->cksum=cksum((h), offsetof(event_handler_t, cksum)); \
} while(0)
#else
#define EVENT_CHECK(e)
#define HANDLER_CHECK(h)
#define EVENT_CKSUM(e)
#define HANDLER_CKSUM(h)
#endif
static void sq_remove_first(void)
{
sched_idx n;
assert(sched_len>0);
if (--sched_len == 0) return;
sched_queue[0]=sched_queue[sched_len];
n=0;
while (1) {
sched_idx l, r, m;
l=2*n+1, r=2*n+2;
m=n;
#if 0
printf("n%u=%d (%8.8lx)", n, sched_queue[n]->prio, sched_queue[n]);
if (l<sched_len) printf(", l%u=%d (%8.8lx)",
l, sched_queue[l]->prio, sched_queue[l]);
if (r<sched_len) printf(", r%u=%d (%8.8lx)\n",
r, sched_queue[r]->prio, sched_queue[r]);
#endif
if (l<sched_len) {
assert(sched_queue[l]!=NULL);
assert(sched_queue[l]->current!=NULL
&& sched_queue[l]->current!=&last_handler);
if (sched_queue[l]->current->prio < sched_queue[m]->current->prio)
m=l;
}
if (r<sched_len) {
assert(sched_queue[r]!=NULL);
assert(sched_queue[r]->current!=NULL
&& sched_queue[r]->current!=&last_handler);
if (sched_queue[r]->current->prio < sched_queue[m]->current->prio)
m=r;
}
if (m!=n) {
//puts(" -> swap");
event_t *t;
t=sched_queue[m];
sched_queue[m]=sched_queue[n];
sched_queue[n]=t;
sched_queue[m]->sq_idx=m+1;
sched_queue[n]->sq_idx=n+1;
n=m;
continue;
}
break;
}
sched_debug("remove: ");
}
static sched_idx sq_inc_prio(sched_idx c)
{
sched_idx p;
event_t *t;
while (c>0) {
p=(c-1)/2;
assert(sched_queue[p]!=NULL);
assert(sched_queue[c]!=NULL);
assert(sched_queue[p]->current!=NULL && sched_queue[p]->current!=&last_handler);
assert(sched_queue[c]->current!=NULL && sched_queue[c]->current!=&last_handler);
if (sched_queue[p]->current->prio <= sched_queue[c]->current->prio)
break;
t=sched_queue[p];
sched_queue[p]=sched_queue[c];
sched_queue[c]=t;
sched_queue[p]->sq_idx=p+1;
sched_queue[c]->sq_idx=c+1;
c=p;
}
return c;
}
static void sq_add(event_t *event)
{
sched_idx c;
assert(event!=NULL);
assert(event->current!=NULL && event->current!=&last_handler);
c=sched_len, sched_len++;
sched_queue[c]=event;
event->sq_idx=sq_inc_prio(c)+1;
}
static void sq_remove_any(sched_idx c)
{
while (c > 0) {
sched_idx p;
event_t *t;
p=(c-1)/2;
assert(sched_queue[c]!=NULL);
assert(sched_queue[p]!=NULL);
t=sched_queue[p];
sched_queue[p]=sched_queue[c];
sched_queue[c]=t;
sched_queue[c]->sq_idx=c+1;
c=p;
}
sq_remove_first();
}
void signal_event(event_t *evptr, uint_fast8_t bcast)
{
USE_CRITICAL;
ASSERT(evptr != NULL);
BEGIN_CRIT();
if (evptr->first!=NULL) {
if (bcast) evptr->bcast=1;
if (evptr->next==NULL) {
evptr->next=pend_events;
pend_events=evptr;
}
}
END_CRIT();
}
static void sched_event(event_t *evptr)
{
sched_idx c;
assert(evptr!=NULL);
EVENT_CHECK(evptr);
if (evptr->first==NULL) return;
assert(evptr->current!=NULL);
if (evptr->current!=&last_handler)
if (evptr->current->prio == evptr->first->prio)
return;
evptr->current=evptr->first;
c=evptr->sq_idx-1;
if ((int)c < 0) sq_add(evptr);
else evptr->sq_idx=sq_inc_prio(c)+1;
EVENT_CKSUM(evptr);
}
static void signal_pending(void)
{
USE_CRITICAL;
event_t *e, *n;
while (1) {
BEGIN_CRIT();
e=pend_events;
pend_events=&last_pending;
END_CRIT();
if (e==&last_pending) break;
do {
assert(e!=NULL);
sched_event(e);
n=e->next;
BEGIN_CRIT();
e->next=NULL;
END_CRIT();
e=n;
} while (e!=&last_pending);
}
sched_debug("signal: ");
}
void unbind_event(event_t *event, event_handler_t *handler)
{
event_handler_t *h, **nh;
sched_idx c;
uint_fast8_t is_planned=0;
assert(handler != NULL && event != NULL);
if (handler->next==NULL) return;
if (event->first==NULL) return;
nh=&event->first;
h=*nh;
while (h!=&last_handler && handler->prio >= h->prio) {
assert(h->next!=NULL);
if (h == event->current) is_planned=1;
if (h == handler) {
//BEGIN_CRIT();
*nh=h->next;
//END_CRIT();
break;
}
nh=&h->next;
h=h->next;
}
if (h!=handler) return;
h->next=NULL;
c=event->sq_idx-1;
if ((int)c < 0) {
assert(is_planned==0);
return;
}
if (is_planned==0) return;
assert(event->current!=&last_handler && event->current!=NULL);
if (event->current->prio <= handler->prio)
return;
sq_remove_any(c);
sq_add(event);
}
void unbind_all(event_t *event)
{
sched_idx c;
event_handler_t *h, *n;
assert(event != NULL);
h=event->first;
while (h!=&last_handler) {
assert(h!=NULL);
n=h->next;
h->next=NULL;
h=n;
}
event->first=event->current=NULL;
c=event->sq_idx-1;
if ((int)c >= 0) {
sq_remove_any(c);
}
signal_pending();
}
void bind_event(event_t *event, event_handler_t *handler)
{
event_handler_t *h, **nh;
uint_fast8_t false_event=0;
assert(event != NULL && handler != NULL);
assert(handler->func != NULL);
assert(handler->next == NULL);
nh=&event->first;
h=*nh;
if (h==NULL) h=&last_handler;
while (h!=&last_handler && handler->prio >= h->prio) {
assert(h->next!=NULL);
if (h == event->current) false_event=1;
if (h == handler)
return;
nh=&h->next;
h=h->next;
}
handler->next=h;
//BEGIN_CRIT();
*nh=handler;
//END_CRIT();
if (event->sq_idx==0) {
sched_maxlen++;
if (sched_maxlen > sched_size) {
unsigned n;
n=sched_size+SCHED_INC_SIZE;
if (sizeof(sched_idx)==1) {
if (n>=UINT8_MAX) n=UINT8_MAX-2;
if (n <= sched_size) _Exit(EX_ASSERT); // TODO
}
sched_size=n;
sched_queue=realloc(sched_queue,
sched_size*sizeof(sched_queue[0]));
if (sched_queue==NULL) _Exit(EX_NOMEM);
}
event->sq_idx=(sched_idx)-1;
return;
}
if (event->sq_idx==(sched_idx)-1) return;
}
void eh_update(event_handler_t *h, prio_t prio, event_func_t func, uintptr_t arg)
{
h->prio=prio;
h->func=func;
h->arg=arg;
}
uint_fast8_t process_event(prio_t prio)
{
event_t *e;
event_handler_t *h;
int_fast8_t r;
signal_pending();
if (sched_len==0) {
return 0;
}
e=sched_queue[0];
assert(e->current!=NULL && e->current!=&last_handler);
EVENT_CHECK(e);
h=e->current;
HANDLER_CHECK(h);
if (h->prio > prio) {
return 0;
}
assert(h->next!=NULL);
HANDLER_CHECK(h->next);
e->current=h->next;
r=h->func(h->arg);
if (e->bcast && r==0) {
if (h->next!=&last_handler && h->prio >= h->next->prio) {
EVENT_CKSUM(e);
return 1;
}
}
sq_remove_first();
e->sq_idx=(sched_idx)-1;
if (e->current!=&last_handler) {
if (e->bcast && r==0) sq_add(e);
} else {
e->bcast=0;
}
EVENT_CKSUM(e);
return 1;
}
void scheduler(void)
{
while (sched_debug("run: "), process_event(PRIO_IDLE));
}
[ZX]