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