Implement provably-correct sequence wrap handling. Add flag XCB_REQUEST_DISCARD_REPLY.
[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 #include <errno.h>
33
34 #include "xcb.h"
35 #include "xcbext.h"
36 #include "xcbint.h"
37 #include "extensions/bigreq.h"
38
39 /* Public interface */
40
41 CARD32 XCBGetMaximumRequestLength(XCBConnection *c)
42 {
43     pthread_mutex_lock(&c->out.reqlenlock);
44     if(!c->out.maximum_request_length)
45     {
46         const XCBQueryExtensionRep *ext;
47         c->out.maximum_request_length = c->setup->maximum_request_length;
48         ext = XCBGetExtensionData(c, &XCBBigRequestsId);
49         if(ext && ext->present)
50         {
51             XCBBigRequestsEnableRep *r = XCBBigRequestsEnableReply(c, XCBBigRequestsEnable(c), 0);
52             c->out.maximum_request_length = r->maximum_request_length;
53             free(r);
54         }
55     }
56     pthread_mutex_unlock(&c->out.reqlenlock);
57     return c->out.maximum_request_length;
58 }
59
60 unsigned int XCBSendRequest(XCBConnection *c, int flags, struct iovec *vector, const XCBProtocolRequest *req)
61 {
62     static const union {
63         struct {
64             CARD8 major;
65             CARD8 pad;
66             CARD16 len;
67         } fields;
68         CARD32 packet;
69     } sync = { { /* GetInputFocus */ 43, 0, 1 } };
70     unsigned int request;
71     CARD32 prefix[3] = { 0 };
72     int veclen = req->count;
73     enum workarounds workaround = WORKAROUND_NONE;
74
75     assert(c != 0);
76     assert(vector != 0);
77     assert(req->count > 0);
78
79     if(!(flags & XCB_REQUEST_RAW))
80     {
81         static const char pad[3];
82         int i;
83         CARD16 shortlen = 0;
84         size_t longlen = 0;
85         assert(vector[0].iov_len >= 4);
86         /* set the major opcode, and the minor opcode for extensions */
87         if(req->ext)
88         {
89             const XCBQueryExtensionRep *extension = XCBGetExtensionData(c, req->ext);
90             if(!(extension && extension->present))
91                 return 0;
92             ((CARD8 *) vector[0].iov_base)[0] = extension->major_opcode;
93             ((CARD8 *) vector[0].iov_base)[1] = req->opcode;
94         }
95         else
96             ((CARD8 *) vector[0].iov_base)[0] = req->opcode;
97
98         /* put together the length field, possibly using BIGREQUESTS */
99         for(i = 0; i < req->count; ++i)
100         {
101             longlen += vector[i].iov_len;
102             if(!vector[i].iov_base)
103             {
104                 vector[i].iov_base = (caddr_t) pad;
105                 assert(vector[i].iov_len <= sizeof(pad));
106             }
107         }
108         assert((longlen & 3) == 0);
109         longlen >>= 2;
110
111         if(longlen <= c->setup->maximum_request_length)
112         {
113             /* we don't need BIGREQUESTS. */
114             shortlen = longlen;
115             longlen = 0;
116         }
117         else if(longlen > XCBGetMaximumRequestLength(c))
118             return 0; /* server can't take this; maybe need BIGREQUESTS? */
119
120         /* set the length field. */
121         ((CARD16 *) vector[0].iov_base)[1] = shortlen;
122         if(!shortlen)
123             prefix[2] = ++longlen;
124     }
125     flags &= ~XCB_REQUEST_RAW;
126
127     /* do we need to work around the X server bug described in glx.xml? */
128     if(req->ext && !req->isvoid && strcmp(req->ext->name, "GLX") &&
129             ((req->opcode == 17 && ((CARD32 *) vector[0].iov_base)[0] == 0x10004) ||
130              req->opcode == 21))
131         workaround = WORKAROUND_GLX_GET_FB_CONFIGS_BUG;
132
133     /* get a sequence number and arrange for delivery. */
134     pthread_mutex_lock(&c->iolock);
135     /* wait for other writing threads to get out of my way. */
136     while(c->out.writing)
137         pthread_cond_wait(&c->out.cond, &c->iolock);
138
139     if(req->isvoid && c->out.request == c->in.request_expected + (1 << 16) - 1)
140     {
141         prefix[0] = sync.packet;
142         request = ++c->out.request;
143         _xcb_in_expect_reply(c, request, WORKAROUND_NONE, XCB_REQUEST_DISCARD_REPLY);
144         c->in.request_expected = c->out.request;
145     }
146
147     request = ++c->out.request;
148     assert(request != 0);
149     _xcb_in_expect_reply(c, request, workaround, flags);
150     if(!req->isvoid)
151         c->in.request_expected = c->out.request;
152
153     if(prefix[0] || prefix[2])
154     {
155         --vector, ++veclen;
156         if(prefix[2])
157         {
158             prefix[1] = ((CARD32 *) vector[1].iov_base)[0];
159             vector[1].iov_base = (CARD32 *) vector[1].iov_base + 1;
160             vector[1].iov_len -= sizeof(CARD32);
161         }
162         vector[0].iov_len = sizeof(CARD32) * (prefix[0] ? 1 : 0 | prefix[2] ? 2 : 0);
163         vector[0].iov_base = prefix + !prefix[0];
164     }
165
166     if(!_xcb_out_write_block(c, vector, veclen))
167         request = 0;
168     pthread_mutex_unlock(&c->iolock);
169     return request;
170 }
171
172 int XCBFlush(XCBConnection *c)
173 {
174     int ret;
175     pthread_mutex_lock(&c->iolock);
176     ret = _xcb_out_flush(c);
177     pthread_mutex_unlock(&c->iolock);
178     return ret;
179 }
180
181 /* Private interface */
182
183 int _xcb_out_init(_xcb_out *out)
184 {
185     if(pthread_cond_init(&out->cond, 0))
186         return 0;
187     out->writing = 0;
188
189     out->queue_len = 0;
190     out->vec = 0;
191     out->vec_len = 0;
192
193     out->request = 0;
194     out->request_written = 0;
195
196     if(pthread_mutex_init(&out->reqlenlock, 0))
197         return 0;
198     out->maximum_request_length = 0;
199
200     return 1;
201 }
202
203 void _xcb_out_destroy(_xcb_out *out)
204 {
205     pthread_cond_destroy(&out->cond);
206     pthread_mutex_destroy(&out->reqlenlock);
207 }
208
209 /* precondition: there must be something for us to write. */
210 int _xcb_out_write(XCBConnection *c)
211 {
212     int n;
213     assert(!c->out.queue_len);
214     n = writev(c->fd, c->out.vec, c->out.vec_len);
215     if(n < 0 && errno == EAGAIN)
216         return 1;
217     if(n <= 0)
218         return 0;
219
220     for(; c->out.vec_len; --c->out.vec_len, ++c->out.vec)
221     {
222         int cur = c->out.vec->iov_len;
223         if(cur > n)
224             cur = n;
225         c->out.vec->iov_len -= cur;
226         c->out.vec->iov_base = (char *) c->out.vec->iov_base + cur;
227         n -= cur;
228         if(c->out.vec->iov_len)
229             break;
230     }
231     if(!c->out.vec_len)
232         c->out.vec = 0;
233     assert(n == 0);
234     return 1;
235 }
236
237 int _xcb_out_write_block(XCBConnection *c, struct iovec *vector, size_t count)
238 {
239     assert(!c->out.vec && !c->out.vec_len);
240     while(count && c->out.queue_len + vector[0].iov_len < sizeof(c->out.queue))
241     {
242         memcpy(c->out.queue + c->out.queue_len, vector[0].iov_base, vector[0].iov_len);
243         c->out.queue_len += vector[0].iov_len;
244         vector[0].iov_base = (char *) vector[0].iov_base + vector[0].iov_len;
245         vector[0].iov_len = 0;
246         ++vector, --count;
247     }
248     if(!count)
249         return 1;
250
251     --vector, ++count;
252     vector[0].iov_base = c->out.queue;
253     vector[0].iov_len = c->out.queue_len;
254     c->out.queue_len = 0;
255
256     c->out.vec_len = count;
257     c->out.vec = vector;
258     return _xcb_out_flush(c);
259 }
260
261 int _xcb_out_flush(XCBConnection *c)
262 {
263     int ret = 1;
264     struct iovec vec;
265     if(c->out.queue_len)
266     {
267         assert(!c->out.vec && !c->out.vec_len);
268         vec.iov_base = c->out.queue;
269         vec.iov_len = c->out.queue_len;
270         c->out.vec = &vec;
271         c->out.vec_len = 1;
272         c->out.queue_len = 0;
273     }
274     while(ret && c->out.vec_len)
275         ret = _xcb_conn_wait(c, /*should_write*/ 1, &c->out.cond);
276     c->out.request_written = c->out.request;
277     pthread_cond_broadcast(&c->out.cond);
278     return ret;
279 }