2

Why does the same code in .Net (C#) hangs, but in C++ it isn't? The really problem is in System.IO.FileStream, but I reduced it down to CreateFile, nevertheless it still hangs in .Net (while being almost 100% analogue to C++ code):

using System;
using System.Runtime.InteropServices;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static IntPtr _handle;

        static void Main()
        {
            _handle = CreateFile("test.txt", GENERIC_READ | GENERIC_WRITE,
                FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero,
                OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

            new Thread(MyProc) { IsBackground = true }.Start();
            new Thread(MyProc) { IsBackground = true }.Start();

            Console.ReadKey();
        }

        static void MyProc()
        {
            for (int i = 0; i < int.MaxValue; i++)
            {
                Console.WriteLine(i);

                var _overlapped = new NativeOverlapped();

                LockFileEx(_handle, LOCKFILE_EXCLUSIVE_LOCK, 0, int.MaxValue, int.MaxValue, ref _overlapped);

                Thread.Sleep(50);

                UnlockFileEx(_handle, 0, int.MaxValue, int.MaxValue, ref _overlapped);
            }
        }

        public const int LOCKFILE_EXCLUSIVE_LOCK = 0x00000002;

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool LockFileEx(IntPtr hFile, int flags, int reserved, int numberOfBytesToLockLow, int numberOfBytesToLockHigh,
            ref NativeOverlapped overlapped);

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool UnlockFileEx(IntPtr hFile, int reserved, int numberOfBytesToUnlockLow, int numberOfBytesToUnlockHigh,
            ref NativeOverlapped overlapped);

        public const int GENERIC_READ = unchecked((int)0x80000000L);
        public const int GENERIC_WRITE = (int)0x40000000L;

        public const int OPEN_ALWAYS = 4;

        public const int FILE_SHARE_READ = 0x00000001;
        public const int FILE_SHARE_WRITE = 0x00000002;
        public const int FILE_SHARE_DELETE = 0x00000004;

        public const int FILE_ATTRIBUTE_NORMAL = 0x00000080;

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern IntPtr CreateFile(string fileName, int desiredAccess, int shareMode,
            IntPtr pSecurityAttributes, int creationDisposition, int flagsAndAttributes, IntPtr hTemplateFile);
    }
}

And here is C++ version which do not hang:

#include "stdafx.h"

#include <thread>
#include <Windows.h>

HANDLE hFile;

DWORD WINAPI MyProc(LPVOID lpParam);

int _tmain(int argc, _TCHAR* argv[])
{
    hFile = CreateFile(L"test.txt", GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  

    HANDLE hThread1 = CreateThread(NULL, 0, MyProc, NULL, 0, NULL);
    HANDLE hThread2 = CreateThread(NULL, 0, MyProc, NULL, 0, NULL);

    int ch = getchar();

    return 0;
}

DWORD WINAPI MyProc(LPVOID lpParam)
{
    for(int i = 0; i < INT_MAX; i++)
    {
        printf("%d\r\n", i);

        OVERLAPPED overlapped = {0};

        LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, INT_MAX, INT_MAX, &overlapped);

        Sleep(50);

        UnlockFileEx(hFile, 0, INT_MAX, INT_MAX, &overlapped);
    }

    return 0;
}
frtnum
  • 177
  • 14
  • 2
    Downvoters, please explain! – frtnum May 17 '15 at 10:19
  • It seems that fact of hanging depends on something in configuration. My is such one: Net 4.5, VS 2012 Express, Win7 64bit, i7-3610QM. .Net variant hangs 100% with two zeroes in console. Very rarely it goes to two tens and then still hangs. – frtnum May 17 '15 at 10:45
  • 1
    Did you try to pause and check where each thread is located when it hangs using Debug->Windows->Threads? (Assuming you are using Visual Studio) – o_weisman May 17 '15 at 11:13
  • @o_weisman Yes. Thread1 is on the LockFileEx, Thread2 is on the UnlockFileEx. I tried to delete the file, arbitrary change lock file range and rebuild project - now my demo project works, but not so straight - after the same actions it hangs again. My work project (which use FileStream) still constantly have the problem, despite of rebuilding and file deletion. Don't even guess what could it be, examining further. – frtnum May 17 '15 at 11:23
  • @Micky Duncan int.MaxValue in C# doesn't depend on x86/x64, but it seems to me that problem is somewhere in this area. Have to investigate it further. Will write the results later. It will be interesting to hear that anyone have the same problem (as it was very surprise for me). – frtnum May 17 '15 at 11:29
  • Whoops my bad, you are correct –  May 17 '15 at 11:31
  • 1
    Why the C# program behaves differently from the C++ program is generally simple to explain, deadlock is highly timing-sensitive and of course the timing isn't the same. You however cannot ignore the elephant in the room, this code is never supposed to deadlock. Also very, very unlikely that anybody else can repro it. You'll need to get your machine fixed, that always start at the kind of shrink-wrapped malware that programmers voluntary install on their machine. Disable your anti-malware scanner and any cloud storage utilities like Dropbox etc. – Hans Passant May 17 '15 at 11:40
  • Unable to reproduce sadly. As Hans said. I wish you well –  May 17 '15 at 11:45
  • Here is my screencast of this nightmare :( http://www.screencast.com/users/frtnum/folders/Jing/media/caf86150-5a52-448e-a8cd-9824c7ffbbb4 – frtnum May 17 '15 at 12:00

1 Answers1

0

It looks like it's invalid to call LockFileEx with LOCKFILE_EXCLUSIVE_LOCK on the same file handle twice. It have to be different file handles or some kind of in-process synchronization for the handle.

The disturbing thing is that there is no unambiguity for the question in MSDN documentation. And there is no clear message of error at runtime. Even more, it seems to me that the problem reproduced only on my computer. This is ugly some kind.

frtnum
  • 177
  • 14
  • I wonder who down vote this right answer? I surely confirm that the problem is in the calling LockFileEx(LOCKFILE_EXCLUSIVE_LOCK) on the same file handle twice (and more). My work project, as I added in-process synchronization between threads of file handle sharing, now work stable. As before it stable hanged. So, downvoters, please explain something! What is the cause of your downvote? I'm totally shocked with such yours behavior. – frtnum May 17 '15 at 17:53
  • The problem is that it has to be clearly stated in the LockFileEx documentation, and to be clear runtime error, and not the probability thing but be determinated. – frtnum May 17 '15 at 17:55