Select in Pascal
Pascal doesn’t have built-in concurrency primitives like channels or goroutines, so we’ll simulate this behavior using threads and a custom channel-like structure.
program SelectExample;
uses
SysUtils, Classes;
type
TStringChannel = class
private
FValue: string;
FReady: Boolean;
FLock: TRTLCriticalSection;
public
constructor Create;
destructor Destroy; override;
procedure Send(const AValue: string);
function Receive(out AValue: string): Boolean;
end;
TWorkerThread = class(TThread)
private
FChannel: TStringChannel;
FDelay: Integer;
FMessage: string;
protected
procedure Execute; override;
public
constructor Create(AChannel: TStringChannel; ADelay: Integer; const AMessage: string);
end;
constructor TStringChannel.Create;
begin
inherited;
InitializeCriticalSection(FLock);
FReady := False;
end;
destructor TStringChannel.Destroy;
begin
DeleteCriticalSection(FLock);
inherited;
end;
procedure TStringChannel.Send(const AValue: string);
begin
EnterCriticalSection(FLock);
try
FValue := AValue;
FReady := True;
finally
LeaveCriticalSection(FLock);
end;
end;
function TStringChannel.Receive(out AValue: string): Boolean;
begin
EnterCriticalSection(FLock);
try
if FReady then
begin
AValue := FValue;
FReady := False;
Result := True;
end
else
Result := False;
finally
LeaveCriticalSection(FLock);
end;
end;
constructor TWorkerThread.Create(AChannel: TStringChannel; ADelay: Integer; const AMessage: string);
begin
inherited Create(False);
FChannel := AChannel;
FDelay := ADelay;
FMessage := AMessage;
FreeOnTerminate := True;
end;
procedure TWorkerThread.Execute;
begin
Sleep(FDelay);
FChannel.Send(FMessage);
end;
var
C1, C2: TStringChannel;
Msg: string;
I: Integer;
begin
// For our example we'll select across two channels.
C1 := TStringChannel.Create;
C2 := TStringChannel.Create;
try
// Each channel will receive a value after some amount
// of time, to simulate e.g. blocking RPC operations
// executing in concurrent threads.
TWorkerThread.Create(C1, 1000, 'one');
TWorkerThread.Create(C2, 2000, 'two');
// We'll use a loop to await both of these values
// simultaneously, printing each one as it arrives.
for I := 1 to 2 do
begin
repeat
if C1.Receive(Msg) then
begin
WriteLn('received ', Msg);
Break;
end;
if C2.Receive(Msg) then
begin
WriteLn('received ', Msg);
Break;
end;
Sleep(10); // Small delay to prevent busy waiting
until False;
end;
finally
C1.Free;
C2.Free;
end;
end.
This Pascal program simulates the behavior of the original code. Here’s how it works:
We define a
TStringChannel
class to simulate channels. It uses a critical section for thread safety.The
TWorkerThread
class simulates goroutines. Each thread waits for a specified delay and then sends a message to its channel.In the main program, we create two channels and start two worker threads.
We then use a loop to continuously check both channels for incoming messages. This simulates the
select
statement in the original code.When a message is received, it’s printed and the loop breaks to check the next message.
To run this program, save it as select_example.pas
and compile it with a Pascal compiler that supports threads (like Free Pascal):
$ fpc select_example.pas
$ ./select_example
received one
received two
Note that the total execution time will be slightly over 2 seconds, as both the 1 and 2 second delays execute concurrently.
While this Pascal version doesn’t have the elegance of Go’s select
statement, it demonstrates how to achieve similar functionality using threads and custom synchronization primitives.