Menu

#2314 CONDITION_VARIABLE support

WSL
pending
nobody
headers (2)
Feature
limbo
No_User_Response
False
2017-07-31
2016-09-14
No

It would be nice if there were condition variables (CONDITION_VARIABLE) support in win32 headers and import libraries. Condition variables are supported in Windows Vista and newer.

API info at http://msdn.microsoft.com/en-us/library/ms682052%28VS.85%29.aspx

This feature request is related with this other request implemented and closed but for MinGW-w64: https://sourceforge.net/p/mingw-w64/feature-requests/29/

Discussion

  • Keith Marshall

    Keith Marshall - 2016-09-15
    • status: unread --> pending
     
  • Keith Marshall

    Keith Marshall - 2016-09-15

    Looks like the necessary hooks are already present in libkernel32.a:

    $ nm -A w32api/libkernel32.a | grep ConditionVariable
    w32api/libkernel32.a:dchcs01232.o:00000000 I __imp__WakeConditionVariable@4
    w32api/libkernel32.a:dchcs01232.o:00000000 T _WakeConditionVariable@4
    w32api/libkernel32.a:dchcs01231.o:00000000 I __imp__WakeAllConditionVariable@4
    w32api/libkernel32.a:dchcs01231.o:00000000 T _WakeAllConditionVariable@4
    w32api/libkernel32.a:dchcs01154.o:00000000 I __imp__SleepConditionVariableSRW@16
    w32api/libkernel32.a:dchcs01154.o:00000000 T _SleepConditionVariableSRW@16
    w32api/libkernel32.a:dchcs01153.o:00000000 I __imp__SleepConditionVariableCS@12
    w32api/libkernel32.a:dchcs01153.o:00000000 T _SleepConditionVariableCS@12
    w32api/libkernel32.a:dchcs00733.o:00000000 I __imp__InitializeConditionVariable@4
    w32api/libkernel32.a:dchcs00733.o:00000000 T _InitializeConditionVariable@4
    

    so, the missing piece of the jigsaw is appropriately guarded declarations in include/winbase.h. Unfortunately, MSDN doesn't seem to give us any indication of how CONDITION_VARIABLE should be typedef'd, but an opaque pointer may suffice, in which case we could add:

    #if _WIN32_WINNT >= _WIN32_WINNT_VISTA
    typedef PVOID SRWLOCK, *PSRWLOCK;
    void WINAPI InitializeSRWLock (PSRWLOCK);
    void WINAPI AcquireSRWLockExclusive (PSRWLOCK);
    void WINAPI AcquireSRWLockShared (PSRWLOCK);
    #if _WIN32_WINNT >= _WIN32_WINNT_WIN7
    BOOLEAN WINAPI TryAcquireSRWLockExclusive (PSRWLOCK);
    BOOLEAN WINAPI TryAcquireSRWLockShared (PSRWLOCK);
    #endif
    void WINAPI ReleaseSRWLockExclusive (PSRWLOCK);
    void WINAPI ReleaseSRWLockShared (PSRWLOCK);
    
    typedef PVOID CONDITION_VARIABLE, *PCONDITION_VARIABLE;
    void WINAPI InitializeConditionVariable (PCONDITION_VARIABLE);
    BOOL WINAPI SleepConditionVariableCS (PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD);
    BOOL WINAPI SleepConditionVariableSRW (PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG);
    void WINAPI WakeAllConditionVariable (PCONDITION_VARIABLE);
    void WINAPI WakeConditionVariable (PCONDITION_VARIABLE);
    #endif
    

    (Don't ask me to explain the BOOL vs. BOOLEAN return value discrepancies ... it's just another example of Microsoft's ineptitude in providing a consistent API design).

    If you paste that into your source, (after including <windows.h>, and bracketting it in _BEGIN_C_DECLS ... _END_C_DECLS, if you need it to compile as C++), are you able to achieve your objective?

     

    Last edit: Keith Marshall 2016-09-15
  • Keith Marshall

    Keith Marshall - 2017-02-01

    If you cannot be bothered to test, and comment as requested, I will reject this request! I'll give you until the end of February-2017, to furnish a suitable response.

     
  • Keith Marshall

    Keith Marshall - 2017-04-12
    • status: pending --> closed
    • Resolution: none --> limbo
    • Category: Unknown --> No_User_Response
     
  • Keith Marshall

    Keith Marshall - 2017-05-29
    • status: closed --> pending
     
  • Keith Marshall

    Keith Marshall - 2017-05-29

    Reopened, as pending, following an expression of interest by another user.

     
  • Hans Baumann

    Hans Baumann - 2017-05-29

    In my current project the support for CONDITION_VARIABLEs would be of great help. I added the above suggested codelines to my project and it worked.
    I also wrote a test-programm (attached to this post) which spawns 4 threads and syncs via a mutex (CRITICAL_SECTION) and a condition (CONDITION_VARIABLE).

     
    • Keith Marshall

      Keith Marshall - 2017-06-03

      Thanks. The test case was useful, although it did raise one issue of coding style ... never use mixed case when referring to system header files: #include <Windows.h> becomes a fatal error when the file system is case-sensitive, (as it is for my Linux hosted cross development environment). That aside, I applied [94803e] and [5a6bfc], and reduced your test case to:

      #define _WIN32_WINNT _WIN32_WINNT_WIN7
      
      #include <windows.h>
      #include <stdio.h>
      
      struct ThreadData
      { int                    id;
        volatile int          *sync;
        CRITICAL_SECTION      *mutex;
        CONDITION_VARIABLE    *cond;
      };
      
      void *ThreadFunction (void *MyThreadData)
      {
        struct ThreadData *MyThreadGlobalData = (struct ThreadData *)(MyThreadData);
      
        fprintf(stderr, "Thread %d: Starting ...\n", MyThreadGlobalData->id);
        if ((MyThreadGlobalData->id & 1) == 1)
          { fprintf(stderr, "Thread %d: TryEnterCriticalSection\n", MyThreadGlobalData->id);
            while (!TryEnterCriticalSection(MyThreadGlobalData->mutex));
          }
        else
          { fprintf(stderr, "Thread %d: EnterCriticalSection\n", MyThreadGlobalData->id);
            EnterCriticalSection(MyThreadGlobalData->mutex);
          }
        fprintf(stderr, "Thread %d: CriticalSection acquired\n", MyThreadGlobalData->id);
        Sleep(1000);
      
        /* thread has exclusive access to sync counter */ 
      
        while (*MyThreadGlobalData->sync < MyThreadGlobalData->id)
          { fprintf(stderr, "Thread %d: SleepConditionVariableCS\n", MyThreadGlobalData->id);
            SleepConditionVariableCS(MyThreadGlobalData->cond, MyThreadGlobalData->mutex, INFINITE);
          }
        fprintf(stderr, "Thread %d: Increment synchronization counter to %d\n",
            MyThreadGlobalData->id, *MyThreadGlobalData->sync += 1
          );
        WakeAllConditionVariable(MyThreadGlobalData->cond);
        fprintf(stderr, "Thread %d: LeaveCriticalSection\n", MyThreadGlobalData->id);
        LeaveCriticalSection(MyThreadGlobalData->mutex);
        return NULL;
      }
      
      int main()
      { int i; volatile int sync = 0;
        CRITICAL_SECTION mutex; CONDITION_VARIABLE cond;
      
        if (!InitializeCriticalSectionAndSpinCount(&mutex, 0x00000400))
          { fprintf(stderr, "ERROR: InitializeCriticalSectionAndSpinCount - %lu!\n", GetLastError());
            exit(1);
          }
        InitializeConditionVariable(&cond);
      
        HANDLE threads[4];
        struct ThreadData myData[4];
      
        EnterCriticalSection(&mutex);
      
        for (i = 0; i < 4; ++i)
          { myData[i].id = i;
            myData[i].sync = &sync;
            myData[i].mutex = &mutex;
            myData[i].cond = &cond;
      
            threads[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFunction, &myData[i], 0, NULL);
            if (threads[i] == NULL)
              { fprintf(stderr, "ERROR: CreateThread - %lu!\n", GetLastError());
                exit(1);
              }
          }
      
        while (sync < 3)
          { SleepConditionVariableCS(&cond, &mutex, INFINITE); /* mutex now unlocked */
      
            /* mutex locked again */
          }
        LeaveCriticalSection(&mutex);
      
        for (i = 0; i < 4; ++i)
          { DWORD status = WaitForSingleObject(threads[i], INFINITE);
            if (status != WAIT_OBJECT_0)
              { fprintf(stderr, "ERROR: WaitForSingleObject - %lu!\n", GetLastError());
                exit(1);
              }
            CloseHandle(threads[i]);
          }
        DeleteCriticalSection(&mutex);
        return 0;
      }
      

      Obviously, that fails when run on my WinXP VM, (because the CONDITION_VARIABLE API is unsupported), but on a Win7 VM it produces:

      $ ./foo.exe
      Thread 0: Starting ...
      Thread 0: EnterCriticalSection
      Thread 0: CriticalSection acquired
      Thread 1: Starting ...
      Thread 1: TryEnterCriticalSection
      Thread 2: Starting ...
      Thread 2: EnterCriticalSection
      Thread 3: Starting ...
      Thread 3: TryEnterCriticalSection
      Thread 0: Increment synchronization counter to 1
      Thread 0: LeaveCriticalSection
      Thread 2: CriticalSection acquired
      Thread 2: SleepConditionVariableCS
      Thread 1: CriticalSection acquired
      Thread 1: Increment synchronization counter to 2
      Thread 1: LeaveCriticalSection
      Thread 2: Increment synchronization counter to 3
      Thread 2: LeaveCriticalSection
      Thread 3: CriticalSection acquired
      Thread 3: Increment synchronization counter to 4
      Thread 3: LeaveCriticalSection
      

      which (I think) represents a successful outcome, for the case of condition variables used to synchronize access to critical sections; I guess we should also try to adapt the test, to cover the case of synchronization across slim reader/writer locks.

       

      Related

      Commit: [5a6bfc]
      Commit: [94803e]


      Last edit: Keith Marshall 2017-06-03
  • Keith Marshall

    Keith Marshall - 2017-06-16

    In reply to private e-mail:

    On 16/06/17 13:39, Johann Baumann wrote:

    Sorry for the late reply. I was on vacation.

    No problem, but please keep all relevant correspondence on the
    tracker ticket; it tends to get lost otherwise.

    I could rewrite the test-case to output a more
    obvious 'good' reply :)

    Thanks. Ultimately I'd like to implement it within a GNU autotest
    framework; that requires all output to stdout and stderr to be 100%
    deterministically reproducible. I suspect that some of the thread
    start-up messages, as currently implemented, may not comply.

    Furthermore i can also write a r/w-lock testcase.
    Should/could be ready in a few days.

    Thanks. Once again, please attach it to the ticket.

     

    Last edit: Keith Marshall 2017-06-16
  • Hans Baumann

    Hans Baumann - 2017-06-27

    I rewrote 'test.c' to give a deterministic output. Furthermore i wrote a super-silly 'test2.c' to
    check the SRWLocks. Both work and give the desired result :)

     
    • Keith Marshall

      Keith Marshall - 2017-06-27

      Thanks! I will take a look, but it may be a few weeks before I can get around to it. In the meantime, I may go ahead and include the implementation in an interim release, albeit lacking a formal test case.

       
  • Keith Marshall

    Keith Marshall - 2017-07-31

    The proposed implementation is now included in the recently published WSL-5.0.1; the accompanying testsuite component remains outstanding.

     
MongoDB Logo MongoDB