toddmo
toddmo

Reputation: 22436

How to put a timeout on Transform

The XslCompiledTransform.Transform will hang under certain conditions (stack overflow, infinite loop, etc). This is a data (input) dependent error, so I don't have complete control in preventing it. If this happens, I'd like to be notified gracefully, but I don't want it to destroy my application process and hence the GUI where the user is inputting the input, which may be "valid" but "incomplete".

If I run the xslt file manually, I get

Process is terminated due to StackOverflowException

But XslCompiledTransform.Transform() will hang my application forever.

So, I want to wrap that call in a timeout, but nothing I've tried seems to work. It still hangs the application.

I want the thread that has the try block to not be hung. I want to create two tasks, one for Transform and the other timeout. Then start both at the same time. I don't know but I think the Run is running before the outer statement gets a chance to wire up the timeout and use the WhenAny.

How can this be fixed?

Update

I updated the code to reflect my current attempt. I can get into the if block if it times out, but whether I abort the thread or not, the application still hangs. I don't understand what it is about XslCompiledTransform.Transform that insists on taking the whole application down if it goes down.

public static Object Load(string mathML)
{
  if (mathML == Notebooks.InputCell.EMPTY_MATH)
    return null;
  XmlDocument input = new XmlDocument();
  input.LoadXml(mathML);
  XmlDocument target = new XmlDocument(input.CreateNavigator().NameTable);
  using (XmlWriter writer = target.CreateNavigator().AppendChild())
  {
    try
    {
      Thread thread = null;
      var task = Task.Run(() => 
      {
        thread = Thread.CurrentThread;
        XmlTransform.Transform(input, writer);
      });
      if (!task.Wait(TimeSpan.FromSeconds(5)))
      {
        thread.Abort();
        throw new TimeoutException();
      }
    }
    catch (XsltException xex)
    {
      if (xex.Message == "An item of type 'Attribute' cannot be constructed within a node of type 'Root'.")
        return null;
      else
        throw;
    }
  }
  return Load(target);
}

Upvotes: 0

Views: 688

Answers (1)

toddmo
toddmo

Reputation: 22436

Here's how I solved the issue

I took my xsl and compiled it into an assembly and referenced that assembly from my project (which is called Library)

Advantages:

  • Fixed the hang
  • Compiled xslt into an assembly is supposedly much faster

Disadvantages:

  • You tell me! I don't know :)

Library Properties / Build Events / Pre-build Event

"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7 Tools\xsltc.exe" /settings:script+ /class:Transform "myStylesheet.xslt"

Library / References

+ myStylesheet.dll

Loading the compiled transform

private static XslCompiledTransform xslTransform;
private static XslCompiledTransform XslTransform
{
  get
  {
    if (xslTransform == null)
    {
      xslTransform = new XslCompiledTransform();
      xslTransform.Load(typeof(Transform));
    }
    return xslTransform;
  }
}

Calling the transform

Same as updated code in the Question

Upvotes: 2

Related Questions