Main Site Documentation

Gadgeteer Dispatcher limitation and possible Fix, thoughts!


#1

Hello guys,
As you all know Gadgeteer runs a main dispatcher where all theMOdules are created. so if you try to add and initilize a Module outside of the main dispatcher, for example inside a new Thread you will get an exception "Modules must be added to the Main Dispatcher " so i followed the exception found in the Module.cs Gadgeteer core source… which looks like this:


   // Note: A constructor summary is auto-generated by the doc builder.
        /// <summary></summary>
        protected Module() 
        {
            if (!Program.Dispatcher.CheckAccess()) throw new Exception("Modules must be created on the Program's Dispatcher thread"); 
  DebugPrintEnabled = false;
                Module.modules.Add(this);
        }

So i was wondering could one use the following code instead.


        // Note: A constructor summary is auto-generated by the doc builder.
        /// <summary></summary>
        protected Module() 
        {
        //    if (!Program.Dispatcher.CheckAccess()) throw new Exception("Modules must be created on the Program's Dispatcher thread");
            if (Program.Dispatcher.CheckAccess())
            {
                DebugPrintEnabled = false;
                Module.modules.Add(this);
            }else
            {
               Program.Dispatcher.Invoke(new TimeSpan(100), myDispCallBack, Module.modules);
            }
        }

        private object myDispCallBack(object arg)
        {
          //  throw new NotImplementedException();
            (Module.modules).Add(this);
            return null;
        }


The above code will run the check and if it fails instead of an Exception error it will get dispatched and run from inside the main dispatch … i tried it and it works… but i’m not sure why it was done this way in the first place… or this will lead to some catastrophic failures down the road?

Here is a sample code that throws the exception before the fix and not after:



//create a new Gadgeteer Project add the spider and the DP Module and that's it no other modules should be added..
//next right click on References and add choose "GTM.GHIElectronics.Button"

//next paste the code below and replace your void ProgramStated() run it and it should throw the exception now open the Module.cs from the Gadgeteer Source code (get it from gadgeteer.codeplex.com), modify the module.cs according to the change above. rebuild the project. now go back to references and delete Gadgeteer from there and right click on References and choose add, click on Browse and choose the Gadgeteer source code folder nad pick the Gadgeteer dll that you've just built.. rebuild your project and run .. now it won't throw the exception and your button press will work as they should.

   // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
Thread myThread = new Thread(MyThreadMethod);
myThread.Start();
}

        private void MyThreadMethod()
        {
            Gadgeteer.Modules.GHIElectronics.Button button;
            button = new Gadgeteer.Modules.GHIElectronics.Button(11);
            //please add a  button module using gadgeteer designer.
            button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);
        }


please let me know your thoughts.

Thanks.
Jay.


#2

Jay Jay,

The reason for this check is not to protect the single line “Module.modules.add(this)”. It is to protect the constructor of the module (i.e. Button() or CellularRadio() or any other module), and also the gadgeteer library calls that these typically use to e.g reserve pins, set up interfaces. Rather than doing locking at every level to ensure we don’t have race conditions (a consequence of multiple threads), we use the dispatcher-thread model in Gadgeteer which avoids these issues in a clean way.

Alternatives:

  1. Use a GT.Timer instead of a thread for your code - any thread with a while(true) { ; Thread.Sleep(); } can be translated into a GT.Timer( ) with the Tick event handler containing

  2. Keep using a different thread but invoke the module creation on the dispatcher using BeginInvoke:

void ProgramStarted()
        {
            new Thread(ThreadMethod).Start();
        }

        void ThreadMethod()
        {
            // run buttoncreator on socket number 11
            BeginInvoke(new intdelegate(ButtonCreator), 11);
        }

        delegate void intdelegate(int x);

        void ButtonCreator(int socketnumber)
        {
            Debug.Print("socket number " + socketnumber);
            // create button here having received socket number
        }

#3

Hi jamesscott,
Thank you for your reply.

Well yes your solution would work if i’m loading the driver from the same application (by adding a reference it at design time)… BUT what if i’m loading the driver from an external assembly using Assembly.Load()… this will cause the pin conflict exception to raise if i try to load the DRIVER twice for example… basically if i load the driver in an AppDomain and unload it… and then try to load it again i will get the pin conflict exception since the CPU is still reserving the pins and never released them when the driver was unloaded… rendering Dynamic Assembly Loading useless for module drivers. but with my fix above it seems to work…

i’m keen to know how i can take a advantage of assembly dynamic loading (Runtime loading of Modules) without getting the pin conflict exception. (my suggested fix seems to work) do you have any other recommendations?

thanks.


#4

Jay, it seems you need a way to release pins, rather than to load modules in threads other than Dispatcher. When a module is disposed, it should have a way to release its lock on a pin.


#5

Yep that would work as well, as i said it above Gadgeteer should give us a method to release the socket and pins… so we can reuse them…

thanks.