只要我希望对象路径上的导出接口正常工作,我就必须让 DBus.Client.export 的调用者保持活动状态吗?

问题描述 投票:0回答:1

如果没有,是什么让我导出来实现接口的函数保持活动状态?


我正在 Haskell 中实现一个通知服务器,目前我有这样的东西,

startServer :: IORef Notifications -> IO ()
startServer notifications = do
    client <- connectSession
    reply <- requestName client "org.freedesktop.Notifications" [nameDoNotQueue]
    export client "/org/freedesktop/Notifications" defaultInterface {
          interfaceName = "org.freedesktop.Notifications",
          interfaceMethods = [
          autoMethod "GetServerInformation" getServerInformation,
          autoMethod "GetCapabilities" getCapabilities,
          makeMethod "Notify" (signature_ notifyInSig) (signature_ notifyOutSig) (notify notifications)
        ]
    }
    when (reply == NamePrimaryOwner) $ forever $ threadDelay oneSec
      where
        oneSec = 1000000

它是从一个更精简但独立的示例演变而来的,您可以在我的问题接受的答案中找到它。

当时,

forever $ threadDelay oneSec
在那里,因为我直接在
main
中进行实验,所以我不希望可执行文件返回,因为我需要时间通过
notify-send
发送通知并查看
notify
是否已存在确实在尽职尽责。

但是现在我的项目已经取得了一些进展,我不确定我是否还需要它。

上面代码目前的使用方式是这样的,

-- In the IO monad; not really in main, but it's ok to think of it as main itself, I think
withAsync (startServer notifications)
          (const $ fancyShow notifications)

其中

notifications
是一个
IORef
包装一些状态(大致为
[a]
),该状态由
notify
(根据上面第一个片段由
export
编辑)和
startServer
突变:前者在新通知到来时将其填充到
fancyShow
的内部,后者每 1/10 秒轮询一次
IORef
并将其清空,同时负责以图形方式显示通知。
现在 

IORef

是使用

fancyShow
的东西,因此永远不会返回,所以我不明白为什么我应该阻止
forever
返回;毕竟,

它在分配
    startServer
  • 的调用者中保持活动状态,因此
    IORef
    返回不会使该状态无效,
    局部变量 
  • startServer
  • 似乎除了传递给
    client
    之外没有其他理由存在,这是一个
    export
    操作,无论
    IO
    是否返回,
    本地 vairbale 
  • startServer
  • 在我的示例中精确地用于决定是否让
    reply
    返回,但我想我可以始终让
    startServer
    返回,并准确地将
    startServer
    返回给调用者以通知
    reply
    是否成功。
    
    
  • 在这里我开始有一些担忧......

一旦

export

返回,接收通知并将其放入

startServer
的机制就位,而
IORef
会永远运行,完成从
fancyShow
中拉出内容的工作(并显示它)在屏幕上),同时...当通知到来时,
IORef
继续完成填充
notify
的工作?
但是谁让

IORef

活着?

notify

是否有效地将

export
(以及其他两种方法,fwiw)放在“安全的地方”,将让它们活着的责任转移到......其他东西?
我很想认为答案是 

“是的,我不需要阻止

notify 返回”

另一方面,

startServer

的存在让我想知道我是否应该使用它,如果是的话,我是否应该在

DBus.Client.unexport
的调用者中使用它,因此暗示我不应该从中返回。
但是回到上一手,

这个通知服务器

好像确实用了一个返回export,而等待只是在

startServer
中完成。
我的问题仍然是:

main

将代码放在哪里运行

export
?是什么决定了它的寿命?
    

haskell notifications lifetime dbus io-monad
1个回答
0
投票
notify

调用使用

connectSession
启动一个循环监听请求的线程。相关代码埋在
forkIO
:
connectWith'

其中,默认情况下,
connectWith' opts addr = do ... threadID <- forkIO $ do client <- readMVar clientMVar threadRunner (mainLoop client) ...

只是

threadRunner
的另一个名称。

forever

调用只是通过共享

export
修改一些数据结构:
IORef

因此循环线程知道导出的接口并可以向其分派适当的请求。调用 
export client path interface = atomicModifyIORef_ (clientObjects client) $ addInterface path interface

只是撤消这些更改,因此循环线程停止分派到接口。

循环线程的工作原理是在套接字上接受消息,然后调用 

unexport

:

dispatch

mainLoop client = do ... received <- Control.Exception.try (DBus.Socket.receive sock) msg <- case received of ... Right msg -> return msg dispatch client msg

的工作原理是在上述数据结构中查找目标并分叉一个线程来运行适当的回调(例如,您的

dispatch
函数):
notify

因为,在 Haskell 程序中,当主线程退出时,通过 
dispatch client = go where ... go (ReceivedMethodCall serial msg) = do pathInfo <- readIORef (clientObjects client) ... _ <- forkIO $ case findMethodForCall (clientInterfaces client) pathInfo msg of Right Method { methodHandler = handler } -> runReaderT (handler msg) client >>= sendResult ... ...

分叉的所有线程都会被终止,为了保持服务请求,您所需要做的就是防止主线程退出。只要主线程存在,循环线程就会在后台继续运行(字面意义上的

forkIO
),接受消息并
forever
线程来调用您的
forkIO
函数。
因此,正如您所猜测的那样,可以安全地从 

notify

中删除

forever
循环,直接从
startServer
中的主线程调用
startServer
(无需分叉或异步任何内容),并允许
main 
返回。只要主线程继续在应用程序中执行操作而不退出,一切都应该“正常工作”,就好像有一个神奇的预言机代表您从分叉线程调用
startServer
一样。特别是,如果
notify
在循环中永远运行,那么您应该能够不需要
fancyShow
,如下所示:
async

© www.soinside.com 2019 - 2024. All rights reserved.