Question

I need to implement the same functionality as this function on Win7 x64.

I initially used SwitchToThread() but this doesn't work as it causes a deadlock under extreme conditions. The only alternative I can find is Sleep() yet this is likely to be a performance killer as it only work on millisecond resolution and I'm still not sure it does the same thing as LockSupport.parkNanos().

I found Java's ability to schedule (if that is what happens) threads on a nanosecond interval suspicious so I implemented what I could only assume they do... spin. However I'm not sure this solves the problem, it may be merely delaying the inevitable as it seems the Java function requires the intervention of the JVM to work. No source code for parkNanos is available; it's implemented in a native Sun library.

class LockSupport
{
public:
    static void ParkNanos(unsigned __int64 aNanos)
    {
        ULONGLONG start;
        ULONGLONG end;

        ::QueryUnbiasedInterruptTime(&start);
        do
        {
            // My issue with this is that nothing is actually 'Parked'.
            ::SwitchToThread();
            ::QueryUnbiasedInterruptTime(&end);
        }
        while ((end - start) < aNanos);
    }
};

The calling code looks like this:

void SomeClass::SomeFunction()
{
    while (someCond)
    {
        LockSupport.parkNanos(1L);
    }
}

FWIW, I am porting LMAX's Disruptor pattern to C++. The deadlock is happening when one thread is in SingleThreadedClaimStrategy::WaitForFreeSlotAt() and another is in BlockingWaitStrategy::WaitFor (no timeout). The deadlock is more apparent when the RingBuffer size is tiny... 1, 2, 4, 8 etc.

The threads are created by the normal CreateThread means.

Edit: It was quite late when I wrote this so here's some more information. The RingBuffer holds __int64s. I have one Producer thread and one Consumer thread. The Consumer thread also spawns off a Timer thread that polls the Consumer every second for the sequence number of the event it last consumed. There comes a point when the Consumer makes no progress and the Producer hasn't finished either. The Producer merely runs in a loop a few hundred million times publishing a counter. So my output looks something like this:

898
97
131
Timer: no progress
Timer: no progress
...

It's only really reproducible in Release Mode, with everything optimised for speed.

Was it helpful?

Solution

Besides the ability to unpark() a thread, LockSupport.parkNanos(...) is nothing more than a sleep. In the OpenJDK Hotspot VM on Windows, it's implemented (line 4436) using WaitForSingleObject(...), and sleeps for a minimum of 1ms.

LMAX disruptor doesn't ever seem to unpark() threads. Consequently, you should get equivalent behavior by calling Sleep(1). You could possibly do better with Sleep(0): you give up the rest of your time slice in the current thread, and become available for rescheduling immediately. This is equivalent to SwitchToThread() with the exception that the latter may simply tell you "nothing ready to run yet, so you can keep the cpu." On the other hand, Sleep(1) may actually pause for 1 ms if your scheduling granularity is sufficiently low.

In the remarks to Sleep() it is noted that you can improve the scheduling granularity of your system (possibly down to as little as 1ms per tick) by invoking timeBeginPeriod().

OTHER TIPS

No source code for parkNanos is available; it's implemented in a native Sun library.

Source code for that native library should be part of the OpenJDK 6 / 7 source code, and should therefore be available for download or browsing.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top