Main Loop Hooks/zh CN

From Lazarus wiki
Jump to navigationJump to search

English (en) français (fr) 日本語 (ja) slovenčina (sk) 中文(中国大陆) (zh_CN)

问题陈述

当你需要等待一些事件(如:socket、pipe...)的触发,在不阻塞主线程(main thread GUI)显示而又不使用多线程方式,反馈到主线程时。解决方法是把事件监视挂接(handles)到主事件循环中。

结决的详细内容

在单元文件 LCLIntf 中 2 个函数用来实现这个功能,如下:

AddEventHandler(AHandle: THandle; AFlags: dword; 
    AEventHandler: TWaitHandleEvent; AData: PtrInt): PEventHandler;
RemoveEventHandler(var AHandler: PEventHandler);
SetEventHandlerFlags(AHandler: PEventHandler; NewFlags: dword);

TWaitHandleEvent 定义于 单元文件 InterfaceBase 其定义如下:

 TWaitHandleEvent = procedure(AData: PtrInt; AFlags: dword) of object;

AddEventHandler 在主事件循环中添加一个事件监视挂接,当挂接被触发,作为 AData 参数传入的回调函数 ACallback 被调用,并传回相应的标志 Flags 信息,包含操作系统指定的信息。回传标志 Flags 到 AddEventHandler 中可以用 SetEventHandlerFlags 修改。使用 AddEventHandler 返回的挂接指针 PEventHandler 被 RemoveEventHandler 用于停止此挂接监视。

RemoveEventHandler 停止指定的挂接监视。RemoveEventHandler 将把之前返回的 AHandler 设为nil, 此变量参数是 AddEventHandler 返回的指针(pointer)。

Windows

在当前的实现中,AddEventHandler 里的 AFlags 参数未被使用, 在 TWaitHandleEvent 中 AFlags 被赋值为 0。

Win32 支持以下的挂接监视类型, 基本上都被 MsgWaitForMultipleObjects 所支持:

  • change notifications (针对文件和文件夹)
  • console input (控制台输入)
  • events: signalled with SetEvent winapi function
  • mutexes: signalled when it is not owned anymore
  • processes: signalled when they terminate
  • semaphore: signalled when it's count is greater than zero
  • threads: signalled when they terminate
  • timers: signalled when expired, see SetWaitableTimer

Gtk/Unix

The AFlags parameter in AddEventHandler specifies the condition in which the handle should be signalled, with the possible values being the ones documented in the GIOCondition type in the glib reference.

In the callback, the AFlags will contain the condition, of the above referenced GIOCondition type, that was satisfied.

Gtk/unix supports the following handle types:

  • files
  • sockets
  • pipes

Note that win32 does not support sockets or pipes (at least, their use is "discouraged" by MSDN). Suggestion: use WSAEventSelect to create an event associated with a socket and pass the event handle.

Pipes 和 Process 的终止

由于要提供交叉平台的解决方法针对 Pipes(Win32 不支持) 和 Process(gtk/unix 不支持),所附加的函数如下:

TChildExitReason = (cerExit, cerSignal);
TPipeReason = (prDataAvailable, prBroken, prCanWrite);
TPipeReasons = set of TPipeReason;

TChildExitEvent = procedure(AData: PtrInt; AReason: TChildExitReason; AInfo: dword) of object;
TPipeEvent = procedure(AData: PtrInt; AReasons: TPipeReasons) of object;
function  AddProcessEventHandler(AHandle: THandle; 
    AEventHandler: TChildExitEvent; AData: PtrInt): PProcessEventHandler;
procedure RemoveProcessEventHandler(var AHandler: PProcessEventHandler);

function  AddPipeEventHandler(AHandle: THandle; 
    AEventHandler: TPipeEvent; AData: PtrInt): PPipeEventHandler;
procedure RemovePipeEventHandler(var AHandler: PPipeEventHandler);

When a process terminates the event handler specified will be called. AInfo will contain the exit code if AReason is cerExit, or (on unix only) the termination signal if AReason is cerSignal. For gtk/unix, use the PID to watch as AHandle. Internally, a signal handler is installed to catch the SIGCHLD signal. On win32, AddEventHandler is used to watch process termination.

To watch a pipe for incoming data, pass the file descriptor (unix) or the pipe read-end handle to AddPipeEventHandler with a custom chosen event handler method as AEventHandler. On gtk/unix, AddEventHandler is called to watch the file descriptor for traffic. On win32, the message loop will timeout every 100 msecs to check all watched pipes for available data; if data is available, the event handler is called.