Hi Alex,

> The drawback of implementing such a fully non-blocking system will
> be that the present separation of event generation (select) and data
> processing (read) cannot be held up any longer, and a completely
> different application flow is required.

yes, indeed.

I attach a simple non-blocking echo server.  There are some functions
in C just to get the C read & write functions and some blocking stuff
up to the picolisp level.  Then the echo server waits for events on a
socket and writes back what it read.

Conceptually it should not block, I am not sure how to test it though.
Any ideas?

There are some things that could be done better, like:

- the buffer could be circular and support for this could be in the C
  functions rdx and wrx.

- buffer could be local for the sockets, so 'callback' should be a
  closure...  I guess I would have to use 'job' but that's next lesson
  I have to look at:-)

It might be nice to build some kind of abstraction above this low
level, non-blocking code.  Maybe to implement continuations to
abstract away the event driven code.  Not sure how difficult it would
be in picolisp.

There should really be -m32 switch in the 'gcc' function.  It would
also be useful if it would be possible optionally switch on -g without
having to modify the gcc.l file.

Cheers,

Tomas

(load "lib/gcc.l")

(gcc "nb" NIL 'eagain 'block 'rdx 'wrx)

//(eagain) -> 'cnt
any eagain(any ex __attribute__((unused))) {
   return boxCnt(EAGAIN);
}

//(block 'any 'flg) -> 'flg
any block(any ex) {
   int sd = (int)evCnt(ex,cdr(ex));
   any y = EVAL(caddr(ex));
   bool flg = isNil(y) ? NO : YES;
   blocking(flg, ex, sd);
   return y;
}

//(rdx 'lst 'cnt ['cnt]) -> 'cnt|NIL
any rdx(any ex) {
   any lst = EVAL(cadr(ex));
   int cnt = (int)evCnt(ex,cddr(ex));
   int off = isNil(cadddr(ex)) ? 0 : (int)evCnt(ex,cdddr(ex));
   int i = 0;
   int j = 0;
   NeedLst(ex,lst);
   byte buf[cnt];
   int n = read(InFile->fd, buf, cnt);
   if (0 < n) {
      for (; j < off && isCell(lst); lst = cdr(lst), j++);
      for (; i < n && i < cnt && isCell(lst); lst = cdr(lst), i++) {
         lst->car = boxCnt(buf[i]);
      }
   }
   return n == 0 ? Nil : boxCnt(n < 0 ? -errno : i);
}

//(wrx 'lst 'cnt ['cnt]) -> 'cnt|NIL
any wrx(any ex) {
   any lst = EVAL(cadr(ex));
   int cnt = (int)evCnt(ex,cddr(ex));
   int off = isNil(cadddr(ex)) ? 0 : (int)evCnt(ex,cdddr(ex));
   int i = 0;
   int j = 0;
   NeedLst(ex,lst);
   byte buf[cnt];
   for (; j < off && isCell(lst); lst = cdr(lst), j++);
   for (; i < cnt && isCell(lst); lst = cdr(lst), i++) {
      buf[i] = (byte)evCnt(ex,lst);
   }
   int n = write(OutFile->fd, buf, i);
   return n == 0 ? Nil : boxCnt(n < 0 ? -errno : n);
}
/**/
(cd (pack (sys "HOME") "/picolisp"))
(load (pack (sys "HOME") "/src/picolisp/nb.l"))

# (out "/tmp/a" (wrx '(1 2 3 4) 4))
# (out "/tmp/a" (wrx '(1 2 3 4) 3 1))

# (setq *B (need 5))
# (in "/tmp/a" (rdx *B 3))
# (in "/tmp/a" (rdx *B 2 3))
# *B

# non-blocking echo server

(setq *N 5) # try bigger buffer;-)
(setq *B (need *N))
(setq *I 0)
(setq *J 0)

(set 'EAGAIN (eagain))

(de _rdx (Sock)
   (in Sock
      (let? N (rdx *B (- *N *I) *I)
         (when (gt0 N)
            (inc '*I N))
         N)))

(de _wrx (Sock)
   (out Sock
      (let? N (wrx *B (- *I *J) *J)
         (when (gt0 N)
            (inc '*J N))
         N)))

(de callback (Sock)
   (let End NIL
      (prinl "callback " Sock " J=" *J " I=" *I " N=" *N)
      (block Sock NIL) # first time would be enough
      (unless End
         (let N (_rdx Sock)
            (prinl "  read " N)
            (unless (or (gt0 N) (= N 'EAGAIN))
               (setq End (cons rd N)))))
      (unless End
         (let N (_wrx Sock)
            (prinl "  written " N)
            (unless (or (gt0 N) (= N 'EAGAIN))
               (setq End (cons wr N)))))
      (when End
         (prinl "  finish")
         (task Sock)
         (close Sock))
      (when (<= *I *J)
         (prinl "  rotate J=" *J " I=" *I " N=" *N)
         (setq *I 0)
         (setq *J 0))
      (prinl "end " Sock " J=" *J " I=" *I " N=" *N)))

(task (port 4444)             # Listen on port 4444
   (when (accept @)           # A connect arrived
      (task @                 # Install another task on this socket
         Sock @               # Keep the socket in the task's env
            (callback Sock) ) ) )

Reply via email to