wayland中有几个概念不太好理解,主要是围绕着wl_display_dispatch容易产生很多问题 。翻了翻源码,发现dispatch等函数基本上都是围绕着
struct wl_event_queue来进行的。
在最早的wl_display_connect中,调用了wl_display_connect_fd;
对应了struct wl_display的两个queue;
对于wl_display_dispatch
WL_EXPORT int wl_display_dispatch(struct wl_display *display) { return wl_display_dispatch_queue(display, &display->default_queue); }
然后就进入了本文的核心内容,分析一下wl_display_dispatch_queue,先贴上源码:
WL_EXPORT int wl_display_dispatch_queue(struct wl_display *display, struct wl_event_queue *queue) { int ret; if (wl_display_prepare_read_queue(display, queue) == -1) return wl_display_dispatch_queue_pending(display, queue); while (true) { ret = wl_display_flush(display); if (ret != -1 || errno != EAGAIN) break; if (wl_display_poll(display, POLLOUT) == -1) { wl_display_cancel_read(display); return -1; } } /* Don't stop if flushing hits an EPIPE; continue so we can read any * protocol error that may have triggered it. */ if (ret < 0 && errno != EPIPE) { wl_display_cancel_read(display); return -1; } if (wl_display_poll(display, POLLIN) == -1) { wl_display_cancel_read(display); return -1; } if (wl_display_read_events(display) == -1) return -1; return wl_display_dispatch_queue_pending(display, queue); }
结合下面的表格来理解这块函数的真正用意:
函数名 | 作用 | 注解 |
wl_display_dispatch | 在display的default queue上,读取server的event,如果没有 event会阻塞在此处,直到收到event为止 | 线程安全 |
wl_display_dispatch_queue | 同上,不过是可以指定一个新的event queue,而非在default queue上 | |
wl_display_dispatch_queue_pending | 把当前pending在in queue(ring buffer)中的events 处理掉,并返回dispatch的event个数 |
如果没有pending的events, 会立即返回 |
wl_display_flush | 将当前pending在out queue中的cmds全部发送给server | |
wl_display_roundtrip_queue | 通过wl_display_sync,告知server端,处理完所有request后,通知到client的回调,这个回调是定义在wayland-client.c中的sync_listener, server在sync ack后,这个函数才会返回 | |
wl_display_roundtrip | 同上,只是在display的default queue上dispatch和等待ack | |
wl_display_prepare_read_queue | 判断指定的event queue中是否是empty,若是返回0,同时使内部的reader_counter++,否则返回-1并且设置errno为EAGAIN | |
wl_display_prepare_read | 同上,只是在default queue上去prepare_read_queue | |
wl_display_cancel_read | 会使display结构体内部维护的一个引用计数reader_counter-- | |
wl_display_read_events |
需要在prepare_read返回0后使用,该函数会使内部的reader_counter--,当某个线程调用该函数后,使reader_counter减为0,则触发真正的从缓冲区中读取数据,并唤醒其它阻塞在condition_wait的线程; 其它调用该函数但是没有使reader_counter减为0的调用者将进入condition_wait的状态,等待被某个wl_display_read_events或者wl_display_cancel_read来唤醒 |
|
综合下来,我的理解就是:
wl_display_dispatch在wl_prepare_read 和 poll 以及 wl_display_read_events 的配合下,可以实现线程安全,即使在多线程中,也不会因为多次调用这个函数而出现问题