Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor router code #728

Merged
merged 1 commit into from Jul 25, 2019
Merged

Conversation

tewinget
Copy link
Collaborator

This PR refactors a lot of the functionality that is "kitchen sink"ed into router.cpp into separate classes.

This PR will be squashed before merge.

There are also some minor behavior changes along with this refactor as I came across behavior that was either incorrect or sub-optimal.

llarp/link/i_link_manager.hpp Show resolved Hide resolved
{
{
util::Lock l(&_mutex);
if (stopping) return nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't need to lock to check an atomic

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed.

LinkManager::SendTo(const RouterID& remote, const llarp_buffer_t& buf)
{
{
util::Lock l(&_mutex);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lock again

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed.

if (!stopping)
{
LogInfo("stopping links");
stopping.store(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stopping = true;

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed.

llarp/link/link_manager.cpp Show resolved Hide resolved
{
DoCallback(msg.second, SendStatus::Success);
}
DoCallback(msg.second, SendStatus::Congestion);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this what you mean? we always call the congestion func rn

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean by this, but I don't see any congestion function anywhere in the old SendToOrQueue codepath.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

struct Logic;
enum class SessionResult;

struct OutboundMessageHandler : public IOutboundMessageHandler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed.


LogInfo("session with ", router, " established");

if (not _rcLookup->RemoteIsAllowed(router))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually prefer the not token over !

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does MSVC support it yet (without #include <ciso646>)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually good question. I'm not sure. you just need iso646.h (since c99) but msvc is weird and is a c++ exclusive compiler 🤷‍♀

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update on this? Also, perhaps make format can change to the preferred style? Not sure, haven't really used it, but I imagine it'd be cool if it could.

llarp/router/outbound_session_maker.cpp Show resolved Hide resolved
}

void
Init(LinkManager *linkManager,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hate init methods. why not a constructor which does this?

[&](const RouterContact &rc) {
if(HavePendingLookup(rc.pubkey))
return;
GetRC(rc.pubkey, nullptr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetRC calls stuff that acquires a lock that is held while visiting.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's...gonna be fun to resolve.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed.

}

void
RCLookupHandler::Init(llarp_dht_context *dht,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be the constructor.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above, not changed.

}

void
LinkManager::Init(IOutboundSessionMaker *sessionMaker)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should ALSO be the constructor.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So for objects which need to point to one another you would end up mixing and matching this style and in-constructor init of pointers. Ideally that situation wouldn't occur, but it's non-trivial to avoid. In order to have consistency among these new classes I opted for this style.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am segfaulting in my local testing because a timer is called with a member being nullptr and i assumed it's happpening before calling init

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Inits are run during Router::FromConfig. If that's not early enough then the lack of segfault before was just masking something using Router before it was fully configured/initialized.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After further discussion, leaving it as-is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Init functions like this are evils in API design :( but okay

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can definitely agree with arguments against doing it this way. It's definitely something to revisit in the future.

}

void
OutboundSessionMaker::Init(ILinkManager *linkManager,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also this should be a constructor. :>

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above, not changed.

}

void
OutboundMessageHandler::Init(ILinkManager* linkManager,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also a constructor

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above, not changed.

false
);

auto sz = connectedRouters.size();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be const

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed.

std::unordered_map< RouterID, llarp_time_t, RouterID::Hash >
m_PersistingSessions GUARDED_BY(_mutex);

IOutboundSessionMaker *_sessionMaker;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be a const * ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only if I change it to be set in the constructor as @michael-loki suggested, but in case / because some of these things need to point to one another I opted to set all of their pointers in an Init function. Naturally the pointers are private to at least mostly mitigate the risk of accidentally changing them rather than using them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose I could make these pointers const and cast the const away in the Init function, but that also feels messy. Thoughts?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not casting const away, no change.

case SessionResult::NoLink:
OnNoLink(router);
break;
default:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should probably error on default case "just in case"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess, but that would be a very interesting case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added LogError and abort.

std::unordered_map< RouterID, MessageQueue, RouterID::Hash >
outboundMessageQueue GUARDED_BY(_mutex);

ILinkManager* _linkManager;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this also be a const *?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above, not changed.

}
else
{
//TODO: shouldn't happen, how to handle?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe abort() ? probably log error

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added LogError but not abort.

ILinkManager *_linkManager;
I_RCLookupHandler *_rcLookup;
std::shared_ptr< Logic > _logic;
llarp_nodedb *_nodedb;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const *

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above, not changed.

RouterID::Hash >
pendingCallbacks GUARDED_BY(_mutex);

ILinkManager *_linkManager;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* const (i kept on saying const * >.>)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which the compiler corrected several times as a result...

// update nodedb if required
if (rc.IsPublicRouter())
{
_nodedb->UpdateAsyncIfNewer(rc);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't update the inserted timestamp so it either should be InsertAsync or make UpdateAsyncIfNewer change insertion timestamps.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think changing UpdateAsyncIfNewer is cleaner, which do you prefer?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changing UpdateAsyncIfNewer works fine

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.


while (not callbackQueue.empty())
{
callbackQueue.front()(router, rc, result);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if the callback eventually wants to acquire the RCLookupHandler lock again?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair point. I don't particularly like the idea of collecting the callbacks in a local container first (and then unlocking) but that's probably for the best.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed.


mutable util::Mutex _mutex; // protects pendingCallbacks, whitelistRouters

llarp_dht_context *_dht = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* const

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above, not changed.

}

void
LinkManager::Init(IOutboundSessionMaker *sessionMaker)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Init functions like this are evils in API design :( but okay

};

using RouterCallback =
std::function< void(const RouterID, const SessionResult) >;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const RouterID&?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, will change now.

void
OutboundMessageHandler::OnConnectFailure(const RouterID &router)
{
FinalizeRequest(router, SendStatus::Timeout);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is a connect failure always a timeout?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when this func is called, yes. Changing func name.

{
DoCallback(msg.second, SendStatus::Success);
}
DoCallback(msg.second, SendStatus::Congestion);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


LinkLayer_ptr link = _linkManager->GetCompatibleLink(rc);

if(link == nullptr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if(!link)?

{
collectedCallbacks.push_back(callbackQueue.front());
callbackQueue.pop();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could be simplified to be:

    std::vector< RCRequestCallback > collectedCallbacks;
    {
      util::Lock l(&_mutex);

      auto itr = pendingCallbacks.find(router);

      if(itr != pendingCallbacks.end())
      {
        auto &callbackQueue = itr->second;

        if(callbackQueue.size())
        {
          collectedCallbacks.reserve(callbackQueue.size());
        }

        collectedCallbacks.assign(callbackQueue.begin(), callbackQueue.end());

        pendingCallbacks.erase(itr);
      }
    }  // lock

    for(const auto &callback : collectedCallbacks)
    {
      callback(router, rc, result);
    }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, probably. neat.

@@ -25,8 +25,6 @@ namespace llarp
return false;
}

buf.sz = buf.cur - buf.base;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ebin af

{
// TODO: do we want to keep it

const auto &router = RouterID(session->GetPubKey());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const RouterID router{session->GetPubKey()};


CreatePendingSession(router);

LogWarn("Creating session establish attempt to ", router, " .");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a stray LogWarn

{
// TODO: retry/num attempts

LogWarn("Session establish attempt to ", RouterID(session->GetPubKey()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm this is fine

llarp_dht_context *_dht = nullptr;
llarp_nodedb *_nodedb = nullptr;
std::shared_ptr< llarp::thread::ThreadPool > _threadpool;
service::Context *_hiddenServiceContext;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should set to nullptr like the other bare pointers

llarp_nodedb *_nodedb = nullptr;
std::shared_ptr< llarp::thread::ThreadPool > _threadpool;
service::Context *_hiddenServiceContext;
ILinkManager *_linkManager;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also this too

Copy link
Contributor

@majestrate majestrate left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

squash it

@majestrate
Copy link
Contributor

100 comments and a 3 way merge conflict ain't one :>

This commit refactors functionality from the Router class into separate,
dedicated classes.
There are a few behavior changes that came as a result of discussion on
what the correct behavior should be.
In addition, many things Router was previously doing can now be provided
callback functions to alert the calling point when the asynchronous
action completes, successfully or otherwise.
@majestrate
Copy link
Contributor

YOLO!

@majestrate majestrate merged commit f7e05ad into oxen-io:master Jul 25, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants