P / Invoke callback (also) Managed callback for unmanaged code

The attached C # unit test and C code file attempts to pass a managed callback to unmanaged code. The code does execute, but the count variable never expands. Therefore, the test does not work.

The fact that it runs at all means that it loads the DLL, finds a link for the DoCallBack () method, and seems to call this method. But nothing happens. So something is not working.

You probably want to know why you are trying to do this? And you wonder if there is a better approach? Well, the ultimate goal is to create a β€œhack” to run threads through AppDomains with almost the same performance as in the same domain.

In the following link, you will find a faster AppDomain cross-application technique so far. The MS.Net AddIn team offers "FastPath", which greatly improves the performance of remote remote operations. We run our example on .Net 3.5, and it works very fast after putting their AddIn contracts in the GAC.

http://blogs.msdn.com/b/clraddins/archive/2008/02/22/add-in-performance-what-can-you-expect-as-you-cross-an-isolation-boundary-and- how-to-make-it-better-jesse-kaplan.aspx

Now let's discuss some points in time to understand why it is not fast enough for our needs. Normal cross domain domains offer about 10,000 calls per second per method with zero arguments. With the FastPath option, which increases to 200,000 calls per second. But comparing this to a C # invocation of an interface method with null arguments (in the same domain), it performs more than 160,000,000 operations per second on the same computer as other tests.

Thus, even the FastPath method is still 1000 times slower than just calling the interface method. But why should we work better?

Or, performance requirements should remove all software bottlenecks from a processor-bound application that processes billions of tuples of information in minutes using multi-core and distributed technology.

But the new requirement for the function will be the ability to offer AddIn or Plugin architecture so that components can be loaded or unloaded without stopping the rest of the system. The only way to do this effectively on .Net is with separate AppDomains.

Please note that we do not want to transfer data through AppDomains, they all work independently of each other.

But as for streaming, it is very inefficient to have a separate stream running in each of hundreds of AppDomains. If so, they compete for the processor and cause a huge loss in performance when switching contexts.

So, the plan is to have a primary or primary domain that has a thread pool, and in turn calls each AppDomain, which should work, and let it work for a while. Thus, this means collaborative multithreading (to avoid context switching). Therefore, AppDomains will return so that the main AppDomain can move on to others.

Unfortunately, each AppDomain cannot work for a very long time no matter how it finishes work, and it needs to return to the main domain in order to allow another AppDomain to work. So a performance time of 200,000 per second from the FastPath method will lead to a significant decrease in overall performance due to cross-calling AppDomain.

Unlike PInvoke below, we measured the time with StopWatch to produce more than 90,000,000 - that's 90 million - calls per second on the same machine as the other tests. Thus, the hope is that by then the reverse P / Invoking in another AppDomain will still allow many millions of operations per second.

90 million per second is much closer to our need to switch threads between AppDomains.

Good. Let's get back to this unit test. The goal of this simple unit test is to first get simple callbacks from unmanaged managed code .... after that, the next step is to create a separate AppDomain and get a delegate callback to it and go to unmanaged code to check cross callback performance -domain.

We know that all of this is possible, we see discussion and examples on the Internet ... but while the code below seems simple ... it just doesn't work as expected.

Here's the unmanaged code created as a DLL without the / CLR command line option:

#include <windows.h> typedef void (CALLBACK *PFN_MYCALLBACK)(); int count = 0; extern "C" { __declspec(dllexport) void __stdcall DoSomeStuff() { ++count; } } extern "C" { __declspec(dllexport) void __stdcall DoCallBack(PFN_MYCALLBACK callback) { PFN_MYCALLBACK(); } } 

Here is the C # unit test code.

 using System.Runtime.InteropServices; using System.Security; using NUnit.Framework; namespace TickZoom.Callback { [SuppressUnmanagedCodeSecurity] [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void MyCallback(); [TestFixture] class CrossAppDomainTesting { public int count; [Test] public void TestCallback() { NativeMethods.DoCallBack( delegate() { ++count; }); Assert.AreEqual(1, count); } [Test] public void TestDate() { NativeMethods.DoSomeStuff(); } } public static class NativeMethods { [SuppressUnmanagedCodeSecurity] [DllImport("CrossAppDomain.dll")] public static extern void DoSomeStuff(); [SuppressUnmanagedCodeSecurity] [DllImport("CrossAppDomain.dll")] public static extern void DoCallBack(MyCallback callback); } } 
+4
source share
1 answer

The first commenter on the post solved the encoding problem. I waited for him to publish it as an answer, to give him credit. If he does, then I will.

Is it just a typo in your DoCallback function? You have PFN_MYCALLBACK (). I think you want it to be callback (). - Jim Michelle

In addition, the result of the timings so that the fastest way to call from one AppDomain to another AppDomain is as follows:

First, you call an unmanaged method to send unmanaged code through a delegate that is marching on a function pointer.

From now on, you call the unmanaged code without any arguments, but reuse the function pointer to call into another AppDomain.

Our testing shows that this works for 90 million calls per second, compared with 300 million per second of a simple C # call on an interface or Interlocked.Increment (), which is 80 million per second.

In other words, this is fast enough to happen quite often for threads to cross the borders of the AppDomain.

NOTE. There are a few things to watch out for. If you save the pointers to the AppDomains that you unload and then try to call them, you will get an exception from the delegate collection. The reason for this is that the function pointer that the CLR provides is not just a function pointer in the code. Instead, it is a pointer to a β€œthunking” code snippet that first checks that the delegate still exists, and it does a little work to move from unmanaged to managed code.

Our plan is to assign an integer descriptor to each AppDomain descriptor. Then the unmanaged code will receive both a "descriptor" and a function pointer for placement in the array.

When we unload the AppDomain, we will also inform the unmanaged code to remove the function pointer for this handle. We will save the freed descriptor in a free list for reuse for the next AppDomain created.

+2
source

Source: https://habr.com/ru/post/1389813/


All Articles