6

I am new to multitasking and IPC and I am trying to construct an approach for fast inter process comunication using shared memory( at first I was researching the IPC term, having in mind wcf sockets and named pipes only to eventually discover about MMF).

Now that I have successfully implemented a small test using shared memory between two processes via using Lock and EventWaitHandle signalling, I am up to an approach that implements non blocking / no-wait pattern. Now, I am trying to combine Thread.MemoryBarrier() & reading a signalling Sector from the MemoryMapedFile.

The problem is unidentified! First round goes through and second was last sited in Bermuda triangle ... out of the scope of the debugger...

say process a is sending a burst of requests for a showMsg() to process b.

                                         //offset positions in mmf
MemoryMappedViewAccessor MmfAcc; const int opReady= 0, opCompleteRead = 4, .....



ReadTrd()
{
  //[0,3] - Reader is stationed
  //[4,7] - Read Complete successfully
  //[8,11] - Data-size 
  //[12,15] - Reader-exiting
  "format" the signals Section (write zeroes). 

   for(;;){if (WrTrd-StepMMF1  Confimed) break;}
  MmfAcc- read DataSize val @offset[8]
  MmfAcc- read Data val @offset[50]
  MmfAcc Write exit to offset....
  ....heavy use of  Thread.MemoryBarrier(); sets !!! (all over the place, on every shared variable...)

}

writeTrd()
{
  heavy use of  Thread.MemoryBarrier() !!!
  //[15-19] - wr is stationed
  //[20-23] - wr Complete successfully
  //[24-27] - wrExiting
  "format" the signals Section . 
   for(;;){if Reader-StepMMF1 is Confim break;}
   MmfAcc- DataSize to offset[8]
   write Data To offset[50] using the method below
   for(;;){if Read StepMMF2 is Confim break;}
 } 

As I was first using the named piped solution, the Mmf approach(despite Lock and EventWaitHandle) was great performance gain compared to the namedpipe approach, but could I even go further using the above approach somehow.. ?

I could just clone this pattern like striping Raid ...

Reader1 + Reader2 & WriteThred1  + WriteThread2

so I tried it and got stuck at that point.

Is this valid approach using the full-memoryfence & sharedmemory for signalling?

If so, all is left is to see why the second iteration failed , the performance difference.

EDIT - added the logic behind the extra threads test

This is the "Bridge" I am using to manipulate writer threads (same approach for readers.

public void Write(byte[] parCurData)
{
    if (ReadPosition < 0 || WritePosition < 0)
        throw new ArgumentException();
    this.statusSet.Add("ReadWrite:-> " + ReadPosition + "-" + WritePosition);
    // var s = (FsMomitorIPCCrier)data;

    ////////lock (this.dataToSend)
    ////////{
    Thread.MemoryBarrier();
        LiveDataCount_CurIndex = dataQue.Where(i => i != null).Count();
    this.dataQue[LiveDataCount_CurIndex] = parCurData;

    Console.WriteLine("^^^^^" + Thread.CurrentThread.Name + " has Entered WritingThreads BRIDGE");
    Console.WriteLine("^^^^^[transactionsQue] = {1}{0}^^^^^[dataQue.LiveDataASIndex = {2}{0}^^^^^[Current Requests Count = {3}{0}", "\r\n", Wtransactions, LiveDataCount_CurIndex, ++dataDelReqCount);

    //this.itsTimeForWTrd2 = false;

    if (Wtransactions != 0 && Wtransactions > ThrededSafeQ_Initial_Capcity - 1)
    if (this.dataQueISFluded) this.DataQXpand();


    if (itsTimeForWTrd2)
    {

        bool firstWt = true;
        while (writerThread2Running)
        {
            if (!firstWt) continue;
            Console.WriteLine("SECOND WRITERThread [2] is In The CoffeeCorner");
                    firstWt=false;
        }

        this.dataDelivery2 = this.dataQue[LiveDataCount_CurIndex];
        Console.WriteLine("Activating SECOND WRITERThread [2]");
        itsTimeForWTrd2 = false;

        writerThread2Running = true;
        //writerThread1Running = true;
        writerThread2 = new System.Threading.Thread(WriterThread2);
        writerThread2.IsBackground = true;
        writerThread2.Name = this.DepoThreadName + "=[WRITER2]";
        writerThread2.Start();

    }
    else
    {
        bool firstWt = true;
        while (writerThread1Running)
        {
            if (!firstWt)continue;
                Console.WriteLine("WRITERThread [1] is In The CoffeeCorner");
            firstWt=false;
        }
        Console.WriteLine("Activating WRITERThread [1]");
        this.dataDelivery1 = this.dataQue[LiveDataCount_CurIndex]; 

        writerThread1Running = true;
        writerThread1 = new System.Threading.Thread(WriterThread1);
        writerThread1.IsBackground = true;
        writerThread1.Name = this.DepoThreadName+"=[WRITER1]";
        writerThread1.Start();
        itsTimeForWTrd2 = true;

    }
    Thread.MemoryBarrier();
}

Using the write handle to read & write the actual data (similar code for the write)

public unsafe byte[] UsReadBytes(int offset, int num)
{
    byte[] arr = new byte[num];
    byte* ptr = (byte*)0;
    this.accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
    Marshal.Copy(IntPtr.Add(new IntPtr(ptr), offset), arr, 0, num);
    this.accessor.SafeMemoryMappedViewHandle.ReleasePointer();
    return arr;
}

As I said, I have researched this issue of synchronization of the data and shared memory via non blocking / no-wait / etc. Semaphores Locks so I am trying to remove any kind of overhead during the process of each transaction of data into the shared memory mapped file. I am here to ask what could be the problem eliminating the Lock And The EventWaitHandle and replacing it with the logic of memory fences and signaling through the mmf?

ProgrammingDude
  • 597
  • 1
  • 5
  • 25
RalfL
  • 109
  • 4
  • 4
    I don't really see why this got three upvotes within a minute (well probably because "ooh cool, memory mapped files"); could you perhaps spend some effort to properly capitalize and apply punctuation to your sentences and try to explain step by step what this code is supposed to do and where it stops doing what you expect? I'm not entirely sure, but I think showing some actual code instead of this pseudocode might also help. – CodeCaster Feb 24 '16 at 16:08
  • @CodeCaster don't be a lemon flavored commenter, try to be positive (:, I will post some more code involving second set of threads I thought it's irrelevant – RalfL Feb 24 '16 at 16:12
  • 1
    No problem, I _think_ you've got quite an interesting question on your hands here, I know a bit about threading and memory-mapped files, it's just that it's very hard to figure out the actual question. That's all. – CodeCaster Feb 24 '16 at 16:25
  • @CodeCaster more like "Cool! lock free programing! we heard its something only experts can do !" – David Haim Feb 24 '16 at 16:46
  • @DavidHaim sorry i'm exhausted ... it's fine... – RalfL Feb 24 '16 at 16:50
  • 5
    This is all rather wrong-headed, you don't need a lock at all. What you really need is an *event*, something that tells you that the other thread has finished writing the data so that you can reliably read it. A waitable event so you don't burn core needlessly waiting for the other thread to get the job done. Readily available in .NET, use a named AutoResetEvent for example. Call its Set() method in the thread that writes, its WaitOne() method in the thread that reads. Making it all reliable without races will keep you busy for a while with very high odds you'll re-invent a named pipe. – Hans Passant Feb 24 '16 at 17:12
  • @HansPassant it's my first go on this but with the locks..didn't try `EventWaitHandle` alone(no-Locks)... but it Sounds promisingcomming from your sugestion...only it is blocking... and has a 1000ms cycle response time i think i read it somewhere (inter-process valid ) takes longer time than slim variants which are not suitable for ipc – RalfL Feb 24 '16 at 17:23
  • 2
    @HansPassant http://blog.benoitblanchon.fr/csharp-concurrency-cheat-sheet/ this is some chart on all overheads – RalfL Feb 24 '16 at 18:05
  • '1000ms cycle response time' - no synchro has a response time that bad, (I hope!). – Martin James Feb 24 '16 at 20:29
  • @ProgrammingDude thanks for editing. – RalfL Feb 25 '16 at 11:52
  • @MartinJames i have Some info concerning timings of every step by now.. Ps. i have eliminated Second Set of threads (no Raid-0 (: ....) – RalfL Feb 25 '16 at 11:55
  • 2
    Premature Optimization is the root... – Fab Mar 06 '16 at 08:58
  • As @Hans pointed out, you are going ahead of yourself slightly here, and I am not sure you understand what `MemoryBarrier` does. You **need** some kind of interprocess synchronization (a `Mutex`, or a named `EventWaitHandle`). But first, what **you** need to do is create an in-process single producer + single consumer concurrent circular FIFO, two threads using an `EventWaitHandle` to signal themselves. Do place your variables inside the same buffer, but keep in mind that multiple threads can access them. When you have **tested** this carefully, it's only a matter of making the buffer a MMF. – vgru Mar 11 '16 at 09:04
  • Also, make sure you understand [r/w atomicity](http://stackoverflow.com/a/23837645/69809) for MMF access for your given architecture. – vgru Mar 11 '16 at 09:06

1 Answers1

0

If you are planning to use this for an specific purpose other than R&D, the easiest way would be to use a library that already provides that. One way to think about this is, Lock Free = Message Passing. Two possible approaches are: For a minimalistic message-passing implementation, ZeroMQ IPC, which provides great .Net support and superb IPC performance (http://www.codeproject.com/Articles/488207/ZeroMQ-via-Csharp-Introduction) and for a more complete Actor-Model implementation (including support for IPC), look at Akka.net (http://getakka.net/docs/#networking).

If the purpose is more R&D in nature (meaning, you want to write your own implementation, which is cool), I would still suggest to look at the source of these products (especially, Akka.net, since it's written in C#), for implementation ideas about Message-passing and Actor-based IPC.