Hello, Thanks for the nice summary!
Andy Wingo <[EMAIL PROTECTED]> writes: > But what if it goes like this: > > S becomes collectable in theory > > mark phase: S is indeed marked as collectable > > C is returned from a callback: get_ptr() return S > > at some later time the card containing S is swept; S's free function > is run, and S is marked as a free cell > > at some later point maybe S gets reused for some other purpose > > however S was already alive in scheme, and we are using it as a smob! > > The point is: > > You cannot do C->Scheme mapping reliably in the presence of lazy > sweeping, because there is a time in which the object is marked as > sweepable but not swept, but the C->Scheme code has no way of knowing > this. > > (While talking with Ludovic we realized that his code has this problem.) The code I had in mind is GnuTLS [0], but it's a slightly more specific scenario and I now don't think the problem applies there. In GnuTLS, there are "session" objects; session objects can have a Scheme port attached to them, and the `session-record-port' procedure returns that port. What we want is: (eq? (session-record-port s) (session-record-port s)) IOW, there must be a mapping from the session to the port so that we don't create a new port each time `session-record-port' is called. Here's how it's achieved. Let `s' be the C session object, `S' the corresponding SMOB, and `P' the port (an `SCM'). We have the following object graph: SCM_STREAM(P) S <-------------------- P | ^ |SCM_SMOB_DATA(S) | | | | _____________________' | / gnutls_session_get_ptr(s) v/ s The mark procedure of `S' marks `P'. Thus, as long as `S' is reachable, `P' is reachable. In addition, as long as `P' is reachable, `S' is reachable. The important difference with the generalized scheme you described is that when `S' becomes unreferenced by Scheme code, there's no way `s' can suddenly reappear at the Scheme level because GnuTLS doesn't have any function that would return `s'. Thus, the race condition you described cannot happen. The key insight here is that `S' and `s' "aggregate" `P', i.e., the lifetime of `S' and `s' is always greater than or equal to that of `P'. I presume that this scheme is applicable to many (most?) object-oriented APIs. It's actually what lead to the inception of the `aggregated' typespec in G-Wrap [1]. Now, I haven't considered call-backs. But maybe a call-back can be seen as a procedure that's aggregated by some object; in turn, the procedure refer to other objects in its environment, such that the lifetime of the objects involved is similarly hierarchical. Sorry for the digression but I think it's important to know whether Guile's API intrinsically makes it hard to handle such common use cases. Thanks, Ludo'. [0] http://git.savannah.gnu.org/gitweb/?p=gnutls.git;a=blob;f=guile/src/core.c#l42 [1] http://www.nongnu.org/g-wrap/manual/Wrapping-a-C-Function.html