VirtualBox

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

Last change on this file since 39947 was 39288, checked in by vboxsync, 13 years ago

CrOpenGL: avoid blocked client polling & extra memcpy (block hgsmi command until completion)

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