Hi Alyssa,

Alyssa Kwan <alyssa.c.k...@gmail.com> writes:

> For what I'm doing (making functions durable), it raises the
> question:  If you persist a function that points to a var, restart the
> JVM, and deserialize/load the function from a data store, what should
> happen?

So you're doing something like this?

   (def x 5)
   (def myref (dref (fn [] x) :somekey store))

I guess the issue we're talking about at the moment is persisting var
references, not fns (which as Ken mentioned is its own kettle of fish)
so I'll simplify to just a dref containing a reference to a var:

   (def x 5)
   (def myref (dref #'x :somekey store))

> 1) In the loading thread, if a var exists with the same namespace and
> symbol, set the internal reference to that var.
>    a) If the function is then passed to a different thread that has a
> different var with that same namespace and symbol, the function will
> still point to the one that was in the loader thread at the time the
> function was deserialized/loaded.

I don't think this situation is possible because Namespace.mapping
(which maps symbols to vars and classes) is *not* thread-local.  For a
given namespace and symbol all threads will resolve the same var object.

It's the binding of the Var to a value that can be thread-local.
You shouldn't have to worry about it, as it happens in the dynamic
environment when the fn is called, not when it is loaded.

> 2) In the loading thread, if a var does not exist with that same
> namespace and symbol:
>    a) Throw an exception saying that the var doesn't exist.
>    b) Create the var with no namespace and symbol and no value.  Wait
> until the function is called to throw an exception saying that the var
> is unbound.

Ow, this is making my head hurt. ;-)  Consider this fairly common case:

    ;; sydney.clj
    (ns sydney)

    (def some-dref (dref nil :somekey store))

    ;; melbourne.clj
    (ns melbourne
      (:require sydney))

    (def some-var 1)

    (dosync 
      (when (nil? @sydney/some-dref)
         (ref-set sydney/some-dref #'some-var))

Suppose you execute melbourne.clj.  What happens?  On the first run:

1. sydney.clj is loaded, some-dref is initialized to nil.

2. melbourne.clj is loaded, the transaction is exeucted, some-dref is
now #'some-var, this gets saved to disk.

Then you restart the JVM and:

1. sydney.clj is loaded, some-dref's value is deserialized, but whoops,
#'some-var doesn't exist yet.

Okay, perhaps you can get around that by only deserializing when
first derefed, instead of at initialization like you currently do.

My preference would probably lean towards (2a).  Anyone using drefs is
going to have to deal explicitly with the issue of objects that aren't
serializable (sockets, streams), so make them be very careful about what
they put in them.

>    c) Create the var with no namespace and symbol but with the value
> that the var had in the persisting thread.  There's no way to access
> the var to modify it.
>    d) Create the var with no namespace and symbol but with the value
> that the var had in the persisting thread.  It's accessible somehow
> (maybe the meta map), so the user can recover it and dynamically bind
> over it.
>    e) Create the var with the namespace and symbol in the function,
> modifying the RT var-space of the loading thread.  Don't initialize
> the var.  This gives the user a chance to dynamically bind over it.
> If the function is called before binding the var to something, throw
> an exception saying that the var is unbound.
>    f) Create the var with the namespace and symbol in the function,
> with the value that the var had in the persisting thread.

Ppersisting the value of the var quickly leads to a cascade of extra
stored data, because you not only have to persist it, but any vars it
references in turn. 

Setting :static aside for the moment, suppose you persist this:

    (fn [x] (empty? x))

Then you have to persist the value of #'empty? which is:

    (fn [coll] (not (seq coll)))

So then you have to persist #'not and #'seq as well and so on.

> The normal case (1) is straightforward, and (1a) is what would happen
> without se/des anyways.  (2) is tricky.  (2f) is most robust, but
> really violates least surprise.  I think Meikel's comments lean
> towards (2a).
>
> Also, the thread-local nature of vars raises the question of what
> should happen when deserializing the same function from different
> threads.  If I create durable refs from different threads pointing to
> the same store/key combo, does the thread that gets there first
> control which var the deserialized function references?  Or does each
> thread get it's own ref?  If so, how do you reconcile that there are
> multiple refs being stored under the same store/key?  That's
> untenable, but it seems totally arbitrary to simply say that the first
> thread that gets there determines which var the function is bound to
> for the duration of the JVM instance.

As I said above, it's the value of the var that is thread-local, not the
var itself.  So for any of the options where you're not persisting the
value, it's not a problem.

If you do want try to persist the value of the var, then you should
probably only worry about the root binding.  The thread-local dynamic
bindings depend on the call stack of the accessing thread, so unless
you're planning on trying persist threads somehow, thread-local values
shouldn't be preserved between JVM instances.  So again, no problem.

> What do you think?

For the sake of practicality, I'd probably just be totally draconian
about it and make fns and vars not durable at all.  Almost any way you
do it they'd make the program very brittle.  They'd tie using the data
very tightly to the structure of the source code, meaning that if you
change the program (say rename a var) you're likely to not be able to
read the data any more.

Cheers,

Alex

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to