-- XConnectionEventQImpl.mesa
-- Created By Jeff Weinstein on 2-May-87 16:03:25

DIRECTORY
  Heap,
  Environment,
  TcpStream,
  XConnection,
  XEventQ,
  XQueue;
  
XConnectionEventQImpl:MONITOR IMPORTS  Heap, TcpStream, XQueue EXPORTS XConnection, XEventQ =
  BEGIN
  
  MaxClients: CARDINAL = XConnection.MaxClients;
  
  ConnectionData:TYPE = RECORD[
			  q:XQueue.QueuePtr ← NIL,
			  state:XConnection.State ← unused,
			  connection:TcpStream.Handle
			  ];
			
  zero: CARDINAL ← 0;
    
  Connections:ARRAY [0..MaxClients) OF ConnectionData ← ALL[[NIL,unused,NIL]];
  
  RequestsPending:ARRAY [0..MaxClients) OF BOOLEAN ← ALL[FALSE];
  
  NumPendingRequests:CARDINAL ← 0;
  
  connectionQ:XQueue.QueuePtr ← XQueue.NewQueue[];
  eventQ:XQueue.QueuePtr ← XQueue.NewQueue[];
  
  zone:UNCOUNTED ZONE ← Heap.systemZone;
  
  eventOrClientHint: CONDITION;
  
  ConnectionInUse:PUBLIC ERROR = CODE;
  ConnectionNotOpen:PUBLIC ERROR = CODE;
  ConnectionNotClosing:PUBLIC ERROR = CODE;
  QueueEmpty:PUBLIC ERROR = CODE;
  
  CreateConnection:PUBLIC ENTRY PROCEDURE [client:XConnection.Client, connection:TcpStream.Handle] =
    BEGIN
    
    IF Connections[client].state # unused THEN RETURN WITH ERROR ConnectionInUse;
    Connections[client] ← [XQueue.NewQueue[],open, connection];
    
    END;
    
  DestroyConnection:PUBLIC ENTRY PROCEDURE [client:XConnection.Client] =
    BEGIN
-- WARNING: DOES NOT FREE STUFF STILL ON QUEUE    
    IF Connections[client].state # closing THEN RETURN WITH ERROR ConnectionNotClosing;
    RecalcPending[];
    XQueue.FreeQueue[@Connections[client].q];
    Connections[client].state ← unused;
    RequestsPending[client] ← FALSE;
    Connections[client].connection.destroy[Connections[client].connection];
    END;
    
  RecalcPending:PRIVATE PROCEDURE =
    BEGIN
    NumPendingRequests ← 0;
    FOR i:INTEGER IN [0..MaxClients) DO
      IF RequestsPending[i] = TRUE THEN
	NumPendingRequests ← NumPendingRequests + XQueue.QueueCount[Connections[i].q];
      ENDLOOP;
    END;

  
  ClosingConnection:PUBLIC ENTRY PROCEDURE [client:XConnection.Client] =
    BEGIN
    IF Connections[client].state = unused THEN RETURN WITH ERROR ConnectionNotOpen;
    IF Connections[client].state = open THEN
      BEGIN
	Connections[client].state ← closing;
	NumPendingRequests ← NumPendingRequests + 1;
	RequestsPending[client] ← TRUE;
	BROADCAST eventOrClientHint;
    
	Connections[client].connection.close[!TcpStream.Closed => CONTINUE;
					      TcpStream.Suspended => CONTINUE];
      END;
    END;
    
  EnQueueRequest:PUBLIC ENTRY PROCEDURE [client:XConnection.Client, request:LONG POINTER TO UNSPECIFIED, size:LONG CARDINAL] =
    BEGIN
    IF Connections[client].state # open THEN RETURN WITH ERROR ConnectionNotOpen;
    XQueue.EnQueue[Connections[client].q, request, size];
    NumPendingRequests ← NumPendingRequests + 1;
    RequestsPending[client] ← TRUE;
    BROADCAST eventOrClientHint;
    END;
    
  DeQueueRequest:PUBLIC ENTRY PROCEDURE [client:XConnection.Client] RETURNS [request:LONG POINTER TO UNSPECIFIED, size:LONG CARDINAL] =
    BEGIN
    IF Connections[client].state = unused THEN RETURN WITH ERROR ConnectionNotOpen;
    [request, size] ← XQueue.DeQueue[Connections[client].q];
    IF request = NIL THEN
      BEGIN
	IF Connections[client].state = closing THEN
	  RETURN WITH ERROR ConnectionNotOpen
	ELSE 
	  RETURN WITH ERROR QueueEmpty;
	END;
    IF XQueue.QueueCount[Connections[client].q] = 0 THEN RequestsPending[client] ← FALSE;
    NumPendingRequests ← NumPendingRequests - 1;
    END;
    
  PendingRequests:PUBLIC ENTRY PROCEDURE RETURNS [numRequests:CARDINAL, clients:ARRAY[0..MaxClients) OF BOOLEAN] =
    BEGIN
    numRequests ← NumPendingRequests;
    clients ← RequestsPending;
    END;
  
  PendingConnections:PUBLIC ENTRY PROCEDURE RETURNS[anyPending:BOOLEAN] =
    BEGIN
    anyPending ← XQueue.QueueCount[connectionQ] # 0;
    END;
    
  EnQueueConnection: PUBLIC ENTRY PROCEDURE [connection:TcpStream.Handle] =
    BEGIN
    XQueue.EnQueue[q:connectionQ, data:connection];
    BROADCAST eventOrClientHint;
    END;
    
  DeQueueConnection: PUBLIC ENTRY PROCEDURE RETURNS [connection:TcpStream.Handle] =
    BEGIN
    [data:connection] ← XQueue.DeQueue[q:connectionQ];
    IF connection = NIL THEN RETURN WITH ERROR QueueEmpty;
    END;
   
  NewEvent:PUBLIC PROCEDURE RETURNS[event:XEventQ.EventPtr] =
    BEGIN
    RETURN[zone.NEW[XEventQ.Event]];
    END;

  FreeEvent:PUBLIC PROCEDURE[eventP:LONG POINTER TO XEventQ.EventPtr] =
    BEGIN
    zone.FREE[eventP];
    END;
    
  EnQEvent:PUBLIC ENTRY PROCEDURE[event:XEventQ.EventPtr] =
    BEGIN
    XQueue.EnQueue[eventQ,event];
    BROADCAST eventOrClientHint;
    END;
    
  DeQEvent:PUBLIC ENTRY PROCEDURE RETURNS[event:XEventQ.EventPtr] =
    BEGIN
      [data:event] ← XQueue.DeQueue[eventQ];
--      IF event = NIL THEN
--        RETURN WITH ERROR QueueEmpty;	   
    END; 
    
  EventCount:PUBLIC ENTRY PROCEDURE RETURNS[count:CARDINAL] =
    BEGIN
    count ← XQueue.QueueCount[eventQ];
    END;
  
  EventQPointers:PUBLIC PROCEDURE RETURNS[p1, p2:LONG POINTER] =
    BEGIN
    p1 ← @eventQ.count;
    p2 ← @zero;
    END;
    
  WaitForEventOrClient:PUBLIC ENTRY PROCEDURE =
    BEGIN
      WHILE ( NumPendingRequests = 0 ) AND ( XQueue.QueueCount[eventQ] = 0 ) AND ( XQueue.QueueCount[connectionQ] = 0 ) DO
        WAIT eventOrClientHint;
        ENDLOOP;
    END;
  
  SendToClient:PUBLIC ENTRY PROCEDURE [client:XConnection.Client, data:Environment.Block] =
    BEGIN
      pad:PACKED ARRAY [0..4) OF Environment.Byte ← [0,0,0,0];
      IF Connections[client].state # open THEN GOTO noconnect;
      Connections[client].connection.put[data, TRUE, FALSE!
           TcpStream.Suspended => GOTO noconnect];
      IF ( ( data.stopIndexPlusOne - data.startIndex ) MOD 4 ) > 0 THEN
        Connections[client].connection.put[
	   [@pad, 0, 4 - ( ( data.stopIndexPlusOne - data.startIndex ) MOD 4 )], 
	   TRUE, FALSE!TcpStream.Suspended => GOTO noconnect];

    EXITS
      noconnect => 
        BEGIN
	ClosingConnection[client];
	RETURN WITH ERROR ConnectionNotOpen;
	END;
    END;
      
  -- Mainline code --
    
  END...