Actions
In the preceding example, it is also possible that the connection will complete and call back the continuation before TSNetConnect
returns. If that occurs, then TSNetConnect
returns a special action that causes TSActionDone
to return 1
. This specifies that the operation has already completed, so it’s pointless to try to cancel the operation. Also 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 TSNetConnect
is made by a continuation and that function returns a valid action (TSActionDone
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 because the continuation may have already been destroyed.
The example above shows a simple plugin that creates a continuation and then schedules it to be called immediately. When the plugin’s handler function is called the first time, the event is TS_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 in cider notation to further clarify what is going on; also note that the above won’t actually compile until the IP address is modified. The action returned from TSNetConnect
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 timeout expires first, then the plugin would cancel the action. The following sample code implements this:
#include <ts/ts.h>
static int
handler (TSCont contp, TSEvent event, void *edata)
{
switch (event) {
case (TS_EVENT_IMMEDIATE):
TSContScheduleOnPool (contp, 30000, TS_THREAD_POOL_NET);
if (!TSActionDone (actionp)) {
TSContDataSet (contp, actionp);
} else {
/* We've already been called back ... */
}
break;
TSAction actionp = TSContDataGet (contp);
if (!TSActionDone(actionp)) {
TSActionCancel (actionp);
}
break;
case (TS_EVENT_NET_CONNECT):
/* Net connection succeeded */
TSContDataSet (contp, NULL);
break;
case (TS_EVENT_NET_CONNECT_FAILED):
TSContDataSet (contp, NULL);
break;
return 0;
}
void
TSPluginInit (int argc, const char *argv[])
{
TSCont contp;
contp = TSContCreate (handler, TSMutexCreate ());
/* We don't want to call things out of TSPluginInit
directly since it's 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. */
TSContScheduleOnPool (contp, 0, TS_THREAD_POOL_NET);
}