locked
IOCompletionPort causes unmanaged host process to crash RRS feed

  • Question

  • Hi,
    I have a problem with a process crash, probably due to memory corruption:

    I have a Delphi process that loads .NET dlls, one in C# and another in C++/CLI.
    I have built a mechanism to send ashynchronous IOCtls to several devices in the kernel driver from the C# dll, via the C++/CLI layer, using an IOCompletionPort. these devices will signal events that would be handled in the C#, and again IOCtls will be sent down to the Kernel endlessly.

    this was supposed to work like this:

    In each device I have a buffer structure which is similar in the UserMode and Kernel that the C# declares here: 
    /// <summary> 
    /// Native - Get events output buffer  
    /// </summary> 
    [StructLayout(LayoutKind.Sequential, CharSet CharSet = CharSet.Ansi)]  
    public unsafe struct NativeGetEventsOutBuffer  
    {  
    /// <summary> 
    /// Number of words in events array  
    /// </summary> 
    public UInt32 NumOfWordsToRead;  
    /// <summary> 
    /// Array that hold all the events  
    /// </summary> 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_NUMBER_OF_EVENTS_IN_REQUEST)]
    public UInt32[] Events;  
    /// <summary> 
    /// Get events status  
    /// </summary> 
    public UInt32 status;  
    /// <summary> 
    /// Tick count  
    /// </summary> 
    public UInt32 TickCount;  
    }  
     

    this buffer will be allocated with Marshal.AllocHGlobal and the pointer referencing it, mGetEventsFromDeviceOutBuffer, is a data member for each device in the UserMode.

    I start an IOCompletion port with as many threads as the number of processors on machine:

            /// <summary>  
            /// Initialization of the IO completion port for the get events mechanism.  
            /// </summary>  
            private void InitializeIOCompletionPortForGetEvents()  
            {  
                try 
                {  
                    int index;  
                    int key;  
                    // Calculate the number of processors to determine the number of concurrent threads  
                    mNumberOfGetEventsThreads = Environment.ProcessorCount;  
                     
                    // Initialize the thread queue  
                    mGetEventsQueueThreads = new Thread[mNumberOfGetEventsThreads];  
     
                    // Create the IOCompletion Port with all the Boards  
                    for (key= 0; key< mNumOfDevicesConfigured; key++)  
                    {  
                            unsafe 
                            {  
                                mIOCompHandleForGetEvents = (IntPtr)(IOCompletionPortObject.CreateIoCompletionPort(pDevicePendingHandle.ToPointer(),  
                                mIOCompHandleForGetEvents.ToPointer(),
                                (UInt32)key,
                                (UInt32)mNumberOfGetEventsThreads));  
                            }                                     
                    }  
                    // Create the Threads for the IOCompletion port  
                    for (index = 0; index < mNumberOfGetEventsThreads; index++)  
                    {  
                        mGetEventsQueueThreads[index] = new Thread(RunGetEvents);  
                        mGetEventsQueueThreads[index].Start();  
     
                    }  
     
                    // Send IOCTL for get events to each of the Boards (for the first time)  
                    for (key= 0; key< mNumOfDevicesConfigured; key++)  
                    {  
                        
                            Device.GetEventsFromKernelDriver(key);  
                    }  
                }  
                catch (Exception e)  
                {  
                    WriteLog("InitializeIOCompletionPortForGetEvents",  
                                            MT_ERROR, "Error Message: " + e.Message);  
                    WriteLog("InitializeIOCompletionPortForGetEvents",  
                                            MT_ERROR, "Stack Trace: " + e.StackTrace);  
                    return;  
                }  
            } 

    on the RunGetEvents function, I am waiting to a signal on the key: 

    status = IOCompletionPortObject.GetQueuedCompletionStatus   
                 (
    mIOCompHandleForGetEvents.ToPointer(),
                 &
    NumberOfBytesTransffered, &key, &lpOverlapped,
                
    unchecked((uint)Timeout.Infinite));

    Than I handle the current Event in HandleEvents function, and send GetEventsFromKernelDriver to the same device. in these Get events function, I allocate the abovemantioned buffer reference and send an asynchronous IOCtl to the driver:

    NativeGetEventsOutBuffer  temp = new NativeGetEventsOutBuffer ();
    temp
    .Events = new uint[Nati3Def.NATI3_MAX_NUMBER_OF_EVENTS_IN_REQUEST];
    UInt32 DriverStatus = VoiceCapExitStatus.STAT_VOICECAP_NATI3_OK;
    bool Result;
    Unsafe
    {
       mGetEventsFromDeviceOutBuffer
    = Marshal.AllocHGlobal(Marshal.SizeOf(temp));
       Result = CPPCLILayer.SendGetEventsIOCtl(mDevicePendingHandle, DeviceId,
                mGetEventsFromDeviceOutBuffer ,
                IntPtr.Zero, // no need for event in overlapped struct
                ref DriverStatus);
    }

    I free the allocated memory at the end of the event Handling function HandleEvents:
    Marshal.FreeHGlobal(mGetEventsFromDeviceOutBuffer );

    The kernel module fills the buffer and signals a completion (with no errors).

    When I run this on a single threaded machine (with win2008) I can handle events and send IOCtls to all devices with no problem.
    When I run this on a dual threaded macine, after a while the Delphi porcess that hosts this threads is crashing. although all of my .NET code is wrapped with try-catch, I don't see exceptions, except for a few times I saw the exception: Trying to read or write protected memory (somtimes indication of memory corruption), but always on different places.

    I sometimes see in the event log:  .NET Runtime version 2.0.50727.1434 - Fatal Execution Engine Error (79FFEE24) (80131506),  but again not all of the times.

    I have tried catching an unhandledException but could only catch it once every few runs, and even than the process crashed after a few seconds.
    I can also say that the handling function is not the problem cause marking it didn't prevent the problem.
    I have also tried to pin the allocated buffer using GCHandle but that didn't work also.

    I am sorry this turn out that long but I have to say that this bug is the most time consuming I have ever expereinced...

    please
    help...

    Thanks.

    Sunday, March 8, 2009 3:58 PM

All replies

  • Could you post a single, complete code sample without highlighting?

    I tried following your post several times but it makes my head hurt. If you post just the code, I'll take a look.

           -Steve
    Tuesday, March 17, 2009 3:15 PM