servlib/contacts/ContactManager.cc
changeset 0 2b3e5ec03512
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlib/contacts/ContactManager.cc	Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,508 @@
+/*
+ *    Copyright 2004-2006 Intel Corporation
+ * 
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <dtn-config.h>
+#endif
+
+#include <oasys/util/StringBuffer.h>
+
+#include "ContactManager.h"
+#include "Contact.h"
+#include "Link.h"
+#include "bundling/BundleDaemon.h"
+#include "bundling/BundleEvent.h"
+#include "conv_layers/ConvergenceLayer.h"
+
+namespace dtn {
+
+//----------------------------------------------------------------------
+ContactManager::ContactManager()
+    : BundleEventHandler("ContactManager", "/dtn/contact/manager"),
+      opportunistic_cnt_(0)
+{
+    links_ = new LinkSet();
+}
+
+//----------------------------------------------------------------------
+ContactManager::~ContactManager()
+{
+    delete links_;
+}
+
+//----------------------------------------------------------------------
+bool
+ContactManager::add_new_link(const LinkRef& link)
+{
+    oasys::ScopeLock l(&lock_, "ContactManager::add_new_link");
+
+    ASSERT(link != NULL);
+    ASSERT(!link->isdeleted());
+    
+    log_debug("adding NEW link %s", link->name());
+    if (has_link(link->name())) {
+        return false;
+    }
+    links_->insert(LinkRef(link.object(), "ContactManager"));
+
+    if (!link->is_create_pending()) {
+        log_debug("posting LinkCreatedEvent");
+        BundleDaemon::post(new LinkCreatedEvent(link));
+    }
+
+    return true;
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::del_link(const LinkRef& link, bool wait,
+                         ContactEvent::reason_t reason)
+{
+    oasys::ScopeLock l(&lock_, "ContactManager::del_link");
+    ASSERT(link != NULL);
+
+    if (!has_link(link)) {
+        log_err("ContactManager::del_link: link %s does not exist",
+                link->name());
+        return;
+    }
+    ASSERT(!link->isdeleted());
+
+    log_debug("ContactManager::del_link: deleting link %s", link->name());
+
+    if (!wait)
+        link->delete_link();
+
+    // Close the link if it is open or in the process of being opened.
+    if (link->isopen() || link->isopening()) {
+        BundleDaemon::instance()->post(
+            new LinkStateChangeRequest(link, Link::CLOSED, reason));
+    }
+
+    // Cancel the link's availability timer (if one exists).
+    AvailabilityTimerMap::iterator iter = availability_timers_.find(link);
+    if (iter != availability_timers_.end()) {
+        LinkAvailabilityTimer* timer = iter->second;
+        availability_timers_.erase(link);
+
+        // Attempt to cancel the timer, relying on the timer system to clean
+        // up the timer state once it bubbles to the top of the timer queue.
+        // If the timer is in the process of firing (i.e., race condition),
+        // the timer should clean itself up in the timeout handler.
+        if (!timer->cancel()) {
+            log_warn("ContactManager::del_link: "
+                     "failed to cancel availability timer -- race condition");
+        }
+    }
+
+    links_->erase(link);
+    
+    if (wait) {
+        l.unlock();
+        // If some parent calling del_link already locked the Contact Manager,
+        // the lock will remain locked, and an event ahead of the
+        // LinkDeletedEvent may wait for the lock, causing deadlock
+        ASSERT(!lock()->is_locked_by_me());
+        oasys::Notifier notifier("ContactManager::del_link");
+        BundleDaemon::post_and_wait(new LinkDeletedEvent(link), &notifier);
+        link->delete_link();
+    } else {
+        BundleDaemon::post(new LinkDeletedEvent(link));
+    }
+}
+
+//----------------------------------------------------------------------
+bool
+ContactManager::has_link(const LinkRef& link)
+{
+    oasys::ScopeLock l(&lock_, "ContactManager::has_link");
+    ASSERT(link != NULL);
+    
+    LinkSet::iterator iter = links_->find(link);
+    if (iter == links_->end())
+        return false;
+    return true;
+}
+
+//----------------------------------------------------------------------
+bool
+ContactManager::has_link(const char* name)
+{
+    oasys::ScopeLock l(&lock_, "ContactManager::has_link");
+    ASSERT(name != NULL);
+    
+    LinkSet::iterator iter;
+    for (iter = links_->begin(); iter != links_->end(); ++iter) {
+        if (strcasecmp((*iter)->name(), name) == 0)
+            return true;
+    }
+    return false;
+}
+
+//----------------------------------------------------------------------
+LinkRef
+ContactManager::find_link(const char* name)
+{
+    oasys::ScopeLock l(&lock_, "ContactManager::find_link");
+    
+    LinkSet::iterator iter;
+    LinkRef link("ContactManager::find_link: return value");
+    
+    for (iter = links_->begin(); iter != links_->end(); ++iter) {
+        if (strcasecmp((*iter)->name(), name) == 0) {
+            link = *iter;
+            ASSERT(!link->isdeleted());
+            return link;
+        }
+    }
+    return link;
+}
+
+//----------------------------------------------------------------------
+const LinkSet*
+ContactManager::links()
+{
+    ASSERTF(lock_.is_locked_by_me(),
+            "ContactManager::links must be called while holding lock");
+    return links_;
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::LinkAvailabilityTimer::timeout(const struct timeval& now)
+{
+    (void)now;
+    cm_->reopen_link(link_);
+    delete this;
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::reopen_link(const LinkRef& link)
+{
+    oasys::ScopeLock l(&lock_, "ContactManager::reopen_link");
+    ASSERT(link != NULL);
+
+    log_debug("reopen link %s", link->name());
+
+    availability_timers_.erase(link);
+
+    if (!has_link(link)) {
+        log_warn("ContactManager::reopen_link: "
+                 "link %s does not exist", link->name());
+        return;
+    }
+    ASSERT(!link->isdeleted());
+    
+    if (link->state() == Link::UNAVAILABLE) {
+        BundleDaemon::post(
+            new LinkStateChangeRequest(link, Link::OPEN,
+                                       ContactEvent::RECONNECT));
+    } else {
+        // state race (possibly due to user action)
+        log_err("availability timer fired for link %s but state is %s",
+                link->name(), Link::state_to_str(link->state()));
+    }
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::handle_link_created(LinkCreatedEvent* event)
+{
+    oasys::ScopeLock l(&lock_, "ContactManager::handle_link_created");
+
+    LinkRef link = event->link_;
+    ASSERT(link != NULL);
+    
+    if(link->isdeleted())
+    {
+        log_warn("ContactManager::handle_link_created: "
+                "link %s is being deleted", link->name());
+        return;
+    }
+        
+    if (!has_link(link)) {
+        log_err("ContactManager::handle_link_created: "
+                "link %s does not exist", link->name());
+        return;
+    }
+
+    // Post initial state events; MOVED from Link::create_link().
+    link->set_initial_state();
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::handle_link_available(LinkAvailableEvent* event)
+{
+    oasys::ScopeLock l(&lock_, "ContactManager::handle_link_available");
+
+    LinkRef link = event->link_;
+    ASSERT(link != NULL);
+    
+    if(link->isdeleted())
+    {
+        log_warn("ContactManager::handle_link_available: "
+                "link %s is being deleted", link->name());
+        return;
+    }
+
+    if (!has_link(link)) {
+        log_warn("ContactManager::handle_link_available: "
+                 "link %s does not exist", link->name());
+        return;
+    }
+
+    AvailabilityTimerMap::iterator iter;
+    iter = availability_timers_.find(link);
+    if (iter == availability_timers_.end()) {
+        return; // no timer for this link
+    }
+
+    LinkAvailabilityTimer* timer = iter->second;
+    availability_timers_.erase(link);
+
+    // try to cancel the timer and rely on the timer system to clean
+    // it up once it bubbles to the top of the queue... if there's a
+    // race and the timer is in the process of firing, it should clean
+    // itself up in the timeout handler.
+    if (!timer->cancel()) {
+        log_warn("ContactManager::handle_link_available: "
+                 "can't cancel availability timer: race condition");
+    }
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::handle_link_unavailable(LinkUnavailableEvent* event)
+{
+    oasys::ScopeLock l(&lock_, "ContactManager::handle_link_unavailable");
+
+    LinkRef link = event->link_;
+    ASSERT(link != NULL);
+
+    if (!has_link(link)) {
+        log_warn("ContactManager::handle_link_unavailable: "
+                 "link %s does not exist", link->name());
+        return;
+    }
+    
+    if(link->isdeleted())
+    {
+        log_warn("ContactManager::handle_link_unavailable: "
+                "link %s is being deleted", link->name());
+        return;
+    }
+
+    // don't do anything for links that aren't ondemand or alwayson
+    if (link->type() != Link::ONDEMAND && link->type() != Link::ALWAYSON) {
+        log_debug("ContactManager::handle_link_unavailable: "
+                  "ignoring link unavailable for link of type %s",
+                  link->type_str());
+        return;
+    }
+    
+    // or if the link wasn't broken but instead was closed by user
+    // action or by going idle
+    if (event->reason_ == ContactEvent::USER ||
+        event->reason_ == ContactEvent::IDLE)
+    {
+        log_debug("ContactManager::handle_link_unavailable: "
+                  "ignoring link unavailable due to %s",
+                  event->reason_to_str(event->reason_));
+        return;
+    }
+    
+    // adjust the retry interval in the link to handle backoff in case
+    // it continuously fails to open, then schedule the timer. note
+    // that if this is the first time the link is opened, the
+    // retry_interval will be initialized to zero so we set it to the
+    // minimum here. the retry interval is reset in the link open
+    // event handler.
+    if (link->retry_interval_ == 0) {
+        link->retry_interval_ = link->params().min_retry_interval_;
+    }
+
+    int timeout = link->retry_interval_;
+    link->retry_interval_ *= 2;
+    if (link->retry_interval_ > link->params().max_retry_interval_) {
+        link->retry_interval_ = link->params().max_retry_interval_;
+    }
+
+    LinkAvailabilityTimer* timer = new LinkAvailabilityTimer(this, link);
+
+    AvailabilityTimerMap::value_type val(link, timer);
+    if (availability_timers_.insert(val).second == false) {
+        log_err("ContactManager::handle_link_unavailable: "
+                "error inserting timer for link %s into table!", link->name());
+        delete timer;
+        return;
+    }
+
+    log_debug("link %s unavailable (%s): scheduling retry timer in %d seconds",
+              link->name(), event->reason_to_str(event->reason_), timeout);
+    timer->schedule_in(timeout * 1000);
+}
+
+//----------------------------------------------------------------------
+void
+ContactManager::handle_contact_up(ContactUpEvent* event)
+{
+    oasys::ScopeLock l(&lock_, "ContactManager::handle_contact_up");
+
+    LinkRef link = event->contact_->link();
+    ASSERT(link != NULL);
+
+    if(link->isdeleted())
+    {
+        log_warn("ContactManager::handle_contact_up: "
+                 "link %s is being deleted, not marking its contact up", link->name());
+        return;
+    }
+
+    if (!has_link(link)) {
+        log_warn("ContactManager::handle_contact_up: "
+                 "link %s does not exist", link->name());
+        return;
+    }
+
+    if (link->type() == Link::ONDEMAND || link->type() == Link::ALWAYSON) {
+        log_debug("ContactManager::handle_contact_up: "
+                  "resetting retry interval for link %s: %d -> %d",
+                  link->name(),
+                  link->retry_interval_,
+                  link->params().min_retry_interval_);
+        link->retry_interval_ = link->params().min_retry_interval_;
+    }
+}
+
+//----------------------------------------------------------------------
+LinkRef
+ContactManager::find_link_to(ConvergenceLayer* cl,
+                             const std::string& nexthop,
+                             const EndpointID& remote_eid,
+                             Link::link_type_t type,
+                             u_int states)
+{
+    oasys::ScopeLock l(&lock_, "ContactManager::find_link_to");
+    
+    LinkSet::iterator iter;
+    LinkRef link("ContactManager::find_link_to: return value");
+    
+    log_debug("find_link_to: cl %s nexthop %s remote_eid %s "
+              "type %s states 0x%x",
+              cl ? cl->name() : "ANY",
+              nexthop.c_str(), remote_eid.c_str(),
+              type == Link::LINK_INVALID ? "ANY" : Link::link_type_to_str(type),
+              states);
+
+    // make sure some sane criteria was specified
+    ASSERT((cl != NULL) ||
+           (nexthop != "") ||
+           (remote_eid != EndpointID::NULL_EID()) ||
+           (type != Link::LINK_INVALID));
+    
+    for (iter = links_->begin(); iter != links_->end(); ++iter) {
+        if ( ((type == Link::LINK_INVALID) || (type == (*iter)->type())) &&
+             ((cl == NULL) || ((*iter)->clayer() == cl)) &&
+             ((nexthop == "") || (nexthop == (*iter)->nexthop())) &&
+             ((remote_eid == EndpointID::NULL_EID()) ||
+              (remote_eid == (*iter)->remote_eid())) &&
+             ((states & (*iter)->state()) != 0) )
+        {
+            link = *iter;
+            log_debug("ContactManager::find_link_to: "
+                      "matched link *%p", link.object());
+            ASSERT(!link->isdeleted());
+            return link;
+        }
+    }
+
+    log_debug("ContactManager::find_link_to: no match");
+    return link;
+}
+
+//----------------------------------------------------------------------
+LinkRef
+ContactManager::new_opportunistic_link(ConvergenceLayer* cl,
+                                       const std::string& nexthop,
+                                       const EndpointID& remote_eid,
+                                       const std::string* link_name)
+{
+    log_debug("new_opportunistic_link: cl %s nexthop %s remote_eid %s",
+              cl->name(), nexthop.c_str(), remote_eid.c_str());
+    
+    oasys::ScopeLock l(&lock_, "ContactManager::new_opportunistic_link");
+
+    // find a unique link name
+    char name[64];
+    
+    if (link_name) {
+        strncpy(name, link_name->c_str(), sizeof(name));
+        
+        while (find_link(name) != NULL) {
+            snprintf(name, sizeof(name), "%s-%d",
+                     link_name->c_str(), opportunistic_cnt_);
+                     
+            opportunistic_cnt_++;
+        }
+    }
+    
+    else {
+        do {
+            snprintf(name, sizeof(name), "link-%d",
+                    opportunistic_cnt_); 
+            opportunistic_cnt_++;
+        } while (find_link(name) != NULL);
+    }
+        
+    LinkRef link = Link::create_link(name, Link::OPPORTUNISTIC, cl,
+                                     nexthop.c_str(), 0, NULL);
+    if (link == NULL) {
+        log_warn("ContactManager::new_opportunistic_link: "
+                 "unexpected error creating opportunistic link");
+        return link;
+    }
+
+    LinkRef new_link(link.object(),
+                     "ContactManager::new_opportunistic_link: return value");
+    
+    new_link->set_remote_eid(remote_eid);
+
+    if (!add_new_link(new_link)) {
+        new_link->delete_link();
+        log_err("ContactManager::new_opportunistic_link: "
+                 "failed to add new opportunistic link %s", new_link->name());
+        new_link = NULL;
+    }
+    
+    return new_link;
+}
+    
+//----------------------------------------------------------------------
+void
+ContactManager::dump(oasys::StringBuffer* buf) const
+{
+    oasys::ScopeLock l(&lock_, "ContactManager::dump");
+    
+    buf->append("Links:\n");
+    LinkSet::iterator iter;
+    for (iter = links_->begin(); iter != links_->end(); ++iter) {
+        buf->appendf("*%p\n", (*iter).object());
+    }
+}
+
+} // namespace dtn