VirtualBox

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

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

crOpenGL/wddm: more multithreading fixes, vista expirience index works now

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

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