public void Receive(ReceiveMessageEventArgs eventargs) { Message message = eventargs.m_message; if (!m_server.m_replicationService.IsMaster) { // TODO: reply, in future: redirect question? DebugInfo ("Got request intended for master."); m_server.m_replicationService.SendImNotMasterMessage (message); return; } // All we need from the request is the source // uri. DebugInfo ("Server {0} got request for sequence number from {1}", m_server.UserName, message.GetSourceUserName ()); // Create response to the requester Message response = new Message (); response.SetSourceUri (m_server.UserName); response.SetDestinationUsers (message.GetSourceUserName ()); response.SetMessageType ("sequencenumber"); long nextSequenceNumber = GetSequenceNumber (); response.PushString (nextSequenceNumber.ToString ()); // this is implictly a blocking call m_server.m_replicationService.ReplicateSequenceNumber (nextSequenceNumber); m_server.m_sendReceiveMiddleLayer.Send (response); }
public void SendInfoMsgToPuppetMaster(string message, params object[] args) { Message m = new Message (); m.SetMessageType ("puppet_info"); m.SetSourceUserName (m_server.UserName); m.SetDestinationUsers (PUPPET_MASTER); m.PushString (String.Format (message, args)); m_server.m_puppetSendReceiveMiddleLayer.Send (m); }
public string Lookup(string user) { lock (this){ // TODO: This should later move into a lookup for // a list of users. // TODO: It should also implement a cache // to avoid remote invocations all the time. // TODO: Avoid restricted "SERVER" username // registrations at server end. // Lookup service knows the servers. // It will also check the cache at a later stage of // development. if (user.Equals ("SERVER")) { // Return server 1. Will be changed later. return m_client.CurrentMasterServer; } else if (user.Equals ("PUPPETMASTER")) { DebugUncond ("Returning address for puppet master {0}", m_client.PuppetMasterAddress); return m_client.PuppetMasterAddress; } // If not SERVER or in cache, create a new // lookup message destined to SERVER. Eventually, // lower layer will query for SERVER, and this // method will answer it. Message m = new Message (); m.SetSourceUserName (m_client.UserName); m.SetDestinationUsers ("SERVER"); m.SetMessageType ("lookup"); m.PushString (user); // This should change to a list later m_client.m_sendReceiveMiddleLayer.Send (m); // This thread will block here until the reset event is sent. // In this case, it happens when the Receive() method is invocated // upon getting a response. DebugInfo ("Sent Lookup request"); m_oSignalEvent.WaitOne(); DebugInfo ("Releasing block"); m_oSignalEvent.Reset (); return m_lookupResponse; } }
private Message PackUserTable() { Message m = new Message (); foreach (string user in m_server.m_userTableService.UserTable.Keys) { m.PushString (String.Format ("{0},{1}", user, m_server.m_userTableService.UserTable[user])); } m.PushString (m_server.m_userTableService.UserTable.Count.ToString ()); return m; }
private void HandleUserDisconnectReplication(string username, string replyTo) { m_server.m_userTableService.UserDisconnect (username); Message ack = new Message (); ack.SetMessageType ("replicate"); ack.SetDestinationUsers (replyTo); ack.SetSourceUserName (m_server.UserName); ack.PushString (username); ack.PushString ("user_disconnect_ack"); m_server.m_sendReceiveMiddleLayer.Send (ack); }
private void HandleSequenceNumberReplication(string number, string replyTo) { m_server.m_sequenceNumberService.SetSequenceNumber (Int32.Parse (number)); Message ack = new Message (); ack.SetMessageType ("replicate"); ack.SetDestinationUsers (replyTo); ack.SetSourceUserName (m_server.UserName); ack.PushString (number); ack.PushString ("sequencenumber_ack"); m_server.m_sendReceiveMiddleLayer.Send (ack); }
public void SendImNotMasterMessage(Message msg) { DebugInfo ("Asking {0} to resend to {1}", msg.GetSourceUserName (), CurrentMaster); Message m = new Message (); m.SetMessageType ("resend"); m.SetDestinationUsers (msg.GetSourceUserName ()); m.MessageForResending = msg; m.SetSourceUserName (m_server.UserName); m.PushString (m_server.m_userTableService.Lookup (CurrentMaster)); m_server.m_sendReceiveMiddleLayer.Send (m,msg.GetSourceUri (), msg.GetSourceUserName ()); }
public void ReplicateUserDisconnect(string username) { if (IsMaster) { DebugInfo ("Sending UserDisconnect replication request"); Message m = new Message (); m.PushString (username); m.PushString ("user_disconnect"); DistributeReplicationMessage (m); } }
public void ReplicateSequenceNumber(long number) { if (IsMaster) { DebugInfo ("Sending sequence number ({0}) replication request", number); Message m = new Message (); m.PushString (number.ToString ()); m.PushString ("sequencenumber"); // subtyped message DistributeReplicationMessage (m); } }
public void Reserve(string description, List<string> userlist, List<int> slotlist) { // 1) Create reservation object // TODO: Make sure m_client VALIDATES the inputs? // TODO: At this point, we assume all requested slots are free. Reservation reservation = new Reservation (); reservation.m_description = description; reservation.m_slotNumberList = slotlist; reservation.m_userList = userlist; reservation.m_reservationState = CalendarServiceClient.ReservationState.INITIATED; reservation.m_acksForSlot = new Dictionary<int, int> (); reservation.m_initiator = m_client.UserName; // Update slot objects foreach (int i in slotlist) { // If slot is being encountered for the // first time... if (!m_numberToSlotMap.ContainsKey (i)) { DebugLogic ("Creating new slot instance for slot-number: {0}", i); // Then create new slot. // TODO: Probably need internal methods for this Slot slot = new Slot (); slot.m_calendarState = CalendarState.FREE; slot.m_slotNumber = i; slot.m_reservationsForThisSlot = new List<Reservation> (); slot.m_preCommitList = new SortedList<int, Reservation> (); slot.m_lockedReservation = -1; // Add to int-to-slot-object map m_numberToSlotMap.Add (i, slot); } // Update the slot's reservation list m_numberToSlotMap[i].m_reservationsForThisSlot.Add (reservation); } //DebugUncond ("----->>>> {0} {1} <<---", userlist.Count, userlist[0]); // Special case, book with yourself if (userlist.Count == 1 && userlist[0] == m_client.UserName) { // TODO: Check if slot is in action perhaps. foreach (int i in slotlist) { Slot s = m_numberToSlotMap[i]; if (s.m_calendarState != CalendarServiceClient.CalendarState.ASSIGNED && s.m_calendarState != CalendarServiceClient.CalendarState.BOOKED) { s.m_calendarState = CalendarServiceClient.CalendarState.ASSIGNED; reservation.m_reservationState = CalendarServiceClient.ReservationState.COMMITTED; s.m_lockedReservation = i; return; } } } // 2) Obtain sequence number reservation.m_sequenceNumber = m_client.GetSequenceNumber (); // 2) Maintain reservation session as per sequence number // Add reservation object to int-to-reservation-object map. m_activeReservationSessions.Add (reservation.m_sequenceNumber, reservation); DebugLogic ("Created reservation object for" + "[Desc: {0}, Users: {1}, Slots: {2} Seq: {3}]",description, userlist, slotlist, reservation.m_sequenceNumber); // Else, trim yourself out of the list (position 0). // Surely it is yourself, but sanity checks are good. if (m_client.UserName.Equals (userlist[0])) { userlist.RemoveAt(0); } // 3) Disseminate reservation request DebugLogic ("Dissemination reservation request " + "[Desc: {0}, Users: {1}, Slots: {2}]",description, userlist, slotlist); Message reservationRequest = new Message (); /* Message format for reservations (stack part) is as follows: * * - SubType TODO: Later register more types up there. * - ReservationSequenceNumber * - User NumberOfSlots * - NumberOfSlots * - Slot 1 * - Slot 2 * ... * - Slot NumberOfUsers * - Description */ reservationRequest.SetDestinationUsers (userlist); reservationRequest.SetMessageType ("calendar"); reservationRequest.SetSourceUserName (m_client.UserName); // Data part. Things will be pushed in the reverse order // of the format reservationRequest.PushString (description); // Since we're pushing on to a stack and would like // to retreive it in the same order at the other end. slotlist.Reverse (); foreach (int i in slotlist) { reservationRequest.PushString (i.ToString ()); } reservationRequest.PushString (reservation.m_slotNumberList.Count.ToString ()); reservationRequest.PushString (reservation.m_sequenceNumber.ToString ()); reservationRequest.PushString ("reservationrequest"); SendMessage (reservationRequest); }
private void SendPreCommitMessage(Reservation res, int s) { // Now send a precommit message to everyone involved. // Party time! Message precommitMsg = new Message (); precommitMsg.SetSourceUserName (m_client.UserName); precommitMsg.SetDestinationUsers (res.m_userList); DebugLogic ("SENDING PRECOMMIT TO {0} many ppl for slot {1}", res.m_userList.Count, s); precommitMsg.SetMessageType ("calendar"); precommitMsg.PushString (s.ToString ()); precommitMsg.PushString (res.m_sequenceNumber.ToString ()); precommitMsg.PushString ("precommit"); SendMessage (precommitMsg); res.m_acksForSlot.Clear (); }
private void ReservationAbortCohort(int reservationSequenceNumber, int s) { Slot slot = m_numberToSlotMap[s]; Reservation oldReservation = m_activeReservationSessions[reservationSequenceNumber]; oldReservation.m_slotNumberList.Remove (s); // The earlier reservation is now removed from this slot slot.m_reservationsForThisSlot.Remove (oldReservation); // If this was the last slot in contention for // the reservation... if (oldReservation.m_slotNumberList.Count == 0) { // ... then the reservation should be in ABORTED state. Sigh. oldReservation.m_reservationState = CalendarServiceClient.ReservationState.ABORTED; } // This slot is currently locked, only an abort can kill it. if (slot.m_lockedReservation == reservationSequenceNumber) { // Promote next reservation in precommit queue if (slot.m_preCommitList.Count > 0) { slot.m_lockedReservation = slot.m_preCommitList.Keys[0]; // Lock top queue elements slot.m_preCommitList.RemoveAt (0); // remove top element from queue. Reservation newres = m_activeReservationSessions[slot.m_lockedReservation]; // Send a yes message for the succeeding reservation Message yes = new Message (); yes.SetDestinationUsers (m_activeReservationSessions[slot.m_lockedReservation].m_initiator); yes.SetMessageType ("calendar"); yes.SetSourceUserName (m_client.UserName); yes.PushString (slot.m_slotNumber.ToString ()); yes.PushString (newres.m_sequenceNumber.ToString ()); yes.PushString ("yes"); SendMessage (yes); } else { // We don't have any more reservations for this slot, so // let's keep it in the ACKNOWLEDGED state. slot.m_calendarState = CalendarServiceClient.CalendarState.ACKNOWLEDGED; // Release locks slot.m_lockedReservation = -1; } } else { // This slot is in the precommit list slot.m_preCommitList.Remove (s); } }
private void ReceiveReservationYes(Message m, string src, List<string> userlist, int reservationSequenceNumber, string messageSubType) { int s = Int32.Parse (m.PopString ()); DebugLogic ("Received YES for slot {0}, reservation number {1}",s,reservationSequenceNumber); Slot slot = m_numberToSlotMap[s]; Reservation res = m_activeReservationSessions[reservationSequenceNumber]; int ackcount; if (res.m_acksForSlot.ContainsKey (s)) { res.m_acksForSlot[s]++; ackcount = res.m_acksForSlot[s]; } else { ackcount = 1; res.m_acksForSlot[s] = ackcount; } if (ackcount == res.m_userList.Count // the below two conditions are sanity checks. && res.m_reservationState != CalendarServiceClient.ReservationState.COMMITTED && m_numberToSlotMap[s].m_calendarState != CalendarServiceClient.CalendarState.ASSIGNED) { DebugLogic ("Woopee! I can haz slot! {0}", s); //send DoCommit Message docommitMsg = new Message (); docommitMsg.SetSourceUserName (m_client.UserName); docommitMsg.SetDestinationUsers (res.m_userList); docommitMsg.SetMessageType ("calendar"); docommitMsg.PushString (s.ToString ()); docommitMsg.PushString (reservationSequenceNumber.ToString ()); docommitMsg.PushString ("docommit"); SendMessage (docommitMsg); // If you have to send this, then you can commit. // TODO: Handle 2/1, 1/2 deadlock. slot.m_calendarState = CalendarServiceClient.CalendarState.ASSIGNED; res.m_reservationState = CalendarServiceClient.ReservationState.COMMITTED; } }
private void ReceiveReservationRequest(Message m, string src, List<string> userlist, int reservationSequenceNumber, string messageSubType) { /* If I get a reservation request for a slot * that is either FREE or ACKNOWLEDGED, then * respond with an ACK, else if it is either * BOOKED, or ASSIGNED, then respond with a NACK */ int numSlots = Int32.Parse (m.PopString ()); // number of slots List<int> slotlist = new List<int> (); // unpack all the slots for (int i = 0; i < numSlots; i++) { slotlist.Add (Int32.Parse (m.PopString ())); } string description = m.PopString (); // description DebugLogic ("Received Message:" + "[{0}, {1}, {2}, {3}, {4}, {5}, {6}]", src, userlist.ToString (), messageSubType, reservationSequenceNumber, numSlots, slotlist.ToString (), description); /* Message unpacked, now create a reservation object */ if (m_activeReservationSessions.ContainsKey (reservationSequenceNumber)) { DebugFatal ("Duplicate reservation request received {0}", reservationSequenceNumber); } /* Create reservation object */ Reservation reservation = new Reservation (); reservation.m_description = description; reservation.m_sequenceNumber = reservationSequenceNumber; reservation.m_userList = userlist; reservation.m_slotNumberList = slotlist; reservation.m_initiator = src; /* * check if slots are free * and proceed to respond with an ACK or NACK. */ List<int> availableslots = new List<int> (); foreach (int i in slotlist) { // If slot is being encountered for the // first time... if (!m_numberToSlotMap.ContainsKey (i)) { DebugLogic ("Creating new slot instance for slot-number: {0}", i); // Then create new slot. // TODO: Probably need internal methods for this Slot tempslot = new Slot (); tempslot.m_calendarState = CalendarState.FREE; tempslot.m_slotNumber = i; tempslot.m_reservationsForThisSlot = new List<Reservation> (); tempslot.m_preCommitList = new SortedList<int, Reservation> (); tempslot.m_lockedReservation = -1; // Add to int-to-slot-object map m_numberToSlotMap.Add (i, tempslot); } // Get the concerned slot Slot slot = m_numberToSlotMap[i]; if (slot.m_calendarState == CalendarServiceClient.CalendarState.FREE || slot.m_calendarState == CalendarServiceClient.CalendarState.ACKNOWLEDGED) { // Update the slot's reservation list slot.m_reservationsForThisSlot.Add (reservation); // Append to list of slots. availableslots.Add (i); // Update slot state slot.m_calendarState = CalendarServiceClient.CalendarState.ACKNOWLEDGED; DebugLogic ("Slot {0} is now in ACKNOWLEDGED state", i); } } // Respond with ACK/NACK if (availableslots.Count > 0) { // We do have at least one free slot, so respond with an ACK Message ack = new Message (); ack.SetSourceUserName (m_client.UserName); ack.SetDestinationUsers (src); ack.SetMessageType ("calendar"); /* * ACK Message format, data part * * - Number of Slots * - Slot 1 * - Slot 2 * ... * - Slot N */ availableslots.Reverse (); foreach (int i in availableslots) { ack.PushString (i.ToString ()); DebugInfo ("Slot {0} is available", i); } ack.PushString (availableslots.Count.ToString ()); ack.PushString (reservationSequenceNumber.ToString ()); ack.PushString ("reservationack"); DebugLogic ("Sending an ack to: {0}", src); SendMessage (ack); m_activeReservationSessions.Add (reservationSequenceNumber, reservation); } else { Message nack = new Message (); // No free slots, so respond with a NACK nack.SetSourceUserName (m_client.UserName); nack.SetDestinationUsers (src); nack.SetMessageType ("calendar"); nack.PushString ("reservationnack"); DebugLogic ("Sending a nack to: {0}", src); SendMessage (nack); } }
private void ReceiveReservationPreCommit(Message m, string src, List<string> userlist, int reservationSequenceNumber, string messageSubType) { /* If I get a PRECOMMIT update for a slot, * and it is still in ACKNOWLEDGED state, then * respond with YES!. Else, respond with a NO! * * If I responded with a YES, then * move into PRECOMMIT state and begin * verification and commit timers. * * When verification timer fires, verify * your neighbours. TODO: Might need ping * utility * * If verification fails, ABORT, else do nothing. * * When commit timer fires, commit. * * If I responded with a NO, then move * into ABORT. * * If between any of the above, the coordinator * sends an ABORT message, then ABORT. DUH! */ int s = Int32.Parse (m.PopString ()); DebugLogic ("Received precommit for slot {0}, reservation number {1}",s,reservationSequenceNumber); Slot slot = m_numberToSlotMap[s]; Reservation res = m_activeReservationSessions[reservationSequenceNumber]; if (slot.m_calendarState == CalendarServiceClient.CalendarState.ACKNOWLEDGED || slot.m_calendarState == CalendarServiceClient.CalendarState.BOOKED) { if (slot.m_preCommitList.Count == 0 && slot.m_lockedReservation == -1) { res.m_reservationState = CalendarServiceClient.ReservationState.TENTATIVELY_BOOKED; slot.m_calendarState = CalendarServiceClient.CalendarState.BOOKED; // No one has a lock, so this reservation gets it. Message yes = new Message (); yes.SetDestinationUsers (src); yes.SetMessageType ("calendar"); yes.SetSourceUserName (m_client.UserName); yes.PushString (s.ToString ()); yes.PushString (reservationSequenceNumber.ToString ()); yes.PushString ("yes"); slot.m_lockedReservation = reservationSequenceNumber; SendMessage (yes); } else if (slot.m_lockedReservation != -1 && slot.m_lockedReservation < s) { // Some reservation older is already locked, so wait. slot.m_preCommitList.Add (reservationSequenceNumber, res); } else { // Add older reservation to queue, we may upgrade or abort this // depending on the result of the next step slot.m_preCommitList.Add (reservationSequenceNumber, res); // A newer reservation has been locked. For now, // we try and ABORT the newer one in favour of the older one. // But the optimisation here would be for coordinators to // initiate an is-there-a-deadlock check, which the cohort // would ask the coordinator to initiate. Message abortmsg = new Message (); abortmsg.SetDestinationUsers (m_activeReservationSessions[reservationSequenceNumber].m_initiator); abortmsg.SetMessageType ("calendar"); abortmsg.SetSourceUserName (m_client.UserName); abortmsg.PushString (s.ToString ()); // we need to mention the slot that is being aborted abortmsg.PushString (slot.m_lockedReservation.ToString ()); abortmsg.PushString ("abort"); SendMessage (abortmsg); ReservationAbortCohort (slot.m_lockedReservation, s); } } }
private void ReceiveReservationDoCommit(Message m, string src, List<string> userlist, int reservationSequenceNumber, string messageSubType) { int s = Int32.Parse (m.PopString ()); DebugLogic ("Received docommit for slot {0}, reservation number {1}",s,reservationSequenceNumber); Slot slot = m_numberToSlotMap[s]; Reservation res = m_activeReservationSessions[reservationSequenceNumber]; slot.m_calendarState = CalendarServiceClient.CalendarState.ASSIGNED; res.m_reservationState = CalendarServiceClient.ReservationState.COMMITTED; DebugLogic ("Reservation ID: {0}, Slot: {1} has COMMITTED", reservationSequenceNumber, s); if (m_client.m_isPuppetControlled) { m_client.m_puppetService.SendInfoMsgToPuppetMaster ("Reservation ID: {0}, Slot: {1} has COMMITTED", reservationSequenceNumber, s); } // Perform cleanup of all reservation objects foreach (int i in res.m_slotNumberList) { if (i != s) { m_numberToSlotMap[i].m_reservationsForThisSlot.Remove (res); } } foreach (int i in slot.m_preCommitList.Keys) { Message abortmsg = new Message (); abortmsg.SetDestinationUsers (m_activeReservationSessions[reservationSequenceNumber].m_initiator); abortmsg.SetMessageType ("calendar"); abortmsg.SetSourceUserName (m_client.UserName); abortmsg.PushString (s.ToString ()); // we need to mention the slot that is being aborted abortmsg.PushString (reservationSequenceNumber.ToString ()); abortmsg.PushString ("abort"); SendMessage (abortmsg); ReservationAbortCohort (i, s); } }
public void Receive(ReceiveMessageEventArgs eventargs) { // parse message and call appropriate action Message m = eventargs.m_message; string message_source = m.GetSourceUserName (); string request_type = m.GetMessageType (); string message_source_uri = m.GetSourceUri (); if (request_type.Equals ("lookup")) { // Client has requested a username lookup, // respond with the URI string for the // user string user_request = m.PopString (); string uri = GetUriForUser (user_request); DebugInfo ("Answering lookup for {0} with {1}", user_request, uri); Message response = new Message (); response.SetMessageType ("lookup"); response.SetDestinationUsers (message_source); response.SetSourceUserName (m_server.UserName); response.PushString (uri); SendReply(response); } else if (request_type.Equals ("connect")) { if (!m_server.m_replicationService.IsMaster) { // TODO: reply, in future: redirect question? DebugInfo ("Got request intended for master."); m_server.m_replicationService.SendImNotMasterMessage (m); return; } // add user to DB UserConnect (message_source, message_source_uri); // Send an ACK or the client starves to death Message ack_message = new Message (); ack_message.SetMessageType ("connect"); ack_message.SetDestinationUsers (message_source); ack_message.SetSourceUserName (m_server.UserName); // replicate message (implicit block) m_server.m_replicationService.ReplicateUserConnect (message_source, message_source_uri); SendReply (ack_message); } else if (request_type.Equals ("disconnect")) { if (!m_server.m_replicationService.IsMaster) { // TODO: reply, in future: redirect question? DebugInfo ("Got request intended for master."); m_server.m_replicationService.SendImNotMasterMessage (m); return; } // remove from replicas (implicit block until 1 ack is received) m_server.m_replicationService.ReplicateUserDisconnect (message_source); UserDisconnect (message_source); // TODO: Find a way to get an ACK across // for a disconnect message } return; }
public bool CommandClient(PuppetInstruction instruction) { if (!Clients.ContainsKey(instruction.ApplyToUser)) { if (instruction.Type == PuppetInstructionType.CONNECT) { if (instruction.ApplyToUser.Contains ("central")) { DebugLogic ("Starting new server: {0}", instruction.ApplyToUser); SpawnServer(instruction.ApplyToUser); // server will self-register, safe to return return true; } else { DebugLogic ("Starting a new client: {0}", instruction.ApplyToUser); SpawnClient (instruction.ApplyToUser); // client will register with puppet master when initing, safe to return return true; } } DebugLogic ("No such user is connected: {0}", instruction.ApplyToUser); return false; } else if (instruction.Type == PuppetInstructionType.DISCONNECT && instruction.ApplyToUser.Contains ("central")) { Clients.Remove (instruction.ApplyToUser); try { SpawnedClients[instruction.ApplyToUser].Kill (); SpawnedClients.Remove (instruction.ApplyToUser); return true; } catch (InvalidOperationException) { DebugLogic ("Couldn't kill server: {0}", instruction.ApplyToUser); } } Message m = new Message (); m.SetSourceUserName ("puppetmaster"); m.SetDestinationUsers (instruction.ApplyToUser); m.SetMessageType ("puppetmaster"); switch (instruction.Type) { case PuppetInstructionType.RESERVATION: m.PushString (BuildStringList (instruction.Slots)); m.PushString (BuildStringList (instruction.Users)); m.PushString (instruction.Description); m.PushString (instruction.Type.ToString ()); break; default: m.PushString (instruction.Type.ToString ()); break; } m_puppetMaster.m_sendReceiveMiddleLayer.Send (m); return true; }