VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c@ 21781

Last change on this file since 21781 was 21127, checked in by vboxsync, 15 years ago

crOpenGL: fix window/context management for multithreaded apps

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 16.8 KB
Line 
1/* Copyright (c) 2001, Stanford University
2 * All rights reserved
3 *
4 * See the file LICENSE.txt for information on redistributing this software.
5 */
6
7#include "server.h"
8#include "cr_unpack.h"
9#include "cr_error.h"
10#include "cr_mem.h"
11#include "server_dispatch.h"
12
13
14/**
15 * Accept a new client connection, create a new CRClient and add to run queue.
16 */
17void
18crServerAddNewClient(void)
19{
20 CRClient *newClient = (CRClient *) crCalloc(sizeof(CRClient));
21
22 if (newClient) {
23 newClient->spu_id = cr_server.client_spu_id;
24 newClient->conn = crNetAcceptClient( cr_server.protocol, NULL,
25 cr_server.tcpip_port,
26 cr_server.mtu, 1 );
27 newClient->currentCtx = cr_server.DummyContext;
28
29 /* add to array */
30 cr_server.clients[cr_server.numClients++] = newClient;
31
32 crServerAddToRunQueue( newClient );
33 }
34}
35
36
37/**
38 * Check if client is in the run queue.
39 */
40static GLboolean
41FindClientInQueue(CRClient *client)
42{
43 RunQueue *q = cr_server.run_queue;
44 while (q) {
45 if (q->client == client) {
46 return 1;
47 }
48 q = q->next;
49 if (q == cr_server.run_queue)
50 return 0; /* back head */
51 }
52 return 0;
53}
54
55
56#if 0
57static int
58PrintQueue(void)
59{
60 RunQueue *q = cr_server.run_queue;
61 int count = 0;
62 crDebug("Queue entries:");
63 while (q) {
64 count++;
65 crDebug("Entry: %p client: %p", q, q->client);
66 q = q->next;
67 if (q == cr_server.run_queue)
68 return count;
69 }
70 return count;
71}
72#endif
73
74
75void crServerAddToRunQueue( CRClient *client )
76{
77 RunQueue *q = (RunQueue *) crAlloc( sizeof( *q ) );
78
79 /* give this client a unique number if needed */
80 if (!client->number) {
81 client->number = crServerGenerateID(&cr_server.idsPool.freeClientID);
82 }
83
84 crDebug("Adding client %p to the run queue", client);
85
86 if (FindClientInQueue(client)) {
87 crError("CRServer: client %p already in the queue!", client);
88 }
89
90 q->client = client;
91 q->blocked = 0;
92
93 if (!cr_server.run_queue)
94 {
95 /* adding to empty queue */
96 cr_server.run_queue = q;
97 q->next = q;
98 q->prev = q;
99 }
100 else
101 {
102 /* insert in doubly-linked list */
103 q->next = cr_server.run_queue->next;
104 cr_server.run_queue->next->prev = q;
105
106 q->prev = cr_server.run_queue;
107 cr_server.run_queue->next = q;
108 }
109}
110
111
112
113void
114crServerDeleteClient( CRClient *client )
115{
116 int i, j;
117 int32_t pos;
118 CRClient *oldclient = cr_server.curClient;
119
120 crDebug("Deleting client %p (%d msgs left)", client, crNetNumMessages(client->conn));
121
122#if 0
123 if (crNetNumMessages(client->conn) > 0) {
124 crDebug("Delay destroying client: message still pending");
125 return;
126 }
127#endif
128
129 if (!FindClientInQueue(client)) {
130 /* this should never happen */
131 crError("CRServer: client %p not found in the queue!", client);
132 }
133
134 /* remove from clients[] array */
135 for (i = 0; i < cr_server.numClients; i++) {
136 if (cr_server.clients[i] == client) {
137 /* found it */
138 for (j = i; j < cr_server.numClients - 1; j++)
139 cr_server.clients[j] = cr_server.clients[j + 1];
140 cr_server.numClients--;
141 break;
142 }
143 }
144
145 cr_server.curClient = client;
146
147 /* Destroy any windows created by the client */
148 for (pos = 0; pos<CR_MAX_WINDOWS && client->windowList[pos]; pos++)
149 {
150 cr_server.dispatch.WindowDestroy(client->windowList[pos]);
151 }
152
153 /* Check if we have context(s) made by this client left, could happen if client side code is lazy */
154 for (pos = 0; pos<CR_MAX_CONTEXTS && client->contextList[pos]; pos++)
155 {
156 cr_server.dispatch.DestroyContext(client->contextList[pos]);
157 }
158
159 cr_server.curClient = oldclient;
160
161 /* remove from the run queue */
162 if (cr_server.run_queue)
163 {
164 RunQueue *q = cr_server.run_queue;
165 RunQueue *qStart = cr_server.run_queue;
166 do {
167 if (q->client == client)
168 {
169 /* this test seems a bit excessive */
170 if ((q->next == q->prev) && (q->next == q) && (cr_server.run_queue == q))
171 {
172 /* We're removing/deleting the only client */
173 CRASSERT(cr_server.numClients == 0);
174 crFree(q);
175 cr_server.run_queue = NULL;
176 cr_server.curClient = NULL;
177 crDebug("Last client deleted - empty run queue.");
178 }
179 else
180 {
181 /* remove from doubly linked list and free the node */
182 if (cr_server.curClient == q->client)
183 cr_server.curClient = NULL;
184 if (cr_server.run_queue == q)
185 cr_server.run_queue = q->next;
186 q->prev->next = q->next;
187 q->next->prev = q->prev;
188 crFree(q);
189 }
190 break;
191 }
192 q = q->next;
193 } while (q != qStart);
194 }
195
196 crNetFreeConnection(client->conn);
197
198 crFree(client);
199}
200
201
202/**
203 * Test if the given client is in the middle of a glBegin/End or
204 * glNewList/EndList pair.
205 * This is used to test if we can advance to the next client.
206 * \return GL_TRUE if so, GL_FALSE otherwise.
207 */
208GLboolean
209crServerClientInBeginEnd(const CRClient *client)
210{
211 if (client->currentCtx &&
212 (client->currentCtx->lists.currentIndex != 0 ||
213 client->currentCtx->current.inBeginEnd ||
214 client->currentCtx->occlusion.currentQueryObject)) {
215 return GL_TRUE;
216 }
217 else {
218 return GL_FALSE;
219 }
220}
221
222
223/**
224 * Find the next client in the run queue that's not blocked and has a
225 * waiting message.
226 * Check if all clients are blocked (on barriers, semaphores), if so we've
227 * deadlocked!
228 * If no clients have a waiting message, call crNetRecv to get something
229 * if 'block' is true, else return NULL if 'block' if false.
230 */
231static RunQueue *
232getNextClient(GLboolean block)
233{
234 while (1)
235 {
236 if (cr_server.run_queue)
237 {
238 GLboolean all_blocked = GL_TRUE;
239 GLboolean done_something = GL_FALSE;
240 RunQueue *start = cr_server.run_queue;
241
242 /* check if this client's connection has gone away */
243 if (!cr_server.run_queue->client->conn
244 || (cr_server.run_queue->client->conn->type == CR_NO_CONNECTION
245 && crNetNumMessages(cr_server.run_queue->client->conn) == 0))
246 {
247 crServerDeleteClient( cr_server.run_queue->client );
248 start = cr_server.run_queue;
249 }
250
251 if (cr_server.run_queue == NULL) {
252 /* empty queue */
253 return NULL;
254 }
255
256 if (crServerClientInBeginEnd(cr_server.run_queue->client)) {
257 /* We _must_ service this client and no other.
258 * If we've got a message waiting on this client's connection we'll
259 * service it. Else, return NULL.
260 */
261 if (crNetNumMessages(cr_server.run_queue->client->conn) > 0)
262 return cr_server.run_queue;
263 else
264 return NULL;
265 }
266
267 /* loop over entries in run queue, looking for next one that's ready */
268 while (!done_something || cr_server.run_queue != start)
269 {
270 done_something = GL_TRUE;
271 if (!cr_server.run_queue->blocked)
272 {
273 all_blocked = GL_FALSE;
274 }
275 if (!cr_server.run_queue->blocked
276 && cr_server.run_queue->client->conn
277 && crNetNumMessages(cr_server.run_queue->client->conn) > 0)
278 {
279 /* OK, this client isn't blocked and has a queued message */
280 return cr_server.run_queue;
281 }
282 cr_server.run_queue = cr_server.run_queue->next;
283 }
284
285 if (all_blocked)
286 {
287 /* XXX crError is fatal? Should this be an info/warning msg? */
288 crError( "crserver: DEADLOCK! (numClients=%d, all blocked)",
289 cr_server.numClients );
290 if (cr_server.numClients < (int) cr_server.maxBarrierCount) {
291 crError("Waiting for more clients!!!");
292 while (cr_server.numClients < (int) cr_server.maxBarrierCount) {
293 crNetRecv();
294 }
295 }
296 }
297 }
298
299 if (!block)
300 return NULL;
301
302 /* no one had any work, get some! */
303 crNetRecv();
304
305 } /* while */
306
307 /* UNREACHED */
308 /* return NULL; */
309}
310
311
312/**
313 * This function takes the given message (which should be a buffer of
314 * rendering commands) and executes it.
315 */
316static void
317crServerDispatchMessage(CRMessage *msg)
318{
319 const CRMessageOpcodes *msg_opcodes;
320 int opcodeBytes;
321 const char *data_ptr;
322
323 if (msg->header.type == CR_MESSAGE_REDIR_PTR)
324 {
325 msg = (CRMessage *) msg->redirptr.pMessage;
326 }
327
328 CRASSERT(msg->header.type == CR_MESSAGE_OPCODES);
329
330 msg_opcodes = (const CRMessageOpcodes *) msg;
331 opcodeBytes = (msg_opcodes->numOpcodes + 3) & ~0x03;
332
333 data_ptr = (const char *) msg_opcodes + sizeof(CRMessageOpcodes) + opcodeBytes;
334 crUnpack(data_ptr, /* first command's operands */
335 data_ptr - 1, /* first command's opcode */
336 msg_opcodes->numOpcodes, /* how many opcodes */
337 &(cr_server.dispatch)); /* the CR dispatch table */
338}
339
340
341typedef enum
342{
343 CLIENT_GONE = 1, /* the client has disconnected */
344 CLIENT_NEXT = 2, /* we can advance to next client */
345 CLIENT_MORE = 3 /* we need to keep servicing current client */
346} ClientStatus;
347
348
349/**
350 * Process incoming/pending message for the given client (queue entry).
351 * \return CLIENT_GONE if this client has gone away/exited,
352 * CLIENT_NEXT if we can advance to the next client
353 * CLIENT_MORE if we have to process more messages for this client.
354 */
355static ClientStatus
356crServerServiceClient(const RunQueue *qEntry)
357{
358 CRMessage *msg;
359 CRConnection *conn;
360
361 /* set current client pointer */
362 cr_server.curClient = qEntry->client;
363
364 conn = cr_server.run_queue->client->conn;
365
366 /* service current client as long as we can */
367 while (conn && conn->type != CR_NO_CONNECTION &&
368 crNetNumMessages(conn) > 0) {
369 unsigned int len;
370
371 /*
372 crDebug("%d messages on %p",
373 crNetNumMessages(conn), (void *) conn);
374 */
375
376 /* Don't use GetMessage, because we want to do our own crNetRecv() calls
377 * here ourself.
378 * Note that crNetPeekMessage() DOES remove the message from the queue
379 * if there is one.
380 */
381 len = crNetPeekMessage( conn, &msg );
382 CRASSERT(len > 0);
383 if (msg->header.type != CR_MESSAGE_OPCODES
384 && msg->header.type != CR_MESSAGE_REDIR_PTR) {
385 crError( "SPU %d sent me CRAP (type=0x%x)",
386 cr_server.curClient->spu_id, msg->header.type );
387 }
388
389 /* Do the context switch here. No sense in switching before we
390 * really have any work to process. This is a no-op if we're
391 * not really switching contexts.
392 *
393 * XXX This isn't entirely sound. The crStateMakeCurrent() call
394 * will compute the state difference and dispatch it using
395 * the head SPU's dispatch table.
396 *
397 * This is a problem if this is the first buffer coming in,
398 * and the head SPU hasn't had a chance to do a MakeCurrent()
399 * yet (likely because the MakeCurrent() command is in the
400 * buffer itself).
401 *
402 * At best, in this case, the functions are no-ops, and
403 * are essentially ignored by the SPU. In the typical
404 * case, things aren't too bad; if the SPU just calls
405 * crState*() functions to update local state, everything
406 * will work just fine.
407 *
408 * In the worst (but unusual) case where a nontrivial
409 * SPU is at the head of a crserver's SPU chain (say,
410 * in a multiple-tiered "tilesort" arrangement, as
411 * seen in the "multitilesort.conf" configuration), the
412 * SPU may rely on state set during the MakeCurrent() that
413 * may not be present yet, because no MakeCurrent() has
414 * yet been dispatched.
415 *
416 * This headache will have to be revisited in the future;
417 * for now, SPUs that could head a crserver's SPU chain
418 * will have to detect the case that their functions are
419 * being called outside of a MakeCurrent(), and will have
420 * to handle the situation gracefully. (This is currently
421 * the case with the "tilesort" SPU.)
422 */
423
424#if 0
425 crStateMakeCurrent( cr_server.curClient->currentCtx );
426#else
427 crStateMakeCurrent( cr_server.curClient->currentCtx );
428
429 /* Check if the current window is the one that the client wants to
430 * draw into. If not, dispatch a MakeCurrent to activate the proper
431 * window.
432 */
433 if (cr_server.curClient) {
434 int clientWindow = cr_server.curClient->currentWindow;
435 int clientContext = cr_server.curClient->currentContextNumber;
436 if (clientWindow && clientWindow != cr_server.currentWindow) {
437 crServerDispatchMakeCurrent(clientWindow, 0, clientContext);
438 /*
439 CRASSERT(cr_server.currentWindow == clientWindow);
440 */
441 }
442 }
443#endif
444
445 /* Force scissor, viewport and projection matrix update in
446 * crServerSetOutputBounds().
447 */
448 cr_server.currentSerialNo = 0;
449
450 /* Commands get dispatched here */
451 crServerDispatchMessage(msg);
452
453 crNetFree( conn, msg );
454
455 if (qEntry->blocked) {
456 /* Note/assert: we should not be inside a glBegin/End or glNewList/
457 * glEndList pair at this time!
458 */
459 return CLIENT_NEXT;
460 }
461
462 } /* while */
463
464 /*
465 * Check if client/connection is gone
466 */
467 if (!conn || conn->type == CR_NO_CONNECTION) {
468 crDebug("Delete client %p at %d", cr_server.run_queue->client, __LINE__);
469 crServerDeleteClient( cr_server.run_queue->client );
470 return CLIENT_GONE;
471 }
472
473 /*
474 * Determine if we can advance to next client.
475 * If we're currently inside a glBegin/End primitive or building a display
476 * list we can't service another client until we're done with the
477 * primitive/list.
478 */
479 if (crServerClientInBeginEnd(cr_server.curClient)) {
480 /* The next message has to come from the current client's connection. */
481 CRASSERT(!qEntry->blocked);
482 return CLIENT_MORE;
483 }
484 else {
485 /* get next client */
486 return CLIENT_NEXT;
487 }
488}
489
490
491
492/**
493 * Check if any of the clients need servicing.
494 * If so, service one client and return.
495 * Else, just return.
496 */
497void
498crServerServiceClients(void)
499{
500 RunQueue *q;
501
502 q = getNextClient(GL_FALSE); /* don't block */
503 while (q)
504 {
505 ClientStatus stat = crServerServiceClient(q);
506 if (stat == CLIENT_NEXT && cr_server.run_queue->next) {
507 /* advance to next client */
508 cr_server.run_queue = cr_server.run_queue->next;
509 }
510 q = getNextClient(GL_FALSE);
511 }
512}
513
514
515
516
517/**
518 * Main crserver loop. Service connections from all connected clients.
519 * XXX add a config option to specify whether the crserver
520 * should exit when there's no more clients.
521 */
522void
523crServerSerializeRemoteStreams(void)
524{
525 /*MSG msg;*/
526
527 while (cr_server.run_queue)
528 {
529 crServerServiceClients();
530 /*if (PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
531 {
532 if (msg.message == WM_QUIT)
533 {
534 PostQuitMessage((int)msg.wParam);
535 break;
536 }
537 TranslateMessage( &msg );
538 DispatchMessage( &msg );
539 }*/
540 }
541}
542
543
544/**
545 * This will be called by the network layer when it's received a new message.
546 */
547int
548crServerRecv( CRConnection *conn, CRMessage *msg, unsigned int len )
549{
550 CRMessage *pRealMsg;
551 (void) len;
552
553 pRealMsg = (msg->header.type!=CR_MESSAGE_REDIR_PTR) ? msg : (CRMessage*) msg->redirptr.pMessage;
554
555 switch( pRealMsg->header.type )
556 {
557 /* Called when using multiple threads */
558 case CR_MESSAGE_NEWCLIENT:
559 crServerAddNewClient();
560 return 1; /* msg handled */
561 default:
562 /*crWarning( "Why is the crserver getting a message of type 0x%x?",
563 msg->header.type ); */
564 ;
565 }
566 return 0; /* not handled */
567}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette