Concurrency bug in constructor Aspose.PDF TextBuilder

Hello,

I want to report a concurrency bug in the implementation of the Aspose.PDF TextBuilder constructor.

When creating multiple PDFs concurrently, I consistently get the following exception:

System.InvalidOperationException : Collection was modified; enumeration operation may not execute.
   at System.Collections.Generic.List`1.Enumerator.MoveNext()
   at #=zbHeZOLM1OQNgzUNlI_q4Ke12e8QteaoFyn3BvKmQD0hB.#=z9OtCFHqJX8ea(String #=zzHsDNvI=, #=zkf94LRBeZGSoXnzPzDUhXbq1ATl2tDuJD9bg7lwHkHvi #=zUw0fpKg=)
   at #=zVQJX7Axdvkip71gRUNFzzZiL6dZcbEeLorR0VdiX_6irTncHdEAHJV8=.#=zX_u5AYw=(Operator #=zFKlvV0g=)
   at #=zVQJX7Axdvkip71gRUNFzzZiL6dZcbEeLorR0VdiX_6irTncHdEAHJV8=.#=zRvqrxn8=(Page #=zgaTw7fg=)
   at #=zzyZl0QJQd9iWYoXDF__cRl6q1MAwNAGtyYrjvBp2HBVT1H8ZmW28doaBBP$m.#=z$w1xkNFPbJ11(BaseOperatorCollection #=zdwaZQWc=, Resources #=zeayQa1c=, Page #=zgaTw7fg=, Rectangle #=ztXDxlObYdng7)
   at #=zzyZl0QJQd9iWYoXDF__cRl6q1MAwNAGtyYrjvBp2HBVT1H8ZmW28doaBBP$m.#=z$w1xkNFPbJ11(BaseOperatorCollection #=zdwaZQWc=, Resources #=zeayQa1c=, Rectangle #=ztXDxlObYdng7)
   at #=zzyZl0QJQd9iWYoXDF__cRl6q1MAwNAGtyYrjvBp2HBVT1H8ZmW28doaBBP$m.#=z7yH$xHo=()
   at #=zzyZl0QJQd9iWYoXDF__cRl6q1MAwNAGtyYrjvBp2HBVT1H8ZmW28doaBBP$m..ctor(Page #=zgaTw7fg=, TextSearchOptions #=ztDfyF35iMc8d, Resources #=zeayQa1c=, BaseOperatorCollection #=znisUKS8=, Boolean #=zw_OciCLxWgPy)
   at #=zzyZl0QJQd9iWYoXDF__cRl6q1MAwNAGtyYrjvBp2HBVT1H8ZmW28doaBBP$m..ctor(Page #=zgaTw7fg=, TextSearchOptions #=ztDfyF35iMc8d, Resources #=zeayQa1c=, BaseOperatorCollection #=znisUKS8=)
   at Aspose.Pdf.Text.TextBuilder..ctor(Page page)

Running a single PDF generation does not show the problem.

The bug was introduced in Aspose.PDF version 23.9.0. The bug does not manifest itself in version 23.8.0. The bug does manifest itself in the most recent version 24.9.0.
I use a licensed version of Aspose.Total in .NET.

Hope this can be fixed soon.

Kind regards,
Ruben Vandeginste

@ruben_vandeginste
Please provide the code you are using.

Hi, I cannot provide all the code that I am using, as it is a big project, and the bug manifests itself when I run all unit tests on the project. It is very difficult to reproduce this using a small test.

Like I said, the bug was introduced in Aspose.PDF version 23.9.0, I tested this using my project.

The code essentially does the following (straightforward pdf manipulation):

using Document pdfDocumentTemplate = DocumentTemplateManager.GetBaseDocument();
using Document pdfDocument = new Document();
Page page = pdfDocument.Pages.Add(pdfDocumentTemplate.Pages[1]);
TextFragment fragment1 =
    new TextFragment("text"){
          Position = new Position(x,y),
          TextState = {
              Font = FontRepository.FindFont("Times Roman"),
              FontSize = 9,
              ForegroundColor = Color.Black,
              BackgroundColor = Color.White
          };
TextFragment fragment2 =
    new TextFragment("other text"){
          Position = new Position(x,y),
          TextState = {
              Font = FontRepository.FindFont("Times Roman"),
              FontSize = 9,
              ForegroundColor = Color.Black,
              BackgroundColor = Color.White
          };
// bug is triggered on the following line when the TextBuilder is called concurrently
TextBuilder textBuilder = new TextBuilder(page);
textBuilder.AppendText(fragment1);
textBuilder.AppendText(fragment2);

When I go through the stacktrace of the exception, I end up on the following code that is not thread-safe. An enumerator is created for a static dictionary, but without any locking code. When the same code is called in another thread the static dictionary is potentially updated in the else-branch and this causes an issue for the code using the enumerator.

internal sealed class \u0023\u003DzwVUF1SnJNYERk6cAFhKVDkawRRodt7stvD89RY1u8DpX
{
  private static Dictionary<string, List<\u0023\u003Dz6ZzVrpwudsgSDrhUM0WOgHLaLdy8wExZ8NKtfLAWBXdu>> \u0023\u003DzKGymOytjz_hK;
...

  public static void \u0023\u003DzpK5Tu6Uap9dw(
    string _param0,
    \u0023\u003Dz6ZzVrpwudsgSDrhUM0WOgHLaLdy8wExZ8NKtfLAWBXdu _param1)
  {
    if (\u0023\u003DzKGymOytjz_hK.ContainsKey(_param0))
    {
      Dictionary<string, List<\u0023\u003Dz6ZzVrpwudsgSDrhUM0WOgHLaLdy8wExZ8NKtfLAWBXdu>>.Enumerator enumerator1 = \u0023\u003DzKGymOytjz_hK.GetEnumerator();
      ...
    }
    else
      \u0023\u003DzKGymOytjz_hK.Add(_param0, new List<\u0023\u003Dz6ZzVrpwudsgSDrhUM0WOgHLaLdy8wExZ8NKtfLAWBXdu>()
      {
        _param1
      });
  }
...

Kind regards,
Ruben Vandeginste

@ruben_vandeginste
Thank you for the information provided.
I will try to reproduce the problem - I will write to you tomorrow.

I would like to note that the status of having a license not displayed (you probably registered with a different email address) and therefore I will not be able to set the task status from a user who has a license.

Just to give you some more context: I see this behavior when running a test suite that contains ~3000 unit tests, which are run with up to 20 concurrent threads. I don’t know how many of those tests use the Aspose.PDF TextBuilder constructor. With each run of that test suite, 1 to 4 tests fail, with one of the following errors (in the call to the TextBuilder constructor):

  • when the static method adds something in the static dictionary: “An item with the same key has already been added. Key: F1”

  • when the code is using an enumerator on the static dictionary: “Collection was modified; enumeration operation may not execute.”

Ruben

@ruben_vandeginste
Thanks for the additional information.

@ruben_vandeginste
Unfortunately, I have not yet been able to reproduce the error.
However, I found the place in the code that you pointed out and yes, it is not thread-safe.
I will discuss it with the development team tomorrow - perhaps it will be possible to make changes for this dictionary without reproducing the error.

@ruben_vandeginste
We have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

Issue ID(s): PDFNET-58227

You can obtain Paid Support Services if you need support on a priority basis, along with the direct access to our Paid Support management team.

@ruben_vandeginste
The mentioned dictionary has been replaced with a thread-safe one. The change will be in the library version 24.10, which will be released next week or the following week. I am not closing the task yet - if the problem remains, please write.
Thank you for not just identifying the problem, but finding it and showing where it is.

Great to hear that, thanks!