通过FFI块从Haskell调用共享库,而在从C程序链接时则不会

och*_*les 6 c c++ haskell ffi

我正在尝试从Haskell应用程序与Basler USB3相机接口,但我遇到了一些困难.相机附带了一个C++库,使其非常直接.以下代码可用于获取摄像机源:

extern "C" {
  void basler_init() {
    PylonAutoInitTerm pylon;
    CInstantCamera camera( CTlFactory::GetInstance().CreateFirstDevice());
    camera.RegisterConfiguration( (CConfigurationEventHandler*) NULL, RegistrationMode_ReplaceAll, Cleanup_None);
    cout << "Using device " << camera.GetDeviceInfo().GetModelName() << endl;
  }
}
Run Code Online (Sandbox Code Playgroud)

我已经使用这个源代码来构建一个共享库 - libbasler.so.为了确认它的工作原理,这里有一个基本的C程序,它连接它并确认一切正常:

void basler_init();

int main () {
  basler_init();
}
Run Code Online (Sandbox Code Playgroud)

我编译并运行它:

$ gcc Test2.c -lbasler -Llib -Wl,--enable-new-dtags -Wl,-rpath,pylon5/lib64 -Wl,-E -lpylonbase -o Test2-c

$ PYLON_CAMEMU=1 LD_LIBRARY_PATH=lib ./Test2-c
Using device Emulation
Run Code Online (Sandbox Code Playgroud)

这是预期的产出.

但是,当我尝试将其与Haskell一起使用时,行为会发生变化,程序会无限期地阻塞.这是Haskell源代码:

{-# LANGUAGE ForeignFunctionInterface #-}

foreign import ccall "basler_init" baslerInit :: IO ()

main :: IO ()
main = baslerInit
Run Code Online (Sandbox Code Playgroud)

我编译并运行它:

$ ghc --make Test2.hs -o Test2-haskell -Llib -lbasler -optl-Wl,--enable-new-dtags -optl-Wl,-rpath,pylon5/lib64 -optl-Wl,-E -lpylonbase

$ PYLON_CAMEMU=1 LD_LIBRARY_PATH=lib ./Test2-haskell
Run Code Online (Sandbox Code Playgroud)

该应用程序现在无限期挂起.

我已经strace试过去尝试了解发生了什么,但我无法真正理解它.输出太长,无法添加,但请看这两个贴:

最重要的是,我曾经gdb尝试确定Haskell应用程序被卡住的位置:

$ PYLON_CAMEMU=1 LD_LIBRARY_PATH=lib gdb Test2-haskell 
GNU gdb (GDB) 7.11
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from Test2-haskell...done.
(gdb) run
Starting program: /home/ollie/work/circuithub/receiving-station/Test2-haskell 
warning: File "/nix/store/9ljgbhb26ca0j9shwh8bwsa77h42izr2-gcc-5.4.0-lib/lib/libstdc++.so.6.0.21-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
        add-auto-load-safe-path /nix/store/9ljgbhb26ca0j9shwh8bwsa77h42izr2-gcc-5.4.0-lib/lib/libstdc++.so.6.0.21-gdb.py
line to your configuration file "/home/ollie/.gdbinit".
To completely disable this security protection add
        set auto-load safe-path /
line to your configuration file "/home/ollie/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
        info "(gdb)Auto-loading safe path"
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/nix/store/bb32xf954imhdrzn7j8h82xs1bx7p3fr-glibc-2.23/lib/libthread_db.so.1".
^C
Program received signal SIGINT, Interrupt.
0x00007ffff6c6fb33 in __recvfrom_nocancel () from /nix/store/98s2znxww6x7h2ch7cj1w5givahxmdna-glibc-2.23/lib/libc.so.6
(gdb) bt
#0  0x00007ffff6c6fb33 in __recvfrom_nocancel () from /nix/store/98s2znxww6x7h2ch7cj1w5givahxmdna-glibc-2.23/lib/libc.so.6
#1  0x00007fffedb885c2 in GxImp::CEnumCollector::OnReady(unsigned int, _GX_SOCKET_INTERFACE_INFO const*) () from /home/ollie/work/circuithub/receiving-station/pylon5/lib64/libgxapi-5.0.1.so
#2  0x00007fffedb8d54d in CCollector::Collect(GxImp::CSocket*, unsigned int, unsigned int, _GX_SOCKET_INTERFACE_INFO const*) () from /home/ollie/work/circuithub/receiving-station/pylon5/lib64/libgxapi-5.0.1.so
#3  0x00007fffedb8817b in CBroadcastSocketCollection::Collect(CCollector&, unsigned int) () from /home/ollie/work/circuithub/receiving-station/pylon5/lib64/libgxapi-5.0.1.so
#4  0x00007fffedb889ab in Gx::Enumerator::Discover(Gx::Enumerator::Callee*, unsigned int, unsigned int, sockaddr const*) () from /home/ollie/work/circuithub/receiving-station/pylon5/lib64/libgxapi-5.0.1.so
#5  0x00007fffeddeaca0 in Pylon::CBaslerGigETl::DoDeviceEnumeration(Pylon::DeviceInfoList&, bool, sockaddr const*) () from pylon5/lib64/libpylon_TL_gige-5.0.1.so
#6  0x00007fffeddeaebc in Pylon::CBaslerGigETl::InternalEnumerateDevices(Pylon::DeviceInfoList&) () from pylon5/lib64/libpylon_TL_gige-5.0.1.so
#7  0x00007fffeddf3c99 in Pylon::CTransportLayerBase<Pylon::IGigETransportLayer>::EnumerateDevices(Pylon::DeviceInfoList&, Pylon::DeviceInfoList const&, bool) () from pylon5/lib64/libpylon_TL_gige-5.0.1.so
#8  0x00007ffff7949669 in Pylon::CTlFactory::EnumerateDevices(Pylon::DeviceInfoList&, Pylon::DeviceInfoList const&, bool) () from pylon5/lib64/libpylonbase-5.0.1.so
#9  0x00007ffff7949c8f in Pylon::CTlFactory::InternalCreateDevice(Pylon::CDeviceInfo const&, GenICam_3_0_Basler_pylon_v5_0::gcstring_vector const&, bool) () from pylon5/lib64/libpylonbase-5.0.1.so
#10 0x00007ffff794a655 in Pylon::CTlFactory::CreateFirstDevice(Pylon::CDeviceInfo const&) () from pylon5/lib64/libpylonbase-5.0.1.so
#11 0x00007ffff7bd7dc5 in basler_init () from lib/libbasler.so
#12 0x0000000000438415 in rFl_info ()
#13 0x0000000000000000 in ?? ()
Run Code Online (Sandbox Code Playgroud)

对于C程序:

Reading symbols from Test2-c...done.
(gdb) run
Starting program: /home/ollie/work/circuithub/receiving-station/Test2-c 
warning: File "/nix/store/9ljgbhb26ca0j9shwh8bwsa77h42izr2-gcc-5.4.0-lib/lib/libstdc++.so.6.0.21-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
        add-auto-load-safe-path /nix/store/9ljgbhb26ca0j9shwh8bwsa77h42izr2-gcc-5.4.0-lib/lib/libstdc++.so.6.0.21-gdb.py
line to your configuration file "/home/ollie/.gdbinit".
To completely disable this security protection add
        set auto-load safe-path /
line to your configuration file "/home/ollie/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
        info "(gdb)Auto-loading safe path"
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/nix/store/bb32xf954imhdrzn7j8h82xs1bx7p3fr-glibc-2.23/lib/libthread_db.so.1".
[New Thread 0x7fffed4ae700 (LWP 13792)]
[New Thread 0x7fffeccad700 (LWP 13793)]
Using device Emulation
[Thread 0x7fffeccad700 (LWP 13793) exited]
[Thread 0x7fffed4ae700 (LWP 13792) exited]
[Inferior 1 (process 13788) exited normally]
Run Code Online (Sandbox Code Playgroud)

我的猜测是GHC的运行时正在做一些导致pthreads有不同行为的东西,但我不确定那可能是什么.

Eri*_*ikR 8

我相信这个TRAC评论是相关的:

https://ghc.haskell.org/trac/ghc/wiki/Commentary/Rts/Signals

差异发生在C strace输出中的第437行与Haskell strace输出中的第495行之间.

此时,库创建两个UDP套接字并发送两个UDP数据报(行448-449 C /行506-507 Haskell).数据包广播到两个本地网络:192.168.1.0/24和192.168.56.0/24.

然后它等待这些套接字中的任何一个的响应,超时(显然)为25微秒(行450 C /行508 Haskell).在C情况下,选择呼叫超时.在Haskell情况下,选择呼叫被GHC RTS使用的SIGVTALRM信号重复中断.这与上述TRAC评论中显示的模式相同.

有关可能的修复,请查看mysql包如何实现和使用block_rts_signals()unblock_rts_signals()宏:

  • 哇,现货!我使用了http://www.serpentine.com/blog/2010/09/04/dealing-with-fragile-c-libraries-eg-mysql-from-haskell/中的"hack",现在一切都很好.谢谢! (3认同)