server_stream.c revision 3a3f10d6081fdacfd53f8c1fafc0efd2be153b7a
8bb7126c063c34d63966733988411f72dfcb2294maczniak/* Copyright (c) 2001, Stanford University
8bb7126c063c34d63966733988411f72dfcb2294maczniak * All rights reserved
8bb7126c063c34d63966733988411f72dfcb2294maczniak *
8bb7126c063c34d63966733988411f72dfcb2294maczniak * See the file LICENSE.txt for information on redistributing this software.
8bb7126c063c34d63966733988411f72dfcb2294maczniak */
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak#include "server.h"
96ad5d81ee4a2cc66a4ae19893efc8aa6d06fae7jailletc#include "cr_unpack.h"
8bb7126c063c34d63966733988411f72dfcb2294maczniak#include "cr_error.h"
8bb7126c063c34d63966733988411f72dfcb2294maczniak#include "cr_mem.h"
d29d9ab4614ff992b0e8de6e2b88d52b6f1f153erbowen#include "server_dispatch.h"
2e545ce2450a9953665f701bb05350f0d3f26275nd
d29d9ab4614ff992b0e8de6e2b88d52b6f1f153erbowen
d29d9ab4614ff992b0e8de6e2b88d52b6f1f153erbowen/**
8bb7126c063c34d63966733988411f72dfcb2294maczniak * Accept a new client connection, create a new CRClient and add to run queue.
8bb7126c063c34d63966733988411f72dfcb2294maczniak */
af33a4994ae2ff15bc67d19ff1a7feb906745bf8rbowenvoid
3f08db06526d6901aa08c110b5bc7dde6bc39905ndcrServerAddNewClient(void)
8bb7126c063c34d63966733988411f72dfcb2294maczniak{
8bb7126c063c34d63966733988411f72dfcb2294maczniak CRClient *newClient = (CRClient *) crCalloc(sizeof(CRClient));
8bb7126c063c34d63966733988411f72dfcb2294maczniak
3f08db06526d6901aa08c110b5bc7dde6bc39905nd if (newClient) {
8bb7126c063c34d63966733988411f72dfcb2294maczniak newClient->spu_id = cr_server.client_spu_id;
8bb7126c063c34d63966733988411f72dfcb2294maczniak newClient->conn = crNetAcceptClient( cr_server.protocol, NULL,
0cf3cdbaa1dad11cbf1ce32e48f1b4ec88cf779fnilgun cr_server.tcpip_port,
f086b4b402fa9a2fefc7dda85de2a3cc1cd0a654rjung cr_server.mtu, 1 );
8bb7126c063c34d63966733988411f72dfcb2294maczniak newClient->currentCtx = cr_server.DummyContext;
4b575a6b6704b516f22d65a3ad35696d7b9ba372rpluem
4b575a6b6704b516f22d65a3ad35696d7b9ba372rpluem /* add to array */
8bb7126c063c34d63966733988411f72dfcb2294maczniak cr_server.clients[cr_server.numClients++] = newClient;
78f97ce162b66a0dbfd7af4dcd9984f162569b04minfrin
8bb7126c063c34d63966733988411f72dfcb2294maczniak crServerAddToRunQueue( newClient );
8bb7126c063c34d63966733988411f72dfcb2294maczniak }
8bb7126c063c34d63966733988411f72dfcb2294maczniak}
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak/**
8bb7126c063c34d63966733988411f72dfcb2294maczniak * Check if client is in the run queue.
8bb7126c063c34d63966733988411f72dfcb2294maczniak */
8bb7126c063c34d63966733988411f72dfcb2294maczniakstatic GLboolean
30471a4650391f57975f60bbb6e4a90be7b284bfhumbedoohFindClientInQueue(CRClient *client)
8bb7126c063c34d63966733988411f72dfcb2294maczniak{
8bb7126c063c34d63966733988411f72dfcb2294maczniak RunQueue *q = cr_server.run_queue;
8bb7126c063c34d63966733988411f72dfcb2294maczniak while (q) {
8bb7126c063c34d63966733988411f72dfcb2294maczniak if (q->client == client) {
8bb7126c063c34d63966733988411f72dfcb2294maczniak return 1;
8bb7126c063c34d63966733988411f72dfcb2294maczniak }
8bb7126c063c34d63966733988411f72dfcb2294maczniak q = q->next;
8bb7126c063c34d63966733988411f72dfcb2294maczniak if (q == cr_server.run_queue)
8bb7126c063c34d63966733988411f72dfcb2294maczniak return 0; /* back head */
8bb7126c063c34d63966733988411f72dfcb2294maczniak }
8bb7126c063c34d63966733988411f72dfcb2294maczniak return 0;
8bb7126c063c34d63966733988411f72dfcb2294maczniak}
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak#if 0
8bb7126c063c34d63966733988411f72dfcb2294maczniakstatic int
8bb7126c063c34d63966733988411f72dfcb2294maczniakPrintQueue(void)
8bb7126c063c34d63966733988411f72dfcb2294maczniak{
8bb7126c063c34d63966733988411f72dfcb2294maczniak RunQueue *q = cr_server.run_queue;
8bb7126c063c34d63966733988411f72dfcb2294maczniak int count = 0;
8bb7126c063c34d63966733988411f72dfcb2294maczniak crDebug("Queue entries:");
8bb7126c063c34d63966733988411f72dfcb2294maczniak while (q) {
8bb7126c063c34d63966733988411f72dfcb2294maczniak count++;
8bb7126c063c34d63966733988411f72dfcb2294maczniak crDebug("Entry: %p client: %p", q, q->client);
8bb7126c063c34d63966733988411f72dfcb2294maczniak q = q->next;
8bb7126c063c34d63966733988411f72dfcb2294maczniak if (q == cr_server.run_queue)
8bb7126c063c34d63966733988411f72dfcb2294maczniak return count;
8bb7126c063c34d63966733988411f72dfcb2294maczniak }
8bb7126c063c34d63966733988411f72dfcb2294maczniak return count;
8bb7126c063c34d63966733988411f72dfcb2294maczniak}
8bb7126c063c34d63966733988411f72dfcb2294maczniak#endif
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniakvoid crServerAddToRunQueue( CRClient *client )
8bb7126c063c34d63966733988411f72dfcb2294maczniak{
8bb7126c063c34d63966733988411f72dfcb2294maczniak RunQueue *q = (RunQueue *) crAlloc( sizeof( *q ) );
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak /* give this client a unique number if needed */
8bb7126c063c34d63966733988411f72dfcb2294maczniak if (!client->number) {
8bb7126c063c34d63966733988411f72dfcb2294maczniak client->number = crServerGenerateID(&cr_server.idsPool.freeClientID);
8bb7126c063c34d63966733988411f72dfcb2294maczniak }
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak crDebug("Adding client %p to the run queue", client);
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak if (FindClientInQueue(client)) {
8bb7126c063c34d63966733988411f72dfcb2294maczniak crError("CRServer: client %p already in the queue!", client);
8bb7126c063c34d63966733988411f72dfcb2294maczniak }
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak q->client = client;
78f97ce162b66a0dbfd7af4dcd9984f162569b04minfrin q->blocked = 0;
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak if (!cr_server.run_queue)
8bb7126c063c34d63966733988411f72dfcb2294maczniak {
8bb7126c063c34d63966733988411f72dfcb2294maczniak /* adding to empty queue */
8bb7126c063c34d63966733988411f72dfcb2294maczniak cr_server.run_queue = q;
8bb7126c063c34d63966733988411f72dfcb2294maczniak q->next = q;
8bb7126c063c34d63966733988411f72dfcb2294maczniak q->prev = q;
8bb7126c063c34d63966733988411f72dfcb2294maczniak }
8bb7126c063c34d63966733988411f72dfcb2294maczniak else
8bb7126c063c34d63966733988411f72dfcb2294maczniak {
8bb7126c063c34d63966733988411f72dfcb2294maczniak /* insert in doubly-linked list */
8bb7126c063c34d63966733988411f72dfcb2294maczniak q->next = cr_server.run_queue->next;
8bb7126c063c34d63966733988411f72dfcb2294maczniak cr_server.run_queue->next->prev = q;
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak q->prev = cr_server.run_queue;
8bb7126c063c34d63966733988411f72dfcb2294maczniak cr_server.run_queue->next = q;
8bb7126c063c34d63966733988411f72dfcb2294maczniak }
8bb7126c063c34d63966733988411f72dfcb2294maczniak}
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak
8bb7126c063c34d63966733988411f72dfcb2294maczniak
0cf3cdbaa1dad11cbf1ce32e48f1b4ec88cf779fnilgunvoid
f086b4b402fa9a2fefc7dda85de2a3cc1cd0a654rjungcrServerDeleteClient( CRClient *client )
727872d18412fc021f03969b8641810d8896820bhumbedooh{
0d0ba3a410038e179b695446bb149cce6264e0abnd int i, j;
727872d18412fc021f03969b8641810d8896820bhumbedooh int32_t pos;
cc7e1025de9ac63bd4db6fe7f71c158b2cf09fe4humbedooh CRClient *oldclient = cr_server.curClient;
0d0ba3a410038e179b695446bb149cce6264e0abnd
cc7e1025de9ac63bd4db6fe7f71c158b2cf09fe4humbedooh crDebug("Deleting client %p (%d msgs left)", client, crNetNumMessages(client->conn));
727872d18412fc021f03969b8641810d8896820bhumbedooh
0d0ba3a410038e179b695446bb149cce6264e0abnd#if 0
0d0ba3a410038e179b695446bb149cce6264e0abnd if (crNetNumMessages(client->conn) > 0) {
0d0ba3a410038e179b695446bb149cce6264e0abnd crDebug("Delay destroying client: message still pending");
ac082aefa89416cbdc9a1836eaf3bed9698201c8humbedooh return;
0d0ba3a410038e179b695446bb149cce6264e0abnd }
0d0ba3a410038e179b695446bb149cce6264e0abnd#endif
0d0ba3a410038e179b695446bb149cce6264e0abnd
727872d18412fc021f03969b8641810d8896820bhumbedooh if (!FindClientInQueue(client)) {
0d0ba3a410038e179b695446bb149cce6264e0abnd /* this should never happen */
0d0ba3a410038e179b695446bb149cce6264e0abnd crError("CRServer: client %p not found in the queue!", client);
30471a4650391f57975f60bbb6e4a90be7b284bfhumbedooh }
205f749042ed530040a4f0080dbcb47ceae8a374rjung
af33a4994ae2ff15bc67d19ff1a7feb906745bf8rbowen /* remove from clients[] array */
0d0ba3a410038e179b695446bb149cce6264e0abnd for (i = 0; i < cr_server.numClients; i++) {
7fec19672a491661b2fe4b29f685bc7f4efa64d4nd if (cr_server.clients[i] == client) {
7fec19672a491661b2fe4b29f685bc7f4efa64d4nd /* found it */
7fec19672a491661b2fe4b29f685bc7f4efa64d4nd for (j = i; j < cr_server.numClients - 1; j++)
8bb7126c063c34d63966733988411f72dfcb2294maczniak cr_server.clients[j] = cr_server.clients[j + 1];
cr_server.numClients--;
break;
}
}
cr_server.curClient = client;
/* Destroy any windows created by the client */
for (pos = 0; pos<CR_MAX_WINDOWS && client->windowList[pos]; pos++)
{
cr_server.dispatch.WindowDestroy(client->windowList[pos]);
}
/* Check if we have context(s) made by this client left, could happen if client side code is lazy */
for (pos = 0; pos<CR_MAX_CONTEXTS && client->contextList[pos]; pos++)
{
cr_server.dispatch.DestroyContext(client->contextList[pos]);
}
cr_server.curClient = oldclient;
/* remove from the run queue */
if (cr_server.run_queue)
{
RunQueue *q = cr_server.run_queue;
RunQueue *qStart = cr_server.run_queue;
do {
if (q->client == client)
{
/* this test seems a bit excessive */
if ((q->next == q->prev) && (q->next == q) && (cr_server.run_queue == q))
{
/* We're removing/deleting the only client */
CRASSERT(cr_server.numClients == 0);
crFree(q);
cr_server.run_queue = NULL;
cr_server.curClient = NULL;
crDebug("Last client deleted - empty run queue.");
}
else
{
/* remove from doubly linked list and free the node */
if (cr_server.curClient == q->client)
cr_server.curClient = NULL;
if (cr_server.run_queue == q)
cr_server.run_queue = q->next;
q->prev->next = q->next;
q->next->prev = q->prev;
crFree(q);
}
break;
}
q = q->next;
} while (q != qStart);
}
crNetFreeConnection(client->conn);
crFree(client);
}
/**
* Test if the given client is in the middle of a glBegin/End or
* glNewList/EndList pair.
* This is used to test if we can advance to the next client.
* \return GL_TRUE if so, GL_FALSE otherwise.
*/
GLboolean
crServerClientInBeginEnd(const CRClient *client)
{
if (client->currentCtx &&
(client->currentCtx->lists.currentIndex != 0 ||
client->currentCtx->current.inBeginEnd ||
client->currentCtx->occlusion.currentQueryObject)) {
return GL_TRUE;
}
else {
return GL_FALSE;
}
}
/**
* Find the next client in the run queue that's not blocked and has a
* waiting message.
* Check if all clients are blocked (on barriers, semaphores), if so we've
* deadlocked!
* If no clients have a waiting message, call crNetRecv to get something
* if 'block' is true, else return NULL if 'block' if false.
*/
static RunQueue *
getNextClient(GLboolean block)
{
while (1)
{
if (cr_server.run_queue)
{
GLboolean all_blocked = GL_TRUE;
GLboolean done_something = GL_FALSE;
RunQueue *start = cr_server.run_queue;
/* check if this client's connection has gone away */
if (!cr_server.run_queue->client->conn
|| (cr_server.run_queue->client->conn->type == CR_NO_CONNECTION
&& crNetNumMessages(cr_server.run_queue->client->conn) == 0))
{
crServerDeleteClient( cr_server.run_queue->client );
start = cr_server.run_queue;
}
if (cr_server.run_queue == NULL) {
/* empty queue */
return NULL;
}
if (crServerClientInBeginEnd(cr_server.run_queue->client)) {
/* We _must_ service this client and no other.
* If we've got a message waiting on this client's connection we'll
* service it. Else, return NULL.
*/
if (crNetNumMessages(cr_server.run_queue->client->conn) > 0)
return cr_server.run_queue;
else
return NULL;
}
/* loop over entries in run queue, looking for next one that's ready */
while (!done_something || cr_server.run_queue != start)
{
done_something = GL_TRUE;
if (!cr_server.run_queue->blocked)
{
all_blocked = GL_FALSE;
}
if (!cr_server.run_queue->blocked
&& cr_server.run_queue->client->conn
&& crNetNumMessages(cr_server.run_queue->client->conn) > 0)
{
/* OK, this client isn't blocked and has a queued message */
return cr_server.run_queue;
}
cr_server.run_queue = cr_server.run_queue->next;
}
if (all_blocked)
{
/* XXX crError is fatal? Should this be an info/warning msg? */
crError( "crserver: DEADLOCK! (numClients=%d, all blocked)",
cr_server.numClients );
if (cr_server.numClients < (int) cr_server.maxBarrierCount) {
crError("Waiting for more clients!!!");
while (cr_server.numClients < (int) cr_server.maxBarrierCount) {
crNetRecv();
}
}
}
}
if (!block)
return NULL;
/* no one had any work, get some! */
crNetRecv();
} /* while */
/* UNREACHED */
/* return NULL; */
}
/**
* This function takes the given message (which should be a buffer of
* rendering commands) and executes it.
*/
static void
crServerDispatchMessage(CRMessage *msg)
{
const CRMessageOpcodes *msg_opcodes;
int opcodeBytes;
const char *data_ptr;
if (msg->header.type == CR_MESSAGE_REDIR_PTR)
{
msg = (CRMessage *) msg->redirptr.pMessage;
}
CRASSERT(msg->header.type == CR_MESSAGE_OPCODES);
msg_opcodes = (const CRMessageOpcodes *) msg;
opcodeBytes = (msg_opcodes->numOpcodes + 3) & ~0x03;
#ifdef VBOXCR_LOGFPS
CRASSERT(cr_server.curClient && cr_server.curClient->conn && cr_server.curClient->conn->id == msg->header.conn_id);
cr_server.curClient->conn->opcodes_count += msg_opcodes->numOpcodes;
#endif
data_ptr = (const char *) msg_opcodes + sizeof(CRMessageOpcodes) + opcodeBytes;
crUnpack(data_ptr, /* first command's operands */
data_ptr - 1, /* first command's opcode */
msg_opcodes->numOpcodes, /* how many opcodes */
&(cr_server.dispatch)); /* the CR dispatch table */
}
typedef enum
{
CLIENT_GONE = 1, /* the client has disconnected */
CLIENT_NEXT = 2, /* we can advance to next client */
CLIENT_MORE = 3 /* we need to keep servicing current client */
} ClientStatus;
/**
* Process incoming/pending message for the given client (queue entry).
* \return CLIENT_GONE if this client has gone away/exited,
* CLIENT_NEXT if we can advance to the next client
* CLIENT_MORE if we have to process more messages for this client.
*/
static ClientStatus
crServerServiceClient(const RunQueue *qEntry)
{
CRMessage *msg;
CRConnection *conn;
/* set current client pointer */
cr_server.curClient = qEntry->client;
conn = cr_server.run_queue->client->conn;
/* service current client as long as we can */
while (conn && conn->type != CR_NO_CONNECTION &&
crNetNumMessages(conn) > 0) {
unsigned int len;
/*
crDebug("%d messages on %p",
crNetNumMessages(conn), (void *) conn);
*/
/* Don't use GetMessage, because we want to do our own crNetRecv() calls
* here ourself.
* Note that crNetPeekMessage() DOES remove the message from the queue
* if there is one.
*/
len = crNetPeekMessage( conn, &msg );
CRASSERT(len > 0);
if (msg->header.type != CR_MESSAGE_OPCODES
&& msg->header.type != CR_MESSAGE_REDIR_PTR) {
crError( "SPU %d sent me CRAP (type=0x%x)",
cr_server.curClient->spu_id, msg->header.type );
}
/* Do the context switch here. No sense in switching before we
* really have any work to process. This is a no-op if we're
* not really switching contexts.
*
* XXX This isn't entirely sound. The crStateMakeCurrent() call
* will compute the state difference and dispatch it using
* the head SPU's dispatch table.
*
* This is a problem if this is the first buffer coming in,
* and the head SPU hasn't had a chance to do a MakeCurrent()
* yet (likely because the MakeCurrent() command is in the
* buffer itself).
*
* At best, in this case, the functions are no-ops, and
* are essentially ignored by the SPU. In the typical
* case, things aren't too bad; if the SPU just calls
* crState*() functions to update local state, everything
* will work just fine.
*
* In the worst (but unusual) case where a nontrivial
* SPU is at the head of a crserver's SPU chain (say,
* in a multiple-tiered "tilesort" arrangement, as
* seen in the "multitilesort.conf" configuration), the
* SPU may rely on state set during the MakeCurrent() that
* may not be present yet, because no MakeCurrent() has
* yet been dispatched.
*
* This headache will have to be revisited in the future;
* for now, SPUs that could head a crserver's SPU chain
* will have to detect the case that their functions are
* being called outside of a MakeCurrent(), and will have
* to handle the situation gracefully. (This is currently
* the case with the "tilesort" SPU.)
*/
#if 0
crStateMakeCurrent( cr_server.curClient->currentCtx );
#else
/* Check if the current window is the one that the client wants to
* draw into. If not, dispatch a MakeCurrent to activate the proper
* window.
*/
if (cr_server.curClient) {
int clientWindow = cr_server.curClient->currentWindow;
int clientContext = cr_server.curClient->currentContextNumber;
if (clientWindow && clientWindow != cr_server.currentWindow) {
crServerDispatchMakeCurrent(clientWindow, 0, clientContext);
/*
CRASSERT(cr_server.currentWindow == clientWindow);
*/
}
}
crStateMakeCurrent( cr_server.curClient->currentCtx );
#endif
/* Force scissor, viewport and projection matrix update in
* crServerSetOutputBounds().
*/
cr_server.currentSerialNo = 0;
/* Commands get dispatched here */
crServerDispatchMessage(msg);
crNetFree( conn, msg );
if (qEntry->blocked) {
/* Note/assert: we should not be inside a glBegin/End or glNewList/
* glEndList pair at this time!
*/
return CLIENT_NEXT;
}
} /* while */
/*
* Check if client/connection is gone
*/
if (!conn || conn->type == CR_NO_CONNECTION) {
crDebug("Delete client %p at %d", cr_server.run_queue->client, __LINE__);
crServerDeleteClient( cr_server.run_queue->client );
return CLIENT_GONE;
}
/*
* Determine if we can advance to next client.
* If we're currently inside a glBegin/End primitive or building a display
* list we can't service another client until we're done with the
* primitive/list.
*/
if (crServerClientInBeginEnd(cr_server.curClient)) {
/* The next message has to come from the current client's connection. */
CRASSERT(!qEntry->blocked);
return CLIENT_MORE;
}
else {
/* get next client */
return CLIENT_NEXT;
}
}
/**
* Check if any of the clients need servicing.
* If so, service one client and return.
* Else, just return.
*/
void
crServerServiceClients(void)
{
RunQueue *q;
q = getNextClient(GL_FALSE); /* don't block */
while (q)
{
ClientStatus stat = crServerServiceClient(q);
if (stat == CLIENT_NEXT && cr_server.run_queue->next) {
/* advance to next client */
cr_server.run_queue = cr_server.run_queue->next;
}
q = getNextClient(GL_FALSE);
}
}
/**
* Main crserver loop. Service connections from all connected clients.
* XXX add a config option to specify whether the crserver
* should exit when there's no more clients.
*/
void
crServerSerializeRemoteStreams(void)
{
/*MSG msg;*/
while (cr_server.run_queue)
{
crServerServiceClients();
/*if (PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
{
if (msg.message == WM_QUIT)
{
PostQuitMessage((int)msg.wParam);
break;
}
TranslateMessage( &msg );
DispatchMessage( &msg );
}*/
}
}
/**
* This will be called by the network layer when it's received a new message.
*/
int
crServerRecv( CRConnection *conn, CRMessage *msg, unsigned int len )
{
CRMessage *pRealMsg;
(void) len;
pRealMsg = (msg->header.type!=CR_MESSAGE_REDIR_PTR) ? msg : (CRMessage*) msg->redirptr.pMessage;
switch( pRealMsg->header.type )
{
/* Called when using multiple threads */
case CR_MESSAGE_NEWCLIENT:
crServerAddNewClient();
return 1; /* msg handled */
default:
/*crWarning( "Why is the crserver getting a message of type 0x%x?",
msg->header.type ); */
;
}
return 0; /* not handled */
}