1+ import scala .annotation .tailrec
2+
3+ import java .lang .management .ManagementFactory
4+ import java .lang .management .RuntimeMXBean
5+
6+ import scala .jdk .CollectionConverters ._
7+
8+ object Test {
9+ trait Txn {}
10+ type AtomicOp [Z ] = Txn ?=> Z
11+ type VanillaAtomicOp [Z ] = Txn => Z
12+
13+ object AbortAndRetry extends scala.util.control.ControlThrowable (" abort and retry" ) {}
14+
15+ def beginTxn : Txn = new Txn {}
16+
17+ @ tailrec def retryN [Z ](n: Int )(txn: AtomicOp [Z ], i: Int = 0 ): Z = {
18+ try {
19+ given Txn = beginTxn
20+ val ret : Z = txn
21+ if (i < n) { throw AbortAndRetry }
22+ ret
23+ } catch {
24+ case AbortAndRetry => retryN(n)(txn, i + 1 )
25+ }
26+ }
27+
28+ @ tailrec def safeRetryN [Z ](n: Int )(txn: VanillaAtomicOp [Z ], i: Int = 0 ): Z = {
29+ try {
30+ given Txn = beginTxn
31+ val ret : Z = txn.asInstanceOf [AtomicOp [Z ]]
32+ if (i < n) { throw AbortAndRetry }
33+ ret
34+ } catch {
35+ case AbortAndRetry => safeRetryN(n)(txn, i + 1 )
36+ }
37+ }
38+
39+ object StackSize {
40+ def unapply (arg: String ): Option [Int ] = {
41+ Option (arg match {
42+ case s " -Xss $rest" => rest
43+ case s " -XX:ThreadStackSize= $rest" => rest
44+ case _ => null
45+ }).map{ rest =>
46+ val shift = (rest.toLowerCase.last match {
47+ case 'k' => 10
48+ case 'm' => 20
49+ case 'g' => 30
50+ case _ => 0
51+ })
52+ (if (shift > 0 ) {
53+ rest.dropRight(1 )
54+ } else {
55+ rest
56+ }).toInt << shift
57+ }
58+ }
59+ }
60+ def main (args: Array [String ]) = {
61+ val arguments = ManagementFactory .getRuntimeMXBean.getInputArguments
62+ // 64bit VM defaults to 1024K / 1M stack size, 32bit defaults to 320k
63+ // Use 1024 as upper bound.
64+ val maxStackSize : Int = arguments.asScala.reverseIterator.collectFirst{case StackSize (stackSize) => stackSize}.getOrElse(1 << 20 )
65+
66+ val testTxn : VanillaAtomicOp [Boolean ] = {
67+ (txn: Txn ) =>
68+ given Txn = txn
69+ true
70+ }
71+
72+ Console .println(try {
73+ // maxStackSize is a good upper bound on linear stack growth
74+ // without assuming too much about frame size
75+ // (1 byte / frame is conservative even for a z80)
76+ retryN(maxStackSize)(testTxn.asInstanceOf [AtomicOp [Boolean ]]) == safeRetryN(maxStackSize)(testTxn)
77+ } catch {
78+ case e: StackOverflowError =>
79+ Console .println(s " Exploded after ${e.getStackTrace.length} frames " )
80+ false
81+ })
82+ }
83+ }
0 commit comments