xcb_out.c revision 602e473d
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
38static 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
58static 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
79void 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
102uint32_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
124unsigned 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
258int 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
279int 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
291int 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
304int _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
328void _xcb_out_destroy(_xcb_out *out)
329{
330    pthread_cond_destroy(&out->cond);
331    pthread_mutex_destroy(&out->reqlenlock);
332}
333
334int _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    return ret;
342}
343
344int _xcb_out_flush_to(xcb_connection_t *c, uint64_t request)
345{
346    assert(XCB_SEQUENCE_COMPARE(request, <=, c->out.request));
347    if(XCB_SEQUENCE_COMPARE(c->out.request_written, >=, request))
348        return 1;
349    if(c->out.queue_len)
350    {
351        struct iovec vec, *vec_ptr = &vec;
352        int count = 1;
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_ptr, &count);
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}
363