[Chicken-users] code snippet (suggested for the wiki) - and several newcomer questions

2008-08-16 Thread F. Wittenberger
Hi all,

in this message
http://lists.gnu.org/archive/html/chicken-users/2008-08/msg00066.html
I posted an snippet, which someone suggested as an explanatory example
for the wiki.

Looking closer, I'd rather tear it apart in some discussion, before I
should go there, since it indeed touches a lot of issues chicken
beginners might want to learn about.

make-internal-pipe returns two values, an input- and an output-port,
connected to each other.  (So it's only useful in multithreaded
contexts, which is why it's interesting, though simple.)

The first version - in the above referenced message - uses only the
documented interface.  Here I'll discuss a more elaborate version, which
uses the currently undocumented interface for read-string and read-line
too.

First lesson.  Pro's and Con's of user level threading.
-

We shall soon need to read a single character from a given input string
buffer and update the buffer index atomically.  Within a typical POSIX
thread system, we would need to lock some mutex, read the character,
update the offset and unlock the mutex.

Now let me tell you a secret of success of a lot of games, databases and
a personal experience from several years as professional computer
scientist: the worst thing you can do with operating systems is actually
use them.  No matter how careful you are, switching to kernel mode and
back is expensive in comparison to register arithmetic.

User level thread systems, as chicken provides one, schedule in a way,
which could be understood as cooperative under the hood.  In chicken,
any C expression is never interrupted.

[[[ Is this actually true or just my understanding? ]]]

To get the character out of the string buffer, we need a C snippet no
longer than the one, we would put inside the critical section.  No need
to use locks at all!

(define string-ref++ 
(foreign-lambda*
   char
   ((scheme-pointer buf) ((c-pointer integer) i))
   char *p=(char *)buf; return(p[(*i)++]);))

Now this could have been the first trap already.

Note that the buf parameter is declared as a scheme-pointer.  A
beginner would have easily chosen then c-string type instead.  After
all, it's a string, which is going to be passed in.  If we declared the
buf parameter as c-string, no visible harm where done.  But behind the
scene chicken would copy the whole input string into a fresh location,
then run the C fragment and leave the string for the garbage collector.
-- We better had used locks.

Now the usual header:

(define make-internal-pipe
  (let ([make-input-port make-input-port]
[make-output-port make-output-port]
[make-mutex make-mutex]
[make-queue make-queue]
[make-condition-variable make-condition-variable]
[string-length string-length]
[string-ref string-ref]
[string-append string-append])
(lambda args
  (define name (or (and (pair? args) (car args))
   'internal-pipe))

Beginners did ask, why all these [x x]-bindings?  We keep a local
reference in case the global one becomes redefined.

This is a questionable practise.  While it's just what the doctor
ordered, if you want to provide a definition, which is guaranteed to be
immune against overwrites, it's difficult to use.  If you would - for
example - (use 'utf-8) *before* this unit is initialised, the captured
string-ref where already utf-8 aware and no longer what we need here.

[[[ Again this is what I understand.  Is it true?  ]]]

Also, if you want, say for debugging, to rely on Scheme's ability to
overwrite existing bindings, you are lost.

  (let-location
   ((off integer 0))

Here we declare off to be the offset in the string buffer to be an
integer value, shall be usable in the form (location off) as the
second parameter to string-ref++.  Otherwise it's nothing but a new
variable.

Now the structure and some predicates for convenience.

   (let ((mutex (make-mutex name))
 (condition (make-condition-variable name))
 (queue (make-queue))
 (buf #f))
 (define (eof?) (eq? #!eof buf))
 (define (buf-empty?) (or (not buf) (fx= off (string-length buf

read-input will either update buf and off or wait for input.  We use
plain SRFI-18 here.  A better version would use the mailbox egg and a
make-mailbox instead of make-queue.  This would safe both, the
condition and the mutex.  (read-input!) would become a simple
mailbox-receive! on the queue.

[[[ correct? ]]]

 (define (read-input!)
   (mutex-lock! mutex)
   (if (buf-empty?)
   (if (queue-empty? queue)
   (begin
 (mutex-unlock! mutex condition)
 (read-input!))
   (begin
 (set! buf #f)
 (set! buf (queue-remove! queue))
 (set! off 0)
 (mutex-unlock! mutex)))
   (mutex-unlock! mutex)))

(read!) shall read the current character

 (define (read!)
   (if (eof?) buf
  

[Chicken-users] Race condition in scheduler.scm

2008-08-16 Thread F. Wittenberger
Hi all,

in this message
http://lists.gnu.org/archive/html/chicken-users/2008-08/msg00094.html
I posted a patch to scheduler.scm, which fixes a race condition wrt. bad
file descriptors in the waiting queue.
(Attached is a slightly brushed up version.)

I have not seen any replies to this message (which is sort of strange on
this list).  Did anyone consider to roll it in?  If not, why?  Anything
bad about it?

The race originally arose in a somewhat strange setup, which might not
be even repeatable for everyone, since it involved accidentally changing
the scheduling sequence (using more or fewer exception handlers), which
may have different results on different machines.

But the issue as such is easy to recreate, if you make a thread wait
asynchronously on any file descriptor and close that fd from a second
thread.  (Something, which may or may not be intentional, but easy to
do.)

thank you

/Jörg

BTW ( why I'm I so desperate about it): I just completed the port of
Askemos (www.askemos.org) to chicken.  This works pretty good on my
laptop now.  I intend to release it as soon as it's better tested.  For
that I need to roll the development environment out the several machines
and I'd really, really love to do so straight from chicken svn without
additional patches.  (And promise: Askemos will run without the patch;
but it handles lots of concurrent network connections.  It will surely
hit the race after a few minutes.)

Index: scheduler.scm
===
--- scheduler.scm	(Revision 11653)
+++ scheduler.scm	(Arbeitskopie)
@@ -36,7 +36,7 @@
 	##sys#update-thread-state-buffer ##sys#restore-thread-state-buffer
 	##sys#remove-from-ready-queue ##sys#unblock-threads-for-i/o ##sys#force-primordial
 	##sys#fdset-input-set ##sys#fdset-output-set ##sys#fdset-clear
-	##sys#fdset-select-timeout ##sys#fdset-restore
+	##sys#fdset-select-timeout ##sys#fdset-restore ##sys#handle-bad-fd!
 	##sys#clear-i/o-state-for-thread!) 
   (foreign-declare #EOF
 #ifdef HAVE_ERRNO_H
@@ -60,6 +60,7 @@
 # include sys/types.h
 # include sys/time.h
 # include time.h
+# include sys/stat.h
 static C_word C_msleep(C_word ms);
 C_word C_msleep(C_word ms) {
 #ifdef __CYGWIN__
@@ -341,6 +342,25 @@
   (##sys#setislot t 13 #f)
   (##sys#setslot t 11 (cons fd i/o)) )
 
+(define-foreign-variable error-bad-file int (errno == EBADF))
+
+(define (##sys#handle-bad-fd! e)
+  (let ((bad ((foreign-lambda*
+	   bool ((integer fd))
+	   struct stat buf;
+	   int i = ( (fstat(fd, buf) == -1  errno == EBADF) ? 1 : 0);
+	   return(i);)
+	  (car e
+(if (and bad (pair? (cdr e)))
+	(thread-signal!
+	 (cadr c)
+	 (##sys#make-structure
+	  'condition
+	  '(exn i/o) ;; better? '(exn i/o net)
+	  (list '(exn . message) bad file descriptor
+		'(exn . arguments) (car e)) )))
+bad))
+
 (define (##sys#unblock-threads-for-i/o)
   (dbg fd-list:  ##sys#fd-list)
   (let* ([to? (pair? ##sys#timeout-list)]
@@ -353,8 +373,23 @@
 		   (fxmax 0 (- tmo1 now)) )
 		 0) ) ] )		; otherwise immediate timeout.
 (dbg n  fds ready)
-(cond [(eq? -1 n) 
-	   (##sys#force-primordial)]
+(cond [(eq? -1 n)
+	   (cond
+	(error-bad-file
+	 (set! ##sys#fd-list
+		   (let loop ((l ##sys#fd-list))
+		 (cond
+		  ((null? l) l)
+		  ((##sys#handle-bad-fd! (car l))
+		   (##sys#fdset-clear (caar l))
+		   ;; This is supposed to be a rare case, catch
+		   ;; them one by one.
+		   ;; (loop (cdr l))
+		   (cdr l))
+		  (else (cons (car l) (loop (cdr l)))
+	 (##sys#fdset-restore)
+	 (##sys#unblock-threads-for-i/o))
+	(else (##sys#force-primordial))) ]
 	  [(fx n 0)
 	   (set! ##sys#fd-list
 	 (let loop ([n n] [lst ##sys#fd-list])
___
Chicken-users mailing list
Chicken-users@nongnu.org
http://lists.nongnu.org/mailman/listinfo/chicken-users


[Chicken-users] egg announcement: remote-repl

2008-08-16 Thread Elf


many people on #chicken and the list have requested a simple remote-repl
egg.  some people have requested a not-so-simple remote-repl egg, too. :)

your requests have been answered.  there is a new remote-repl egg as soon
as the repo gets updated.  it should be plugin-able to any existing code
without modification.  it doesnt require any knowledge of threads or 
conditions.  its highly configurable: it uses the tcp egg by default, but
you can tell it to use any type of sockets or wrappers that you'd like. 
it supports authentication, if you want it.  it supports evaluation in any

type of environment; everything is hooked on a per-session/per-server basis.

for those of you who dont want or need anything so fancy, the defaults are 
(like i said) tcp and no auth, so you only NEED to specify a port and hostname.

errors are handled nicely, so you dont have to worry about some typo in the
repl erroring out your whole process.  all of that is handled under the hood,
even if you do use the hooks.

hope this satisfies the requests.  let me know if it doesnt.

-elf



___
Chicken-users mailing list
Chicken-users@nongnu.org
http://lists.nongnu.org/mailman/listinfo/chicken-users