Skip to content

Commit

Permalink
Add RestoringWeakReference to utils.
Browse files Browse the repository at this point in the history
RestoringWeakReference contains a scala.ref.WeakReference that, after it
has been nulled out, uses a restorer function to restore the value.
This can be used for data that can afford to be evicted by the garbage
collector, but will be needed later. One good example is Lift form
callbacks, which may need the value of an object, but where you don't
necessarily want to be retaining the object indefinitely while someone
is on a page in the face of GC contention.
  • Loading branch information
Shadowfiend committed Nov 9, 2012
1 parent 731c4dc commit 5833892
Showing 1 changed file with 83 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2006-2011 WorldWide Conferencing, LLC

This comment has been minimized.

Copy link
@fmpwizard

fmpwizard Nov 9, 2012

Member

2012 :)

This comment has been minimized.

Copy link
@Shadowfiend

Shadowfiend Nov 9, 2012

Author Member

Womp womp :p

*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.liftweb
package util

import scala.ref.WeakReference

/**
* `RestoringWeakReference` contains a `scala.ref.WeakReference` that,
* after it has been nulled out, uses a restorer function to restore the
* value. This can be used for data that can afford to be evicted by
* the garbage collector, but will be needed later. One good example is
* Lift form callbacks, which may need the value of an object, but where
* you don't necessarily want to be retaining the object indefinitely
* while someone is on a page in the face of GC contention.
*
* You can use `RestoringWeakReference` in a couple of basic ways:
*
* {{{
* val ref = RestoringWeakReference(() => User.find(id))
* }}}
* In this situation, the `RestoringWeakReference` will immediately call
* the passed function to provide the starting value for the reference,
* and then the same function will be used if the reference is evicted.
*
* {{{
* val ref = RestoringWeakReference(starter, () => User.find(id))
* }}}
* Here, starter is an `Option[User]` and `User.find` returns an
* `Option[User]`. The first parameter will be used to initialize the
* weak reference, while the function will be used to restore the value
* if the reference is evicted.
*
* {{{
* val baseRef = new WeakReference(starter)
* val ref = new RestoringWeakReference(baseRef, () => User.find(id))
* }}}
* If you already have a `WeakReference` instance, you can instantiate
* `RestoringWeakReference` directly and pass that reference as the
* starting value, as well.
*
* In all these cases, you use `ref.value` to get a hold of the value.
* That function will return the value if it is available, and, if not,
* it will restore the WeakReference and then return the value.
*/
class RestoringWeakReference[T <: AnyRef](private var reference:WeakReference[T], restorer:()=>T) {
def value : T = {
val existing = reference.get
if (! existing.isDefined) {
restoreReference
value
} else {
existing.get
}
}

private def restoreReference = {
reference = new WeakReference(restorer())
}
}
object RestoringWeakReference {
def apply[T <: AnyRef](restorer:()=>T) = {
new RestoringWeakReference(new WeakReference(restorer()), restorer)
}
def apply[T <: AnyRef](starter:T, restorer:()=>T) = {
new RestoringWeakReference(new WeakReference(starter), restorer)
}
}

4 comments on commit 5833892

@fmpwizard
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding this, any chance to get a spec test with the pull req?

@nafg
Copy link
Contributor

@nafg nafg commented on 5833892 Nov 12, 2012

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks nice.
It might be more in line with scala conventions to rename value to apply. Then you would write ref() instead of ref.value.

P.S. Would you you mind to fix the indentation?
Thanks.

@Shadowfiend
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow. No idea what happened to the indentation there. I'll definitely fix that.

As for apply vs value… Let me ponder that one for a bit.

@Shadowfiend
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, looks like WeakReference uses apply, no reason why we shouldn't do the same. Pushed that change to master.

Please sign in to comment.