The event driven programming style
cnet employs an event-driven style of programming similar,
though not identical,
to the data-link layer protocols presented in A.S. Tanenbaum
[Prentice-Hall,1988,...2003].
Execution proceeds when cnet
informs protocols that an event of interest has occurred.
Protocols are expected to respond to these events.
Events occur when
a node reboots,
the Application Layer has a message for delivery,
the Physical Layer receives a frame on a link,
a timer event expires,
a debugging button is selected,
and a node is (politely) shutdown.
No event is delivered if a node pauses, crashes or suffers a hardware failure.
These last few events only occur in simulations designed to
address the higher layers of the OSI model (say, the Session Layer).
- IMPORTANT:
- cnet employs an event-driven style of
programming, not an interrupt-driven style.
In particular,
while an event handler is executing it will not be interrupted by
the arrival of another event.
In support of this,
there are no facilities to specify non-interruptable, high-priority
handlers nor to protect data structures with semaphores or monitors
(you won't need them!).
Event-handling functions must first be registered to receive incoming
events with a call to CNET_set_handler() .
Event-handling functions will be later called by cnet
with three parameters.
The first is the type of event (the reason the handler is being called),
one of the CnetEvent enumerated values.
The second parameter is a unique timer (described later)
and the third,
some user-specified data.
Each node is initially rebooted by calling its reboot_node() function.
This is the only function that you must provide and is assumed to have
the name reboot_node() unless overridden with either the
-R option
or the rebootfunc node attribute.
The purpose of calling reboot_node() is to give protocols
a chance to allocate any necessary dynamic memory,
initialize variables,
inform cnet in which events protocols are interested,
and which handlers that cnet should call when these events occur.
- IMPORTANT:
- Event-handling functions must execute to their completion -
they must perform their actions and then simply return.
cnet does not employ pre-emptive scheduling - once an event
handling function is being executed, it will not be interrupted
by the arrival of another event.
Event-handling functions are of type
void -
that is, they do not return a value.
If event-handling functions do not return,
the whole simulation, including all windowing,
will block and cnet must be interrupted via the shell.
An example of cnet's event handlers
Consider the following protocol skeleton.
Like all cnet protocol files,
the standard cnet header file is first included.
This contains all definitions and function prototypes necessary for protocols.
Execution commences at the reboot_node() function
(like commencing at main() in a standard C program).
reboot_node() first informs cnet
that when the Application Layer has a message for delivery to another host
(because EV_APPLICATIONREADY occurs)
cnet should call the function new_message() which
reads the new message from the Application Layer to commence the
delivery process.
The function frame_arrived() will similarly be called when the
EV_PHYSICALREADY event occurs.
#include <cnet.h>
void new_message(CnetEvent ev, CnetTimerID timer, CnetData data)
{
...
success = CNET_read_application( ... );
...
}
void frame_arrived(CnetEvent ev, CnetTimerID timer, CnetData data)
{
...
success = CNET_read_physical( ... );
...
}
void reboot_node(CnetEvent ev, CnetTimerID timer, CnetData data)
{
...
success = CNET_set_handler(EV_APPLICATIONREADY, new_message, 0);
success = CNET_set_handler(EV_PHYSICALREADY, frame_arrived, 0);
...
}
A user-defined data value must be
initially provided as the third parameter to CNET_set_handler() .
When the handler's event eventually occurs,
the same value is passed as the third parameter to the handler.
Within more complex protocols,
you will typically want to pass an integer or, with care,
a pointer, via this third parameter.
The user-defined data value for timeout handlers
(for EV_TIMER0..EV_TIMER9 )
must also be provided as the third parameter to CNET_start_timer() .
We can also write the above code as:
#include <cnet.h>
EVENT_HANDLER(new_message)
{
...
success = CNET_read_application( ... );
...
}
EVENT_HANDLER(frame_arrived)
{
...
success = CNET_read_physical( ... );
...
}
EVENT_HANDLER(reboot_node)
{
...
success = CNET_set_handler(EV_APPLICATIONREADY, new_message, 0);
success = CNET_set_handler(EV_PHYSICALREADY, frame_arrived, 0);
...
}
EVENT_HANDLER is simply a C macro
defined in the <cnet.h> header file.
While this (completely optional)
style improves both readability and consistency,
it can introduce confusion as to where the three formal parameters,
ev, timer and data ,
actually "come from".
However, the parameters passed to event handlers are rarely used in
practice, so this form is considered more convenient.
Providing command-line parameters when a node reboots
As a special case, the data (third) parameter to each node's
handler for the EV_REBOOT event provides "command-line"
arguments when that node reboots.
The value passed in data is a pointer to a
NULL -terminated vector of character strings,
akin to the value of argv in a traditional C program.
Consider the following example code:
#include <cnet.h>
void reboot_node(CnetEvent ev, CnetTimerID timer, CnetData data)
{
char **argv = (char **)data;
int argc = 0;
if(argv[0] != NULL) {
printf("command line arguments:");
for(argc=0 ; argv[argc] != NULL ; ++argc)
printf(" '%s'", argv[argc]);
printf("\n");
}
else
printf("no command line arguments\n");
...
}
The optional command-line arguments for the EV_REBOOT handler
may either be passed on cnet's own command-line
(after the name of the topology file or
the -r option),
or provided (as a single string) via the
rebootargs
node attribute in the topology file.
Timers
cnet supports 10 timer event queues
providing a call-back mechanism for the protocol code.
For example,
the event EV_TIMER1 may be requested to be "raised"
in 5000000usec , and cnet will call
the EV_TIMER1 event-handler in 5 seconds' time.
Timers are referenced via unique values of type CnetTimerID .
For example:
CnetTimerID timer1;
.....
timer1 = CNET_start_timer(EV_TIMER1, (CnetTime)5000000, 0);
The timer has significance for functions handling timer events;
all other handlers will simply receive the special NULLTIMER .
Notice that timers do not reflect the current time,
nor how much time remains until they expire;
they simply specify which timer has expired.
When a timer expires,
the event-handler for the corresponding event is invoked with the event and
the unique timer as parameters.
Timers may be cancelled prematurely
with CNET_stop_timer()
to prevent them expiring,
for example:
(void)CNET_stop_timer(timer1);
though they are automatically cancelled
as a result of their handler being invoked.
Determining which physical link has just been severed or reconnected
As a special case, the data (third) parameter to each node's
handler for the EV_LINKSTATE event indicates which
physical link has just been severed or reconnected.
Consider the following example code:
#include <cnet.h>
void link_state_changed(CnetEvent ev, CnetTimerID timer, CnetData data)
{
int link = (int)data;
if(linkinfo[link].linkup) {
......
}
else {
......
}
}
When the EV_LINKSTATE hander is invoked,
the node's values of linkinfo[] reflect
the new (current) state of each link.
|