From b035ca4e70dd696f4504622ea43eed0eb99fb5ba Mon Sep 17 00:00:00 2001 From: Lars Kanis Date: Thu, 6 Aug 2015 22:08:19 +0200 Subject: Fix test for :blocking=>true with a sleep()-less version. This tests for concurrency instead of timing. It however has a timeout, so that the test doesn't hang in a failure condition. This fixes: https://github.com/ffi/ffi/pull/406 --- spec/ffi/fixtures/FunctionTest.c | 58 +++++++++++++++++++++++++++++++++++++--- spec/ffi/function_spec.rb | 24 ++++++++++------- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/spec/ffi/fixtures/FunctionTest.c b/spec/ffi/fixtures/FunctionTest.c index 5310ab0..1c9a247 100644 --- a/spec/ffi/fixtures/FunctionTest.c +++ b/spec/ffi/fixtures/FunctionTest.c @@ -6,7 +6,6 @@ #ifdef _WIN32 #include -#define sleep(x) Sleep((x)*1000) #endif #ifndef _WIN32 @@ -14,6 +13,9 @@ #include #endif +#include +#include + int testAdd(int a, int b) { return a + b; @@ -24,10 +26,60 @@ int testFunctionAdd(int a, int b, int (*f)(int, int)) return f(a, b); }; -void testBlocking(int seconds) { - sleep(seconds); +struct testBlockingData { + int pipe1[2]; + int pipe2[2]; }; +struct testBlockingData *testBlockingOpen() +{ + struct testBlockingData *self = malloc(sizeof(struct testBlockingData)); + + if( pipe(self->pipe1) == -1 ) return NULL; + if( pipe(self->pipe2) == -1 ) return NULL; + return self; +} + +char testBlockingReadChar(int fd) +{ + char d; + struct timeval timeout; + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + + timeout.tv_sec = 10; // timeout after x seconds + timeout.tv_usec = 0; + + if(select(fd + 1, &read_fds, NULL, NULL, &timeout) <= 0) + return 0; + + if( read(fd, &d, 1) != 1) + return 0; + return d; +} + +char testBlockingWR(struct testBlockingData *self, char c) { + if( write(self->pipe1[1], &c, 1) != 1) + return 0; + return testBlockingReadChar(self->pipe2[0]); +} + +char testBlockingRW(struct testBlockingData *self, char c) { + char d = testBlockingReadChar(self->pipe1[0]); + if( write(self->pipe2[1], &c, 1) != 1) + return 0; + return d; +} + +void testBlockingClose(struct testBlockingData *self) { + close(self->pipe1[0]); + close(self->pipe1[1]); + close(self->pipe2[0]); + close(self->pipe2[1]); + free(self); +} + struct async_data { void (*fn)(int); int value; diff --git a/spec/ffi/function_spec.rb b/spec/ffi/function_spec.rb index ab08f35..8dbbc72 100644 --- a/spec/ffi/function_spec.rb +++ b/spec/ffi/function_spec.rb @@ -12,7 +12,7 @@ describe FFI::Function do attach_function :testFunctionAdd, [:int, :int, :pointer], :int end before do - @libtest = FFI::DynamicLibrary.open(TestLibrary::PATH, + @libtest = FFI::DynamicLibrary.open(TestLibrary::PATH, FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL) end @@ -22,7 +22,7 @@ describe FFI::Function do end it 'raises an error when passing a wrong signature' do - expect { FFI::Function.new([], :int).new { } }.to raise_error TypeError + expect { FFI::Function.new([], :int).new { } }.to raise_error TypeError end it 'returns a native pointer' do @@ -63,15 +63,19 @@ describe FFI::Function do end it 'can wrap a blocking function' do - fp = FFI::Function.new(:void, [ :int ], @libtest.find_function('testBlocking'), :blocking => true) - threads = 10.times.map do |x| - Thread.new do - time = Time.now - fp.call(2) - expect(Time.now - time).to be >= 2 - end + fpOpen = FFI::Function.new(:pointer, [ ], @libtest.find_function('testBlockingOpen')) + fpRW = FFI::Function.new(:char, [ :pointer, :char ], @libtest.find_function('testBlockingRW'), :blocking => true) + fpWR = FFI::Function.new(:char, [ :pointer, :char ], @libtest.find_function('testBlockingWR'), :blocking => true) + fpClose = FFI::Function.new(:void, [ :pointer ], @libtest.find_function('testBlockingClose')) + handle = fpOpen.call + begin + thWR = Thread.new { fpWR.call(handle, 63) } + thRW = Thread.new { fpRW.call(handle, 64) } + expect(thWR.value).to eq(64) + expect(thRW.value).to eq(63) + ensure + fpClose.call(handle) end - threads.each { |t| t.join } end it 'autorelease flag is set to true by default' do -- cgit v1.2.1 From 97e518404c2cdb20b3b3a13c64677da50215c519 Mon Sep 17 00:00:00 2001 From: Lars Kanis Date: Fri, 7 Aug 2015 09:02:45 +0200 Subject: Add Windows port to blocking function test. This separates pipe functions from test code as a POSIX and a Windows implementation. --- spec/ffi/fixtures/FunctionTest.c | 51 ++++++++----------------- spec/ffi/fixtures/PipeHelper.h | 21 ++++++++++ spec/ffi/fixtures/PipeHelperPosix.c | 41 ++++++++++++++++++++ spec/ffi/fixtures/PipeHelperWindows.c | 72 +++++++++++++++++++++++++++++++++++ spec/ffi/function_spec.rb | 1 + 5 files changed, 151 insertions(+), 35 deletions(-) create mode 100644 spec/ffi/fixtures/PipeHelper.h create mode 100644 spec/ffi/fixtures/PipeHelperPosix.c create mode 100644 spec/ffi/fixtures/PipeHelperWindows.c diff --git a/spec/ffi/fixtures/FunctionTest.c b/spec/ffi/fixtures/FunctionTest.c index 1c9a247..a37373a 100644 --- a/spec/ffi/fixtures/FunctionTest.c +++ b/spec/ffi/fixtures/FunctionTest.c @@ -11,10 +11,10 @@ #ifndef _WIN32 #include #include +#include #endif -#include -#include +#include "PipeHelper.h" int testAdd(int a, int b) { @@ -27,56 +27,37 @@ int testFunctionAdd(int a, int b, int (*f)(int, int)) }; struct testBlockingData { - int pipe1[2]; - int pipe2[2]; + FD_TYPE pipe1[2]; + FD_TYPE pipe2[2]; }; struct testBlockingData *testBlockingOpen() { struct testBlockingData *self = malloc(sizeof(struct testBlockingData)); - if( pipe(self->pipe1) == -1 ) return NULL; - if( pipe(self->pipe2) == -1 ) return NULL; + if( pipeHelperCreatePipe(self->pipe1) == -1 ) return NULL; + if( pipeHelperCreatePipe(self->pipe2) == -1 ) return NULL; return self; } -char testBlockingReadChar(int fd) -{ - char d; - struct timeval timeout; - fd_set read_fds; - FD_ZERO(&read_fds); - FD_SET(fd, &read_fds); - - timeout.tv_sec = 10; // timeout after x seconds - timeout.tv_usec = 0; - - if(select(fd + 1, &read_fds, NULL, NULL, &timeout) <= 0) - return 0; - - if( read(fd, &d, 1) != 1) - return 0; - return d; -} - char testBlockingWR(struct testBlockingData *self, char c) { - if( write(self->pipe1[1], &c, 1) != 1) - return 0; - return testBlockingReadChar(self->pipe2[0]); + if( pipeHelperWriteChar(self->pipe1[1], c) != 1) + return 0; + return pipeHelperReadChar(self->pipe2[0], 10); } char testBlockingRW(struct testBlockingData *self, char c) { - char d = testBlockingReadChar(self->pipe1[0]); - if( write(self->pipe2[1], &c, 1) != 1) - return 0; + char d = pipeHelperReadChar(self->pipe1[0], 10); + if( pipeHelperWriteChar(self->pipe2[1], c) != 1) + return 0; return d; } void testBlockingClose(struct testBlockingData *self) { - close(self->pipe1[0]); - close(self->pipe1[1]); - close(self->pipe2[0]); - close(self->pipe2[1]); + pipeHelperClosePipe(self->pipe1[0]); + pipeHelperClosePipe(self->pipe1[1]); + pipeHelperClosePipe(self->pipe2[0]); + pipeHelperClosePipe(self->pipe2[1]); free(self); } diff --git a/spec/ffi/fixtures/PipeHelper.h b/spec/ffi/fixtures/PipeHelper.h new file mode 100644 index 0000000..4a02111 --- /dev/null +++ b/spec/ffi/fixtures/PipeHelper.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2015 Lars Kanis. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#ifndef PIPEHELPER_H +#define PIPEHELPER_H + +#ifdef _WIN32 +#define FD_TYPE HANDLE +#else +#define FD_TYPE int +#endif + +int pipeHelperCreatePipe(FD_TYPE pipefd[2]); +char pipeHelperReadChar(FD_TYPE fd, int timeout); +int pipeHelperWriteChar(FD_TYPE fd, char c); +void pipeHelperClosePipe(FD_TYPE fd); + +#endif diff --git a/spec/ffi/fixtures/PipeHelperPosix.c b/spec/ffi/fixtures/PipeHelperPosix.c new file mode 100644 index 0000000..c1252b4 --- /dev/null +++ b/spec/ffi/fixtures/PipeHelperPosix.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 Lars Kanis. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#ifndef _WIN32 +#include +#include +#include "PipeHelper.h" + +int pipeHelperCreatePipe(FD_TYPE pipefd[2]) +{ + return pipe(pipefd); +} + +char pipeHelperReadChar(FD_TYPE fd, int timeout) +{ + char d; + struct timeval time = {timeout, 0}; // timeout after x seconds + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + + if(select(fd + 1, &read_fds, NULL, NULL, &time) <= 0) + return 0; + + if( read(fd, &d, 1) != 1) + return 0; + return d; +} + +int pipeHelperWriteChar(FD_TYPE fd, char c) +{ + return write(fd, &c, 1); +} + +void pipeHelperClosePipe(FD_TYPE fd) { + close(fd); +} +#endif diff --git a/spec/ffi/fixtures/PipeHelperWindows.c b/spec/ffi/fixtures/PipeHelperWindows.c new file mode 100644 index 0000000..470bacc --- /dev/null +++ b/spec/ffi/fixtures/PipeHelperWindows.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#ifdef _WIN32 +#include +#include "PipeHelper.h" + +int pipeHelperCreatePipe(FD_TYPE pipefd[2]) +{ + char name[ MAX_PATH ]; + static int pipe_idx = 0; + sprintf( name, "\\\\.\\Pipe\\pipeHelper-%u-%i", + (unsigned int)GetCurrentProcessId(), pipe_idx++ ); + + pipefd[0] = CreateNamedPipe( name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_WAIT, + 1, // Number of pipes + 5, // Out buffer size + 5, // In buffer size + 60 * 1000, // Timeout in ms + NULL ); + if(pipefd[0] == INVALID_HANDLE_VALUE) + return -1; + + pipefd[1] = CreateFile( name, GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if(pipefd[1] == INVALID_HANDLE_VALUE) { + CloseHandle( pipefd[0] ); + return -1; + } + return 0; +} + +char pipeHelperReadChar(FD_TYPE fd, int timeout) +{ + char d; + OVERLAPPED ovl; + ZeroMemory(&ovl, sizeof(ovl)); + ovl.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if( ReadFile(fd, &d, 1, NULL, &ovl) == 0) { + DWORD recvd = 0;; + DWORD res = WaitForSingleObject(ovl.hEvent, timeout * 1000); + if( res != WAIT_OBJECT_0 ) { + CloseHandle(ovl.hEvent); + return 0; + } + if( GetOverlappedResult(fd, &ovl, &recvd, FALSE) == 0 ) { + CloseHandle(ovl.hEvent); + return 0; + } + } + CloseHandle(ovl.hEvent); + return d; +} + +int pipeHelperWriteChar(FD_TYPE fd, char c) +{ + DWORD written; + return WriteFile(fd, &c, 1, &written, NULL) == 0 ? 0 : 1; +} + +void pipeHelperClosePipe(FD_TYPE fd) { + CloseHandle(fd); +} + +#endif diff --git a/spec/ffi/function_spec.rb b/spec/ffi/function_spec.rb index 8dbbc72..dd4d2ea 100644 --- a/spec/ffi/function_spec.rb +++ b/spec/ffi/function_spec.rb @@ -68,6 +68,7 @@ describe FFI::Function do fpWR = FFI::Function.new(:char, [ :pointer, :char ], @libtest.find_function('testBlockingWR'), :blocking => true) fpClose = FFI::Function.new(:void, [ :pointer ], @libtest.find_function('testBlockingClose')) handle = fpOpen.call + expect(handle).not_to be_null begin thWR = Thread.new { fpWR.call(handle, 63) } thRW = Thread.new { fpRW.call(handle, 64) } -- cgit v1.2.1