Wait in C # for an event in C ++ - as a task or handle to wait

I program mostly in .NET and I like its asynchronous / concurrency primitives like Tasks, ResetEvents, etc. Today I made significant changes to a C ++ program for the first time and understood how the whole build process works (I updated LigthningDB.NET Project to 0.9.14). But I still lack knowledge of C ++.

One new feature I want to add to the LMDB project (for my own needs) is a notification system (similar to Redis):

  • I want the cursor to return the expected object that will signal every time the data in its table changes.
  • I want to get some data along with a signal (pointer to data structure) for example. key or key +.
  • This object will be located together with the cursor and is part of the cursor, but it signals about changes in the database, for which the cursor has a link.
  • This should work cross platform once (or after a year). I'm fine with any dirty Windows vulnerabilities as long as they do the job.

A typical use case is writer and N readers waiting for new posts. This will allow IPC and fast 2-in-1 retention. LMDB supports simultaneous reading from different processes (while Esent and LevelDB do not).

System.Threading

primitives are available from C ++ and I understand how to use WaitHandle for a blocking call. Is there a way to make this asynchronous? There is a long series of articles on asynchronous synchronization primitives, but they only use TaskCompletionSource

and work within .NET. Can a similar solution be made for inline communication?

One solution could be called converting pipes or sockets into a single listener (Redis or Rhino.Queues ), but performance will suffer: records will have to allocate, copy, and move data, and data will have to move - much worse than passing a pointer to a data structure that is already in memory.

Another option is to move the listener cursor to a key and send a signal. After the signal, the C # listener will know that the cursor has values ​​on the updated key. This kinf solves the data transfer part, but with WaitHandles it blocks - in my case blocking code is worse than the socket combination [allocation / copy / delay].

Are there any better options?

Additional questions:

  • Am I reinventing the wheel here?
  • Could you please point to open source libraries where the .NET program expects (does not block) for a C / C ++ signal (if they exist)?
  • Should I be using LMDB for this workflow? Windows is my priority and I was having a really bad time getting LevelDB working at all (stopped). Are there any better alternatives for everything LMDB does + signaling?

Update

I found this method in the documentation :

public static Task WaitOneAsync(this WaitHandle waitHandle)
{
    if (waitHandle == null) throw new ArgumentNullException("waitHandle");

    var tcs = new TaskCompletionSource<bool>();
    var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle, 
        delegate { tcs.TrySetResult(true); }, null, -1, true);
    var t = tcs.Task;
    t.ContinueWith(_ => rwh.Unregister(null));
    return t;
}

      

doc for ThreadPool.RegisterWaitForSingleObject

says that:

The wait operation is performed using a thread from the thread pool. the delegate is executed by the worker thread when the state of the object becomes signaled or the timeout interval expires .... The wait thread uses the Win32 WaitForMultipleObjects function to monitor registered wait operations.

I fix that these are two different threads and the first wait thread is the only one that will block if there are no signals?

+3


source to share


2 answers


RWFSO uses special thread pool threads to wait for multiple descriptors. There's a built-in limit of 63 pens per stream, so it's not as efficient as IOCP. I would not recommend a handle-based solution, although MRE can be used to solve it (MREs can be used to solve almost anything ...).



C ++ has no concept of "events". The traditional approach is to use a callback function pointer (which is combined with Boost.Bind

and is Boost.Function

not that painful these days). A more modern approach would be Boost.Signals2

.

+3


source


Update. There was a logical flaw: to use a C callback, there must be one C process and multiple C # processes. But with MMF only memory is split into B / W 2 different C processes. So I need either a C server like Redis (but we already have Redis!) Or interprocess signaling.

I had experience with reliable sync and Redis and that was the root of the issue - I wanted a solution like this, but for a local machine with persistence.

Rewriting the Redis logic with the named MREs / AREs is probably the simplest approach. As Stephen said, MRE can do something.


(any votes should go to Stephen Cleary's answer, here's just an example)

The answer is to use a TaskCompletionSource and a callback to set its result.



Callback example:

FROM#:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int CompareCallback(int a, int b);

[SuppressUnmanagedCodeSecurity]
class NativeMethod
{

    [DllImport("CppDynamicLinkLibrary.dll", CharSet = CharSet.Auto)]
    public static extern int CompareInts(int a, int b, CompareCallback cmpFunc);

}

      

FROM

// Type-definition: 'PFN_COMPARE' now can be used as type
typedef int (CALLBACK *PFN_COMPARE)(int, int);

// An exported/imported stdcall function using a DEF file
// It requires a callback function as one of the arguments
int __stdcall CompareInts(int a, int b, PFN_COMPARE cmpFunc)
{
    // Make the callback to the comparison function

    // If a is greater than b, return a; 
    // If b is greater than or equal to a, return b.
    return ((*cmpFunc)(a, b) > 0) ? a : b;
}

      

example code: https://code.msdn.microsoft.com/windowsdesktop/CSPInvokeDll-b05779d0

0


source







All Articles