Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

open() on channel fails at runtime in WindowsXP #24208

Open
armed opened this issue Sep 30, 2024 · 0 comments
Open

open() on channel fails at runtime in WindowsXP #24208

armed opened this issue Sep 30, 2024 · 0 comments

Comments

@armed
Copy link

armed commented Sep 30, 2024

Description

This code compiles but fails due to missing function in kernel32.dll in WindowsXP.

import std/typedthreads

var testChan: Channel[string]
testChan.open() # fails here

proc testWorker() {.thread.} =
  let msg = testChan.recv()
  echo "Message received: ", msg

echo "channel opened"

var testThread: Thread[void]
createThread(testThread, testWorker)
testChan.send("Hello, World")

joinThread(testThread)

testChan.close()
echo "channel closed"

Nim Version

2.0.8

Current Output

could not import: InitializeConditionVariable

Expected Output

channel opened
Message received: Hello, World
channel closed

Known Workarounds

With my very limited knowledge I was able to make example above work with a patched version of sts/private/syslocks which uses events (no proper error handling). I'm pretty sure there is a better way to do this:

when defined(windows):
  type
    Handle = int

    SysLock* {.importc: "CRITICAL_SECTION",
              header: "<windows.h>", final, pure, byref.} = object # CRITICAL_SECTION in WinApi
      DebugInfo: pointer
      LockCount: int32
      RecursionCount: int32
      OwningThread: int
      LockSemaphore: int
      SpinCount: int

  when defined(windowsxp):
    type
      SysCond* = object
        hEvent: Handle
  else:
    type
      SysCond* {.importc: "RTL_CONDITION_VARIABLE", header: "<windows.h>", byref.} = object
        thePtr {.importc: "Ptr".} : Handle

  proc initSysLock*(L: var SysLock) {.importc: "InitializeCriticalSection",
                                     header: "<windows.h>".}
    ## Initializes the lock `L`.

  proc tryAcquireSysAux(L: var SysLock): int32 {.importc: "TryEnterCriticalSection",
                                                 header: "<windows.h>".}
    ## Tries to acquire the lock `L`.

  proc tryAcquireSys*(L: var SysLock): bool {.inline.} =
    result = tryAcquireSysAux(L) != 0'i32

  proc acquireSys*(L: var SysLock) {.importc: "EnterCriticalSection",
                                    header: "<windows.h>".}
    ## Acquires the lock `L`.

  proc releaseSys*(L: var SysLock) {.importc: "LeaveCriticalSection",
                                    header: "<windows.h>".}
    ## Releases the lock `L`.

  proc deinitSys*(L: SysLock) {.importc: "DeleteCriticalSection",
                                   header: "<windows.h>".}

  when defined(windowsxp):
    proc CreateEventA(
      lpEventAttributes: pointer, bManualReset: bool, bInitialState: bool, lpName: cstring
    ): Handle {.stdcall, dynlib: "kernel32", importc.}

    proc SetEvent(hEvent: Handle): bool {.stdcall, dynlib: "kernel32", importc.}

    proc WaitForSingleObject(
      hHandle: Handle, dwMilliseconds: int
    ): int {.stdcall, dynlib: "kernel32", importc.}

    proc CloseHandle(hObject: Handle): bool {.stdcall, dynlib: "kernel32", importc.}

    proc ResetEvent(hEvent: Handle): bool {.stdcall, dynlib: "kernel32", importc.}

    proc initSysCond*(cond: var SysCond) =
      cond.hEvent = CreateEventA(nil, false, false, nil)

    proc signalSysCond*(cond: var SysCond) =
      discard SetEvent(cond.hEvent)

    proc waitSysCond*(cond: var SysCond, lock: var SysLock) =
      discard WaitForSingleObject(cond.hEvent, -1'i32)

    proc deinitSysCond*(cond: SysCond) =
      discard CloseHandle(cond.hEvent)

    proc broadcastSysCond*(cond: var SysCond) =
      signalSysCond(cond)
  else:
    proc initializeConditionVariable(
      conditionVariable: var SysCond
    ) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "InitializeConditionVariable".}

    proc sleepConditionVariableCS(
      conditionVariable: var SysCond,
      PCRITICAL_SECTION: var SysLock,
      dwMilliseconds: int
    ): int32 {.stdcall, noSideEffect, dynlib: "kernel32", importc: "SleepConditionVariableCS".}


    proc signalSysCond*(hEvent: var SysCond) {.stdcall, noSideEffect,
      dynlib: "kernel32", importc: "WakeConditionVariable".}

    proc broadcastSysCond*(hEvent: var SysCond) {.stdcall, noSideEffect,
      dynlib: "kernel32", importc: "WakeAllConditionVariable".}

    proc initSysCond*(cond: var SysCond) {.inline.} =
      initializeConditionVariable(cond)
    proc deinitSysCond*(cond: SysCond) {.inline.} =
      discard
    proc waitSysCond*(cond: var SysCond, lock: var SysLock) =
      discard sleepConditionVariableCS(cond, lock, -1'i32)
# elif ...

Additional Information

std/private/syslocks module has InitializeConditionVariable function being imported from kernel32.dll which was added in Windows Vista and up (https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializeconditionvariable) and does not exist on previous versions.

  proc initializeConditionVariable(
    conditionVariable: var SysCond
  ) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "InitializeConditionVariable".}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants