#include #include "strategy.h" #include "event.h" #include "stats.h" // linked list of all events event *event::top = NULL; // rescheduling strategy strategy *event::sched_strategy; // We need to keep track of when the network was last known to be busy, to be // able to process non-collision conflicts properly. A collision is always // going to mark all colliding hosts as colliding, but a continued // transmission won't manage to do this for hosts which are not yet in // the queue at the time of the original determination that it is ok for // the previous host to transmit. long event::last_busy = 0; // non-inclusive // When we have a collision, we don't transmit for the full amount of time we // originally thought we were going to transmit for. We only transmit for // 3 microseconds, because we try to stop as soon as possible. const int collision_noise_time = 3; void event::set_strategy(strategy *arg) { sched_strategy = arg; } event::event(long firsttime, long firstsize) { sched_strategy->initialize(&x); // whatever it wants to store there nexttime = firsttime; nextsize = firstsize; shuffle(); // specially for shuffle(), it's ok if it's not linked in yet } void event::shuffle() // allows event not to be in linked list yet { // unlink event **p; for (p = ⊤ *p && *p != this; p = &(*p)->next) ; if (*p) *p = (*p)->next; // else it's a new event, not yet in the linked list // find where I _should_ be for (p = ⊤ *p && (*p)->nexttime < nexttime; p = &(*p)->next) ; // link me in there next = *p; *p = this; } void event::process_one() { top->process(); // changes linked list via resched_*() } void event::process() { checkconflict(); // sets flags, for me and sometimes others if (iscollision) { stats_collision(nexttime); if (nexttime + collision_noise_time > last_busy) last_busy = nexttime + collision_noise_time; //long lasttime = nexttime; resched_conflict(); //printf("collision hence rescheduled %ld later\n", nexttime - lasttime); } else if (isconflict) { resched_conflict(); } else { record_stats(); if (nexttime + nextsize > last_busy) last_busy = nexttime + nextsize; resched_successful(last_busy); } shuffle(); } void event::checkconflict() { // Questions: // 1) does anyone else conflict with my transmission? This is in the // next two microseconds; after that, they'll wait. // 2) do I cause anyone else to have a collision or conflict? // Note that question 1 affects the answer to question 2. // If I don't actually do my transmission, I don't busy the network for // as long. // The "isconflict" flag is pretty much private to us, so I'm going to // give it a restricted meaning here. It means that there is a conflict // which is NOT a collision. So isconflict and iscollision are never // both 1. // It's possible that we have a conflict which is not yet diagnosed, if // we have been inserted into the queue at a position which overlaps // a transmission which has already been checked for conflict and // collision. However, in all such cases, the last_busy time will be // greater than our own start time. if (nexttime < last_busy) // last_busy is non-inclusive isconflict = 1; // If someone else already told me I'm going to have a conflict which is // not a collision, or we just noticed it immediately above, then skip // question #1 above; I'm not even going to be trying to transmit. // Otherwise, loop through and look for collisions or conflicts, and mark // others appropriately. if (!isconflict) { // if we have a collision, the duration is a fixed, short time. int mysize = iscollision ? collision_noise_time : nextsize; for (event *p = top; p; p = p->next) { if (p != this && p->nexttime >= nexttime && p->nexttime < nexttime + nextsize) { // Trouble. // If the start times are within two microseconds, it's a // collision; else not. If it's a collision, both parties // are affected. Else only the later one, which will be the // other host so long as we are calling checkconflict() on // the queue items in order. if (labs(nexttime - p->nexttime) <= 2) { iscollision = p->iscollision = 1; mysize = collision_noise_time; } else { p->isconflict = 1; } } } } } void event::resched_conflict() { nexttime += sched_strategy->choose_time(&x); iscollision = isconflict = 0; } event_quit::event_quit(long when) : event(when, 0) { } void event_quit::process() { stats_dump(); exit(0); } // we need to override other virtual functions to make class event_quit // non-abstract. These will never be called because it will exit first. void event_quit::resched_successful(long completion_time) { } void event_quit::record_stats() { }