xref: /aosp_15_r20/external/mesa3d/src/loader/loader_wayland_helper.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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, &current_time);
161    timespec_sub_saturate(&remaining_timeout, end_time, &current_time);
162    return wl_display_dispatch_queue_timeout(wl_display,
163                                             queue,
164                                             &remaining_timeout);
165 }
166