En este problema se propone buscar una solución que gestione las operaciones, tanto de lectura como de escritura, que realizan unos usuarios sobre la información almacenada en varios discos independientes. Como se proponía en el gráfico del enunciado, cada disco va a tener asociados: un proceso Manejador (manipulador directo) del disco y un proceso Servidor que atiende y gestiona todas las peticiones que se hagan sobre él. Todos los discos son similares, y se diferencian porque cada uno tiene un identificador diferente. Por tanto, la solución del problema sólo requiere la implementación de tres tipos de tareas: Manejador, Servidor y Cliente.
Obsérvese la función que desempeñan las operaciones de las colas de peticiones, en la implementación de la sincronización en el proceso servidor. Por otro lado, resulta relevante el hecho de que el proceso manejador desempeñe una actitud activa en la ejecución, no teniendo que esperar a que exista una petición de operación del Servidor, sino adelantándose a ésta e indicando su situación de ``preparado'' para operar sobre el disco.
MODULE Discos; FROM COLAS IMPORT TipoCola, (* TipoPeticion *) CreaVacia, Llena, Vacia, Insertar, Borrar, Primero; CONST NumDiscos = 3; NumClientes = 5; TYPE TipoDato = INTEGER; TYPE TipoPeticion = RECORD sector: INTEGER; CASE esLectura: BOOLEAN OF TRUE: cliente: INTEGER | FALSE: dato: TipoDato END END; TYPE RespuestaLectura = RECORD dato: TipoDato; cliente: INTEGER END; VAR datoLeido: ARRAY [1..NumClientes] OF CHANNEL; (* cliente *) VAR pedirOperacion, pedirOrden, retornoLectura, (* servidor *) orden: (* disco *) ARRAY [1..NumDiscos] OF CHANNEL; TASK Manejador (i: INTEGER); VAR peticion: TipoPeticion; respuesta: RespuestaLectura; BEGIN (*... iniciar disco ...*) LOOP Send(pedirOrden[i], TRUE); Receive(orden[i], peticion); IF peticion.esLectura THEN (*... Leer 'respuesta.dato' del sector 'peticion.sector' ...*) respuesta.cliente := peticion.cliente; Send(retornoLectura[i], respuesta) ELSE (*... Escribir 'peticion.dato' en 'peticion.sector' ...*) END END END Manejador; TASK Servidor(i: INTEGER); VAR cola: TipoCola; x: BOOLEAN; peticion: TipoPeticion; respuesta: RespuestaLectura; BEGIN GetChannel(pedirOperacion[i]); GetChannel(pedirOrden[i]); GetChannel(retornoLectura); CrearVacia (cola); LOOP SELECT WHEN NOT Llena(cola), Receive(pedirOperacion[i], peticion) DO Insertar(cola, peticion) WHEN NOT Vacia(cola), Receive(pedirOrden[i], x) DO Primero (cola, peticion); Borrar (cola); Send(orden[i], peticion) WHEN TRUE, Receive(retornoLectura[i], respuesta) DO Send(datoLeido[respuesta.cliente], respuesta.dato) END END END Servidor;
La actividad de los clientes está sujeta a la ejecución de una de las dos operaciones (Leer o Escribir) para actuar sobre el disco. Dichas operaciones son, precisamente, los ``servicios'' que ofrece cada servidor de disco a los clientes. Junto a todo esto, la implementación de las operaciones debe hacerse bajo la forma de procedimientos con unas cabeceras determinadas.
Las operaciones de lectura y escritura son diferentes en cuanto que lo es el sentido de la comunicación que producen. Así, mientras que la operación de escritura no implica la espera del proceso cliente (una vez se haya notificado al servidor la petición no hay que esperar a que se escriba realmente), para el caso de la lectura esto no es así, y el proceso cliente debe esperar a recibir los datos que haya solicitado. Lo anterior tiene una trascendencia mayor que la esperada para el caso de las lecturas:
... para que los datos puedan ser remitidos desde el servidor al cliente, éste previamente debe haberle indicado cuál es el canal por el que deberá enviarle dichos datos.
Como el enunciado obligaba a la utilización de unas cabeceras de procedimientos en las cuales no se hace ninguna mención al canal de recepción, la solución pasa por incluir, cuando la operación es de lectura, el canal de recepción dentro del mensaje enviado al servidor.
TASK Cliente(i: INTEGER); VAR disco, sector : CARDINAL; dato : TipoDato; PROCEDURE Leer(disco, sector: INTEGER; VAR dato: TipoDato); VAR lectura: PeticionLectura; BEGIN lectura.sector := sector; lectura.cliente := i; Send(pedirLectura[disco], lectura); Receive(datoLeido[i], dato) END Leer; PROCEDURE Escribir(disco, sector: INTEGER; dato: TipoDato); VAR escritura: PeticionEscritura; BEGIN escritura.sector := sector; escritura.dato := dato; Send(pedirEscritura[disco], escritura) END Escribir; BEGIN GetChannel(datoLeido[i]); LOOP (* .. Operaciones de lectura y/o escritura .. *) END END Cliente; VAR k: INTEGER; BEGIN COBEGIN FORALL k := 1 TO NumDiscos DO Manejador(k); Servidor(k) END; FORALL k := 1 TO NumClientes DO Cliente(k) END; COEND END Discos.