Un pipe o tubería es una estructura de datos que representa un canal de comunicación con una estructura de tipo FIFO (primero en entrar, primero en salir).
La denominación de tubería (en ingles pipe) viene del concepto de que los datos que introducimos o empujamos por un extremo de la tubería "aparecen" en el extremo contrario. Si realizamos una operación de lectura en la tubería y no hay datos en esta el hilo llamante se quedará bloqueado hasta que haya datos. Si realizamos una escritura en la tubería y esta ha alcanzado su tamaño máximo el proceso que desea escribir en la tubería se quedará bloqueado hasta que algun lector saque un dato.
Vamos a definir dos operaciones básicas sobre la tubería, Read y Write, Write esperará indefinidamente si el pipe está lleno mientras que Read aceptará un parametro que indicará el tiempo máximo de espera en la lectura. Para señalizar cuando hay datos en el pipe y cuando esta lleno usaremos sendos objetos TEvent.
interface
uses
Classes, SysUtils, SyncObjs, Windows;
type TPipe = class
protected
// La lista que almacena los objetos del pipe
FList : TThreadList;
// Identifica el máximo tamaño del pipe
FMaxSize : Integer;
// El evento usado para indicar la presencia de
// datos en el pipe.
FHayDatos : TEvent;
// El evento usado para indicar que hay espacio
// en el pipe
FHayEspacio : TEvent;
public
constructor Create(Size : Cardinal);
{ Escribe un objeto en el pipe
Devuelve -1 si error, 0 en otro caso
}
function Write(obj : TObject) : integer;
function Read(timeout : cardinal) : TObject;
end;
implementation
{ TPipe }
constructor TPipe.Create(Size: Cardinal);
begin
FList := TThreadList.Create;
FMaxSize := Size;
// AutoResetEvent, desactivado de inicio
FHayDatos := TEvent.Create(nil,false,false,'');
// ManualResetEvent, activado de inicio
FHayEspacio := TEvent.Create(nil,true,true,'');
end;
function TPipe.Read(timeout: cardinal): TObject;
var
res : TWaitResult;
list : TList;
EstabaLleno : boolean;
begin
res := FHayDatos.WaitFor(timeout);
case res of
wrSignaled :
begin
// Proteger el acceso y devolver el objeto
list := FList.LockList;
try
EstabaLleno := list.Count = FMaxSize;
result := list[0];
list.Delete(0);
if EstabaLleno then
FHayEspacio.SetEvent; // Ya no está lleno
if list.Count > 0 then
FHayDatos.SetEvent; // Aún quedan datos
finally
FList.UnlockList;
end;
end;
else
result := nil;
end;
end;
function TPipe.Write(obj: TObject) : integer;
var
res : TWaitResult;
list : TList;
begin
// Esperar si el pipe esta lleno
res := FHayEspacio.WaitFor(INFINITE);
case res of
wrSignaled:
begin
list := FList.LockList;
try
list.Add(obj);
FHayDatos.SetEvent; // Ahora hay datos
if list.Count = FMaxSize then
FHayEspacio.ResetEvent; // Esta lleno
finally
FList.UnlockList;
end;
result := 0;
end
else
result := -1;
end;
end;