1 /*
2 * Copyright © 2022 Red Hat, Inc.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23 #include <poll.h>
24 #include <errno.h>
25
26 #include "util/perf/cpu_trace.h"
27
28 #include "loader_wayland_helper.h"
29
30 #ifndef HAVE_WL_DISPATCH_QUEUE_TIMEOUT
31 static int
wl_display_poll(struct wl_display * display,short int events,const struct timespec * timeout)32 wl_display_poll(struct wl_display *display,
33 short int events,
34 const struct timespec *timeout)
35 {
36 int ret;
37 struct pollfd pfd[1];
38 struct timespec now;
39 struct timespec deadline = {0};
40 struct timespec result;
41 struct timespec *remaining_timeout = NULL;
42
43 if (timeout) {
44 clock_gettime(CLOCK_MONOTONIC, &now);
45 timespec_add(&deadline, &now, timeout);
46 }
47
48 pfd[0].fd = wl_display_get_fd(display);
49 pfd[0].events = events;
50 do {
51 if (timeout) {
52 clock_gettime(CLOCK_MONOTONIC, &now);
53 timespec_sub_saturate(&result, &deadline, &now);
54 remaining_timeout = &result;
55 }
56 ret = ppoll(pfd, 1, remaining_timeout, NULL);
57 } while (ret == -1 && errno == EINTR);
58
59 return ret;
60 }
61
62 int
wl_display_dispatch_queue_timeout(struct wl_display * display,struct wl_event_queue * queue,const struct timespec * timeout)63 wl_display_dispatch_queue_timeout(struct wl_display *display,
64 struct wl_event_queue *queue,
65 const struct timespec *timeout)
66 {
67 int ret;
68 struct timespec now;
69 struct timespec deadline = {0};
70 struct timespec result;
71 struct timespec *remaining_timeout = NULL;
72
73 if (timeout) {
74 clock_gettime(CLOCK_MONOTONIC, &now);
75 timespec_add(&deadline, &now, timeout);
76 }
77
78 if (wl_display_prepare_read_queue(display, queue) == -1)
79 return wl_display_dispatch_queue_pending(display, queue);
80
81 while (true) {
82 ret = wl_display_flush(display);
83
84 if (ret != -1 || errno != EAGAIN)
85 break;
86
87 if (timeout) {
88 clock_gettime(CLOCK_MONOTONIC, &now);
89 timespec_sub_saturate(&result, &deadline, &now);
90 remaining_timeout = &result;
91 }
92 ret = wl_display_poll(display, POLLOUT, remaining_timeout);
93
94 if (ret <= 0) {
95 wl_display_cancel_read(display);
96 return ret;
97 }
98 }
99
100 /* Don't stop if flushing hits an EPIPE; continue so we can read any
101 * protocol error that may have triggered it. */
102 if (ret < 0 && errno != EPIPE) {
103 wl_display_cancel_read(display);
104 return -1;
105 }
106
107 while (true) {
108 if (timeout) {
109 clock_gettime(CLOCK_MONOTONIC, &now);
110 timespec_sub_saturate(&result, &deadline, &now);
111 remaining_timeout = &result;
112 }
113
114 ret = wl_display_poll(display, POLLIN, remaining_timeout);
115 if (ret <= 0) {
116 wl_display_cancel_read(display);
117 break;
118 }
119
120 ret = wl_display_read_events(display);
121 if (ret == -1)
122 break;
123
124 ret = wl_display_dispatch_queue_pending(display, queue);
125 if (ret != 0)
126 break;
127
128 /* wl_display_dispatch_queue_pending can return 0 if we ended up reading
129 * from WL fd, but there was no complete event to dispatch yet.
130 * Try reading again. */
131 if (wl_display_prepare_read_queue(display, queue) == -1)
132 return wl_display_dispatch_queue_pending(display, queue);
133 }
134
135 return ret;
136 }
137 #endif
138
139 #ifndef HAVE_WL_CREATE_QUEUE_WITH_NAME
140 struct wl_event_queue *
wl_display_create_queue_with_name(struct wl_display * display,const char * name)141 wl_display_create_queue_with_name(struct wl_display *display, const char *name)
142 {
143 return wl_display_create_queue(display);
144 }
145 #endif
146
147 int
loader_wayland_dispatch(struct wl_display * wl_display,struct wl_event_queue * queue,struct timespec * end_time)148 loader_wayland_dispatch(struct wl_display *wl_display,
149 struct wl_event_queue *queue,
150 struct timespec *end_time)
151 {
152 struct timespec current_time;
153 struct timespec remaining_timeout;
154
155 MESA_TRACE_FUNC();
156
157 if (!end_time)
158 return wl_display_dispatch_queue(wl_display, queue);
159
160 clock_gettime(CLOCK_MONOTONIC, ¤t_time);
161 timespec_sub_saturate(&remaining_timeout, end_time, ¤t_time);
162 return wl_display_dispatch_queue_timeout(wl_display,
163 queue,
164 &remaining_timeout);
165 }
166