Make xcb_take_socket keep flushing until idle
[free-sw/xcb/libxcb] / src / xcb_out.c
1 /* Copyright (C) 2001-2004 Bart Massey and Jamey Sharp.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a
4  * copy of this software and associated documentation files (the "Software"),
5  * to deal in the Software without restriction, including without limitation
6  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
7  * and/or sell copies of the Software, and to permit persons to whom the
8  * Software is furnished to do so, subject to the following conditions:
9  * 
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  * 
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
17  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  * 
20  * Except as contained in this notice, the names of the authors or their
21  * institutions shall not be used in advertising or otherwise to promote the
22  * sale, use or other dealings in this Software without prior written
23  * authorization from the authors.
24  */
25
26 /* Stuff that sends stuff to the server. */
27
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32
33 #include "xcb.h"
34 #include "xcbext.h"
35 #include "xcbint.h"
36 #include "bigreq.h"
37
38 static inline void send_request(xcb_connection_t *c, int isvoid, enum workarounds workaround, int flags, struct iovec *vector, int count)
39 {
40     if(c->has_error)
41         return;
42
43     ++c->out.request;
44     if(!isvoid)
45         c->in.request_expected = c->out.request;
46     if(workaround != WORKAROUND_NONE || flags != 0)
47         _xcb_in_expect_reply(c, c->out.request, workaround, flags);
48
49     while(count && c->out.queue_len + vector[0].iov_len <= sizeof(c->out.queue))
50     {
51         memcpy(c->out.queue + c->out.queue_len, vector[0].iov_base, vector[0].iov_len);
52         c->out.queue_len += vector[0].iov_len;
53         vector[0].iov_base = (char *) vector[0].iov_base + vector[0].iov_len;
54         vector[0].iov_len = 0;
55         ++vector, --count;
56     }
57     if(!count)
58         return;
59
60     --vector, ++count;
61     vector[0].iov_base = c->out.queue;
62     vector[0].iov_len = c->out.queue_len;
63     c->out.queue_len = 0;
64     _xcb_out_send(c, vector, count);
65 }
66
67 static void send_sync(xcb_connection_t *c)
68 {
69     static const union {
70         struct {
71             uint8_t major;
72             uint8_t pad;
73             uint16_t len;
74         } fields;
75         uint32_t packet;
76     } sync_req = { { /* GetInputFocus */ 43, 0, 1 } };
77     struct iovec vector[2];
78     vector[1].iov_base = (char *) &sync_req;
79     vector[1].iov_len = sizeof(sync_req);
80     send_request(c, 0, WORKAROUND_NONE, XCB_REQUEST_DISCARD_REPLY, vector + 1, 1);
81 }
82
83 static void get_socket_back(xcb_connection_t *c)
84 {
85     while(c->out.return_socket && c->out.socket_moving)
86         pthread_cond_wait(&c->out.socket_cond, &c->iolock);
87     if(!c->out.return_socket)
88         return;
89
90     c->out.socket_moving = 1;
91     pthread_mutex_unlock(&c->iolock);
92     c->out.return_socket(c->out.socket_closure);
93     pthread_mutex_lock(&c->iolock);
94     c->out.socket_moving = 0;
95
96     pthread_cond_broadcast(&c->out.socket_cond);
97     c->out.return_socket = 0;
98     c->out.socket_closure = 0;
99     _xcb_in_replies_done(c);
100 }
101
102 /* Public interface */
103
104 void xcb_prefetch_maximum_request_length(xcb_connection_t *c)
105 {
106     if(c->has_error)
107         return;
108     pthread_mutex_lock(&c->out.reqlenlock);
109     if(c->out.maximum_request_length_tag == LAZY_NONE)
110     {
111         const xcb_query_extension_reply_t *ext;
112         ext = xcb_get_extension_data(c, &xcb_big_requests_id);
113         if(ext && ext->present)
114         {
115             c->out.maximum_request_length_tag = LAZY_COOKIE;
116             c->out.maximum_request_length.cookie = xcb_big_requests_enable(c);
117         }
118         else
119         {
120             c->out.maximum_request_length_tag = LAZY_FORCED;
121             c->out.maximum_request_length.value = c->setup->maximum_request_length;
122         }
123     }
124     pthread_mutex_unlock(&c->out.reqlenlock);
125 }
126
127 uint32_t xcb_get_maximum_request_length(xcb_connection_t *c)
128 {
129     if(c->has_error)
130         return 0;
131     xcb_prefetch_maximum_request_length(c);
132     pthread_mutex_lock(&c->out.reqlenlock);
133     if(c->out.maximum_request_length_tag == LAZY_COOKIE)
134     {
135         xcb_big_requests_enable_reply_t *r = xcb_big_requests_enable_reply(c, c->out.maximum_request_length.cookie, 0);
136         c->out.maximum_request_length_tag = LAZY_FORCED;
137         if(r)
138         {
139             c->out.maximum_request_length.value = r->maximum_request_length;
140             free(r);
141         }
142         else
143             c->out.maximum_request_length.value = c->setup->maximum_request_length;
144     }
145     pthread_mutex_unlock(&c->out.reqlenlock);
146     return c->out.maximum_request_length.value;
147 }
148
149 unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *req)
150 {
151     uint64_t request;
152     uint32_t prefix[2];
153     int veclen = req->count;
154     enum workarounds workaround = WORKAROUND_NONE;
155
156     if(c->has_error)
157         return 0;
158
159     assert(c != 0);
160     assert(vector != 0);
161     assert(req->count > 0);
162
163     if(!(flags & XCB_REQUEST_RAW))
164     {
165         static const char pad[3];
166         unsigned int i;
167         uint16_t shortlen = 0;
168         size_t longlen = 0;
169         assert(vector[0].iov_len >= 4);
170         /* set the major opcode, and the minor opcode for extensions */
171         if(req->ext)
172         {
173             const xcb_query_extension_reply_t *extension = xcb_get_extension_data(c, req->ext);
174             if(!(extension && extension->present))
175             {
176                 _xcb_conn_shutdown(c);
177                 return 0;
178             }
179             ((uint8_t *) vector[0].iov_base)[0] = extension->major_opcode;
180             ((uint8_t *) vector[0].iov_base)[1] = req->opcode;
181         }
182         else
183             ((uint8_t *) vector[0].iov_base)[0] = req->opcode;
184
185         /* put together the length field, possibly using BIGREQUESTS */
186         for(i = 0; i < req->count; ++i)
187         {
188             longlen += vector[i].iov_len;
189             if(!vector[i].iov_base)
190             {
191                 vector[i].iov_base = (char *) pad;
192                 assert(vector[i].iov_len <= sizeof(pad));
193             }
194         }
195         assert((longlen & 3) == 0);
196         longlen >>= 2;
197
198         if(longlen <= c->setup->maximum_request_length)
199         {
200             /* we don't need BIGREQUESTS. */
201             shortlen = longlen;
202             longlen = 0;
203         }
204         else if(longlen > xcb_get_maximum_request_length(c))
205         {
206             _xcb_conn_shutdown(c);
207             return 0; /* server can't take this; maybe need BIGREQUESTS? */
208         }
209
210         /* set the length field. */
211         ((uint16_t *) vector[0].iov_base)[1] = shortlen;
212         if(!shortlen)
213         {
214             prefix[0] = ((uint32_t *) vector[0].iov_base)[0];
215             prefix[1] = ++longlen;
216             vector[0].iov_base = (uint32_t *) vector[0].iov_base + 1;
217             vector[0].iov_len -= sizeof(uint32_t);
218             --vector, ++veclen;
219             vector[0].iov_base = prefix;
220             vector[0].iov_len = sizeof(prefix);
221         }
222     }
223     flags &= ~XCB_REQUEST_RAW;
224
225     /* do we need to work around the X server bug described in glx.xml? */
226     /* XXX: GetFBConfigs won't use BIG-REQUESTS in any sane
227      * configuration, but that should be handled here anyway. */
228     if(req->ext && !req->isvoid && !strcmp(req->ext->name, "GLX") &&
229             ((req->opcode == 17 && ((uint32_t *) vector[0].iov_base)[1] == 0x10004) ||
230              req->opcode == 21))
231         workaround = WORKAROUND_GLX_GET_FB_CONFIGS_BUG;
232
233     /* get a sequence number and arrange for delivery. */
234     pthread_mutex_lock(&c->iolock);
235     /* wait for other writing threads to get out of my way. */
236     while(c->out.writing)
237         pthread_cond_wait(&c->out.cond, &c->iolock);
238     get_socket_back(c);
239
240     /* send GetInputFocus (sync_req) when 64k-2 requests have been sent without
241      * a reply. */
242     if(req->isvoid && c->out.request == c->in.request_expected + (1 << 16) - 2)
243         send_sync(c);
244     /* Also send sync_req (could use NoOp) at 32-bit wrap to avoid having
245      * applications see sequence 0 as that is used to indicate
246      * an error in sending the request */
247     if((unsigned int) (c->out.request + 1) == 0)
248         send_sync(c);
249
250     /* The above send_sync calls could drop the I/O lock, but this
251      * thread will still exclude any other thread that tries to write,
252      * so the sequence number postconditions still hold. */
253     send_request(c, req->isvoid, workaround, flags, vector, veclen);
254     request = c->has_error ? 0 : c->out.request;
255     pthread_mutex_unlock(&c->iolock);
256     return request;
257 }
258
259 int xcb_take_socket(xcb_connection_t *c, void (*return_socket)(void *closure), void *closure, int flags, uint64_t *sent)
260 {
261     int ret;
262     if(c->has_error)
263         return 0;
264     pthread_mutex_lock(&c->iolock);
265     get_socket_back(c);
266
267     /* _xcb_out_flush may drop the iolock allowing other threads to
268      * write requests, so keep flushing until we're done
269      */
270     do
271             ret = _xcb_out_flush_to(c, c->out.request);
272     while (ret && c->out.request != c->out.request_written);
273     if(ret)
274     {
275         c->out.return_socket = return_socket;
276         c->out.socket_closure = closure;
277         if(flags)
278             _xcb_in_expect_reply(c, c->out.request, WORKAROUND_EXTERNAL_SOCKET_OWNER, flags);
279         assert(c->out.request == c->out.request_written);
280         *sent = c->out.request;
281     }
282     pthread_mutex_unlock(&c->iolock);
283     return ret;
284 }
285
286 int xcb_writev(xcb_connection_t *c, struct iovec *vector, int count, uint64_t requests)
287 {
288     int ret;
289     if(c->has_error)
290         return 0;
291     pthread_mutex_lock(&c->iolock);
292     c->out.request += requests;
293     ret = _xcb_out_send(c, vector, count);
294     pthread_mutex_unlock(&c->iolock);
295     return ret;
296 }
297
298 int xcb_flush(xcb_connection_t *c)
299 {
300     int ret;
301     if(c->has_error)
302         return 0;
303     pthread_mutex_lock(&c->iolock);
304     ret = _xcb_out_flush_to(c, c->out.request);
305     pthread_mutex_unlock(&c->iolock);
306     return ret;
307 }
308
309 /* Private interface */
310
311 int _xcb_out_init(_xcb_out *out)
312 {
313     if(pthread_cond_init(&out->socket_cond, 0))
314         return 0;
315     out->return_socket = 0;
316     out->socket_closure = 0;
317     out->socket_moving = 0;
318
319     if(pthread_cond_init(&out->cond, 0))
320         return 0;
321     out->writing = 0;
322
323     out->queue_len = 0;
324
325     out->request = 0;
326     out->request_written = 0;
327
328     if(pthread_mutex_init(&out->reqlenlock, 0))
329         return 0;
330     out->maximum_request_length_tag = LAZY_NONE;
331
332     return 1;
333 }
334
335 void _xcb_out_destroy(_xcb_out *out)
336 {
337     pthread_cond_destroy(&out->cond);
338     pthread_mutex_destroy(&out->reqlenlock);
339 }
340
341 int _xcb_out_send(xcb_connection_t *c, struct iovec *vector, int count)
342 {
343     int ret = 1;
344     while(ret && count)
345         ret = _xcb_conn_wait(c, &c->out.cond, &vector, &count);
346     c->out.request_written = c->out.request;
347     pthread_cond_broadcast(&c->out.cond);
348     _xcb_in_wake_up_next_reader(c);
349     return ret;
350 }
351
352 void _xcb_out_send_sync(xcb_connection_t *c)
353 {
354     /* wait for other writing threads to get out of my way. */
355     while(c->out.writing)
356         pthread_cond_wait(&c->out.cond, &c->iolock);
357     get_socket_back(c);
358     send_sync(c);
359 }
360
361 int _xcb_out_flush_to(xcb_connection_t *c, uint64_t request)
362 {
363     assert(XCB_SEQUENCE_COMPARE(request, <=, c->out.request));
364     if(XCB_SEQUENCE_COMPARE(c->out.request_written, >=, request))
365         return 1;
366     if(c->out.queue_len)
367     {
368         struct iovec vec;
369         vec.iov_base = c->out.queue;
370         vec.iov_len = c->out.queue_len;
371         c->out.queue_len = 0;
372         return _xcb_out_send(c, &vec, 1);
373     }
374     while(c->out.writing)
375         pthread_cond_wait(&c->out.cond, &c->iolock);
376     assert(XCB_SEQUENCE_COMPARE(c->out.request_written, >=, request));
377     return 1;
378 }