Always wake up readers after writing.
[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 int write_block(xcb_connection_t *c, struct iovec *vector, int count)
39 {
40     while(count && c->out.queue_len + vector[0].iov_len <= sizeof(c->out.queue))
41     {
42         memcpy(c->out.queue + c->out.queue_len, vector[0].iov_base, vector[0].iov_len);
43         c->out.queue_len += vector[0].iov_len;
44         vector[0].iov_base = (char *) vector[0].iov_base + vector[0].iov_len;
45         vector[0].iov_len = 0;
46         ++vector, --count;
47     }
48     if(!count)
49         return 1;
50
51     --vector, ++count;
52     vector[0].iov_base = c->out.queue;
53     vector[0].iov_len = c->out.queue_len;
54     c->out.queue_len = 0;
55     return _xcb_out_send(c, vector, count);
56 }
57
58 static void get_socket_back(xcb_connection_t *c)
59 {
60     while(c->out.return_socket && c->out.socket_moving)
61         pthread_cond_wait(&c->out.socket_cond, &c->iolock);
62     if(!c->out.return_socket)
63         return;
64
65     c->out.socket_moving = 1;
66     pthread_mutex_unlock(&c->iolock);
67     c->out.return_socket(c->out.socket_closure);
68     pthread_mutex_lock(&c->iolock);
69     c->out.socket_moving = 0;
70
71     pthread_cond_broadcast(&c->out.socket_cond);
72     c->out.return_socket = 0;
73     c->out.socket_closure = 0;
74     _xcb_in_replies_done(c);
75 }
76
77 /* Public interface */
78
79 void xcb_prefetch_maximum_request_length(xcb_connection_t *c)
80 {
81     if(c->has_error)
82         return;
83     pthread_mutex_lock(&c->out.reqlenlock);
84     if(c->out.maximum_request_length_tag == LAZY_NONE)
85     {
86         const xcb_query_extension_reply_t *ext;
87         ext = xcb_get_extension_data(c, &xcb_big_requests_id);
88         if(ext && ext->present)
89         {
90             c->out.maximum_request_length_tag = LAZY_COOKIE;
91             c->out.maximum_request_length.cookie = xcb_big_requests_enable(c);
92         }
93         else
94         {
95             c->out.maximum_request_length_tag = LAZY_FORCED;
96             c->out.maximum_request_length.value = c->setup->maximum_request_length;
97         }
98     }
99     pthread_mutex_unlock(&c->out.reqlenlock);
100 }
101
102 uint32_t xcb_get_maximum_request_length(xcb_connection_t *c)
103 {
104     if(c->has_error)
105         return 0;
106     xcb_prefetch_maximum_request_length(c);
107     pthread_mutex_lock(&c->out.reqlenlock);
108     if(c->out.maximum_request_length_tag == LAZY_COOKIE)
109     {
110         xcb_big_requests_enable_reply_t *r = xcb_big_requests_enable_reply(c, c->out.maximum_request_length.cookie, 0);
111         c->out.maximum_request_length_tag = LAZY_FORCED;
112         if(r)
113         {
114             c->out.maximum_request_length.value = r->maximum_request_length;
115             free(r);
116         }
117         else
118             c->out.maximum_request_length.value = c->setup->maximum_request_length;
119     }
120     pthread_mutex_unlock(&c->out.reqlenlock);
121     return c->out.maximum_request_length.value;
122 }
123
124 unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *req)
125 {
126     static const union {
127         struct {
128             uint8_t major;
129             uint8_t pad;
130             uint16_t len;
131         } fields;
132         uint32_t packet;
133     } sync_req = { { /* GetInputFocus */ 43, 0, 1 } };
134     uint64_t request;
135     uint32_t prefix[3] = { 0 };
136     int veclen = req->count;
137     enum workarounds workaround = WORKAROUND_NONE;
138
139     if(c->has_error)
140         return 0;
141
142     assert(c != 0);
143     assert(vector != 0);
144     assert(req->count > 0);
145
146     if(!(flags & XCB_REQUEST_RAW))
147     {
148         static const char pad[3];
149         unsigned int i;
150         uint16_t shortlen = 0;
151         size_t longlen = 0;
152         assert(vector[0].iov_len >= 4);
153         /* set the major opcode, and the minor opcode for extensions */
154         if(req->ext)
155         {
156             const xcb_query_extension_reply_t *extension = xcb_get_extension_data(c, req->ext);
157             if(!(extension && extension->present))
158             {
159                 _xcb_conn_shutdown(c);
160                 return 0;
161             }
162             ((uint8_t *) vector[0].iov_base)[0] = extension->major_opcode;
163             ((uint8_t *) vector[0].iov_base)[1] = req->opcode;
164         }
165         else
166             ((uint8_t *) vector[0].iov_base)[0] = req->opcode;
167
168         /* put together the length field, possibly using BIGREQUESTS */
169         for(i = 0; i < req->count; ++i)
170         {
171             longlen += vector[i].iov_len;
172             if(!vector[i].iov_base)
173             {
174                 vector[i].iov_base = (char *) pad;
175                 assert(vector[i].iov_len <= sizeof(pad));
176             }
177         }
178         assert((longlen & 3) == 0);
179         longlen >>= 2;
180
181         if(longlen <= c->setup->maximum_request_length)
182         {
183             /* we don't need BIGREQUESTS. */
184             shortlen = longlen;
185             longlen = 0;
186         }
187         else if(longlen > xcb_get_maximum_request_length(c))
188         {
189             _xcb_conn_shutdown(c);
190             return 0; /* server can't take this; maybe need BIGREQUESTS? */
191         }
192
193         /* set the length field. */
194         ((uint16_t *) vector[0].iov_base)[1] = shortlen;
195         if(!shortlen)
196             prefix[2] = ++longlen;
197     }
198     flags &= ~XCB_REQUEST_RAW;
199
200     /* do we need to work around the X server bug described in glx.xml? */
201     /* XXX: GetFBConfigs won't use BIG-REQUESTS in any sane
202      * configuration, but that should be handled here anyway. */
203     if(req->ext && !req->isvoid && !strcmp(req->ext->name, "GLX") &&
204             ((req->opcode == 17 && ((uint32_t *) vector[0].iov_base)[1] == 0x10004) ||
205              req->opcode == 21))
206         workaround = WORKAROUND_GLX_GET_FB_CONFIGS_BUG;
207
208     /* get a sequence number and arrange for delivery. */
209     pthread_mutex_lock(&c->iolock);
210     /* wait for other writing threads to get out of my way. */
211     while(c->out.writing)
212         pthread_cond_wait(&c->out.cond, &c->iolock);
213     get_socket_back(c);
214
215     request = ++c->out.request;
216     /* send GetInputFocus (sync_req) when 64k-2 requests have been sent without
217      * a reply.
218      * Also send sync_req (could use NoOp) at 32-bit wrap to avoid having
219      * applications see sequence 0 as that is used to indicate
220      * an error in sending the request */
221     while((req->isvoid &&
222         c->out.request == c->in.request_expected + (1 << 16) - 1) ||
223        request == 0)
224     {
225         prefix[0] = sync_req.packet;
226         _xcb_in_expect_reply(c, request, WORKAROUND_NONE, XCB_REQUEST_DISCARD_REPLY);
227         c->in.request_expected = c->out.request;
228         request = ++c->out.request;
229     }
230
231     if(workaround != WORKAROUND_NONE || flags != 0)
232         _xcb_in_expect_reply(c, request, workaround, flags);
233     if(!req->isvoid)
234         c->in.request_expected = c->out.request;
235
236     if(prefix[0] || prefix[2])
237     {
238         --vector, ++veclen;
239         if(prefix[2])
240         {
241             prefix[1] = ((uint32_t *) vector[1].iov_base)[0];
242             vector[1].iov_base = (uint32_t *) vector[1].iov_base + 1;
243             vector[1].iov_len -= sizeof(uint32_t);
244         }
245         vector[0].iov_len = sizeof(uint32_t) * ((prefix[0] ? 1 : 0) + (prefix[2] ? 2 : 0));
246         vector[0].iov_base = prefix + !prefix[0];
247     }
248
249     if(!write_block(c, vector, veclen))
250     {
251         _xcb_conn_shutdown(c);
252         request = 0;
253     }
254     pthread_mutex_unlock(&c->iolock);
255     return request;
256 }
257
258 int xcb_take_socket(xcb_connection_t *c, void (*return_socket)(void *closure), void *closure, int flags, uint64_t *sent)
259 {
260     int ret;
261     if(c->has_error)
262         return 0;
263     pthread_mutex_lock(&c->iolock);
264     get_socket_back(c);
265     ret = _xcb_out_flush_to(c, c->out.request);
266     if(ret)
267     {
268         c->out.return_socket = return_socket;
269         c->out.socket_closure = closure;
270         if(flags)
271             _xcb_in_expect_reply(c, c->out.request, WORKAROUND_EXTERNAL_SOCKET_OWNER, flags);
272         assert(c->out.request == c->out.request_written);
273         *sent = c->out.request;
274     }
275     pthread_mutex_unlock(&c->iolock);
276     return ret;
277 }
278
279 int xcb_writev(xcb_connection_t *c, struct iovec *vector, int count, uint64_t requests)
280 {
281     int ret;
282     if(c->has_error)
283         return 0;
284     pthread_mutex_lock(&c->iolock);
285     c->out.request += requests;
286     ret = _xcb_out_send(c, vector, count);
287     pthread_mutex_unlock(&c->iolock);
288     return ret;
289 }
290
291 int xcb_flush(xcb_connection_t *c)
292 {
293     int ret;
294     if(c->has_error)
295         return 0;
296     pthread_mutex_lock(&c->iolock);
297     ret = _xcb_out_flush_to(c, c->out.request);
298     pthread_mutex_unlock(&c->iolock);
299     return ret;
300 }
301
302 /* Private interface */
303
304 int _xcb_out_init(_xcb_out *out)
305 {
306     if(pthread_cond_init(&out->socket_cond, 0))
307         return 0;
308     out->return_socket = 0;
309     out->socket_closure = 0;
310     out->socket_moving = 0;
311
312     if(pthread_cond_init(&out->cond, 0))
313         return 0;
314     out->writing = 0;
315
316     out->queue_len = 0;
317
318     out->request = 0;
319     out->request_written = 0;
320
321     if(pthread_mutex_init(&out->reqlenlock, 0))
322         return 0;
323     out->maximum_request_length_tag = LAZY_NONE;
324
325     return 1;
326 }
327
328 void _xcb_out_destroy(_xcb_out *out)
329 {
330     pthread_cond_destroy(&out->cond);
331     pthread_mutex_destroy(&out->reqlenlock);
332 }
333
334 int _xcb_out_send(xcb_connection_t *c, struct iovec *vector, int count)
335 {
336     int ret = 1;
337     while(ret && count)
338         ret = _xcb_conn_wait(c, &c->out.cond, &vector, &count);
339     c->out.request_written = c->out.request;
340     pthread_cond_broadcast(&c->out.cond);
341     _xcb_in_wake_up_next_reader(c);
342     return ret;
343 }
344
345 int _xcb_out_flush_to(xcb_connection_t *c, uint64_t request)
346 {
347     assert(XCB_SEQUENCE_COMPARE(request, <=, c->out.request));
348     if(XCB_SEQUENCE_COMPARE(c->out.request_written, >=, request))
349         return 1;
350     if(c->out.queue_len)
351     {
352         struct iovec vec;
353         vec.iov_base = c->out.queue;
354         vec.iov_len = c->out.queue_len;
355         c->out.queue_len = 0;
356         return _xcb_out_send(c, &vec, 1);
357     }
358     while(c->out.writing)
359         pthread_cond_wait(&c->out.cond, &c->iolock);
360     assert(XCB_SEQUENCE_COMPARE(c->out.request_written, >=, request));
361     return 1;
362 }