Main Site Documentation

BeginInvoke() unsupported exception


#1

I’m attempting to use delegate.BeginInvoke() (for the first time) and get an unsupported exception on execution with the break on the line: cb.BeginInvoke(null, null);
. It’s a simple test program for a timer that maintains a list of delegates for callbacks and attempts asynchronous invocation (on separate thread) for each. Not sure, if I got it working, if separate threads are actually used, but it would be nice to find out. Help appreciated. I’m running SDK 4.1 on Panda II. Here’s the simple test program code followed by a copy of the output.

using System;
using Microsoft.SPOT;
using System.Threading;
using System.Collections;

namespace MFTimerTest
{
    public class Program
    {
        //***** PUBLIC *****//

        public static void Main()
        {
            Debug.Print("Hello from Async Timer Test");
            AsyncTimer _asyncTimer = new AsyncTimer();
            _asyncTimer.Register(Callback1);
            _asyncTimer.Register(Callback2);
            _asyncTimer.Start();
            while(true);
        }

        //***** PRIVATE *****//

        private static void Callback1()
        {
            Debug.Print("   Hello from Callback1");
        }

        private static void Callback2()
        {
            Debug.Print("   Hello from Callback2");
        }
    }

    public class AsyncTimer
    {
        //***** PUBLIC *****//

        public delegate void CBDel();

        public AsyncTimer()
        {
            _cbList = new ArrayList();
            _systemTimer = new System.Threading.Timer(OnPeriod, null, Timeout.Infinite, Timeout.Infinite);
        }

        public void Start()
        {
            _systemTimer.Change(1000, Timeout.Infinite);
        }

        public void Register(CBDel tcb)
        {
            _cbList.Add(tcb);
        }
 
        //***** PRIVATE *****//

        private void OnPeriod(object o)
        {
            Debug.Print("This is AsyncTimer's OnPeriod");
            foreach (CBDel cb in _cbList)
                cb.BeginInvoke(null, null);
        }

        private ArrayList               _cbList;
        private System.Threading.Timer  _systemTimer;
    }
}

Output:

Hello from Async Timer Test
This is AsyncTimer’s OnPeriod
#### Exception System.NotSupportedException - CLR_E_NOT_SUPPORTED (3) ####
#### Message:
#### MFTimerTest.AsyncTimer::OnPeriod [IP: 0021] ####
A first chance exception of type ‘System.NotSupportedException’ occurred in MFTimerTest.exe
An unhandled exception of type ‘System.NotSupportedException’ occurred in MFTimerTest.exe


#2

I would assume that the BeginInvoke method is not implemented (supported) by netmf, even that the function is there.

If you have ReSharper, Reflector or similar installed, hit F12 on BeginInvoke to see how it’s implemented.

The most asynchronous patterns are not implemented in netmf


#3

Reinhard, Not familiar with Reflection, etc. but I installed ReSharper. Nothing happens when I hit F12 (as opposed to jumping to a definition for some other calls.) Don’t know if this is definitive, but I wouldn’t be surprised if BeginInvoke()isn’t implemented, although passing the compiler seems odd. It doesn’t show up in the object browser or MF4.1 reference under Delegate, but then neither does Invoke(), which works, BTW. Not sure if the IL or Meta info says anything about the implementation:

The IL (ildasm):

IL_002a: callvirt instance class [mscorlib]System.IAsyncResult MFTimerTest.AsyncTimer/CBDel::BeginInvoke(class [mscorlib]System.AsyncCallback, object)

Meta info looks like this:

Method #3 (0600000b) 
-------------------------------------------------------
	MethodName: BeginInvoke (0600000B)
	Flags     : [Public] [Virtual] [HideBySig] [NewSlot]  (000001c6)
	RVA       : 0x00000000
	ImplFlags : [Runtime] [Managed]  (00000003)
	CallCnvntn: [DEFAULT]
	hasThis 
	ReturnType: Class System.IAsyncResult
	2 Arguments
		Argument #1:  Class System.AsyncCallback
		Argument #2:  Object
	2 Parameters
		(1) ParamToken : (08000005) Name : callback flags: [none] (00000000)
		(2) ParamToken : (08000006) Name : object flags: [none] (00000000)

#4

Just an idea.

Try this:


#5

Hi,
I think that in NETMF the next timer event cannot be handeled before the last one has finished. As asynchronous calls are not implemented in NETMF, I think it is required to create and start a new thread for each entry in the list. May be that Reinhards ThreadPool for NETMF can be useful for this
https://www.ghielectronics.com/community/codeshare/entry/806


#6

BeginInvoke seams not to be there, I mean only metadata is there.
If I press F12 on Thread.Sleep, I get decompiled C# code from ReSharper.
BeginInvoke gives me nothing.
Also IAsyncResult is empty, which means to me implementation was started, but not finished.
And you did only implement a half implementation of the async pattern… You need to provide a AsyncCallback in which you call EndInvoke. Just wanted to mention that.

As RoSchmi wrote, you can use my ThreadPoool to do the same thing:

private void OnPeriod(object o)
{
    Debug.Print("This is AsyncTimer's OnPeriod");
    foreach (CBDel cb in _cbList)
        ThreadPool.QueueUserWorkItem(_ => cb());
}

#7

I would log it as an issue on netmf codeplex (just in case).


#8

Reinhard:
For me, F12 jumps to the definition of the object–Maybe I have ReSharper configured differently? Still, F12 on BeginInvoke() does nothing.

I’ve been thinking that I can safely ignore IAsysncResult and EndInvoke() in this case as the methods called don’t have a return value. I’m working with Andrew Troelsen’s "C# 2010 book which says, "If you asynchronously invoke a method that provides a void return value, you can simply ‘fire and forget.’ You will never need to cache the IAsyncResult compatible object or call EndInvoke() in the first place (as there is no return value to retrieve).

Before trying BeginInvoke() I tried manually creating a thread, replacing BeginInvoke() like this:

        protected virtual void OnPeriod(object o)
        // runs each each registered callback delegate on a new thread
        {
            foreach (CBDel cb in _cbList) {
                Thread t = new Thread(new ThreadStart(cb));
                t.Start();
            }
        }

It seems to work fine in the simple test program but I get a memory error (failed allocation) when I run it in a program that uses several timers (2-3 OK, 4-5 fails)that cycle every second. I’m still chasing the bug, but wondering if I need to deal with the threads when they are finished??

Alternatively, I bumped into your ThreadPool implementation earlier and I’ve been studying that and may very will be using it. Thanks.


#9

@ Matt5 - Spawning a new thread like you did bears the risk that the tread gets garbage collected because there is no reference for it. t runs out of scope immediately.
Also spawning a lot of new threads might cause your allocation problem.

My ThreadPool reuses the same threads over and over, as soon as the work is done.
Please be aware that the default maximum number of threads is 10.
But you can increase it at any time. The minimum number of threads only describes how many threads are started initially. As long as max count stays the same, no thread is released at any time.
It also has an event that is fired if all threads are busy.
If all threads are busy, the callback goes to a queue and is executed later.


#10

By design the thread will continue to execute until the thread procedure completes, there is no need to maintain a reference to the Thread instance.


#11

@ taylorza - Nice to know detail, didn’t know that.
But it’s different for System.Threading.Timer, right?


#12

@ Reinhard Ostermeier - Yes, if System.Threading.Timer instance is garbage collected the resources for the timer are reclaimed, therefore it is necessary to maintain a reference to the timer as long as you want to keep it alive.


#13

@ Taylorza, Well that’s interesting. Like, Reinhard, I was under the impression the GC ate threads without a reference, but I really don’t know anything about it, only what I’ve read.


#14

@ Matt5 - In my view it is a sensible design choice. Threads lifetime is governed not the references to the thread, but by the lifetime of the thread procedure.

When I get to my PC I will find the reference, but the documentation for the desktop framework Thread class explicitly states that the is no need to keep a reference to the Thread instance. One of the first things I did when I started tinkering with .NETMF was verify but experiment and code review that .NETMF’s implementation followed the same approach.


#15

@ taylorza, makes total sense, thanks. Leaves me to wonder why manual thread creation and start causes a memory allocation error in a non-simple case. Of course that means there could be a hidden memory error in my program, or that I’m bumping up against some ceiling in MF/Panda II.


#16

@ Matt5 - Creating a thread consumes memory resources, at the very least there is stack space reserved for the thread and a thread control block allocated to manage the thread and support the context switch. The only difference is that that memory is not reclaimed when the thread class instance is GC it is reclaimed when the thread execution completes.


#17

Here is the reference to the documentation (this is for the full framework).

http://msdn.microsoft.com/en-us/library/system.threading.thread(v=vs.110).aspx


#18

Thanks, taylorza. The “(v=vs.110)” seems to bad address–this link to the reference works:
http://msdn.microsoft.com/en-us/library/system.threading.thread

I tried a simple code test that creates an array of timers and registered two callbacks with each that contained sleep periods. Running on my laptop w/ emulator worked fine (up to 10 sec sleep period, 1000 timers,) Running on a Panda II experienced a memory alloc error with 10 timers and the callback sleep periods reached 6.65 seconds.