├── .gitignore
├── BUILDING.txt
├── LICENSE.txt
├── README
├── RELEASE-NOTES.txt
├── bin
├── all_tests
├── fix_copyrights
├── perf_sample
└── test_coverage
├── build.sbt
├── dep_tests
├── PUBLISHING.txt
├── check_central
├── maven
│ ├── RUN_MAVEN_HERE_TO_TEST_DEPS
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── scala
│ │ └── HelloWorld.scala
└── sbt
│ ├── RUN_SBT_HERE_TO_TEST_DEPS
│ ├── build.sbt
│ └── src
│ └── main
│ └── scala
│ └── HelloWorld.scala
├── disabled
└── stmbench7
│ └── scalastm
│ ├── AssemblyImpl.scala
│ ├── AtomicPartImpl.scala
│ ├── BaseAssemblyImpl.scala
│ ├── ComplexAssemblyImpl.scala
│ ├── CompositePartImpl.scala
│ ├── DesignObjImpl.scala
│ ├── DocumentImpl.scala
│ ├── IdPoolImpl.scala
│ ├── ImmutableSeqImpl.scala
│ ├── ImmutableSetImpl.scala
│ ├── IndexImpl.scala
│ ├── LargeSetImpl.scala
│ ├── ManualImpl.scala
│ ├── ModuleImpl.scala
│ └── ScalaSTMInitializer.scala
├── lib
├── sb7_java-v1.2.tgz
├── stmbench7-VELOX-1.2.LICENSE
└── stmbench7-VELOX-1.2.jar
├── project
├── build.properties
└── plugins.sbt
└── src
├── main
└── scala
│ └── scala
│ └── concurrent
│ └── stm
│ ├── CommitBarrier.scala
│ ├── InTxn.scala
│ ├── InTxnEnd.scala
│ ├── MaybeTxn.scala
│ ├── NestingLevel.scala
│ ├── PendingAtomicBlock.scala
│ ├── Ref.scala
│ ├── RefLike.scala
│ ├── Sink.scala
│ ├── SinkLike.scala
│ ├── Source.scala
│ ├── SourceLike.scala
│ ├── TArray.scala
│ ├── TMap.scala
│ ├── TSet.scala
│ ├── Txn.scala
│ ├── TxnDebuggable.scala
│ ├── TxnExecutor.scala
│ ├── TxnLocal.scala
│ ├── TxnUnknown.scala
│ ├── ccstm
│ ├── AccessHistory.scala
│ ├── CCSTM.scala
│ ├── CCSTMExecutor.scala
│ ├── CCSTMRefs.scala
│ ├── CommitBarrierImpl.scala
│ ├── Counter.scala
│ ├── GV6.scala
│ ├── Handle.scala
│ ├── InTxnImpl.scala
│ ├── InTxnRefOps.scala
│ ├── NonTxn.scala
│ ├── RefOps.scala
│ ├── RetrySet.scala
│ ├── RetrySetBuilder.scala
│ ├── Stats.scala
│ ├── TArrayImpl.scala
│ ├── TxnLevelImpl.scala
│ ├── TxnLocalImpl.scala
│ ├── TxnSlotManager.scala
│ ├── UnrecordedRead.scala
│ ├── ViewOps.scala
│ └── WakeupManager.scala
│ ├── impl
│ ├── AlternativeResult.scala
│ ├── RefFactory.scala
│ ├── STMImpl.scala
│ └── TxnContext.scala
│ ├── japi
│ └── STM.scala
│ ├── package.scala
│ └── skel
│ ├── AbstractInTxn.scala
│ ├── AbstractNestingLevel.scala
│ ├── AtomicArray.scala
│ ├── AtomicArrayBuilder.scala
│ ├── CallbackList.scala
│ ├── HashTrieTMap.scala
│ ├── HashTrieTSet.scala
│ ├── RollbackError.scala
│ ├── SimpleRandom.scala
│ ├── StubInTxn.scala
│ ├── StubSTMImpl.scala
│ ├── TMapViaClone.scala
│ ├── TSetViaClone.scala
│ └── TxnHashTrie.scala
└── test
├── java
└── scala
│ └── concurrent
│ └── stm
│ ├── JavaAPITests.java
│ └── TestException.java
└── scala
└── scala
└── concurrent
└── stm
├── CallbackSuite.scala
├── CommitBarrierSuite.scala
├── ContentionSuite.scala
├── FlipperSuite.scala
├── HistogramSuite.scala
├── InterruptSuite.scala
├── IsolatedRefSuite.scala
├── JavaAPISuite.scala
├── MaybeTxnSuite.scala
├── RelaxedValidationSuite.scala
├── RetrySuite.scala
├── Slow.scala
├── TMapSuite.scala
├── TSetSuite.scala
├── TokenRingSuite.scala
├── TxnLocalSuite.scala
├── TxnSuite.scala
├── UnrecordedTxnSuite.scala
├── WriteSkewSuite.scala
├── examples
├── BasicSyntax.scala
├── ConcurrentIntList.scala
├── DiningPhilosophers.scala
├── IndexedMap.scala
├── RealityShowPhilosophers.scala
└── SyntaxCheatSheet.scala
├── impl
└── RefFactorySuite.scala
└── skel
├── AtomicArraySuite.scala
└── SimpleRandomSuite.scala
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iws
2 | .*.swp
3 | .idea/
4 | out/
5 | target
6 | lib_managed/
7 | src_managed/
8 | project/boot/
9 | dep_tests/sbt/project/boot/
10 | cover/
11 |
--------------------------------------------------------------------------------
/BUILDING.txt:
--------------------------------------------------------------------------------
1 | BUILDING FROM SOURCE
2 |
3 | scala-stm is built using sbt: http://www.scala-sbt.org/
4 | Once you've installed sbt you can download scala-stm's compile dependency
5 | (ScalaTest), build, and run the unit tests with
6 |
7 | sbt update test
8 |
9 | You can get a list of sbt build targets with "sbt tasks". If you
10 | compile, generate documentation or build a JAR, look in target/scala_* for
11 | the results. The Scala version is configured in ./build.sbt. To run
12 | a build target for all of the versions prefix the action with a "+",
13 | for example "sbt +test".
14 |
15 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009-2012 Stanford University, unless otherwise specified.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright
9 | notice, this list of conditions and the following disclaimer in the
10 | documentation and/or other materials provided with the distribution.
11 | * Neither the name of the Stanford University nor the
12 | names of its contributors may be used to endorse or promote products
13 | derived from this software without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL STANFORD UNIVERSITY BE LIABLE FOR ANY
19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | ScalaSTM is a lightweight software transactional memory for Scala,
2 | inspired by the STMs in Haskell and Clojure.
3 |
4 | The current release is 0.8. There is not currently a published
5 | snapshot release. For download info and documentation see
6 | http://scala-stm.org
7 |
--------------------------------------------------------------------------------
/RELEASE-NOTES.txt:
--------------------------------------------------------------------------------
1 | ScalaSTM - 0.8-SNAPSHOT RELEASE NOTES
2 |
3 | Changes between 0.7 and 0.8-SNAPSHOT:
4 |
5 | * correctness fix for TArray[Long] and AtomicArray.ofLong.
6 |
7 | * small improvement to TxnLocal interface.
8 |
9 | * add 2.12 build and remove 2.10 build.
10 |
11 | * add deprecated message about incomplete deadlock detection for
12 | CommitBarrier.
13 |
14 | Snapshot releases deployed to the oss.sonatype.org repository are tested
15 | and functional, but may have changing APIs.
16 |
17 | ----
18 |
19 | Changes between 0.6 and 0.7:
20 |
21 | * better support for interactive debuggers, via TxnDebuggable and
22 | atomic.unrecorded. IntelliJ IDEA and Eclipse can now watch Ref
23 | values inside a transaction without affecting the read or write sets.
24 |
25 | * ScalaSTM cooperates with 2.10's scala.concurrent.BlockingContext.
26 |
27 | * added transformAndExtract, which allows an arbitrary value to be
28 | returned from the transformation function.
29 |
30 | * added transformAndGet and getAndTransform to Ref and TxnLocal,
31 | previously these were only defined for Ref.View.
32 |
33 | ----
34 |
35 | Changes between 0.5 and 0.6:
36 |
37 | * retry and retryFor added to the Java compatibility interface.
38 |
39 | * uses of scala.actor.threadpool.TimeUnit in the interface replaced with
40 | java.util.concurrent.TimeUnit, to avoid making ScalaSTM depend on the
41 | separate scala-actors jar in Scala 2.10.
42 |
43 | ----
44 |
45 | Changes between 0.4 and 0.5:
46 |
47 | * Added scala.concurrent.stm.japi.STM, which makes it much cleaner to
48 | access ScalaSTM functionality from Java.
49 |
50 | ----
51 |
52 | Changes between 0.3 and 0.4:
53 |
54 | * CommitBarrier added, which allows multiple atomic blocks (each on its
55 | own thread) to commit together.
56 |
57 | * Small performance improvements.
58 |
59 | * STMBench7 benchmark support added.
60 |
61 | * Automatic selection of STMImpl in most cases.
62 |
63 | ----
64 |
65 | Changes between 0.2 and 0.3:
66 |
67 | * Support for Scala 2.9.0.RC1.
68 |
69 | * Bug fixes (see https://github.com/nbronson/scala-stm/issues/closed ).
70 |
71 | * Timeouts for modular blocking. Set timeouts at the atomic block using
72 | atomic.withRetryTimeout, or at the retry site using retryFor.
73 |
74 | ----
75 |
76 | Changes between 0.1 and 0.2:
77 |
78 | * Substantial performance improvements, especially for nested atomic
79 | blocks.
80 |
81 | * TSet.View and TMap.View are integrated into the Scala collection
82 | class hierarchy, with factory companion objects and Builder and
83 | CanBuildFrom instances.
84 |
85 | * A fix for whileCommitting handlers (issue #3).
86 |
87 | * TxnLocal can now be read and written from while-preparing and while-
88 | committing handlers. Combining TxnLocal and life-cycle handlers is
89 | now more concise.
90 |
91 | * Transaction statistics can be enabled for the default algorithm
92 | with the VM argument -Dccstm.stats=1 (details in the ScalaDoc for
93 | scala.concurrent.stm.ccstm.CCSTM).
94 |
95 |
--------------------------------------------------------------------------------
/bin/all_tests:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -x
4 | set -e
5 |
6 | BASE="`dirname $0`/.."
7 | cd $BASE
8 |
9 | if [ "x$SCALA_VERSIONS" = "x" ]; then
10 | SCALA_VERSIONS="`awk -F= '$1==\"build.scala.versions\" {print $2}' project/build.properties`"
11 | fi
12 |
13 | for v in $SCALA_VERSIONS; do
14 | TARGET_BASE="target/scala_$v"
15 | SCALATEST=$(echo lib_managed/scala_$v/compile/scalatest*.jar)
16 | sbt ++$v test-compile
17 | scala -Dccstm.stats=1 -cp ${SCALATEST}:$TARGET_BASE/classes org.scalatest.tools.Runner -l slow -oW -p $TARGET_BASE/test-classes
18 | scala -cp ${SCALATEST}:$TARGET_BASE/classes org.scalatest.tools.Runner -oW -p $TARGET_BASE/test-classes
19 | done
20 |
--------------------------------------------------------------------------------
/bin/fix_copyrights:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | cd `dirname $0`/..
4 | for f in `grep -l -r '(c) 2009' src`; do
5 | d=`git log -1 --date=short --no-notes -- $f | awk 'NR==3 {print $2}'`
6 | y=`echo $d | sed 's/-.*//'`
7 | sed -i "s/(c) 2009-201./(c) 2009-$y/" $f
8 | done
9 |
--------------------------------------------------------------------------------
/bin/perf_sample:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -x
4 |
5 | BASE="`dirname $0`/.."
6 | BRANCH="`git status | awk '/^# On branch/ {print $NF}'`"
7 |
8 | cd $BASE
9 | WHEN=`date '+%Y%m%d-%H%M%S'`
10 | DIR="perf/`hostname`/perf-$BRANCH-$WHEN"
11 |
12 | if [ "x$VERSIONS" = "x" ]; then
13 | VERSIONS="`awk -F= '$1==\"build.scala.versions\" {print $2}' project/build.properties | sed 's/,/ /g'`"
14 | fi
15 |
16 | mkdir -p "$DIR"
17 | git show --quiet HEAD > "$DIR"/git.hash
18 |
19 | for v in $VERSIONS; do
20 | sbt ++$v test-compile > /dev/null 2>&1
21 | sbt ++$v test test > "$DIR"/.out 2>&1
22 | (
23 | cd "$DIR"
24 | sed 's/[^m]*m//g' .out | uniq | gawk '
25 | /== test-start ==/ {x=x+1;y=0}
26 | /== test-finish ==/ {y=1}
27 | {z=1}
28 | /^.info/ {z=0}
29 | x>0 && (y || z) {print >> ("." x)}'
30 | rm .out
31 | mv .1 test-first-$v.log
32 | mv .2 test-second-$v.log
33 | )
34 | done
35 |
--------------------------------------------------------------------------------
/bin/test_coverage:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -x
4 |
5 | COBERTURA_DIR=$HOME/cobertura/cobertura-1.9.4.1
6 | SCALATEST_VERSION=1.2
7 |
8 | BASE="`dirname $0`/.."
9 | cd $BASE
10 |
11 | WHEN=`date '+%Y%m%d-%H%M%S'`
12 | DIR="cover/cover-$WHEN"
13 | SER="$DIR/cobertura.ser"
14 |
15 | if [ "x$SCALA_VERSION" = "x" ]; then
16 | SCALA_VERSION="`awk -F= '$1==\"build.scala.versions\" {print $2}' project/build.properties | sed 's/ .*//g'`"
17 | fi
18 | TARGET_BASE="target/scala_$SCALA_VERSION"
19 |
20 | SCALATEST="lib_managed/scala_$SCALA_VERSION/compile/scalatest-$SCALATEST_VERSION.jar"
21 |
22 | mkdir -p $DIR
23 | git show --quiet HEAD > $DIR/git.hash
24 |
25 | sbt ++$SCALA_VERSION test-compile > "$DIR"/log 2>&1
26 |
27 | rm -rf $TARGET_BASE/classes-instr
28 | sh $COBERTURA_DIR/cobertura-instrument.sh \
29 | --basedir $TARGET_BASE/classes \
30 | --datafile $SER \
31 | --destination $TARGET_BASE/classes-instr \
32 | scala
33 |
34 | # we have to include classes because cobertura skips those with no methods
35 | scala -cp ${SCALATEST}:$COBERTURA_DIR/cobertura.jar:$TARGET_BASE/classes-instr:$TARGET_BASE/classes \
36 | -Dnet.sourceforge.cobertura.datafile=$SER \
37 | -Dccstm.stats=1 \
38 | org.scalatest.tools.Runner \
39 | -oW -l slow -p $TARGET_BASE/test-classes
40 |
41 | sh $COBERTURA_DIR/cobertura-report.sh \
42 | --datafile $SER \
43 | --destination $DIR \
44 | $BASE/src/main/scala
45 |
--------------------------------------------------------------------------------
/build.sbt:
--------------------------------------------------------------------------------
1 |
2 | name := "scala-stm"
3 | organization := "org.scala-stm"
4 | version := "0.8-SNAPSHOT"
5 | def mimaVersion = "0.8"
6 | scalaVersion := "2.12.4"
7 | crossScalaVersions := Seq("2.11.12", "2.12.4", "2.13.0-M3")
8 | scalacOptions ++= Seq("-deprecation", "-feature")
9 |
10 | javacOptions in (Compile, compile) ++= {
11 | val javaVersion = if (scalaVersion.value.startsWith("2.11")) "1.6" else "1.8"
12 | Seq("-source", javaVersion, "-target", javaVersion)
13 | }
14 |
15 | libraryDependencies += {
16 | val v = if (scalaVersion.value == "2.13.0-M3") "3.0.5-M1" else "3.0.5"
17 | "org.scalatest" %% "scalatest" % v % "test"
18 | }
19 |
20 | libraryDependencies += ("junit" % "junit" % "4.12" % "test")
21 |
22 | mimaPreviousArtifacts := Set(organization.value %% name.value % mimaVersion)
23 |
24 | // skip exhaustive tests
25 | testOptions += Tests.Argument("-l", "slow")
26 |
27 | // test of TxnExecutor.transformDefault must be run by itself
28 | parallelExecution in Test := false
29 |
30 | ////////////////////
31 | // publishing
32 |
33 | pomExtra := {
34 | http://nbronson.github.com/scala-stm/
35 |
36 |
37 | BSD
38 | https://github.com/nbronson/scala-stm/blob/master/LICENSE.txt
39 | repo
40 |
41 |
42 |
43 | scm:git:git@github.com:nbronson/scala-stm.git
44 | git@github.com:nbronson/scala-stm.git
45 |
46 |
47 |
48 | nbronson
49 | Nathan Bronson
50 | ngbronson@gmail.com
51 |
52 |
53 | }
54 |
55 | publishMavenStyle := true
56 |
57 | publishTo := {
58 | val base = "https://oss.sonatype.org/"
59 | if (version.value.trim.endsWith("SNAPSHOT"))
60 | Some("snapshots" at base + "content/repositories/snapshots/")
61 | else
62 | Some("releases" at base + "service/local/staging/deploy/maven2/")
63 | }
64 |
65 | // exclude scalatest from the Maven POM
66 | pomPostProcess := { xi: scala.xml.Node =>
67 | import scala.xml._
68 | val badDeps = (xi \\ "dependency").filter {
69 | x => (x \ "artifactId").text != "scala-library"
70 | } .toSet
71 | def filt(root: Node): Node = root match {
72 | case x: Elem =>
73 | val ch = x.child.filter(!badDeps(_)).map(filt)
74 | Elem(x.prefix, x.label, x.attributes, x.scope, ch.isEmpty, ch: _*)
75 |
76 | case x => x
77 | }
78 | filt(xi)
79 | }
80 |
81 | credentials += Credentials(Path.userHome / ".ivy2" / ".credentials")
82 |
--------------------------------------------------------------------------------
/dep_tests/PUBLISHING.txt:
--------------------------------------------------------------------------------
1 | DEPLOYING THE SCALA-STM ARTIFACT
2 |
3 | With a proper credentials file publishing a snapshot or release artifact is
4 | accomplished with
5 |
6 | sbt +test (or bin/all_tests)
7 | sbt +publish
8 |
9 | TESTING DEPLOYMENT AND ARTIFACT USAGE
10 |
11 | To test using published artifacts via sbt, select the desired scala-stm
12 | artifact in dep_tests/sbt/build.sbt . Then
13 |
14 | cd dep_tests/sbt
15 | sbt +run
16 |
17 | To test building using maven2, edit dep_tests/maven/pom.xml, then
18 |
19 | cd dep_tests/maven
20 | mvn -U compile
21 |
--------------------------------------------------------------------------------
/dep_tests/check_central:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # http://search.maven.org/remotecontent?filepath=org/scala-tools/scala-stm_2.10.0-M7/0.6/scala-stm_2.10.0-M7-0.6.pom
4 |
5 | BUILD_SBT=$(dirname $0)/../build.sbt
6 | GROUP_ID="scala-stm"
7 |
8 | echo "Checking groupId [[$GROUP_ID]]"
9 | echo
10 |
11 | sed 's/[":=(,)]/ /g' "$BUILD_SBT" | \
12 | awk '$1=="version" {stm=$2}
13 | $1=="crossScalaVersions" {for(i=3;i<=NF;++i) {print stm,$i}}' | \
14 | while read x y; do
15 | /bin/echo -en "stm $x scala $y \t"
16 | # URL="http://search.maven.org/remotecontent?filepath=org/scala-stm/scala-stm_$y/$x/scala-stm_$y-$x.pom"
17 | URL="http://repo1.maven.org/maven2/org/$GROUP_ID/scala-stm_$y/$x/scala-stm_$y-$x.pom"
18 | curl -s "$URL" | grep -q "scala-stm_$y"
19 | if [ $? -eq 0 ]; then
20 | echo "synced"
21 | else
22 | echo "NOT SYNCED"
23 | fi
24 | done
25 |
26 |
27 |
--------------------------------------------------------------------------------
/dep_tests/maven/RUN_MAVEN_HERE_TO_TEST_DEPS:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nbronson/scala-stm/150408e695684cb0df034bf870860361a434e327/dep_tests/maven/RUN_MAVEN_HERE_TO_TEST_DEPS
--------------------------------------------------------------------------------
/dep_tests/maven/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 2.12.0
4 | 2.12
5 | 0.8-SNAPSHOT
6 |
7 |
8 | 4.0.0
9 | org.scala-stm
10 | scala-stm-dep-tests-maven
11 | 0.1-SNAPSHOT
12 | jar
13 |
14 |
15 |
16 | oss.sonatype.org
17 | OSS Sonatype Release Repository
18 | https://oss.sonatype.org/content/repositories/releases
19 |
20 |
21 | oss.sonatype.org.snapshots
22 | OSS Sonatype Snapshot Repository
23 | https://oss.sonatype.org/content/repositories/snapshots
24 |
25 |
26 |
27 |
28 |
29 |
30 | oss.sonatype.org
31 | OSS Sonatype Release Repository
32 | https://oss.sonatype.org/content/repositories/releases
33 |
34 |
35 | oss.sonatype.org.snapshots
36 | OSS Sonatype Snapshot Repository
37 | https://oss.sonatype.org/content/repositories/snapshots
38 |
39 |
40 |
41 |
42 |
43 | org.scala-lang
44 | scala-compiler
45 | ${scala.version}
46 |
47 |
48 | org.scala-lang
49 | scala-library
50 | ${scala.version}
51 |
52 |
53 | org.scala-stm
54 | scala-stm_${scala.major.version}
55 | ${scala.stm.version}
56 |
57 |
58 |
59 |
60 |
61 |
62 | org.apache.maven.plugins
63 | maven-compiler-plugin
64 | 2.0.2
65 |
66 | 1.6
67 | 1.6
68 |
69 |
70 |
71 | org.scala-tools
72 | maven-scala-plugin
73 | 2.14
74 |
75 |
76 |
77 | compile
78 | testCompile
79 |
80 |
81 |
82 |
83 | ${scala.version}
84 |
85 | -target:jvm-1.5
86 |
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/dep_tests/maven/src/main/scala/HelloWorld.scala:
--------------------------------------------------------------------------------
1 | import scala.concurrent.stm._
2 |
3 | object HelloWorld {
4 | def main(args: Array[String]) {
5 | val x = Ref("hello world!")
6 | println(x.single())
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/dep_tests/sbt/RUN_SBT_HERE_TO_TEST_DEPS:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nbronson/scala-stm/150408e695684cb0df034bf870860361a434e327/dep_tests/sbt/RUN_SBT_HERE_TO_TEST_DEPS
--------------------------------------------------------------------------------
/dep_tests/sbt/build.sbt:
--------------------------------------------------------------------------------
1 |
2 | name := "scala-stm-dep-tests-sbt"
3 |
4 | organization := "org.scala-stm"
5 |
6 | version := "0.1-SNAPSHOT"
7 |
8 | scalaVersion := "2.12.0"
9 |
10 | crossScalaVersions := Seq("2.12.0", "2.11.6")
11 |
12 | resolvers += ("releases" at "https://oss.sonatype.org/content/repositories/releases")
13 |
14 | resolvers += ("snapshots" at "https://oss.sonatype.org/content/repositories/snapshots")
15 |
16 | libraryDependencies += ("org.scala-stm" %% "scala-stm" % "0.8-SNAPSHOT")
17 |
--------------------------------------------------------------------------------
/dep_tests/sbt/src/main/scala/HelloWorld.scala:
--------------------------------------------------------------------------------
1 | import scala.concurrent.stm._
2 |
3 | object HelloWorld {
4 | def main(args: Array[String]) {
5 | val x = Ref("hello world!")
6 | println(x.single())
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/AssemblyImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.concurrent.stm._
6 | import stmbench7.core._
7 |
8 | abstract class AssemblyImpl(id: Int, typ: String, buildDate: Int, module0: Module, superAssembly0: ComplexAssembly
9 | ) extends DesignObjImpl(id, typ, buildDate) with Assembly {
10 |
11 | private val module = Ref(module0).single
12 | private val superAssembly = Ref(superAssembly0).single
13 |
14 | def getSuperAssembly = superAssembly()
15 | def getModule = module()
16 |
17 | def clearPointers() {
18 | superAssembly() = null
19 | module() = null
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/AtomicPartImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.concurrent.stm._
6 | import stmbench7.core._
7 | import stmbench7.impl.core.ConnectionImpl
8 |
9 | class AtomicPartImpl(id0: Int, typ0: String, bd0: Int, x0: Int, y0: Int) extends DesignObjImpl(id0, typ0, bd0) with AtomicPart {
10 | val x = Ref(x0)
11 | val y = Ref(y0)
12 | val partOf = Ref(null : CompositePart).single
13 | val from = TSet.empty[Connection].single // this is the equivant of SmallSetImpl
14 | val to = TSet.empty[Connection].single
15 |
16 | def connectTo(dest: AtomicPart, typ: String, length: Int) {
17 | val c = new ConnectionImpl(this, dest, typ, length)
18 | to += c
19 | dest.addConnectionFromOtherPart(c.getReversed)
20 | }
21 | def addConnectionFromOtherPart(c: Connection) { from += c }
22 | def setCompositePart(po: CompositePart) { partOf() = po }
23 | def getNumToConnections = to.size
24 | def getToConnections = new ImmutableSetImpl[Connection](to)
25 | def getFromConnections = new ImmutableSetImpl[Connection](from)
26 | def getPartOf = partOf()
27 | def swapXY() {
28 | atomic { implicit t =>
29 | y() = x.swap(y())
30 | }
31 | }
32 | def getX = x.single()
33 | def getY = y.single()
34 | def clearPointers() {
35 | atomic { implicit t =>
36 | x() = 0
37 | y() = 0
38 | to.clear()
39 | from.clear()
40 | partOf() = null
41 | }
42 | }
43 |
44 | // Comparable[AtomicPart]
45 | def compareTo(rhs: AtomicPart) = getId - rhs.getId // subtraction is faithful to reference impl
46 | }
47 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/BaseAssemblyImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.concurrent.stm._
6 | import stmbench7.core._
7 |
8 | class BaseAssemblyImpl(id: Int, typ: String, buildDate: Int, module: Module, superAssembly: ComplexAssembly
9 | ) extends AssemblyImpl(id, typ, buildDate, module, superAssembly) with BaseAssembly {
10 |
11 | val components = Ref(List.empty[CompositePart]).single // the original BagImpl was just an ArrayList
12 |
13 | def addComponent(component: CompositePart) {
14 | components.transform(component :: _)
15 | component.addAssembly(this)
16 | }
17 |
18 | def removeComponent(component: CompositePart) = {
19 | val f = components transformIfDefined {
20 | case x if x contains component => x filterNot { _ == component }
21 | }
22 | if (f) component.removeAssembly(this)
23 | f
24 | }
25 |
26 | def getComponents = new ImmutableSeqImpl[CompositePart](components())
27 |
28 | override def clearPointers() {
29 | super.clearPointers()
30 | components() = null
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/ComplexAssemblyImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.concurrent.stm._
6 | import stmbench7.core._
7 | import stmbench7.Parameters
8 |
9 | class ComplexAssemblyImpl(id: Int, typ: String, buildDate: Int, module: Module, superAssembly: ComplexAssembly
10 | ) extends AssemblyImpl(id, typ, buildDate, module, superAssembly) with ComplexAssembly {
11 |
12 | val subAssemblies = TSet.empty[Assembly].single
13 | val level = if (superAssembly == null) Parameters.NumAssmLevels else superAssembly.getLevel - 1
14 |
15 | def addSubAssembly(assembly: Assembly) = {
16 | if(assembly.isInstanceOf[BaseAssembly] && level != 2)
17 | throw new RuntimeError("ComplexAssembly.addAssembly: BaseAssembly at wrong level!");
18 |
19 | subAssemblies.add(assembly)
20 | }
21 |
22 | def removeSubAssembly(assembly: Assembly) = subAssemblies.remove(assembly)
23 |
24 | def getSubAssemblies = new ImmutableSetImpl[Assembly](subAssemblies)
25 |
26 | def getLevel = level.asInstanceOf[Short]
27 |
28 | override def clearPointers() {
29 | super.clearPointers()
30 | subAssemblies.clear()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/CompositePartImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.concurrent.stm._
6 | import stmbench7.core._
7 | import stmbench7.backend.BackendFactory
8 |
9 | class CompositePartImpl(id: Int, typ: String, buildDate: Int, documentation0: Document
10 | ) extends DesignObjImpl(id, typ, buildDate) with CompositePart {
11 | val documentation = Ref(documentation0).single
12 | val usedIn = Ref(List.empty[BaseAssembly]).single
13 | val parts = Ref(BackendFactory.instance.createLargeSet[AtomicPart]).single
14 | val rootPart = Ref(null : AtomicPart).single
15 | documentation0.setPart(this)
16 |
17 | def addAssembly(assembly: BaseAssembly) { usedIn.transform(assembly :: _) }
18 |
19 | def addPart(part: AtomicPart) = {
20 | val f = parts().add(part)
21 | if (f) {
22 | part.setCompositePart(this)
23 | if(rootPart() == null) rootPart() = part
24 | }
25 | f
26 | }
27 |
28 | def getRootPart = rootPart()
29 | def setRootPart(part: AtomicPart) { rootPart() = part }
30 |
31 | def getDocumentation = documentation()
32 |
33 | def getParts = parts()
34 |
35 | def removeAssembly(assembly: BaseAssembly ) {
36 | usedIn.transform { _.filterNot(_ == assembly) }
37 | }
38 |
39 | def getUsedIn = new ImmutableSeqImpl[BaseAssembly](usedIn())
40 |
41 | def clearPointers() {
42 | documentation() = null
43 | parts() = null
44 | usedIn() = null
45 | rootPart() = null
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/DesignObjImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.concurrent.stm._
6 | import stmbench7.core._
7 |
8 | class DesignObjImpl(id: Int, typ: String, buildDate0: Int) extends DesignObj {
9 | val bd = Ref(buildDate0).single
10 |
11 | def getId = id
12 | def getType = typ
13 | def getBuildDate = bd()
14 | def updateBuildDate() { if (bd() % 2 == 0) bd -= 1 else bd += 1 }
15 | def nullOperation() {}
16 | }
17 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/DocumentImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.annotation.tailrec
6 | import scala.concurrent.stm._
7 | import stmbench7.core._
8 |
9 | class DocumentImpl(id: Int, title: String, text0: String) extends Document {
10 | val text = Ref(text0).single
11 | val part = Ref(null : CompositePart).single
12 |
13 | def getDocumentId = id
14 | def getTitle = title
15 |
16 | def getCompositePart = part()
17 | def setPart(v: CompositePart) { part() = v }
18 |
19 | def nullOperation() {}
20 |
21 | def getText = text()
22 | def searchText(symbol: Char): Int = searchText(text(), symbol, 0, 0)
23 | @tailrec private def searchText(s: String, c: Char, pos: Int, n: Int): Int = {
24 | val i = s.indexOf(c, pos)
25 | if (i < 0) n else searchText(s, c, i + 1, n + 1)
26 | }
27 | def replaceText(from: String, to: String) = {
28 | val f = text transformIfDefined {
29 | case s if s.startsWith(from) => s.replaceFirst(from, to)
30 | }
31 | if (f) 1 else 0
32 | }
33 | def textBeginsWith(prefix: String) = text().startsWith(prefix)
34 | }
35 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/IdPoolImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.concurrent.stm._
6 | import stmbench7.core._
7 | import stmbench7.backend.IdPool
8 |
9 | object IdPoolImpl {
10 | class BoxedList(n: Int) extends IdPool {
11 | val underlying = Ref(List.range(1, n + 1)).single
12 |
13 | def putUnusedId(id: Int) { underlying.transform(id :: _) }
14 |
15 | def getId = {
16 | underlying.getAndTransform(_.drop(1)) match {
17 | case head :: _ => head
18 | case _ => throw new OperationFailedException
19 | }
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/ImmutableSeqImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.collection.JavaConversions
6 | import stmbench7.backend.ImmutableCollection
7 |
8 | class ImmutableSeqImpl[A](contents: Seq[A]) extends ImmutableCollection[A] {
9 | override def clone = this
10 | def contains(element: A) = contents.contains(element)
11 | def size = contents.size
12 | def iterator = JavaConversions.asJavaIterator(contents.iterator)
13 | }
14 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/ImmutableSetImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.collection.JavaConversions
6 | import scala.concurrent.stm._
7 | import stmbench7.backend.ImmutableCollection
8 |
9 | /** Read-only wrapper */
10 | class ImmutableSetImpl[A](contents: TSet.View[A], shared: Boolean = true) extends ImmutableCollection[A] {
11 | override def clone: ImmutableSetImpl[A] = if (!shared) this else new ImmutableSetImpl(contents.clone(), false)
12 | def contains(element: A) = contents.contains(element)
13 | def size = contents.size
14 | def iterator = JavaConversions.asJavaIterator(contents.iterator)
15 | }
16 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/IndexImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.annotation._
6 | import scala.collection.immutable.{RedBlack, TreeMap}
7 | import scala.collection.mutable.ArrayBuffer
8 | import scala.collection.JavaConversions
9 | import scala.concurrent.stm._
10 | import stmbench7.backend.Index
11 |
12 | object IndexImpl {
13 | class BoxedImmutable[A <: Comparable[A], B] extends Index[A,B] {
14 | // needed for 2.8, harmless in 2.9
15 | implicit val order = new Ordering[A] {
16 | def compare(lhs: A, rhs: A) = lhs compareTo rhs
17 | }
18 |
19 | val underlying = Ref(TreeMap.empty[A,B]).single
20 |
21 | def get(key: A) = underlying().getOrElse(key, null.asInstanceOf[B])
22 |
23 | def put(key: A, value: B) { underlying.transform(_ + (key -> value)) }
24 |
25 | def putIfAbsent(key: A, value: B): B = {
26 | underlying.getAndTransform({ m => if (m.contains(key)) m else m + (key -> value) }).getOrElse(key, null.asInstanceOf[B])
27 | }
28 |
29 | def remove(key: A) = underlying transformIfDefined {
30 | case m if m.contains(key) => m - key
31 | }
32 |
33 | def iterator = makeValuesIterator(underlying())
34 |
35 | def getKeys = JavaConversions.asJavaSet(underlying().keySet)
36 |
37 | def getRange(minKey: A, maxKey: A) = new java.lang.Iterable[B] {
38 | val range = underlying().range(minKey, maxKey)
39 | def iterator = makeValuesIterator(range)
40 | }
41 |
42 | private def makeValuesIterator(m: TreeMap[A, B]) = {
43 | if (treeMapTreeField == null)
44 | JavaConversions.asJavaIterator(m.values.iterator)
45 | else
46 | makeCustomValuesIterator(m)
47 | }
48 |
49 | // We implement our own iterator because the Scala one generates
50 | // a large quantity of garbage pre 2.10.
51 | private def makeCustomValuesIterator(m: TreeMap[A, B]) = {
52 | val root = treeMapTreeField.get(m).asInstanceOf[RedBlack[A]#Tree[B]]
53 | new java.util.Iterator[B] {
54 | val avail = new ArrayBuffer[RedBlack[A]#NonEmpty[B]] // values ready to return
55 | pushAll(root)
56 |
57 | @tailrec final def pushAll(n: RedBlack[A]#Tree[B]) {
58 | n match {
59 | case ne: RedBlack[A]#NonEmpty[B] => {
60 | avail += ne
61 | pushAll(ne.left)
62 | }
63 | case _ =>
64 | }
65 | }
66 |
67 | def hasNext = !avail.isEmpty
68 |
69 | def next(): B = {
70 | val n = avail.remove(avail.size - 1)
71 | pushAll(n.right)
72 | n.value
73 | }
74 |
75 | def remove() = throw new UnsupportedOperationException
76 | }
77 | }
78 | }
79 |
80 | private val treeMapTreeField = {
81 | val f = classOf[TreeMap[String, String]].getDeclaredField("tree")
82 | if (f.getType == classOf[RedBlack[String]#Tree[String]]) {
83 | // pre Scala 2.10, we need the custom iterator hack
84 | f.setAccessible(true)
85 | f
86 | } else {
87 | // Scala 2.10 or later uses RedBlackTree, which array-based iter
88 | null
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/LargeSetImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.collection.JavaConversions
6 | import scala.concurrent.stm._
7 | import stmbench7.backend.LargeSet
8 |
9 | // LargeSetImpl
10 |
11 | class LargeSetImpl[A <: Comparable[A]] extends LargeSet[A] {
12 | val underlying = TMap.empty[A,Unit].single
13 |
14 | def add(e: A) = underlying.put(e, ()).isEmpty
15 | def remove(e: A) = !underlying.remove(e).isEmpty
16 | def contains(e: A) = underlying.contains(e)
17 | def size = underlying.size
18 | def iterator = JavaConversions.asJavaIterator(underlying.keysIterator)
19 | }
20 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/ManualImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.concurrent.stm._
6 | import scala.collection.immutable.WrappedString
7 | import scala.annotation.tailrec
8 | import stmbench7.core._
9 |
10 | class ManualImpl(id: Int, title0: String, text0: String) extends Manual {
11 | val title = Ref(title0).single
12 | val text = Ref(text0).single
13 | val module = Ref(null : Module).single
14 |
15 | def getId = id
16 | def getTitle = title()
17 | def getText = text()
18 | def getModule = module()
19 |
20 | def setModule(v: Module) { module() = v }
21 |
22 | def countOccurences(ch: Char): Int = countOccurrences(ch)
23 | def countOccurrences(ch: Char): Int = countOccurrences(text(), ch, 0, 0)
24 | @tailrec private def countOccurrences(s: String, c: Char, pos: Int, n: Int): Int = {
25 | val i = s.indexOf(c, pos)
26 | if (i < 0) n else countOccurrences(s, c, i + 1, n + 1)
27 | }
28 |
29 | def checkFirstLastCharTheSame = {
30 | val t = text()
31 | if (t.charAt(0) == t.charAt(t.length - 1)) 1 else 0
32 | }
33 |
34 | def startsWith(ch: Char) = text().charAt(0) == ch
35 |
36 | def replaceChar(from: Char, to: Char) = {
37 | text.transform(_.replace(from, to))
38 | countOccurences(to)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/ModuleImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.concurrent.stm._
6 | import stmbench7.core._
7 |
8 | class ModuleImpl(id: Int, typ: String, buildDate: Int, man: Manual
9 | ) extends DesignObjImpl(id, typ, buildDate) with Module {
10 |
11 | val designRoot = Ref(null : ComplexAssembly).single
12 |
13 | man.setModule(this)
14 |
15 | def setDesignRoot(v: ComplexAssembly ) { designRoot() = v }
16 | def getDesignRoot = designRoot()
17 | def getManual = man
18 | }
19 |
--------------------------------------------------------------------------------
/disabled/stmbench7/scalastm/ScalaSTMInitializer.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package stmbench7.scalastm
4 |
5 | import scala.concurrent.stm._
6 | import stmbench7.backend._
7 | import stmbench7.core._
8 | import stmbench7.impl.core.ConnectionImpl
9 | import stmbench7._
10 |
11 | class ScalaSTMInitializer extends SynchMethodInitializer {
12 | def createOperationExecutorFactory() = new OperationExecutorFactory {
13 |
14 | val timestamp = Ref(0)
15 |
16 | def createOperationExecutor(op: Operation) = new OperationExecutor {
17 | var lastTS = 0
18 |
19 | def execute(): Int = {
20 | atomic { implicit t =>
21 | val z = op.performOperation()
22 | if (Parameters.sequentialReplayEnabled) {
23 | timestamp += 1
24 | lastTS = timestamp()
25 | }
26 | z
27 | }
28 | }
29 |
30 | def getLastOperationTimestamp = lastTS
31 | }
32 | }
33 |
34 | def createBackendFactory() = new BackendFactory {
35 |
36 | // a couple hundred, not ordered, except for huge numbers of discarded
37 | // sets with exactly 1 element
38 | def createLargeSet[E <: Comparable[E]](): LargeSet[E] = new LargeSetImpl[E]
39 |
40 | // ordered
41 | def createIndex[K <: Comparable[K],V](): Index[K,V] = new IndexImpl.BoxedImmutable[K,V]
42 |
43 | def createIdPool(maxNumberOfIds: Int): IdPool = new IdPoolImpl.BoxedList(maxNumberOfIds)
44 | }
45 |
46 | def createDesignObjFactory() = new DesignObjFactory {
47 |
48 | def createAtomicPart(id: Int, typ: String, bd: Int, x: Int, y: Int) =
49 | new AtomicPartImpl(id, typ, bd, x, y)
50 |
51 | def createConnection(from: AtomicPart, to: AtomicPart, typ: String, length: Int) =
52 | new ConnectionImpl(from, to, typ, length)
53 |
54 | def createBaseAssembly(id: Int, typ: String, buildDate: Int, module: Module, superAssembly: ComplexAssembly) =
55 | new BaseAssemblyImpl(id, typ, buildDate, module, superAssembly)
56 |
57 | def createComplexAssembly(id: Int, typ: String, buildDate: Int, module: Module, superAssembly: ComplexAssembly) =
58 | new ComplexAssemblyImpl(id, typ, buildDate, module, superAssembly)
59 |
60 | def createCompositePart(id: Int, typ: String, buildDate: Int, documentation: Document) =
61 | new CompositePartImpl(id, typ, buildDate, documentation)
62 |
63 | def createDocument(id: Int, title: String, text: String) =
64 | new DocumentImpl(id, title, text)
65 |
66 | def createManual(id: Int, title: String, text: String) =
67 | new ManualImpl(id, title, text)
68 |
69 | def createModule(id: Int, typ: String, buildDate: Int, man: Manual) =
70 | new ModuleImpl(id, typ, buildDate, man)
71 | }
72 |
73 | def createThreadFactory() = new stmbench7.ThreadFactory {
74 | def createThread(body: Runnable) = new Thread(body)
75 | }
76 | }
--------------------------------------------------------------------------------
/lib/sb7_java-v1.2.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nbronson/scala-stm/150408e695684cb0df034bf870860361a434e327/lib/sb7_java-v1.2.tgz
--------------------------------------------------------------------------------
/lib/stmbench7-VELOX-1.2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nbronson/scala-stm/150408e695684cb0df034bf870860361a434e327/lib/stmbench7-VELOX-1.2.jar
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=1.1.1
2 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.18") // test binary compatibility between minor versions
2 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/InTxn.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | /** The presence of an implicit `InTxn` instance grants the caller permission
6 | * to perform transactional reads and writes on `Ref` instances, as well as
7 | * permission to call `object Txn` methods that require an `InTxnEnd`.
8 | * `InTxn` instances themselves might be reused by the STM, use
9 | * `NestingLevel.current` or `NestingLevel.root` to get a `NestingLevel` if
10 | * you need to track an individual execution attempt.
11 | *
12 | * @author Nathan Bronson
13 | */
14 | trait InTxn extends InTxnEnd
15 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/InTxnEnd.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | /** The presence of an implicit `InTxnEnd` instance inside a transaction
6 | * life-cycle handler grants permission to call methods in `object Txn` that
7 | * locate nesting levels or register additional handlers. This functionality
8 | * is separated from that granted by `InTxn` because `Ref` operations are not
9 | * allowed from handlers after commit has begun.
10 | *
11 | * @author Nathan Bronson
12 | */
13 | trait InTxnEnd extends MaybeTxn {
14 | import Txn._
15 |
16 | // The user-visible versions of these methods are in the Txn object.
17 |
18 | protected[stm] def status: Status
19 | protected[stm] def rootLevel: NestingLevel
20 | protected[stm] def currentLevel: NestingLevel
21 | protected[stm] def rollback(cause: RollbackCause): Nothing
22 | protected[stm] def retry(): Nothing
23 | protected[stm] def retryFor(timeoutNanos: Long): Unit
24 | protected[stm] def beforeCommit(handler: InTxn => Unit): Unit
25 | protected[stm] def whilePreparing(handler: InTxnEnd => Unit): Unit
26 | protected[stm] def whileCommitting(handler: InTxnEnd => Unit): Unit
27 | protected[stm] def afterCommit(handler: Status => Unit): Unit
28 | protected[stm] def afterRollback(handler: Status => Unit): Unit
29 | protected[stm] def afterCompletion(handler: Status => Unit): Unit
30 | protected[stm] def setExternalDecider(decider: ExternalDecider): Unit
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/MaybeTxn.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | object MaybeTxn {
6 | implicit val unknown = TxnUnknown
7 | }
8 |
9 | /** `MaybeTxn` allows lookup of the implicit `InTxn` instance without failing
10 | * if the `InTxn` is not known at compile time. `implicitly[MaybeTxn]` will
11 | * bind to an implicit `InTxn` if one is available, otherwise it will bind to
12 | * the object `TxnUnkown`. A `MaybeTxn` of `TxnUnknown` should trigger a
13 | * dynamically-scoped `InTxn` search using `Txn.findCurrent`.
14 | *
15 | * @author Nathan Bronson
16 | */
17 | trait MaybeTxn
18 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/NestingLevel.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | object NestingLevel {
6 |
7 | /** Returns the `NestingLevel` that represents the current atomic block
8 | * execution attempt.
9 | */
10 | def current(implicit txn: InTxnEnd): NestingLevel = txn.currentLevel
11 |
12 | /** Returns the `NestingLevel` that represents the outermost atomic block
13 | * that is currently being attempted.
14 | */
15 | def root(implicit txn: InTxnEnd): NestingLevel = txn.rootLevel
16 | }
17 |
18 | /** A `NestingLevel` instance describes a single attempt to execute an atomic
19 | * block inside a transaction. Reads and writes performed by a transaction
20 | * will only be made visible to other threads after (if) the root nesting
21 | * level commits.
22 | *
23 | * Methods on this class may be called from any thread, and may be called
24 | * after the corresponding execution attempt has been completed.
25 | *
26 | * @author Nathan Bronson
27 | */
28 | trait NestingLevel {
29 | import Txn._
30 |
31 | /** Returns the `TxnExecutor` in which this attempt is executing. */
32 | def executor: TxnExecutor
33 |
34 | /** Returns the nearest enclosing nesting level, if any. */
35 | def parent: Option[NestingLevel]
36 |
37 | /** Returns the outermost enclosing nested transaction context, or this
38 | * instance if it is the outermost nesting level. It is always true that
39 | * `a.parent.isEmpty == (a.root == a)`.
40 | */
41 | def root: NestingLevel
42 |
43 | /** Returns a snapshot of this nesting level's current status. The status
44 | * may change to `Txn.RolledBack` due to the actions of a concurrent
45 | * thread. This method may be called from any thread.
46 | */
47 | def status: Status
48 |
49 | /** Requests that a transaction attempt be marked for rollback, possibly
50 | * also rolling back some or all of the enclosing nesting levels. Returns
51 | * the resulting status, which will be one of `Prepared`, `Committed` or
52 | * `RolledBack`. Regardless of the status, this method does not throw an
53 | * exception.
54 | *
55 | * Unlike `Txn.rollback(cause)`, this method may be called from any thread.
56 | * Note that there is no facility for remotely triggering a rollback during
57 | * the `Prepared` state, as the `ExplicitDecider` is given the final choice.
58 | */
59 | def requestRollback(cause: RollbackCause): Status
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/PendingAtomicBlock.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | /** Instances of `PendingAtomicBlock` defer the execution of an atomic block
6 | * until all of the alternatives can be gathered from the user. There is an
7 | * implicit conversion in the `stm` package object from any type `A` to a
8 | * `PendingAtomicBlock[A]`, which will kick in if there is an attempt to call
9 | * `.orAtomic` on a value.
10 | *
11 | * @author Nathan Bronson
12 | */
13 | class PendingAtomicBlock[A](above: => A) {
14 |
15 | /** See `atomic.oneOf`. */
16 | def orAtomic[B >: A](below: InTxn => B)(implicit mt: MaybeTxn): B = {
17 | // Execution of Delayed.orAtomic proceeds bottom to top, with the upper
18 | // portions being captured by-name in `above`. The actual transactional
19 | // execution is all performed inside the top-most block (the one that
20 | // used atomic rather than orAtomic). If a block other than the top one
21 | // is the one that eventually succeeds, we must tunnel the value out with
22 | // an exception because the alternatives may have a wider type. We only
23 | // catch the exception if we are the bottom-most alternative, because
24 | // only it is guaranteed to have been fully widened.
25 | if (!atomic.pushAlternative(mt, below)) {
26 | // we're not the bottom
27 | above
28 | } else {
29 | // we're the bottom, this is the end of the result tunnel
30 | try {
31 | above
32 | } catch {
33 | case impl.AlternativeResult(x) => x.asInstanceOf[B]
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/RefLike.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 |
6 | /** Provides all of the operations of a `Ref[A]`, without the ability to get a
7 | * `Ref.View`.
8 | *
9 | * @author Nathan Bronson
10 | */
11 | trait RefLike[A, Context] extends SourceLike[A, Context] with SinkLike[A, Context] {
12 |
13 | // read-only operations (covariant) are in SourceLike
14 | // write-only operations (contravariant) are in SinkLike
15 | // read+write operations go here
16 |
17 | /** Works like `set(v)`, but returns the old value.
18 | * @return the previous value of this `Ref`, as observed by `txn`.
19 | * @throws IllegalStateException if `txn` is not active.
20 | */
21 | def swap(v: A)(implicit txn: Context): A
22 |
23 | /** Transforms the value referenced by this `Ref` by applying the function
24 | * `f`. Acts like `ref.set(f(ref.get))`, but the execution of `f` may be
25 | * deferred or repeated by the STM to reduce transaction conflicts.
26 | * @param f a function that is safe to call multiple times, and safe to
27 | * call later during the transaction.
28 | * @throws IllegalStateException if `txn` is not active.
29 | */
30 | def transform(f: A => A)(implicit txn: Context): Unit
31 |
32 | /** Transforms the value referenced by this `Ref` by applying the function
33 | * `f`, and returns the new value.
34 | * @param f a function that is safe to call multiple times.
35 | * @return the new value of this `Ref` (the value returned from `f`).
36 | * @throws IllegalStateException if `txn` is not active.
37 | */
38 | def transformAndGet(f: A => A)(implicit txn: Context): A = { val z = f(get) ; set(z) ; z }
39 |
40 | /** Transforms the value referenced by this `Ref` by applying the function
41 | * `f`, and returns the previous value.
42 | * @param f a function that is safe to call multiple times.
43 | * @return the previous value of this `Ref` (the value passed to `f`).
44 | * @throws IllegalStateException if `txn` is not active.
45 | */
46 | def getAndTransform(f: A => A)(implicit txn: Context): A = { val z = get ; set(f(z)) ; z }
47 |
48 | /** Transforms the value referenced by this `Ref` from ''v'' to
49 | * `f`(''v'')`._1`, and returns `f`(''v'')`._2`.
50 | * @param f a function that is safe to call multiple times.
51 | * @return the second element of the pair returned by `f`.
52 | * @throws IllegalStateException if `txn` is not active.
53 | */
54 | def transformAndExtract[B](f: A => (A,B))(implicit txn: Context): B = { val p = f(get) ; set(p._1) ; p._2 }
55 |
56 | /** Transforms the value ''v'' referenced by this `Ref` by to
57 | * `pf.apply`(''v''), but only if `pf.isDefinedAt`(''v''). Returns true if
58 | * a transformation was performed, false otherwise. `pf.apply` and
59 | * `pf.isDefinedAt` may be deferred or repeated by the STM to reduce
60 | * transaction conflicts.
61 | * @param pf a partial function that is safe to call multiple times, and
62 | * safe to call later in the transaction.
63 | * @return `pf.isDefinedAt(v)`, where `v` was the value of this `Ref`
64 | * before transformation (if any).
65 | * @throws IllegalStateException if `txn` is not active.
66 | */
67 | def transformIfDefined(pf: PartialFunction[A,A])(implicit txn: Context): Boolean
68 |
69 | /** Transforms the value stored in the `Ref` by incrementing it.
70 | *
71 | * '''Note: Implementations may choose to ignore the provided `Numeric[A]`
72 | * instance if `A` is a primitive type.'''
73 | *
74 | * @param rhs the quantity by which to increment the value of this `Ref`.
75 | * @throws IllegalStateException if `txn` is not active. */
76 | def += (rhs: A)(implicit txn: Context, num: Numeric[A]): Unit = transform { v => num.plus(v, rhs) }
77 |
78 | /** Transforms the value stored in the `Ref` by decrementing it.
79 | *
80 | * '''Note: Implementations may choose to ignore the provided `Numeric[A]`
81 | * instance if `A` is a primitive type.'''
82 | *
83 | * @param rhs the quantity by which to decrement the value of this `Ref`.
84 | * @throws IllegalStateException if `txn` is not active. */
85 | def -= (rhs: A)(implicit txn: Context, num: Numeric[A]): Unit = transform { v => num.minus(v, rhs) }
86 |
87 | /** Transforms the value stored in the `Ref` by multiplying it.
88 | *
89 | * '''Note: Implementations may choose to ignore the provided `Numeric[A]`
90 | * instance if `A` is a primitive type.'''
91 | *
92 | * @param rhs the quantity by which to multiply the value of this `Ref`.
93 | * @throws IllegalStateException if `txn` is not active.
94 | */
95 | def *= (rhs: A)(implicit txn: Context, num: Numeric[A]): Unit = transform { v => num.times(v, rhs) }
96 |
97 | /** Transforms the value stored the `Ref` by performing a division on it,
98 | * throwing away the remainder if division is not exact for instances of
99 | * type `A`. The careful reader will note that division is actually
100 | * provided by `Fractional[A]` or `Integral[A]`, it is not defined on
101 | * `Numeric[A]`. To avoid compile-time ambiguity this method accepts a
102 | * `Numeric[A]` and assumes that it can be converted at runtime into
103 | * either a `Fractional[A]` or an `Integral[A]`.
104 | *
105 | * '''Note: Implementations may choose to ignore the provided `Numeric[A]`
106 | * instance if `A` is a primitive type.'''
107 | *
108 | * @param rhs the quantity by which to divide the value of this `Ref`.
109 | * @throws IllegalStateException if `txn` is not active.
110 | */
111 | def /= (rhs: A)(implicit txn: Context, num: Numeric[A]): Unit =
112 | num match {
113 | //case numF: Fractional[A] => transform { v => numF.div(v, rhs) }
114 | case numF: Fractional[_] => transform { v => numF.asInstanceOf[Fractional[A]].div(v, rhs) }
115 | //case numI: Integral[A] => transform { v => numI.quot(v, rhs) }
116 | case numI: Integral[_] => transform { v => numI.asInstanceOf[Integral[A]].quot(v, rhs) }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/Sink.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | object Sink {
6 |
7 | /** `Sink.View[+A]` consists of the contra-variant write-only operations of
8 | * `Ref.View[A]`.
9 | */
10 | trait View[-A] {
11 |
12 | /** Returns a `Sink` that accesses the same memory location as this view.
13 | * The returned `Sink` might be the original reference that was used to
14 | * construct this view, or it might be a `Sink` that is equivalent
15 | * (and `==`) to the original.
16 | * @return a `Sink` that accesses the same memory location as this view.
17 | */
18 | def ref: Sink[A]
19 |
20 | /** Performs an atomic write of the value in `ref`. If an atomic block
21 | * is active (see `Txn.findCurrent`) then the write will be performed
22 | * as part of the transaction, otherwise it will act as if it was
23 | * performed inside a new atomic block. Equivalent to `set(v)`.
24 | */
25 | def update(v: A): Unit = set(v)
26 |
27 | /** Performs an atomic write; equivalent to `update(v)`. */
28 | def set(v: A): Unit
29 |
30 | /** Performs an atomic write and returns true, or returns false. The
31 | * STM implementation may choose to return false to reduce (not
32 | * necessarily avoid) blocking. If no other threads are performing any
33 | * transactional or atomic accesses then this method will succeed.
34 | */
35 | def trySet(v: A): Boolean
36 | }
37 | }
38 |
39 | /** `Sink[+A]` consists of the contra-variant write-only operations of
40 | * `Ref[A]`.
41 | *
42 | * @author Nathan Bronson
43 | */
44 | trait Sink[-A] extends SinkLike[A, InTxn] {
45 |
46 | /** See `Ref.single`. */
47 | def single: Sink.View[A]
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/SinkLike.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 |
6 | /** Provides all of the operations of a `Sink[A]`, without the ability to get
7 | * a `Sink.View`.
8 | *
9 | * @author Nathan Bronson
10 | */
11 | trait SinkLike[-A, Context] {
12 |
13 | /** Performs a transactional write. The new value will not be visible by
14 | * any other threads until (and unless) `txn` successfully commits.
15 | * Equivalent to `set(v)`.
16 | *
17 | * Example: {{{
18 | * val x = Ref(0)
19 | * atomic { implicit t =>
20 | * ...
21 | * x() = 10 // perform a write inside a transaction
22 | * ...
23 | * }
24 | * }}}
25 | * @param v a value to store in the `Ref`.
26 | * @throws IllegalStateException if `txn` is not active. */
27 | def update(v: A)(implicit txn: Context): Unit = set(v)
28 |
29 | /** Performs a transactional write. The new value will not be visible by
30 | * any other threads until (and unless) `txn` successfully commits.
31 | * Equivalent to `update(v)`.
32 | * @param v a value to store in the `Ref`.
33 | * @throws IllegalStateException if `txn` is not active.
34 | */
35 | def set(v: A)(implicit txn: Context): Unit
36 |
37 | /** Performs a transactional write and returns true, or returns false. The
38 | * STM implementation may choose to return false to reduce (not necessarily
39 | * avoid) blocking. If no other threads are performing any transactional or
40 | * atomic accesses then this method will succeed.
41 | */
42 | def trySet(v: A)(implicit txn: Context): Boolean
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/Source.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | import java.util.concurrent.TimeUnit
6 |
7 | object Source {
8 |
9 | /** `Source.View[+A]` consists of the covariant read-only operations of
10 | * `Ref.View[A]`.
11 | */
12 | trait View[+A] extends TxnDebuggable {
13 |
14 | /** Returns a `Source` that accesses the same memory location as this view.
15 | * The returned `Source` might be the original reference that was used to
16 | * construct this view, or it might be a `Source` that is equivalent
17 | * (and `==`) to the original.
18 | * @return a `Source` that accesses the same memory location as this view.
19 | */
20 | def ref: Source[A]
21 |
22 | /** Performs an atomic read of the value in `ref`. If an atomic block is
23 | * active (see `Txn.findCurrent`) then the read will be performed as part
24 | * of the transaction, otherwise it will act as if it was performed inside
25 | * a new atomic block. Equivalent to `get`.
26 | * @return the value of the `Ref` as observed by the current context.
27 | */
28 | def apply(): A = get
29 |
30 | /** Performs an atomic read; equivalent to `apply()`.
31 | * @return the value of the `Ref` as observed by the current context.
32 | */
33 | def get: A
34 |
35 | /** Acts like `ref.getWith(f)` if there is an active transaction, otherwise
36 | * just returns `f(get)`.
37 | * @param f an idempotent function.
38 | * @return the result of applying `f` to the value contained in `ref`.
39 | */
40 | def getWith[Z](f: A => Z): Z
41 |
42 | /** Acts like `ref.relaxedGet(equiv)` if there is an active transaction,
43 | * otherwise just returns `get`.
44 | * @param equiv an equivalence function that returns true if a transaction
45 | * that observed the first argument will still complete correctly,
46 | * where the second argument is the actual value that should have been
47 | * observed.
48 | * @return a value of the `Ref`, not necessary consistent with the rest of
49 | * the reads performed by the active transaction, if any.
50 | */
51 | def relaxedGet(equiv: (A, A) => Boolean): A
52 |
53 | /** Blocks until `f(get)` is true, in a manner consistent with the current
54 | * context. Requires that the predicate be safe to reevaluate, and that
55 | * `f(x) == f(y)` if `x == y`.
56 | *
57 | * `v.await(f)` is equivalent to {{{
58 | * atomic { implicit t =>
59 | * if (!f(v.get)) retry
60 | * }
61 | * }}}
62 | *
63 | * If you want to wait for a predicate that involves more than one `Ref`
64 | * then use `retry` directly.
65 | * @param f a predicate that is safe to evaluate multiple times.
66 | */
67 | def await(f: A => Boolean): Unit
68 |
69 | /** Blocks until `f(get)` is true and returns true, or returns false if
70 | * the condition does not become true within within the specified timeout.
71 | *
72 | * `v.tryAwait(timeout)(f)` is equivalent to {{{
73 | * atomic { implicit t =>
74 | * f(v.get) || { retryFor(timeout) ; false }
75 | * }
76 | * }}}
77 | *
78 | * @param f a predicate that is safe to evaluate multiple times.
79 | * @param timeout the maximum amount of time to wait, in units of `unit`.
80 | * @param unit the units in which the timeout is measured, defaulting to
81 | * milliseconds.
82 | * @return true if the predicate was satisfied, false if the wait timed
83 | * out.
84 | */
85 | def tryAwait(timeout: Long, unit: TimeUnit = TimeUnit.MILLISECONDS)(f: A => Boolean): Boolean
86 |
87 | def dbgStr: String = ref.dbgStr
88 | def dbgValue: Any = ref.dbgValue
89 | }
90 | }
91 |
92 | /** `Source[+A]` consists of the covariant read-only operations of `Ref[A]`. */
93 | trait Source[+A] extends SourceLike[A, InTxn] with TxnDebuggable {
94 |
95 | /** See `Ref.single`. */
96 | def single: Source.View[A]
97 |
98 | def dbgStr: String = atomic.unrecorded({ implicit txn => "Ref(" + get + ")" }, { _.toString })
99 | def dbgValue: Any = atomic.unrecorded({ get(_) }, { x => x })
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/SourceLike.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 |
6 | /** Provides all of the operations of a `Source[A]`, without the ability to get
7 | * a `Source.View`.
8 | *
9 | * @author Nathan Bronson
10 | */
11 | trait SourceLike[+A, Context] {
12 |
13 | /** Performs a transactional read and checks that it is consistent with all
14 | * reads already made by `txn`. Equivalent to `get`.
15 | *
16 | * Example: {{{
17 | * val x = Ref(0)
18 | * atomic { implicit t =>
19 | * ...
20 | * val v = x() // perform a read inside a transaction
21 | * ...
22 | * }
23 | * }}}
24 | * @param txn an active transaction.
25 | * @return the value of the `Ref` as observed by `txn`.
26 | * @throws IllegalStateException if `txn` is not active.
27 | */
28 | def apply()(implicit txn: Context): A = get
29 |
30 | /** Performs a transactional read and checks that it is consistent with all
31 | * reads already made by `txn`. Equivalent to `apply()`, which is more
32 | * concise in many situations.
33 | * @param txn an active transaction.
34 | * @return the value of the `Ref` as observed by `txn`.
35 | * @throws IllegalStateException if `txn` is not active.
36 | */
37 | def get(implicit txn: Context): A
38 |
39 | /** Returns `f(get)`, possibly reevaluating `f` to avoid rollback if a
40 | * conflicting change is made but the old and new values are equal after
41 | * application of `f`. Requires that `f(x) == f(y)` if `x == y`.
42 | *
43 | * `getWith(f)` is equivalent to `f(relaxedGet({ f(_) == f(_) }))`, although
44 | * perhaps more efficient.
45 | * @param f an idempotent function.
46 | * @return the result of applying `f` to the value contained in this `Ref`.
47 | */
48 | def getWith[Z](f: A => Z)(implicit txn: Context): Z
49 |
50 | /** Returns the same value as `get`, but allows the caller to determine
51 | * whether `txn` should be rolled back if another thread changes the value
52 | * of this `Ref` before `txn` is committed. If `ref.relaxedGet(equiv)`
53 | * returns `v0` in `txn`, another context changes `ref` to `v1`, and
54 | * `equiv(v0, v1) == true`, then `txn` won't be required to roll back (at
55 | * least not due to this read). If additional changes are made to `ref`
56 | * additional calls to the equivalence function will be made, always with
57 | * `v0` as the first parameter.
58 | *
59 | * `equiv` will always be invoked on the current thread. Extreme care
60 | * should be taken if the equivalence function accesses any `Ref`s.
61 | *
62 | * As an example, to perform a read that will not be validated during commit
63 | * you can use the maximally permissive equivalence function: {{{
64 | * val unvalidatedValue = ref.relaxedGet({ (_, _) => true })
65 | * }}}
66 | * To check view serializability rather than conflict serializability for a
67 | * read: {{{
68 | * val viewSerializableValue = ref.relaxedGet({ _ == _ })
69 | * }}}
70 | * The `getWith` method provides related functionality.
71 | * @param equiv an equivalence function that returns true if a transaction
72 | * that observed the first argument will still complete correctly,
73 | * where the second argument is the actual value that should have been
74 | * observed.
75 | * @return a value of the `Ref`, not necessary consistent with the rest of
76 | * the reads performed by `txn`.
77 | */
78 | def relaxedGet(equiv: (A, A) => Boolean)(implicit txn: Context): A
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/TArray.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | import scala.collection.{immutable, mutable}
6 | import scala.reflect.ClassTag
7 |
8 | object TArray {
9 |
10 | /** A view that supports accesses to a `TArray` instance outside the static
11 | * scope of a `Txn`. `TArray.View` is to `TArray` as `Ref.View` is to
12 | * `Ref`.
13 | */
14 | trait View[A] extends mutable.IndexedSeq[A] with TxnDebuggable {
15 | /** The `TArray` from which this view was created. */
16 | def tarray: TArray[A]
17 |
18 | def length: Int
19 |
20 | /** Performs an atomic read of the `index`th element of `array`. If an
21 | * atomic block is active (see `Txn.findCurrent`) then the read will be
22 | * performed as part of the transaction, otherwise it will act as if it
23 | * was performed inside a new atomic block.
24 | */
25 | def apply(index: Int): A
26 |
27 | /** Performs an atomic write of the `index`th element of `array`. If an
28 | * atomic block is active (see `Txn.findCurrent`) then the write will be
29 | * performed as part of the transaction, otherwise it will act as if it
30 | * was performed inside a new atomic block.
31 | */
32 | def update(index: Int, v: A): Unit
33 |
34 | /** Returns a sequence of `Ref.View` that are backed by the elements of
35 | * `array`. All operations on the contained `Ref.View`s are supported.
36 | */
37 | def refViews: immutable.IndexedSeq[Ref.View[A]]
38 | }
39 |
40 | //////////////// factory methods
41 |
42 | // We don't include apply(xs: A*) because it is surprising when
43 | // TArray[Int](1000) creates a TArray of length 1.
44 |
45 | /** Returns a new `TArray[A]` containing `length` copies of the default value
46 | * for elements of type `A`.
47 | */
48 | def ofDim[A : ClassTag](length: Int): TArray[A] = impl.STMImpl.instance.newTArray[A](length)
49 |
50 | /** Returns a new `TArray[A]` containing the elements of `data`. */
51 | def apply[A : ClassTag](data: TraversableOnce[A]): TArray[A] = impl.STMImpl.instance.newTArray[A](data)
52 | }
53 |
54 | /** Bulk transactional storage, roughly equivalent to `Array[Ref[T]]` but
55 | * potentially much more space efficient. Elements can be read and written
56 | * directly, or the `refs` method can be used to obtain transient `Ref`
57 | * instances backed by the elements of the `TArray`.
58 | *
59 | * @author Nathan Bronson
60 | */
61 | trait TArray[A] extends TxnDebuggable {
62 |
63 | /** Returns the length of this `TArray`, which does not change. */
64 | def length: Int
65 |
66 | /** Performs a transactional read of the `index`th element of this
67 | * transactional array. Equivalent to `refs(index).get`.
68 | */
69 | def apply(index: Int)(implicit txn: InTxn): A
70 |
71 | /** Performs a transactional write to the `index`th element of this
72 | * transactional array. Equivalent to `refs(index).set(v)`.
73 | */
74 | def update(index: Int, v: A)(implicit txn: InTxn): Unit
75 |
76 | /** Returns a `TArray.View` that allows access to the contents of this
77 | * `TArray` without requiring that an `InTxn` be available. See `Ref.View`.
78 | */
79 | def single: TArray.View[A]
80 |
81 | /** Returns a sequence of `Ref` instances that are backed by elements of this
82 | * `TArray`. All operations on the contained `Ref`s are supported.
83 | *
84 | * As an example, the following code tests whether `a(i)` is greater than
85 | * 10 without requiring the transaction to roll back for all writes to
86 | * `a(i)`: {{{
87 | * atomic { implicit t =>
88 | * if (a.refs(i).getWith( _ > 10 )) {
89 | * ... lots of stuff
90 | * }
91 | * }
92 | * }}}
93 | */
94 | def refs: immutable.IndexedSeq[Ref[A]]
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/TMap.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | import scala.collection.{generic, immutable, mutable}
6 | import scala.language.implicitConversions
7 |
8 | object TMap {
9 |
10 | object View extends generic.MutableMapFactory[View] {
11 |
12 | implicit def canBuildFrom[A, B]: generic.CanBuildFrom[Coll, (A, B), View[A, B]] = new MapCanBuildFrom[A, B]
13 |
14 | def empty[A, B]: View[A, B] = TMap.empty[A, B].single
15 |
16 | override def newBuilder[A, B]: mutable.Builder[(A, B), View[A, B]] = new mutable.Builder[(A, B), View[A, B]] {
17 | private val underlying = TMap.newBuilder[A, B]
18 |
19 | def clear(): Unit = underlying.clear()
20 | def += (kv: (A, B)): this.type = { underlying += kv ; this }
21 | def result(): View[A, B] = underlying.result().single
22 | }
23 |
24 | override def apply[A, B](kvs: (A, B)*): TMap.View[A, B] = (TMap.newBuilder[A, B] ++= kvs).result().single
25 | }
26 |
27 | /** A `Map` that provides atomic execution of all of its methods. */
28 | trait View[A, B] extends mutable.Map[A, B] with mutable.MapLike[A, B, View[A, B]] with TxnDebuggable {
29 | /** Returns the `TMap` perspective on this transactional map, which
30 | * provides map functionality only inside atomic blocks.
31 | */
32 | def tmap: TMap[A, B]
33 |
34 | def clone: TMap.View[A, B]
35 |
36 | /** Takes an atomic snapshot of this transactional map. */
37 | def snapshot: immutable.Map[A, B]
38 |
39 | override def empty: View[A, B] = TMap.empty[A, B].single
40 |
41 | override protected[this] def newBuilder: mutable.Builder[(A, B), View[A, B]] = View.newBuilder[A, B]
42 |
43 | override def stringPrefix = "TMap"
44 | }
45 |
46 |
47 | /** Constructs and returns a new empty `TMap`. */
48 | def empty[A, B]: TMap[A, B] = impl.STMImpl.instance.newTMap[A, B]
49 |
50 | /** Returns a builder of `TMap`. */
51 | def newBuilder[A, B]: mutable.Builder[(A, B), TMap[A, B]] = impl.STMImpl.instance.newTMapBuilder[A, B]
52 |
53 | /** Constructs and returns a new `TMap` that will contain the key/value pairs
54 | * from `kvs`.
55 | */
56 | def apply[A, B](kvs: (A, B)*): TMap[A, B] = (newBuilder[A, B] ++= kvs).result()
57 |
58 |
59 | /** Allows a `TMap` in a transactional context to be used as a `Map`. */
60 | implicit def asMap[A, B](m: TMap[A, B])(implicit txn: InTxn): View[A, B] = m.single
61 | }
62 |
63 |
64 | /** A transactional map implementation that requires that all of its map-like
65 | * operations be called from inside an atomic block. Rather than extending
66 | * `Map`, an implicit conversion is provided from `TMap` to `Map` if the
67 | * current scope is part of an atomic block (see `TMap.asMap`).
68 | *
69 | * The keys (with type `A`) must be immutable, or at least not modified while
70 | * they are in the map. The `TMap` implementation assumes that it can safely
71 | * perform key equality and hash checks outside a transaction without
72 | * affecting atomicity.
73 | *
74 | * @author Nathan Bronson
75 | */
76 | trait TMap[A, B] extends TxnDebuggable {
77 |
78 | /** Returns an instance that provides transactional map functionality without
79 | * requiring that operations be performed inside the static scope of an
80 | * atomic block.
81 | */
82 | def single: TMap.View[A, B]
83 |
84 | def clone(implicit txn: InTxn): TMap[A, B] = single.clone.tmap
85 |
86 | // The following method work fine via the asMap mechanism, but is important
87 | // enough that we don't want the implicit conversion to make it invisible to
88 | // ScalaDoc or IDE auto-completion
89 |
90 | def snapshot: immutable.Map[A, B] = single.snapshot
91 |
92 | // The following methods work fine via the asMap mechanism, but are heavily
93 | // used. We add transactional versions of them to allow overrides to get
94 | // access to the InTxn instance without a ThreadLocal lookup.
95 |
96 | def isEmpty(implicit txn: InTxn): Boolean
97 | def size(implicit txn: InTxn): Int
98 | def foreach[U](f: ((A, B)) => U)(implicit txn: InTxn): Unit
99 | def contains(key: A)(implicit txn: InTxn): Boolean
100 | def apply(key: A)(implicit txn: InTxn): B
101 | def get(key: A)(implicit txn: InTxn): Option[B]
102 | def update(key: A, value: B)(implicit txn: InTxn): Unit = put(key, value)
103 | def put(key: A, value: B)(implicit txn: InTxn): Option[B]
104 | def remove(key: A)(implicit txn: InTxn): Option[B]
105 |
106 | // The following methods return the wrong receiver when invoked via the asMap
107 | // conversion. They are exactly the methods of mutable.Map whose return type
108 | // is this.type. Note that there are other methods of mutable.Map that we
109 | // allow to use the implicit mechanism, such as getOrElseUpdate(k).
110 |
111 | def += (kv: (A, B))(implicit txn: InTxn): this.type = { put(kv._1, kv._2) ; this }
112 | def += (kv1: (A, B), kv2: (A, B), kvs: (A, B)*)(implicit txn: InTxn): this.type = { this += kv1 += kv2 ++= kvs }
113 | def ++= (kvs: TraversableOnce[(A, B)])(implicit txn: InTxn): this.type = { for (kv <- kvs) this += kv ; this }
114 | def -= (k: A)(implicit txn: InTxn): this.type = { remove(k) ; this }
115 | def -= (k1: A, k2: A, ks: A*)(implicit txn: InTxn): this.type = { this -= k1 -= k2 --= ks }
116 | def --= (ks: TraversableOnce[A])(implicit txn: InTxn): this.type = { for (k <- ks) this -= k ; this }
117 |
118 | def transform(f: (A, B) => B)(implicit txn: InTxn): this.type
119 | def retain(p: (A, B) => Boolean)(implicit txn: InTxn): this.type
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/TSet.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | import scala.collection.{generic, immutable, mutable}
6 | import scala.language.implicitConversions
7 |
8 | object TSet {
9 |
10 | object View extends generic.MutableSetFactory[View] {
11 |
12 | implicit def canBuildFrom[A]: generic.CanBuildFrom[Coll, A, View[A]] = setCanBuildFrom[A]
13 |
14 | override def empty[A]: View[A] = TSet.empty[A].single
15 |
16 | override def newBuilder[A]: mutable.Builder[A, View[A]] = new mutable.Builder[A, View[A]] {
17 | private val underlying = TSet.newBuilder[A]
18 |
19 | def clear(): Unit = underlying.clear()
20 | def += (x: A): this.type = { underlying += x ; this }
21 | def result(): View[A] = underlying.result().single
22 | }
23 |
24 | override def apply[A](xs: A*): TSet.View[A] = (TSet.newBuilder[A] ++= xs).result().single
25 | }
26 |
27 | /** A `Set` that provides atomic execution of all of its methods. */
28 | trait View[A] extends mutable.Set[A] with mutable.SetLike[A, View[A]] with TxnDebuggable {
29 |
30 | /** Returns the `TSet` perspective on this transactional set, which
31 | * provides set functionality only inside atomic blocks.
32 | */
33 | def tset: TSet[A]
34 |
35 | def clone: TSet.View[A]
36 |
37 | /** Takes an atomic snapshot of this transactional set. */
38 | def snapshot: immutable.Set[A]
39 |
40 | override def empty: View[A] = TSet.empty[A].single
41 | override def companion: generic.GenericCompanion[View] = View
42 | override protected[this] def newBuilder: mutable.Builder[A, View[A]] = View.newBuilder[A]
43 |
44 | override def stringPrefix = "TSet"
45 | }
46 |
47 |
48 | /** Constructs and returns a new empty `TSet`. */
49 | def empty[A]: TSet[A] = impl.STMImpl.instance.newTSet[A]
50 |
51 | /** Returns a builder of `TSet`. */
52 | def newBuilder[A]: mutable.Builder[A, TSet[A]] = impl.STMImpl.instance.newTSetBuilder[A]
53 |
54 | /** Constructs and returns a new `TSet` that will contain the elements from
55 | * `xs`.
56 | */
57 | def apply[A](xs: A*): TSet[A] = (newBuilder[A] ++= xs).result()
58 |
59 |
60 | /** Allows a `TSet` in a transactional context to be used as a `Set`. */
61 | implicit def asSet[A](s: TSet[A])(implicit txn: InTxn): View[A] = s.single
62 | }
63 |
64 |
65 | /** A transactional set implementation that requires that all of its set-like
66 | * operations be called from inside an atomic block. Rather than extending
67 | * `Set`, an implicit conversion is provided from `TSet` to `Set` if the
68 | * current scope is part of an atomic block (see `TSet.asSet`).
69 | *
70 | * The elements (with type `A`) must be immutable, or at least not modified
71 | * while they are in the set. The `TSet` implementation assumes that it can
72 | * safely perform equality and hash checks outside a transaction without
73 | * affecting atomicity.
74 | *
75 | * @author Nathan Bronson
76 | */
77 | trait TSet[A] extends TxnDebuggable {
78 |
79 | /** Returns an instance that provides transactional set functionality without
80 | * requiring that operations be performed inside the static scope of an
81 | * atomic block.
82 | */
83 | def single: TSet.View[A]
84 |
85 | def clone(implicit txn: InTxn): TSet[A] = single.clone.tset
86 |
87 | // Fast snapshots are one of TSet's core features, so we don't want the
88 | // implicit conversion to hide it from ScalaDoc and IDE completion
89 | def snapshot: immutable.Set[A] = single.snapshot
90 |
91 | // The following methods work fine via the asSet mechanism, but are heavily
92 | // used. We add transactional versions of them to allow overrides.
93 |
94 | def isEmpty(implicit txn: InTxn): Boolean
95 | def size(implicit txn: InTxn): Int
96 | def foreach[U](f: A => U)(implicit txn: InTxn): Unit
97 | def contains(elem: A)(implicit txn: InTxn): Boolean
98 | def apply(elem: A)(implicit txn: InTxn): Boolean = contains(elem)
99 | def add(elem: A)(implicit txn: InTxn): Boolean
100 | def update(elem: A, included: Boolean)(implicit txn: InTxn): Unit = if (included) add(elem) else remove(elem)
101 | def remove(elem: A)(implicit txn: InTxn): Boolean
102 |
103 | // The following methods return the wrong receiver when invoked via the asSet
104 | // conversion. They are exactly the methods of mutable.Set whose return type
105 | // is this.type.
106 |
107 | def += (x: A)(implicit txn: InTxn): this.type = { add(x) ; this }
108 | def += (x1: A, x2: A, xs: A*)(implicit txn: InTxn): this.type = { this += x1 += x2 ++= xs }
109 | def ++= (xs: TraversableOnce[A])(implicit txn: InTxn): this.type = { for (x <- xs) this += x ; this }
110 | def -= (x: A)(implicit txn: InTxn): this.type = { remove(x) ; this }
111 | def -= (x1: A, x2: A, xs: A*)(implicit txn: InTxn): this.type = { this -= x1 -= x2 --= xs }
112 | def --= (xs: TraversableOnce[A])(implicit txn: InTxn): this.type = { for (x <- xs) this -= x ; this }
113 |
114 | def retain(p: A => Boolean)(implicit txn: InTxn): this.type
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/TxnDebuggable.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | /** This trait implements methods that can be used to examine the content of
6 | * transactional data structures in a debugger with minimal modification to
7 | * the behavior of the program. Normal transactional reads would add to an
8 | * atomic block's read set, which could reduce the number of valid program
9 | * execution orders. `dbgStr` and `dbgValue` perform transactional reads,
10 | * but then erase them from the enclosing transaction (if any).
11 | *
12 | * You can use these methods from an IDE debugger manually, by watching
13 | * `x.dbgStr` or `x.dbgValue` rather than `x`.
14 | *
15 | * If you use Eclipse, you can make this method the default view by going to
16 | * ''Window->Preferences->Java[+]->Debug[+]->Detail Formatters'' and
17 | * entering the code snippet `dbgStr()` (or `dbgValue()`) for instances of
18 | * `scala.concurrent.stm.TxnDebuggable`.
19 | *
20 | * If you use IntelliJ IDEA, go to
21 | * ''File->Settings...->Debugger->Data Type Renderers'' and create a new
22 | * renderer for `scala.concurrent.stm.TxnDebuggable` that uses
23 | * `dbgStr()` for rendering and `dbgValue()` for node expansion.
24 | *
25 | * @author Nathan Bronson
26 | */
27 | trait TxnDebuggable {
28 |
29 | /** Returns a string representation of the transactional value in this
30 | * instance for debugging convenience. The `Ref` reads (and writes)
31 | * performed while constructing the result will be discarded before
32 | * returning. This method works fine outside a transaction.
33 | *
34 | * If this method is called from within a transaction that is already
35 | * doomed (status `Txn.Rolledback`), a string describing the reason
36 | * for the outer transaction's rollback will be returned.
37 | */
38 | def dbgStr: String
39 |
40 | /** Returns some value that is suitable for examination in a debugger,
41 | * or returns a `Txn.RollbackCause` if called from inside a doomed atomic
42 | * block.
43 | */
44 | def dbgValue: Any
45 |
46 | /** Helper function for generating `dbgStr` of a transactional type that
47 | * can produce an iterable view.
48 | */
49 | private[stm] def mkStringPrefix(typeName: String, values: Iterable[_], unabbrevLen: Int = 1000): String = {
50 | val buf = new StringBuilder(typeName + "[size=" + values.size + "](")
51 | val iter = values.iterator
52 | while (iter.hasNext && buf.length < unabbrevLen - 1) {
53 | buf.append(iter.next())
54 | if (iter.hasNext)
55 | buf.append(", ")
56 | }
57 | if (iter.hasNext)
58 | buf.append("...")
59 | buf.append(")")
60 | buf.toString()
61 | }
62 | }
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/TxnLocal.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 |
6 | object TxnLocal {
7 | /** Returns a new transaction-local that holds values of type `A`. One
8 | * `TxnLocal` instance holds a separate value for each transaction in which
9 | * it has been accessed. The value associated with a particular atomic
10 | * block is created on demand, and discarded at the same time that the
11 | * atomic block's after-completion handlers are invoked. `TxnLocal` has a
12 | * similar relationship to transactions as `ThreadLocal` has to threads.
13 | *
14 | * There are two ways to specify the initial value that will be used if the
15 | * first access inside a transaction is not a `set`. If no `InTxn` context
16 | * is needed to compute the initial value then the by-name parameter `init`
17 | * is the most convenient. Because this is the first parameter, you can
18 | * omit the parameter name. To construct a `TxnLocal` with a default value
19 | * of `aValue`, simply {{{
20 | * val tl = TxnLocal(aValue)
21 | * }}}
22 | * If computing the initial value requires access to `Ref`s, then it is
23 | * better to use the `initialValue` parameter, which lets you write {{{
24 | * val tl = TxnLocal(initialValue = { implicit txn =>
25 | * // Ref reads or writes, or handler registration
26 | * })
27 | * }}}
28 | *
29 | * Unlike `Ref`s, `TxnLocal`s can be read or written from inside
30 | * while-preparing or while-committing callbacks, with two conditions: if
31 | * the first access is from one of these callbacks then no `beforeCommit`
32 | * parameter can be present; and if the first access is from one of these
33 | * callbacks and it is not a write then you must use the `init`
34 | * initialization method.
35 | *
36 | * This factory method also accepts parameters that correspond to `Txn`'s
37 | * transaction life-cycle handlers. These handlers will be registered in any
38 | * transaction that reads or writes the returned `TxnLocal`. They are
39 | * roughly
40 | * - `beforeCommit` - the last time that `Ref`s can be read or written;
41 | * - `whilePreparing` - the last time that the transaction can be rolled
42 | * back;
43 | * - `whileCommitting` - actions that should be atomic with respect to the
44 | * transaction (keep them fast to avoid scalability issues);
45 | * - `afterCommit` - called at some time after commit;
46 | * - `afterRollback` - called at some time after rollback of the nesting
47 | * level in which the `TxnLocal` was first accessed; and
48 | * - `afterCompletion` - called either after commit or after rollback.
49 | *
50 | * The value stored in a `TxnLocal` is subject to partial rollback: initial
51 | * value computations and writes from a nested atomic block will be
52 | * discarded if the block is rolled back.
53 | */
54 | def apply[A](init: => A = null.asInstanceOf[A],
55 | initialValue: InTxn => A = null,
56 | beforeCommit: InTxn => Unit = null,
57 | whilePreparing: InTxnEnd => Unit = null,
58 | whileCommitting: InTxnEnd => Unit = null,
59 | afterCommit: A => Unit = null,
60 | afterRollback: Txn.Status => Unit = null,
61 | afterCompletion: Txn.Status => Unit = null): TxnLocal[A] = {
62 | impl.STMImpl.instance.newTxnLocal(
63 | init, initialValue, beforeCommit, whilePreparing, whileCommitting, afterCommit, afterRollback, afterCompletion)
64 | }
65 | }
66 |
67 | /** `TxnLocal[A]` holds an instance of `A` that is local to an atomic block.
68 | * See the factory method in the companion object for information about the
69 | * life-cycle.
70 | *
71 | * @author Nathan Bronson
72 | */
73 | trait TxnLocal[A] extends RefLike[A, InTxnEnd] {
74 |
75 | /** Returns true if a value is already associated with this `TxnLocal` in the
76 | * current transactional context, false otherwise.
77 | */
78 | def isInitialized(implicit txn: InTxnEnd): Boolean
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/TxnUnknown.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | /** An object that represents the absence of a statically-bound current
6 | * transaction.
7 | * @see scala.concurrent.stm.MaybeTxn
8 | *
9 | * @author Nathan Bronson
10 | */
11 | object TxnUnknown extends MaybeTxn
12 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/ccstm/CCSTMExecutor.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package ccstm
5 |
6 | import scala.util.control.ControlThrowable
7 |
8 | private[ccstm] object CCSTMExecutor {
9 | val DefaultControlFlowTest: Throwable => Boolean = _.isInstanceOf[ControlThrowable]
10 |
11 | val DefaultPostDecisionFailureHandler: (Txn.Status, Throwable) => Unit = { (status, x) =>
12 | new Exception("status=" + status, x).printStackTrace()
13 | }
14 | }
15 |
16 | private[ccstm] case class CCSTMExecutor private (
17 | retryTimeoutNanos: Option[Long],
18 | controlFlowTest: Throwable => Boolean,
19 | postDecisionFailureHandler: (Txn.Status, Throwable) => Unit
20 | ) extends TxnExecutor {
21 |
22 | def this() = this(None, CCSTMExecutor.DefaultControlFlowTest, CCSTMExecutor.DefaultPostDecisionFailureHandler)
23 |
24 | def apply[Z](block: InTxn => Z)(implicit mt: MaybeTxn): Z = InTxnImpl().atomic(this, block)
25 |
26 | def oneOf[Z](blocks: (InTxn => Z)*)(implicit mt: MaybeTxn): Z = InTxnImpl().atomicOneOf(this, blocks)
27 |
28 | def unrecorded[Z](block: InTxn => Z, outerFailure: Txn.RollbackCause => Z)(implicit mt: MaybeTxn): Z =
29 | InTxnImpl().unrecorded(this, block, outerFailure)
30 |
31 | def pushAlternative[Z](mt: MaybeTxn, block: (InTxn) => Z): Boolean = InTxnImpl().pushAlternative(block)
32 |
33 | def compareAndSet[A, B](a: Ref[A], a0: A, a1: A, b: Ref[B], b0: B, b1: B): Boolean = {
34 | val ah = a.asInstanceOf[Handle.Provider[A]].handle
35 | val bh = b.asInstanceOf[Handle.Provider[B]].handle
36 | InTxnImpl.dynCurrentOrNull match {
37 | case null => NonTxn.transform2[A, B, Boolean](ah, bh, { (av, bv) => if (a0 == av && b0 == bv) (a1, b1, true) else (av, bv, false) })
38 | case txn => a0 == txn.get(ah) && b0 == txn.get(bh) && { txn.set(ah, a1) ; txn.set(bh, b1) ; true }
39 | }
40 | }
41 |
42 | def compareAndSetIdentity[A <: AnyRef, B <: AnyRef](a: Ref[A], a0: A, a1: A, b: Ref[B], b0: B, b1: B): Boolean = {
43 | val aRead = a0 eq a1
44 | val bRead = b0 eq b1
45 | if (aRead && bRead)
46 | cci(a, a0, b, b0)
47 | else if (aRead)
48 | ccasi(a, a0, b, b0, b1)
49 | else if (bRead)
50 | ccasi(b, b0, a, a0, a1)
51 | else
52 | dcasi(a, a0, a1, b, b0, b1)
53 | }
54 |
55 | private def cci[A <: AnyRef, B <: AnyRef](a: Ref[A], a0: A, b: Ref[B], b0: B): Boolean = {
56 | val ah = a.asInstanceOf[Handle.Provider[A]].handle
57 | val bh = b.asInstanceOf[Handle.Provider[B]].handle
58 | InTxnImpl.dynCurrentOrNull match {
59 | case null => NonTxn.cci(ah, a0, bh, b0)
60 | case txn => (a0 eq txn.get(ah)) && (b0 eq txn.get(bh))
61 | }
62 | }
63 |
64 | private def ccasi[A <: AnyRef, B <: AnyRef](a: Ref[A], a0: A, b: Ref[B], b0: B, b1: B): Boolean = {
65 | val ah = a.asInstanceOf[Handle.Provider[A]].handle
66 | val bh = b.asInstanceOf[Handle.Provider[B]].handle
67 | InTxnImpl.dynCurrentOrNull match {
68 | case null => NonTxn.ccasi(ah, a0, bh, b0, b1)
69 | case txn => (a0 eq txn.get(ah)) && (b0 eq txn.get(bh)) && { txn.set(bh, b1) ; true }
70 | }
71 | }
72 |
73 | private def dcasi[A <: AnyRef, B <: AnyRef](a: Ref[A], a0: A, a1: A, b: Ref[B], b0: B, b1: B): Boolean = {
74 | val ah = a.asInstanceOf[Handle.Provider[A]].handle
75 | val bh = b.asInstanceOf[Handle.Provider[B]].handle
76 | InTxnImpl.dynCurrentOrNull match {
77 | case null => NonTxn.transform2[A, B, Boolean](ah, bh, { (av, bv) => if ((a0 eq av) && (b0 eq bv)) (a1, b1, true) else (av, bv, false) })
78 | case txn => (a0 eq txn.get(ah)) && (b0 eq txn.get(bh)) && { txn.set(ah, a1) ; txn.set(bh, b1) ; true }
79 | }
80 | }
81 |
82 | def withRetryTimeoutNanos(timeout: Option[Long]): TxnExecutor = copy(retryTimeoutNanos = timeout)
83 |
84 | def isControlFlow(x: Throwable): Boolean = controlFlowTest(x)
85 |
86 | def withControlFlowRecognizer(pf: PartialFunction[Throwable, Boolean]): TxnExecutor = {
87 | copy(controlFlowTest = { x: Throwable => if (pf.isDefinedAt(x)) pf(x) else controlFlowTest(x) })
88 | }
89 |
90 | def withPostDecisionFailureHandler(handler: (Txn.Status, Throwable) => Unit): TxnExecutor = {
91 | copy(postDecisionFailureHandler = handler)
92 | }
93 |
94 | override def toString: String = {
95 | "CCSTMExecutor@" + hashCode.toHexString +
96 | "(retryTimeoutNanos=" + retryTimeoutNanos +
97 | ", controlFlowTest=" +
98 | (if (controlFlowTest eq CCSTMExecutor.DefaultControlFlowTest) "default" else controlFlowTest) +
99 | ", postDecisionFailureHandler=" +
100 | (if (postDecisionFailureHandler eq CCSTMExecutor.DefaultPostDecisionFailureHandler) "default" else postDecisionFailureHandler) +
101 | ")"
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/ccstm/Counter.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm.ccstm
4 |
5 | import java.util.concurrent.atomic.AtomicLong
6 | import scala.annotation.tailrec
7 |
8 | /** A counter with a linearizable increment operator and adaptive contention
9 | * avoidance. Reading the counter with `apply()` is not linearizable (unless
10 | * the only delta passed to += is 1) and is not optimized.
11 | */
12 | private[ccstm] class Counter extends {
13 | private final val MaxStripes = 64
14 |
15 | // this doesn't need to be volatile because when we grow it we retain all of
16 | // the old AtomicLong-s
17 | private var _stripes = Array(new AtomicLong)
18 |
19 | private def grow(): Unit =
20 | synchronized {
21 | if (_stripes.length < MaxStripes) {
22 | val repl = new Array[AtomicLong](_stripes.length * 2)
23 | System.arraycopy(_stripes, 0, repl, 0, _stripes.length)
24 | var i = _stripes.length
25 | while (i < repl.length) {
26 | repl(i) = new AtomicLong
27 | i += 1
28 | }
29 | _stripes = repl
30 | }
31 | }
32 |
33 | def += (delta: Int): Unit =
34 | if (delta != 0) {
35 | incr(delta)
36 | }
37 |
38 | @tailrec private def incr(delta: Int): Unit = {
39 | val s = _stripes
40 | val i = CCSTM.hash(Thread.currentThread) & (s.length - 1)
41 | val prev = s(i).get
42 | if (!s(i).compareAndSet(prev, prev + delta)) {
43 | grow()
44 | incr(delta)
45 | }
46 | }
47 |
48 | def apply(): Long = _stripes.foldLeft(0L)( _ + _.get )
49 |
50 | override def toString: String = apply().toString
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/ccstm/GV6.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package ccstm
5 |
6 | import java.util.concurrent.atomic.AtomicLong
7 |
8 |
9 | private[ccstm] trait GV6 {
10 |
11 | /** The global timestamp. We use TL2's GV6 scheme to avoid the need to
12 | * increment this during every transactional commit. Non-transactional
13 | * writes are even more conservative, incrementing the global version only
14 | * when it lags the local version by a (configurable) fixed amount. This
15 | * helps reduce contention (especially when there are many non-transactional
16 | * writes), but it means we must always validate transactions that are not
17 | * read-only. To help reduce the spurious revalidations caused by GV6, we
18 | * adaptively adjust the ratio of silent commits. Spurious revalidations
19 | * can also be triggered by silent non-txn run ahead, so we use bit 0 of the
20 | * version to differentiate the type of the most recent write, 0 for non-txn
21 | * and 1 for txn.
22 | */
23 | val globalVersion = new AtomicLong(1)
24 |
25 | private val silentCommitRatioMin = System.getProperty("ccstm.gv6.min", "1").toInt
26 | private val silentCommitRatioMax =
27 | System.getProperty("ccstm.gv6.max", (Runtime.getRuntime.availableProcessors min 16).toString).toInt
28 |
29 | /** The approximate ratio of the number of commits to the number of
30 | * increments of `globalVersion`, as in TL2's GV6 scheme. If
31 | * greater than one, the actual choice to advance or not is made with a
32 | * random number generator.
33 | */
34 | private var silentCommitRatio =
35 | (Runtime.getRuntime.availableProcessors / 2) max silentCommitRatioMin min silentCommitRatioMax
36 |
37 | private var silentCommitCutoff = intRangeFraction(silentCommitRatio)
38 |
39 | private val silentCommitRand = skel.SimpleRandom
40 |
41 | /** The maximum value of `nonTxnWriteVersion - globalVersion` that
42 | * will be allowed before a non-transactional store attempts to increase
43 | * `globalVersion`. Any value larger than zero admits the
44 | * possibility that a non-transactional write will leave a version number
45 | * that forces revalidation of a transaction that discovers it (like a
46 | * silently-committed txn under GV6). Larger values can help amortize the
47 | * cost of updating the counter. This is double the "ccstm.nontxn.runahead"
48 | * property because we reserve bit 0 to record the txn state of the last
49 | * writer.
50 | */
51 | private val nonTxnSilentRunAhead = 2 * System.getProperty("ccstm.nontxn.runahead", "32").toInt
52 |
53 | /** If `x` is a signed integer evenly chosen from a uniform distribution
54 | * between Integer.MIN_VALUE and Integer.MAX_VALUE, then the test
55 | * `(x <= intRangeFraction(r))` will succeed `1.0 / r` of the time.
56 | */
57 | private def intRangeFraction(r: Int) = ((1 << 31) + ((1L << 32) / r) - 1).asInstanceOf[Int]
58 |
59 | /** Returns a value that is greater than `prevVersion` and greater
60 | * than the value of `globalVersion` on entry. May increase
61 | * `globalVersion`.
62 | */
63 | def nonTxnWriteVersion(prevVersion: Long): Long = {
64 | val g = globalVersion.get
65 | val result = (math.max(g, prevVersion) + 2) & ~1L
66 | if (result > g + nonTxnSilentRunAhead) {
67 | globalVersion.compareAndSet(g, prevVersion + 1)
68 | }
69 | result
70 | }
71 |
72 | /** Returns a version to use for reading in a new transaction. */
73 | def freshReadVersion: Long = globalVersion.get
74 |
75 | /** Guarantees that `globalVersion.get` is ≥
76 | * `minRV`, and returns `globalVersion.get`. This form of
77 | * `freshReadVersion` is used when the read version of a transaction needs
78 | * to be advanced by revalidating.
79 | */
80 | def freshReadVersion(minRV: Long): Long = {
81 | // A call to this method corresponds to an mid-txn revalidation, which is
82 | // relatively costly. If minRV is odd then the revalidation is due to a
83 | // silent commit, so consider adjusting the silent commit ratio. We only
84 | // do this 1/64 of the time, though.
85 | if ((minRV & 127) == 1) {
86 | reduceRevalidateProbability()
87 | }
88 |
89 | (while (true) {
90 | val g = globalVersion.get
91 | if (g >= minRV) {
92 | return g
93 | }
94 | if (globalVersion.compareAndSet(g, minRV)) {
95 | return minRV
96 | }
97 | }).asInstanceOf[Nothing]
98 | }
99 |
100 | /** Returns a value that is greater than `globalVersion.get` and greater than
101 | * `readVersion`, possibly increasing `globalVersion`.
102 | */
103 | def freshCommitVersion(readVersion: Long): Long = {
104 | val cutoff = silentCommitCutoff
105 | val install = cutoff == Int.MaxValue || silentCommitRand.nextInt <= cutoff
106 |
107 | val gvSnap = globalVersion.get
108 | val result = (math.max(readVersion, gvSnap) + 1) | 1L
109 |
110 | // if we fail to install with a CAS, 1/128 prob of adjusting
111 | // silentCommitRatio
112 | if (install &&
113 | !globalVersion.compareAndSet(gvSnap, result) &&
114 | (result & 254) == 0)
115 | reduceContentionProbability()
116 |
117 | result
118 | }
119 |
120 | private def reduceRevalidateProbability(): Unit = {
121 | // increment the commit version on more commits, so reduce the ratio
122 | val r = silentCommitRatio
123 | if (r > silentCommitRatioMin) {
124 | silentCommitRatio = r - 1
125 | silentCommitCutoff = intRangeFraction(r - 1)
126 | //System.err.println("reduced ratio to " + (r - 1))
127 | }
128 | }
129 |
130 | private def reduceContentionProbability(): Unit = {
131 | // increment the commit version on fewer commits, so reduce the ratio
132 | val r = silentCommitRatio
133 | if (r < silentCommitRatioMax) {
134 | silentCommitRatio = r + 1
135 | silentCommitCutoff = intRangeFraction(r + 1)
136 | //System.err.println("increased ratio to " + (r + 1))
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/ccstm/Handle.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package ccstm
5 |
6 |
7 | private[ccstm] object Handle {
8 |
9 | /** A `Handle.Provider` has a single associated handle. */
10 | trait Provider[T] {
11 | def handle: Handle[T]
12 |
13 | override def hashCode: Int = {
14 | val h = handle
15 | CCSTM.hash(h.base, h.offset)
16 | }
17 |
18 | override def equals(rhs: Any): Boolean = {
19 | (this eq rhs.asInstanceOf[AnyRef]) || (rhs match {
20 | case r: Handle.Provider[_] =>
21 | val h1 = handle
22 | val h2 = r.handle
23 | (h1.base eq h2.base) && (h1.offset == h2.offset)
24 |
25 | case r: Ref[_] => r equals this
26 | case v: Ref.View[_] => v equals this
27 | case _ => false
28 | })
29 | }
30 | }
31 | }
32 |
33 | /** A `Handle` defines the operations that must be available for a particular
34 | * memory location in order for it to be managed by CCSTM. The identity of
35 | * the location is determined by `base` and `offset`, with `base` be used only
36 | * in comparisons using `eq` or `ne` (no methods of `base` will ever be
37 | * invoked). Metadata is identified by `base` and `metaOffset` (the assumption
38 | * made during blocking is that a write to a handle's `meta` may affect the
39 | * `meta` read by any handle with the same `base` and `metaOffset`).
40 | *
41 | * @author Nathan Bronson
42 | */
43 | private[ccstm] abstract class Handle[T] {
44 | def meta: Long
45 | def meta_=(v: Long): Unit
46 | def metaCAS(before: Long, after: Long): Boolean
47 | def base: AnyRef
48 | def offset: Int
49 | def metaOffset: Int
50 | def data: T
51 | def data_=(v: T): Unit
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/ccstm/RefOps.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package ccstm
5 |
6 | /** The default implementation of `Ref`'s operations in CCSTM. */
7 | private[ccstm] trait RefOps[T] extends Ref[T] with Handle.Provider[T] {
8 |
9 | private def impl(implicit txn: InTxn): InTxnImpl = txn.asInstanceOf[InTxnImpl]
10 |
11 | /** Override this to provide the handle that this `RefOps` uses. */
12 | def handle: Handle[T]
13 |
14 | //////////////// Source stuff
15 |
16 | override def apply()(implicit txn: InTxn): T = impl.get(handle)
17 | def get(implicit txn: InTxn): T = impl.get(handle)
18 | def getWith[Z](f: (T) => Z)(implicit txn: InTxn): Z = impl.getWith(handle, f)
19 | def relaxedGet(equiv: (T, T) => Boolean)(implicit txn: InTxn): T = impl.relaxedGet(handle, equiv)
20 |
21 | //////////////// Sink stuff
22 |
23 |
24 | override def update(v: T)(implicit txn: InTxn): Unit = impl.set(handle, v)
25 | def set(v: T)(implicit txn: InTxn): Unit = impl.set(handle, v)
26 | def trySet(v: T)(implicit txn: InTxn): Boolean = impl.trySet(handle, v)
27 |
28 | //////////////// Ref stuff
29 |
30 | def swap(v: T)(implicit txn: InTxn): T = impl.swap(handle, v)
31 |
32 | def transform(f: T => T)(implicit txn: InTxn): Unit = {
33 | // only sub-types of Ref actually perform deferral, the base implementation
34 | // evaluates f immediately
35 | impl.getAndTransform(handle, f)
36 | }
37 |
38 | override def transformAndGet(f: T => T)(implicit txn: InTxn): T = impl.transformAndGet(handle, f)
39 |
40 | override def getAndTransform(f: T => T)(implicit txn: InTxn): T = impl.getAndTransform(handle, f)
41 |
42 | override def transformAndExtract[V](f: T => (T,V))(implicit txn: InTxn): V = impl.transformAndExtract(handle, f)
43 |
44 | def transformIfDefined(pf: PartialFunction[T,T])(implicit txn: InTxn): Boolean = {
45 | impl.transformIfDefined(handle, pf)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/ccstm/RetrySet.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm.ccstm
4 |
5 | import annotation.tailrec
6 | import java.util.concurrent.TimeUnit
7 |
8 | /** A retry set representation. */
9 | private[ccstm] class RetrySet(val size: Int,
10 | handles: Array[Handle[_]],
11 | versions: Array[CCSTM.Version]) {
12 | import CCSTM._
13 |
14 | /** Returns the number of nanoseconds of blocking that was performed. */
15 | @throws(classOf[InterruptedException])
16 | def awaitRetry(timeoutNanos: Long): Long = {
17 | if (size == 0 && timeoutNanos == Long.MaxValue)
18 | throw new IllegalStateException("explicit retries cannot succeed because cumulative read set is empty")
19 |
20 | val begin = System.nanoTime
21 |
22 | val d = begin + timeoutNanos
23 | val deadline = if (d < 0) Long.MaxValue else d // handle arithmetic overflow
24 |
25 | attemptAwait(deadline)
26 |
27 | val actualElapsed = System.nanoTime - begin
28 |
29 | if (Stats.top != null) {
30 | Stats.top.retrySet += size
31 | val millis = TimeUnit.NANOSECONDS.toMillis(actualElapsed)
32 | Stats.top.retryWaitElapsed += millis.asInstanceOf[Int]
33 | }
34 |
35 | // to cause the proper retryFor to wake up we need to present an illusion
36 | // that we didn't block for too long
37 | //
38 | // reportedElapsed = min(now, deadline) - begin
39 | // reportedElapsed = min(now - begin, deadline - begin)
40 | math.min(actualElapsed, deadline - begin)
41 | }
42 |
43 | /** Returns true if something changed, false if the deadline was reached. */
44 | @throws(classOf[InterruptedException])
45 | private def attemptAwait(nanoDeadline: Long): Boolean = {
46 | // Spin a few times, counting one spin per read set element
47 | var spins = 0
48 | while (size > 0 && spins < SpinCount + YieldCount) {
49 | if (changed)
50 | return true
51 | spins += size
52 | if (spins > SpinCount) {
53 | Thread.`yield`()
54 | if (nanoDeadline != Long.MaxValue && System.nanoTime > nanoDeadline)
55 | return false
56 | }
57 | }
58 |
59 | blockingAttemptAwait(nanoDeadline)
60 | }
61 |
62 | @throws(classOf[InterruptedException])
63 | @tailrec
64 | private def blockingAttemptAwait(nanoDeadline: Long,
65 | event: WakeupManager.Event = wakeupManager.subscribe,
66 | i: Int = size - 1): Boolean = {
67 | if (i < 0) {
68 | // event has been completed, time to block
69 | if (!event.tryAwaitUntil(nanoDeadline))
70 | false // timed out
71 | else
72 | changed || blockingAttemptAwait(nanoDeadline) // event fired
73 | } else {
74 | // still building the event
75 | val h = handles(i)
76 | if (!event.addSource(h))
77 | changed || blockingAttemptAwait(nanoDeadline) // event fired
78 | else if (!addPendingWakeup(h, versions(i)))
79 | true // direct evidence of change
80 | else
81 | blockingAttemptAwait(nanoDeadline, event, i - 1) // keep building
82 | }
83 | }
84 |
85 | @tailrec
86 | private def addPendingWakeup(handle: Handle[_], ver: CCSTM.Version): Boolean = {
87 | val m = handle.meta
88 | if (changing(m) || version(m) != ver)
89 | false // handle has already changed
90 | else if (pendingWakeups(m) || handle.metaCAS(m, withPendingWakeups(m)))
91 | true // already has pending wakeup, or CAS to add it was successful
92 | else
93 | addPendingWakeup(handle, ver) // try again
94 | }
95 |
96 | private def changed: Boolean = {
97 | var i = size - 1
98 | while (i >= 0) {
99 | val m = handles(i).meta
100 | if (changing(m) || version(m) != versions(i))
101 | return true
102 | i -= 1
103 | }
104 | false
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/ccstm/RetrySetBuilder.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm.ccstm
4 |
5 | import annotation.tailrec
6 |
7 |
8 | /** This is basically a specialized builder for a map from `Handle` to
9 | * `Version`.
10 | *
11 | * @author Nathan Bronson
12 | */
13 | private[ccstm] final class RetrySetBuilder {
14 | private final val InitialCap = 16
15 | private var _size = 0
16 | private var _handles = new Array[Handle[_]](maxSizeForCap(InitialCap) + 1)
17 | private var _versions = new Array[CCSTM.Version](maxSizeForCap(InitialCap) + 1)
18 | private var _next = new Array[Int](maxSizeForCap(InitialCap) + 1)
19 | private var _dispatch = new Array[Int](InitialCap)
20 |
21 | private def maxSizeForCap(cap: Int) = cap - (cap / 4)
22 |
23 | def size: Int = _size
24 |
25 | def += (handle: Handle[_], version: CCSTM.Version): Unit = {
26 | val slot = CCSTM.hash(handle.base, handle.offset) & (_dispatch.length - 1)
27 | addImpl(slot, _dispatch(slot), handle, version)
28 | }
29 |
30 | @tailrec
31 | private def addImpl(slot: Int, i: Int, handle: Handle[_], version: CCSTM.Version): Unit = {
32 | if (i == 0)
33 | append(slot, handle, version)
34 | else if (!hEq(_handles(i - 1), handle))
35 | addImpl(slot, _next(i - 1), handle, version)
36 | // else it is a duplicate
37 | }
38 |
39 | private def append(slot: Int, handle: Handle[_], version: CCSTM.Version): Unit = {
40 | val i = _size + 1
41 | _size = i
42 | _handles(i - 1) = handle
43 | _versions(i - 1) = version
44 | _next(i - 1) = _dispatch(slot)
45 | _dispatch(slot) = i
46 | if (_size > maxSizeForCap(_dispatch.length))
47 | grow()
48 | }
49 |
50 | private def grow(): Unit = {
51 | // store the current contents
52 | val s = _size
53 | val hh = _handles
54 | val vv = _versions
55 |
56 | // reallocate
57 | _size = 0
58 | val c = _dispatch.length * 2
59 | _handles = new Array[Handle[_]](maxSizeForCap(c) + 1)
60 | _versions = new Array[CCSTM.Version](maxSizeForCap(c) + 1)
61 | _next = new Array[Int](maxSizeForCap(c) + 1)
62 | _dispatch = new Array[Int](c)
63 |
64 | // reinsert the current contents
65 | var i = 0
66 | while (i < s) {
67 | val h = hh(i)
68 | append(CCSTM.hash(h.base, h.offset) & (c - 1), h, vv(i))
69 | i += 1
70 | }
71 | }
72 |
73 | private def hEq(a: Handle[_], b: Handle[_]) = (a eq b) || ((a.base eq b.base) && (a.offset == b.offset))
74 |
75 | def result(): RetrySet = {
76 | _dispatch = null
77 | _next = null
78 | new RetrySet(_size, _handles, _versions)
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/ccstm/Stats.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm.ccstm
4 |
5 | import collection.mutable.ArrayBuffer
6 |
7 | private[ccstm] object Stats {
8 |
9 | val Enabled: Boolean = "yYtT1".indexOf((System.getProperty("ccstm.stats", "") + "0").charAt(0)) >= 0
10 |
11 | class LazyCounterMap[A] {
12 | import scala.collection.JavaConverters._
13 |
14 | private val _counters = new java.util.concurrent.ConcurrentHashMap[A, Counter]
15 |
16 | def += (k: A): Unit = {
17 | var v = _counters.get(k)
18 | if (v == null) {
19 | _counters.putIfAbsent(k, new Counter)
20 | v = _counters.get(k)
21 | }
22 | v += 1
23 | }
24 |
25 | def toStr(k: A): String = k.toString
26 |
27 | def contents: Seq[(String, Long)] = {
28 | val aa = _counters.entrySet.asScala.toSeq map { e => toStr(e.getKey) -> e.getValue.apply() }
29 | aa sortBy { -_._2 }
30 | }
31 | }
32 |
33 | class Histo(numBuckets: Int) {
34 | private val _sum = new Counter
35 | private val _buckets = Array.tabulate(numBuckets) { _ => new Counter }
36 |
37 | def += (value: Int): Unit =
38 | if (value != 0) {
39 | _sum += value
40 | _buckets(bucketFor(value)) += 1
41 | }
42 |
43 | protected def bucketFor(value: Int): Int = {
44 | if (value < 0 || value >= _buckets.length)
45 | _buckets.length - 1
46 | else
47 | value
48 | }
49 |
50 | def contents: Seq[Long] = {
51 | val snap = _buckets map { _.apply() }
52 | snap.take(1 + snap.lastIndexWhere { _ != 0L })
53 | }
54 |
55 | override def toString: String = {
56 | val s = _sum()
57 | val c = contents
58 | val count = c.foldLeft(0L)( _ + _ )
59 | val avg = if (count == 0) 0.0 else s * 1.0 / count
60 | "sum= %-10d count= %-8d avg= %-5.1f [%s]".format(s, count, avg, c.mkString(" "))
61 | }
62 | }
63 |
64 | class ExponentialHisto extends Histo(32) {
65 | override protected def bucketFor(value: Int): Int = {
66 | var v = value >>> 1
67 | var i = 0
68 | while (v != 0) {
69 | v >>>= 1
70 | i += 1
71 | }
72 | i
73 | }
74 | }
75 |
76 | class Level(isTop: Boolean) {
77 | val commits = new Counter
78 | val alternatives = new Histo(10)
79 | val retrySet = if (isTop) new ExponentialHisto else null
80 | val retryWaitElapsed = if (isTop) new ExponentialHisto else null
81 | val explicitRetries = new Counter
82 | val unrecordedTxns = new Counter
83 | val optimisticRetries = new LazyCounterMap[Symbol]
84 | val failures = new LazyCounterMap[Class[_]] { override def toStr(k: Class[_]): String = k.getSimpleName }
85 | val blockingAcquires = new Counter
86 | val commitReadSet = if (isTop) new ExponentialHisto else null
87 | val commitBargeSet = if (isTop) new ExponentialHisto else null
88 | val commitWriteSet = if (isTop) new ExponentialHisto else null
89 | val rollbackReadSet = new ExponentialHisto
90 | val rollbackBargeSet = new ExponentialHisto
91 | val rollbackWriteSet = new ExponentialHisto
92 |
93 | def contents: Seq[String] = {
94 | val buf = new ArrayBuffer[String]
95 | for (f <- getClass.getDeclaredFields) {
96 | val name = f.getName
97 | val value = getClass.getDeclaredMethod(name).invoke(this)
98 | value match {
99 | case null =>
100 | case c: Counter => buf += "%17s= %d".format(name, c())
101 | case m: LazyCounterMap[_] =>
102 | for ((k, v) <- m.contents)
103 | buf += "%17s: %7d %s".format(name, v, k)
104 |
105 | case h: Histo => buf += "%17s: %s".format(name, h)
106 | }
107 | }
108 | buf.result
109 | }
110 |
111 | def mkString(prefix: String): String = {
112 | prefix + ("-" * 64) + "\n" + contents.map( prefix + _ ).mkString("\n")
113 | }
114 | }
115 |
116 | val top : Level = if (Enabled) new Level(true) else null
117 | val nested: Level = if (Enabled) new Level(false) else null
118 | registerShutdownHook()
119 |
120 | private def registerShutdownHook(): Unit =
121 | if (top != null)
122 | Runtime.getRuntime.addShutdownHook(new Thread("shutdown stats printer") {
123 | override def run(): Unit = println(Stats)
124 | })
125 |
126 | override def toString: String =
127 | top.mkString("CCSTM: top: ") + "\n" + nested.mkString("CCSTM: nested: ")
128 | }
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/ccstm/TArrayImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package ccstm
5 |
6 | import java.util.concurrent.atomic.AtomicLongArray
7 |
8 | import scala.collection.immutable
9 | import scala.concurrent.stm.skel.AtomicArray
10 | import scala.reflect.ClassTag
11 |
12 |
13 | private[ccstm] class TArrayImpl[A](private val values: AtomicArray[A])(implicit m: ClassTag[A]) extends TArray[A] with TArray.View[A] {
14 | import TArray._
15 |
16 | def this(length0: Int)(implicit m: ClassTag[A]) = this(AtomicArray[A](length0))
17 |
18 | def this(data0: TraversableOnce[A])(implicit m: ClassTag[A]) = this(AtomicArray[A](data0))
19 |
20 | val length: Int = values.length
21 |
22 | //////////////// TArray
23 |
24 | def single: View[A] = this
25 |
26 | def apply(index: Int)(implicit txn: InTxn): A = getRef(index).get
27 |
28 | def update(index: Int, v: A)(implicit txn: InTxn): Unit = getRef(index).set(v)
29 |
30 | /** Returns a sequence that will produce transient `Ref` instances that are
31 | * backed by elements of this `TArray`. This allows use of all of `Ref`'s
32 | * functionality for reading, writing, and transforming elements.
33 | */
34 | def refs: immutable.IndexedSeq[Ref[A]] = new immutable.IndexedSeq[Ref[A]] {
35 | def length: Int = TArrayImpl.this.length
36 | def apply(index0: Int): Ref[A] = getRef(index0)
37 | }
38 |
39 | //////////////// TArray.View
40 |
41 | def tarray = TArrayImpl.this
42 |
43 | def apply(index: Int): A = getRef(index).single.get
44 |
45 | def update(index: Int, v: A): Unit = getRef(index).single.set(v)
46 |
47 | def refViews: immutable.IndexedSeq[Ref.View[A]] = new immutable.IndexedSeq[Ref.View[A]] {
48 | def length: Int = tarray.length
49 | def apply(index: Int): Ref.View[A] = getRef(index).single
50 | }
51 |
52 | /////////////// TxnDebuggable
53 |
54 | def dbgStr: String = atomic.unrecorded({ _ => mkStringPrefix("TArray", single) }, { _.toString })
55 |
56 | def dbgValue: Any = atomic.unrecorded({ _ => toArray }, { x => x })
57 |
58 | /////////////// Internal implementation
59 |
60 | private val metaIndexMask = {
61 | // We use min(length, nextPowerOfTwo(6 + length/16)) metadata elements.
62 | // The mask is always nextPowerOfTwo(6 + length/16) - 1, even if that is
63 | // too large.
64 | val n = 6 + length / 16
65 | var m = 7
66 | while (m < n - 1)
67 | m = (m << 1) + 1
68 | m
69 | }
70 |
71 | private val metaValues = new AtomicLongArray(math.min(metaIndexMask + 1, length))
72 |
73 | private def getRef(index: Int): Ref[A] = {
74 | if (index < 0 || index >= length)
75 | throw new ArrayIndexOutOfBoundsException(index)
76 |
77 | new Handle[A] with RefOps[A] with ViewOps[A] {
78 | def handle: Handle[A] = this
79 | def single: Ref.View[A] = this
80 | def ref: Ref[A] = this
81 |
82 | def meta: Long = metaValues.get(metaOffset)
83 | def meta_=(v: Long): Unit = metaValues.set(metaOffset, v)
84 | def metaCAS(before: Long, after: Long): Boolean = metaValues.compareAndSet(metaOffset, before, after)
85 | def base = TArrayImpl.this
86 | def offset: Int = index
87 | def metaOffset: Int = index & metaIndexMask
88 | def data: A = values(index)
89 | def data_=(v: A): Unit = values(index) = v
90 |
91 | override def dbgStr: String = super[RefOps].dbgStr
92 | override def dbgValue: Any = super[RefOps].dbgValue
93 |
94 | override def toString: String = {
95 | "TArray@" + Integer.toHexString(System.identityHashCode(TArrayImpl.this)) + "(" + index + ")"
96 | }
97 | }
98 | }
99 | }
100 |
101 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/ccstm/TxnLocalImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package ccstm
5 |
6 | // TxnLocalImpl
7 |
8 | private[ccstm] class TxnLocalImpl[A](init: => A,
9 | initialValue: InTxn => A,
10 | beforeCommit: InTxn => Unit,
11 | whilePreparing: InTxnEnd => Unit,
12 | whileCommitting: InTxnEnd => Unit,
13 | afterCommit: A => Unit,
14 | afterRollback: Txn.Status => Unit,
15 | afterCompletion: Txn.Status => Unit) extends Handle[A] with TxnLocal[A] {
16 |
17 | //////// stateless Handle
18 |
19 | // TODO: Return types should probably not be refined from Unit to Nothing
20 |
21 | def meta: Long = CCSTM.txnLocalMeta
22 | def meta_=(v: Long): Nothing = throw new Error
23 | def metaCAS(before: Long, after: Long): Boolean = throw new Error
24 | def base: AnyRef = this
25 | def offset: Int = 0
26 | def metaOffset: Int = 0
27 | def data: A = throw new Error
28 | def data_=(v: A): Unit = ()
29 |
30 |
31 | //////// TxnLocal
32 |
33 | def isInitialized(implicit txn: InTxnEnd): Boolean = {
34 | txn.asInstanceOf[InTxnImpl].txnLocalFind(this) >= 0
35 | }
36 |
37 | //////// SourceLike
38 |
39 | def get(implicit txn: InTxnEnd): A = {
40 | val impl = txn.asInstanceOf[InTxnImpl]
41 | val i = impl.txnLocalFind(this)
42 | if (i >= 0)
43 | impl.txnLocalGet[A](i)
44 | else
45 | initialize(impl)
46 | }
47 |
48 | private def initialize(impl: InTxnImpl): A = {
49 | if (initialValue != null || beforeCommit != null)
50 | impl.requireActive()
51 |
52 | val v = if (initialValue != null) initialValue(impl) else init
53 | impl.txnLocalInsert(this, v)
54 |
55 | registerCallbacks(impl)
56 |
57 | v
58 | }
59 |
60 | private def registerCallbacks(impl: InTxnImpl): Unit = {
61 | // need to do afterRollback and afterCompletion first so that if there is a
62 | // remote txn cancel we've got them in place
63 | if (afterRollback != null)
64 | impl.afterRollback(afterRollback)
65 | if (afterCompletion != null)
66 | impl.afterCompletion(afterCompletion)
67 |
68 | if (beforeCommit != null)
69 | impl.beforeCommit(beforeCommit)
70 | if (whilePreparing != null)
71 | impl.whilePreparing(whilePreparing)
72 | if (whileCommitting != null)
73 | impl.whileCommitting(whileCommitting)
74 | if (afterCommit != null) {
75 | impl.whileCommitting { _ =>
76 | val finalValue = impl.txnLocalGet[A](impl.txnLocalFind(this))
77 | impl.afterCommit { _ => afterCommit(finalValue) }
78 | }
79 | }
80 | }
81 |
82 | def getWith[Z](f: (A) => Z)(implicit txn: InTxnEnd): Z = f(get)
83 |
84 | def relaxedGet(equiv: (A, A) => Boolean)(implicit txn: InTxnEnd): A = get
85 |
86 | //////// SinkLike
87 |
88 | def set(v: A)(implicit txn: InTxnEnd): Unit = {
89 | val impl = txn.asInstanceOf[InTxnImpl]
90 | val i = impl.txnLocalFind(this)
91 | if (i >= 0)
92 | impl.txnLocalUpdate(i, v)
93 | else {
94 | impl.txnLocalInsert(this, v)
95 | registerCallbacks(impl)
96 | }
97 | }
98 |
99 | def trySet(v: A)(implicit txn: InTxnEnd): Boolean = { set(v) ; true }
100 |
101 | //////// RefLike
102 |
103 | def swap(v: A)(implicit txn: InTxnEnd): A = { val z = get ; set(v) ; z }
104 |
105 | def transform(f: (A) => A)(implicit txn: InTxnEnd): Unit = set(f(get))
106 |
107 | def transformIfDefined(pf: PartialFunction[A, A])(implicit txn: InTxnEnd): Boolean = {
108 | val v0 = get
109 | pf.isDefinedAt(v0) && { set(pf(v0)) ; true }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/ccstm/TxnSlotManager.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package ccstm
5 |
6 |
7 | import java.util.concurrent.atomic.AtomicReferenceArray
8 |
9 |
10 | private case class SlotLock(txn: AnyRef, refCount: Int)
11 |
12 | /** This class manages a mapping from active transaction to a bounded integral
13 | * range, so that transaction identities to be packed into some of the bits of
14 | * an integral value.
15 | *
16 | * @author Nathan Bronson
17 | */
18 | private[ccstm] final class TxnSlotManager[T <: AnyRef](range: Int, reservedSlots: Int) {
19 |
20 | assert(range >= 16 & (range & (range - 1)) == 0)
21 | assert(range >= reservedSlots + 16)
22 |
23 | private def nextSlot(tries: Int) = {
24 | ((skel.SimpleRandom.nextInt << 4) | ((-tries >> 1) & 0xf)) & (range - 1)
25 | }
26 |
27 | /** CAS on the entries manages the actual acquisition. Entries are either
28 | * transactions, or SlotLock objects.
29 | */
30 | private val slots = new AtomicReferenceArray[AnyRef](range)
31 |
32 | @throws(classOf[InterruptedException])
33 | def assign(txn: T, slotHint: Int): Int = {
34 | // We advance to the next slot number after the hint, wrapping around in a
35 | // 64 byte space. This avoids rollback from late steals, but keeps us in
36 | // a cache line we already own.
37 | var s = ((slotHint & ~0xf) | ((slotHint + 1) & 0xf)) & (range - 1)
38 |
39 | var tries = 0
40 | while (s < reservedSlots || slots.get(s) != null || !slots.compareAndSet(s, null, txn)) {
41 | s = nextSlot(tries)
42 | tries += 1
43 | if (tries > 100) {
44 | if (Thread.interrupted)
45 | throw new InterruptedException
46 | Thread.`yield`()
47 | }
48 | }
49 | s
50 | }
51 |
52 | /** Returns the slot associated with `slot` at some instant. The
53 | * returned value may be obsolete before this method returns.
54 | */
55 | def lookup(slot:Int): T = unwrap(slots.get(slot))
56 |
57 | private def unwrap(e: AnyRef): T = {
58 | e match {
59 | case SlotLock(txn, _) => txn.asInstanceOf[T]
60 | case txn => txn.asInstanceOf[T]
61 | }
62 | }
63 |
64 | /** A non-racy version of `lookup`, that must be paired with
65 | * `endLookup`.
66 | */
67 | def beginLookup(slot: Int): T = {
68 | var e: AnyRef = null
69 | do {
70 | e = slots.get(slot)
71 | } while (e != null && !slots.compareAndSet(slot, e, locked(e)))
72 | unwrap(e)
73 | }
74 |
75 | private def locked(e: AnyRef): AnyRef = {
76 | e match {
77 | case SlotLock(txn, rc) => SlotLock(txn, rc + 1)
78 | case txn => SlotLock(txn, 1)
79 | }
80 | }
81 |
82 | def endLookup(slot: Int, observed: T): Unit =
83 | if (null != observed) release(slot)
84 |
85 | def release(slot: Int): Unit = {
86 | var e: AnyRef = null
87 | do {
88 | e = slots.get(slot)
89 | } while (!slots.compareAndSet(slot, e, unlocked(e)))
90 | }
91 |
92 | private def unlocked(e: AnyRef): AnyRef = {
93 | e match {
94 | case SlotLock(txn, 1) => txn
95 | case SlotLock(txn, rc) => SlotLock(txn, rc - 1)
96 | case _ => null
97 | }
98 | }
99 |
100 | // def assertAllReleased() {
101 | // for (i <- 0 until range) {
102 | // val e = slots.get(i)
103 | // if (null != e) {
104 | // assert(false, i + " -> " + e)
105 | // }
106 | // }
107 | // }
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/ccstm/UnrecordedRead.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm.ccstm
4 |
5 |
6 | /** Holds the result of an unrecorded read, which may be used to avoid
7 | * transaction conflicts, or to detect ABA changes when performing
8 | * single-operation transactions. `ReleasableRead`s provide a
9 | * related functionality.
10 | *
11 | * When an unrecorded read is performed in a transaction, the caller is
12 | * responsible for guaranteeing that the transaction's behavior is correct,
13 | * even if the read becomes invalid prior to commit. Unrecorded reads may be
14 | * useful for heuristic decisions that can tolerate inconsistent or stale
15 | * data, for methods that register transaction handlers to perform
16 | * validation at a semantic level, or for optimistically traversing linked
17 | * data structures while tolerating mutations to earlier links. When used in
18 | * combination with transaction resource callbacks, it is important to
19 | * consider the case that the unrecorded read is already invalid before it is
20 | * returned to the requester.
21 | *
22 | * Writes by the same transaction that performed the unrecorded read are
23 | * '''not''' considered to invalidate the read.
24 | *
25 | * When called from a non-transactional context the returned instance can be
26 | * used to determine if a value has remained unchanged for a particular
27 | * interval, which may be useful to detect ABA situations.
28 | *
29 | * @author Nathan Bronson
30 | */
31 | private[ccstm] trait UnrecordedRead[+T] {
32 |
33 | /** Returns the value observed by this `UnrecordedRead`, regardless of
34 | * whether it is still valid.
35 | */
36 | def value: T
37 |
38 | /** Returns true if definitely no writes have been performed by a context
39 | * other than the one that performed this unrecorded read.
40 | */
41 | def stillValid: Boolean
42 |
43 | /** Returns true if the unrecorded read was performed in a transaction, and
44 | * the source of this read is known to be in the transaction's read or write
45 | * set, in which case `stillValid` will definitely be true at the
46 | * transaction's commit (linearization) point if the transaction commits.
47 | * Returns false for non-transactional unrecorded reads. This method is for
48 | * optimization purposes only, a false result does not guarantee that the
49 | * read is not in the transaction's read or write set. If this method
50 | * returns true for an `UnrecordedRead` instance it will always
51 | * return true.
52 | * @return true if the caller may assume that `stillValid` will
53 | * be true for this `UnrecordedRead` if the bound transaction
54 | * is successfully committed.
55 | */
56 | def recorded: Boolean
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/ccstm/ViewOps.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package ccstm
5 |
6 | import java.util.concurrent.TimeUnit
7 |
8 | /** The default implementation of `Ref.View`'s operations in CCSTM. */
9 | private[ccstm] trait ViewOps[T] extends Ref.View[T] with Handle.Provider[T] {
10 |
11 | def handle: Handle[T]
12 |
13 | def get: T = InTxnImpl.dynCurrentOrNull match {
14 | case null => NonTxn.get(handle)
15 | case txn => txn.get(handle)
16 | }
17 | def getWith[Z](f: T => Z): Z = InTxnImpl.dynCurrentOrNull match {
18 | case null => f(NonTxn.get(handle))
19 | case txn => txn.getWith(handle, f)
20 | }
21 | def relaxedGet(equiv: (T, T) => Boolean): T = InTxnImpl.dynCurrentOrNull match {
22 | case null => NonTxn.get(handle)
23 | case txn => txn.relaxedGet(handle, equiv)
24 | }
25 | def await(f: T => Boolean): Unit = InTxnImpl.dynCurrentOrNull match {
26 | case null => NonTxn.await(handle, f)
27 | case txn => if (!f(txn.get(handle))) Txn.retry(txn)
28 | }
29 | def tryAwait(timeout: Long, unit: TimeUnit)(f: T => Boolean): Boolean = InTxnImpl.dynCurrentOrNull match {
30 | case null => NonTxn.tryAwait(handle, f, unit.toNanos(timeout))
31 | case txn => f(txn.get(handle)) || { Txn.retryFor(timeout, unit)(txn) ; false }
32 | }
33 | def set(v: T): Unit = InTxnImpl.dynCurrentOrNull match {
34 | case null => NonTxn.set(handle, v)
35 | case txn => txn.set(handle, v)
36 | }
37 | def trySet(v: T): Boolean = InTxnImpl.dynCurrentOrNull match {
38 | case null => NonTxn.trySet(handle, v)
39 | case txn => txn.trySet(handle, v)
40 | }
41 | def swap(v: T): T = InTxnImpl.dynCurrentOrNull match {
42 | case null => NonTxn.swap(handle, v)
43 | case txn => txn.swap(handle, v)
44 | }
45 | def compareAndSet(before: T, after: T): Boolean = InTxnImpl.dynCurrentOrNull match {
46 | case null => NonTxn.compareAndSet(handle, before, after)
47 | case txn => txn.compareAndSet(handle, before, after)
48 | }
49 | def compareAndSetIdentity[R <: AnyRef with T](before: R, after: T): Boolean = InTxnImpl.dynCurrentOrNull match {
50 | case null => NonTxn.compareAndSetIdentity(handle, before, after)
51 | case txn => txn.compareAndSetIdentity(handle, before, after)
52 | }
53 | def transform(f: T => T): Unit = InTxnImpl.dynCurrentOrNull match {
54 | case null => NonTxn.transformAndGet(handle, f)
55 | case txn => txn.transformAndGet(handle, f)
56 | }
57 | def getAndTransform(f: T => T): T = InTxnImpl.dynCurrentOrNull match {
58 | case null => NonTxn.getAndTransform(handle, f)
59 | case txn => txn.getAndTransform(handle, f)
60 | }
61 | def transformAndGet(f: T => T): T = InTxnImpl.dynCurrentOrNull match {
62 | case null => NonTxn.transformAndGet(handle, f)
63 | case txn => txn.transformAndGet(handle, f)
64 | }
65 | override def transformAndExtract[V](f: T => (T,V)): V = InTxnImpl.dynCurrentOrNull match {
66 | case null => NonTxn.transformAndExtract(handle, f)
67 | case txn => txn.transformAndExtract(handle, f)
68 | }
69 |
70 | def transformIfDefined(pf: PartialFunction[T,T]): Boolean = InTxnImpl.dynCurrentOrNull match {
71 | case null => NonTxn.transformIfDefined(handle, pf)
72 | case txn => txn.transformIfDefined(handle, pf)
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/impl/AlternativeResult.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package impl
5 |
6 | import util.control.ControlThrowable
7 |
8 | /** See `PendingAtomicBlock` */
9 | private[stm] case class AlternativeResult(value: Any) extends ControlThrowable
10 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/impl/RefFactory.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package impl
5 |
6 | import scala.collection.mutable
7 | import scala.reflect.ClassTag
8 |
9 | /** `RefFactory` is responsible for creating concrete `Ref` instances. */
10 | trait RefFactory {
11 | def newRef(v0: Boolean): Ref[Boolean]
12 | def newRef(v0: Byte): Ref[Byte]
13 | def newRef(v0: Short): Ref[Short]
14 | def newRef(v0: Char): Ref[Char]
15 | def newRef(v0: Int): Ref[Int]
16 | def newRef(v0: Float): Ref[Float]
17 | def newRef(v0: Long): Ref[Long]
18 | def newRef(v0: Double): Ref[Double]
19 | def newRef(v0: Unit): Ref[Unit]
20 |
21 | /** `T` will not be one of the primitive types (for which a `newRef`
22 | * specialization exists).
23 | */
24 | def newRef[A : ClassTag](v0: A): Ref[A]
25 |
26 | def newTxnLocal[A](init: => A,
27 | initialValue: InTxn => A,
28 | beforeCommit: InTxn => Unit,
29 | whilePreparing: InTxnEnd => Unit,
30 | whileCommitting: InTxnEnd => Unit,
31 | afterCommit: A => Unit,
32 | afterRollback: Txn.Status => Unit,
33 | afterCompletion: Txn.Status => Unit): TxnLocal[A]
34 |
35 | def newTArray[A : ClassTag](length: Int): TArray[A]
36 | def newTArray[A : ClassTag](xs: TraversableOnce[A]): TArray[A]
37 |
38 | def newTMap[A, B]: TMap[A, B]
39 | def newTMapBuilder[A, B]: mutable.Builder[(A, B), TMap[A, B]]
40 |
41 | def newTSet[A]: TSet[A]
42 | def newTSetBuilder[A]: mutable.Builder[A, TSet[A]]
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/impl/STMImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package impl
5 |
6 | import java.util.concurrent.TimeUnit
7 |
8 | private[impl] object STMImplHolder {
9 | var instance: STMImpl = STMImpl.createInstance()
10 | }
11 |
12 | /** `STMImpl` gathers all of the functionality required to plug an STM
13 | * implementation into `scala.concurrent.stm`. Only one implementation can
14 | * be selected, because `Ref`s and atomic blocks from different STM
15 | * implementations are not compatible. `STMImpl.instance` returns the
16 | * `STMImpl` instance that has been selected for this program execution.
17 | *
18 | * There are two ways to explicitly select the `STMImpl` instance:
19 | *
20 | * 1. set the JVM system property "scala.stm.impl" to the name of a class
21 | * that implements `STMImpl`; or
22 | *
23 | * 2. arrange for `STMImpl.select` or `STMImpl.trySelect` to be called
24 | * before any `Ref`s are constructed and before any atomic blocks are
25 | * executed.
26 | *
27 | * Setting the JVM system property "scala.stm.impl" is equivalent to making a
28 | * call to `STMImpl.select(System.getProperty("scala.stm.impl"))` before any
29 | * other `STMImpl` selections.
30 | *
31 | * If there is no explicitly selected `STMImpl` instance and the classpath
32 | * contains a class `scala.concurrent.stm.impl.DefaultFactory` that extends
33 | * `scala.concurrent.stm.impl.STMImpl.Factory`, then an instance of that
34 | * class will be instantiated and used to generate the `STMImpl` instance.
35 | * ScalaSTM implementations are encouraged to implement `DefaultFactory` so
36 | * that if a user includes the implementation's JAR file, it will be
37 | * automatically selected.
38 | *
39 | * If no explicit selection has been made and there is no definition of
40 | * `scala.concurrent.stm.impl.DefaultFactory` present in the classpath, then
41 | * ScalaSTM will fall back to the reference implementation
42 | * "scala.concurrent.stm.ccstm.CCSTM".
43 | *
44 | * @author Nathan Bronson
45 | */
46 | object STMImpl {
47 | trait Factory {
48 | def createInstance(): STMImpl
49 | }
50 |
51 | /** Returns the instance of `STMImpl` that should be used to implement all
52 | * ScalaSTM functionality. Calling this method forces the implementation
53 | * to be chosen if it has not already been selected.
54 | */
55 | def instance: STMImpl = STMImplHolder.instance
56 |
57 | // We duplicate the implementation of select() to avoid the need to
58 | // instantiate an STM that we won't end up using
59 |
60 | /** If no `STMImpl` instance has yet been selected, installs an instance of
61 | * `Class.forName(implClassName)` as the system-wide STM implementation.
62 | * Returns true if `implClassName` was newly or previously selected, or
63 | * returns false if another STM implementation was chosen.
64 | */
65 | def trySelect(implClassName: String): Boolean = {
66 | explicitChoice = implClassName
67 | instance.getClass.getName == implClassName
68 | }
69 |
70 | /** Installs `Class.forName(implClassName)` as the system-wide STM
71 | * implementation if no `STMImpl` has yet been chosen, or verifies that
72 | * `implClassName` was previously selected, throwing
73 | * `IllegalStateException` if a different STM implementation has already
74 | * been selected
75 | */
76 | def select(implClassName: String): Unit = {
77 | if (!trySelect(implClassName)) {
78 | throw new IllegalStateException(
79 | "unable to select STMImpl class " + implClassName + ", " + instance + " already installed")
80 | }
81 | }
82 |
83 | /** Installs `impl` as the system-wide STM implementation if no `STMImpl`
84 | * has yet been chosen, or verifies that `impl` is equal to the previously
85 | * selected instance, throwing `IllegalStateException` if an STM
86 | * implementation has already been selected and `impl != instance`
87 | */
88 | def select(impl: STMImpl): Unit = {
89 | explicitChoice = impl
90 | if (impl != instance) {
91 | throw new IllegalStateException(
92 | "unable to select STMImpl " + impl + ", " + instance + " already installed")
93 | }
94 | }
95 |
96 |
97 | /** May be a String class name, an STMImpl, or null */
98 | @volatile private var explicitChoice: AnyRef = null
99 |
100 | private[impl] def createInstance(): STMImpl = {
101 | var choice: AnyRef = System.getProperty("scala.stm.impl")
102 |
103 | if (choice == null)
104 | choice = explicitChoice
105 |
106 | if (choice == null) {
107 | choice = try {
108 | val fc = Class.forName("scala.concurrent.stm.impl.DefaultFactory")
109 | fc.newInstance().asInstanceOf[STMImpl.Factory].createInstance()
110 | } catch {
111 | case _: ClassNotFoundException => "scala.concurrent.stm.ccstm.CCSTM"
112 | }
113 | }
114 |
115 | choice match {
116 | case s: String => Class.forName(s).newInstance().asInstanceOf[STMImpl]
117 | case i: STMImpl => i
118 | }
119 | }
120 | }
121 |
122 | /** `STMImpl` gathers all of the functionality required to plug an STM
123 | * implementation into `scala.concurrent.stm`. See the `STMImpl` companion
124 | * object for information on controlling which `STMImpl` is selected at run
125 | * time.
126 | *
127 | * @author Nathan Bronson
128 | */
129 | trait STMImpl extends RefFactory with TxnContext with TxnExecutor {
130 |
131 | /** Returns a new commit barrier suitable for coordinating commits by this
132 | * STM implementation.
133 | */
134 | def newCommitBarrier(timeout: Long, unit: TimeUnit): CommitBarrier
135 | }
136 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/impl/TxnContext.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package impl
5 |
6 | /** `TxnContext` captures the implementation-specific functionality of locating
7 | * the `InTxn` dynamically bound to the current `Thread`. Users should use the
8 | * lookup methods provided by `object Txn`.
9 | *
10 | * @author Nathan Bronson
11 | */
12 | trait TxnContext {
13 |
14 | /** Returns `Some(txn)` if `txn` is the `InTxn` active or in the process of
15 | * committing on the current thread, `None` otherwise.
16 | */
17 | def findCurrent(implicit mt: MaybeTxn): Option[InTxn]
18 |
19 | /** Returns the current `InTxn` instance if it is active or in the process of
20 | * committing on the current thread, `null` otherwise. Always performs a
21 | * dynamic lookup.
22 | */
23 | def dynCurrentOrNull: InTxn
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/package.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent
4 |
5 | import java.util.concurrent.TimeUnit
6 |
7 | import scala.language.implicitConversions
8 |
9 | package object stm {
10 |
11 | /** Atomically executes atomic blocks using the default `TxnExecutor`. See
12 | * `TxnExecutor.apply`.
13 | */
14 | def atomic: scala.concurrent.stm.TxnExecutor = scala.concurrent.stm.TxnExecutor.defaultAtomic
15 |
16 | /** Equivalent to `Txn.retry`. */
17 | def retry(implicit txn: scala.concurrent.stm.InTxn): Nothing = scala.concurrent.stm.Txn.retry
18 |
19 | /** Equivalent to `Txn.retryFor(timeout, unit)`. */
20 | def retryFor(timeout: Long, unit: TimeUnit = TimeUnit.MILLISECONDS)(implicit txn: scala.concurrent.stm.InTxn): Unit =
21 | scala.concurrent.stm.Txn.retryFor(timeout, unit)
22 |
23 | /** This is the first half of the machinery for implementing `orAtomic`. */
24 | implicit def wrapChainedAtomic[A](lhs: => A): PendingAtomicBlock[A] = new PendingAtomicBlock(lhs)
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/skel/AbstractNestingLevel.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package skel
5 |
6 | private[stm] trait AbstractNestingLevel extends NestingLevel {
7 | def txn: AbstractInTxn
8 | def parLevel: AbstractNestingLevel
9 | override def root: AbstractNestingLevel
10 |
11 | def parent: Option[NestingLevel] = Option(parLevel)
12 |
13 | private[skel] var _beforeCommitSize = 0
14 | private[skel] var _whileValidatingSize = 0
15 | private[skel] var _whilePreparingSize = 0
16 | private[skel] var _whileCommittingSize = 0
17 | private[skel] var _afterCommitSize = 0
18 | private[skel] var _afterRollbackSize = 0
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/skel/CallbackList.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package skel
5 |
6 | import annotation.tailrec
7 |
8 |
9 | private[stm] class CallbackList[A] {
10 |
11 | private final val InitialCapacity = 128
12 | private final val MaxEmptyCapacity = 8192
13 |
14 | private var _size = 0
15 | private var _data = new Array[A => Unit](InitialCapacity)
16 |
17 | def isEmpty: Boolean = _size == 0
18 |
19 | def size: Int = _size
20 |
21 | def size_= (newSize: Int): Unit = {
22 | if (newSize != _size)
23 | changeSize(newSize)
24 | }
25 |
26 | private def changeSize(newSize: Int): Unit = {
27 | if (newSize < 0 || newSize > _size)
28 | throw new IllegalArgumentException
29 |
30 | if (newSize == 0 && _data.length > MaxEmptyCapacity) {
31 | // reallocate if the array is too big, so that a single large txn doesn't
32 | // permanently increase the memory footprint of this thread
33 | reset()
34 | } else {
35 | java.util.Arrays.fill(_data.asInstanceOf[Array[AnyRef]], newSize, _size, null)
36 | _size = newSize
37 | }
38 | }
39 |
40 | private def reset(): Unit = {
41 | _data = new Array[A => Unit](InitialCapacity)
42 | _size = 0
43 | }
44 |
45 | def += (handler: A => Unit): Unit = {
46 | if (_size == _data.length)
47 | grow()
48 | _data(_size) = handler
49 | _size += 1
50 | }
51 |
52 | private def grow(): Unit = {
53 | val a = new Array[A => Unit](_data.length * 2)
54 | System.arraycopy(_data, 0, a, 0, _data.length)
55 | _data = a
56 | }
57 |
58 | def apply(i: Int): (A => Unit) = _data(i)
59 |
60 | def fire(level: NestingLevel, arg: A): Unit = {
61 | if (_size > 0)
62 | fire(level, arg, 0)
63 | }
64 |
65 | @tailrec private def fire(level: NestingLevel, arg: A, i: Int): Unit = {
66 | if (i < _size && shouldFire(level)) {
67 | try {
68 | _data(i)(arg)
69 | } catch {
70 | case x: Throwable =>
71 | val s = level.requestRollback(Txn.UncaughtExceptionCause(x))
72 | assert(s.isInstanceOf[Txn.RolledBack])
73 | }
74 | fire(level, arg, i + 1)
75 | }
76 | }
77 |
78 | private def shouldFire(level: NestingLevel): Boolean = !level.status.isInstanceOf[Txn.RolledBack]
79 |
80 | /** Sets the size of this callback list to `newSize`, and returns a the
81 | * discarded handlers.
82 | */
83 | def truncate(newSize: Int): Array[A => Unit] = {
84 | if (_size == newSize) {
85 | // nothing to do
86 | null
87 | } else {
88 | // copy
89 | val z = new Array[A => Unit](_size - newSize)
90 | System.arraycopy(_data, newSize, z, 0, z.length)
91 | size = newSize
92 | z
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/skel/HashTrieTMap.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package skel
5 |
6 | import scala.collection.mutable
7 |
8 | private[stm] object HashTrieTMap {
9 |
10 | def empty[A, B]: TMap[A, B] = new HashTrieTMap(Ref(TxnHashTrie.emptyMapNode[A, B]).single)
11 |
12 | def newBuilder[A, B]: mutable.Builder[(A, B), TMap[A, B]] = new mutable.Builder[(A, B), TMap[A, B]] {
13 | var root: TxnHashTrie.BuildingNode[A, B] = TxnHashTrie.emptyMapBuildingNode[A, B]
14 |
15 | def clear(): Unit = { root = TxnHashTrie.emptyMapBuildingNode[A, B] }
16 |
17 | def += (kv: (A, B)): this.type = { root = TxnHashTrie.buildingPut(root, kv._1, kv._2) ; this }
18 |
19 | def result(): TMap[A, B] = {
20 | val r = root
21 | root = null
22 | new HashTrieTMap(Ref(r.endBuild).single)
23 | }
24 | }
25 | }
26 |
27 | private[skel] class HashTrieTMap[A, B] private (root0: Ref.View[TxnHashTrie.Node[A, B]]
28 | ) extends TxnHashTrie[A, B](root0) with TMapViaClone[A, B] {
29 |
30 | //// construction
31 |
32 | override def empty: TMap.View[A, B] = new HashTrieTMap(Ref(TxnHashTrie.emptyMapNode[A, B]).single)
33 | override def clone: HashTrieTMap[A, B] = new HashTrieTMap(cloneRoot)
34 |
35 | //// TMap.View aggregates
36 |
37 | override def isEmpty: Boolean = singleIsEmpty
38 | override def size: Int = singleSize
39 | override def iterator: Iterator[(A, B)] = mapIterator
40 | override def keysIterator: Iterator[A] = mapKeyIterator
41 | override def valuesIterator: Iterator[B] = mapValueIterator
42 | override def foreach[U](f: ((A, B)) => U): Unit = singleMapForeach(f)
43 |
44 | override def clear(): Unit = { root() = TxnHashTrie.emptyMapNode[A, B] }
45 |
46 | //// TMap.View per-element
47 |
48 | override def contains(key: A): Boolean = singleContains(key)
49 | override def apply(key: A): B = singleGetOrThrow(key)
50 | def get(key: A): Option[B] = singleGet(key)
51 |
52 | override def put(key: A, value: B): Option[B] = singlePut(key, value)
53 | override def update(key: A, value: B): Unit = singlePut(key, value)
54 | override def += (kv: (A, B)): this.type = { singlePut(kv._1, kv._2) ; this }
55 |
56 | override def remove(key: A): Option[B] = singleRemove(key)
57 | override def -= (key: A): this.type = { singleRemove(key) ; this }
58 |
59 | //// optimized TMap versions
60 |
61 | def isEmpty(implicit txn: InTxn): Boolean = txnIsEmpty
62 | def size(implicit txn: InTxn): Int = singleSize
63 | def foreach[U](f: ((A, B)) => U)(implicit txn: InTxn): Unit = txnMapForeach(f)
64 |
65 | def contains(key: A)(implicit txn: InTxn): Boolean = txnContains(key)
66 | def apply(key: A)(implicit txn: InTxn): B = txnGetOrThrow(key)
67 | def get(key: A)(implicit txn: InTxn): Option[B] = txnGet(key)
68 | def put(key: A, value: B)(implicit txn: InTxn): Option[B] = txnPut(key, value)
69 | def remove(key: A)(implicit txn: InTxn): Option[B] = txnRemove(key)
70 |
71 | def transform(f: (A, B) => B)(implicit txn: InTxn): this.type = { single transform f ; this }
72 | def retain(p: (A, B) => Boolean)(implicit txn: InTxn): this.type = { single retain p ; this }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/skel/HashTrieTSet.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package skel
5 |
6 | import scala.collection.mutable
7 | import scala.concurrent.stm.skel.TxnHashTrie.SetBuildingNode
8 |
9 | private[stm] object HashTrieTSet {
10 |
11 | def empty[A]: TSet[A] = new HashTrieTSet(Ref(TxnHashTrie.emptySetNode[A]).single)
12 |
13 | def newBuilder[A]: mutable.Builder[A, TSet[A]] = new mutable.Builder[A, TSet[A]] {
14 | var root: SetBuildingNode[A] = TxnHashTrie.emptySetBuildingNode[A]
15 |
16 | def clear(): Unit = { root = TxnHashTrie.emptySetBuildingNode[A] }
17 |
18 | def += (elem: A): this.type = { root = TxnHashTrie.buildingAdd(root, elem) ; this }
19 |
20 | def result(): TSet[A] = {
21 | val r = root
22 | root = null
23 | new HashTrieTSet(Ref(r.endBuild).single)
24 | }
25 | }
26 | }
27 |
28 | private[skel] class HashTrieTSet[A] private (root0: Ref.View[TxnHashTrie.SetNode[A]]
29 | ) extends TxnHashTrie[A, AnyRef](root0) with TSetViaClone[A] {
30 |
31 | //// construction
32 |
33 | override def empty: TSet.View[A] = new HashTrieTSet(Ref(TxnHashTrie.emptySetNode[A]).single)
34 | override def clone: HashTrieTSet[A] = new HashTrieTSet(cloneRoot)
35 |
36 | //// TSet.View aggregates
37 |
38 | override def isEmpty: Boolean = singleIsEmpty
39 | override def size: Int = singleSize
40 | override def iterator: Iterator[A] = setIterator
41 | override def foreach[U](f: A => U): Unit = { singleSetForeach(f) }
42 | override def clear(): Unit = { root() = TxnHashTrie.emptySetNode[A] }
43 |
44 | //// TSet.View per-element
45 |
46 | def contains(elem: A): Boolean = singleContains(elem)
47 |
48 | override def add(elem: A): Boolean = singlePut(elem, null).isEmpty
49 | def += (elem: A): this.type = { singlePut(elem, null) ; this }
50 |
51 | override def remove(elem: A): Boolean = singleRemove(elem).isDefined
52 | def -= (elem: A): this.type = { singleRemove(elem) ; this }
53 |
54 | //// optimized TSet versions
55 |
56 | def isEmpty(implicit txn: InTxn): Boolean = txnIsEmpty
57 | def size(implicit txn: InTxn): Int = singleSize
58 | def foreach[U](f: A => U)(implicit txn: InTxn): Unit = txnSetForeach(f)
59 |
60 | def contains(elem: A)(implicit txn: InTxn): Boolean = txnContains(elem)
61 | def add(elem: A)(implicit txn: InTxn): Boolean = txnPut(elem, null ).isEmpty
62 | def remove(elem: A)(implicit txn: InTxn): Boolean = txnRemove(elem).isDefined
63 |
64 | def retain(p: (A) => Boolean)(implicit txn: InTxn): this.type = { single retain p ; this }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/skel/RollbackError.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm.skel
4 |
5 | import util.control.ControlThrowable
6 |
7 |
8 | /** A reusable exception instance, thrown by CCSTM when a transaction is doomed
9 | * and should not continue executing. User code should either rethrow this
10 | * exception or not catch it.
11 | *
12 | * @author Nathan Bronson
13 | */
14 | private[stm] object RollbackError extends Error with ControlThrowable {
15 | override def fillInStackTrace(): Throwable = this
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/skel/SimpleRandom.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm.skel
4 |
5 |
6 | /** A random number generator that focuses on speed and lack of inter-thread
7 | * interference, rather than on the quality of the numbers returned. The
8 | * `object SimpleRandom` is striped internally to reduce
9 | * contention when accessed from multiple threads. The `class
10 | * SimpleRandom` should only be used by a single thread.
11 | *
12 | * The constants in this 64-bit linear congruential random number generator
13 | * are from http://nuclear.llnl.gov/CNP/rng/rngman/node4.html.
14 | *
15 | * @author Nathan Bronson
16 | */
17 | object SimpleRandom {
18 | // 64 byte cache lines are typical, so there are 8 slots per cache line.
19 | // This means that the probability that any two threads have false sharing is
20 | // p = 8 / #slots. If there are n processors, each of which is running 1
21 | // thread, then the probability that no other threads have false sharing with
22 | // the current thread is (1-p)^(n-1). If p is small, that is about
23 | // 1 - (n-1)p, which is pretty close to 1 - np. If we want the probability
24 | // of false conflict for a thread to be less than k, then we need np < k, or
25 | // p < k/n, or 8/Slots < k/n, or #slots > 8n/k. If we let k = 1/8, then we
26 | // get #slots=64*n.
27 | private val mask = {
28 | val min = 64 * Runtime.getRuntime.availableProcessors
29 | var slots = 1
30 | while (slots < min) slots *= 2
31 | slots - 1
32 | }
33 |
34 | private val states = Array.tabulate(mask + 1)({ _ * 0x123456789abcdefL })
35 |
36 | /** Returns a random value chosen from a uniform distribution of all valid
37 | * `Int`s.
38 | */
39 | def nextInt(): Int = {
40 | val id = (Thread.currentThread.getId.asInstanceOf[Int] * 13) & mask
41 |
42 | val next = step(states(id))
43 | states(id) = next
44 |
45 | extract(next)
46 | }
47 |
48 | /** Returns a random value chosen from a uniform distribution of the
49 | * non-negative integers less than `n`, or throws `IllegalArgumentException`
50 | * if `n` is negative or zero.
51 | */
52 | def nextInt(n: Int): Int = {
53 | if (n <= 0)
54 | throw new IllegalArgumentException
55 |
56 | var x = -1
57 | while (x == -1) x = tryClamp(nextInt(), n)
58 | x
59 | }
60 |
61 | private def step(x: Long) = x * 2862933555777941757L + 3037000493L
62 |
63 | private def extract(x: Long) = (x >> 30).asInstanceOf[Int]
64 |
65 | /** r is the random, returns -1 on failure. */
66 | private def tryClamp(r: Int, n: Int): Int = {
67 | // get a positive number
68 | val x = r & Int.MaxValue
69 |
70 | if ((n & -n) == n) {
71 | // for powers of two, we use high bits instead of low bits
72 | ((x.toLong * n) >> 31).toInt
73 | } else {
74 | val z = x % n
75 | if (x - z + (n - 1) < 0) {
76 | // x is bigger than floor(MAX_INT/n)*n, so we are not getting an even
77 | // distribution. Try again.
78 | -1
79 | } else {
80 | z
81 | }
82 | }
83 | }
84 | }
85 |
86 | /** An clonable unsynchronized random number generator that uses the same
87 | * algorithm as the concurrent `object SimpleRandom`. The caller must ensure
88 | * that each `SimpleRandom` instance is used from only one thread at a time.
89 | *
90 | * @author Nathan Bronson
91 | */
92 | class SimpleRandom private (private var _state: Long, dummy: Boolean) {
93 | import SimpleRandom._
94 |
95 | def this(seed: Int) = this(SimpleRandom.step(SimpleRandom.step(seed)), false)
96 | def this() = this(System.identityHashCode(Thread.currentThread))
97 |
98 | override def clone = new SimpleRandom(_state, false)
99 |
100 | /** Returns a random value chosen from a uniform distribution of all valid
101 | * `Int`s.
102 | */
103 | def nextInt(): Int = {
104 | _state = step(_state)
105 | extract(_state)
106 | }
107 |
108 | /** Returns a random value chosen from a uniform distribution of the
109 | * non-negative integers less than `n`, or throws `IllegalArgumentException`
110 | * if `n` is negative or zero.
111 | */
112 | def nextInt(n: Int): Int = {
113 | if (n <= 0)
114 | throw new IllegalArgumentException
115 |
116 | var x = -1
117 | while (x == -1) x = tryClamp(nextInt(), n)
118 | x
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/skel/StubInTxn.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package skel
5 |
6 | class StubInTxn extends InTxn {
7 | import concurrent.stm.Txn._
8 |
9 | // TODO: Return types should probably not be refined from Unit to Nothing
10 |
11 | def executor: TxnExecutor = throw new AbstractMethodError
12 | def status: Status = throw new AbstractMethodError
13 | def rootLevel: NestingLevel = throw new AbstractMethodError
14 | def currentLevel: NestingLevel = throw new AbstractMethodError
15 | def rollback(cause: RollbackCause): Nothing = throw new AbstractMethodError
16 | def retry(): Nothing = throw new AbstractMethodError
17 | def retryFor(timeoutNanos: Long): Unit = { throw new AbstractMethodError }
18 | def beforeCommit(handler: InTxn => Unit): Nothing = throw new AbstractMethodError
19 | def whilePreparing(handler: InTxnEnd => Unit): Nothing = throw new AbstractMethodError
20 | def whileCommitting(handler: InTxnEnd => Unit): Nothing = throw new AbstractMethodError
21 | def afterCommit(handler: Status => Unit): Nothing = throw new AbstractMethodError
22 | def afterRollback(handler: Status => Unit): Nothing = throw new AbstractMethodError
23 | def afterCompletion(handler: Status => Unit): Nothing = throw new AbstractMethodError
24 | def setExternalDecider(decider: ExternalDecider): Nothing = throw new AbstractMethodError
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/skel/StubSTMImpl.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package skel
5 |
6 | import java.util.concurrent.TimeUnit
7 |
8 | import scala.collection.mutable
9 | import scala.reflect.ClassTag
10 |
11 | class StubSTMImpl extends impl.STMImpl {
12 |
13 | //////// RefFactory
14 |
15 | def newRef(v0: Boolean) : Ref[Boolean] = throw new AbstractMethodError
16 | def newRef(v0: Byte) : Ref[Byte] = throw new AbstractMethodError
17 | def newRef(v0: Short) : Ref[Short] = throw new AbstractMethodError
18 | def newRef(v0: Char) : Ref[Char] = throw new AbstractMethodError
19 | def newRef(v0: Int) : Ref[Int] = throw new AbstractMethodError
20 | def newRef(v0: Float) : Ref[Float] = throw new AbstractMethodError
21 | def newRef(v0: Long) : Ref[Long] = throw new AbstractMethodError
22 | def newRef(v0: Double) : Ref[Double] = throw new AbstractMethodError
23 | def newRef(v0: Unit) : Ref[Unit] = throw new AbstractMethodError
24 | def newRef[A : ClassTag](v0: A): Ref[A] = throw new AbstractMethodError
25 |
26 | def newTxnLocal[A](init: => A,
27 | initialValue: InTxn => A,
28 | beforeCommit: InTxn => Unit,
29 | whilePreparing: InTxnEnd => Unit,
30 | whileCommitting: InTxnEnd => Unit,
31 | afterCommit: A => Unit,
32 | afterRollback: Txn.Status => Unit,
33 | afterCompletion: Txn.Status => Unit): TxnLocal[A] = throw new AbstractMethodError
34 |
35 | def newTArray[A : ClassTag](length: Int): TArray[A] = throw new AbstractMethodError
36 | def newTArray[A : ClassTag](xs: TraversableOnce[A]): TArray[A] = throw new AbstractMethodError
37 |
38 | def newTMap[A, B]: TMap[A, B] = throw new AbstractMethodError
39 | def newTMapBuilder[A, B]: mutable.Builder[(A, B), TMap[A, B]] = throw new AbstractMethodError
40 |
41 | def newTSet[A]: TSet[A] = throw new AbstractMethodError
42 | def newTSetBuilder[A]: mutable.Builder[A, TSet[A]] = throw new AbstractMethodError
43 |
44 | //////// TxnContext
45 |
46 | def findCurrent(implicit mt: MaybeTxn): Option[InTxn] = throw new AbstractMethodError
47 | def dynCurrentOrNull: InTxn = throw new AbstractMethodError
48 |
49 | //////// TxnExecutor
50 |
51 | def apply[Z](block: InTxn => Z)(implicit mt: MaybeTxn): Z = throw new AbstractMethodError
52 | def oneOf[Z](blocks: (InTxn => Z)*)(implicit mt: MaybeTxn): Z = throw new AbstractMethodError
53 | def unrecorded[Z](block: InTxn => Z, outerFailure: Txn.RollbackCause => Z)(implicit mt: MaybeTxn): Z = throw new AbstractMethodError
54 | def pushAlternative[Z](mt: MaybeTxn, block: InTxn => Z): Boolean = throw new AbstractMethodError
55 | def compareAndSet[A, B](a: Ref[A], a0: A, a1: A, b: Ref[B], b0: B, b1: B): Boolean = throw new AbstractMethodError
56 | def compareAndSetIdentity[A <: AnyRef, B <: AnyRef](a: Ref[A], a0: A, a1: A, b: Ref[B], b0: B, b1: B): Boolean = throw new AbstractMethodError
57 | def retryTimeoutNanos: Option[Long] = throw new AbstractMethodError
58 | def withRetryTimeoutNanos(timeout: Option[Long]): TxnExecutor = throw new AbstractMethodError
59 | def isControlFlow(x: Throwable): Boolean = throw new AbstractMethodError
60 | def withControlFlowRecognizer(pf: PartialFunction[Throwable, Boolean]): TxnExecutor = throw new AbstractMethodError
61 | def postDecisionFailureHandler: (Txn.Status, Throwable) => Unit = throw new AbstractMethodError
62 | def withPostDecisionFailureHandler(handler: (Txn.Status, Throwable) => Unit): TxnExecutor = throw new AbstractMethodError
63 |
64 | //////// STMImpl
65 |
66 | def newCommitBarrier(timeout: Long, unit: TimeUnit): CommitBarrier = throw new AbstractMethodError
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/skel/TMapViaClone.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package skel
5 |
6 | import scala.collection.{immutable, mutable}
7 |
8 | private[stm] object TMapViaClone {
9 | class FrozenMutableMap[A, B](self: mutable.Map[A, B]) extends immutable.Map[A, B] {
10 | override def isEmpty: Boolean = self.isEmpty
11 | override def size: Int = self.size
12 | def get(key: A): Option[B] = self.get(key)
13 | def iterator: Iterator[(A, B)] = self.iterator
14 | override def foreach[U](f: ((A, B)) => U): Unit = { self foreach f }
15 | def + [B1 >: B](kv: (A, B1)): immutable.Map[A, B1] =
16 | new FrozenMutableMap(self.clone().asInstanceOf[mutable.Map[A, B1]] += kv)
17 | def - (k: A): immutable.Map[A, B] = new FrozenMutableMap(self.clone() -= k)
18 | }
19 | }
20 |
21 | /** Provides an implementation for the bulk of the functionality of `TMap` and
22 | * `TMap.View` by making extensive use of `clone()`. Assumes that the
23 | * underlying implementation of `clone()` is O(1).
24 | *
25 | * @author Nathan Bronson
26 | */
27 | private[stm] trait TMapViaClone[A, B] extends TMap.View[A, B] with TMap[A, B] {
28 | import TMapViaClone._
29 |
30 | // Implementations may be able to do better.
31 | override def snapshot: immutable.Map[A, B] = new FrozenMutableMap(clone())
32 |
33 | def tmap: TMap[A, B] = this
34 | def single: TMap.View[A, B] = this
35 |
36 | /** Something like `"TMap[size=1]((1 -> 10))"`, stopping after 1K chars */
37 | def dbgStr: String = atomic.unrecorded({ _ =>
38 | mkStringPrefix("TMap", single.view.map { kv => kv._1 + " -> " + kv._2 })
39 | }, { _.toString })
40 |
41 | /** Returns an array of key/value pairs, since that is likely to be the
42 | * easiest to examine in a debugger. Also, this avoids problems with
43 | * relying on copy-on-write after discarding `Ref` writes.
44 | */
45 | def dbgValue: Any = atomic.unrecorded({ _ => toArray }, { x => x })
46 |
47 | //////////// builder functionality (from mutable.MapLike via TMap.View)
48 |
49 | override protected[this] def newBuilder: TMap.View[A, B] = empty
50 |
51 | override def result(): TMap.View[A, B] = this
52 |
53 |
54 | //////////// construction of new TMaps
55 |
56 | // A cheap clone means that mutable.MapLike's implementations of +, ++,
57 | // -, and -- are all pretty reasonable.
58 |
59 | override def clone: TMap.View[A, B]
60 |
61 | //////////// atomic compound ops
62 |
63 | override def getOrElseUpdate(key: A, op: => B): B = {
64 | single.get(key) getOrElse {
65 | atomic { implicit txn =>
66 | tmap.get(key) getOrElse { val v = op ; tmap.put(key, v) ; v }
67 | }
68 | }
69 | }
70 |
71 | override def transform(f: (A, B) => B): this.type = {
72 | atomic { implicit txn =>
73 | for (kv <- tmap)
74 | tmap.update(kv._1, f(kv._1, kv._2))
75 | }
76 | this
77 | }
78 |
79 | override def retain(p: (A, B) => Boolean): this.type = {
80 | atomic { implicit txn =>
81 | for (kv <- tmap)
82 | if (!p(kv._1, kv._2))
83 | tmap -= kv._1
84 | }
85 | this
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/scala/scala/concurrent/stm/skel/TSetViaClone.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package skel
5 |
6 | import scala.collection.{immutable, mutable}
7 |
8 | private[stm] object TSetViaClone {
9 | class FrozenMutableSet[A](self: mutable.Set[A]) extends immutable.Set[A] {
10 | override def isEmpty: Boolean = self.isEmpty
11 | override def size: Int = self.size
12 | def contains(key: A): Boolean = self.contains(key)
13 | def iterator: Iterator[(A)] = self.iterator
14 | override def foreach[U](f: A => U): Unit = { self foreach f }
15 | def + (x: A): immutable.Set[A] = new FrozenMutableSet(self.clone() += x)
16 | def - (x: A): immutable.Set[A] = new FrozenMutableSet(self.clone() -= x)
17 | }
18 | }
19 |
20 | /** Provides an implementation for the bulk of the functionality of `TSet` and
21 | * `TSet.View` by making extensive use of `clone()`. Assumes that the
22 | * underlying implementation of `clone()` is O(1).
23 | *
24 | * @author Nathan Bronson
25 | */
26 | private[stm] trait TSetViaClone[A] extends TSet.View[A] with TSet[A] {
27 | import TSetViaClone._
28 |
29 | // Implementations may be able to do better.
30 | override def snapshot: immutable.Set[A] = new FrozenMutableSet(clone())
31 |
32 | def tset: TSet[A] = this
33 | def single: TSet.View[A] = this
34 |
35 | /** Something like `"TSet[size=3](3, 1, 10)"`, stopping after 1K chars */
36 | def dbgStr: String = atomic.unrecorded({ _ => mkStringPrefix("TSet", single) }, { _.toString })
37 |
38 | /** Returns an array of elements, since that is likely to be the
39 | * easiest to examine in a debugger. Also, this avoids problems with
40 | * relying on copy-on-write after discarding `Ref` writes.
41 | */
42 | def dbgValue: Any = atomic.unrecorded({ _ => toArray[Any] }, { x => x })
43 |
44 | //////////// builder functionality (from mutable.SetLike via TSet.View)
45 |
46 | override protected[this] def newBuilder: TSet.View[A] = empty
47 |
48 | override def result(): TSet.View[A] = this
49 |
50 |
51 | //////////// construction of new TSets
52 |
53 | // A cheap clone() means that mutable.SetLike's implementations of +, ++,
54 | // -, and -- are all pretty reasonable.
55 |
56 | override def clone: TSet.View[A]
57 |
58 | //////////// atomic compound ops
59 |
60 | override def retain(p: A => Boolean): Unit = {
61 | atomic { implicit txn =>
62 | for (x <- tset)
63 | if (!p(x))
64 | tset -= x
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/test/java/scala/concurrent/stm/JavaAPITests.java:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm;
4 |
5 | import static org.junit.Assert.*;
6 | import org.junit.Test;
7 |
8 | import static scala.concurrent.stm.japi.STM.*;
9 |
10 | import java.util.concurrent.Callable;
11 |
12 | import java.util.Map;
13 | import java.util.Set;
14 | import java.util.List;
15 |
16 | public class JavaAPITests {
17 | @Test
18 | public void createIntegerRef() {
19 | Ref.View ref = newRef(0);
20 | int unboxed = ref.get();
21 | assertEquals(0, unboxed);
22 | }
23 |
24 | @Test
25 | public void atomicWithRunnable() {
26 | final Ref.View ref = newRef(0);
27 | atomic(new Runnable() {
28 | public void run() {
29 | ref.set(10);
30 | }
31 | });
32 | int value = ref.get();
33 | assertEquals(10, value);
34 | }
35 |
36 | @Test
37 | public void atomicWithCallable() {
38 | final Ref.View ref = newRef(0);
39 | int oldValue = atomic(new Callable() {
40 | public Integer call() {
41 | return ref.swap(10);
42 | }
43 | });
44 | assertEquals(0, oldValue);
45 | int newValue = ref.get();
46 | assertEquals(10, newValue);
47 | }
48 |
49 | @Test(expected = TestException.class)
50 | public void failingTransaction() {
51 | final Ref.View ref = newRef(0);
52 | try {
53 | atomic(new Runnable() {
54 | public void run() {
55 | ref.set(10);
56 | throw new TestException();
57 | }
58 | });
59 | } catch (TestException e) {
60 | int value = ref.get();
61 | assertEquals(0, value);
62 | throw e;
63 | }
64 | }
65 |
66 | @Test
67 | public void transformInteger() {
68 | Ref.View ref = newRef(0);
69 | transform(ref, new Transformer() {
70 | public Integer apply(Integer i) {
71 | return i + 10;
72 | }
73 | });
74 | int value = ref.get();
75 | assertEquals(10, value);
76 | }
77 |
78 | @Test
79 | public void getAndTransformInteger() {
80 | Ref.View ref = newRef(0);
81 | int value = getAndTransform(ref, new Transformer() {
82 | public Integer apply(Integer i) {
83 | return i + 10;
84 | }
85 | });
86 | assertEquals(0, value);
87 | }
88 |
89 | @Test
90 | public void transformAndGetInteger() {
91 | Ref.View ref = newRef(0);
92 | int value = transformAndGet(ref, new Transformer() {
93 | public Integer apply(Integer i) {
94 | return i + 10;
95 | }
96 | });
97 | assertEquals(10, value);
98 | }
99 |
100 | @Test
101 | public void incrementInteger() {
102 | Ref.View ref = newRef(0);
103 | increment(ref, 10);
104 | int value = ref.get();
105 | assertEquals(10, value);
106 | }
107 |
108 | @Test
109 | public void incrementLong() {
110 | Ref.View ref = newRef(0L);
111 | increment(ref, 10L);
112 | long value = ref.get();
113 | assertEquals(10L, value);
114 | }
115 |
116 | @Test
117 | public void createAndUseTMap() {
118 | Map map = newMap();
119 | map.put(1, "one");
120 | map.put(2, "two");
121 | assertEquals("one", map.get(1));
122 | assertEquals("two", map.get(2));
123 | assertTrue(map.containsKey(2));
124 | map.remove(2);
125 | assertFalse(map.containsKey(2));
126 | }
127 |
128 | @Test(expected = TestException.class)
129 | public void failingTMapTransaction() {
130 | final Map map = newMap();
131 | try {
132 | atomic(new Runnable() {
133 | public void run() {
134 | map.put(1, "one");
135 | map.put(2, "two");
136 | assertTrue(map.containsKey(1));
137 | assertTrue(map.containsKey(2));
138 | throw new TestException();
139 | }
140 | });
141 | } catch (TestException e) {
142 | assertFalse(map.containsKey(1));
143 | assertFalse(map.containsKey(2));
144 | throw e;
145 | }
146 | }
147 |
148 | @Test
149 | public void createAndUseTSet() {
150 | Set set = newSet();
151 | set.add("one");
152 | set.add("two");
153 | assertTrue(set.contains("one"));
154 | assertTrue(set.contains("two"));
155 | assertEquals(2, set.size());
156 | set.add("one");
157 | assertEquals(2, set.size());
158 | set.remove("two");
159 | assertFalse(set.contains("two"));
160 | assertEquals(1, set.size());
161 | }
162 |
163 | @Test
164 | public void createAndUseTArray() {
165 | List list = newArrayAsList(3);
166 | assertEquals(null, list.get(0));
167 | assertEquals(null, list.get(1));
168 | assertEquals(null, list.get(2));
169 | list.set(0, "zero");
170 | list.set(1, "one");
171 | list.set(2, "two");
172 | assertEquals("zero", list.get(0));
173 | assertEquals("one", list.get(1));
174 | assertEquals("two", list.get(2));
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/src/test/java/scala/concurrent/stm/TestException.java:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm;
4 |
5 | public class TestException extends RuntimeException {
6 | public TestException() {
7 | super("Expected failure");
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/HistogramSuite.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | import java.util.concurrent.CyclicBarrier
6 |
7 | import org.scalatest.{FunSuite, Tag}
8 |
9 |
10 | class HistogramSuite extends FunSuite {
11 |
12 | for ((opsPerTest, name, slow) <- List((10000, "10K", false),
13 | (1000000, "1M", true))) {
14 | for (buckets <- List(1, 30, 10000)) {
15 | for (threads <- List(1, 2, 4, 8, 16, 32, 64, 128, 256, 512) if threads <= 2 * Runtime.getRuntime.availableProcessors) {
16 | for (useTArray <- List(false, true)) {
17 | val str = "" + buckets + " buckets, " + threads + " threads, " +
18 | (if (useTArray) "TArray[Int]" else "Array[Ref[Int]]")
19 | addTest("single-op-txn, " + str + ", " + name, Slow) {
20 | histogram(buckets, threads, opsPerTest / threads, useTArray, 100, 1)
21 | }
22 |
23 | for ((accesses, slow2) <- List((1, true), (3, false), (100, true))) {
24 | addTest("txn, " + str + ", " + accesses + " incr per txn, " + name, Slow) {
25 | histogram(buckets, threads, opsPerTest / threads, useTArray, 0, accesses)
26 | }
27 | val g = if (slow || slow2) List[Tag](Slow) else List.empty[Tag]
28 | addTest("mix, " + str + ", " + accesses + " incr per txn " + name, g:_*) {
29 | histogram(buckets, threads, opsPerTest / threads, useTArray, 50, accesses)
30 | }
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
37 | private def addTest(name: String, tags: Tag*)(block: => Unit): Unit =
38 | test(name, tags:_*)(block)
39 |
40 | def histogram(bucketCount: Int,
41 | workerCount: Int,
42 | samplesPerWorker: Int,
43 | useTArray: Boolean,
44 | singlePct: Int,
45 | samplesPerTxn: Int): Unit = {
46 |
47 | val buckets: IndexedSeq[Ref[Int]] = if (useTArray) {
48 | TArray.ofDim[Int](bucketCount).refs
49 | } else {
50 | Array.tabulate(bucketCount)({ _ => Ref(0) })
51 | }
52 | val threads = new Array[Thread](workerCount)
53 | val barrier = new CyclicBarrier(workerCount, new Runnable {
54 | var start = 0L
55 | def run(): Unit = {
56 | val now = System.nanoTime
57 | if (start == 0) {
58 | start = now
59 | } else {
60 | val elapsed = now - start
61 | println("hist(" + bucketCount + "," + workerCount + "," + samplesPerWorker + "," +
62 | useTArray + "," + singlePct +
63 | "," + samplesPerTxn + ") total_elapsed=" + elapsed + " nanos, throughput=" +
64 | (samplesPerWorker * workerCount * 1000000000L) / elapsed + " ops/sec, per_thread_latency=" +
65 | elapsed / samplesPerWorker + " nanos/op, avg_arrival=" +
66 | elapsed / (samplesPerWorker * workerCount) + " nanos/op")
67 | }
68 | }
69 | })
70 |
71 | for (worker <- 0 until workerCount) {
72 | val work = new Runnable {
73 | def run(): Unit = {
74 | barrier.await
75 | var i = 0
76 | while (i < samplesPerWorker) {
77 | if (math.abs(hash(i, worker) % 100) < singlePct) {
78 | if ((i % 2) == 0) {
79 | buckets(math.abs(hash(worker, i) % bucketCount)).single.transform(_ + 1)
80 | } else {
81 | val nt = buckets(math.abs(hash(worker, i) % bucketCount)).single
82 | var x = nt()
83 | while (!nt.compareAndSet(x, x + 1)) x = nt()
84 | }
85 | i += 1
86 | } else {
87 | atomic { implicit t =>
88 | var j = 0
89 | while (j < samplesPerTxn && i + j < samplesPerWorker) {
90 | val tv = buckets(math.abs(hash(worker, i + j) % bucketCount))
91 | //tv.getAndTransform(_ + 1)
92 | tv() = tv() + 1
93 | //tv() = tv.bind.readForWrite + 1
94 | j += 1
95 | }
96 | }
97 | i += samplesPerTxn
98 | }
99 | }
100 | barrier.await
101 | }
102 | }
103 | if (worker < workerCount - 1) {
104 | threads(worker) = new Thread(work, "worker " + worker)
105 | threads(worker).start()
106 | } else {
107 | work.run()
108 | }
109 | }
110 |
111 | for (worker <- 0 until workerCount - 1) threads(worker).join()
112 |
113 | val sum = buckets.map(_.single.get).sum
114 | assert(samplesPerWorker * workerCount === sum)
115 | }
116 |
117 | private def hash(i: Int, j: Int) = {
118 | var h = i * 37 + j * 101
119 | h ^= (h >>> 20) ^ (h >>> 12)
120 | h ^ (h >>> 7) ^ (h >>> 4)
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/InterruptSuite.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2016, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | import org.scalatest.{Tag, FunSuite}
6 | import skel.SimpleRandom
7 | import java.util.concurrent.atomic.AtomicInteger
8 |
9 | /** Verifies that blocking STM operations can be interrupted. */
10 | class InterruptSuite extends FunSuite {
11 |
12 |
13 | test("txn retry arriving interrupt") {
14 | delayedInterrupt(100)
15 | val x = Ref(0)
16 | intercept[InterruptedException] {
17 | atomic { implicit txn =>
18 | if (x() == 0) retry
19 | }
20 | }
21 | }
22 |
23 | test("txn retry pending interrupt") {
24 | Thread.currentThread.interrupt()
25 | val x = Ref(0)
26 | intercept[InterruptedException] {
27 | atomic { implicit txn =>
28 | if (x() == 0) retry
29 | }
30 | }
31 | }
32 |
33 | test("single await arriving interrupt") {
34 | delayedInterrupt(100)
35 | val x = Ref(0)
36 | intercept[InterruptedException] {
37 | x.single.await( _ != 0 )
38 | }
39 | }
40 |
41 | test("single await pending interrupt") {
42 | Thread.currentThread.interrupt()
43 | val x = Ref(0)
44 | intercept[InterruptedException] {
45 | x.single.await( _ != 0 )
46 | }
47 | }
48 |
49 | test("random interrupts during contention") {
50 | val refs = Array.tabulate(100)( _ => Ref(0) )
51 | val txnInterrupts = new AtomicInteger
52 | val nonTxnInterrupts = new AtomicInteger
53 | var failure = null : Throwable
54 | lazy val threads: Array[Thread] = Array.tabulate[Thread](10)( _ => new Thread {
55 | override def run(): Unit = {
56 | try {
57 | for (_ <- 0 until 10000) {
58 | try {
59 | atomic { implicit txn =>
60 | for (r <- refs) r() = r() + 1
61 | }
62 | } catch {
63 | case _: InterruptedException => txnInterrupts.incrementAndGet
64 | }
65 | for (r <- refs) {
66 | try {
67 | r.single += 1
68 | } catch {
69 | case _: InterruptedException => nonTxnInterrupts.incrementAndGet
70 | }
71 | }
72 | threads(SimpleRandom.nextInt(threads.length)).interrupt()
73 | }
74 | } catch {
75 | case x: Throwable => failure = x
76 | }
77 | }
78 | })
79 | for (t <- threads) t.start()
80 | for (t <- threads) t.join()
81 | if (failure != null)
82 | throw failure
83 | println(txnInterrupts.get + " txn rollbacks, " + nonTxnInterrupts.get + " non-txn interrupts")
84 | }
85 |
86 | //////// machinery for InterruptSuite
87 |
88 | private val pendingInterrupts = new ThreadLocal[List[Thread]] {
89 | override def initialValue: List[Thread] = Nil
90 | }
91 |
92 | override protected def test(testName: String, testTags: Tag*)(f: => Any)(implicit pos: org.scalactic.source.Position): Unit = {
93 | super.test(testName, testTags: _*) {
94 | // we have to use another thread, because sbt overrides .interrupt() on
95 | // its worker threads
96 | var failure = null : Throwable
97 | val t = new Thread {
98 | override def run(): Unit = {
99 | try {
100 | f
101 | } catch {
102 | case x: Throwable => failure = x
103 | } finally {
104 | while (pendingInterrupts.get.nonEmpty) {
105 | try {
106 | pendingInterrupts.get.head.join()
107 | pendingInterrupts.set(pendingInterrupts.get.tail)
108 | } catch {
109 | case _: Throwable =>
110 | }
111 | }
112 | Thread.interrupted
113 | }
114 | }
115 | }
116 | t.start()
117 | t.join()
118 | if (failure != null)
119 | throw failure
120 | }
121 | }
122 |
123 | private def delayedInterrupt(delay: Long): Unit = { delayedInterrupt(Thread.currentThread, delay) }
124 |
125 | private def delayedInterrupt(target: Thread, delay: Long): Unit = {
126 | val t = new Thread {
127 | override def run(): Unit = {
128 | Thread.sleep(delay)
129 | target.interrupt()
130 | }
131 | }
132 | pendingInterrupts.set(t :: pendingInterrupts.get)
133 | t.start()
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/JavaAPISuite.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | import org.scalatest.junit.JUnitWrapperSuite
6 |
7 | class JavaAPISuite extends JUnitWrapperSuite("scala.concurrent.stm.JavaAPITests", Thread.currentThread.getContextClassLoader)
8 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/MaybeTxnSuite.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | import org.scalatest.FunSuite
6 |
7 | class MaybeTxnSuite extends FunSuite {
8 | test("implicit InTxn match") {
9 | implicit val txn: InTxn = new skel.StubInTxn
10 |
11 | assert(implicitly[MaybeTxn] eq txn)
12 | }
13 |
14 | test("implicit TxnUnknown match") {
15 | assert(implicitly[MaybeTxn] eq TxnUnknown)
16 | }
17 |
18 | test("TxnUnknown is found") {
19 | assert(context eq TxnUnknown)
20 | }
21 |
22 | test("InTxn is found") {
23 | atomic { t0 =>
24 | implicit val t: InTxn = t0
25 | assert(context eq t)
26 | }
27 | atomic { implicit t =>
28 | assert(context eq t)
29 | }
30 | }
31 |
32 | private def context(implicit mt: MaybeTxn) = mt
33 |
34 | test("Static nesting lookup") {
35 | val x = Ref(10)
36 | atomic { implicit t =>
37 | assert(x() === 10)
38 | x() = 11
39 | atomic { implicit t =>
40 | assert(x() === 11)
41 | x() = 12
42 | atomic { implicit t =>
43 | assert(x() === 12)
44 | x() = 13
45 | }
46 | assert(x() === 13)
47 | }
48 | assert(x() === 13)
49 | }
50 | assert(x.single() === 13)
51 | }
52 |
53 | test("Dynamic nesting lookup") {
54 | val x = Ref(10)
55 | val xs = x.single
56 | def loop(expected: Int): Unit = {
57 | atomic { implicit t =>
58 | assert(x() === expected)
59 | assert(xs() === expected)
60 | x() = expected + 1
61 | if (expected < 100)
62 | loop(expected + 1)
63 | assert(x() === 101)
64 | }
65 | }
66 | loop(10)
67 | assert(xs() === 101)
68 | assert(x.single() === 101)
69 | }
70 |
71 | test("Static vs dynamic lookup") {
72 | implicit var t0: InTxn = null
73 | val n0 = atomic { t =>
74 | t0 = t
75 | assert(Txn.findCurrent === Some(t))
76 | assert(impl.STMImpl.instance.findCurrent === Some(t))
77 | NestingLevel.root
78 | }
79 | assert(n0.status === Txn.Committed)
80 | assert(Txn.findCurrent === None)
81 | assert(impl.STMImpl.instance.findCurrent === None)
82 | atomic { t =>
83 | assert(NestingLevel.current(t) != n0)
84 | assert(NestingLevel.root(t).status === Txn.Active)
85 | assert(Txn.status === Txn.Active)
86 | assert(Txn.findCurrent === Some(t))
87 | assert(impl.STMImpl.instance.findCurrent === Some(t))
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/RelaxedValidationSuite.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | import java.util.concurrent.CountDownLatch
6 |
7 | import org.scalatest.FunSuite
8 |
9 |
10 | /** Tests of the relaxed validation methods `getWith` and `relaxedGet` in
11 | * multi-threaded contexts. Single-threaded tests are found in
12 | * `IsolatedRefSuite` and more multi-threaded tests are embedded in
13 | * `FlipperSuite`.
14 | */
15 | class RelaxedValidationSuite extends FunSuite {
16 |
17 | test("self-write vs getWith") {
18 | val x = Ref(0)
19 | atomic { implicit txn =>
20 | assert(x.getWith { _ & 1 } === 0)
21 | x() = 1
22 | }
23 | assert(x.single() === 1)
24 | }
25 |
26 | test("self-write vs getWith with interference") {
27 | val x = Ref(0)
28 | val b1 = new CountDownLatch(1)
29 | val b2 = new CountDownLatch(1)
30 |
31 | new Thread {
32 | override def run(): Unit = {
33 | b1.await()
34 | x.single() = 2
35 | b2.countDown()
36 | }
37 | }.start()
38 |
39 | atomic { implicit txn =>
40 | assert(x.getWith { _ & 1 } === 0)
41 | b1.countDown()
42 | b2.await()
43 | assert(x.swap(1) === 2)
44 | }
45 | assert(x.single() === 1)
46 | }
47 |
48 | test("getWith multiple revalidations") {
49 | val x = Ref("abc")
50 |
51 | // sleep is okay for this test because all interleavings should pass
52 |
53 | new Thread {
54 | override def run(): Unit = {
55 | for (_ <- 0 until 10) {
56 | Thread.sleep(10)
57 | x.single.transform {
58 | _ + "X"
59 | }
60 | }
61 | x.single() = ""
62 | }
63 | }.start()
64 |
65 | assert(atomic { implicit txn =>
66 | for (_ <- 0 until 10) {
67 | x.getWith { _.take(3) }
68 | Thread.sleep(15)
69 | }
70 | x.getWith { _.take(3) }
71 | } === "")
72 | }
73 |
74 | test("self-write vs failing transformIfDefined") {
75 | val x = Ref(0)
76 | atomic { implicit txn =>
77 | assert(!x.transformIfDefined {
78 | case 1 => 2
79 | })
80 | x() = 1
81 | }
82 | assert(x.single() === 1)
83 | }
84 |
85 | test("self-write vs failing transformIfDefined with interference") {
86 | val x = Ref(0)
87 | val b1 = new CountDownLatch(1)
88 | val b2 = new CountDownLatch(1)
89 |
90 | new Thread {
91 | override def run(): Unit = {
92 | b1.await()
93 | x.single() = 2
94 | b2.countDown()
95 | }
96 | }.start()
97 |
98 | atomic { implicit txn =>
99 | assert(!x.transformIfDefined {
100 | case v if (v & 1) != 0 => v
101 | })
102 | b1.countDown()
103 | b2.await()
104 | assert(x.swap(1) === 2)
105 | }
106 | assert(x.single() === 1)
107 | }
108 |
109 | test("self-write vs relaxedGet") {
110 | val x = Ref(0)
111 | atomic { implicit txn =>
112 | assert(x.relaxedGet( _ == _ ) === 0)
113 | x() = 1
114 | }
115 | assert(x.single() === 1)
116 | }
117 |
118 | test("self-write vs relaxedGet with interference") {
119 | val x = Ref(0)
120 | val b1 = new CountDownLatch(1)
121 | val b2 = new CountDownLatch(1)
122 |
123 | new Thread {
124 | override def run(): Unit = {
125 | b1.await()
126 | x.single() = 2
127 | b2.countDown()
128 | }
129 | }.start()
130 |
131 | atomic { implicit txn =>
132 | assert(x.relaxedGet({ (seen, correct) => (seen & 1) == (correct & 1) }) === 0)
133 | b1.countDown()
134 | b2.await()
135 | assert(x.swap(1) === 2)
136 | }
137 | assert(x.single() === 1)
138 | }
139 |
140 | test("relaxedGet multiple accepting revalidations") {
141 | val x = Ref("abc")
142 |
143 | // sleep is okay for this test because all interleavings should pass
144 |
145 | new Thread {
146 | override def run(): Unit = {
147 | for (_ <- 0 until 10) {
148 | Thread.sleep(10)
149 | x.single.transform {
150 | _ + "X"
151 | }
152 | }
153 | x.single() = ""
154 | }
155 | }.start()
156 |
157 | val (first, last) = atomic { implicit txn =>
158 | val first = x.relaxedGet( (_, _) => true )
159 | for (_ <- 0 until 10) {
160 | x.relaxedGet( (_, _) => true )
161 | Thread.sleep(15)
162 | }
163 | (first, x.relaxedGet( (_, _) => true ))
164 | }
165 | assert(first === "abc")
166 | assert(last === "")
167 | }
168 |
169 | test("relaxedGet multiple ending with equality check") {
170 | val x = Ref("abc")
171 |
172 | // sleep is okay for this test because all interleavings should pass
173 |
174 | new Thread {
175 | override def run(): Unit = {
176 | for (_ <- 0 until 10) {
177 | Thread.sleep(10)
178 | x.single.transform {
179 | _ + "X"
180 | }
181 | }
182 | x.single() = ""
183 | }
184 | }.start()
185 |
186 | val (first, last) = atomic { implicit txn =>
187 | val first = x.relaxedGet( _.isEmpty == _.isEmpty )
188 | for (_ <- 0 until 10) {
189 | x.relaxedGet( _.isEmpty == _.isEmpty )
190 | Thread.sleep(15)
191 | }
192 | (first, x.relaxedGet( _ == _ ))
193 | }
194 | assert(first === "")
195 | assert(last === "")
196 | }
197 | }
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/Slow.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | import org.scalatest.Tag
6 |
7 | object Slow extends Tag("slow")
8 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/TokenRingSuite.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | import java.util.concurrent.CyclicBarrier
6 |
7 | import org.scalatest.FunSuite
8 |
9 |
10 | /** This test uses the transactional retry mechanism to pass a token around a
11 | * ring of threads. When there are two threads this is a ping-pong test. A
12 | * separate `Ref` is used for each handoff.
13 | *
14 | * @author Nathan Bronson
15 | */
16 | class TokenRingSuite extends FunSuite {
17 | test("small non-txn threesome") { tokenRing(3, 10000, false, false) }
18 | test("small txn threesome") { tokenRing(3, 1000, true, false) }
19 | test("small txn threesome reading via write") { tokenRing(3, 1000, true, true) }
20 |
21 | test("non-txn ping-pong", Slow) { tokenRing(2, 1000000, false, false) }
22 | test("non-txn threesome", Slow) { tokenRing(3, 1000000, false, false) }
23 | test("non-txn large ring", Slow) { tokenRing(32, 10000, false, false) }
24 | test("txn ping-pong", Slow) { tokenRing(2, 100000, true, false) }
25 | test("txn threesome", Slow) { tokenRing(3, 100000, true, false) }
26 | test("txn large ring", Slow) { tokenRing(32, 10000, true, false) }
27 | test("txn ping-pong reading via write", Slow) { tokenRing(2, 100000, true, true) }
28 | test("txn threesome reading via write", Slow) { tokenRing(3, 100000, true, true) }
29 | test("txn large ring reading via write", Slow) { tokenRing(32, 10000, true, true) }
30 |
31 | def tokenRing(ringSize: Int, handoffsPerThread: Int, useTxns: Boolean, useSwap: Boolean): Unit = {
32 | val ready = Array.tabulate(ringSize)(i => Ref(i == 0))
33 | val threads = new Array[Thread](ringSize - 1)
34 | val barrier = new CyclicBarrier(ringSize, new Runnable {
35 | var start = 0L
36 | def run(): Unit = {
37 | val now = System.currentTimeMillis
38 | if (start == 0) {
39 | start = now
40 | } else {
41 | val elapsed = now - start
42 | val handoffs = handoffsPerThread * ringSize
43 | println("tokenRing(" + ringSize + "," + handoffsPerThread + "," + useTxns +
44 | ") total_elapsed=" + elapsed + " msec, throughput=" +
45 | (handoffs * 1000L) / elapsed + " handoffs/sec, latency=" +
46 | (elapsed * 1000000L) / handoffs + " nanos/handoff")
47 | }
48 | }
49 | })
50 |
51 | for (index <- 0 until ringSize) {
52 | val work = new Runnable {
53 | def run(): Unit = {
54 | val next = (index + 1) % ringSize
55 | barrier.await
56 | for (_ <- 0 until handoffsPerThread) {
57 | if (!useTxns) {
58 | ready(index).single await { _ == true }
59 | ready(index).single() = false
60 | ready(next).single() = true
61 | } else {
62 | atomic { implicit t =>
63 | if (!useSwap) {
64 | if (ready(index).get == false) retry
65 | ready(index)() = false
66 | } else {
67 | if (ready(index).swap(false) == false) retry
68 | }
69 | ready(next)() = true
70 | }
71 | }
72 | }
73 | barrier.await
74 | }
75 | }
76 | if (index < ringSize - 1) {
77 | threads(index) = new Thread(work, "worker " + index)
78 | threads(index).start()
79 | } else {
80 | work.run()
81 | }
82 | }
83 |
84 | for (t <- threads) t.join()
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/UnrecordedTxnSuite.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2012, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | import org.scalatest.FunSuite
6 | import java.util.concurrent.CountDownLatch
7 |
8 |
9 | class UnrecordedTxnSuite extends FunSuite {
10 |
11 | test("fixed unrecorded txn") {
12 | val z = atomic.unrecorded { implicit txn => "foo" }
13 | assert(z === "foo")
14 | }
15 |
16 | test("nested fixed unrecorded txn") {
17 | val x = Ref(0)
18 | val z = atomic { implicit txn =>
19 | x() = 1
20 | atomic.unrecorded { implicit txn => "foo" }
21 | }
22 | assert(z === "foo")
23 | }
24 |
25 | test("writing unrecorded txn") {
26 | val x = Ref(0)
27 | val z = atomic.unrecorded { implicit txn =>
28 | x() = 1
29 | "foo"
30 | }
31 | assert(z === "foo")
32 | assert(x.single() === 0)
33 | }
34 |
35 | test("nested unrecorded txn") {
36 | val x = Ref(0)
37 | val z = atomic.unrecorded { implicit txn =>
38 | x += 1
39 | atomic.unrecorded { implicit txn =>
40 | x += 1
41 | atomic.unrecorded { implicit txn =>
42 | x += 1
43 | atomic.unrecorded { implicit txn =>
44 | x += 1
45 | atomic.unrecorded { implicit txn =>
46 | x += 1
47 | atomic.unrecorded { implicit txn =>
48 | x += 1
49 | atomic.unrecorded { implicit txn =>
50 | x()
51 | }
52 | }
53 | }
54 | }
55 | }
56 | }
57 | }
58 | assert(z === 6)
59 | assert(x.single() === 0)
60 | }
61 |
62 | test("nested new write unrecorded txn") {
63 | val x = Ref(0)
64 | val z = atomic { implicit txn =>
65 | atomic.unrecorded { implicit txn =>
66 | x() = 1
67 | "foo"
68 | }
69 | }
70 | assert(x.single() === 0)
71 | assert(z === "foo")
72 | }
73 |
74 | test("nested update unrecorded txn") {
75 | val x = Ref(0)
76 | val z = atomic { implicit txn =>
77 | x() = 1
78 | atomic.unrecorded { implicit txn =>
79 | x() = 2
80 | "foo"
81 | }
82 | }
83 | assert(x.single() === 1)
84 | assert(z === "foo")
85 | }
86 |
87 | test("nested preceding unrecorded txn") {
88 | val x = Ref(0)
89 | val z = atomic { implicit txn =>
90 | val z = atomic.unrecorded { implicit txn =>
91 | x() = 2
92 | "foo"
93 | }
94 | x() = 1
95 | z
96 | }
97 | assert(x.single() === 1)
98 | assert(z === "foo")
99 | }
100 |
101 | test("read set emptied") {
102 | val b = new CountDownLatch(1)
103 | val e = new CountDownLatch(1)
104 |
105 | val x = Ref(0)
106 |
107 | new Thread {
108 | override def run(): Unit = {
109 | b.await()
110 | x.single() = 1
111 | e.countDown()
112 | }
113 | }.start()
114 |
115 | var tries = 0
116 | val (z1, z2) = atomic { implicit txn =>
117 | tries += 1
118 | val z1 = atomic.unrecorded { implicit txn => x() }
119 | b.countDown()
120 | e.await()
121 | (z1, x())
122 | }
123 |
124 | assert(z1 === 0)
125 | assert(z2 === 1)
126 | assert(tries === 1)
127 | }
128 |
129 | class TestException extends Exception
130 |
131 | test("outerFailure handler") {
132 |
133 | val x = Ref(0)
134 |
135 | var z: Any = null
136 | intercept[TestException] {
137 | atomic { implicit txn =>
138 | val level = NestingLevel.root
139 | val done = new CountDownLatch(1)
140 | new Thread {
141 | override def run(): Unit = {
142 | level.requestRollback(Txn.UncaughtExceptionCause(new TestException))
143 | done.countDown()
144 | }
145 | }.start()
146 | done.await()
147 |
148 | z = atomic.unrecorded({ implicit txn => x() }, { cause => cause })
149 | }
150 | }
151 |
152 | assert(z.isInstanceOf[Txn.UncaughtExceptionCause])
153 | }
154 | }
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/WriteSkewSuite.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2016, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 |
5 | import org.scalatest.FunSuite
6 |
7 |
8 | class WriteSkewSuite extends FunSuite {
9 | val IncrCount = 1000000
10 |
11 | test("write skew test 1K") { runTest(1000) }
12 | test("write skew test 1M", Slow) { runTest(1000000) }
13 |
14 | def runTest(incrCount: Int): Unit = {
15 | // Two threads, each of which increments its own Ref if the other Ref is
16 | // even. Neither thread should ever observe that both Refs are odd.
17 | // MVCC STMs will require the addition of something like Clojure's "ensure"
18 | // or SQL's "select for update" to avoid the write skew.
19 | val refs = Array(Ref(0), Ref(0))
20 | val threads = new Array[Thread](2)
21 |
22 | @volatile var failure: Throwable = null
23 | for (id <- 0 to 1) {
24 | threads(id) = new Thread("write skew #" + id) {
25 | val self = refs(id)
26 | val other = refs(1 - id)
27 |
28 | override def run(): Unit = {
29 | try {
30 | for (_ <- 0 until incrCount) {
31 | if (null != failure)
32 | return
33 | atomic { implicit t =>
34 | if ((other() % 2) != 0) {
35 | if ((self() % 2) != 0)
36 | fail("refs=" + refs.map(_.get))
37 | retry
38 | }
39 | self() = self() + 1
40 | }
41 | }
42 | } catch {
43 | case x: Throwable =>
44 | if (null == failure)
45 | failure = x
46 | }
47 | }
48 | }
49 | }
50 |
51 | val begin = System.currentTimeMillis
52 | for (t <- threads) t.start()
53 | for (t <- threads) t.join()
54 |
55 | if (null != failure)
56 | throw failure
57 | val elapsed = System.currentTimeMillis - begin
58 | println("writeSkew(" + (2 * incrCount) + "): " + elapsed + " millis total")
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/examples/BasicSyntax.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package examples
5 |
6 | object BasicSyntax {
7 | case class Point(x: Int, y: Int)
8 | val origin = Point(0, 0)
9 |
10 | val top = Ref(origin)
11 | val bottom = Ref(origin)
12 | val left = Ref(origin)
13 | val right = Ref(origin)
14 |
15 | def updateExtremes(p: Point): Unit = {
16 | atomic { implicit txn =>
17 | if (p.x < left().x)
18 | left() = p
19 | if (p.x > right().x)
20 | right() = p
21 | if (p.y < top().y)
22 | top() = p
23 | if (p.y > bottom().y)
24 | bottom() = p
25 | }
26 | }
27 |
28 | def alternatives(): Unit = {
29 | val z = atomic { implicit txn =>
30 | if (!(left().x < -100))
31 | retry
32 | "left"
33 | } orAtomic { implicit txn =>
34 | if (!(right().x > +100))
35 | retry
36 | "right"
37 | } orAtomic { implicit txn =>
38 | if (!(top().y < -100))
39 | retry
40 | "top"
41 | } orAtomic { implicit txn =>
42 | if (!(bottom().y > +100))
43 | retry
44 | "bottom"
45 | }
46 | println("first out was " + z)
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/examples/ConcurrentIntList.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm.examples
4 |
5 | import scala.concurrent.stm._
6 |
7 | class ConcurrentIntList {
8 | private class Node(val elem: Int, prev0: Node, next0: Node) {
9 | val isHeader: Boolean = prev0 == null
10 | val prev = Ref(if (isHeader) this else prev0)
11 | val next = Ref(if (isHeader) this else next0)
12 | }
13 |
14 | private val header = new Node(-1, null, null)
15 |
16 | def addLast(elem: Int): Unit = {
17 | atomic { implicit txn =>
18 | val p = header.prev()
19 | val newNode = new Node(elem, p, header)
20 | p.next() = newNode
21 | header.prev() = newNode
22 | }
23 | }
24 |
25 | def addLast(e1: Int, e2: Int, elems: Int*): Unit = {
26 | atomic { implicit txn =>
27 | addLast(e1)
28 | addLast(e2)
29 | elems.foreach(addLast)
30 | }
31 | }
32 |
33 | //def isEmpty = atomic { implicit t => header.next() == header }
34 | def isEmpty: Boolean = header.next.single() == header
35 |
36 | def removeFirst(): Int = atomic { implicit txn =>
37 | val n = header.next()
38 | if (n == header)
39 | retry
40 | val nn = n.next()
41 | header.next() = nn
42 | nn.prev() = header
43 | n.elem
44 | }
45 |
46 | def maybeRemoveFirst(): Option[Int] = {
47 | atomic { implicit txn =>
48 | Some(removeFirst())
49 | } orAtomic { implicit txn =>
50 | None
51 | }
52 | }
53 |
54 | override def toString: String =
55 | atomic { implicit txn =>
56 | val buf = new StringBuilder("ConcurrentIntList(")
57 | var n = header.next()
58 | while (n != header) {
59 | buf ++= n.elem.toString
60 | n = n.next()
61 | if (n != header) buf ++= ","
62 | }
63 | buf.++=(")").toString
64 | }
65 | }
66 |
67 | object ConcurrentIntList {
68 | def select(stacks: ConcurrentIntList*): (ConcurrentIntList, Int) = {
69 | atomic { implicit txn =>
70 | for (s <- stacks) {
71 | s.maybeRemoveFirst() match {
72 | case Some(e) => return (s, e)
73 | case None =>
74 | }
75 | }
76 | retry
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/examples/DiningPhilosophers.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package examples
5 |
6 | /** See http://en.wikipedia.org/wiki/Dining_philosophers_problem
7 | * The STM solution is particularly straightforward because we can
8 | * simultaneously pick up two forks.
9 | */
10 | object DiningPhilosophers {
11 |
12 | class Fork {
13 | val inUse = Ref(false)
14 | }
15 |
16 | class PhilosopherThread(meals: Int, left: Fork, right: Fork) extends Thread {
17 | override def run(): Unit = {
18 | for (_ <- 0 until meals) {
19 | // THINK
20 | pickUpBothForks()
21 | // EAT
22 | putDown(left)
23 | putDown(right)
24 | }
25 | }
26 |
27 | def pickUpBothForks(): Unit = {
28 | atomic { implicit txn =>
29 | if (left.inUse() || right.inUse())
30 | retry
31 | left.inUse() = true
32 | right.inUse() = true
33 | }
34 | }
35 |
36 | def putDown(f: Fork): Unit = {
37 | f.inUse.single() = false
38 | }
39 | }
40 |
41 | def time(tableSize: Int, meals: Int): Long = {
42 | val forks = Array.tabulate(tableSize) { _ => new Fork }
43 | val threads = Array.tabulate(tableSize) { i => new PhilosopherThread(meals, forks(i), forks((i + 1) % tableSize)) }
44 | val start = System.currentTimeMillis
45 | for (t <- threads) t.start()
46 | for (t <- threads) t.join()
47 | System.currentTimeMillis - start
48 | }
49 |
50 | def main(args: Array[String]): Unit = {
51 | val meals = 100000
52 | for (_ <- 0 until 3) {
53 | val elapsed = time(5, meals)
54 | printf("%3.1f usec/meal\n", (elapsed * 1000.0) / meals)
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/examples/IndexedMap.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package examples
5 |
6 | import util.Random
7 |
8 | class IndexedMap[A, B] {
9 |
10 | private class Index[C](view: (A, B) => Iterable[C]) extends (C => Map[A, B]) {
11 | val mapping: TMap[C, Map[A, B]] = TMap.empty
12 |
13 | def apply(derived: C): Map[A, B] = mapping.single.getOrElse(derived, Map.empty[A, B])
14 |
15 | def += (kv: (A, B))(implicit txn: InTxn): Unit = {
16 | for (c <- view(kv._1, kv._2))
17 | mapping(c) = apply(c) + kv
18 | }
19 |
20 | def -= (kv: (A, B))(implicit txn: InTxn): Unit = {
21 | for (c <- view(kv._1, kv._2)) {
22 | val after = mapping(c) - kv._1
23 | if (after.isEmpty)
24 | mapping -= c
25 | else
26 | mapping(c) = after
27 | }
28 | }
29 | }
30 |
31 | private val contents = TMap.empty[A, B]
32 | private val indices = Ref(List.empty[Index[_]])
33 |
34 | def addIndex[C](view: (A, B) => Iterable[C]): (C => Map[A, B]) = atomic { implicit txn =>
35 | val index = new Index(view)
36 | indices() = index :: indices()
37 | contents foreach { index += _ }
38 | index
39 | }
40 |
41 | def get(key: A): Option[B] = contents.single.get(key)
42 |
43 | def put(key: A, value: B): Option[B] = atomic { implicit txn =>
44 | val prev = contents.put(key, value)
45 | for (p <- prev; i <- indices())
46 | i -= (key -> p)
47 | for (i <- indices())
48 | i += (key -> value)
49 | prev
50 | }
51 |
52 | def remove(key: A): Option[B] = atomic { implicit txn =>
53 | val prev = contents.remove(key)
54 | for (p <- prev; i <- indices())
55 | i -= (key -> p)
56 | prev
57 | }
58 | }
59 |
60 | object IndexedMap {
61 |
62 | case class User(id: Int, name: String, likes: Set[String])
63 |
64 | val users = new IndexedMap[Int, User]
65 | val byName: String => Map[Int, User] = users.addIndex { (_, u) => Some(u.name) }
66 | val byLike: String => Map[Int, User] = users.addIndex { (_, u) => u.likes }
67 |
68 | //////// data
69 |
70 | val topBabyNames = Array(
71 | "Ethan", "Isabella", "Jacob", "Olivia", "Noah", "Sophia", "Logan", "Emma", "Liam", "Ava", "Aiden", "Abigail",
72 | "Mason", "Chloe", "Jackson", "Madison", "Jack", "Ella", "Jayden", "Addison", "Ryan", "Emily", "Matthew", "Lily",
73 | "Lucas", "Mia", "Michael", "Avery", "Alexander", "Grace", "Nathan", "Hannah")
74 |
75 | val languages = Array("scala", "java", "C++", "haskell", "clojure", "python", "ruby", "pascal", "perl")
76 | val sports = Array("climbing", "cycling", "hiking", "football", "baseball", "underwater hockey")
77 |
78 | val numIDs = 1000
79 |
80 | //////// operations
81 |
82 | def pick[A](a: Array[A])(implicit rand: Random) = a(rand.nextInt(a.length))
83 |
84 | def newName(id: Int)(implicit rand: Random): Unit = {
85 | atomic { implicit txn =>
86 | val before = users.get(id).getOrElse(User(id, "John Doe", Set.empty))
87 | val after = before copy (name = pick(topBabyNames))
88 | users.put(id, after)
89 | }
90 | }
91 |
92 | def newLikes(id: Int)(implicit rand: Random): Unit = {
93 | atomic { implicit txn =>
94 | val before = users.get(id).getOrElse(User(id, "John Doe", Set.empty))
95 | val after = before copy (likes = Set(pick(languages), pick(sports)))
96 | users.put(id, after)
97 | }
98 | }
99 |
100 | def randomOp()(implicit rand: Random): Unit = {
101 | val pct = rand.nextInt(100)
102 | if (pct < 10) {
103 | // query by ID
104 | users.get(rand.nextInt(numIDs))
105 | } else if (pct < 50) {
106 | // query by name
107 | byName(pick(topBabyNames))
108 | } else if (pct < 70) {
109 | // query by sport
110 | byLike(pick(sports))
111 | } else if (pct < 90) {
112 | // query by language
113 | byLike(pick(languages))
114 | } else if (pct < 95) {
115 | newName(rand.nextInt(numIDs))
116 | } else {
117 | newLikes(rand.nextInt(numIDs))
118 | }
119 | }
120 |
121 | def populate(): Unit = {
122 | implicit val rand: Random = new Random
123 | for (id <- 0 until numIDs) {
124 | newName(id)
125 | newLikes(id)
126 | }
127 | }
128 |
129 | def run(numThreads: Int, opsPerThread: Int): Long = {
130 | val threads = Array.tabulate(numThreads) { _ =>
131 | new Thread() {
132 | override def run(): Unit = {
133 | implicit val rand: Random = new Random
134 | for (_ <- 0 until opsPerThread) randomOp()
135 | }
136 | }
137 | }
138 | val begin = System.currentTimeMillis
139 | for (t <- threads) t.start()
140 | for (t <- threads) t.join()
141 | System.currentTimeMillis - begin
142 | }
143 |
144 | def main(args: Array[String]): Unit = {
145 | populate()
146 |
147 | for (_ <- 0 until 3; tc <- List(1, 2, 4, 8)) {
148 | val elapsed = run(tc, 1000000 / tc)
149 | printf("%d thread: %4.2f usec/op total throughput (90%% read)\n", tc, elapsed * 0.001)
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/examples/RealityShowPhilosophers.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package examples
5 |
6 | import annotation.tailrec
7 |
8 | /** This extends a solution to the dining philosopher's problem to include an
9 | * outside perspective that occasionally examines everything that is
10 | * happening.
11 | */
12 | object RealityShowPhilosophers {
13 |
14 | class Fork {
15 | val owner = Ref(None : Option[String])
16 | }
17 |
18 | class PhilosopherThread(val name: String, val meals: Int, left: Fork, right: Fork) extends Thread {
19 | val mealsEaten = Ref(0)
20 |
21 | override def run(): Unit = {
22 | for (_ <- 0 until meals) {
23 | // thinking
24 | atomic { implicit txn =>
25 | if (!(left.owner().isEmpty && right.owner().isEmpty))
26 | retry
27 | left.owner() = Some(name)
28 | right.owner() = Some(name)
29 | }
30 | // eating
31 | atomic { implicit txn =>
32 | mealsEaten += 1
33 | left.owner() = None
34 | right.owner() = None
35 | }
36 | }
37 | }
38 |
39 | def done: Boolean = mealsEaten.single() == meals
40 |
41 | override def toString: String =
42 | "%s is %5.2f%% done".format(name, mealsEaten.single() * 100.0 / meals)
43 | }
44 |
45 | class CameraThread(intervalMilli: Int, forks: Seq[Fork], philosophers: Seq[PhilosopherThread]) extends Thread {
46 |
47 | @tailrec final override def run(): Unit = {
48 | Thread.sleep(intervalMilli)
49 | val (str, done) = image
50 | println(str)
51 | if (!done)
52 | run()
53 | }
54 |
55 | /** We want to print exactly one image of the final state, so we check
56 | * completion at the same time as building the image.
57 | */
58 | def image: (String, Boolean) = atomic { implicit txn =>
59 | val buf = new StringBuilder
60 | for (i <- forks.indices)
61 | buf ++= "fork %d is owned by %s\n".format(i, forks(i).owner.single())
62 | var done = true
63 | for (p <- philosophers) {
64 | buf ++= p.toString += '\n'
65 | done &&= p.done
66 | }
67 | (buf.toString, done)
68 | }
69 | }
70 |
71 | def time(names: Seq[String], meals: Int): Long = {
72 | val forks = Array.tabulate(names.size) { _ => new Fork }
73 | val pthreads = Array.tabulate(names.size) { i => new PhilosopherThread(names(i), meals, forks(i), forks((i + 1) % forks.length)) }
74 | val camera = new CameraThread(1000 / 60, forks, pthreads)
75 | val start = System.currentTimeMillis
76 | camera.start()
77 | for (t <- pthreads) t.start()
78 | for (t <- pthreads) t.join()
79 | val elapsed = System.currentTimeMillis - start
80 | camera.join()
81 | elapsed
82 | }
83 |
84 | def main(args: Array[String]): Unit = {
85 | val meals = 100000
86 | for (_ <- 0 until 3) {
87 | val elapsed = time(List("Aristotle", "Hippocrates", "Plato", "Pythagoras", "Socrates"), meals)
88 | printf("%3.1f usec/meal\n", (elapsed * 1000.0) / meals)
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/examples/SyntaxCheatSheet.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm.examples
4 |
5 | object SyntaxCheatSheet {
6 | import scala.concurrent.stm._
7 |
8 | val x: Ref[Int] = Ref(0) // allocate a Ref[Int]
9 | val y: Ref[String] = Ref.make[String]() // type-specific default
10 | val z: Ref.View[Int] = x.single // Ref.View[Int]
11 |
12 | atomic { implicit txn =>
13 | val i = x() // read
14 | y() = "x was " + i // write
15 | val eq = atomic { implicit txn => // nested atomic
16 | x() == z() // both Ref and Ref.View can be used inside atomic
17 | }
18 | assert(eq)
19 | y.set(y.get + ", long-form access")
20 | }
21 |
22 | // only Ref.View can be used outside atomic
23 | println("y was '" + y.single() + "'")
24 | println("z was " + z())
25 |
26 | atomic { implicit txn =>
27 | y() = y() + ", first alternative"
28 | if (x getWith { _ > 0 }) // read via a function
29 | retry // try alternatives or block
30 | } orAtomic { implicit txn =>
31 | y() = y() + ", second alternative"
32 | }
33 |
34 | val prev: Int = z.swap(10) // atomic swap
35 | val success: Boolean = z.compareAndSet(10, 11) // atomic compare-and-set
36 | z.transform { _ max 20 } // atomic transformation
37 | val pre : String = y.single.getAndTransform { _.toUpperCase }
38 | val post: String = y.single.transformAndGet { _.filterNot { _ == ' ' } }
39 | }
40 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/impl/RefFactorySuite.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2010, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package impl
5 |
6 | import org.scalatest.FunSuite
7 |
8 | import scala.reflect.ClassTag
9 |
10 | class RefFactorySuite extends FunSuite {
11 |
12 | private case class Fact(expected: String) extends skel.StubSTMImpl with RefFactory {
13 |
14 | private def called(w: String) = {
15 | assert(w === expected)
16 | null
17 | }
18 |
19 | override def newRef(v0: Boolean): Ref[Boolean] = called("Boolean")
20 | override def newRef(v0: Byte): Ref[Byte] = called("Byte")
21 | override def newRef(v0: Short): Ref[Short] = called("Short")
22 | override def newRef(v0: Char): Ref[Char] = called("Char")
23 | override def newRef(v0: Int): Ref[Int] = called("Int")
24 | override def newRef(v0: Float): Ref[Float] = called("Float")
25 | override def newRef(v0: Long): Ref[Long] = called("Long")
26 | override def newRef(v0: Double): Ref[Double] = called("Double")
27 | override def newRef(v0: Unit): Ref[Unit] = called("Unit")
28 | override def newRef[T](v0: T)(implicit m: ClassTag[T]): Ref[T] = called("Any")
29 | }
30 |
31 | object TestRef extends RefCompanion {
32 | var factory: RefFactory = null
33 | }
34 |
35 | test("signature specialization") {
36 | TestRef.factory = Fact("Boolean")
37 | TestRef(false)
38 |
39 | TestRef.factory = Fact("Byte")
40 | TestRef(0 : Byte)
41 |
42 | TestRef.factory = Fact("Short")
43 | TestRef(0 : Short)
44 |
45 | TestRef.factory = Fact("Char")
46 | TestRef(0 : Char)
47 |
48 | TestRef.factory = Fact("Int")
49 | TestRef(0 : Int)
50 |
51 | TestRef.factory = Fact("Float")
52 | TestRef(0 : Float)
53 |
54 | TestRef.factory = Fact("Long")
55 | TestRef(0 : Long)
56 |
57 | TestRef.factory = Fact("Double")
58 | TestRef(0 : Double)
59 |
60 | TestRef.factory = Fact("Unit")
61 | TestRef(())
62 |
63 | TestRef.factory = Fact("Any")
64 | TestRef("abc")
65 | TestRef(null)
66 | TestRef(0.asInstanceOf[AnyRef])
67 | val x: Any = 0
68 | TestRef(x)
69 | }
70 |
71 | test("missing manifest TestRef.apply") {
72 | TestRef.factory = Fact("Any")
73 |
74 | def go[T](x: T) = TestRef(x)
75 |
76 | go(123)
77 | go(1.23)
78 | go(null)
79 | go("abc")
80 | }
81 |
82 | test("dynamic specialization") {
83 | def go[T : ClassTag](v0: T, which: String): Unit = {
84 | TestRef.factory = Fact(which)
85 | TestRef(v0)
86 | }
87 |
88 | go(false, "Boolean")
89 | go(0 : Byte, "Byte")
90 | go(0 : Short, "Short")
91 | go(0 : Char, "Char")
92 | go(0 : Int, "Int")
93 | go(0 : Float, "Float")
94 | go(0 : Long, "Long")
95 | go(0 : Double, "Double")
96 | go((), "Unit")
97 | go("abc", "Any")
98 | go(null, "Any")
99 | go(0.asInstanceOf[AnyRef], "Any")
100 | val x: Any = 0
101 | go(x, "Any")
102 | }
103 |
104 | test("default value specialization") {
105 | def go[T : ClassTag](default: T, which: String): Unit = {
106 | TestRef.factory = Fact(which)
107 | TestRef.make[T]()
108 | //assert(x.single() == default)
109 | }
110 |
111 | go(false, "Boolean")
112 | go(0 : Byte, "Byte")
113 | go(0 : Short, "Short")
114 | go(0 : Char, "Char")
115 | go(0 : Int, "Int")
116 | go(0 : Float, "Float")
117 | go(0 : Long, "Long")
118 | go(0 : Double, "Double")
119 | go((), "Unit")
120 | go[String](null, "Any")
121 | go[AnyRef](null, "Any")
122 | go[Null](null, "Any")
123 | go[Any](null, "Any")
124 | }
125 |
126 | test("missing manifest TestRef.make") {
127 | TestRef.factory = Fact("Any")
128 |
129 | def go[T]() = TestRef.make[T]()
130 |
131 | go[Boolean]()
132 | go[Byte]()
133 | go[Short]()
134 | go[Char]()
135 | go[Int]()
136 | go[Float]()
137 | go[Long]()
138 | go[Double]()
139 | go[Unit]()
140 | go[String]()
141 | go[AnyRef]()
142 | go[Null]()
143 | go[Any]()
144 | }
145 |
146 | }
147 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/skel/AtomicArraySuite.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2014, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package skel
5 |
6 | import org.scalatest.FunSuite
7 |
8 | import scala.reflect.ClassTag
9 |
10 | class AtomicArraySuite extends FunSuite {
11 |
12 | test("Unit") {
13 | runIsolatedTest(List((), (), ()))
14 | }
15 |
16 | test("Boolean") {
17 | runIsolatedTest(List(false, true, false, true))
18 | }
19 |
20 | test("Byte") {
21 | runIsolatedTest(Array(0 : Byte, 1 : Byte, 2 : Byte))
22 | }
23 |
24 | test("Short") {
25 | runIsolatedTest(List(0 : Short, 1 : Short, 2 : Short))
26 | }
27 |
28 | test("Char") {
29 | runIsolatedTest("abcdefg".toSeq)
30 | }
31 |
32 | test("Int") {
33 | runIsolatedTest(100 until 200)
34 | }
35 |
36 | test("Float") {
37 | runIsolatedTest((20 to 30) map { _ * 0.1f })
38 | }
39 |
40 | test("Long") {
41 | runIsolatedTest((100 until 200) map { _ - 100L * Int.MaxValue })
42 | }
43 |
44 | test("Double") {
45 | runIsolatedTest((10 until 20) map { math.exp(_) })
46 | }
47 |
48 | test("AnyRef") {
49 | runIsolatedTest((10 until 20) map { i => "x" + i : AnyRef })
50 | }
51 |
52 | test("Any") {
53 | runIsolatedTest[Any]((10 until 20) map { i => i : Any })
54 | }
55 |
56 | def runIsolatedTest[A](values: Seq[A])(implicit am: ClassTag[A]): Unit = {
57 | val singleton = AtomicArray[A](1)
58 | if (am != implicitly[ClassTag[Unit]])
59 | assert(singleton(0) === am.newArray(1)(0))
60 |
61 | val aa = AtomicArray(values)
62 | for (i <- 0 until aa.length)
63 | assert(values(i) === aa(i))
64 | for (i <- 0 until aa.length)
65 | aa(i) = values(aa.length - 1 - i)
66 | for (i <- 0 until aa.length)
67 | assert(aa(i) === values(aa.length - 1 - i))
68 | for (i <- 0 until aa.length) {
69 | if (aa(i) == values(0)) {
70 | assert(aa.compareAndSet(i, values(0), values(i)))
71 | assert(aa(i) === values(i))
72 | } else {
73 | assert(!aa.compareAndSet(i, values(0), values(i)))
74 | assert(aa(i) === values(aa.length - 1 - i))
75 | }
76 | }
77 | for (i <- 0 until aa.length)
78 | assert(aa(i) === aa.getAndTransform(i)( v => v ))
79 | for (i <- 0 until aa.length) {
80 | val prev = aa(i)
81 | assert(aa.swap(i, values(i)) === prev)
82 | }
83 |
84 | intercept[IndexOutOfBoundsException] {
85 | aa(-1)
86 | }
87 | intercept[IndexOutOfBoundsException] {
88 | aa(-1) = aa(0)
89 | }
90 | intercept[IndexOutOfBoundsException] {
91 | aa(aa.length)
92 | }
93 | intercept[IndexOutOfBoundsException] {
94 | aa(aa.length) = aa(0)
95 | }
96 | intercept[IndexOutOfBoundsException] {
97 | aa.compareAndSet(-1, aa(0), aa(0))
98 | }
99 | intercept[IndexOutOfBoundsException] {
100 | aa.compareAndSet(aa.length, aa(0), aa(0))
101 | }
102 | intercept[IndexOutOfBoundsException] {
103 | aa(Int.MinValue)
104 | }
105 | intercept[IndexOutOfBoundsException] {
106 | aa(Int.MaxValue)
107 | }
108 |
109 | val copy = aa.clone
110 | for (i <- 0 until aa.length)
111 | assert(copy(i) === aa(i))
112 |
113 | val str0 = aa map { _.toString }
114 | val str: AtomicArray[String] = str0
115 | for (i <- 0 until aa.length)
116 | assert(aa(i).toString === str(i))
117 |
118 | val seq0 = aa.toList
119 | for (i <- 0 until aa.length)
120 | assert(aa(i) == seq0(i))
121 |
122 | val seq1 = aa.iterator.toList
123 | for (i <- 0 until aa.length)
124 | assert(aa(i) == seq1(i))
125 |
126 | val bb = aa ++ seq0
127 | assert(bb.length === aa.length * 2)
128 | for (i <- 0 until aa.length) {
129 | assert(aa(i) === bb(i))
130 | assert(aa(i) === bb(i + aa.length))
131 | }
132 |
133 | assert(aa.toString.startsWith("AtomicArray"))
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/test/scala/scala/concurrent/stm/skel/SimpleRandomSuite.scala:
--------------------------------------------------------------------------------
1 | /* scala-stm - (c) 2009-2011, Stanford University, PPL */
2 |
3 | package scala.concurrent.stm
4 | package skel
5 |
6 | import org.scalatest.FunSuite
7 |
8 | class SimpleRandomSuite extends FunSuite {
9 |
10 | test("nextInt") {
11 | val f = new SimpleRandom
12 | var s = 0
13 | for (_ <- 0 until 100000) {
14 | s |= SimpleRandom.nextInt
15 | s |= f.nextInt
16 | }
17 | assert(s != 0)
18 | }
19 |
20 | test("nextInt(n) in range") {
21 | val f = new SimpleRandom
22 | val rand = new scala.util.Random
23 | for (_ <- 0 until 100000) {
24 | val n = rand.nextInt(Int.MaxValue - 1) + 1
25 | val gr = SimpleRandom.nextInt(n)
26 | assert(gr >= 0 && gr < n)
27 | val lr = f.nextInt(n)
28 | assert(lr >= 0 && lr < n)
29 | }
30 | }
31 |
32 | test("clone") {
33 | val f1 = new SimpleRandom
34 | for (_ <- 0 until 1000)
35 | f1.nextInt()
36 | val f2 = f1.clone
37 | for (_ <- 0 until 1000)
38 | assert(f1.nextInt(9999) === f2.nextInt(9999))
39 | }
40 |
41 | test("seeded") {
42 | val f1 = new SimpleRandom(100)
43 | val f2 = new SimpleRandom(100)
44 | for (_ <- 0 until 1000)
45 | assert(f1.nextInt === f2.nextInt)
46 | }
47 |
48 | test("global SimpleRandom distribution") {
49 | val buckets = new Array[Int](100)
50 | for (_ <- 0 until 100000)
51 | buckets(SimpleRandom.nextInt(buckets.length)) += 1
52 | for (b <- buckets)
53 | assert(b > 0)
54 | }
55 |
56 | test("local SimpleRandom distribution") {
57 | val f = new SimpleRandom
58 | val buckets = new Array[Int](100)
59 | for (_ <- 0 until 100000)
60 | buckets(f.nextInt(buckets.length)) += 1
61 | for (b <- buckets)
62 | assert(b > 0)
63 | }
64 | }
--------------------------------------------------------------------------------