123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- discard """
- exitcode: 0
- output: ""
- matrix: "; -d:nimInheritHandles; --mm:refc"
- joinable: false
- """
- import os, osproc, strutils, nativesockets, net, selectors, memfiles,
- asyncdispatch, asyncnet
- import std/[assertions, syncio]
- when defined(windows):
- import winlean
- # Note: Windows 10-only API
- proc compareObjectHandles(first, second: Handle): WINBOOL
- {.stdcall, dynlib: "kernelbase",
- importc: "CompareObjectHandles".}
- else:
- import posix
- proc leakCheck(f: AsyncFD | int | FileHandle | SocketHandle, msg: string,
- expectLeak = defined(nimInheritHandles)) =
- var args = @[$f.int, msg, $expectLeak]
- when defined(windows):
- var refFd: Handle
- # NOTE: This function shouldn't be used to duplicate sockets,
- # as this function may mess with the socket internal refcounting.
- # but due to the lack of type segmentation in the stdlib for
- # Windows (AsyncFD can be a file or a socket), we will have to
- # settle with this.
- #
- # Now, as a poor solution for the refcounting problem, we just
- # simply let the duplicated handle leak. This should not interfere
- # with the test since new handles can't occupy the slot held by
- # the leaked ones.
- if duplicateHandle(getCurrentProcess(), f.Handle,
- getCurrentProcess(), addr refFd,
- 0, 1, DUPLICATE_SAME_ACCESS) == 0:
- raiseOSError osLastError(), "Couldn't create the reference handle"
- args.add $refFd
- discard startProcess(
- getAppFilename(),
- args = args,
- options = {poParentStreams}
- ).waitForExit
- proc isValidHandle(f: int): bool =
- ## Check if a handle is valid. Requires OS-native handles.
- when defined(windows):
- var flags: DWORD
- result = getHandleInformation(f.Handle, addr flags) != 0
- else:
- result = fcntl(f.cint, F_GETFD) != -1
- proc main() =
- if paramCount() == 0:
- # Parent process
- let f = syncio.open("__test_fdleak", fmReadWrite)
- defer: close f
- leakCheck(f.getOsFileHandle, "system.open()")
- doAssert f.reopen("__test_fdleak2", fmReadWrite), "reopen failed"
- leakCheck(f.getOsFileHandle, "reopen")
- let sock = createNativeSocket()
- defer: close sock
- leakCheck(sock, "createNativeSocket()")
- if sock.setInheritable(not defined(nimInheritHandles)):
- leakCheck(sock, "createNativeSocket()", not defined(nimInheritHandles))
- else:
- raiseOSError osLastError()
- let server = newSocket()
- defer: close server
- server.bindAddr(address = "127.0.0.1")
- server.listen()
- let (_, port) = server.getLocalAddr
- leakCheck(server.getFd, "newSocket()")
- let client = newSocket()
- defer: close client
- client.connect("127.0.0.1", port)
- var input: Socket
- server.accept(input)
- leakCheck(input.getFd, "accept()")
- # ioselectors_select doesn't support returning a handle.
- when not defined(windows):
- let selector = newSelector[int]()
- leakCheck(selector.getFd, "selector()", false)
- var mf = memfiles.open("__test_fdleak3", fmReadWrite, newFileSize = 1)
- defer: close mf
- when defined(windows):
- leakCheck(mf.mapHandle, "memfiles.open().mapHandle", false)
- else:
- leakCheck(mf.handle, "memfiles.open().handle", false)
- let sockAsync = createAsyncNativeSocket()
- defer: closeSocket sockAsync
- leakCheck(sockAsync, "createAsyncNativeSocket()")
- if sockAsync.setInheritable(not defined(nimInheritHandles)):
- leakCheck(sockAsync, "createAsyncNativeSocket()", not defined(nimInheritHandles))
- else:
- raiseOSError osLastError()
- let serverAsync = newAsyncSocket()
- defer: close serverAsync
- serverAsync.bindAddr(address = "127.0.0.1")
- serverAsync.listen()
- let (_, portAsync) = serverAsync.getLocalAddr
- leakCheck(serverAsync.getFd, "newAsyncSocket()")
- let clientAsync = newAsyncSocket()
- defer: close clientAsync
- waitFor clientAsync.connect("127.0.0.1", portAsync)
- let inputAsync = waitFor serverAsync.accept()
- leakCheck(inputAsync.getFd, "accept() async")
- else:
- let
- fd = parseInt(paramStr 1)
- expectLeak = parseBool(paramStr 3)
- msg = (if expectLeak: "not " else: "") & "leaked " & paramStr 2
- let validHandle =
- when defined(windows):
- # On Windows, due to the use of winlean, causes the program to open
- # a handle to the various dlls that's loaded. This handle might
- # collide with the handle sent for testing.
- #
- # As a walkaround, we pass an another handle that's purposefully leaked
- # as a reference so that we can verify whether the "leaked" handle
- # is the right one.
- let refFd = parseInt(paramStr 4)
- fd.isValidHandle and compareObjectHandles(fd, refFd) != 0
- else:
- fd.isValidHandle
- if expectLeak xor validHandle:
- echo msg
- when isMainModule: main()
|