|
Play Framework/Scala example source code file (ClosableLazy.scala)
The ClosableLazy.scala Play Framework example source code
/*
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
*/
package play.core
/**
* Provides functionality like Scala's builtin lazy values, except allows cleanup of
* the lazily initialized value. The value is lazily initialized when the `get()` method
* is first called. Later, if the value is no longer needed, it can be cleaned up by
* calling the `close()` method.
*
* Calling `close()` on an uninitialized ClosableLazy will not initialize the value
* if it is not initialized. A ClosableLazy can be closed multiple times.
*
* After being closed, the value cannot be initialized again. ClosableLazy is designed
* to make it easier to clean up resources when shutting down Play. If resources were able
* to be reinitialized after closing, then it would be easy to accidentally allocate resources
* when shutting down. To prevert reinitialization, galling the `get()` method after `close()`
* will result in an `IllegalStateException`.
*
* The performance of this class should be similar to Scala's lazy values. Once initialized,
* a single read of a volatile value is all that is needed to get the value. If the value is not initialized,
* then initialization occurs, Initialization is synchronized on the ClosableLazy object.
*
* This class exposes `T` as the type of value that is lazily created. Subclasses
* should implement the `create()` method to create this value. The `create()` method
* also returns a function that will be called when `close()` is called. This allows
* any resources associated with the value to be closed.
*/
private[play] abstract class ClosableLazy[T >: Null <: AnyRef] {
protected type CloseFunction = (() => Unit)
@volatile
private var value: T = null
private var closeFunction: CloseFunction = null
private var hasBeenClosed: Boolean = false
/**
* Get the value. Calling this method may allocate resources, such as a thread pool.
*
* Calling this method after the `close()` method has been called will result in an
* IllegalStateException.
*/
final def get(): T = {
val currentValue = value
if (currentValue != null) return currentValue
synchronized {
if (hasBeenClosed) throw new IllegalStateException("Can't get ClosableLazy value after it has been closed")
if (value == null) {
val (v, cf): (T, CloseFunction) = create()
if (v == null) throw new IllegalStateException("Can't initialize ClosableLazy to a null value")
if (cf == null) throw new IllegalStateException("Can't initialize ClosableLazy's close function to a null value")
value = v
closeFunction = cf
v
} else {
// Value was initialized by another thread before we got the monitor
value
}
}
}
/**
* Close the value. Calling this method is safe, but does nothing, if the value
* has not been initialized.
*/
final def close(): Unit = {
val optionalClose: Option[CloseFunction] = synchronized {
if (hasBeenClosed) {
// Already closed
None
} else if (value == null) {
// Close before first call to get
hasBeenClosed = true
None
} else {
// Close and call the close function
hasBeenClosed = true
val prevCloseFunction = closeFunction
value = null
closeFunction = null
Some(prevCloseFunction)
}
}
// Perform actual close outside the synchronized block,
// just in case the close function calls get or close
// from another thread.
optionalClose.foreach(_.apply())
}
/**
* Called when the lazy value is first initialized. Returns the value and
* a function to close the value when `close` is called.
*/
protected def create(): (T, CloseFunction)
}
Other Play Framework source code examplesHere is a short list of links related to this Play Framework ClosableLazy.scala source code file: |
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.