VirtualBox

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

Last change on this file since 18255 was 17964, checked in by vboxsync, 16 years ago

crOpenGL: fix client dissconection handling

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