"The ThreadLocal object has been disposed" exception on shutdown

In my plug-in, I get the following exception on Rhino shutdown. Has anybody seen something similar or can give me a pointer as to what kind of operations could lead to this behavior? I’m not yet really able to track down the culprit, but will update the post if I can narrow it down to a few calls that are involved in causing it.

System.Transactions Critical: 0 : <TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Critical"><TraceIdentifier>http://msdn.microsoft.com/TraceCodes/System/ActivityTracing/2004/07/Reliability/Exception/Unhandled</TraceIdentifier><Description>Unhandled exception</Description><AppDomain>DefaultDomain</AppDomain><Exception><ExceptionType>System.ObjectDisposedException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>Cannot access a disposed object.
Object name: 'The ThreadLocal object has been disposed.'.</Message><StackTrace>   at System.Threading.ThreadLocal`1.GetValueSlow()
   at System.Collections.Concurrent.ConcurrentBag`1.GetThreadList(Boolean forceCreate)
   at Rhino.DocObjects.ObjRef.Finalize()</StackTrace><ExceptionString>System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'The ThreadLocal object has been disposed.'.
   at System.Threading.ThreadLocal`1.GetValueSlow()
   at System.Collections.Concurrent.ConcurrentBag`1.GetThreadList(Boolean forceCreate)
   at Rhino.DocObjects.ObjRef.Finalize()</ExceptionString></Exception></TraceRecord>

Are you clearing out your ConcurrentBag in the OnShutdown function of your plugin?

It’s not my ConcurrentBag that is being cleared out here. I assume, it is the ConcurrentBag that is being cleared in Rhino’s HostUtils.DeleteObjectsOnMainThread.

It looks like something related to the bug described here: c# - Finalization of ConcurrentBag containing unmanaged objects - Stack Overflow

The question is if I can do something from my side to prevent this error.

Sorry, I didn’t word properly. I meant to ask: did you make sure you clear out your concurrent bag in the OnShutdown? You might want to do that explicitly.

I use a couple of ConcurrentDictionary and a couple of ConcurrentQueue in RhinoCycles. I make sure I clear them out manually during the plug-in OnShutdown.

Clearing of log queues

Called from RcCore.It.Shutdown()

Called from RhinoCycles plug-in OnShutdown

Maybe I do misunderstand the issue here, but my assumption is the following:

There is some ObjRef whose finalizer is being called. In this finalizer, Rhino adds the object to a ConcurrentBag (HostUtils.AddObjectsToDeleteOnMainThread) to be cleared out. Then, when clearing out the ConcurrentBag in HostUtils.DeleteObjectsOnMainThread, Rhino iterates through those objects in the following way:

IDisposable result;
while (g_objectsToDisposeOnMainThread.TryTake(out result))
{
    result?.Dispose();
}

In the StackOverflow page linked above, there is an explanation how this can lead to an ObjectDisposedException.

Ah right, I see now what you mean.

This is something added by @stevebaer . To be able to debug this properly are you able to recreate the crash with a minimal example plug-in of which you can share the code here?

I haven’t been able to extract a minimal example, but eventually I found a way to fix the issue by disposing manually of the object references gathered during the command that causes it.

This is how it looks like more or less:

protected override Result RunCommand(RhinoDoc doc, RunMode mode)
{
  var getObject = new GetObject
  {
      GeometryFilter = ObjectType.Mesh,
      GroupSelect = true,
      SubObjectSelect = false,
      DeselectAllBeforePostSelect = false
  };
  getObject.AcceptNothing(true);
  getObject.EnableClearObjectsOnEntry(false);
  getObject.EnableUnselectObjectsOnExit(false);

  while (true)
  {
    var getResult = getObject.GetMultiple(1, 0);
    if (getObject.CommandResult() != Result.Success)
    {
        return getObject.CommandResult();
    }
    
    if (getObject.ObjectsWerePreselected)
    {
        getObject.EnablePreSelect(false, false);
        continue;
    }

    if (getResult == GetResult.Object)
    {
        break;
    }
  }

  var objectReferences = getObject.Objects();
  var meshObjects = objectReferences.Select(o => o.Object()).OfType<MeshObject>().ToList();

  // Something inside this method or its sub methods causes the issue
  DoAShitTonOfStuff(meshObjects);

  // Those lines fix it
  foreach (var objectReference in objRefs)
  {
      objectReference.Dispose();
  }

  return Result.Success;
}
1 Like