winpr/synch: add spinning support to barrier
authorNorbert Federa <norbert.federa@thincast.com>
Fri, 3 Jun 2016 22:55:27 +0000 (00:55 +0200)
committerNorbert Federa <norbert.federa@thincast.com>
Fri, 3 Jun 2016 22:55:27 +0000 (00:55 +0200)
Implemented the SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY and
SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY flags.

winpr/libwinpr/synch/barrier.c
winpr/libwinpr/synch/test/TestSynchBarrier.c

index bcb03ad..dda1e3f 100644 (file)
@@ -175,21 +175,46 @@ BOOL WINAPI EnterSynchronizationBarrier(LPSYNCHRONIZATION_BARRIER lpBarrier, DWO
 
        if (remainingThreads > 0)
        {
-               /* TODO: add spincount support */
-               WaitForSingleObject(hCurrentEvent, INFINITE);
+               DWORD dwProcessors = lpBarrier->Reserved4;
+               BOOL spinOnly = dwFlags & SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY;
+               BOOL blockOnly = dwFlags & SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY;
+               BOOL block = TRUE;
+
+               /**
+                * If SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY is set we will
+                * always spin and trust that the user knows what he/she/it
+                * is doing. Otherwise we'll only spin if the flag
+                * SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY is not set and
+                * the number of remaining threads is less than the number
+                * of processors.
+                */
+
+               if (spinOnly || (remainingThreads < dwProcessors && !blockOnly))
+               {
+                       DWORD dwSpinCount = lpBarrier->Reserved5;
+                       DWORD sp = 0;
+                       /* we spin until the last thread _completed_ the event switch */
+                       while ((block = (lpBarrier->Reserved3[0] == (ULONG_PTR)hCurrentEvent)))
+                               if (!spinOnly && ++sp > dwSpinCount)
+                                       break;
+               }
+
+               if (block)
+                       WaitForSingleObject(hCurrentEvent, INFINITE);
+
                return FALSE;
        }
 
-       /* switch events */
-       lpBarrier->Reserved3[0] = (ULONG_PTR)hDormantEvent;
-       lpBarrier->Reserved3[1] = (ULONG_PTR)hCurrentEvent;
-
        /* reset the dormant event first */
        ResetEvent(hDormantEvent);
 
        /* reset the remaining counter */
        lpBarrier->Reserved1 = lpBarrier->Reserved2;
 
+       /* switch events - this will also unblock the spinning threads */
+       lpBarrier->Reserved3[1] = (ULONG_PTR)hCurrentEvent;
+       lpBarrier->Reserved3[0] = (ULONG_PTR)hDormantEvent;
+
        /* signal the blocked threads */
        SetEvent(hCurrentEvent);
 
index 4283450..800c452 100644 (file)
@@ -27,7 +27,9 @@ DWORD WINAPI test_synch_barrier_thread(LPVOID lpParam)
        BOOL status = FALSE;
        DWORD i, tnum = InterlockedIncrement(&gThreadCount) - 1;
 
-       printf("Thread #%03u entered.\n", tnum);
+       DWORD dwFlags = *(DWORD*)lpParam;
+
+       //printf("Thread #%03u entered.\n", tnum);
 
        /* wait for start event from main */
        if (WaitForSingleObject(gStartEvent, INFINITE) != WAIT_OBJECT_0)
@@ -36,13 +38,13 @@ DWORD WINAPI test_synch_barrier_thread(LPVOID lpParam)
                goto out;
        }
 
-       printf("Thread #%03u unblocked.\n", tnum);
+       //printf("Thread #%03u unblocked.\n", tnum);
 
        for (i = 0; i < LOOP_COUNT && gErrorCount == 0; i++)
        {
                /* simulate different execution times before the barrier */
                Sleep(rand() % MAX_SLEEP_MS);
-               status = EnterSynchronizationBarrier(&gBarrier, 0);
+               status = EnterSynchronizationBarrier(&gBarrier, dwFlags);
                //printf("Thread #%03u status: %s\n", tnum, status ? "TRUE" : "FALSE");
                if (status)
                        InterlockedIncrement(&gTrueCount);
@@ -50,55 +52,40 @@ DWORD WINAPI test_synch_barrier_thread(LPVOID lpParam)
                        InterlockedIncrement(&gFalseCount);
        }
 out:
-       printf("Thread #%03u leaving.\n", tnum);
+       //printf("Thread #%03u leaving.\n", tnum);
        return 0;
 }
 
 
-int TestSynchBarrier(int argc, char* argv[])
+BOOL TestSynchBarrierWithFlags(DWORD dwFlags)
 {
        HANDLE threads[THREAD_COUNT];
        DWORD dwStatus;
        int i;
 
-       /* Test invalid parameters */
-       if (InitializeSynchronizationBarrier(&gBarrier, 0, -1))
-       {
-               printf("%s: InitializeSynchronizationBarrier unecpectedly succeeded with lTotalThreads = 0\n", __FUNCTION__);
-               return -1;
-       }
+       gThreadCount = 0;
+       gTrueCount   = 0;
+       gFalseCount  = 0;
 
-       if (InitializeSynchronizationBarrier(&gBarrier, -1, -1))
-       {
-               printf("%s: InitializeSynchronizationBarrier unecpectedly succeeded with lTotalThreads = -1\n", __FUNCTION__);
-               return -1;
-       }
+       printf("%s: >> Testing with EnterSynchronizationBarrier flags 0x%08x\n", __FUNCTION__, dwFlags);
 
-       if (InitializeSynchronizationBarrier(&gBarrier, 1, -2))
-       {
-               printf("%s: InitializeSynchronizationBarrier unecpectedly succeeded with lSpinCount = -2\n", __FUNCTION__);
-               return -1;
-       }
-
-
-       /* Functional test */
        if (!InitializeSynchronizationBarrier(&gBarrier, THREAD_COUNT, -1))
        {
                printf("%s: InitializeSynchronizationBarrier failed. GetLastError() = 0x%08x", __FUNCTION__, GetLastError());
                DeleteSynchronizationBarrier(&gBarrier);
-               return -1;
+               return FALSE;
        }
 
        if (!(gStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
        {
                printf("%s: CreateEvent failed with error 0x%08x", __FUNCTION__, GetLastError());
                DeleteSynchronizationBarrier(&gBarrier);
-               return -1;
+               return FALSE;
        }
 
        for (i = 0; i < THREAD_COUNT; i++)
        {
-               if (!(threads[i] = CreateThread(NULL, 0, test_synch_barrier_thread, NULL, 0, NULL)))
+               if (!(threads[i] = CreateThread(NULL, 0, test_synch_barrier_thread, &dwFlags, 0, NULL)))
                {
                        printf("%s: CreateThread failed for thread #%u with error 0x%08x\n", __FUNCTION__, i, GetLastError());
                        InterlockedIncrement(&gErrorCount);
@@ -127,18 +114,53 @@ int TestSynchBarrier(int argc, char* argv[])
        if (gFalseCount != EXPECTED_FALSE_COUNT)
                InterlockedIncrement(&gErrorCount);
 
-       printf("%s: gErrorCount: %d (expected 0)\n", __FUNCTION__, gErrorCount);
+       printf("%s: gErrorCount: %d\n", __FUNCTION__, gErrorCount);
        printf("%s: gTrueCount:  %d (expected %d)\n", __FUNCTION__, gTrueCount, LOOP_COUNT);
        printf("%s: gFalseCount: %d (expected %d)\n", __FUNCTION__, gFalseCount, LOOP_COUNT * (THREAD_COUNT - 1));
 
        if (gErrorCount > 0)
        {
                printf("%s: Error test failed with %d reported errors\n", __FUNCTION__, gErrorCount);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+
+int TestSynchBarrier(int argc, char* argv[])
+{
+       /* Test invalid parameters */
+       if (InitializeSynchronizationBarrier(&gBarrier, 0, -1))
+       {
+               printf("%s: InitializeSynchronizationBarrier unecpectedly succeeded with lTotalThreads = 0\n", __FUNCTION__);
+               return -1;
+       }
+
+       if (InitializeSynchronizationBarrier(&gBarrier, -1, -1))
+       {
+               printf("%s: InitializeSynchronizationBarrier unecpectedly succeeded with lTotalThreads = -1\n", __FUNCTION__);
+               return -1;
+       }
+
+       if (InitializeSynchronizationBarrier(&gBarrier, 1, -2))
+       {
+               printf("%s: InitializeSynchronizationBarrier unecpectedly succeeded with lSpinCount = -2\n", __FUNCTION__);
                return -1;
        }
 
+       /* Functional tests */
+
+       if (!TestSynchBarrierWithFlags(0))
+               return -1;
+
+       if (!TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY))
+               return -1;
+
+       if (!TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY))
+               return -1;
+
        printf("%s: Test successfully completed\n", __FUNCTION__);
 
        return 0;
 }
-