unofficial mirror of bug-guile@gnu.org 
 help / color / mirror / Atom feed
* bug#59021: Unbounded heap growth when combining dynamic states & delimited continuation
@ 2022-11-04 18:24 Ludovic Courtès
  2022-11-05 22:04 ` Ludovic Courtès
  2022-11-07 16:03 ` Maxim Cournoyer
  0 siblings, 2 replies; 5+ messages in thread
From: Ludovic Courtès @ 2022-11-04 18:24 UTC (permalink / raw)
  To: 59021

(This is a followup to <https://github.com/wingo/fibers/issues/65>,
itself a followup to <https://issues.guix.gnu.org/58631>.)

Consider this code:

--8<---------------cut here---------------start------------->8---
;; https://issues.guix.gnu.org/58631
;; https://github.com/wingo/fibers/issues/65

(define loss
  (make-vector 1000000))

(let ((tag (make-prompt-tag "my prompt")))
  (define handler
    (lambda (k i)
      (when (zero? (modulo i 2000000))
        (pk 'heap-size (assoc-ref (gc-stats) 'heap-size)))

      (call-with-prompt tag
        (lambda ()
          (k (modulo (+ 1 i) 10000000)))
        handler)))

  (call-with-prompt tag
    (let ((state (current-dynamic-state)))
      (lambda ()
        ;; (define (with-dynamic-state state thunk)
        ;;   (let ((previous #f))
        ;;     (dynamic-wind
        ;;       (lambda () (set! previous (set-current-dynamic-state state)))
        ;;       thunk
        ;;       (lambda () (set-current-dynamic-state previous)))))
        (with-dynamic-state state
                            (lambda ()
                              (let loop ((i 0))
                                (loop (abort-to-prompt tag i)))))))
    handler))
--8<---------------cut here---------------end--------------->8---

On Guile 3.0.8, this program exhibits seemingly unbounded heap growth.
Uncommenting the local ‘with-dynamic-state’ definition fixes the
problem.

Ludo’.





^ permalink raw reply	[flat|nested] 5+ messages in thread

* bug#59021: Unbounded heap growth when combining dynamic states & delimited continuation
  2022-11-04 18:24 bug#59021: Unbounded heap growth when combining dynamic states & delimited continuation Ludovic Courtès
@ 2022-11-05 22:04 ` Ludovic Courtès
  2022-11-20 17:28   ` Ludovic Courtès
  2022-11-07 16:03 ` Maxim Cournoyer
  1 sibling, 1 reply; 5+ messages in thread
From: Ludovic Courtès @ 2022-11-05 22:04 UTC (permalink / raw)
  To: 59021; +Cc: Andy Wingo

[-- Attachment #1: Type: text/plain, Size: 1350 bytes --]

Ludovic Courtès <ludo@gnu.org> skribis:

> Consider this code:
>
> ;; https://issues.guix.gnu.org/58631
> ;; https://github.com/wingo/fibers/issues/65
>
> (define loss
>   (make-vector 1000000))
>
> (let ((tag (make-prompt-tag "my prompt")))
>   (define handler
>     (lambda (k i)
>       (when (zero? (modulo i 2000000))
>         (pk 'heap-size (assoc-ref (gc-stats) 'heap-size)))
>
>       (call-with-prompt tag
>         (lambda ()
>           (k (modulo (+ 1 i) 10000000)))
>         handler)))
>
>   (call-with-prompt tag
>     (let ((state (current-dynamic-state)))
>       (lambda ()
>         ;; (define (with-dynamic-state state thunk)
>         ;;   (let ((previous #f))
>         ;;     (dynamic-wind
>         ;;       (lambda () (set! previous (set-current-dynamic-state state)))
>         ;;       thunk
>         ;;       (lambda () (set-current-dynamic-state previous)))))
>         (with-dynamic-state state
>                             (lambda ()
>                               (let loop ((i 0))
>                                 (loop (abort-to-prompt tag i)))))))
>     handler))
>
> On Guile 3.0.8, this program exhibits seemingly unbounded heap growth.

This is fixed by the patch below (tested against the test case above and
the Fibers and Shepherd test cases mentioned before):


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: Type: text/x-patch, Size: 836 bytes --]

diff --git a/libguile/vm.c b/libguile/vm.c
index 6fd5c554f..516bae773 100644
--- a/libguile/vm.c
+++ b/libguile/vm.c
@@ -165,11 +165,13 @@ capture_stack (union scm_vm_stack_element *stack_top,
                scm_t_dynstack *dynstack, uint32_t flags)
 {
   struct scm_vm_cont *p;
+  size_t stack_size;
 
-  p = scm_gc_malloc (sizeof (*p), "capture_vm_cont");
-  p->stack_size = stack_top - sp;
-  p->stack_bottom = scm_gc_malloc (p->stack_size * sizeof (*p->stack_bottom),
-                                   "capture_vm_cont");
+  stack_size = stack_top - sp;
+  p = scm_gc_malloc (sizeof (*p) + stack_size * sizeof (*p->stack_bottom),
+                     "capture_vm_cont");
+  p->stack_size = stack_size;
+  p->stack_bottom = (void *) ((char *) p + sizeof (*p));
   p->vra = vra;
   p->mra = mra;
   p->fp_offset = stack_top - fp;

[-- Attachment #3: Type: text/plain, Size: 745 bytes --]


Using a simple heap profiler (more on that later), I noticed that the
stacks allocated at ‘p->stack_bottom’ would be partly retained,
explaining the heap growth.

I couldn’t pinpoint what exactly is keeping a pointer to the stack, but
what I can tell is that the trick above makes that impossible (because
we disable interior pointer tracing), hence the difference.

Also, why changing the SCM_DYNSTACK_TYPE_DYNAMIC_STATE entry to an
SCM_DYNSTACK_TYPE_UNWINDER entry would make a difference remains a
mystery to me.

I’m interested in theories that would explain all this in more detail!
I’ll go ahead with the fix above if there are no objections.

It’s not fully satisfying but still it’s a relief.

Ludo’.

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* bug#59021: Unbounded heap growth when combining dynamic states & delimited continuation
  2022-11-04 18:24 bug#59021: Unbounded heap growth when combining dynamic states & delimited continuation Ludovic Courtès
  2022-11-05 22:04 ` Ludovic Courtès
@ 2022-11-07 16:03 ` Maxim Cournoyer
  2022-11-07 21:52   ` Maxim Cournoyer
  1 sibling, 1 reply; 5+ messages in thread
From: Maxim Cournoyer @ 2022-11-07 16:03 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 59021

Hi Ludovic,

Ludovic Courtès <ludo@gnu.org> writes:

> (This is a followup to <https://github.com/wingo/fibers/issues/65>,
> itself a followup to <https://issues.guix.gnu.org/58631>.)
>
> Consider this code:
>
> ;; https://issues.guix.gnu.org/58631
> ;; https://github.com/wingo/fibers/issues/65
>
> (define loss
>   (make-vector 1000000))
>
> (let ((tag (make-prompt-tag "my prompt")))
>   (define handler
>     (lambda (k i)
>       (when (zero? (modulo i 2000000))
>         (pk 'heap-size (assoc-ref (gc-stats) 'heap-size)))
>
>       (call-with-prompt tag
>         (lambda ()
>           (k (modulo (+ 1 i) 10000000)))
>         handler)))
>
>   (call-with-prompt tag
>     (let ((state (current-dynamic-state)))
>       (lambda ()
>         ;; (define (with-dynamic-state state thunk)
>         ;;   (let ((previous #f))
>         ;;     (dynamic-wind
>         ;;       (lambda () (set! previous (set-current-dynamic-state state)))
>         ;;       thunk
>         ;;       (lambda () (set-current-dynamic-state previous)))))
>         (with-dynamic-state state
>                             (lambda ()
>                               (let loop ((i 0))
>                                 (loop (abort-to-prompt tag i)))))))
>     handler))
>
> On Guile 3.0.8, this program exhibits seemingly unbounded heap growth.
> Uncommenting the local ‘with-dynamic-state’ definition fixes the
> problem.

I've tested both 3.0.8 from Guix on multiple machines (including Berlin)
and 2.2 from Debian 10, and ran the above snippet; it grows initially
but stabilize quickly and then doesn't budge.  I've let it run for more
than an hour.

So there's a problem there (?), but it doesn't seem like an unbound leak
from my experiments.  Perhaps the reproducer needs to be tweaked to
mimic better what is happening in Shepherd?

-- 
Thanks,
Maxim





^ permalink raw reply	[flat|nested] 5+ messages in thread

* bug#59021: Unbounded heap growth when combining dynamic states & delimited continuation
  2022-11-07 16:03 ` Maxim Cournoyer
@ 2022-11-07 21:52   ` Maxim Cournoyer
  0 siblings, 0 replies; 5+ messages in thread
From: Maxim Cournoyer @ 2022-11-07 21:52 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 59021

Hi,

Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

[...]

> I've tested both 3.0.8 from Guix on multiple machines (including Berlin)
> and 2.2 from Debian 10, and ran the above snippet; it grows initially
> but stabilize quickly and then doesn't budge.  I've let it run for more
> than an hour.

Actually, it does grow, it just takes a lot of time.  It's now at
150751472 from the original "stable" value of 87846912, so it seems like
an unbound leak after all.

-- 
Thanks,
Maxim





^ permalink raw reply	[flat|nested] 5+ messages in thread

* bug#59021: Unbounded heap growth when combining dynamic states & delimited continuation
  2022-11-05 22:04 ` Ludovic Courtès
@ 2022-11-20 17:28   ` Ludovic Courtès
  0 siblings, 0 replies; 5+ messages in thread
From: Ludovic Courtès @ 2022-11-20 17:28 UTC (permalink / raw)
  To: 59021-done; +Cc: Andy Wingo

Hi,

Ludovic Courtès <ludo@gnu.org> skribis:

> Ludovic Courtès <ludo@gnu.org> skribis:
>
>> Consider this code:
>>
>> ;; https://issues.guix.gnu.org/58631
>> ;; https://github.com/wingo/fibers/issues/65
>>
>> (define loss
>>   (make-vector 1000000))
>>
>> (let ((tag (make-prompt-tag "my prompt")))
>>   (define handler
>>     (lambda (k i)
>>       (when (zero? (modulo i 2000000))
>>         (pk 'heap-size (assoc-ref (gc-stats) 'heap-size)))
>>
>>       (call-with-prompt tag
>>         (lambda ()
>>           (k (modulo (+ 1 i) 10000000)))
>>         handler)))
>>
>>   (call-with-prompt tag
>>     (let ((state (current-dynamic-state)))
>>       (lambda ()
>>         ;; (define (with-dynamic-state state thunk)
>>         ;;   (let ((previous #f))
>>         ;;     (dynamic-wind
>>         ;;       (lambda () (set! previous (set-current-dynamic-state state)))
>>         ;;       thunk
>>         ;;       (lambda () (set-current-dynamic-state previous)))))
>>         (with-dynamic-state state
>>                             (lambda ()
>>                               (let loop ((i 0))
>>                                 (loop (abort-to-prompt tag i)))))))
>>     handler))
>>
>> On Guile 3.0.8, this program exhibits seemingly unbounded heap growth.
>
> This is fixed by the patch below (tested against the test case above and
> the Fibers and Shepherd test cases mentioned before):

Pushed as e47a153317c046ea5d335940412999e7dc604c33.

> Using a simple heap profiler (more on that later), I noticed that the
> stacks allocated at ‘p->stack_bottom’ would be partly retained,
> explaining the heap growth.
>
> I couldn’t pinpoint what exactly is keeping a pointer to the stack, but
> what I can tell is that the trick above makes that impossible (because
> we disable interior pointer tracing), hence the difference.
>
> Also, why changing the SCM_DYNSTACK_TYPE_DYNAMIC_STATE entry to an
> SCM_DYNSTACK_TYPE_UNWINDER entry would make a difference remains a
> mystery to me.
>
> I’m interested in theories that would explain all this in more detail!
> I’ll go ahead with the fix above if there are no objections.

I still am.  :-)

Ludo’.





^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-11-20 17:28 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-04 18:24 bug#59021: Unbounded heap growth when combining dynamic states & delimited continuation Ludovic Courtès
2022-11-05 22:04 ` Ludovic Courtès
2022-11-20 17:28   ` Ludovic Courtès
2022-11-07 16:03 ` Maxim Cournoyer
2022-11-07 21:52   ` Maxim Cournoyer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).