Welcome to MSDN Blogs Sign in | Join | Help

ThreadPool.BindHandle

I mentioned that we can use ThreadPool.BindHandle to implement asynchronous IO. Here are roughly the steps necessary to make it happen:

1.       Create an overlapped file handle

            SafeFileHandle handle = CreateFile(

                                filename,

                                Win32.GENERIC_READ_ACCESS,

                                Win32.FILE_SHARE_READ | Win32.FILE_SHARE_WRITE | Win32.FILE_SHARE_DELETE,

                                (IntPtr)null,

                                Win32.OPEN_EXISTING,

                                Win32.FILE_FLAG_OVERLAPPED,

                                new SafeFileHandle(IntPtr.Zero, false));

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]

        private static extern SafeFileHandle CreateFile(

           string lpFileName,

           uint dwDesiredAccess,

           uint dwShareMode,

            //SECURITY_ATTRIBUTES lpSecurityAttributes,

           IntPtr lpSecurityAttributes,

           uint dwCreationDisposition,

           int dwFlagsAndAttributes,

           SafeFileHandle hTemplateFile);

2.       Bind the handle to thread pool.

            if (!ThreadPool.BindHandle(handle))

            {

                Console.WriteLine("Fail to BindHandle to threadpool.");

                return;

        }

3.       Prepare your asynchronous IO callback.

                byte[] bytes = new byte[0x8000];

 

                IOCompletionCallback iocomplete = delegate(uint errorCode, uint numBytes, NativeOverlapped* _overlapped)

                {

                    unsafe

                    {

                        try

                        {

                            if (errorCode == Win32.ERROR_HANDLE_EOF)

                                Console.WriteLine("End of file in callback.");

 

                            if (errorCode != 0 && numBytes != 0)

                            {

                                Console.WriteLine("Error {0} when reading file.", errorCode);

                            }

                            Console.WriteLine("Read {0} bytes.", numBytes);

                        }

                        finally

                        {

                            Overlapped.Free(pOverlapped);

                        }

                    }

                };   

 

4.       Create a NativeOverlapped* pointer.

                    Overlapped overlapped = new Overlapped();

 

                    NativeOverlapped* pOverlapped = overlapped.Pack(iocomplete, bytes);

 

                pOverlapped->OffsetLow = (int)offset;

5.       Call the asynchronous IO API and pass the NativeOverlapped * to it.

                    fixed (byte* p = bytes)

                    {

                        r = ReadFile(handle, p, bytes.Length, IntPtr.Zero, pOverlapped);

                        if (r == 0)

                        {

                            r = Marshal.GetLastWin32Error();

                            if (r == Win32.ERROR_HANDLE_EOF)

                            {

                                Console.WriteLine("Done.");

                                break;

                            }

 

                            if (r != Win32.ERROR_IO_PENDING)

                            {

                                Console.WriteLine("Failed to read file. LastError is {0}", Marshal.GetLastWin32Error());

                                Overlapped.Free(pOverlapped);

                                return;

                            }

                        }

                    }

 

        [DllImport("KERNEL32.dll", SetLastError = true)]

        unsafe internal static extern int ReadFile(

            SafeFileHandle handle,

            byte* bytes,

            int numBytesToRead,

            IntPtr numBytesRead_mustBeZero,

            NativeOverlapped* overlapped);

 

Your IO callback will be invoked by CLR thread when the IO completed.

 

So when should you use ThreadPool.BindHandle? The answer is almost *Never*. .Net Framework's FileStream class internally uses ThreadPool.BindHandle to implement the async IO. You should always use FileStream if possible.

Published Monday, December 01, 2008 8:00 AM by junfeng
Filed under:

Comments

# infoblog » ThreadPool.BindHandle

Monday, December 01, 2008 11:12 AM by infoblog » ThreadPool.BindHandle

# re: ThreadPool.BindHandle

Tuesday, December 02, 2008 4:34 PM by Jelle Hissink

In step 5 when you leave the fixed {} block the p is free to be moved by the GC. Doesn't this buffer needs to be allocated using Marshall.MAlloc and freed after the read has finished?

Regards

# re: ThreadPool.BindHandle

Wednesday, December 03, 2008 7:21 AM by JM

@Jelle: you're quite right. Using Marshal every time is slow, though. A better approach is to allocate a GCHandle with type GCHandleType.Pinned, which will prevent the GC from moving the array (and collecting it, incidentally).

# re: ThreadPool.BindHandle

Wednesday, December 03, 2008 2:54 PM by Jelle Hissink

JM,

Thanks for the pointer tot the GCHandle, hadn't seen that class before. Till now I always try to allocate a buffer using Marshal.MAlloc and reuse it for multiple reads.

# re: ThreadPool.BindHandle

Wednesday, December 03, 2008 5:03 PM by junfeng

In step 4,

                   NativeOverlapped* pOverlapped = overlapped.Pack(iocomplete, bytes);

This statement implicitly pins the bytes object. The object is unpinned when the overlap is freed.

# re: ThreadPool.BindHandle

Thursday, December 04, 2008 11:07 AM by Jelle Hissink

junfeng,

thanks for pointing it out, I must have somehow missed that while reading the documentation.

# Multithreading: I/O and the thread pool

Thursday, June 04, 2009 9:10 AM by LA.NET [EN]

When we started looking at how we could use the thread pool for asynchronous work, I (only!) mentioned

# Multithreading: I/O and the thread pool

Thursday, June 04, 2009 9:22 AM by ASPInsiders

When we started looking at how we could use the thread pool for asynchronous work, I (only!) mentioned

New Comments to this post are disabled
 
Page view tracker