Skip to content

Instantly share code, notes, and snippets.

@heuristicfencepost
Created February 4, 2011 07:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save heuristicfencepost/810840 to your computer and use it in GitHub Desktop.
Save heuristicfencepost/810840 to your computer and use it in GitHub Desktop.
Implementation of various Scala tests to demonstrate integration with Java's fork/join framework
package org.fencepost.forkjoin
import scala.collection.mutable.LinkedList
import jsr166y._
import org.scalatest.Suite
class ForkJoinTest extends Suite {
val cache = new LinkedList[(()=>Int,Wrapper[Int])]()
class Wrapper[T](somefunc:()=>T) extends RecursiveTask[T] {
override def compute = somefunc.apply
// RecursiveTask.compute() is protected and there's no obvious way to
// elevate it's access level within a Scala class definition. The type
// system (very correctly) doesn't allow a random function access to
// protected methods of a class even when that method is invoked by
// a method within the class itself. Defining a new method with standard
// access resolves the issue.
def surrogate = compute
}
// Rationale for a caching implicit conversion can be found below. We use
// a list of tuples rather than a map since hashing buys us nothing here;
// there is always one (and exactly one) object with a given object identity.
implicit def fn2wrapper(fn:()=>Int):Wrapper[Int] = {
val cacheval = cache find (t => (t != null) && (t._1 eq fn))
cacheval match {
case Some((_,cval)) => cval
case None => {
val newcval = new Wrapper[Int](fn)
cache.next = cache.next :+ (fn,newcval)
newcval
}
}
}
def testFibonacci() = {
def fib(n:Int):Int = {
if (n <= 1)
return n
// The approach used here only works if our implicit conversion always
// returns the same Wrapper instance for a given function. For a given
// RecursiveTask instance join() must be called on the same instance that
// originally called fork(). It follows that an implicit conversion such
// as:
//
// implicit def fn2wrapper(fn:()=>Int):Wrapper[Int] = {
// return new Wrapper(fn)
// }
//
// won't be adequate; fork() and join() will be called on two separate
// objects (since implicit conversion is performed on each method call)
// and join() will never return.
//
// Alternately we can force conversion when f1 and f2 are assigned values
// using something like the following;
//
// val f1:Wrapper[Int] = { ()=> fib(n - 1) }
// val f2:Wrapper[Int] = { ()=> fib(n - 2) }
//
// Clearly in this case f1.fork() and f1.join() will be called on the
// same instance of a RecursiveTask subclass.
val f1 = { ()=> fib(n - 1) }
f1 fork
val f2 = { ()=> fib(n - 2) }
(f2 surrogate) + (f1 join)
}
val pool = new ForkJoinPool()
assert ((pool invoke { ()=> fib(1) }) == 1)
assert ((pool invoke { ()=> fib(2) }) == 1)
assert ((pool invoke { ()=> fib(3) }) == 2)
assert ((pool invoke { ()=> fib(4) }) == 3)
assert ((pool invoke { ()=> fib(5) }) == 5)
assert ((pool invoke { ()=> fib(6) }) == 8)
assert ((pool invoke { ()=> fib(7) }) == 13)
}
}
package org.fencepost.forkjoin
import jsr166y._
import org.scalatest.Suite
class ForkJoinTestFailOne extends Suite {
implicit def fn2wrapper(fn:()=>Int):RecursiveTask[Int] = {
return new RecursiveTask[Int] { override def compute = fn.apply }
}
def testFibonacci() = {
def fib(n:Int):Int = {
if (n <= 1)
return n
val f1 = { ()=> fib(n - 1) }
f1 fork
val f2 = { ()=> fib(n - 2) }
(f2 compute) + (f1 join)
}
val pool = new ForkJoinPool()
assert ((pool invoke { ()=> fib(1) }) == 1)
assert ((pool invoke { ()=> fib(2) }) == 1)
assert ((pool invoke { ()=> fib(3) }) == 2)
assert ((pool invoke { ()=> fib(4) }) == 3)
assert ((pool invoke { ()=> fib(5) }) == 5)
assert ((pool invoke { ()=> fib(6) }) == 8)
assert ((pool invoke { ()=> fib(7) }) == 13)
}
}
package org.fencepost.forkjoin
import jsr166y._
import org.scalatest.Suite
class ForkJoinTestFailTwo extends Suite {
class Wrapper[T](somefunc:()=>T) extends RecursiveTask[T] {
override def compute = somefunc.apply
// RecursiveTask.compute() is protected and there's no obvious way to
// elevate it's access level within a Scala class definition. The type
// system (very correctly) doesn't allow a random function access to
// protected methods of a class even when that method is invoked by
// a method within the class itself. Defining a new method with standard
// access resolves the issue.
def surrogate = compute
}
implicit def fn2wrapper(fn:()=>Int):Wrapper[Int] = {
return new Wrapper[Int](fn)
}
def testFibonacci() = {
def fib(n:Int):Int = {
if (n <= 1)
return n
val f1 = { ()=> fib(n - 1) }
f1 fork
val f2 = { ()=> fib(n - 2) }
(f2 compute) + (f1 join)
}
val pool = new ForkJoinPool()
assert ((pool invoke { ()=> fib(1) }) == 1)
assert ((pool invoke { ()=> fib(2) }) == 1)
assert ((pool invoke { ()=> fib(3) }) == 2)
assert ((pool invoke { ()=> fib(4) }) == 3)
assert ((pool invoke { ()=> fib(5) }) == 5)
assert ((pool invoke { ()=> fib(6) }) == 8)
assert ((pool invoke { ()=> fib(7) }) == 13)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment