/* $Id: event.c,v 10.1 92/10/06 23:10:23 ca Exp $ */ /*LINTLIBRARY*/ #define _EVENT_ /* so event.h knows that this is event.c */ #include #include #include "sim.h" #include "mempool.h" #include "component.h" #include "log.h" #include "packet.h" #include "event.h" /* event manager library. Unless otherwise specified, all routines return 0 for failure, non-zero for success: ev_init() init event queue ev_reset() clear event queue & set time to zero Event *ev_alloc() allocates and returns an event ev_free() frees an allocated event Event *ev_enqueue() add an event to the event queue ev_set_time() change an event's fire time ev_set_rtn() change an event's fire routine ev_set_arg() change an event's private fire routine argument ev_set_pkt() change the packet argument provided to an event's fire routine ev_set_src() change the event's source component ev_set_dest() change the event's destination component ev_display() display an event void ev_toggle_debug() toggle event library debugging messages void ev_fire_before_time() as above but all earlier events fire too ev_fire_first() cause first event in the queue to fire ev_fire_all_first() cause all events at head of queue with the same time to fire. Event *ev_dequeue() removes a particular event from the event queue and returns it void ev_dequeue_by_comp_and_time() remove from the queue any subset of events with particular source and destination components and fire times void ev_dequeue_by_time() remove from the queue all events due to expire at a particular time tick_t ev_now() return the simulator's clock value ***This is now a macro. Event *ev_member() if an event is in the queue return TRUE, else FALSE void ev_set_delay() Set the delay between event firings (in usec) tick_t ev_get_delay() Return the delay " Internal routines: ev_adda() adds a formatted event to the queue in firing order ev_fire() Actually call the action routine of an event. */ /* Made a global so that ev_now() can be made a macro. */ /*static tick_t event_standard_time = 0L;*/ /* * The functions surrounded by the following #ifndef/#endif are * declared in event.h as inline if using GCC. */ #ifndef INLINE /* These vars are made global to work with the inline functions. */ static tick_t event_delay_between_events = 0; /* Delay in uSec */ static Heap *event_queue = (Heap *) NULL; static Mempool *event_mempool = NULL; static int evdebug = 0; /* frees ev's storage */ static ev_free(ev) register Event *ev; { if(! ev) { return(FALSE); } mp_free(event_mempool, (char *) ev); return(TRUE); } /* allocate and return an empty event structure */ static Event *ev_alloc() { return((Event *)mp_alloc(event_mempool)); } /* add ev to the event queue, preserving time order */ static ev_adda(ev) register Event *ev; { heap_insert(event_queue, ev->ev_time, (caddr_t)ev); return(TRUE); } /* cause an event to happen */ static ev_fire(ev) Event *ev; { /* bump simulator time if this event is later than the current time */ if(event_standard_time < ev->ev_time) { event_standard_time = ev->ev_time; } /* usleep() is a BSDism, and sleep() would be much too long here. So, if not BSD, don't do anything. */ #ifdef BSD /* Wait for some amount of time (if necessary) */ if (event_delay_between_events) usleep(event_delay_between_events); #endif /* BSD */ if (evdebug) printf("Firing event at time %d, %d left in queue\n", event_standard_time, event_queue->size); if (record_flag) if (ev->ev_type & EV_CLASS_RECORD) fprintf(record_file, "%u '%s' %d %d\n", ev_now() * USECS_PER_TICK, ev->ev_dest->co_name, ev->ev_type, ev->ev_arg); /* execute routine */ (*(ev->ev_rtn))(ev->ev_src, ev->ev_dest, ev->ev_type, ev->ev_pkt, ev->ev_arg); ev_free(ev); return(TRUE); } /* enqueue a given event. If ev is NULL, allocate an event and then enqueue. it. Returns a pointer to the event. */ Event *ev_enqueue(type, src, dest, time, rtn, pkt, arg) Evtype type; Component *src, *dest; tick_t time; PFP rtn; Packet *pkt; caddr_t arg; { Event *ev; /* Allocate an event to enqueue */ if(evdebug) { printf("ev_enqueue: allocating event\n"); } if(! (ev = ev_alloc())) { if(evdebug) { printf("ev_enqueue: no memory for event object\n"); } return((Event *) NULL); } ev->ev_type = type; ev->ev_time = time; ev->ev_rtn = rtn; ev->ev_src = src; ev->ev_dest = dest; ev->ev_pkt = pkt; ev->ev_arg = arg; if(! ev_adda(ev)) { ev_free(ev); return((Event *) NULL); } return(ev); } #endif /* not INLINE */ ev_init() { if(evdebug) { printf("ev_init: creating queue\n"); } if (!(event_mempool = mp_init(EVENT_POOL_SIZE, sizeof(Event)))) return((int) NULL); if (!(event_queue = heap_create())) { mp_destroy(event_mempool); return((int) NULL); } return(TRUE); } /* Start over */ ev_reset() { Event *e; if (!event_queue) return(FALSE); if (evdebug) { printf("ev_reset: clearing queue; resetting time\n"); mp_stat(event_mempool); } while (e = (Event *)heap_extract_min(event_queue)) mp_free(event_mempool, (caddr_t)e); event_standard_time = 0L; return(TRUE); } /* set event ev's fire time to be newtime. Complain if simulator'scheduler's clock is later than requested time, or if event is not in the event queue. Note that the event must be dequeued and re-queued in timeout order, so this is a fairly expensive operation */ ev_set_time(ev, newtime) Event *ev; tick_t newtime; { if(! ev_member(ev)) { if(evdebug) { printf("ev_set_time: no event %x in queue\n", ev); } return(FALSE); } /* if requested time has already passed, return error */ if(ev_now() > newtime) { if(evdebug) { printf("ev_set_time: time %D earlier than current time %D\n", newtime, ev_now()); } } if(! ev_dequeue(ev)) { return(FALSE); } if(! ev_enqueue(ev->ev_type, ev->ev_src, ev->ev_dest, newtime, ev->ev_rtn, ev->ev_pkt, ev->ev_arg)) { return(FALSE); } return(TRUE); } /* set event ev's fire routine to be rtn. Complain if ev is not in the event queue */ ev_set_rtn(ev, rtn) Event *ev; PFP rtn; { if(! ev_member(ev)) { if(evdebug) { printf("ev_set_rtn: no event %x in queue\n", ev); } return(FALSE); } ev->ev_rtn = rtn; return(TRUE); } /* set event ev's fire routine's private argument to be arg. Complain if ev is not in the event queue */ ev_set_arg(ev, arg) Event *ev; caddr_t arg; { if(! ev_member(ev)) { if(evdebug) { printf("ev_set_arg: no event %x in queue\n", ev); } return(FALSE); } ev->ev_arg = arg; return(TRUE); } /* set event ev's fire routine's packet argument to be pkt. Complain if ev is not in the event queue */ ev_set_pkt(ev, pkt) Event *ev; Packet *pkt; { if(! ev_member(ev)) { if(evdebug) { printf("ev_set_pkt: no event %x in queue\n", ev); } return(FALSE); } ev->ev_pkt = pkt; return(TRUE); } /* set event ev's source component to be src. Complain if ev is not in the event queue */ ev_set_src(ev, src) Event *ev; Component *src; { if(! ev_member(ev)) { if(evdebug) { printf("ev_set_pkt: no event %x in queue\n", ev); } return(FALSE); } ev->ev_src = src; return(TRUE); } /* set event ev's destination component to be dest. Complain if ev is not in the event queue */ ev_set_dest(ev, dest) Event *ev; Component *dest; { if(! ev_member(ev)) { if(evdebug) { printf("ev_set_pkt: no event %x in queue\n", ev); } return(FALSE); } ev->ev_dest = dest; return(TRUE); } /** Set the delay between event firings, in usec */ void ev_set_delay(usecs) tick_t usecs; { event_delay_between_events = usecs; } tick_t ev_get_delay() { return(event_delay_between_events); } /* display an event */ ev_display(ev) Event *ev; { if(! ev_member(ev)) { if(evdebug) { printf("ev_display: no event %x in queue\n", ev); } return(FALSE); } printf("Event %x: fires at: %D, time now: %D, type: %d\n", ev, ev->ev_time, ev_now(), ev->ev_type); printf(" rtn: %x, pkt: %x, dest: %x, arg: %x\n", ev->ev_rtn, ev->ev_pkt, ev->ev_dest, ev->ev_arg); return(TRUE); } /* toggle event subroutine call tracing */ void ev_toggle_debug() { evdebug = !evdebug; printf("ev_toggle_debug: event tracing %s\n", evdebug ? "started" : "stopped"); } /* cause all events before and including time t to happen *now* */ void ev_fire_before_time(t) tick_t t; { Event *e; while (e = (Event *)heap_min(event_queue)) { if (e->ev_time > t) { break; } ev_fire((Event *)heap_extract_min(event_queue)); } } /* cause soonest event to fire. */ ev_fire_first() { Event *e; if (!(e = (Event *)heap_extract_min(event_queue))) return(FALSE); return(ev_fire(e)); } /* Cause all the events at the head of the queue with the same time to happen. */ ev_fire_all_first() { register Event *e = (Event *)heap_min(event_queue); if (!e) return(FALSE); ev_fire_before_time(e->ev_time); return(TRUE); } /* Uck. Dequeue a subset of events matching source, destination, and type arguments. A NULL source or destination matches all events. */ /* Since we are not allowed to modify the heap while iterating over it, I'll make this into a recursive function that looks for matching events on the way down, then deletes them on the way back up. I'm just using the stack to store a list of events that must be deleted, rather than creating my own list/array/whatever. */ static void ev_dequeue_by_comp_and_type1(); void ev_dequeue_by_comp_and_type(src, dest, type) Component *src, *dest; Evtype type; { ev_dequeue_by_comp_and_type1(src, dest, type, (Event *)heap_iter_init(event_queue)); } static void ev_dequeue_by_comp_and_type1(src, dest, type, ev) Component *src, *dest; Evtype type; Event *ev; { for ( ; ev; ev = (Event *)heap_iter(event_queue)) { if ( (ev->ev_src == src || src == NULL) && (ev->ev_dest == dest || dest == NULL) && (ev->ev_type == type) ) { ev_dequeue_by_comp_and_type1(src, dest, type, (Event *)heap_iter(event_queue)); if(evdebug) { printf("ev_dequeue_by_comp_and_type: removed event %x, source %x, dest %x, type %d\n", ev, ev->ev_src, ev->ev_dest, ev->ev_type); } (void) ev_dequeue(ev); break; } } } /* remove events with expire time t */ /* Since we are not allowed to modify the heap while iterating over it, I'll make this into a recursive function that looks for matching events on the way down, then deletes them on the way back up. I'm just using the stack to store a list of events that must be deleted, rather than creating my own list/array/whatever. */ static void ev_dequeue_by_time1(); void ev_dequeue_by_time(t) tick_t t; { ev_dequeue_by_time1(t, (Event *)heap_iter_init(event_queue)); } static void ev_dequeue_by_time1(t, ev) tick_t t; Event *ev; { for ( ; ev; ev = (Event *)heap_iter(event_queue)) { if (ev->ev_time == t) { ev_dequeue_by_time1(t, (Event *)heap_iter(event_queue)); ev_dequeue(ev); break; } } } /* ev_now() is now a macro. */ #ifdef UNDEFINED tick_t ev_now() { return(event_standard_time); } #endif Event *ev_member(ev) Event *ev; { if(! ev) { return((Event *) NULL); } return((Event *) heap_member(event_queue, (caddr_t)ev)); } /* remove event ev from the event queue */ Event *ev_dequeue(ev) register Event *ev; { if(heap_delete(event_queue, (caddr_t)ev)) { ev_free(ev); return(ev); } return((Event *) NULL); } ev_stat() { mp_stat(event_mempool); } ev_call(type, src, dest, rtn, pkt, arg) /* written at UMCP to uniform event callings. */ Evtype type; Component *src, *dest; PFP rtn; Packet *pkt; caddr_t arg; { if (evdebug) printf("Calling event at time %d, %d left in queue\n", event_standard_time, event_queue->size); /* execute routine */ (*(rtn))(src, dest, type, pkt, arg); return(TRUE); }