[fpc-pascal] Portable coroutines

Mark Morgan Lloyd markMLl.fpc-pascal at telemetry.co.uk
Sun Mar 22 13:42:16 CET 2015


Marco van de Voort wrote:
> In our previous episode, Mark Morgan Lloyd said:
>> Efficient implementation of coroutines requires CPU-specific code in the 
>> RTL and possibly the compiler. However 
>> http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html suggests a 
>> way that coroutines can be implemented in a portable fashion in C, how 
>> can this be done in Object Pascal?
> 
> Seems more an oddity than a system, but it relies heavily on preprocessor
> and fallthrough case.
> 
> The preprocessor part can be done, just use whatever preprocessor
> (maybe even cpp) and then haul the resulting code through fpc.
> 
> But there is no fallthrough case (and personally I think that is a good
> thing)

Generally agreed. I'm not particularly bothered about the syntax, 
although obviously having it fairly compact would be an advantage. I 
don't think it's possible to use a case/goto arrangement since this 
wouldn't be happy jumping into repeat or while loops, but it does look 
as though it's possible to use LongJmp() (insomnia has its advantages):


program coroutines;

uses SysUtils;

type    TState= record
            env: jmp_buf;
            line: cardinal
         end;

var     state: TState;
         once: boolean= false;
         scratch: char;


   function getchar(): char;

   var   sanity: jmp_buf;

   begin

     // Check that we're not trying to jump into exception blocks etc.

     if state.line <> 0 then begin
       SetJmp(sanity);
       Assert(PtrUInt(state.env.sp) + PtrUInt(state.env.bp) =
                 PtrUInt(sanity.sp) + PtrUInt(sanity.bp),
                 'Bad SP or BP at xfer to line ' + IntToStr(state.line));
       LongJmp(state.env, 1)
     end;

     SetJmp(state.env);
     if state.line = 0 then begin
       state.line := StrToInt( (*$I %LINE% *) );
       exit('A')
     end else
       state.line := 0;

..

     repeat
       SetJmp(state.env);
       if state.line = 0 then begin
         state.line := StrToInt( (*$I %LINE% *) );
         exit(#$ff)
       end else
         state.line := 0;
       once := true
     until once
   end { getChar } ;


begin
   FillByte(state, SizeOf(state), 0);
   scratch := getChar();
   while scratch <> #$ff do begin
     WriteLn(scratch);
     scratch := getChar()
   end
end.


The sanity check at the start is obviously processor-specific, but it 
doesn't use anything hidden and can at a pinch be omitted. It's 
obviously excessively verbose, but it is comparatively regular and does 
appear to handle at least some loop types properly.

-- 
Mark Morgan Lloyd
markMLl .AT. telemetry.co .DOT. uk

[Opinions above are the author's, not those of his employers or colleagues]



More information about the fpc-pascal mailing list