VirtualBox

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

Last change on this file since 39265 was 33989, checked in by vboxsync, 14 years ago

crOpenGL: those ain't continuous arrays

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