我正在使用外部调用在Haskell中构建ODBC应用程序。在同一线程中分配的句柄上调用forkIO或forkOS线程(即无界或有界线程)中的odbc函数时,该函数返回无效的句柄错误。
在主线程中进行同样的调用是完美的。
答案 0 :(得分:1)
我发现问题是由unixODBC中的错误引起的,我在此处记录了它:https://sourceforge.net/p/unixodbc/bugs/41/
简而言之,unixODBC使用句柄指向分配的内存来保存这些句柄的数据。由于句柄是32位整数,在64位体系结构上,它们被截断到最后一个重要的一半(对于x86处理器)。
因此,如果指针值小于2G,一切正常,但如果它超过2G(而不是因为符号位扩展而不是4G)那么unixODBC将无法定位数据结构对于句柄,将报告无效句柄。
当在主线程中从Haskell调用SQLAllocHandle时,分配的指针的值小于2G,因此一切正常。但是当从另一个线程(liftIO或liftOS)调用它时,分配的指针的值大于2G,因此在Haskell中使用unixODBC的多线程ODBC应用程序是不可能的,除非所有句柄分配都在主线程中完成。
我找到的解决方法基于以下事实:在主线程中我有一个等待线程中的工作完成的函数。我修改了该函数以监听句柄分配请求的通道,然后分配句柄并返回响应。
这是我用于workarround的示例代码:
-- All actions are ReaderT monad actions that share a global environment
-- for threads execution
-- | wait for worker threads to complete the work
waitForWorkToEnd :: (MonadIO m) => ReaderT MyEnvironment m ()
waitForWorkToEnd = do
threadsCountVar <- asks threads_WorkerThreadsVar
allocHandleChan <- asks threads_AllocHandleChan
let waitIO = join $ atomically $ orElse (readTVar threadsCountVar >>= check . (<= 0) >> (return $ return ())) (allocHandleT allocHandleChan >>= \ io -> return (io >> waitIO))
liftIO $ waitIO
liftIO $ log $ fromString $ "all worker threads have finished"
-- | creates an IO action inside a STM monad to allocate a new handler in the current thread
allocHandleT :: (MonadIO m, MonadFail m) => TQueue (SQLSMALLINT, SQLINTEGER, TMVar SQLINTEGER) -> STM (m ())
allocHandleT chan = do
(hType, hParent, retVar) <- readTQueue chan
return $ allocHandle hType hParent >>= liftIO . atomically . (putTMVar retVar)
-- | make a handle alloc request to the main thread and wait for result
allocHandleReq :: (MonadIO m, MonadFail m) => SQLSMALLINT -> SQLINTEGER -> ReaderT MyEnvironment m SQLINTEGER
allocHandleReq htype hparent = do
allocHandleChan <- asks threads_AllocHandleChan
resultVar <- liftIO $ atomically $ newEmptyTMVar
liftIO $ atomically $ writeTQueue allocHandleChan (htype, hparent, resultVar)
liftIO $ atomically $ takeTMVar resultVar
-- allocHandle simply calls SQLAllocHandle and takes care of the diagnostics
-- information; it is part of the sqlcli package you can find it here:
-- https://hub.darcs.net/mihaigiurgeanu/sqlcli