Catching interrupt events from your application's service code was an error-prone, manual and inefficient task in previous versions of Symphony (3.2 and earlier).
For example, here is a C# Symphony service written for Symphony 3.2. It tries to catch and handle KILL events caused by killing a session. The sample code below first makes a temporary directory in
onSessionEnter and the temporary directory is deleted at
onSessionLeave.
When the session gets killed, the service attempts to delete the directory at onInvoke by catching the KILL event using the InterruptEvent object.
Code:
public override void OnInvoke(TaskContext taskContext)
{
try
{
do
{
InterruptEvent iEvent = serviceContext.LastInterruptEvent;
if (iEvent.EventCode != InterruptEventCode.None)
{
// Terminate Decendant Processes and Remove Working Dir
try
{
// Remove Working Dir here
}
catch {}
if (iEvent.EventCode == InterruptEventCode.SessionSuspended)
{
// Some actions for session suspend
}
else
{
// Some actions for session kill
}
}
}
while (!se.WaitForExit(WAIT_PERIOD)); // Just rotate "while" several times
}
catch (StandardExecutorException e)
{
// Reaction for exception
}
catch (SoamException e)
{
// Reaction for exception
}
catch (Exception e)
{
// Reaction for exception
}
}
Failure to match up the WAIT_PERIOD above and the
taskCleanupPeriod in the <Session> section of your application profile may cause the service to miss the session KILL event.
In previous versions of Symphony, you had to go through the hassle of manually setting
taskCleanupPeriod to be always greater than the
WAIT_PERIOD. Also, this way of polling for an event is not efficient.
For usability and efficiency reasons, Symphony 4.0 introduced
onServiceInterrupt(), an event-based callback method. Inside the implementation of onServiceInterrupt, you can retrieve the last caught event through the method
getLastInterruptEvent() in the ServiceContext class as shown in the sample service code below. The method will return the
gracePeriod set as the
taskCleanupPeriod in the application profile and you don't have to worry about the service prematurely exiting without doing the necessary operations.
Code:
public override void OnServiceInterrupt(ServiceContext serviceContext)
{
//@@onInterrupt_GLOBAL@@
const string fname="onServiceInterrupt()";
m_serviceOutput.WriteLine(fname + ": enter");
// print interrupt information
InterruptEvent eve = serviceContext.LastInterruptEvent;
string eventString;
switch(eve.EventCode)
{
case InterruptEventCode.TaskKilled:
eventString = "TaskKilled";
break;
case InterruptEventCode.TaskSuspended:
eventString = "TaskSuspended";
break;
default:
eventString = "Unkown";
break;
}
m_serviceOutput.WriteLine("InterruptEvent = " + eventString + ", gracePeriod = " + eve.GracePeriod + "ms");
closeDB(fname);
m_serviceOutput.WriteLine(fname + ": exit");
m_serviceOutput.WriteLine();
m_serviceOutput.Flush();
// close the stream
m_serviceOutput.Close();
}
// simulate connection to DB
private void connectDB()
{
m_serviceOutput.WriteLine("connected to DB.");
m_serviceOutput.Flush();
}
// simulate some processing in DB with the given arguments
private void processDB(int iter, int sleep)
{
//no-op
}
// simulate closing connection DB
private void closeDB(string fname)
{
m_serviceOutput.WriteLine("CONNECTION TO DB CLOSED IN " + fname);
m_serviceOutput.Flush();
}
// simulate some intensive operation is happening.
private void intensiveOp(int iter, int sleepTime)
{
// supposedly intensive operation
m_result += iter + sleepTime;
Thread.Sleep(sleepTime * 1000);
}
}
You can find the complete sample code for
onServiceInterrupt under %SOAM_HOME%/4.0/samples.