func (bra boundRemoteAddress) removeNotifyAddress(addr Address) { // as this is internal only, we can just hard-assert the local address // is a "real" mailbox bra.remoteMailboxes.Send(internal.UnnotifyRemote{ Remote: internal.IntMailboxID(bra.mailboxID), Local: internal.IntMailboxID(addr.GetID().(mailboxID)), }) }
func (bra boundRemoteAddress) send(message interface{}) error { // FIMXE: Have to pass along the mailboxID here. return bra.remoteMailboxes.Send(internal.OutgoingMailboxMessage{ Target: internal.IntMailboxID(bra.mailboxID), Message: message, }) }
func TestRemoteLinkErrorPaths(t *testing.T) { ntb := testbed(nil) defer ntb.terminate() // Send the remoteMailbox a message for the wrong node. (Verified that // this goes down the right code path via coverage analysis.) ntb.remote2to1.Send(&internal.IncomingMailboxMessage{internal.IntMailboxID(ntb.mailbox1_1.getID().(mailboxID)), "moo"}) time.Sleep(time.Second) }
// Unregister removes the given claim from a given global name. // Unregistration will only occur if the current registrant matches the // address passed in. It is not an error for it not to match; the call will // simply be ignored. // // On a given node, only one Address can have a claim on a // name. If you wish to supercede a claim with a new address, you can // simply register the new claim, and it will overwrite the previous one. func (r *registry) Unregister(name string, addr Address) { if !addr.GetID().canBeGloballyRegistered() { return } // at the moment, only mailboxID can pass the check above r.Send(internal.UnregisterName{ internal.IntNodeID(r.thisNode), name, internal.IntMailboxID(addr.GetID().(mailboxID)), }) }
// Register claims the given global name in the registry. // // This does not happen synchronously, as there seems to be no reason // for the caller to synchronously wait for this. // // A registered mailbox should stand ready to receive MultipleClaim // messages from the cluster. func (r *registry) Register(name string, addr Address) error { if !addr.GetID().canBeGloballyRegistered() { return ErrCantGloballyRegister } // only mailboxID can pass the canBeGloballyRegistered test above r.Send(internal.RegisterName{ internal.IntNodeID(r.thisNode), name, internal.IntMailboxID(addr.GetID().(mailboxID)), }) return nil }
// This registers a node's registry mailbox. This should only happen once // per connection to that node. Even if it is the same as last time, it // triggers the synchronization process. func (r *registry) registerNodeRegistryMailbox(node NodeID, addr Address) { r.nodeRegistries[node] = addr // send initial synchronization claims := map[string]internal.IntMailboxID{} anc := internal.AllNodeClaims{internal.IntNodeID(r.thisNode), claims} // Copy the claims. This has to be a fresh copy to make sure we don't // lose anything to race conditions. If this becomes a performance // bottleneck, we could try to do a serialization immediately in this // step, since this gets copied and then serialized anyhow. for name, mID := range r.nodeClaims[r.thisNode] { claims[name] = internal.IntMailboxID(mID) } addr.Send(anc) }
func (rm *remoteMailboxes) Serve() { defer func() { for remoteID, localIDs := range rm.linksToRemote { for localID := range localIDs { // FIXME: sendByID? var addr Address addr.id = localID addr.connectionServer = rm.connectionServer addr.Send(MailboxTerminated(remoteID)) } } rm.linksToRemote = make(map[mailboxID]map[mailboxID]voidtype) if r := recover(); r != nil { rm.Error("While handling mailbox, got fatal error (this is a serious bug): %s", myString(r)) if rm.connection != nil { rm.connection.terminate() } panic(r) } }() var m interface{} for { if rm.doneProcessing != nil { if !rm.doneProcessing(m) { rm.doneProcessing = nil } } m = rm.outgoingMailbox.ReceiveNext() if rm.examineMessages != nil { if !rm.examineMessages(m) { rm.examineMessages = nil } } switch msg := m.(type) { case internal.OutgoingMailboxMessage: rm.send(internal.IncomingMailboxMessage{msg.Target, msg.Message}, "normal message") // all of the gob encoding stuff seems to end up with this getting // an extra layer of pointer indirection added to it. case *internal.IncomingMailboxMessage: var addr Address addr.id = mailboxID(msg.Target) addr.connectionServer = rm.connectionServer addr.Send(msg.Message) case internal.NotifyRemote: // FIXME: if the local addr dies, this never cleans out // link. This will eventually be a memory leak. // Unfortunately it implies we need another map of local // address to their relevant entries and to subscribe to them too. remoteID := mailboxID(msg.Remote) localID := mailboxID(msg.Local) linksToRemote, remoteLinksExist := rm.linksToRemote[remoteID] if remoteLinksExist { _, thisAddressAlreadyLinked := linksToRemote[localID] if thisAddressAlreadyLinked { // a no-op; msg.local has already set notify for msg.remote continue } } else { linksToRemote = make(map[mailboxID]voidtype) rm.linksToRemote[remoteID] = linksToRemote } if len(linksToRemote) == 0 { // Since this is the first link to this particular // remote mailbox we are recording, we need to send along // the registration message err := rm.send(&internal.NotifyNodeOnTerminate{internal.IntMailboxID(remoteID)}, "termination notification") if err != nil { var addr Address addr.id = localID addr.connectionServer = rm.connectionServer addr.Send(MailboxTerminated(remoteID)) // FIXME: Really? Panic? panic(err) } } linksToRemote[localID] = void case internal.UnnotifyRemote: remoteID := mailboxID(msg.Remote) localID := mailboxID(msg.Local) linksToRemote, remoteLinksExist := rm.linksToRemote[remoteID] if !remoteLinksExist || len(linksToRemote) == 0 { continue } delete(linksToRemote, localID) if len(linksToRemote) == 0 { // if that was the last link, we need to unregister from // the remote node // send does all the error handling I need here _ = rm.send(&internal.RemoveNotifyNodeOnTerminate{internal.IntMailboxID(remoteID)}, "remove notify node") } case *internal.RemoteMailboxTerminated: // A remote mailbox has been terminated that we indicated // interest in. remoteID := mailboxID(msg.IntMailboxID) links, linksExist := rm.linksToRemote[remoteID] if !linksExist || len(links) == 0 { continue } for subscribed := range links { var addr Address addr.id = subscribed addr.connectionServer = rm.connectionServer addr.Send(MailboxTerminated(remoteID)) } delete(rm.linksToRemote, remoteID) case *internal.NotifyNodeOnTerminate: // this has to be a localID, or we wouldn't be receiving this // message localID := mailboxID(msg.IntMailboxID) var addr Address addr.id = localID addr.connectionServer = rm.connectionServer addr.NotifyAddressOnTerminate(rm.Address) case *internal.RemoveNotifyNodeOnTerminate: localID := mailboxID(msg.IntMailboxID) var addr Address addr.id = localID addr.connectionServer = rm.connectionServer addr.RemoveNotifyAddress(rm.Address) // Note this is a local mailbox. case MailboxTerminated: id, isRealMailbox := AddressID(msg).(mailboxID) if isRealMailbox { // if we are receiving this, apparently the other side wants to // hear about it _ = rm.send(&internal.RemoteMailboxTerminated{internal.IntMailboxID(id)}, "mailbox terminated normally") } else { rm.Trace("Somehow got a mailbox termination for a non-mailboxID: %#v", msg) } // This allows us to test proper error handling, despite // the fact I don't know how to panic any of the above code case internal.PanicHandler: panic("Panicking as requested due to panic handler") case internal.DestroyConnection: rm.connection.terminate() case newExamineMessages: rm.examineMessages = msg.f case newDoneProcessing: rm.doneProcessing = msg.f case terminateRemoteMailbox: return default: fmt.Printf("Unexpected message received: %#v", msg) rm.Error("Unexpected message arrived in our node mailbox: %#v", msg) } } }