Table of Contents
This chapter contains:
An action is a handle to an operation initiated by a plugin that
has not yet completed. For example: when a plugin connects to a remote
server, it uses the call INKNetConnect
which takes
an INKCont
as an argument to call back when the
connection is established. INKNetConnect
might not
call the continuation back immediately and will return an
INKAction
structure that the caller can use to
cancel the operation. Cancelling the operation does not necessarily mean
that the operation will not occur; it means that the continuation passed in
to the operation will not be called back. In the above example, the
connection might still occur if the action is cancelled, but the
continuation that initiated the connection would not be called back when
that occurred.
In the preceding example, it is possible that the connection will
complete and callback the continuation before
INKNetConnect
returns. If this occurs, then
INKNetConnect
returns a special action that
causes INKActionDone
to return 1. Basically,
this specifies that the operation has already completed; there is no
point in trying to cancel the operation. Note that an action will never
change from non-completed to completed. When the operation actually
succeeds and the continuation is called back, the
continuation must zero out its action pointer to indicate to itself that
the operation succeeded.
The asynchronous nature of all operations in Traffic Server
necessitates actions. You should notice from the above discussion that
once a call to a function like INKNetConnect
is
made by a continuation and that function returns a valid action
(INKActionDone
returns 0), it is not safe for
the continuation to do anything else except return from its handler
function. It is not safe to modify or examine the continuation’s data
since the continuation may have already been destroyed.
Below is an example of typical usage for an action:
#include “InkAPI.h” static int handler (INKCont contp, INKEvent event, void *edata) { if (event == INK_EVENT_IMMEDIATE) { INKAction actionp = INKNetConnect (contp, 127.0.0.1, 9999); if (!INKActionDone (actionp)) { INKContDataSet (contp, actionp); } else { /* we've already been called back... */ return 0; } } else if (event == INK_EVENT_NET_CONNECT) { /* net connection succeeded */ INKContDataSet (contp, NULL); return 0; } else if (event == INK_EVENT_NET_CONNECT_FAILED) { /* net connection failed */ INKContDataSet (contp, NULL); return 0; } return 0; } void INKPluginInit (int argc, const char *argv[]) { INKCont contp; contp = INKContCreate (handler, INKMutexCreate ()); /* We don't want to call things out of INKPluginInit directly since it is called before the rest of the system is initialized. We'll simply schedule an event on the continuation to occur as soon as the rest of the system is started up. */ INKContSchedule (contp, 0); }
The example above shows a simple plugin that creates a
continuation and schedules it to be called immediately. When the
plugin’s handler function is called the first time, the event will be
INK_EVENT_IMMEDIATE
. The plugin then tries to open a net
connection to port 9999 on localhost
(127.0.0.1). The IP
description was left cider notation to make it clearer what is going on; also note that the above won’t actually compile until the IP address is
modified. The action returned from INKNetConnect
is
examined by the plugin. If the operation has not completed, then the plugin
stores the action in its continuation. Otherwise, the plugin knows it has
already been called back and there is no reason to store the action
pointer.
A final question might be why would a plugin want to cancel an action. In the above example, a valid reason could be to place a limit on the length of time it takes to open a connection. The plugin could schedule itself to get called back in 30 seconds and then initiate the net connection. If the time-out expires first, then the plugin would cancel the action. The following sample code implements this:
#include “InkAPI.h” static int handler (INKCont contp, INKEvent event, void *edata) { switch (event) { case (INK_EVENT_IMMEDIATE): INKContSchedule (contp, 30000); INKAction actionp = INKNetConnect(contp, 127.0.0.1, 9999); if (!INKActionDone (actionp)) { INKContDataSet (contp, actionp); } else { /* we’ve already been called back ... */ } break; case (INK_EVENT_TIMEOUT): INKAction actionp = INKContDataGet (contp); if (!INKActionDone(actionp)) { INKActionCancel (actionp); } break; case (INK_EVENT_NET_CONNECT): /* net connection succeeded */ INKContDataSet (contp, NULL); break; case (INK_EVENT_NET_CONNECT_FAILED): /* net connection failed */ INKContDataSet (contp, NULL); break; } return 0; } void INKPluginInit (int argc, const char *argv[]) { INKCont contp; contp = INKContCreate (handler, INKMutexCreate ()); /* We don't want to call things out of INKPluginInit directly since it is called before the rest of the system is initialized. We'll simply schedule an event on the continuation to occur as soon as the rest of the system is started up. */ INKContSchedule (contp, 0); }
The action functions are: