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

DIRECTORY
  Environment,
  TcpStream,
  XConnection,
  XQueue;
  
XConnectionImpl:MONITOR IMPORTS TcpStream, XQueue EXPORTS XConnection =
  BEGIN
  
  MaxClients: CARDINAL = XConnection.MaxClients;
  
  ConnectionData:TYPE = RECORD[
			  q:XQueue.QueuePtr ← NIL,
			  state:XConnection.State ← unused,
			  connection:TcpStream.Handle
			  ];
			  
  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[];
  
  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;
    NumPendingRequests ← NumPendingRequests - XQueue.QueueCount[Connections[client].q];
    XQueue.FreeQueue[@Connections[client].q];
    Connections[client].state ← unused;
    RequestsPending[client] ← FALSE;
    END;
  
  ClosingConnection:PUBLIC ENTRY PROCEDURE [client:XConnection.Client] =
    BEGIN
    IF Connections[client].state # open THEN RETURN WITH ERROR ConnectionNotOpen;
    Connections[client].state ← closing;
    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;
    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];
    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;
  
  SendToClient:PUBLIC ENTRY PROCEDURE [client:XConnection.Client, data:Environment.Block] =
    BEGIN
      IF Connections[client].state # open THEN GOTO noconnect;
      Connections[client].connection.put[data, TRUE, FALSE!
           TcpStream.Suspended => GOTO noconnect];
    EXITS
      noconnect => 
        BEGIN
	Connections[client].state ← closing;
	RETURN WITH ERROR ConnectionNotOpen;
	END;
    END;
  -- Mainline code --
    
  END...