зеркало из https://github.com/mozilla/pjs.git
444 строки
25 KiB
HTML
444 строки
25 KiB
HTML
<HTML>
|
|
<HEAD>
|
|
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
|
|
<META NAME="GENERATOR" CONTENT="Mozilla/4.04 [en] (WinNT; U) [Netscape]">
|
|
<TITLE>System Scheduler Spec</TITLE>
|
|
</HEAD>
|
|
<BODY>
|
|
|
|
<H1>
|
|
System Scheduler specification</H1>
|
|
Last Update: Jan. 12, 1998
|
|
|
|
<P>The Scheduler is responsible for keeping track of when events such as scheduled
|
|
updates are supposed to occur, and to cause those events to occur.
|
|
In the interest of reusable components, the scheduler itself doesn't have
|
|
any knowledge of the client or of scheduled updates; rather, it will be
|
|
implemented as an API level service that will execute a specific callback
|
|
when the designated time occurs. The scheduling service will not
|
|
have any UI of its own but will rely on its clients to provide user indication
|
|
of scheduled events.
|
|
<H2>
|
|
Functionality</H2>
|
|
The scheduling service provides the following functionality:
|
|
<UL>
|
|
<LI>
|
|
Allows clients to register an event to occur at a particular time, optionally
|
|
within a range. When the time occurs, the function specified in the
|
|
event is called in its own thread, which terminates after the event occurs.
|
|
The scheduler can schedule events to occur only once or to repeat at a
|
|
given interval.</LI>
|
|
|
|
<LI>
|
|
Allows registration of callbacks to notify observers of events that are
|
|
scheduled, executed, or removed from the scheduling queue</LI>
|
|
|
|
<LI>
|
|
Allows all items in the queue to be executed immediately, without regard
|
|
to the current time</LI>
|
|
|
|
<LI>
|
|
Allows all items in the queue to be held so they do not execute, regardless
|
|
of whether the events' times have elapsed.</LI>
|
|
</UL>
|
|
The service does not
|
|
<UL>
|
|
<LI>
|
|
Guarantee any level of accuracy; the time specified in the event is the
|
|
earliest time at which the event will fire, and events in an time range
|
|
will not fire after the specified end time, but otherwise, the scheduler
|
|
will make a best-effort attempt to match the given schedule</LI>
|
|
|
|
<LI>
|
|
Persist information across Navigator startup. Items in the queue
|
|
when the scheduler is shutdown will be discarded and will not automatically
|
|
be reinstated into the queue when Navigator starts up again.</LI>
|
|
|
|
<LI>
|
|
Allow clients to determine the state of their scheduled events, or whether
|
|
a specific event has fired. However, a client can register itself
|
|
as an observer of event execution, after which it will receive notifications
|
|
of every event that was fired.</LI>
|
|
</UL>
|
|
In order to use the services that the scheduler provides, a scheduler instance
|
|
must first be created; subsequent calls to the scheduler can then reference
|
|
this instance of the scheduler. The scheduler does not make use of
|
|
global variables and therefore is not limited to a single instance, however,
|
|
it is expected that there will be only scheduler instance of the scheduler in
|
|
the client.
|
|
<H2>
|
|
APIs - Application Programming Interfaces</H2>
|
|
Interfaces for the scheduler fall into three categories. The first
|
|
set controls the operation of the scheduler itself, including starting
|
|
and stopping the scheduler; the second type of interface allows clients
|
|
to add and remove events and observers from the scheduler. The third
|
|
type covers the interfaces to the callbacks that the scheduler issues in
|
|
order to fire an event or to notify its observers.
|
|
<H3>
|
|
Scheduler Types and Structures</H3>
|
|
|
|
<H5>
|
|
<U>SchedulerPtr</U></H5>
|
|
<TT>typedef void *SchedulerPtr;</TT>
|
|
|
|
<P>All Scheduler APIs (with the exception of <TT>SchedulerStart</TT>) require
|
|
a reference to a scheduler instance in the form of a reference of type
|
|
<TT>SchedulerPtr</TT>. A <TT>SchedulerPtr</TT> is returned from <TT>SchedulerStart</TT>,
|
|
and its value is used as a handle for a scheduler. The structure
|
|
referenced by a <TT>SchedulerPtr</TT> should be considered an opaque entity;
|
|
do not directly manipulate any data referenced by a <TT>SchedulerPtr</TT>
|
|
reference.
|
|
<H5>
|
|
<U>SchedulerErr</U></H5>
|
|
<TT>typedef enum SchedulerErr {</TT>
|
|
<BR><TT> SCHED_ERR_BEGIN = -5,</TT>
|
|
<BR><TT> SCHED_ERR_BAD_EVENT
|
|
= -5,</TT>
|
|
<BR><TT> SCHED_ERR_BAD_TIME,</TT>
|
|
<BR><TT> SCHED_ERR_BAD_PARAMETER,</TT>
|
|
<BR><TT> SCHED_ERR_OUT_OF_MEMORY,</TT>
|
|
<BR><TT> SCHED_ERR_INVALID_SCHEDULER,</TT>
|
|
<BR><TT> SCHED_ERR_NOERR = 0,</TT>
|
|
<BR><TT> SCHED_WARN_EVENTS_DROPPED,</TT>
|
|
<BR><TT> SCHED_WARN_END = 1</TT>
|
|
<BR><TT>} SchedulerErr;</TT>
|
|
|
|
<P>Most APIs for the scheduler return a <TT>SchedulerErr</TT> type.
|
|
<TT>SchedulerErr</TT> is a typedef for a signed integer value; zero is
|
|
defined as no error (successful operation). Negative values are errors
|
|
(could not complete operation), and positive values are warnings (operation
|
|
completed with comments). All functions (even those which below indicate
|
|
that they "cannot fail") can return a <TT>ERR_SCHEDULER_INVALID</TT> if
|
|
the passed in scheduler reference is <TT>NULL</TT>.
|
|
<H5>
|
|
<U>SchedulerTime</U></H5>
|
|
<TT>typedef struct _SchedulerTime {</TT>
|
|
<BR><TT> PRUint32
|
|
repeating;</TT>
|
|
<BR><TT> PRInt32
|
|
range;</TT>
|
|
<BR><TT> PRTime
|
|
baseTime;</TT>
|
|
<BR><TT> PRTime
|
|
start;</TT>
|
|
<BR><TT> PRTime
|
|
end;</TT>
|
|
<BR><TT>} SchedulerTime;</TT>
|
|
|
|
<P>In order to instruct the scheduler to execute an event at a particular
|
|
time, clients specify a scheduled time expressed in a SchedulerTime structure.
|
|
The SchedulerTime structure has values which correspond to the event's
|
|
firing time (<TT>baseTime</TT>), and a start and end time from which the
|
|
event is valid (<TT>start</TT> and <TT>end</TT>, respectively). Additionally,
|
|
SchedulerTime specifies a <TT>range</TT>, which acts as a randomization
|
|
value within which the event's scheduled time can "drift," and a <TT>repeating</TT>
|
|
interval, both of whch are expressed in seconds.
|
|
|
|
<P>The exact time at which an event fires is compued as follows.
|
|
|
|
<P>Each time an event is to fire, a random value (-<TT>range</TT> <
|
|
value < <TT>range</TT>) is computed. This value, given as seconds,
|
|
is added to the specified<I> </I><TT>baseTime</TT> to derive the time that
|
|
the event is to occur. If the computed firing time is before the
|
|
<TT>start</TT> or after the <TT>end</TT> times, the firing time is pinned
|
|
to the start or end time.
|
|
|
|
<P>For repeating events, the event's next firing time is computed by adding
|
|
the value of <TT>repeating</TT> expressed in seconds, to the event's <TT>baseTime</TT>.
|
|
If the new computed time is falls outside the time interval specified by
|
|
the <TT>start</TT> and <TT>end</TT> times, the event "expires" and is removed
|
|
from the scheduler. Upon removal from the scheduling queue, expired
|
|
events If the event has not expired, a random value is selected as above
|
|
and added to this time to derive the new firing time. Note that repeating
|
|
events are computed based on the pre-randomized times; that is, the time
|
|
around which the range is computed will always be an integral number of
|
|
<TT>repeating</TT> intervals past the original <TT>baseTime</TT>.
|
|
|
|
<P>For example, if an event was scheduled with a time of noon, a range
|
|
of 300 (5 minutes) and an interval of 3600 (1 hour), the first event would
|
|
fire between 11:55 and 12:05 pm. Subseqent events would be fired
|
|
between :55 and :05 of every hour. If an <TT>end</TT> time were also
|
|
scheduled at 4:02 pm, then the event that was scheduled for 4 pm (noon
|
|
plus three iterations of <TT>interval</TT>) may, with randomization, attempt
|
|
to be scheduled for 4:04pm. This value would be pinned to 4:02 for
|
|
the actual firing time. However, the next time through, the new base
|
|
time would be 5 pm, which is beyond the end. In this case, the event
|
|
would simply cease to be scheduled and would be removed from the queue.
|
|
|
|
<P>Because of the way in which range and repetion are computed, it generally
|
|
makes sense to have <TT>repeat</TT> >> <TT>range</TT>. That is, the
|
|
interval at which events repeat should generally be greater than the amount
|
|
of time within which those events are allowed to drift. However,
|
|
from a practical standpoint, the implementation prevents scheduled events
|
|
from firing out of sequence, so specifying a range which is larger than
|
|
an interval is not an error, however, it may resuilt in a number of events
|
|
firing near-simultaneously. If an event added to the scheduler was scheduled
|
|
for a time in the past, the event will be fired immediately upon being
|
|
added to the scheduler.
|
|
<H5>
|
|
<U>SchedFuncPtr and SchedFuncDataPtr</U></H5>
|
|
<TT>typedef void *SchedFuncDataPtr;</TT>
|
|
<BR><TT>typedef void (PR_CALLBACK *SchedFuncPtr)(SchedulerPtr pScheduler,
|
|
PRUint32 eventID,</TT>
|
|
<BR><TT>
|
|
SchedFuncDataPtr pData)</TT>
|
|
|
|
<P><TT>SchedFuncPtr</TT> is a prototype for the function which is called
|
|
when the scheduler fires an event. The function returns nothing and
|
|
accepts three parameters, a reference to the scheduler from which the event
|
|
was dispatched, the event ID which identifies the event that executed the
|
|
function and an arbitrary argument passed in at event creation. The
|
|
third argument is opaque to the scheduler and can be used by clients to
|
|
transmit or store state information, etc. The function is called
|
|
asynchronously in its own thread, which terminates when the function exits.
|
|
<H5>
|
|
<U>SchedObsPtr and SchedObsDataPtr</U></H5>
|
|
<TT>typedef void *SchedObsDataPtr;</TT>
|
|
<BR><TT>typedef void (PR_CALLBACK *SchedObsPtr)(SchedulerPtr pScheduler,
|
|
PRUint32 observerID,</TT>
|
|
<BR><TT>
|
|
SchedObsDataPtr pData, SchedObsType type,</TT>
|
|
<BR><TT>
|
|
PRUint32 eventID, const char *eventName,</TT>
|
|
<BR><TT>
|
|
SchedFuncDataPtr pEventData);</TT>
|
|
|
|
<P>Observer callbacks are specified using the prototype given as <TT>SchedObsPtr</TT>.
|
|
When an event is added or removed from the scheduler, or when an event
|
|
fires, each observer who has registered with that instance of the scheduler
|
|
receives a notification message. The function returns nothing and
|
|
receives information on the event that triggered the notification (its
|
|
id, name, and event data), what kind of operation (add, remove, fire.)
|
|
occurred, and the id and data associated with the specific observer.
|
|
Both data pointers are opaque to the scheduler. The event data passed
|
|
in (<TT>pEventData</TT>) is the actual data pointer for the event, not
|
|
a copy, so observers must be aware that changing the information referred
|
|
to in <TT>pEventData</TT> may have unexpected results. Additionally,
|
|
because there is no type information associated with <TT>pEventData</TT>,
|
|
observers must take particular care when making assumptions about the structure
|
|
or usage of <TT>pEventData</TT>.
|
|
<H3>
|
|
Scheduler Operation</H3>
|
|
<TT>SchedulerPtr SchedulerStart(void)</TT>
|
|
|
|
<P>Creates and initializes an instance of the scheduler. <TT>SchedulerStart</TT>
|
|
creates a new (local) NSPR thread in which to run the instance of the scheduler
|
|
and returns a reference to a newly created scheduler object. This
|
|
object is required for all calls to the scheduler API. The newly
|
|
created scheduler object is immediately ready to register and dispatch
|
|
events. <TT>SchedulerStart</TT> allocates only enough memory for
|
|
the scheduler object itself; any supplementary data structures including
|
|
the event and observer queues are allocated when they are first referenced
|
|
by <TT>EventAdd</TT> or <TT>ObserverAdd</TT>. If the thread can not
|
|
be created or memory can not be allocated, <TT>SchedulerStart</TT> returns
|
|
<TT>NULL</TT>. In order to properly cleanup, <TT>SchedulerStop()</TT>
|
|
should be called when the scheduler is to cease operation.
|
|
|
|
<P><TT>SchedulerErr SchedulerStop(SchedulerPtr pScheduler);</TT>
|
|
|
|
<P><TT>SchedulerStop</TT> halts scheduling of the specified scheduler instance,
|
|
deletes any memory referenced by the scheduler's event and observer queues,
|
|
and frees the instance of the Scheduler. After calling <TT>SchedulerStop</TT>,
|
|
the instance of the scheduler referenced by the specified <TT>SchedulerPtr</TT>
|
|
is invalid and must not be used. <TT>SchedulerStop</TT> can not fail
|
|
and will always return a successful result code.
|
|
|
|
<P><TT>SchedulerStop</TT> will instruct the scheduler's thread to halt,
|
|
but due to thread synchronization issues, the target thread may take some
|
|
time to get around to completing. Halting the scheduler does not
|
|
have any impact on any threads running as a result of scheduled events;
|
|
once the scheduler fires an event, it has no further control over that
|
|
event.
|
|
|
|
<P><TT>SchedulerErr SchedulerPause(SchedulerPtr pScheduler);</TT>
|
|
|
|
<P><TT>SchedulerPause</TT> causes the scheduler to cease firing events.
|
|
Pausing the scheduler does not affect the the addition of new events to
|
|
the scheduling queue, nor does it prevent management of the observer list.
|
|
While the scheduler is paused, any events scheduled to fire will be held
|
|
in the queue until the scheduler's operation is resumed with <TT>SchedulerResume</TT>.
|
|
Calling <TT>SchedulerPause</TT> multiple times will have no effect beyond
|
|
the first; <TT>SchedulerPause</TT> can not fail and will always return
|
|
a successful result code.
|
|
|
|
<P><TT>SchedulerErr SchedulerResume(SchedulerPtr pScheduler);</TT>
|
|
|
|
<P><TT>SchedulerResume</TT> reverses the effect of a previous <TT>SchedulerPause</TT>
|
|
call. Events that did not fire while the scheduler queue was paused
|
|
will immediately fire, unless their scheduled end time (expiration) has
|
|
passed, in which case those items are removed from the queue. Items
|
|
removed from the queue in this way cause an event deletion notification
|
|
to be sent to the scheduler's observers. All other conditions, including
|
|
sending <TT>SchedulerResume</TT> to a scheduler which has not been paused
|
|
will always return a successful result code.
|
|
|
|
<P>Pausing and resuming the scheduler is not reference counted; if multiple
|
|
threads have paused the scheduler, the first to resume it will cause the
|
|
scheduler to start actively processing events.
|
|
|
|
<P><TT>SchedulerErr SchedulerFreeze(SchedulerPtr pScheduler);</TT>
|
|
|
|
<P><TT>SchedulerFreeze</TT> causes the scheduler to both stop processing
|
|
events and to reject the addition of new items to the queue. This
|
|
function is typically only called internally, since clients do not have
|
|
any way to unfreeze the scheduler. The scheduler calls this routine
|
|
while it is executing the <TT>FireAll</TT> function in order to make sure
|
|
that the scheduling queue stays in a consistent state while it is processing
|
|
all of the events.
|
|
|
|
<P><TT>SchedulerFreeze</TT> will not fail and will always return a successful
|
|
result code.
|
|
|
|
<P><TT>SchedulerErr SchedulerFireAll(SchedulerPtr)</TT>
|
|
<B><I>This function is not yet implemented</I></B>
|
|
|
|
<P><TT>SchedulerFireAll</TT> instructs the scheduler to close the queue
|
|
to new requests and sequentially fire all events in the current event queue
|
|
without regard to their scheduled time. Notification events will
|
|
be sent to observers for each fired event.
|
|
|
|
<P>In order to prevent new events from being added to the queue as all
|
|
existing events are being fired, and therefore creating a potential infinite
|
|
loop, the <TT>SchedulerFireAll</TT> function freezes the existing event
|
|
queue prior to beginning operation. It will then create a new, empty
|
|
event queue for the given scheduler instance. The event queue is
|
|
ready to receive new events, but it is paused, preventing execution of
|
|
any events from ocurring while the <TT>FireAll</TT> request is processed.
|
|
The scheduler then fires each event in the previous queue in sequence;
|
|
when it has fired the last event, it will destroy the old event queue and
|
|
resume the new one it has created. Normal operation of the scheduler
|
|
can then continue.
|
|
|
|
<P>(insert diagram here)
|
|
<H3>
|
|
Event and Observer List management</H3>
|
|
<TT>SchedulerErr SchedulerAddEvent(SchedulerPtr pScheduler, PRUint32 *pEventID,
|
|
const char *szName,</TT>
|
|
<BR><TT>
|
|
SchedulerTime *pTime, SchedFuncPtr function, SchedFuncDataPtr pData);</TT>
|
|
|
|
<P><TT>SchedulerAddEvent</TT> adds an event to the scheduler's event queue.
|
|
An event is defined as the combination of a function and its data, scheduled
|
|
to fire at a particular time (or set of times). Accordingly, a client
|
|
which wishes to add an event needs to specify a time at which the event
|
|
should fire, a function to call when the time occurs, and any amount of
|
|
data which will be passed to the specified function at the time that the
|
|
event is fired. A client may also supply a user-visible name with
|
|
which to identify the event. Note that this
|
|
name is not used by the scheduler at all and may be <TT>NULL</TT> if no
|
|
user-visible name is desired. Both the time and name are copied into
|
|
the scheduler's internal structures, so the memory referenced by these
|
|
events may be released after making this call.
|
|
|
|
<P>If successful, SchedulerAddEvent adds the event to the event queue,
|
|
assigns a unique <TT>eventID</TT> which can be used to later refer to this
|
|
specific event, and returns a successful result code. If <TT>NULL</TT>
|
|
is passed in as the reference to the <TT>eventID</TT>, an event ID is still
|
|
generated, but the client will be unable to change or delete the event
|
|
in the future. Note that <TT>eventID</TT>s are unique only to a particular
|
|
scheduler instance; they are not guaranteed to be globally unique across
|
|
scheduler instances.
|
|
|
|
<P><TT>SchedulerAddEvent</TT> can fail for a number of reasons. The
|
|
most common is lack of memory. If the event queue needs to be created
|
|
or expanded in size to accomodate the new event, the scheduler will fail,
|
|
returning <TT>SCHED_ERR_OUT_OF_MEMORY</TT>, and will return 0 as the <TT>eventID</TT>.
|
|
Event addition will also fail if the time specified is invalid, is outside
|
|
the bounds specified by the event's <TT>start</TT> and <TT>end</TT> times,
|
|
or if the given range is negative (<TT>SCHED_ERR_BAD_TIME</TT>); or if
|
|
a <TT>NULL</TT> value is given for the function (<TT>SCHED_ERR_BAD_PARAMETER</TT>).
|
|
|
|
<P>Upon successful addition of the event, the scheduler will send notification
|
|
messages to its observers indicating that an event was added to the queue.
|
|
Included in the notification will be a reference to the scheduler and the
|
|
<TT>eventID</TT>, from which the observers can determine additional information
|
|
about the event. Because notification messages are synchronous, the
|
|
observers should not perform any lengthy operation when notified, as that
|
|
will impact the performance of event addition. Internally, the scheduler
|
|
adds the new event into its event queue, sorted by when the event is scheduled
|
|
to fire.
|
|
|
|
<P><TT>SchedulerErr SchedulerRemoveEvent(SchedulerPtr pScheduler, PRUint32
|
|
eventID);</TT>
|
|
|
|
<P><TT>SchedulerRemoveEvent</TT> removes a previously added event from
|
|
the scheduler's event queue. The event to remove is specified by
|
|
its <TT>eventID</TT>, previously assigned by <TT>SchedulerAddEvent</TT>.
|
|
If the specified event ID is invalid, the scheduler will return an error,
|
|
<TT>SCHED_ERR_BAD_EVENT</TT>, and no operation will be performed.
|
|
|
|
<P>Upon successful removal of an event, the scheduler will send notification
|
|
messages to its observers indicating that an event was removed. Including
|
|
in the notification will be the <TT>eventID</TT>, from which the observers
|
|
can determine additional information about the event. Because notification
|
|
messages are synchronous, the observers should not perform any lengthy
|
|
operation when notified, as that will impact the performance of event removal.
|
|
After all observers have been notified, the memory associated with the
|
|
event will be destroyed and the <TT>eventID</TT> will no longer be valid.
|
|
|
|
<P><TT>SchedulerErr SchedulerFindEvent(SchedulerPtr, uint eventID, &time,
|
|
&name, &function, &data</TT>)
|
|
<BR> <B><I>This function is not yet implemented</I></B>
|
|
|
|
<P><TT>SchedulerErr SchedulerAddObserver(SchedulerPtr pScheduler, PRUint32
|
|
*pObserverID, SchedObsType type,</TT>
|
|
<BR><TT>
|
|
SchedObsPtr function, SchedObsDataPtr pData);</TT>
|
|
|
|
<P><TT>SchedulerAddObserver</TT> adds an observer to the specified scheduler's
|
|
observer list. An observer is a function which is called when the
|
|
scheduler adds, removes, or fires an event. Which of these actions
|
|
trigger a notification for a given observer is specified in the observer's
|
|
type, which is a bitfield made up of the union of the following values:
|
|
|
|
<P><TT>#define SCHED_OBSERVE_ADD 1</TT>
|
|
<BR><TT>#define SCHED_OBSERVE_FIRE 2</TT>
|
|
<BR><TT>#define SCHED_OBSERVE_REMOVE 4</TT>
|
|
|
|
<P>When an observer is notified that something has happened, the observer's
|
|
function is called with the id and name of the event that fired, along
|
|
with the event's data pointer and the observer's id and the data pointer
|
|
specified in the AddObserver call. Note that the event data that
|
|
is passed in is not a copy of the event's data, but a pointer to it, so
|
|
altering the data will cause the data to be altered for the next iteration
|
|
of the event, as well as for subsequent observers which are notified.
|
|
However, since observers will receive notification for all events on an
|
|
event queue, observers must take care that they actually know the datatype
|
|
of the information passed in the event's data fields. In no case
|
|
should the event data be freed.
|
|
|
|
<P>Observer lists are not terribly efficient; the mechanism used to determine
|
|
which observers receive a given notification is done by linearly searching
|
|
the list of observers, so for performance reasons, callers should not specify
|
|
a large number of observers for any given scheduler instance. Indeed,
|
|
the scheduler was designed with only one specific observer in mind -- the
|
|
code which handles the display of status information within the client
|
|
user interface.
|
|
|
|
<P>Like the corresponding <TT>AddEvent</TT> function, above, adding an
|
|
observer causes the scheduler to generate a unique <TT>observerID</TT>,
|
|
which can later be used to remove the observer. Also like <TT>AddEvent</TT>,
|
|
the <TT>observerID</TT> is unique only within a scheduler instance.
|
|
<TT>ObserverIDs</TT> and <TT>EventID</TT>s are not interchangeable; their
|
|
namespace may overlap. Therefore, it's important that the caller
|
|
keep track of which identifiers represent events and which represent observers.
|
|
If NULL is passed in for the observerID, an observer ID is generated, but
|
|
it is not returned to the caller.
|
|
|
|
<P><TT>SchedulerAddObserver</TT> can fail if memory can not be allocated
|
|
to store the observer or create the observer queue, or if the function
|
|
specified is <TT>NULL</TT>. In each failure case, the <TT>observerID</TT>
|
|
returned is undefined.
|
|
|
|
<P><TT>SchedulerErr SchedulerRemoveObserver(SchedulerPtr pScheduler, PRUint32
|
|
observerID)</TT>
|
|
|
|
<P><TT>SchedulerRemoveObserver</TT> removes an observer from the scheduer's
|
|
observer list. That observer no longer receives notification messages
|
|
and the scheduler frees any memory it has allocated to track the specified
|
|
observer. If no observer with the given ID exists, the function returns
|
|
<TT>SCHED_ERR_BAD_PARAMETER</TT>, and no operation is performed.
|
|
<BR>
|
|
</BODY>
|
|
</HTML>
|