Network surface multithreaded crash

I’m crashing my rhino when I throw in a Parallel.For() call in my c# script component, and I can’t figure out the cause.

What’s paralleled is the CreateNetworkSurface() method of Surface

Here’s my snippet for pattern. Abbreviated for legibility.
The code runs perfectly if I swap the parallel call with a foreach loop. With the parallel, it occasionally succeeds.

using System.Threading.Tasks;
using System.Linq;
// using more stuff

// omitted bunch of boilerplate codes

// my class object for parallel
protected class NWSrf{
    // omited some variables

    protected Curve[] crvs = null; // network curves
    internal NurbsSurface srf = null; // result surface

    public NWSrf(IEnumerable<Curve> c){
      crvs = new Curve[c.Count()];
      int i = 0;
      foreach (Curve crv in c)
        crvs.SetValue(crv.DuplicateCurve(), i++);
    }

    internal void MakeSrf(int ctn, double et, double it, double at){
      srf = NurbsSurface.CreateNetworkSurface(crvs, ctn, et, it, at, out ei);
    }
  }

// the standard runscript in a C# component of grasshopper
private void RunScript(DataTree<Curve> C, ref object A)
  {
    // some variables

    NWSrf[] nwsobjs = new NWSrf[C.BranchCount];
    NurbsSurface[] nurbs = new NurbsSurface[C.BranchCount];

    // make all the object for parallel
    for (int bi = 0; bi < C.BranchCount; bi++){
      nwsobjs.SetValue(new NWSrf(C.Branch(bi)), bi);
    }

    // compute in parallel
    Parallel.For(0, nwsobjs.Length, bi => {
      nwsobjs[bi].MakeSrf(2, 0.01, 0.01, 0.01);
      });

    // query the results of each objects
    for (int bi = 0; bi < nwsobjs.Length; bi++){
      nurbs.SetValue(nwsobjs[bi].srf, bi);
    }

    A = nurbs;
  }

Hello,

First, I think you shouldn’t refer to “newsobjs” in parallel actions

Parallel.For(0, nwsobjs.Length, bi => {
      nwsobjs/*This can be accessed by multiple actions at the same time*/[bi].MakeSrf(2, 0.01, 0.01, 0.01);
});

You may need to pass the object “nwsobjs [bi]” to action like with Task(..., nwsobjs [bi])
Another option is to use ThreadPool.QueueUserWorkItem and CountdownEvent

Apart from “nwsobjs”, I don’t see

jmv

You can also create a function to lock the current thread to guarantee synchronisation between multiple threads. That way only one thread is allowed to index your data. But this all depends if create network surface is a thread safe rhinocommon method.

I changed this

Parallel.For(0, nwsobjs.Length, bi => {
      nwsobjs[bi].MakeSrf(2, 0.01, 0.01, 0.01);
      });

to the following

object locker = new object();
Parallel.For(0, nwsobjs.BranchCount, bi => {
    NWSrf obj;
    lock (locker) obj = nwsobjs.Branch(bi)[0];
    obj.MakeSrf(2, 0.01, 0.01, 0.01);
    });

Is that what you mean? It still crashes. Does it tell us that the rhinocommon method is NOT thread safe?

I guess this error is not from your code.
Maybe the McNell team will have more ideas.
I did several tests, nothing worked correctly.

ah thanks for testing it out!
the indexing within Parallel.For() actually works on many rhinocommon calls such as sweeping or boolean differences, although it does look like a bad practice.
really makes me think this network srf api has something else…

Sorry, I really think this is from “CreateNetworkSurface” out of managed code.

Hi @Will_Wang,

Can you post a GH file I can load and run from here?

Thanks,

– Dale

here
don’t toggle to true or it blows up :stuck_out_tongue_winking_eye:

networksrf_parallel_crash_test.gh (21.0 KB)