Calling the finalizer when scope block ends

15 views
Skip to first unread message

Karlo Licudine

unread,
May 5, 2020, 8:55:00 PM5/5/20
to chibi-scheme
Good day,

So I'm trying to figure out why the object created by my function does not get freed (via it's finalizer) when the scope exits

(let ((pos (make-vec3 0.0 0.0 0.0)))
  ;; do something
  )
;; I expect the finalizer to fire here. Because it's now out of scope

Only when the context is destroyed does the finalizer get called.

Could I just call the finalizer manually at the end of the code block?

(let ((pos (make-vec3 0.0 0.0 0.0)))
  ;; do something
  (FreeVec3 pos)
  )

This is how I defined things in my .stub file:

(define-c-struct Vec3
  predicate: vec3?
  finalizer: FreeVec3
  (float x vec3-get-x vec3-set-x)
  (float y vec3-get-y vec3-set-y)
  (float z vec3-get-z vec3-set-z))

(define-c (free Vec3) (make-vec3 MakeVec3) (float float float))

and the finalizer

Vec3* MakeVec3(float x, float y, float z)
{
  Vec3 * v = (Vec3*)malloc(sizeof(Vec3));
  v->x = x;
  v->y = y;
  v->z = z;
  return v;
}

Any help would be welcome. Just trying to figure out how to properly manage my resources as I plan to use this as a scripting language for a game engine.

Thank you very much.

Karlo

J. Vincent Toups

unread,
May 5, 2020, 9:00:10 PM5/5/20
to chibi-...@googlegroups.com
You may want to contact Alex about this, but my hunch is that the finalizer won't get called at a predictable time. A few things - the optimizer might substantially rewrite your code before its executed, but even if it was semantically identical, in general, you might not expect to see the result until a GC event occurs, which can be unpredictable.

-V

--
You received this message because you are subscribed to the Google Groups "chibi-scheme" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chibi-scheme...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/chibi-scheme/49a5a205-b378-4297-82f5-133400da8746%40googlegroups.com.

Alex Shinn

unread,
May 5, 2020, 9:09:34 PM5/5/20
to chibi-...@googlegroups.com
Hi Karlo,

On Wed, May 6, 2020 at 9:55 AM Karlo Licudine <acciden...@gmail.com> wrote:

So I'm trying to figure out why the object created by my function does not get freed (via it's finalizer) when the scope exits

(let ((pos (make-vec3 0.0 0.0 0.0)))
  ;; do something
  )
;; I expect the finalizer to fire here. Because it's now out of scope

This is a common misconception, as that is the behavior in C++ (which finalizes local variables once out of scope) and Python (which uses reference counting-based GC, though this is not always reliable).  However, in general GC happens at unpredictable times, so this won't work in Scheme (or Java, or Haskell, ...).
 
Only when the context is destroyed does the finalizer get called.

No, the finalizer is called when the object is gc'ed.  You can call (gc) manually if you want.
 
Could I just call the finalizer manually at the end of the code block?

(let ((pos (make-vec3 0.0 0.0 0.0)))
  ;; do something
  (FreeVec3 pos)
  )

You could manually manage memory, but in general finalizers are not safe to call multiple times, so you'd also need to disable automatic finalization (remove the finalizer: FreeVec3 from the struct def and define it as a normal function).  I don't recommend this though - this is not the kind of object where timing of finalization matters.  Mostly you want to care about precise control of finalization for resource-intensive objects, like open files and database connections (although in chibi-scheme open files are gc'ed automatically if you run out of file descriptors).

Also, in case your code is using continuations you want to be careful about just putting finalization at the end of a block, since that block may be reentered!  dynamic-wind is your friend if you can close something on leaving and restore on re-entering.

--
Alex

Karlo Licudine

unread,
May 5, 2020, 11:28:05 PM5/5/20
to chibi-...@googlegroups.com
Hi Vincent and Alex,

Thank you for answering. I see what you mean. I'll be doing some further tests on this for me to understand things further.

As a follow up question. I tried doing this code just for testing:

  (let ((pos (make-vec3 0.0 0.0 0.0)))
    #t
    )
  (gc)

  (let ((pos (make-vec3 0.0 0.0 0.0)))
    #t
    )
  (gc)

  (let ((pos (make-vec3 0.0 0.0 0.0)))
    #t
    )
  (gc)

I am getting a segmentation fault after the 3rd call to (gc).

Program received signal SIGSEGV, Segmentation fault.
sexp_mark_one (types=0x7ffff4c7b5d0, x=0x7ffff4c855e0) at gc.c:203
203  len = sexp_type_num_slots_of_object(t, x) - 1;

Now this tells me that forcing gc should done sparingly. I did it this way because I'm planning to have a (draw) function that will be called every frame and I'm wondering if I could call (gc) after calling it.

..frame 1
(draw)
(gc)
..frame 2
(draw)
(gc)
... and so on

Or... I should just let the gc automatically do its thing. This would mean I'd have to change how objects are managed during scripting.

Again, I'm super thankful for your time. I can try to step through the code and figure it out but asking would, of course, be quicker.

Thanks!

Karlo

Alex Shinn

unread,
May 6, 2020, 2:37:40 AM5/6/20
to chibi-...@googlegroups.com
On Wed, May 6, 2020 at 12:28 PM Karlo Licudine <acciden...@gmail.com> wrote:
Hi Vincent and Alex,

Thank you for answering. I see what you mean. I'll be doing some further tests on this for me to understand things further.

As a follow up question. I tried doing this code just for testing:

  (let ((pos (make-vec3 0.0 0.0 0.0)))
    #t
    )
  (gc)
  (let ((pos (make-vec3 0.0 0.0 0.0)))
    #t
    )
  (gc)
  (let ((pos (make-vec3 0.0 0.0 0.0)))
    #t
    )
  (gc)

I am getting a segmentation fault after the 3rd call to (gc).

This is a bug, either in chibi itself or, more likely, your FFI code.
Simply calling (or indirectly triggering) GC should never segfault.

--
Alex

Reply all
Reply to author
Forward
0 new messages