Faré | 1 Jun 02:34 2008
Picon

Re: sb-posix:fork does not take thread post-mortem cleanups into account

When I read you write about fork and threads, I shudder. Fork should
basically not be used when there are active threads. Not just the
Lisp, but the libc, and any library that uses threads or mutexes
(possibly including a threaded malloc) will be mighty confused. I'm
not even sure you can use fork when there is but one thread left after
others die (might or might not work in practice).

In simple cases (static set of mutexes) you can survive with
pthread_atfork (assuming lisp and all libraries play well with it). I
wouldn't bet on it though.

I don't know much about sbcl and its test suite, so I can't comment
about the particulars.

(PS: oh, and the sbcl implementation of run-program has a lot of scary
race conditions. Playing games with aynchronous signal is braindead.)

[ François-René ÐVB Rideau | Reflection&Cybernethics | http://fare.tunes.org ]
Science is like sex: sometimes something useful comes out,
but that is not the reason we are doing it
        -- Richard Feynman

2008/5/14 Nikodemus Siivola <nikodemus <at> random-state.net>:
> I'm seeing a fair deal of hanging tests when building threaded on
> Darwin. For ex ample, this
>
>  sh run-tests.sh threads.pure.lisp clos-add-remove-method.impure.lisp
>
> reliably hangs in the first MAKE-THREAD call in the impure file: the
> thread is spawned, and starts running, but MAKE-THREAD never returns.
(Continue reading)

Nikodemus Siivola | 1 Jun 03:10 2008
Picon

Re: sb-posix:fork does not take thread post-mortem cleanups into account

On Sun, Jun 1, 2008 at 3:34 AM, Faré <fahree <at> gmail.com> wrote:

> When I read you write about fork and threads, I shudder. Fork should
> basically not be used when there are active threads. Not just the
> Lisp, but the libc, and any library that uses threads or mutexes
> (possibly including a threaded malloc) will be mighty confused. I'm
> not even sure you can use fork when there is but one thread left after
> others die (might or might not work in practice).
>
> In simple cases (static set of mutexes) you can survive with
> pthread_atfork (assuming lisp and all libraries play well with it). I
> wouldn't bet on it though.

I'm inclined to agree. I think there are a bunch of not-hard things to
make forking a bit more sane in the presence of lisp threads, but yes:
to robustly fork all running threads need to be informed about the
fork -- and then we're off in the interrupt land again. Not worth the
effort. Making sure GC doesn't deadlock, etc, it, needs to be done,
though: otherwise an unlucky GC just after fork() but before exec()
would be pretty nasty.

The problem I outlined is a lot more modest, though: in the test-suite
case there is only a single thread running when the fork happens, but
there have been other threads earlier -- which is the bit that SBCL
should deal with properly, but doesn't -- with occasionally
spectacular results on Darwin. I don't think this is a real issue on
Linux, though.

> (PS: oh, and the sbcl implementation of run-program has a lot of scary
> race conditions. Playing games with aynchronous signal is braindead.)
(Continue reading)

Faré | 1 Jun 03:49 2008
Picon

Re: sb-posix:fork does not take thread post-mortem cleanups into account

2008/5/31 Nikodemus Siivola <nikodemus <at> random-state.net>:
> an unlucky GC just after fork() but before exec()
> would be pretty nasty.
This is possible when you're single-threaded, by looking at the
available space and gc'ing if not enough is there -- but that's
inherently racy in a multi-threaded world.

> The problem I outlined is a lot more modest, though: in the test-suite
> case there is only a single thread running when the fork happens, but
> there have been other threads earlier -- which is the bit that SBCL
> should deal with properly, but doesn't -- with occasionally
> spectacular results on Darwin. I don't think this is a real issue on
> Linux, though.
Fixing thread cleanup is good (though I dont understand what cleanup
there is to do), but another obvious solution then is DON'T DO IT!
Make sure to put the thread test last, after any forking test - or
better, do the thread test in a different process, maybe inside a
fork.

>> (PS: oh, and the sbcl implementation of run-program has a lot of scary
>> race conditions. Playing games with aynchronous signal is braindead.)
> If you have something particular in mind, please point it out. I
> assume you're referring to SIGCHLD handling? Where's the race?
I sent email on the list before (or was that only to RmK?). In any
case, it is just NOT POSSIBLE to safely share data between an
interrupted program and an asynchronous signal handler. Typical
condition: the signal arrives before you setup the shared
datastructure, and the signal handler has nowhere to store things.
Worse: you need to grab some mutex on the datastructure or otherwise
modify it, and the handler pops in just in the middle of that
(Continue reading)

Larry Valkama | 1 Jun 09:22 2008
Picon
Picon

memory leak ?

What is happening here ? ROOM reports memory growth.
Thanks for any help.
/larry

(defclass foo ()
  ((res :accessor foo-res)))

(defun do-leak ()
  (let ((group (cons nil nil))
        (bar (make-instance 'foo)))
    ;    (bar (make-hash-table)))
    ;(setf (gethash 1 bar) (make-array 1000000))
    (setf (foo-res bar) (make-array 1000000))
    (setf (car group) bar)
    (eval `(lambda () ',group))))
    ;(funcall (eval `(defun a () (print ',group))))))

(defun ml ()
    (loop for x from 0 do
      #+clisp (gc)
      #+sbcl (sb-ext:gc :full t)
      (format t "~a " x)
      (room)
      (do-leak)
      ;(disassemble 'a)
      ))

(ml)

-------------------------------------------------------------------------
(Continue reading)

Nikodemus Siivola | 1 Jun 11:46 2008
Picon

Re: memory leak ?

On Sun, Jun 1, 2008 at 10:22 AM, Larry Valkama
<remlali <at> users.sourceforge.net> wrote:
> What is happening here ? ROOM reports memory growth.

I don't see that -- the numbers wobble a bit, but look pretty stable
to me. FWIW, ROOM is not accurate: it doesn't walk the object graph --
rather, it walks the address space from start to end, which means it
also counts garbage objects whose pages have not been zeroed yet.

Cheers,

 -- Nikodemus

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Sidney Markowitz | 1 Jun 13:51 2008

Re: allocating really large strings leads to heap exhaustion

I don't know if this is practical, but running the following to force 
every gc to be a full gc allows the loop to run without exhausting the heap.

(setf *after-gc-hooks*
    (push #'(lambda()
              (let ((*after-gc-hooks* nil))
                 (gc :full t)))
          *after-gc-hooks*))

I think the problem has been discussed before that in the general case 
once a heap exhaustion condition is detected it really is too late to do 
anything about it, especially with a copying GC that can nearly double 
the amount of memory that is being used before it reclaims the space 
used by garbage.

If I were writing a real-world application that used such large data 
structures, I think the practical solution would be to have an explicit 
call to (gc :full t) after releasing instances of the big data 
structure, or else use some sort of object pool to explicitly manage the 
use of instances of the object.

  -- sidney

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Nikodemus Siivola | 1 Jun 14:04 2008
Picon

Re: allocating really large strings leads to heap exhaustion

Part of the issue is that we don't have a properly documented GC
tuning interface.

What I believe happens in the string-output-stream case is that the
vectors constructed to hold the output end up being promoted to higher
generations, and so survive minor GC -- and by the time we're
seriously running out of space it is too late, as you note.

Controlling the number of bytes consed between GCs is one solution.
Another would be something like

 (with-garbage ...)

hinting to the GC that most of the things consed inside the dynamic
extent for the body end up as garbage, so tenuring should be avoided,
and a deeper collection after the body might be in oder.

...of course, the best thing would be to have a more flexible heap, as
in David Lichteblau's work in this area...

Cheers,

 -- Nikodemus

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
Richard M Kreuter | 1 Jun 16:30 2008
Picon

Re: sb-posix:fork does not take thread post-mortem cleanups into account

"Faré" <fahree <at> gmail.com>

> In any case, it is just NOT POSSIBLE to safely share data between an
> interrupted program and an asynchronous signal handler. Typical
> condition: the signal arrives before you setup the shared
> datastructure, and the signal handler has nowhere to store things.
> Worse: you need to grab some mutex on the datastructure or otherwise
> modify it, and the handler pops in just in the middle of that
> operation. "Solution": disable the signal handler (for all threads! Of
> course, in practice it is only safe for only one thread to handle
> signals non-trivially) around any write operation on the shared
> datastructure. But if you're ready to pay that cost, you may as well
> do wholly without signal handler and just poll using waitpid when you
> want to read the child status, or use a signalfd in your event loop,
> etc. Or if you really love threads, have the signal handler just wake
> up a waiting thread (may as wel use a sigfd at least on linux). And
> yes, I ran into this run-program race condition in real life.

FWIW, I believe it turns out that Windows doesn't offer an analogue of
SIGCHLD (and so, I think, RUN-PROGRAM doesn't fully work there at
present).  So I think the thing to do on Windows is to deal with child
processes in the event loop, and if that's the way to go on Windows,
then (modulo the details) I think it might be worthwhile to use the same
approach on Unix, too.

--
Richard

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
(Continue reading)

Juho Snellman | 1 Jun 20:21 2008
Picon
Picon

Re: allocating really large strings leads to heap exhaustion

"Nikodemus Siivola" <nikodemus <at> random-state.net> writes:

> Part of the issue is that we don't have a properly documented GC
> tuning interface.
> 
> What I believe happens in the string-output-stream case is that the
> vectors constructed to hold the output end up being promoted to higher
> generations, and so survive minor GC -- and by the time we're
> seriously running out of space it is too late, as you note.
> 
> Controlling the number of bytes consed between GCs is one solution.
> Another would be something like
> 
>  (with-garbage ...)
> 
> hinting to the GC that most of the things consed inside the dynamic
> extent for the body end up as garbage, so tenuring should be avoided,
> and a deeper collection after the body might be in oder.
> 
> ...of course, the best thing would be to have a more flexible heap, as
> in David Lichteblau's work in this area...

No, the best thing would be to have a GC that doesn't die horribly
when there is too much data in old generations.

Allowing the heap to grow just sweeps the problem under the rug for a
while, but it'll resurface once the heap will not be able to grow any
more. Either due to killing your machine by to eating all the physical
memory and swap, or due to running out of address space.

(Continue reading)

Nathan Froyd | 2 Jun 02:47 2008
Picon

fixing up representation selection

I casually mentioned on #lisp that I had written a bit of
IR2-rewriting code and it wasn't much of a pain.  Nikodemus prodded me
to send some of this stuff that's kicking around on my hard drive to
sbcl-devel so it won't be forgotten, so here's the code for what I
mentioned, along with a bit of introduction.  I know some of the
regulars understand why representation selection is sub-optimal, but
maybe there's a newcomer or two who doesn't and needs a little
background.

Representation selection is the pass of the compiler where we
determine how data is going to be represented--is this number going to
be a tagged integer, untagged integer, boxed double-float, etc.
However, there's a phase ordering problem: we do representation
selection only after we have determined what VOPs we are going to use
to implement certain operations.  Most of the time, this doesn't hurt
us.  But on specific operations--mostly operations on small
integers--we get bitten by the manner in which we select VOPs: we
always prefer doing tagged operations to untagged ones.  For example,
in the function IRONCLAD::MD5REGS-DIGEST:

(defun md5regs-digest (regs buffer start)
   (declare (type md5-regs regs) (type (simple-array (unsigned-byte 8) (*)))
    (type (integer 0 536870895) start)
    (optimize (speed 3) (safety 0) (space 0) (debug 0) (compilation-speed 0)))
   (if (= start 0) (sb-kernel:ub8-bash-copy regs 0 buffer 0 16)
       (setf (ub32ref/le buffer (+ start 0))
               (md5-regs-a regs)
             (ub32ref/le buffer (+ start 4))
               (md5-regs-b regs)
             (ub32ref/le buffer (+ start 8))
(Continue reading)


Gmane