From 3c60eb0a1b02d6abfa4dbfacc37bd29d08e5a820 Mon Sep 17 00:00:00 2001 From: Charles Beauvile Date: Mon, 20 Apr 2020 18:35:46 +0200 Subject: [PATCH] HW 7 done --- .../scala/actorbintree/BinaryTreeSet.scala | 99 ++++++++++++++++--- 1 file changed, 85 insertions(+), 14 deletions(-) diff --git a/src/main/scala/actorbintree/BinaryTreeSet.scala b/src/main/scala/actorbintree/BinaryTreeSet.scala index e1c210d..3c44e27 100644 --- a/src/main/scala/actorbintree/BinaryTreeSet.scala +++ b/src/main/scala/actorbintree/BinaryTreeSet.scala @@ -58,7 +58,7 @@ class BinaryTreeSet extends Actor { var root = createRoot - // optional (used to stash incoming operations during garbage collection) + // optional var pendingQueue = Queue.empty[Operation] // optional @@ -66,15 +66,30 @@ class BinaryTreeSet extends Actor { // optional /** Accepts `Operation` and `GC` messages. */ - val normal: Receive = { case _ => ??? } + val normal: Receive = { + case op:Operation => root ! op + case GC => { + val newRoot = createRoot; + root ! CopyTo(newRoot) + context.become(garbageCollecting(newRoot)) + } + } // optional /** Handles messages while garbage collection is performed. * `newRoot` is the root of the new binary tree where we want to copy * all non-removed elements into. */ - def garbageCollecting(newRoot: ActorRef): Receive = ??? - + def garbageCollecting(newRoot: ActorRef): Receive = { + case op:Operation => pendingQueue = pendingQueue.enqueue(op) + case CopyFinished => + pendingQueue.foreach(newRoot ! _) //foreach preserves order of a queue (same as dequeueing) + root ! PoisonPill //Will also stop all of its children + pendingQueue = Queue.empty + root = newRoot; + context.become(normal) + //Ignore GC messages here + } } object BinaryTreeNode { @@ -84,11 +99,6 @@ object BinaryTreeNode { case object Right extends Position case class CopyTo(treeNode: ActorRef) - /** - * Acknowledges that a copy has been completed. This message should be sent - * from a node to its parent, when this node and all its children nodes have - * finished being copied. - */ case object CopyFinished def props(elem: Int, initiallyRemoved: Boolean) = Props(classOf[BinaryTreeNode], elem, initiallyRemoved) @@ -104,15 +114,76 @@ class BinaryTreeNode(val elem: Int, initiallyRemoved: Boolean) extends Actor { // optional def receive = normal + def goDownTo(elem : Int) : Position = if(elem < this.elem) Left else Right // optional /** Handles `Operation` messages and `CopyTo` requests. */ - val normal: Receive = { case _ => ??? } + val normal: Receive = { + case Insert (requester, id, elem) => + if(elem == this.elem && !removed){ + requester ! OperationFinished(id) + }else{ + val nextPos = goDownTo(elem) + + subtrees get nextPos match{ + case Some(node) => node ! Insert(requester, id, elem) + case None => { + val newActorSubtree = (nextPos, context.actorOf(BinaryTreeNode.props(elem, false))) + subtrees = subtrees + newActorSubtree + requester ! OperationFinished(id); + } + } + } + case Contains(requester, id, elem) => + if(elem == this.elem && !removed) + requester ! ContainsResult(id, true) + else{ + //Need to search subtrees + subtrees get goDownTo(elem) match{ + case Some(node) => node ! Contains(requester, id, elem) + case None => requester ! ContainsResult(id, false) + } + } + + case Remove (requester, id, elem) => + if(elem == this.elem && !removed){ + removed = true + requester ! OperationFinished(id) + }else{ + subtrees get goDownTo(elem) match{ + case Some(node) => node ! Remove(requester, id, elem) + case None => requester ! OperationFinished(id) // (elem isn't in the tree) + } + } + + case CopyTo(newRoot) => + //We are already done, nothing to do + if(removed && subtrees.isEmpty) context.parent ! CopyFinished + else{ + if(!removed) newRoot ! Insert(self, elem, elem) + subtrees.values foreach(_ ! CopyTo(newRoot)) //Copy subtrees elems + //val insertConfirmed = if(removed) true else false, hence we can simply pass removed + context.become(copying(subtrees.values.toSet, removed)) + } + } // optional /** `expected` is the set of ActorRefs whose replies we are waiting for, * `insertConfirmed` tracks whether the copy of this node to the new tree has been confirmed. */ - def copying(expected: Set[ActorRef], insertConfirmed: Boolean): Receive = ??? - - -} + def copying(expected: Set[ActorRef], insertConfirmed: Boolean): Receive = { + //To catch the insert of this node into the new tree beeing finished + case OperationFinished(_) => { + if(expected.isEmpty) context.parent ! CopyFinished + else context.become(copying(expected, true)) + } + + case CopyFinished => { + val newExp = expected-sender + if(insertConfirmed && newExp.isEmpty){ + context.parent ! CopyFinished + }else{ + context.become(copying(newExp, insertConfirmed)) + } + } + } +} \ No newline at end of file