Grasshopper

algorithmic modeling for Rhino

Hi,

I've seen similar issues crop up before (http://www.grasshopper3d.com/forum/topics/grasshopper-display-error...) however what I am doing is a little bit different. and the error messages are different, so perhaps it is a separate issue.

I am using a C# component and a Task to poll some hardware at small intervals. This is to avoid using Threads and all that fun. The Task is created and run when the component is enabled, and it is cancelled when the component is disabled.

It seems to work fine when it is just updating the component message box, or driving another component that is not displayed in the GH viewport (i.e. a TextTag). When a display component such as a Panel is attached to the output, however, it becomes unstable and very soon gives me the attached errors, followed by the red screen.

At the moment, the 'hardware polling' is replaced just with a growing counter to keep things simple.

Code for the task is:

cTokenSrc = new System.Threading.CancellationTokenSource();
token = cTokenSrc.Token;
listener = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
    while(true)
    {
        Print(counter.ToString());
        counter++;
        if (Component != null)
        {
        Component.Message = string.Format("{0}", counter);
        Component.ExpireSolution(true);
        }

        System.Threading.Thread.Sleep(delay);
        if (token.IsCancellationRequested)
        {
        Print("Cancelling...");
        break;
        }
    }
}, token, System.Threading.Tasks.TaskCreationOptions.LongRunning,
System.Threading.Tasks.TaskScheduler.Default);

Is this a problem with the GH display pipeline? Is there anything that could be happening between the Task updating the counter value and GH trying to read it? It seems to happen with any poll rate.

Views: 971

Attachments:

Replies to This Discussion

Since you're using Tasks (and thus probably threading behind the scenes) any exceptions that you don't catch can percolate into Grasshopper, where they could be mistaken for display errors since they just happen to occur whenever the screen is being redrawn. This makes it difficult to narrow it down.

Furthermore any update of the UI must happen from within the UI thread. If you're messing with component messages or solutions and you're calling from some unspecified thread, all hell can break loose.

So I have two suggestions you can start with:

First any calls that you make that might cause GH to either update its display or start a new solution should happen on the UI thread. This will require invoke calls, probably on the Grasshopper document editor or canvas instance.

Second you should add a bunch of try...catch blocks so that you catch and examine your own exceptions (assuming the exceptions actually originate in your code).

You're  showing some bad coding practice here.

You can't simply expire the solution from another thread (the task creates it for you), you have to ask Rhino politely to do it in the right moment. Use the RhinoWindow.Invoke() to run the code in the main thread when it's ready. 

Given this is the code you want to run from the task:

public void RunThisCode() {

Component.Message = thismessage; 

Component.ExpireSolution(true); 

}

And the string declared globally: 

public string thismessage = "currentmessage";

And this method which creates a delegate method and uses rhino to invoke it:

 public void RunAction()  {

    System.Action act = delegate () { RunThisCode(); };
    Rhino.RhinoApp.MainApplicationWindow.Invoke(act);
  }

You can change the task code to do it just like that:

  while(true)

    {
        Print(counter.ToString()); //where do you print it ? if in the main thread then it will still cause the crashes
        counter++;
        if (Component != null)
        {

 thismessage = "Current count " + counter.ToString(); 

RunAction(); 
        }

        System.Threading.Thread.Sleep(delay);
        if (token.IsCancellationRequested)
        {
        Print("Cancelling...");
        break;
        }
    }

The last thing is the message change... you might have to use the Interlock.Exchange() to change the string value... but I'm not totally sure about it.

Thanks, David and Mateusz.

I believe it's my noob-ness when it comes to Tasks. I've understood that it's generally safer to use Tasks as they take care of much of the threading issues under the hood, though I still need to read up some more about how to properly implement them.

Thanks for the fix, I will give that a try!

You're  showing some bad coding practice here.

If you would attend our weekly meetings, we wouldn't have this problem ;)

The Task and ThreadPools are considered safer but only because of the thread setup (thread priority, when to start, what to do on abort etc.)

What you're trying to do (run cross thread methods) is not really solved with those classes, and the microsoft msdn tutorials are laughable... I literally saw an example which was considered viable which went somewhere along : 

public bool threadisrunning = false; 

public void DoTheJob() {

threadisrunning = true; 

// here the thread setup and starting it .. 

While (threadisrunning) { 

Thread.Sleep(100); 

}

}

//and in the background thread

public void ThreadVoid() {

//the actual code in here

threadisrunning = false;

}

As you can see this isn't any fancy mechanism... I would expect something more high-level from MS, but other than crossthread safe collections, there isn't much I know about.

Yes, in some of the examples, it makes it seem like you can get away without the whole Invoke thing and share variables without locking or anything. Oh well, wishful thinking!

RSS

About

Translate

Search

Photos

  • Add Photos
  • View All

Videos

  • Add Videos
  • View All

© 2024   Created by Scott Davidson.   Powered by

Badges  |  Report an Issue  |  Terms of Service