Monitoring progress or aborting (ITransform)
Introduction
In response to a customer request, Mako 4.7.3 and later implements a mechanism to signal an abort to a transform process (using ITransform
), and another to enable its progress to be monitored.
Aborting an ITransform
An IAbortPtr
object has been added to CTransformState
. This structure is passed to the various handlers in ITransform
and ICustomTransform::IImplementation
. The handlers can call CHECKABORT()
to check if an abort has been signaled. This throws an exception if an abort was requested. Numerous calls to the CHECKABORT()
macro in the default handlers in CCustomTransform
, CCustomTransform::CGenericImplementation
and CTransformCommon
for the convenience of the implementors. If a client creates their own handler then they should call the CHECKABORT()
macro.
Also added to the work item API is ITPWorkItem::getHasAborted() and ITPWorkItem::getHasErrored() so that the caller that submitted the work item can distinguish between them. If an abort is encountered, it can rethrow that exception, so the abort signal is not lost.
Code Snippets
First, we can create an IAbort instance:
CTransformState state;
state.abort = IAbort::create();
And use it to signal an abort:
state.abort->signalAbort();
This is where these elements are implemented in the code example provided.
// Transform on a thread
CTransformState state;
state.abort = IAbort::create();
CTransformThread transformThreadInfo(jawsMako, content, state);
The last line passes the state into the thread implementation and is subsequently passed to the transform in the thread runner class:
IDOMNodePtr transformed = transform->transform(m_content, changed, true, m_state);
The example runs the transform in its own thread so that the main thread can monitor the keyboard, waiting for the user to press ESC. Should they do so, an abort is signaled:
// Wait for the worker thread to complete
while (!transformThreadInfo.aborted() && !transformThreadInfo.completed()) {
if (_kbhit())
{
if (_getch() == 27) // ESC
state.abort->signalAbort();
}
}
Obtaining progress information from an ITransform
A new class, IProgressTick
, allows a call back method to monitor the progress of a task. The callback can pass an integer (from 0 to maximum value) or a float (0.0 to 1.0) which represents the progress of a task. The task needs to call the tick() method to indicate progress. A value can be passed to the IProgressTick::create()
method to specify an (estimated) maximum value for the integer progress count.
To implement progress monitoring of an ITransform
, the following steps are needed:
Create a transform (in this example, using a custom transformation)
Create an
IProgressTick
instanceCreate a monitor to record progress
Set the callback method to be called by the
IProgressTick
Set the
IProgressTick
to be called by the transform.
In the example, these steps are coded as follows:
First we create a transform:
CShadingPatternTransformImplementation implementation(m_jawsMako);
ITransformPtr transform = ICustomTransform::create(m_jawsMako, &implementation);
Next we create an IProgressTick
:
uint32 numNodes = countChildren(m_content) + 1; /* num of subnodes + this one */
IProgressTickPtr progressTick = IProgressTick::create(numNodes);
If you don't really need a callback for each node then, you can set an optional parameter (after the maximum value) that indicates the frequency in which the callback is invoked. For example we can set the frequency to 5 which means the callback is only invoked every 5 ticks. This can help minimize the effect of the callback on performance.
TickMonitorInt* mon = new TickMonitorInt();
This is the tick monitor class implementation:
class TickMonitorInt
{
public:
TickMonitorInt()
{
last_val = 0;
}
std::vector<uint32> ticks;
uint32 last_val;
};
Next, we set the callback method to be called by IProgressTick
:
progressTick->setCallback(progressCallbackInt, mon);
This is the progress tick callback implementation, which includes an updating the user on progress:
static void progressCallbackInt(void* priv, uint32 count, uint32 maxVal)
{
TickMonitorInt* mon = (TickMonitorInt*)priv;
mon->ticks.push_back(count);
mon->last_val = count;
globalMtx.lock();
std::wcout << L"Node " << count << L" of " << maxVal << L"\r";
if (count >= maxVal)
std::wcout << std::endl;
globalMtx.unlock();
}
The progress of the transformation is reported as a count from 1 to n, where n is the number of nodes on the page.
Finally:
transform->setProgressTick(progressTick);