Dave Peticolas <[EMAIL PROTECTED]> writes:
> Note: Rob, you'll probably want to clean up my scheme code :)
:>
If I can finally get a chance to look at it.
Also, did you see my warning about scheme side pointers in the
*options.[hc] files? I just wanted to make sure you're aware of the
issue. The issue is that anyone passing a function to the C side
right now has to make sure that they keep a pointer to the function on
the Scheme side. That's because Guile's garbage collector has no idea
that a bit of storage is reachable if there's only a C side pointer to
it (Guile's GC doesn't know about C-side pointers that aren't on the
stack.).
Short-term, this means that code like this:
(gnc:create-a-dialog-where-pushing-the-button-does-this
(lambda ()
(display "Hello")
(newline)))
Presuming that the GNOME side only takes the scheme-side function
pointer passed in as an argument (the anonymous lambda in this case)
and sticks it into a push button callback struct, shows some dialog
with the push button, and then returns (leaving the dialog up), then
there's potential for trouble. This is dangerous because as soon as
the anonymous lambda is passed to the C side, the Guile side forgets
about it and could throw away its storage.
The short term solution is for users to make sure that in cases like
this, they actually store the function pointer somewhere on the Guile
side for as long as it's possible that it might be used on the C
side. i.e.:
(define temporary-holder (lambda () (display "Hello") (newline)))
(gnc:create-a-dialog-where-pushing-the-button-does-this temporary-holder)
But this sucks because then the guile side has to know when the C side
is finished with the pointer. The *right* solution is for the C side
to register the pointers that it's holding on to until it's done with
them.
To support that, we need a place to keep these pointers. There are
several ways this could be handled. The easiest way would be to just
to have one giant hash where the C-side can insert/remove pointers to
all the scheme side objects it wants to hang on to. Something like
this:
void
gnc_show_pushbutton_dialog(const char *msg, SCM callback) {
...
registration_id = gnc_register_c_side_scheme_ptr(callback);
gnome_dialog_button_connect(..., callback,...);
}
and then in the dialog's destructor
gnc_unregister_c_side_scheme_ptr(registration_id);
This is trivial to implement, so let's do that. If you're feeling
motiviated (I'll do it later if you don't feel like it, but I'm too
busy to do it right now), here's what needs to happen:
First, stick something like this in a scheme-side file:
(define gnc:register-c-side-scheme-ptr #f)
(define gnc:unregister-c-side-scheme-ptr #f)
(let ((next-registration-id 0)
;; Not sure this has to be prime, and not sure how large it needs
;; to be, but on both fronts, this should be fairly safe...
(pointer-storage (make-vector 313)))
(define (register-c-side-scheme-ptr ptr)
(let ((id next-registration-id))
(set! next-registration-id (+ next-registration-id 1))
(hashv-set! pointer-storage id ptr)
id))
(define (gnc:unregister-c-side-scheme-ptr id)
(hashv-remove! pointer-storage id))
(set! gnc:register-c-side-scheme-ptr register-c-side-scheme-ptr)
(set! gnc:unregister-c-side-scheme-ptr unregister-c-side-scheme-ptr))
Then call these two functions at the right times to register and
unregister scheme side pointers from the C side. You only have to
mess with registration if the C side keeps the pointer longer than the
scheme side might. In general, to see if that's true, you have to
look at the scheme side code, and it's better to be safe than sorry,
so when in doubt, we should register.
We'll also need to update the *-options.[ch] docs once this approach
fixes the problems mentioned there.
On an unrelated note, I'm wondering how close the GNOME side will be
to usable if/when you get the GnomeCanvas stuff in. It'll be
interesting to see.
--
Rob Browning <[EMAIL PROTECTED]> PGP=E80E0D04F521A094 532B97F5D64E3930
--
Gnucash Developer's List
To unsubscribe send empty email to: [EMAIL PROTECTED]