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