PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/orc/run/core/Token.scala

https://github.com/laurenyew/cOrcS
Scala | 616 lines | 405 code | 72 blank | 139 comment | 39 complexity | 872f45a9ee19413b637ad1b36a20407c MD5 | raw file
Possible License(s): BSD-3-Clause
  1. //
  2. // Token.scala -- Scala class Token
  3. // Project OrcScala
  4. //
  5. // $Id: Token.scala 2933 2011-12-15 16:26:02Z jthywissen $
  6. //
  7. // Created by dkitchin on Aug 12, 2011.
  8. //
  9. // Copyright (c) 2011 The University of Texas at Austin. All rights reserved.
  10. //
  11. // Use and redistribution of this file is governed by the license terms in
  12. // the LICENSE file found in the project's top-level directory and also found at
  13. // URL: http://orc.csres.utexas.edu/license.shtml .
  14. //
  15. package orc.run.core
  16. import orc.{ Schedulable, OrcRuntime, OrcEvent, CaughtEvent }
  17. import orc.ast.oil.nameless.{ Variable, UnboundVariable, Stop, Sequence, Prune, Parallel, Otherwise, Hole, HasType, Expression, Def, DeclareType, DeclareDefs, Constant, Call, Argument }
  18. import orc.error.runtime.{ TokenException, StackLimitReachedError, ArityMismatchException, ArgumentTypeMismatchException }
  19. import orc.error.OrcException
  20. import orc.lib.time.{ Vtime, Vclock, Vawait }
  21. import orc.util.BlockableMapExtension.addBlockableMapToList
  22. import orc.values.sites.TotalSite
  23. import orc.values.{ Signal, OrcRecord, Field }
  24. /** @author dkitchin
  25. */
  26. class Token protected (
  27. protected var node: Expression,
  28. protected var stack: Frame = EmptyFrame,
  29. protected var env: List[Binding] = Nil,
  30. protected var group: Group,
  31. protected var clock: Option[VirtualClock] = None,
  32. protected var state: TokenState = Live)
  33. extends GroupMember with Schedulable {
  34. var functionFramesPushed: Int = 0
  35. val runtime: OrcRuntime = group.runtime
  36. val options = group.root.options
  37. /** Execution of a token cannot indefinitely block the executing thread. */
  38. override val nonblocking = true
  39. /** Public constructor */
  40. def this(start: Expression, g: Group) = {
  41. this(node = start, group = g, stack = GroupFrame(EmptyFrame))
  42. }
  43. /** Copy constructor with defaults */
  44. private def copy(
  45. node: Expression = node,
  46. stack: Frame = stack,
  47. env: List[Binding] = env,
  48. group: Group = group,
  49. clock: Option[VirtualClock] = clock,
  50. state: TokenState = state): Token = {
  51. new Token(node, stack, env, group, clock, state)
  52. }
  53. /*
  54. * On creation: Add a token to its group if it is not halted or killed.
  55. */
  56. state match {
  57. case Publishing(_) | Live | Blocked(_) | Suspending(_) | Suspended(_) => group.add(this)
  58. case Halted | Killed => {}
  59. }
  60. /** Change this token's state.
  61. *
  62. * Return true if the token's state was successfully set
  63. * to the requested state.
  64. *
  65. * Return false if the token's state was already Killed.
  66. */
  67. protected def setState(newState: TokenState): Boolean = synchronized {
  68. /*
  69. * Make sure that the token has not been killed.
  70. * If it has been killed, return false immediately.
  71. */
  72. if (state != Killed) { state = newState; true } else false
  73. }
  74. //@volatile
  75. //var scheduledBy: Throwable = null //FIXME: Remove "scheduledBy" debug facility
  76. /* When a token is scheduled, notify its clock accordingly */
  77. override def onSchedule() {
  78. //scheduledBy = new Throwable("Task scheduled by")
  79. //if (runtime.asInstanceOf[OrcWithThreadPoolScheduler].executor.asInstanceOf[OrcThreadPoolExecutor].getQueue().contains(this) && !group.isKilled()) Console.err.println("Token scheduled, in queue, && group alive! state="+state+"\n"+scheduledBy.toString())
  80. clock foreach { _.unsetQuiescent() }
  81. super.onSchedule()
  82. }
  83. /* When a token is finished running, notify its clock accordingly */
  84. override def onComplete() {
  85. clock foreach { _.setQuiescent() }
  86. super.onComplete()
  87. }
  88. /** Pass an event to this token's enclosing group.
  89. *
  90. * This method is asynchronous:
  91. * it may be called from a thread other than
  92. * the thread currently running this token.
  93. */
  94. def notifyOrc(event: OrcEvent) { group.notifyOrc(event) }
  95. /** Kill this token.
  96. *
  97. * This method is asynchronous:
  98. * it may be called from a thread other than
  99. * the thread currently running this token.
  100. */
  101. def kill() {
  102. def collapseState(victimState: TokenState) {
  103. victimState match {
  104. case Suspending(s) => collapseState(s)
  105. case Suspended(s) => collapseState(s)
  106. case Blocked(handle: SiteCallHandle) => { handle.kill() }
  107. case Live | Publishing(_) | Blocked(_) | Halted | Killed => {}
  108. }
  109. }
  110. synchronized {
  111. collapseState(state)
  112. if (setState(Killed)) {
  113. /* group.remove(this) conceptually runs here, but as an optimization,
  114. * this is unnecessary. Note that the current Group.remove implementation
  115. * relies on this optimization for correctness of the tokenCount. */
  116. }
  117. }
  118. }
  119. /** Make this token block on some resource.
  120. *
  121. * This method is synchronous:
  122. * it must only be called from a thread that is currently
  123. * executing the run() method of this token.
  124. */
  125. def blockOn(blocker: Blocker) {
  126. state match {
  127. case Live => setState(Blocked(blocker))
  128. case Killed => {}
  129. case _ => throw new AssertionError("Only live tokens may be blocked: state=" + state)
  130. }
  131. }
  132. /** Unblock a token that is currently blocked on some resource.
  133. * Schedule the token to run.
  134. *
  135. * This method is synchronous:
  136. * it must only be called from a thread that is currently
  137. * executing the run() method of this token.
  138. */
  139. def unblock() {
  140. state match {
  141. case Blocked(_) => {
  142. if (setState(Live)) { schedule() }
  143. }
  144. case Suspending(Blocked(_: OtherwiseGroup)) => {
  145. if (setState(Suspending(Live))) { schedule() }
  146. }
  147. case Suspended(Blocked(_: OtherwiseGroup)) => {
  148. setState(Suspended(Live))
  149. }
  150. case Killed => {}
  151. case _ => { throw new AssertionError("unblock on a Token that is not Blocked/Killed: state=" + state) }
  152. }
  153. }
  154. /** Suspend the token in preparation for a program rewrite.
  155. *
  156. * This method is asynchronous:
  157. * it may be called from a thread other than
  158. * the thread currently running this token.
  159. */
  160. def suspend() {
  161. state match {
  162. case Live | Blocked(_) | Publishing(_) => setState(Suspending(state))
  163. case Suspending(_) | Suspended(_) | Halted | Killed => {}
  164. }
  165. }
  166. /** Resume the token from suspension after a program rewrite.
  167. *
  168. * This method is asynchronous:
  169. * it may be called from a thread other than
  170. * the thread currently running this token.
  171. */
  172. def resume() {
  173. state match {
  174. case Suspending(prevState) => setState(prevState)
  175. case Suspended(prevState) => {
  176. if (setState(prevState)) { schedule() }
  177. }
  178. case Publishing(_) | Live | Blocked(_) | Halted | Killed => {}
  179. }
  180. }
  181. def schedule() = runtime.schedule(this)
  182. protected def fork() = synchronized { (this, copy()) }
  183. def move(e: Expression) = { node = e; this }
  184. def jump(context: List[Binding]) = { env = context; this }
  185. protected def push(newStack: Frame) = {
  186. if (newStack.isInstanceOf[FunctionFrame]) {
  187. functionFramesPushed = functionFramesPushed + 1
  188. if (options.stackSize > 0 && functionFramesPushed > options.stackSize) {
  189. this !! new StackLimitReachedError(options.stackSize)
  190. }
  191. }
  192. stack = newStack
  193. this
  194. }
  195. /** Remove the top frame of this token's stack.
  196. *
  197. * This method is synchronous:
  198. * it must only be called from a thread that is currently
  199. * executing the run() method of this token.
  200. */
  201. def pop() = {
  202. if (stack.isInstanceOf[FunctionFrame]) {
  203. functionFramesPushed = functionFramesPushed - 1
  204. }
  205. stack = stack.asInstanceOf[CompositeFrame].previous
  206. }
  207. def getGroup(): Group = { group }
  208. def getNode(): Expression = { node }
  209. def getEnv(): List[Binding] = { env }
  210. def getStack(): Frame = { stack }
  211. def getClock(): Option[VirtualClock] = { clock }
  212. def migrate(newGroup: Group) = {
  213. require(newGroup != group)
  214. val oldGroup = group
  215. newGroup.add(this); oldGroup.remove(this)
  216. group = newGroup
  217. this
  218. }
  219. protected def join(newGroup: Group) = {
  220. push(GroupFrame(stack))
  221. migrate(newGroup)
  222. this
  223. }
  224. def bind(b: Binding) = {
  225. env = b :: env
  226. stack match {
  227. case BindingFrame(n, previous) => { stack = BindingFrame(n + 1, previous) }
  228. /* Tail call optimization (part 1 of 2) */
  229. case _: FunctionFrame if (!options.disableTailCallOpt) => { /* Do not push a binding frame over a tail call.*/ }
  230. case _ => { push(BindingFrame(1, stack)) }
  231. }
  232. this
  233. }
  234. def unbind(n: Int) = { env = env.drop(n); this }
  235. protected def lookup(a: Argument): Binding = {
  236. a match {
  237. case Constant(v) => BoundValue(v)
  238. case Variable(n) => env(n)
  239. case UnboundVariable(x) => BoundStop //TODO: Also report the presence of an unbound variable.
  240. }
  241. }
  242. /** Attempt to resolve a binding to a value.
  243. * When the binding resolves to v, call k(v).
  244. * (If it is already resolved, k is called immediately)
  245. *
  246. * If the binding resolves to a halt, halt this token.
  247. */
  248. protected def resolve(b: Binding)(k: AnyRef => Unit) {
  249. resolveOptional(b) {
  250. case Some(v) => k(v)
  251. case None => halt()
  252. }
  253. }
  254. /** Attempt to resolve a binding to a value.
  255. * When the binding resolves to v, call k(Some(v)).
  256. * (If it is already resolved, k is called immediately)
  257. *
  258. * If the binding resolves to a halt, call k(None).
  259. *
  260. * Note that resolving a closure also encloses its context.
  261. */
  262. protected def resolveOptional(b: Binding)(k: Option[AnyRef] => Unit): Unit = {
  263. b match {
  264. case BoundValue(v) =>
  265. v match {
  266. case c: Closure =>
  267. enclose(c.lexicalContext) { newContext: List[Binding] =>
  268. k(Some(Closure(c.defs, c.pos, newContext)))
  269. }
  270. case u => k(Some(u))
  271. }
  272. case BoundStop => k(None)
  273. case BoundFuture(g) => {
  274. push(FutureFrame(k, stack))
  275. g read this
  276. }
  277. }
  278. }
  279. /** Create a new Closure object whose lexical bindings are all resolved and replaced.
  280. * Such a closure will have no references to any group.
  281. * This object is then passed to the continuation.
  282. */
  283. protected def enclose(bs: List[Binding])(k: List[Binding] => Unit): Unit = {
  284. def resolveBound(b: Binding)(k: Binding => Unit) =
  285. resolveOptional(b) {
  286. case Some(v) => k(BoundValue(v))
  287. case None => k(BoundStop)
  288. }
  289. bs.blockableMap(resolveBound)(k)
  290. }
  291. protected def functionCall(d: Def, context: List[Binding], params: List[Binding]) {
  292. if (params.size != d.arity) {
  293. this !! new ArityMismatchException(d.arity, params.size) /* Arity mismatch. */
  294. } else {
  295. /* 1) If this is not a tail call, push a function frame referring to the current environment.
  296. * 2) Change the current environment to the closure's saved environment.
  297. * 3) Add bindings for the arguments to the new current environment.
  298. *
  299. * Caution: The ordering of these operations is very important;
  300. * do not permute them.
  301. */
  302. /* Tail call optimization (part 2 of 2) */
  303. /*
  304. * Push a new FunctionFrame
  305. * only if the call is not a tail call.
  306. */
  307. if (!stack.isInstanceOf[FunctionFrame] || options.disableTailCallOpt) {
  308. push(FunctionFrame(node, env, stack))
  309. }
  310. /* Jump into the function context */
  311. jump(context)
  312. /* Bind the args */
  313. for (p <- params) { bind(p) }
  314. /* Move into the function body */
  315. move(d.body)
  316. schedule()
  317. }
  318. }
  319. protected def clockCall(vc: VirtualClockOperation, actuals: List[AnyRef]): Unit = {
  320. (vc, actuals) match {
  321. case (`Vclock`, List(f)) => {
  322. f match {
  323. case totalf: TotalSite => {
  324. def ordering(x: AnyRef, y: AnyRef) = {
  325. // TODO: Add error handling, either here or in the scheduler.
  326. // A comparator error should kill the engine.
  327. val i = totalf.evaluate(List(x, y)).asInstanceOf[Int]
  328. assert(i == -1 || i == 0 || i == 1)
  329. i
  330. }
  331. clock = Some(new VirtualClock(clock, ordering, runtime))
  332. publish()
  333. }
  334. case _ => {
  335. this !! (new ArgumentTypeMismatchException(0, "TotalSite", f.toString()))
  336. }
  337. }
  338. }
  339. case (`Vawait`, List(t)) => {
  340. clock match {
  341. case Some(cl) => cl.await(this, t)
  342. case None => halt()
  343. }
  344. }
  345. case (`Vtime`, Nil) => {
  346. clock flatMap { _.now() } match {
  347. case Some(t) => publish(t)
  348. case None => halt()
  349. }
  350. }
  351. case _ => throw new ArityMismatchException(vc.arity, actuals.size)
  352. }
  353. }
  354. protected def siteCall(s: AnyRef, actuals: List[AnyRef]): Unit = {
  355. s match {
  356. case vc: VirtualClockOperation => {
  357. clockCall(vc, actuals)
  358. }
  359. case _ => {
  360. val sh = new SiteCallHandle(this, s, actuals)
  361. blockOn(sh)
  362. runtime.schedule(sh)
  363. }
  364. }
  365. }
  366. protected def makeCall(target: AnyRef, params: List[Binding]): Unit = {
  367. target match {
  368. case c: Closure => {
  369. functionCall(c.code, c.context, params)
  370. }
  371. /* I wish this didn't need a special case...
  372. * but if the record element is a closure,
  373. * it can't be handled by an invocation trait.
  374. * -dkitchin
  375. */
  376. case r @ OrcRecord(entries) if entries contains "apply" => {
  377. params.blockableMap(resolve) {
  378. case args @ List(Field(_)) => siteCall(r, args) // apply isn't allowed to supersede other member accesses
  379. case _ => makeCall(entries("apply"), params)
  380. }
  381. }
  382. case s => {
  383. params.blockableMap(resolve) { siteCall(s, _) }
  384. }
  385. }
  386. }
  387. def stackOK(testStack: Array[java.lang.StackTraceElement], offset: Int): Boolean =
  388. testStack.length == 4 + offset && testStack(1 + offset).getMethodName() == "runTask" ||
  389. testStack(1 + offset).getMethodName() == "eval" && testStack(2 + offset).getMethodName() == "run" && stackOK(testStack, offset + 2)
  390. def run() {
  391. //val ourStack = new Throwable("Entering Token.run").getStackTrace()
  392. //assert(stackOK(ourStack, 0), "Token run not in ThreadPoolExecutor.Worker! sl="+ourStack.length+", m1="+ourStack(1).getMethodName()+", state="+state)
  393. try {
  394. if (group.isKilled()) { kill() }
  395. state match {
  396. case Live => eval(node)
  397. case Suspending(prevState) => setState(Suspended(prevState))
  398. case Blocked(b) => b.check(this)
  399. case Publishing(v) => if (setState(Live)) { stack(this, v) }
  400. case Killed => {} // This token was killed while it was on the schedule queue; ignore it
  401. case Suspended(_) => throw new AssertionError("suspended token scheduled")
  402. case Halted => throw new AssertionError("halted token scheduled")
  403. }
  404. } catch {
  405. case e: OrcException => this !! e
  406. case e: InterruptedException => { halt(); Thread.currentThread().interrupt() } //Thread interrupt causes halt without notify
  407. case e => { notifyOrc(CaughtEvent(e)); halt() }
  408. }
  409. }
  410. protected def eval(node: orc.ast.oil.nameless.Expression) {
  411. node match {
  412. case Stop() => halt()
  413. case Hole(_, _) => halt()
  414. case (a: Argument) => resolve(lookup(a)) { publish }
  415. case Call(target, args, _) => {
  416. val params = args map lookup
  417. lookup(target) match {
  418. case BoundValue(c: Closure) => functionCall(c.code, c.context, params)
  419. case b => resolve(b) { makeCall(_, params) }
  420. }
  421. }
  422. case Parallel(left, right) => {
  423. val (l, r) = fork()
  424. l.move(left)
  425. r.move(right)
  426. runtime.schedule(l, r)
  427. }
  428. case Sequence(left, right) => {
  429. push(SequenceFrame(right, stack))
  430. move(left)
  431. schedule()
  432. }
  433. case Prune(left, right) => {
  434. val (l, r) = fork()
  435. val pg = new PruningGroup(group)
  436. l.bind(BoundFuture(pg))
  437. r.join(pg)
  438. l.move(left)
  439. r.move(right)
  440. runtime.schedule(l, r)
  441. }
  442. case Otherwise(left, right) => {
  443. val (l, r) = fork
  444. r.move(right)
  445. val region = new OtherwiseGroup(group, r)
  446. l.join(region)
  447. l.move(left)
  448. runtime.schedule(l)
  449. }
  450. case decldefs @ DeclareDefs(openvars, defs, body) => {
  451. /* Closure compaction: Bind only the free variables
  452. * of the defs in this lexical context.
  453. */
  454. val lexicalContext = openvars map { i: Int => lookup(Variable(i)) }
  455. for (i <- defs.indices) {
  456. bind(BoundValue(Closure(defs, i, lexicalContext)))
  457. }
  458. move(body)
  459. schedule()
  460. }
  461. case HasType(expr, _) => {
  462. move(expr)
  463. run()
  464. }
  465. case DeclareType(_, expr) => {
  466. move(expr)
  467. run()
  468. }
  469. }
  470. }
  471. def publish(v: AnyRef) {
  472. state match {
  473. case Blocked(_: OtherwiseGroup) => throw new AssertionError("publish on a pending Token")
  474. case Live | Blocked(_) => {
  475. setState(Publishing(v))
  476. schedule()
  477. }
  478. case Suspending(_) => {
  479. setState(Suspending(Publishing(v)))
  480. schedule()
  481. }
  482. case Suspended(_) => {
  483. setState(Suspended(Publishing(v)))
  484. }
  485. case Publishing(_) => throw new AssertionError("Already publishing!")
  486. case Halted | Killed => {}
  487. }
  488. }
  489. def publish() { publish(Signal) }
  490. def halt() {
  491. state match {
  492. case Publishing(_) | Live | Blocked(_) | Suspending(_) => {
  493. setState(Halted)
  494. group.halt(this)
  495. }
  496. case Suspended(_) => throw new AssertionError("halt on a suspended Token")
  497. case Halted | Killed => {}
  498. }
  499. }
  500. def !!(e: OrcException) {
  501. e.setPosition(node.pos)
  502. e match {
  503. case te: TokenException if (te.getBacktrace() == null || te.getBacktrace().length == 0) => {
  504. val callPoints = stack.toList collect { case f: FunctionFrame => f.callpoint.pos }
  505. te.setBacktrace(callPoints.toArray)
  506. }
  507. case _ => {} // Not a TokenException; no need to collect backtrace
  508. }
  509. notifyOrc(CaughtEvent(e))
  510. halt()
  511. }
  512. }
  513. /** */
  514. trait TokenState {
  515. val isLive: Boolean
  516. }
  517. /** Token is ready to make progress */
  518. case object Live extends TokenState {
  519. val isLive = true
  520. }
  521. /** Token is propagating a published value */
  522. case class Publishing(v: AnyRef) extends TokenState {
  523. val isLive = true
  524. }
  525. /** Token is waiting on another task */
  526. case class Blocked(blocker: Blocker) extends TokenState {
  527. val isLive = true
  528. }
  529. /** Token has been told to suspend, but it's still in the scheduler queue */
  530. case class Suspending(prevState: TokenState) extends TokenState {
  531. val isLive = prevState.isLive
  532. }
  533. /** Suspended Tokens must be re-scheduled upon resume */
  534. case class Suspended(prevState: TokenState) extends TokenState {
  535. val isLive = prevState.isLive
  536. }
  537. /** Token halted itself */
  538. case object Halted extends TokenState {
  539. val isLive = false
  540. }
  541. /** Token killed by engine */
  542. case object Killed extends TokenState {
  543. val isLive = false
  544. }