Fork me on GitHub
#clojure-dev
<
2024-07-06
>
dmiller15:07:26

A question on the implementation of clojure.lang.Ref. Ref has a field TVal tvals. TVal clearly is implemented as a circular, doubly-linked list, via fields next and prior A singular object of type TVal has these fields initialized to point to itself. An inspection of all code touching these two fields seems to confirm that the requirements of a circular, doubly-linked list are maintained. (BTW, the most fun piece of code is in Ref.trimHistory where we find tvals.next = tvals; tvals.prior = tval, discarding the all of the list except the head node itself -- a nice confirmation of the semantics.) Ref has two constructors. The one-arg ctor just calls the two-arg ctor. The two-arg ctor initializes the tvals field to a new Tval(...) and hence the tvals field is not null upon construction. There are a few places in the code (in Ref but also in LockingTransaction) that do set the tvals field, but in each case it is either to a new TVal(...) or ref.tvals = ref.tvals.next; . For the latter, given the circular nature, tvals.next will not be null. Nevertheless, I count 13 places where ref.tvals is tested to see if it is null. Have I missed some situation where tvals can be null? Is someone running around creating bare Refs without using the supplied constructors? are there any other loopholes? Modification to Ref outside of the Ref and LockingTransaction code that I have overlooked. (This is not idle curiousity -- I'm implementing in a language where if this field can be null I have to choose among declaring the type as allowing null values, declaring the field to be a nullable reference type, or using an option type. Yes vs no here impacts code design.)

Alex Miller (Clojure team)15:07:43

don't know of anything else, but since Ref is a non-final class, something else could subclass it and write this field

Alex Miller (Clojure team)16:07:12

maybe it's just defensive for that sake

Alex Miller (Clojure team)16:07:48

the only other magic constructor path that springs to mind is serialization, but this isn't serializable

Alex Miller (Clojure team)16:07:31

so I think you would be ok making the presumption that its non-null in your impl

dmiller16:07:22

Serialization came to mind, but as you say ... I think I'm willing to take the chance and seal the class. If anyone wants to subclass, they can ask. 🙂 Thanks for the quick response.