private void UpdateAckCounter(Ack ack)
        {
            if (ack.Message.GetType().IsSubclassOf(typeof(Request)))
            {
                Request request = (Request)ack.Message;
                AcksReceivedPerRequest.TryRemove(request.SeqNum, out var oldCounter);
                AcksReceivedPerRequest.TryAdd(request.SeqNum, ++oldCounter);
            }

            if (ack.Message.GetType() == typeof(TakeRemove))
            {
                TakeRemove takeRemove = (TakeRemove)ack.Message;
                AcksReceivedPerRequest.TryRemove(takeRemove.TakeRequestSeqNumber, out var oldCounter);
                AcksReceivedPerRequest.TryAdd(takeRemove.TakeRequestSeqNumber, ++oldCounter);
            }
        }
        public override Tuple Take(Tuple tuple)
        {
            int            timeStep      = 250; // ms
            List <Message> messages      = null;
            Message        resultMessage = null;
            List <Tuple>   intersection  = new List <Tuple>();
            Tuple          selectedTuple = new Tuple(new List <object>());

            // PHASE 1
            var takeRequest = new TakeRequest(ClientRequestSeqNumber, EndpointURL, tuple);

            // wait for all responses
            MulticastMessageWaitAll(View.Nodes, takeRequest);
            WaitMessage(takeRequest, View.Nodes);
            ClientRequestSeqNumber++;

            lock (ReplyResultQueue)
            {
                ReplyResultQueue.TryGetValue(takeRequest, out var replyResult);
                messages = new List <Message>(replyResult?.GetAllResults());
            }

            // get the intersection
            Response response;

            intersection = ((Response)messages.First()).Tuples;
            foreach (Message msg in messages)
            {
                response     = (Response)msg;
                intersection = new List <Tuple>(intersection.Intersect(response.Tuples));
            }

            // choose random tuple from intersection
            selectedTuple = intersection.ElementAt((new Random()).Next(0, intersection.Count));

            // phase 2, issue a take remove and wait for all acks
            Message takeRemove = new TakeRemove(EndpointURL, selectedTuple, takeRequest.SeqNum);

            MulticastMessageWaitAll(View.Nodes, takeRemove);
            WaitMessage(takeRemove, View.Nodes);

            return(selectedTuple);
        }
        /**
         * Remove the selected tuple and add the remaining locked tuples back to the tuplespace
         */
        private Message ProcessTakeRemove(Message message)
        {
            TakeRemove takeRemove = (TakeRemove)message;

            LockedTuples.TryRemove(takeRemove.TakeRequestSeqNumber, out List <Tuple> lockedTuples);

            // remove the selected tuple specified in the request
            if (lockedTuples != null && takeRemove.SelectedTuple != null)
            {
                lockedTuples.Remove(takeRemove.SelectedTuple);

                // add back the remaining tuples to the tuple space
                foreach (Tuple tuple in lockedTuples)
                {
                    TupleSpace.Write(tuple);
                }
            }

            // Send back an Ack of the remove
            Ack ack = new Ack(EndpointURL, takeRemove);

            SendMessageToRemoteURL(takeRemove.SrcRemoteURL, ack);
            return(ack);
        }