Re: wip-threads-and-fork

2012-02-27 Thread Andy Wingo
Hi,

On Sun 26 Feb 2012 23:00, l...@gnu.org (Ludovic Courtès) writes:

>> A guile built without
>> threads may fork to its heart's content.  However a guile built with
>> threads -- the default, recommended configuration -- should not call
>> primitive-fork.
>
> That’s a strong statement.

Don't shoot the messenger ;)  I tried hard to make it work, but there's
no getting around POSIX here.

> When the only threads are the main thread
> and the signal thread, everything’s alright.  For example, this works
> fine on GNU/Linux:
>
> (let ((p (primitive-fork)))
>   (case p
> ((0)
>  (sigaction SIGINT (lambda args
>  (format #t "kid ~a got ~a~%" (getpid) args)
>  (exit 0)))
>  (let loop () (loop))
>  (exit 1))
> (else
>  (sleep 2)
>  (kill p SIGINT)
>  (format #t "killed ~a~%" p)
>  (waitpid p
>
> It works because the signal thread is stuck in a read(2) with no lock
> taken.

"Works" ;-) It mostly works on GNU/Linux.  But not all the time!  Other
processes can send your thread signals -- indeed, one would expect that
it's for that reason that you installed a signal handler.  If the
signal-handling thread wakes up and queues an async, it could be in the
middle of allocation, hold the alloc lock, and thereby prevent the child
from allocating anything.  Or it could have the async mutex.

I really doubt that we can build a stable system on top of such shaky
guarantees.

I 

> Things that don’t work include this:
>
> (use-modules (ice-9 futures))
>
> (let* ((f (future (begin (sleep 4) (getpid
>(p (primitive-fork)))
>   (case p
> ((0)
>  (format #t "kid -> ~a~%" (touch f)))
> (else
>  (format #t "parent -> ~a~%" (touch f))
>  (waitpid p
>
> Here the child waits forever because it has only one thread.

Or because some other thread has the allocation lock, or some other
lock, including even the gconv lock deep in libc, etc.

> As for popen, that’s a bug (or undocumented limitation) of (ice-9
> futures) itself, more than anything else.

Popen now works with threads.

Andy
-- 
http://wingolog.org/



Re: wip-threads-and-fork

2012-02-27 Thread Andy Wingo
On Sun 26 Feb 2012 23:03, l...@gnu.org (Ludovic Courtès) writes:

> Andy Wingo  skribis:
>
>> +  if (scm_ilength (scm_all_threads ()) != 1)
>> +/* Other threads may be holding on to resources that Guile needs --
>> +   it is not safe to permit one thread to fork while others are
>> +   running.
>> +
>> +   In addition, POSIX clearly specifies that if a multi-threaded
>> +   program forks, the child must only call functions that are
>> +   async-signal-safe.  We can't guarantee that in general.  The best
>> +   we can do is to allow forking only very early, before any call to
>> +   sigaction spawns the signal-handling thread.  */
>> +SCM_MISC_ERROR ("attempt to fork while multiple threads are 
>> running",
>> +SCM_EOL);
>
> Just like fork(2) lets one shoot themself in the foot, I think this is
> beyond libguile’s scope.  After all, libguile just wraps the OS
> features, however crippled they may be.

Dunno.  That is certainly the case for things like close-fdes, and the
FFI.  But if someone really wants an unsafe fork, the FFI can give it to
them, right?  Keep in mind that portably speaking, you can't even call
`malloc' in the child, if you forked a program with multiple threads.

Primitive-fork is much less necessary now that open-process, the most
important use case, has been rewritten in C.

Finally, I think it's particularly important to constrain primitive-fork
because it prevents composition of various modules.  If I have a module
that uses futures, and a module that forks, I can't compose them.

Andy
-- 
http://wingolog.org/



Re: [Guile-commits] GNU Guile branch, master, updated. v2.1.0-68-g79eb47e

2012-02-27 Thread Ludovic Courtès
Hi,

Andy Wingo  skribis:

> On Sun 26 Feb 2012 22:38, l...@gnu.org (Ludovic Courtès) writes:
>
>> "Andy Wingo"  skribis:
>>
>>> commit 79eb47ea47650ef42c545931726277a7118a0210
>>> Author: Andy Wingo 
>>> Date:   Fri Feb 24 23:05:02 2012 +0100
>>>
>>> port i/o optimizations for iso-8859-1
>>> 
>> The problems with fast paths is that they become less fast when there
>> are many of them.
>>
>> How does this change influence ‘benchmark-suite/benchmarks/ports.bm’?
>
> Dunno, that wasn't my benchmark.  It makes the web server go faster
> (e.g. guile examples/web/debug-sxml.scm).

Can you check with that benchmark?  I think it’s important to have
concrete figures when doing such a change, to make sure the
performance/maintenance cost ratio is good.

> The reason it makes sense to optimize for this encoding is because it's
> our narrow encoding.

I don’t see how Guile’s internal representation is relevant here, since
we’re talking about input/output.

UTF-8 is used very widely out there, and that’s why I’d consider it
important to have fast UTF-8 I/O.  The same cannot be said of Latin-1.

Thanks,
Ludo’.



Re: TODO list for Guile R7RS support

2012-02-27 Thread Ludovic Courtès
Hi Alex,

Alex Shinn  skribis:

> 2012/2/26 Ludovic Courtès :
>> Hi,
>>
>> Alex Shinn  skribis:
>>
>>> On Thu, Feb 23, 2012 at 7:06 AM, Andy Wingo  wrote:
>>
>> [...]
>>
> * R7RS exceptions

 Are they like R6RS exceptions?
>>>
>>> Yes, just the exceptions with no condition hierarchy.
>>
>> Ouch, like SRFI-35--.  I’d be curious to see the rationale for yet
>> another record API, and one that doesn’t support inheritance.
>
> No, not like SRFI-35, no condition types at all.
> Instead you raise arbitrary objects

OK, sort-of like SRFI-34 then, right?

> You seem to be confused about inheritance.
> SRFI-35 and R6RS provided multiple inheritance,
> while the record system supported only single
> inheritance.  We will not repeat this mistake.

Thanks for the clarification.  I see, and I concur.

Ludo’.



Re: [Guile-commits] GNU Guile branch, master, updated. v2.1.0-68-g79eb47e

2012-02-27 Thread Andy Wingo
Hi!

On Mon 27 Feb 2012 16:57, l...@gnu.org (Ludovic Courtès) writes:

>>> How does this change influence ‘benchmark-suite/benchmarks/ports.bm’?
>>
>> Dunno, that wasn't my benchmark.  It makes the web server go faster
>> (e.g. guile examples/web/debug-sxml.scm).
>
> Can you check with that benchmark?  I think it’s important to have
> concrete figures when doing such a change, to make sure the
> performance/maintenance cost ratio is good.

Before:

wingo@badger:~/src/guile-master$ ./benchmark-guile ports.bm
Benchmarking /home/wingo/src/guile-master/meta/guile ... ports.bm
with GUILE_LOAD_PATH=/home/wingo/src/guile-master/benchmark-suite
;; running guile version 2.1.0.43-4813a-dirty
;; calibrating the benchmarking framework...
;; framework time per iteration: 3.81469669342041e-8
("ports.bm: peek-char: latin-1 port" 70 user 0.08 benchmark 
0.0532971231460571 bench/interp 0.0532971231460571 gc 0.0)
("ports.bm: peek-char: utf-8 port, ascii character" 70 user 0.05 
benchmark 0.0232971231460571 bench/interp 0.0232971231460571 gc 0.0)
("ports.bm: peek-char: utf-8 port, Korean character" 70 user 0.08 
benchmark 0.0532971231460571 bench/interp 0.0532971231460571 gc 0.0)
("ports.bm: read-char: latin-1 port" 1000 user 0.7 benchmark 
0.318530330657959 bench/interp 0.318530330657959 gc 0.0)
("ports.bm: read-char: utf-8 port, ascii character" 1000 user 0.68 
benchmark 0.298530330657959 bench/interp 0.298530330657959 gc 0.0)
("ports.bm: read-char: utf-8 port, Korean character" 1000 user 0.68 
benchmark 0.298530330657959 bench/interp 0.298530330657959 gc 0.0)
("ports.bm: char-ready?: latin-1 port" 1000 user 0.57 benchmark 
0.188530330657959 bench/interp 0.188530330657959 gc 0.0)
("ports.bm: char-ready?: utf-8 port, ascii character" 1000 user 0.57 
benchmark 0.188530330657959 bench/interp 0.188530330657959 gc 0.0)
("ports.bm: char-ready?: utf-8 port, Korean character" 1000 user 0.57 
benchmark 0.188530330657959 bench/interp 0.188530330657959 gc 0.0)
("ports.bm: rdelim: read-line" 1000 user 0.38 benchmark 0.379961853033066 
bench/interp 0.297933677033066 gc 0.082028176)

After:

;; running guile version 2.1.0.43-4813a-dirty
;; calibrating the benchmarking framework...
;; framework time per iteration: 3.81469669342041e-8
("ports.bm: peek-char: latin-1 port" 70 user 0.05 benchmark 
0.0232971231460571 bench/interp 0.0232971231460571 gc 0.0)
("ports.bm: peek-char: utf-8 port, ascii character" 70 user 0.05 
benchmark 0.0232971231460571 bench/interp 0.0232971231460571 gc 0.0)
("ports.bm: peek-char: utf-8 port, Korean character" 70 user 0.08 
benchmark 0.0532971231460571 bench/interp 0.0532971231460571 gc 0.0)
("ports.bm: read-char: latin-1 port" 1000 user 0.74 benchmark 
0.358530330657959 bench/interp 0.358530330657959 gc 0.0)
("ports.bm: read-char: utf-8 port, ascii character" 1000 user 0.76 
benchmark 0.378530330657959 bench/interp 0.378530330657959 gc 0.0)
("ports.bm: read-char: utf-8 port, Korean character" 1000 user 0.77 
benchmark 0.388530330657959 bench/interp 0.388530330657959 gc 0.0)
("ports.bm: char-ready?: latin-1 port" 1000 user 0.68 benchmark 
0.298530330657959 bench/interp 0.298530330657959 gc 0.0)
("ports.bm: char-ready?: utf-8 port, ascii character" 1000 user 0.64 
benchmark 0.258530330657959 bench/interp 0.258530330657959 gc 0.0)
("ports.bm: char-ready?: utf-8 port, Korean character" 1000 user 0.62 
benchmark 0.238530330657959 bench/interp 0.238530330657959 gc 0.0)
("ports.bm: rdelim: read-line" 1000 user 0.39 benchmark 0.389961853033066 
bench/interp 0.306389294033066 gc 0.083572559)

The times are quite variable, though.

Regards,

Andy
-- 
http://wingolog.org/