Skip to content

sempare/sempare-delphi-fiber-generator

Repository files navigation

Sempare Fiber Generator Demo

Copyright (C) 2022 Conrad Vermeulen, Sempare Limited

License: MIT License

This is a demonstration of using fibers under Windows.

Fibers are not commonly used, but in certain circumstances are quite useful. Fibers are seen as light weight threads, where scheduling is managed by the developer and not by the operating system. This is where it makes things complicated for most developers. They are fast as there is no context switching to the system.

This description is not going to go into the pros/cons of using fibers, but will just illustrate the utilisation in the form of a generator pattern that people may have seen in languages like Python.

A use case that I've found particularly useful is where you want to make asynchronous functions appear as synchronous functions.

src/Sempare.Win32.Fiber.pas contains some missing functions (GetFiberData, GetCurrentFiber) that are not included with the RTL Windows units at present (Delphi 11.3).

This works in Win32 and Win64.

This example illustrates the utilisation of fibers - note the problem can be solved more simply using normal language constructs.

The generator example

Assume you want to have a function that just produces numbers continually. You do not have to care about when it completes.

In this example, generic TGenerator is provided that controls the sequence being generate.

The generator needs a procedure that produces values. In order to return a value, the TGenerator.Yield(value) must be called.

Further the generator has an end action - it can either raise an exception, or resume generating values from the begining.

A simple sequence

procedure SeqProducer();
begin
  TGenerator<integer>.Yield(1);
  TGenerator<integer>.Yield(2);
  TGenerator<integer>.Yield(3);
end;

procedure TestSeq;
var
  LGenerator: TGenerator<integer>;
  i: integer;
begin
  LGenerator.Producer := SeqProducer;
  // 1,2,3,1,2,3,1,2,3,1
  for i := 1 to 10 do
    writeln(inttostr(LGenerator.Value));
end;

Fibonacci

procedure FibonaciProducer();
var
  i, j, t: integer;
begin
  i := 1; j := 1;
  TGenerator<integer>.Yield(1);
  TGenerator<integer>.Yield(1);
  while true do
  begin
        t := i + j;
        TGenerator<integer>.Yield(t);
        i := j;
        j := t;
  end;
end;

procedure TestFibonnaci;
var
  LGenerator: TGenerator<integer>;
  i: integer;
begin
  LGenerator.Producer := FibonaciProducer;
  for i := 1 to 10 do
    writeln(inttostr(LGenerator.Value));
end;

Enforcing one time iteration of values

The TGenerator.EndAction can be set to geaException. If you attempt to consume more values than the producer has to offer, an exception is raised.

procedure SeqProducer();
begin
  TGenerator<integer>.Yield(1);
  TGenerator<integer>.Yield(2);
  TGenerator<integer>.Yield(3);
end;

procedure TestFibonnaci;
var
  LGenerator: TGenerator<integer>;
  i: integer;
begin
  LGenerator.Producer := SeqProducer;
  LGenerator.EndAction := geaException;
  for i := 1 to 3 do
    writeln(inttostr(LGenerator.Value));
  try
        writeln(inttostr(LGenerator.Value)); // exception will be thrown
  except on e:Exception do
        writeln('Exception was raised');
  end;
end;

About

This project demonstrates the use of Windows Fibers to implement a generator pattern (much like an enumerable).

Topics

Resources

Stars

Watchers

Forks

Languages