Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Indicators, Threads and Dispatchers

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Indicators, Threads and Dispatchers

    The Indicator class, derived from IndicatorRenderBase inherits the Dispatcher object. What is the purpose of this dispatcher object? I had the understanding that the Dispatcher was used to keep the Indicator's access confined to a single thread but I discovered two things.
    1. The thread that is calling your indicator can change, even for the same call. OnBarUpdate can be called from different threads. I have observed OnBarUpdate first called by different threads when going from Historical to Realtime.
    2. Dispatcher.CheckAccess() fails for OnBarUpdate most of the time. What exactly is this Dispatcher for?
    3. OnRender and OnBarUpdate are called from different threads. How does NinjaTrader prevent thread synchronization issues between OnBarUpdate and OnRender?
    4. OnBarUpdate and TestTriggerCustomEvent execute in the same thread, separate from OnBarUpdate.
    I used the following code to determine the above.

    If the indicator is connecting to other external events, for example Account.ExecutionUpdate, how can I ensure that the code runs in the correct thread? Initially I thought I could use Dispatcher.InvokeAsync from the indicator but the indicator.OnBarUpdate could be called from a thread other then the one held by that dispatcher. This is assuming I need to access my own member variables and the bar series.

    For example

    Code:
    class MyIndicator: Indicator
    {
        IDispose someDisposableObject = new SomeDisposableObject();
    
        const double someValue = ...;
    
        protected override void void OnBarUpdate()
        {
             if(Close[0] > someValue)
             {
                  someDisposableObject.Dispose();
                  someDisposableObject = null;
             }
        } 
    
        protected override void OnRender(ChartControl control, ChartScale scale)
        {
             if(someDisposableObject != null)
             {
                  someDisposableObject.CallSomeFunction();
             }
        }
    }
    In the above example since OnBarUpdate and OnRender can run from different threads, its possible that OnBarUpdate is calling Dispose at the same time that OnRender is trying to render. This can result in the object being disposed between the check for null and the call to CallSomeFunction() or someDisposableObject being disposed of at the same time as the CallSomeFunction resulting in partial cleanup while attempting to use it.

    Do I need to do thread synchronization to make sure someDisposableObject is not modified by two threads at once?

    Here is the code used to determine what threads are executing which functions.

    Code:
    #region Using declarations
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Xml.Serialization;
    using NinjaTrader.Cbi;
    using NinjaTrader.Gui;
    using NinjaTrader.Gui.Chart;
    using NinjaTrader.Gui.SuperDom;
    using NinjaTrader.Gui.Tools;
    using NinjaTrader.Data;
    using NinjaTrader.NinjaScript;
    using NinjaTrader.Core.FloatingPoint;
    using NinjaTrader.NinjaScript.DrawingTools;
    using System.Runtime.CompilerServices;
    using System.Threading;
    #endregion
    
    //This namespace holds Indicators in this folder and is required. Do not change it.
    namespace NinjaTrader.NinjaScript.Indicators
    {
        public class ThreadTests : Indicator
        {
            protected override void OnStateChange()
            {
                TestCheckAccess();
                if (State == State.SetDefaults)
                {
                    Description                  = @"Tests thread behavior for indicators.";
                    Name                         = "ThreadTests";
                    Calculate                    = Calculate.OnBarClose;
                    IsOverlay                    = true;
                    DisplayInDataBox             = true;
                    DrawOnPricePanel             = true;
                    DrawHorizontalGridLines      = true;
                    DrawVerticalGridLines        = true;
                    PaintPriceMarkers            = true;
                    ScaleJustification           = NinjaTrader.Gui.Chart.ScaleJustification.Right;
                    //Disable this property if your indicator requires custom values that cumulate with each new market data event.
                    //See Help Guide for additional information.
                    IsSuspendedWhileInactive     = true;
                    Calculate = Calculate.OnEachTick;
                }
                else if (State == State.Configure)
                {
                }
            }
    
            protected override void OnMarketData(MarketDataEventArgs marketDataUpdate)
            {
                TestCheckAccess();
            }
    
            protected override void OnBarUpdate()
            {
                TestCheckAccess();
            }
    
            void TestCheckAccess([CallerMemberName] string callingMethod = null)
            {
                if(Dispatcher != null)
                {
                    var checkAccess = Dispatcher.CheckAccess();
                    Print(string.Format("Function {0} State = {1} Dispatcher.CheckAccess = {2} ThreadID = {3}", callingMethod, State, checkAccess, Thread.CurrentThread.ManagedThreadId));
                }
            }
        }
    }
    
    #region NinjaScript generated code. Neither change nor remove.
    
    namespace NinjaTrader.NinjaScript.Indicators
    {
    public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
    {
    private ThreadTests[] cacheThreadTests;
    public ThreadTests ThreadTests()
    {
    return ThreadTests(Input);
    }
    
    public ThreadTests ThreadTests(ISeries<double> input)
    {
    if (cacheThreadTests != null)
    for (int idx = 0; idx < cacheThreadTests.Length; idx++)
    if (cacheThreadTests[idx] != null &&  cacheThreadTests[idx].EqualsInput(input))
    return cacheThreadTests[idx];
    return CacheIndicator<ThreadTests>(new ThreadTests(), input, ref cacheThreadTests);
    }
    }
    }
    
    namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
    {
    public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
    {
    public Indicators.ThreadTests ThreadTests()
    {
    return indicator.ThreadTests(Input);
    }
    
    public Indicators.ThreadTests ThreadTests(ISeries<double> input )
    {
    return indicator.ThreadTests(input);
    }
    }
    }
    
    namespace NinjaTrader.NinjaScript.Strategies
    {
    public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
    {
    public Indicators.ThreadTests ThreadTests()
    {
    return indicator.ThreadTests(Input);
    }
    
    public Indicators.ThreadTests ThreadTests(ISeries<double> input )
    {
    return indicator.ThreadTests(input);
    }
    }
    }
    
    #endregion
    Last edited by ntbone; 12-22-2022, 10:59 PM.

    #2
    Hello ntbone,

    The Indicator class, derived from IndicatorRenderBase inherits the Dispatcher object. What is the purpose of this dispatcher object? I had the understanding that the Dispatcher was used to keep the Indicator's access confined to a single thread but I discovered two things.​
    The platform has multiple threads for many different components so many objects have dispatchers for internal reasons. For your uses you would not need to use that at all or do any type of threading.​ You can search for Dispatcher in the help guide to see some valid use cases for using a dispatcher: https://ninjatrader.com/support/help...sub=dispatcher

    The only time you need to use dispatchers in NinjaScript is when you are working with WPF controls that are in the user interface. The only other time would be if a help guide sample shows using a dispatcher for a specific purpose.

    If the indicator is connecting to other external events, for example Account.ExecutionUpdate, how can I ensure that the code runs in the correct thread?
    For any internal method event or override you don't need to worry about threads, the platform handles all of that. You would simply need to follow what the help guide shows in terms of using any given override event or code. To use the account objects ExecutionUpdate event you can see an example in the help guide, no threading or dispatching is needed: https://ninjatrader.com/support/help...b=ExecutionUpd ate

    OnRender and OnBarUpdate are called from different threads. How does NinjaTrader prevent thread synchronization issues between OnBarUpdate and OnRender?
    These two methods are not called in sync, they are called separately for different types of events. Properrties of your script you will have access to without any threrading considerations, the platform handles any threading related to the script. You do not need to worry about threading with a NinjaScript file.

    Do I need to do thread synchronization to make sure someDisposableObject is not modified by two threads at once?

    I wouldn't be able to answer these type of questions about how your custom code may work in that use case, you would have to test this on your end to find out if what you are trying to do has errors or not and adjust your code if needed. Generally checking for null would be sufficient for checking if an object variable had been set to null.

    In general you should not need to manage or alter how a scripts threading works, the platform is expecting the objects to be in specific states that it puts them in and specific threading arrangements based on internal logic. Dispatching is only needed for very limited use cases, you don't have to use the base classes dispatcher or check access for threading considerations in an indicator.

    Keep in mind the note on this page: https://ninjatrader.com/support/help...sub=dispatcher about Thread Access is continuing with the UI sample above it. You do not need to CheckAccess in a indicator, that would be if you were accessing a UI control like ChartControl as the example shows. It is simply showing Dispatcher generically because that type of code could be used with any UI control.

    Comment


      #3
      Thanks for the feedback.

      I have an object that is used by indicator. This object
      • Renders during the indicators render event
      • Updates during the indicators bar update
      • Attaches to the Account events for when executions occur
      I have run into the very scenario I was describing above. Its a timing problem so very difficult to reproduce. What I suspect is happening is the following
      • The rendering code is executing on the rendering thread
      • Events are triggering via either OnBarUpdate, or OnExecutionUpdate.
      The events are disposing of objects that are no longer needed whilst they are simultaneously being used to render. This is resulting in a fatal crash that takes down the entire application. When this crash happens there are no logging events in the logs or trace files. I have managed to capture .dmp files for it and inspect the call stacks. This was when I realized there was a potential threading issue going on and upon further inspection of the code I am getting an issue. Its vary precarious timing issue that happens when multiple threads are simultaneously manipulating the same object. If OnBarUpdate and OnRender for an indicator can never execute at the same time, then its likely the events from the Account that are causing the issue. The account events would not be synchronized with the indicator.

      Comment


        #4
        Hello ntbone,

        All I could suggest would be to avoid disposing of an object that is still in use. If you absolutely need to dispose of the object you could make a list to collect objects to be disposed and do that later in State.Terminated. If the object is still in use and you need to change what is being rendered you can just assign a new value to the variable that is being used and collect the old object for disposal at a safe time.

        The rendering events are not in sync with the bar or account events, those all happen for their own reasons. If you are doing conflicting actions that will result in errors.

        You can see an example of one situation for rendering where an object is disposed in the following link. I am unsure of the reason you need to dispose of your object, if it is for other reasons then you would still need to implement a list and dispose the objects later to avoid the problem you are seeing. The code in the following link is specifically for rendering because DXBrushes need recreated if the render target changes. https://ninjatrader.com/support/help...onrendertarget

        The order of OnRenderTargetChanged and OnRender is fixed so no null check is needed inside OnRender for that brush. For other variations where you dispose an object in a different method, that will not be in a fixed order so that may cause a problem. You would need null checks in OnRender and possibly try/catch logic to avoid trying to use a disposed objec unless you fix the dispose logic to be at a better time.

        Comment


          #5
          The underlying problem here is thread safety. The dispose is making it clear that its possible for rendering to occur at the same time as code for either OnBarUpdate or Account.ExecutionUpdate. If I remove the dispose that will prevent it from crashing due to disposed object but that still won't prevent something from changing the state of objects while they are being rendered. This can still result in odd bugs that are hard to reproduce, track down and fix.

          The code that disposes the objects is assigning null to the fields that are being used to render. The render code is checking to see if they are null before using them. If one thread is assigning them to null and then disposing them after the rendering thread has done its check, the rendering thread will attempt to use an object that is disposed and won't know it.

          Is it possible for OnBarUpdate which runs in its own thread to be executing simultaneously while OnRender is also being executed? Does NinjaTrader guarantee that never happens even though they both run in different threads?

          If the answer is yes, they never run at the same time, then the problem is due to Account.ExecutionUpdate. It would mean that any time an indicator uses these events it will need to use thread synchronization to ensure that objects used in Account.ExecutionUpdate and OnBarUpdate/OnRender are accessed in a thread safe way.

          Comment


            #6
            Hello ntbone,

            What you are doing is something custom so you need to implement your own correction here, it doesn't matter what the platform is doing in regard to threads. if you hit an error because you disposed an object that was still in use you need to avoid doing that and use different logic for your disposal.

            Nearly all code does not require disposal due to C# garbage collector. Native resources like rendering brushes need disposed which you can do in OnRender or OnRenderTargetChanged, those are in a fixed order so there is an expectation to use OnRenderTargetChanged for that purpose or dispose inline within OnRender. Regular C# classes or code do not need to be disposed so if you implemented disposable on a custom class and you are not managing a native resource you can just remove that all together and let the garbage collector work. If you are actually managing a resource that needs to be disposed of then you need to implement a better pattern for disposing of that object, where you are trying to dispose of it now is not working based on your comments so you need to avoid doing that.

            Is it possible for OnBarUpdate which runs in its own thread to be executing simultaneously while OnRender is also being executed? Does NinjaTrader guarantee that never happens even though they both run in different threads?
            OnRender runs on its own based on a rendering loop and other events like mouse events in the chart or system events. Other NinjaScript events can also trigger render passes, OnRender is not in sync with any other method. If you are using an object that is being disposed of then you need to stop doing that and avoid disposing the object while its being used. You could make your own list of objects that need disposed and dispose of them at a safe time.







            Comment


              #7
              I have more concerns now regarding thread safety and the methods of the indicator. From what has been said so far in this thread.
              • Render functions happen on their own independent of OnBarUpdate or other events.
              • Render functions can be called from a separate thread from OnBarUpdate
              • Other events can also be called from a different thread.
              Since render functions are not in sync with any other methods, on a multi-core system it is possible for the render functions to execute at the exact same time as OnBarUpdate or other events. In my specific case, an object that holds rendering resources is being disposed when it is determined it is no longer needed it and then assigned null.. All other functions in my indicator check to see if its null before using it. Since these functions may all be called from different threads a null check won't suffice in protecting those functions from using the object while its may be also disposed.

              In general, when objects which are accessed or used by multiple threads need to be coded in a way that makes them thread safe. This requires using some sort of mechanism to ensure that only one thread is modifying it at any given time and no other object is reading it while it is also being modified. This is a fundamental concept for multi-threaded development. Unpredictable behavior can result otherwise.

              Any indicator developing custom rendering functions is subject to multi-threaded problems. The common pattern for an indicator is
              • Modify the state of the object based on data analyzed in OnBarUpdate. This could be storing new values in a series, or storing values in other fields that are part of the indicator.
              • Rendering via the OnRender function.
              • Modifying the state of the object based on market data events or Account events.
              Since all of these methods can be called from different threads and there is no existing mechanism in the NinjaTrader framework to synchronize OnBarUpdate, OnRender and other events, multi-threaded bugs can result.

              In summary, OnBarUpdate, OnRender and other event methods (like market data and account events ) are NOT thread safe. Without using locks or other mechanisms to synchronize access to data, multi-threaded bugs can arise if any data is modified by any one of these methods. The documentation should be updated to point this out to developers so they know to make their code thread safe when using these methods.
              Last edited by ntbone; 01-17-2023, 10:28 AM.

              Comment


                #8
                Hello ntbone,

                All normal properties of a script can be accessed without those type of considerations. You can review any script included with the platform that uses OnRender and see it has no special threading/locking/dispatching logic for accessing basic NinjaScript properties. You would only need to use these type of considerations if the help guide mentions it. A good way to judge if you are going to need any of these items would be if you are accessing the GUI (dispatching) or accessing something which is not a part of your script like an internal platform collection that is not controlled by the script (lock or use .ToList() to make a local copy of the information).

                The events are disposing of objects that are no longer needed whilst they are simultaneously being used to render. This is resulting in a fatal crash that takes down the entire application. When this crash happens there are no logging events in the logs or trace files.


                This type of crash could indicate that you are incorrectly managing some native resource in your custom object. You would be able to see a crash like that if you incorrectly managed a native sharpDX resources as well. Native errors cannot be handled in C# code so you get a resulting crash or freeze, there are also very limited errors reported so it may be difficult to diagnose.

                In this case with your custom object you may need to have different logic for its disposal to avoid errors or you may need to dispose of it in a better location as previously mentioned. The internal threading is irrelevant here, you would instead want to focus on fixing your custom object so that it is better compliant with NinjaScript use. You can do that by avoiding requiring disposing, if that is not possible then you may need to change where you are disposing to avoid the error. Using locks/dispatching/threading with your custom object wouldn't necessarily be helpful if your dispose method is ultimately what is causing the crash, if that method is having a native error you would need to fix that instead to prevent that from happening.


                Comment


                  #9
                  The functions above are not thread safe. Any properties that exist in an indicator or are properties I specifically add to it will not be thread safe. Consider the following example.

                  Code:
                  	class TestIndicator: Indicator
                  	{
                  		protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                  		{			
                  			//(2)
                  			if(someDenominator != 0)
                  			{
                                  //(3)
                  				var someValue = 1/someDenominator;
                  				// do some rednering code with someValue
                  			}			
                  		}
                  
                  		protected override void OnBarUpdate()
                  		{
                  			// (1)
                  			someDenominator = Close[0] - Open[0];	// example formula that may make something that is 0.
                  		}
                  
                  
                  		double someDenominator;
                  	}
                  If (1) executes after (2) and before (3) then you get a divide by 0 error. Since OnRender and OnBarUpdate are in different threads with no synchronization between their executions this can happen. Please bear in mind that this is a simple example. All sorts of issues can arise when code in one thread is changing data while code in another thread is accessing that data, making decisions.

                  Replace the check for 0 with the check for null and you will end up with the same situation as my code. At (1) it is being disposed and assigned null, between (2) and (3). Since the null check that would be done at (2) has passed OnRender assumes the object still exists and proceeds to use it while OnBarUpdate simultaneously disposes it and sets it to null.

                  One way to code the above and prevent this issue is to do the following.

                  Code:
                  	class TestIndicator: Indicator
                  	{
                  		protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                  		{
                  			double someValue = 0;
                  
                  			lock(someLock)
                  			{
                  				if(someDenominator != 0)
                  				{
                  					someValue = 1/someDenominator;
                  
                  				}
                  			}
                  
                  			// do some rednering code with someValue
                  		}
                  
                  		protected override void OnBarUpdate()
                  		{
                  			lock(someLock)
                  			{
                  				someDenominator = Close[0] - Open[0];   // example formula that may make something that is 0.
                  			}
                  		}
                  
                  		object someLock = new object();
                  
                  
                  		double someDenominator;
                  	}
                  If Series<T> is not designed to be thread safe, and since OnBarUpdate and OnRender are not synchronized to be thread safe to each other, it too will be subject to these problems as will any other properties used by indicators. These problems will be difficult to debug, or reproduce as they relying on specific timings in order to occur.

                  One of the fundamental principles to follow with multi-threaded code is that any time more then one thread has access to data, and one or more threads might modify that data, protection must be put in place.

                  Comment


                    #10
                    Hello ntbone,

                    This is not like your custom object/dispose problem and you are also mixing up issues with this new sample.

                    This sample has a double type variable that your script manages. You will get the last set value when accessed from OnRender. The default for a double is 0 so your logic is inherently going to cause an error because you leave the possibility of diving by zero in the logic (this is the mix-up, different error). In this case you should have just set your variable to the end result in OnbarUpdate instead of doing the division in OnRender. There is no reason to use a lock for that use case because nothing except your script is going to set or access that variable.

                    You should never try to halt OnRender by using locks or waiting using while loops. OnRender is the rendering loop for the chart. If you hit a situation where achieving a lock takes a long time or you are doing something to slow down/stop the render pass you will cause lagging or a freeze in the UI. Its best to avoid using any special logic in OnRender and only using that to display end results.

                    The issue you are seeing with dispose and the platform crashing is something else. A null object won't crash the platform, that causes a object not set to a reference error which will stop the script and be reported to the log. A native error or a infinite loop would cause a crash without logging. If you have a native error in your dispose method or with how you use that object that is the real issue that you need to address.

                    If Series<T> is not designed to be thread safe, and since OnBarUpdate and OnRender are not synchronized to be thread safe to each other, it too will be subject to these problems as will any other properties used by indicators. These problems will be difficult to debug, or reproduce as they relying on specific timings in order to occur.

                    One of the fundamental principles to follow with multi-threaded code is that any time more then one thread has access to data, and one or more threads might modify that data, protection must be put in place.


                    When you access bar data in OnRender you use specific indexes because OnRender is not in sync with OnBarUpdate. OnRender knows about what the user is looking at right now which includes what bar indexes are currently being viewed in the chart so you can pull data from series for those indexes. There is no thread safety needed here, you just pass an index to the series GetValueAt method to get a value for that index. There is also no thread safety needed for any script variables like private variables that you make. You only need to add conditions to OnRender to make sure the values are acceptable before trying to use them rather than trying to worry about threading. There are also convenience methods like IsValidDataPoint for series for this purpose.



                    If you have further questions about your crash situation we can go further into that, otherwise I will close this post as resolved so you can debug the dispose situation further. We should not venture into hypothetical situations with alternate unrelated code, the double example is not helpful in relation to your actual error because that's not the actual code that's having a problem. That was only a problem because you introduced invalid logic that does invalid math. if you see an actual error with your custom object you should focus on that. If you are not sure what the problem is in that code you need to make a very limited sample much like you did in the last post but with the actual code you are using that causes a problem so we know how to actually assist.










                    Last edited by NinjaTrader_Jesse; 01-17-2023, 02:08 PM.

                    Comment


                      #11
                      My focus is not regarding my crash but the overall behavior and thread safety of OnBarUpdate and OnRender. The example I gave was a simple example to illustrate that there are multiple ways at which data can be modified in OnBarUpdate and while simultaneously being accessed in OnRender. Since they can execute simultaneously if the timing is right this results in unpredictable behavior. I have reiterated this fundamental concept of multi-threaded development numerous times in this thread.

                      In my case calling Dispose causes the object, which uses rendering resources to release those resources. If those resources are in the process of being used at the exact same time, this will result in a low level crash somewhere in the SharpDX framework. This is what I see in the call stacks and crashes that I have had in my instance and what I suspect is happening with the code I created.

                      Thread safety is a very important concern since OnRender and OnBarUpdate (and OnMarketData or any other events) can occur from different threads that can execute at the same time.

                      Consider @CandleStickPattern.cs. In OnBarUpdate it modifies BarBrushes[1] and BarBrushes[2]. Its modifying series past the current bar. It can change the value of a brush while the rendering code is simultaneously reading the value. That can result in unpredictable behavior. In many cases its modifying several brushes consecutively. Its possible for the rendering code to only get some of those brush changes instead of all of them. While the side effects here may not be crash inducing, they are still side effects.

                      Consider @VolumeProfile.cs. In OnMarketData it modifies a collecton sortedDictList which is also modified by OnRender. Since OnMarketData runs on a different thread form OnRender and sortedDictList is not of a thread safe collection its possible for the OnMarketData to modify the collection while its being iterated on in OnRender. OnRender and OnMarketData both add to the collection as well. Two different threads are reading and modifying a shared collection that is itself not thread safe.

                      Consider @BuySellPressure.cs. OnMarketData modifies buys and sells but so does OnBarUpdate. Is there any mechanism that ensures that OnBarUpdate and OnMarketData do not execute on different threads or at the same time? In OnMarketData buys and sells are incremented while in OnBarUpdate they can be set to 1. If these two functions run on different threads simultaneously, the incorrect value for either buys or sells will result.
                      Last edited by ntbone; 01-17-2023, 08:05 PM.

                      Comment


                        #12
                        Hello ntbone,

                        The last sample you provided goes against how OnRender is suggested to be used. You should do all bar related math or other calculations outside of OnRender where its associated with OnBarUpdate. OnRender is for custom drawing and is not a good location to execute logic or do math with variables that are set outside OnRender. This is the same thing you should be doing with your custom object, you should pull any values needed during OnBarUpdate while its valid and set them to class level variables. OnRender can use those variables instead of your object avoiding the disposal issue.

                        CandleStickPattern, VolumeProfile, BuySellPressure
                        These new examples are all not good comparisons as those are all programmed to work by using NinjaScript properties directly. There is no concern of threading in any of these examples. All 3 of these scripts use specific NinjaScript concepts like how OnMarketData is called after OnBarUpdate which produces a specific calculation effect. Modifying a dictionary from OnBarUpdate and OnRender is not a problem because its just going to render the last set value at the time of the pass. You are not going to hit any errors by doing that because you are not creating a situation where the total variable is reset or null by an unrelated method.
                        7.The OnMarketData() method is expected to be called after OnBarUpdate()



                        We have gone pretty far off topic from your post 1 at this point, if you need further assistance with the crash that you are facing we can go further into that. I won't be replying back further to go into other thread safety topics. I highly suggest that you review the scripts that come with the platform to gain a better understanding of how you should structure your scripts. The Pivots or ZigZag are good examples that set data from OnBarUpdate and render it later. You really don't need to worry about threading at all for nearly all NinjaScript use cases, the included scripts are all good examples of that and show valid ways to work with your script and its data.


                        Comment


                          #13
                          My initial post was all about understanding thread safety and how threads play into the overrides of an Indicator and other events like Account events.

                          From what you have said in your past post OnMarketData and OnBarUpdate should not execute at the same time so it should be safe to modify data in either one.

                          The worst offender of the examples I posted, was VolumeProfile.cs Modifying a collection while its being iterated will result in an exception thrown. Also modifying a collection from two different threads at the exact same time can have unpredictable results. OnRender for VolumeProfile iterates through that collection and if OnBarUpdate adds to that collection while OnRender is iterating an exception will be thrown. Even if OnRender was updated to not modify the collection, it will still result in an exception thrown if the two methods hit that timing.

                          So in summary, OnBarUpdate and OnRender can execute in different threads and there is no built in mechanism in the framework that prevents this or synchronizes it so that they don't execute simultaneously.
                          • Avoid modifying any data in OnRender that may be used elsewhere. If its data that is specific to rendering and only ever accessed or modified by OnRender/OnRenderTargetChanged it will be safe.
                          • OnBarUpdate and OnMarketData should not execute simultaneously. Modifying data in either of these should be safe with respect to the other.
                          • Account.ExecutionUpdate, PositionUpdate, OrderUpdate are unknowns. They may execute from some other thread that isn't OnBarUpdate or OnMarketData. It would be best to use TriggerCustomEvent when handling these in indicators.
                          • OnRender is going to be accessing data that may be simultaneously modified in another thread, whether this is Series<T> that are part of the Indicator class, custom Series<T>, properties or other fields added to the indicator. As long as OnRender does not modify any of that data the data itself should remain intact. However, any collection iterated on could throw a InvalidOperationException with the message "A collection was modified" if the collection is modified by OnBarUpdate while OnRender is iterating. If the logic in OnRender relies on the state of any member variables or data from the Indicator that logic may execute incorrectly. If the state of the variables checked changes in other threads immediately after OnRender checks the state, OnRender could execute the wrong path. Without proper thread synchronization to any of the data in the indicator, other unpredictable, difficult to reproduce issues could arise.
                          I strongly suggest a good understanding of multi-threaded behaviors and principles for anyone developing indicators that do custom rendering or listen to events external to the indicator. The documentation in NinjaTrader help covers some of this with TriggerCustomEvent but does not mention the potential dangers with OnRender.

                          Comment


                            #14
                            Hello ntbone,

                            All normal properties of a script can be accessed without those type of considerations. You can review any script included with the platform that uses OnRender and see it has no special threading/locking/dispatching logic for accessing basic NinjaScript properties. You would only need to use these type of considerations if the help guide mentions it. A good way to judge if you are going to need any of these items would be if you are accessing the GUI (dispatching) or accessing something which is not a part of your script like an internal platform collection that is not controlled by the script (lock or use .ToList() to make a local copy of the information).

                            I'd like to hear some clarifications on this place in Help Guide:

                            Accessing objects which terminate


                            To protect against race conditions and access errors, you should temporarily check for reference errors any time you attempt to do something with an object.
                            Why: OnStateChange() runs asynchronous to other NinjaScript events. You can run into scenarios where you State.Terminated logic is called in the middle of OnBarUpdate(), OnRender() etc.


                            The Help Guide suggests checking that the object is not null, like

                            OnBarUpdate()
                            {
                            if (MyObject != null)

                            I wonder how it helps if any thread can be pre-empted. If the MyObject is set to null in OnStateChange() when the state changes to Terminated then, I guess, one needs to use a lock in both functions.

                            Am I missing something?

                            Thanks

                            Comment


                              #15
                              I have run into this scenario and problem numerous times. I have some code that is using an object, often a render object that should be disposed. What happens, when the timing is just right, is that Terminate is called, the object is disposed while it is being used by OnRender.

                              I even did their recommended checks but because Terminate can be called while OnRender or OnBarUpdate is half way through execution, its always possible that the object will be assigned null/disposed while half way through execution of OnRender/OnBarUpdate.

                              When accessing things from multiple threads, some sort of synchronization mechanism is required to prevent, say the OnStateChange from destroying or changing things used by OnBarUpdate/OnRender.

                              Comment

                              Latest Posts

                              Collapse

                              Topics Statistics Last Post
                              Started by Geovanny Suaza, 02-11-2026, 06:32 PM
                              0 responses
                              571 views
                              0 likes
                              Last Post Geovanny Suaza  
                              Started by Geovanny Suaza, 02-11-2026, 05:51 PM
                              0 responses
                              331 views
                              1 like
                              Last Post Geovanny Suaza  
                              Started by Mindset, 02-09-2026, 11:44 AM
                              0 responses
                              101 views
                              0 likes
                              Last Post Mindset
                              by Mindset
                               
                              Started by Geovanny Suaza, 02-02-2026, 12:30 PM
                              0 responses
                              549 views
                              1 like
                              Last Post Geovanny Suaza  
                              Started by RFrosty, 01-28-2026, 06:49 PM
                              0 responses
                              549 views
                              1 like
                              Last Post RFrosty
                              by RFrosty
                               
                              Working...
                              X