{lsocket.pas}

{io and timer code by plugwash}

{ Copyright (C) 2005 Bas Steendijk and Peter Green
  For conditions of distribution and use, see copyright notice in zlib_license.txt
  which is included in the package
  ----------------------------------------------------------------------------- }

{note: you must use the @ in the last param to tltask.create not doing so will
 compile without error but will cause an access violation -pg}

//note: events after release are normal and are the apps responsibility to deal with safely

unit lcore;
{$ifdef fpc}
  {$mode delphi}
{$endif}
{$ifdef win32}
  {$define nosignal}
{$endif}
interface
  uses
    sysutils,
    {$ifndef win32}
      {$ifdef VER1_0}
        linux,
      {$else}
        baseunix,unix,unixutil,
      {$endif}
      fd_utils,
    {$endif}
    classes,pgtypes,bfifo;
  procedure processtasks;


  const
    {how this number is made up:
    - ethernet: MTU 1500
    - be safe for either "ethernet v1" or "PPPoE", both take 8 bytes
    - IPv6 header: 40 bytes (IPv4 is 20)
    - TCP/UDP header: 20 bytes
    }
    packetbasesize = 1432;
    receivebufsize=packetbasesize*8;

  var
    absoloutemaxs:integer=0;

  type
    {$ifdef ver1_0}
      sigset= array[0..31] of longint;
    {$endif}

    ESocketException   = class(Exception);
    TBgExceptionEvent  = procedure (Sender : TObject;
                                  E : Exception;
                                  var CanClose : Boolean) of object;

    // note : tsocketstate is defined in the same way as it is in François PIETTE's twsocket
    // however tlsocket currently only uses wsClosed wsConnecting wsconnected and wsListening
    TSocketState       = (wsInvalidState,
                        wsOpened,     wsBound,
                        wsConnecting, wsConnected,
                        wsAccepting,  wsListening,
                        wsClosed);

    TWSocketOption       = (wsoNoReceiveLoop, wsoTcpNoDelay);
    TWSocketOptions      = set of TWSocketOption;

    TSocketevent     = procedure(Sender: TObject; Error: word) of object;
    //Tdataavailevent  = procedure(data : string);
    TSendData          = procedure (Sender: TObject; BytesSent: Integer) of object;

    tlcomponent = class(tcomponent)
    private
      procedure releasetaskhandler(wparam,lparam:longint);
    public
      procedure release; virtual;
      destructor destroy; override;
    end;

    tlasio = class(tlcomponent)
    public
      state              : tsocketstate      ;
      ComponentOptions   : TWSocketOptions;
      fdhandlein         : Longint           ;  {file discriptor}
      fdhandleout        : Longint           ;  {file discriptor}

      onsessionclosed    : tsocketevent      ;
      ondataAvailable    : tsocketevent      ;
      onsessionAvailable : tsocketevent      ;

      onsessionconnected : tsocketevent      ;
      onsenddata         : tsenddata      ;
      ondatasent         : tsocketevent      ;
      //connected          : boolean         ;

      recvq              : tfifo;
      OnBgException      : TBgExceptionEvent ;
      //connectread        : boolean           ;
      sendq              : tfifo;
      closehandles       : boolean           ;
      writtenthiscycle   : boolean           ;
      onfdwrite           : procedure (Sender: TObject; Error: word) of object; //added for bewarehttpd
      lasterror:integer;
      destroying:boolean;
      recvbufsize:integer;
      function receivestr:string; virtual;
      procedure close;
      procedure abort;
      procedure internalclose(error:word); virtual;
      constructor Create(AOwner: TComponent); override;

      destructor destroy; override;
      procedure fdcleanup;
      procedure HandleBackGroundException(E: Exception);
      procedure handlefdtrigger(readtrigger,writetrigger:boolean); virtual;
      procedure dup(invalue:longint);

      function sendflush : integer;
      procedure sendstr(const str : string);virtual;
      procedure putstringinsendbuffer(const newstring : string);
      function send(data:pointer;len:integer):integer;virtual;
      procedure putdatainsendbuffer(data:pointer;len:integer); virtual;
      procedure deletebuffereddata;

      //procedure messageloop;
      function Receive(Buf:Pointer;BufSize:integer):integer; virtual;
      procedure flush;virtual;
      procedure dodatasent(wparam,lparam:longint);
      procedure doreceiveloop(wparam,lparam:longint);
      procedure sinkdata(sender:tobject;error:word);

      procedure release; override; {test -beware}

      function RealSend(Data : Pointer; Len : Integer) : Integer; //added for bewarehttpd

      procedure myfdclose(fd : integer); virtual;{$ifdef win32}abstract;{$endif}
      function myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt; virtual;{$ifdef win32}abstract;{$endif}
      function myfdread(fd: LongInt;var buf;size: LongInt):LongInt; virtual;{$ifdef win32}abstract;{$endif}
    protected
      procedure dupnowatch(invalue:longint);
    end;
    ttimerwrapperinterface=class(tlcomponent)
    public
      function createwrappedtimer : tobject;virtual;abstract;
//      procedure setinitialevent(wrappedtimer : tobject;newvalue : boolean);virtual;abstract;
      procedure setontimer(wrappedtimer : tobject;newvalue:tnotifyevent);virtual;abstract;
      procedure setenabled(wrappedtimer : tobject;newvalue : boolean);virtual;abstract;
      procedure setinterval(wrappedtimer : tobject;newvalue : integer);virtual;abstract;
    end;

  var
    timerwrapperinterface : ttimerwrapperinterface;
  type
    {$ifdef win32}
      ttimeval = record
        tv_sec : longint;
        tv_usec : longint;
      end;
    {$endif}
    tltimer=class(tlcomponent)
    protected


      wrappedtimer : tobject;


//      finitialevent       : boolean           ;
      fontimer            : tnotifyevent      ;
      fenabled            : boolean           ;
      finterval	          : integer	     ; {miliseconds, default 1000}
      {$ifndef win32}
        procedure resettimes;
      {$endif}
//      procedure setinitialevent(newvalue : boolean);
      procedure setontimer(newvalue:tnotifyevent);
      procedure setenabled(newvalue : boolean);
      procedure setinterval(newvalue : integer);
    public
      //making theese public for now, this code should probablly be restructured later though
      prevtimer          : tltimer           ;
      nexttimer          : tltimer           ;
      nextts	         : ttimeval          ;

      constructor create(aowner:tcomponent);override;
      destructor destroy;override;
//      property initialevent : boolean read finitialevent write setinitialevent;
      property ontimer : tnotifyevent read fontimer write setontimer;
      property enabled : boolean read fenabled write setenabled;
      property interval	: integer read finterval write setinterval;

    end;

    ttaskevent=procedure(wparam,lparam:longint) of object;

    tltask=class(tobject)
    public
      handler  : ttaskevent;
      obj      : tobject;
      wparam   : longint;
      lparam   : longint;
      nexttask : tltask;
      constructor create(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
    end;



    teventcore=class
    public
      procedure processmessages; virtual;abstract;
      procedure messageloop; virtual;abstract;
      procedure exitmessageloop; virtual;abstract;
      procedure setfdreverse(fd : integer;reverseto : tlasio);virtual;abstract;
      procedure rmasterset(fd : integer;islistensocket : boolean);  virtual;abstract;
      procedure rmasterclr(fd: integer);  virtual;abstract;
      procedure wmasterset(fd : integer); virtual;abstract;
      procedure wmasterclr(fd: integer);  virtual;abstract;
    end;
var
    eventcore : teventcore;

procedure processmessages;
procedure messageloop;
procedure exitmessageloop;

var
  firsttimer                            : tltimer    ;
  firsttask  , lasttask   , currenttask : tltask     ;

  numread                               : integer    ;
  mustrefreshfds                        : boolean    ;
{  lcoretestcount:integer;}

  asinreleaseflag:boolean;


procedure disconnecttasks(aobj:tobject);
procedure addtask(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
type
  tonaddtask = procedure(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
var
  onaddtask : tonaddtask;


procedure sleep(i:integer);
{$ifndef nosignal}
  procedure prepsigpipe;{$ifndef ver1_0}inline;{$endif}
{$endif}


implementation
{$ifndef nosignal}
  uses {sockets,}lloopback,lsignal;
{$endif}
{$ifdef win32}
  uses windows,winsock;
{$endif}
{$ifndef win32}
  {$include unixstuff.inc}
{$endif}
{$include ltimevalstuff.inc}


{!!! added sleep call -beware}
procedure sleep(i:integer);
var
  tv:ttimeval;
begin
  {$ifdef win32}
    windows.sleep(i);
  {$else}
    tv.tv_sec := i div 1000;
    tv.tv_usec := (i mod 1000) * 1000;
    select(0,nil,nil,nil,@tv);
  {$endif}
end;

destructor tlcomponent.destroy;
begin
  disconnecttasks(self);
  inherited destroy;
end;

procedure tlcomponent.releasetaskhandler(wparam,lparam:longint);
begin
  free;
end;


procedure tlcomponent.release;
begin
  addtask(releasetaskhandler,self,0,0);
end;

procedure tlasio.release;
begin
  asinreleaseflag := true;
  inherited release;
end;

procedure tlasio.doreceiveloop;
begin
  if recvq.size = 0 then exit;
  if assigned(ondataavailable) then ondataavailable(self,0);
  if not (wsonoreceiveloop in componentoptions) then
  if recvq.size > 0 then tltask.create(self.doreceiveloop,self,0,0);
end;

function tlasio.receivestr;
begin
  setlength(result,recvq.size);
  receive(@result[1],length(result));
end;

function tlasio.receive(Buf:Pointer;BufSize:integer):integer;
var
  i,a,b:integer;
  p:pointer;
begin
  i := bufsize;
  if recvq.size < i then i := recvq.size;
  a := 0;
  while (a < i) do begin
    b := recvq.get(p,i-a);
    move(p^,buf^,b);
    inc(taddrint(buf),b);
    recvq.del(b);
    inc(a,b);
  end;
  result := i;
  if wsonoreceiveloop in componentoptions then begin
    if recvq.size = 0 then eventcore.rmasterset(fdhandlein,false);
  end;
end;

constructor tlasio.create;
begin
  inherited create(AOwner);
  if not assigned(eventcore) then raise exception.create('no event core');
  sendq := tfifo.create;
  recvq := tfifo.create;
  state := wsclosed;
  fdhandlein := -1;
  fdhandleout := -1;
end;

destructor tlasio.destroy;
begin
  destroying := true;
  if state <> wsclosed then close;
  recvq.free;
  sendq.free;
  inherited destroy;
end;

procedure tlasio.close;
begin
  internalclose(0);
end;

procedure tlasio.abort;
begin
  close;
end;

procedure tlasio.fdcleanup;
begin
  if fdhandlein <> -1 then begin
    eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster)
  end;
  if fdhandleout <> -1 then begin
    eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster)
  end;
  if fdhandlein=fdhandleout then begin
    if fdhandlein <> -1 then begin
      myfdclose(fdhandlein);
    end;
  end else begin
    if fdhandlein <> -1 then begin
      myfdclose(fdhandlein);
    end;
    if fdhandleout <> -1 then begin
      myfdclose(fdhandleout);
    end;
  end;
  fdhandlein := -1;
  fdhandleout := -1;
end;

procedure tlasio.internalclose(error:word);
begin
  if (state<>wsclosed) and (state<>wsinvalidstate) then begin
    // -2 is a special indication that we should just exist silently
    // (used for connect failure handling when socket creation fails)
    if (fdhandlein = -2) and (fdhandleout = -2) then exit;
    if (fdhandlein < 0) or (fdhandleout < 0) then raise exception.create('internalclose called with invalid fd handles');
    eventcore.rmasterclr(fdhandlein);//fd_clr(fdhandlein,fdsrmaster);
    eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);

    if closehandles then begin
      {$ifndef win32}
        //anyone remember why this is here? --plugwash
        fcntl(fdhandlein,F_SETFL,0);
      {$endif}
      myfdclose(fdhandlein);
      if fdhandleout <> fdhandlein then begin
        {$ifndef win32}
          fcntl(fdhandleout,F_SETFL,0);
        {$endif}
        myfdclose(fdhandleout);
      end;
      eventcore.setfdreverse(fdhandlein,nil);
      eventcore.setfdreverse(fdhandleout,nil);

      fdhandlein := -1;
      fdhandleout := -1;
    end;
    state := wsclosed;

    if assigned(onsessionclosed) then if not destroying then onsessionclosed(self,error);
  end;
  if assigned(sendq) then sendq.del(maxlongint);
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ All exceptions *MUST* be handled. If an exception is not handled, the     }
{ application will most likely be shut down !                               }
procedure tlasio.HandleBackGroundException(E: Exception);
var
  CanAbort : Boolean;
begin
  CanAbort := TRUE;
  { First call the error event handler, if any }
  if Assigned(OnBgException) then begin
    try
      OnBgException(Self, E, CanAbort);
    except
    end;
  end;
  { Then abort the socket }
  if CanAbort then begin
    try
      close;
    except
    end;
  end;
end;

procedure tlasio.sendstr(const str : string);
begin
  putstringinsendbuffer(str);
  sendflush;
end;

procedure tlasio.putstringinsendbuffer(const newstring : string);
begin
  if newstring <> '' then putdatainsendbuffer(@newstring[1],length(newstring));
end;

function tlasio.send(data:pointer;len:integer):integer;
begin
  if state <> wsconnected then begin
    result := -1;
    exit;
  end;
  if len < 0 then len := 0;
  result := len;
  putdatainsendbuffer(data,len);
  sendflush;
end;


procedure tlasio.putdatainsendbuffer(data:pointer;len:integer);
begin
  sendq.add(data,len);
end;

function tlasio.sendflush : integer;
var
  lensent : integer;
  data:pointer;
//  fdstestr : fdset;
//  fdstestw : fdset;
begin
  if state <> wsconnected then begin
    result := -1;
    exit;
  end;

  lensent := sendq.get(data,packetbasesize*2);
  if assigned(data) then result := myfdwrite(fdhandleout,data^,lensent) else result := 0;

  if result = -1 then lensent := 0 else lensent := result;

  //sendq := copy(sendq,lensent+1,length(sendq)-lensent);
  sendq.del(lensent);

  //fd_clr(fdhandleout,fdsw); // this prevents the socket being closed by a write
                            // that sends nothing because a previous socket has
                            // slready flushed this socket when the message loop
                            // reaches it
//  if sendq.size > 0 then begin
    eventcore.wmasterset(fdhandleout);//fd_set(fdhandleout,fdswmaster);
//  end else begin
//    wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
//  end;
  if result > 0 then begin
    if assigned(onsenddata) then onsenddata(self,result);
//    if sendq.size=0 then if assigned(ondatasent) then begin
//      tltask.create(self.dodatasent,self,0,0);
//      //begin test code
//      fd_zero(fdstestr);
//      fd_zero(fdstestw);
//      fd_set(fdhandlein,fdstestr);
//      fd_set(fdhandleout,fdstestw);
//      select(maxs,@fdstestr,@fdstestw,nil,0);
//      writeln(fd_isset(fdhandlein,fdstestr),' ',fd_isset(fdhandleout,fdstestw));
//      //end test code
//    
//    end;
    writtenthiscycle := true;
  end;
end;

procedure tlasio.dupnowatch(invalue:longint);
begin
  {  debugout('invalue='+inttostr(invalue));}
  //readln;
  if state<> wsclosed then close;
  fdhandlein := invalue;
  fdhandleout := invalue;
  eventcore.setfdreverse(fdhandlein,self);
  {$ifndef win32}
    fcntl(fdhandlein,F_SETFL,OPEN_NONBLOCK);
  {$endif}
  state := wsconnected;

end;


procedure tlasio.dup(invalue:longint);
begin
  dupnowatch(invalue);
  eventcore.rmasterset(fdhandlein,false);//fd_set(fdhandlein,fdsrmaster);
  eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
end;


procedure tlasio.handlefdtrigger(readtrigger,writetrigger:boolean);
var
  sendflushresult : integer;
  tempbuf:array[0..receivebufsize-1] of byte;
  a:integer;
begin
  if (state=wsconnected) and writetrigger then begin
    //writeln('write trigger');

    if (sendq.size >0) then begin

      sendflushresult := sendflush;
      if (sendflushresult <= 0) and (not writtenthiscycle) then begin
        if sendflushresult=0 then begin // linuxerror := 0;
          internalclose(0);

        end else begin
          {$ifdef win32}
          if getlasterror=WSAEWOULDBLOCK then begin
            //the asynchronous nature of windows messages means we sometimes
            //get here with the buffer full
            //so do nothing in that case
          end else
          {$endif}
          begin
            internalclose({$ifdef win32}getlasterror{$else}linuxerror{$endif});
          end  
        end;
      end;

    end else begin
      //everything is sent fire off ondatasent event
      if fdhandleout >= 0 then eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
      if assigned(ondatasent) then tltask.create(self.dodatasent,self,0,0);
    end;
    if assigned(onfdwrite) then onfdwrite(self,0);
  end;
  writtenthiscycle := false;
  if (state =wsconnected) and readtrigger then begin
    if recvq.size=0 then begin
      a := recvbufsize;
      if (a <= 0) or (a > sizeof(tempbuf)) then a := sizeof(tempbuf);
      numread := myfdread(fdhandlein,tempbuf,a);
      if (numread=0) and (not mustrefreshfds) then begin
        {if i remember correctly numread=0 is caused by eof
        if this isn't dealt with then you get a cpu eating infinite loop
        however if onsessionconencted has called processmessages that could
        cause us to drop to here with an empty recvq and nothing left to read
        and we don't want that to cause the socket to close}

        internalclose(0);
      end else if (numread=-1) then begin
        {$ifdef win32}
          //sometimes on windows we get stale messages due to the inherent delays
          //in the windows message queue
          if WSAGetLastError = wsaewouldblock then begin
            //do nothing
          end else
        {$endif}
        begin
          numread := 0;
          internalclose({$ifdef win32}wsagetlasterror{$else}linuxerror{$endif});
        end;
      end else if numread > 0 then recvq.add(@tempbuf,numread);
    end;

    if recvq.size > 0 then begin
      if wsonoreceiveloop in componentoptions then eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster);
      if assigned(ondataavailable) then ondataAvailable(self,0);
      if not (wsonoreceiveloop in componentoptions) then if recvq.size > 0 then
      tltask.create(self.doreceiveloop,self,0,0);
    end;
    //until (numread = 0) or (currentsocket.state<>wsconnected);
{    debugout('inner loop complete');}
  end;
end;

procedure tlasio.flush;
{$ifdef win32}
type fdset = tfdset;
{$endif}
var
  fds : fdset;
begin
  fd_zero(fds);
  fd_set(fdhandleout,fds);
  while sendq.size>0 do begin
    select(fdhandleout+1,nil,@fds,nil,nil);
    if sendflush <= 0 then exit;
  end;
end;

procedure tlasio.dodatasent(wparam,lparam:longint);
begin
  if assigned(ondatasent) then ondatasent(self,lparam);
end;

procedure tlasio.deletebuffereddata;
begin
  sendq.del(maxlongint);
end;

procedure tlasio.sinkdata(sender:tobject;error:word);
begin
  tlasio(sender).recvq.del(maxlongint);
end;

{$ifndef win32}
  procedure tltimer.resettimes;
  begin
    gettimeofday(nextts);
    {if not initialevent then} tv_add(nextts,interval);
  end;
{$endif}

{procedure tltimer.setinitialevent(newvalue : boolean);
begin
  if newvalue <> finitialevent then begin
    finitialevent := newvalue;
    if assigned(timerwrapperinterface) then begin
      timerwrapperinterface.setinitialevent(wrappedtimer,newvalue);
    end else begin
      resettimes;
    end;
  end;
end;}

procedure tltimer.setontimer(newvalue:tnotifyevent);
begin
  if @newvalue <> @fontimer then begin
    fontimer := newvalue;
    if assigned(timerwrapperinterface) then begin
      timerwrapperinterface.setontimer(wrappedtimer,newvalue);
    end else begin

    end;
  end;

end;


procedure tltimer.setenabled(newvalue : boolean);
begin
  if newvalue <> fenabled then begin
    fenabled := newvalue;
    if assigned(timerwrapperinterface) then begin
      timerwrapperinterface.setenabled(wrappedtimer,newvalue);
    end else begin
      {$ifdef win32}
        raise exception.create('non wrapper timers are not permitted on windows');
      {$else}
        resettimes;
      {$endif}
    end;
  end;
end;

procedure tltimer.setinterval(newvalue:integer);
begin
  if newvalue <> finterval then begin
    finterval := newvalue;
    if assigned(timerwrapperinterface) then begin
      timerwrapperinterface.setinterval(wrappedtimer,newvalue);
    end else begin
      {$ifdef win32}
        raise exception.create('non wrapper timers are not permitted on windows');
      {$else}
        resettimes;
      {$endif}
    end;
  end;

end;




constructor tltimer.create;
begin
  inherited create(AOwner);
  if assigned(timerwrapperinterface) then begin
    wrappedtimer := timerwrapperinterface.createwrappedtimer;
  end else begin


    nexttimer := firsttimer;
    prevtimer := nil;

    if assigned(nexttimer) then nexttimer.prevtimer := self;
    firsttimer := self;
  end;
  interval := 1000;
  enabled := true;
end;

destructor tltimer.destroy;
begin
  if assigned(timerwrapperinterface) then begin
    wrappedtimer.free;
  end else begin
    if prevtimer <> nil then begin
      prevtimer.nexttimer := nexttimer;
    end else begin
      firsttimer := nexttimer;
    end;
    if nexttimer <> nil then begin
      nexttimer.prevtimer := prevtimer;
    end;
    
  end;
  inherited destroy;
end;

constructor tltask.create(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
begin
  inherited create;
  if assigned(onaddtask) then onaddtask(ahandler,aobj,awparam,alparam);
  handler   := ahandler;
  obj       := aobj;
  wparam    := awparam;
  lparam    := alparam;
  {nexttask  := firsttask;
  firsttask := self;}
  if assigned(lasttask) then begin
    lasttask.nexttask := self;
  end else begin
    firsttask := self;
  end;
  lasttask := self;
  //ahandler(wparam,lparam);
end;

procedure addtask(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
begin

  tltask.create(ahandler,aobj,awparam,alparam);
end;

{$ifndef nosignal}
  procedure prepsigpipe;{$ifndef ver1_0}inline;
{$endif}
  begin
    starthandlesignal(sigpipe);
    if not assigned(signalloopback) then begin
      signalloopback := tlloopback.create(nil);
      signalloopback.ondataAvailable := signalloopback.sinkdata;

    end;

  end;
{$endif}

procedure processtasks;//inline;
var
  temptask                : tltask   ;

begin

  if not assigned(currenttask) then begin
    currenttask := firsttask;
    firsttask := nil;
    lasttask  := nil;
  end;
  while assigned(currenttask) do begin

    if assigned(currenttask.handler) then currenttask.handler(currenttask.wparam,currenttask.lparam);
    if assigned(currenttask) then begin
      temptask := currenttask;
      currenttask := currenttask.nexttask;
      temptask.free;
    end;
    //writeln('processed a task');
  end;

end;




procedure disconnecttasks(aobj:tobject);
var
  currenttasklocal : tltask ;
  counter          : byte   ;
begin
  for counter := 0 to 1 do begin
    if counter = 0 then begin
      currenttasklocal := firsttask; //main list of tasks
    end else begin
      currenttasklocal := currenttask; //needed in case called from a task
    end;
    // note i don't bother to sestroy the links here as that will happen when
    // the list of tasks is processed anyway
    while assigned(currenttasklocal) do begin
      if currenttasklocal.obj = aobj then begin
        currenttasklocal.obj := nil;
        currenttasklocal.handler := nil;
      end;
      currenttasklocal := currenttasklocal.nexttask;
    end;
  end;
end;


procedure processmessages;
begin
  eventcore.processmessages;
end;
procedure messageloop;
begin
  eventcore.messageloop;
end;

procedure exitmessageloop;
begin
  eventcore.exitmessageloop;
end;

function tlasio.RealSend(Data : Pointer; Len : Integer) : Integer;
begin
  result := myfdwrite(fdhandleout,data^,len);
  if (result > 0) and assigned(onsenddata) then onsenddata(self,result);
  eventcore.wmasterset(fdhandleout);
end;
{$ifndef win32}
  procedure tlasio.myfdclose(fd : integer);
  begin
    fdclose(fd);
  end;
  function tlasio.myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt;
  begin
    result := fdwrite(fd,buf,size);
  end;

  function tlasio.myfdread(fd: LongInt;var buf;size: LongInt):LongInt;
  begin
    result := fdread(fd,buf,size);
  end;


{$endif}


begin
  firsttask := nil;
  

  {$ifndef nosignal}
    signalloopback := nil;
  {$endif}
end.





