Internal and External Events
This section describes what happens when an event arrives to the
Library - either from the application or from the network. The Library
provides three different ways of handling events, and it is necessary
to be aware of these modes in the design phase of an application as
they have an impact on the architecture of the application. The
Library can be used in multiple modes simultaneously and an
application can change mode as a function of the action requested by
the user. The three different modes are described in the
following:
- Base Mode (preemptive)
- In this mode all requests are handled in a preemptive way that does
not allow for any events to pause the execution of a thread or kill
it. This mode is in other words strictly single threaded and the major
difference between this mode and the next two modes is that all
sockets are made blocking instead of non-blocking. This mode can
either be used in forking applications or in threaded applications
using an external thread model where non-blocking I/O is not a
requirement.
- Active Mode (Internal Event Loop)
- In this mode the event loop is placed in the Library in the HTEvntrg module. The mode
can either be used by character based applications with a limited
capability of user interaction, or it can be used by more advanced GUI
clients where the window widget allows redirection of user events to
one or more sockets that can be recognized by a select() call. It
should be noted, that even though all sockets are non-blocking, the
select() function is blocking if no sockets are pending so if no
actions are pending, the select call will be put to sleep.
The HTNet module
contains a thread scheduler which gives highest priority to the events
on the redirected user events which allows a smooth operation on GUI
applications with a fast response time. This mode has a major impact
on the design of the application as much of the application code may
find itself within call back functions. As an example, this mode is
currently used by the Arena client and
the Line Mode Browser.
- Passive mode (External Event Loop)
- This mode is intended for applications where user events can not
be redirected to a socket or there is already an event loop that can
not work together with the event loop in the Library. The major
difference from the Active mode is that instead of using the
event loop defined in the HTEvntrg module, this
module is overridden by the application as described in the "User's Guide". The Passive mode has the
same impact on the application architecture as the Active mode
except for the event loop, as all library interactions with the
application are based on call back function.
One important limitation in the libwww thread model is that the
behavior is undefined if an external scheduler is provided using the
internal threads in the Library with preemptive scheduling mechanism.
The reason for this is that the Library is "libwww thread safe" when
using one stack and one set of registers as in Active mode only
when a change of active thread is done as a result of a blocking I/O
operation. However, using an external thread model, this problem does
not exist.
Providing Call Back Functions
The thread model in the Library is foreseen to work with native thread
interfaces but can also be used in a non-threaded environment. In the
latter case, the Library handles the creation and termination of its
internal threads without any interaction required by the
application. The thread model is based on call back functions of which
at least one user event handler and a event terminator must must be
supplied by the application. However, the application is free to
register as many additional user event handlers as it wants.
The dashed lines from the event loop to some of the access modules
symbolizes that the access method is not yet implemented using
non-blocking I/O, but the event loop is still a part of the
call-stack. In this situation the Library will automatically use
blocking sockets which is equivalent to the Base Mode.
- User Event Handlers
- An application can register a set of user event handlers to handle
events on sockets defined by the application to contain actions taken
by the user. This can for example be interrupting a request, start a
new request, or scroll a page. However, this requires that the actual
window manager supports redirection of event on sockets.
- Event Termination
- This function is called from the Library every time a request is
terminated. It passes the result of the request so that the
application can update the history list etc. depending on the
result. From the Library's point of view there is little difference
between a user event handler and this function, as it in both cases is
a call back function.
- Timeout Handler
- In Active mode, the select() function in the Library
event loop is blocking even though the sockets are non-blocking. This
means that if no actions are pending on any of the registered sockets
then the application will block in the select() call. However, in
order to avoid sockets hanging around forever, a timeout is provided
so that hanging threads can be terminated.
Often an event handler needs to return information about a change of
state as a result of an action executed by the handler, for example if
a new request is issued, a ongoing request is interrupted, the
application is to terminated etc. This information can be handed back
to the Library using the return values of the call back function.
There are several situations where a thread has to be killed before it
has terminated normally. This can either be done internally by the
Library or the application. The application indicates that a thread is
to be interrupted, for example if the user has requested the operation
to stop, by using a specific return value from one of the user event
handlers. The Library then kills the thread immediately and the result
is returned to the application.
Henrik Frystyk, libwww@w3.org, December 1995