Skip to content

Commit

Permalink
Add initial support for aliased multi-resources. More efficient locki…
Browse files Browse the repository at this point in the history
…ng to avoid deadlocking the server during awaken/teardown of resources.
  • Loading branch information
connormanning committed Nov 28, 2017
1 parent 698d75c commit 3818d78
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 141 deletions.
56 changes: 24 additions & 32 deletions greyhound/manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,6 @@ namespace
}
}

TimedResource::TimedResource(SharedResource resource)
: m_resource(resource)
, m_touched(getNow())
{ }

void TimedResource::touch() { m_touched = getNow(); }
std::size_t TimedResource::since() const { return secondsSince(m_touched); }

Manager::Manager(const Configuration& config)
: m_cache(
config["cacheSize"].isString() ?
Expand All @@ -64,23 +56,7 @@ Manager::Manager(const Configuration& config)
m_headers.emplace("Access-Control-Allow-Headers", "Content-Type");

m_timeoutSeconds = std::max<double>(
60.0 * config["resourceTimeoutMinutes"].asDouble(), 30);
m_lastSweep = getNow();

auto loop([this]()
{
std::unique_lock<std::mutex> lock(m_mutex);
while (!m_done)
{
m_cv.wait_for(lock, std::chrono::seconds(m_timeoutSeconds), [this]()
{
return m_done || secondsSince(m_lastSweep) > m_timeoutSeconds;
});
sweep();
}
});

m_sweepThread = std::thread(loop);
60.0 * config["resourceTimeoutMinutes"].asDouble(), 15);

std::cout << "Settings:" << std::endl;
std::cout << "\tCache: " << m_cache.maxBytes() << " bytes" << std::endl;
Expand Down Expand Up @@ -108,6 +84,23 @@ Manager::Manager(const Configuration& config)
std::cout << "\tFailure timeout: " << m_auth->badSeconds() << "s" <<
std::endl;
}

m_lastSweep = getNow();

auto loop([this]()
{
std::unique_lock<std::mutex> lock(m_mutex);
while (!m_done)
{
m_cv.wait_for(lock, std::chrono::seconds(m_timeoutSeconds), [this]()
{
return m_done || secondsSince(m_lastSweep) > m_timeoutSeconds;
});
sweep();
}
});

m_sweepThread = std::thread(loop);
}

Manager::~Manager()
Expand All @@ -123,16 +116,15 @@ Manager::~Manager()
void Manager::sweep()
{
m_lastSweep = getNow();
auto it(m_resources.begin());
while (it != m_resources.end())
for (auto it(m_readers.begin()); it != m_readers.end(); ++it)
{
if (it->second.since() > m_timeoutSeconds)
TimedReader& tr(it->second);
std::lock_guard<std::mutex> lock(tr.mutex());
if (tr.exists() && tr.since() > m_timeoutSeconds)
{
std::cout << "Purging " << it->first << std::endl;
m_cache.release(it->second.get()->reader());
it = m_resources.erase(it);
tr.reset();
return;
}
else ++it;
}
}

Expand Down
78 changes: 52 additions & 26 deletions greyhound/manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,6 @@
namespace greyhound
{

class TimedResource
{
public:
TimedResource(SharedResource resource);

SharedResource& get() { return m_resource; }
void touch();
std::size_t since() const;

private:
SharedResource m_resource;
TimePoint m_touched;
};

class Manager
{
public:
Expand All @@ -49,6 +35,13 @@ class Manager
const Configuration& config() const { return m_config; }

private:
std::vector<std::string> resolve(std::string name) const
{
// TODO If this name refers to a multi-resource, aggregate its
// constituent names here. For now, this is one-to-one.
return std::vector<std::string>{ name };
}

void sweep();

mutable entwine::Cache m_cache;
Expand All @@ -60,7 +53,8 @@ class Manager

const Configuration& m_config;

std::map<std::string, TimedResource> m_resources;
std::map<std::string, TimedReader> m_readers;
std::map<std::string, SharedResource> m_resources;
std::unique_ptr<Auth> m_auth;

mutable std::mutex m_mutex;
Expand All @@ -75,25 +69,57 @@ class Manager
template<typename Req>
SharedResource Manager::get(std::string name, Req& req)
{
std::lock_guard<std::mutex> lock(m_mutex);
std::unique_lock<std::mutex> lock(m_mutex);

if (m_auth)
auto it(m_resources.find(name));
if (it == m_resources.end())
{
const auto code(m_auth->check(name, req));
if (!ok(code)) throw HttpError(code, "Authorization failure");
std::vector<TimedReader*> readers;

for (const auto s : resolve(name))
{
auto rit(m_readers.find(s));
if (rit == m_readers.end())
{
rit = m_readers.emplace(
std::piecewise_construct,
std::forward_as_tuple(s),
std::forward_as_tuple(*this, s)).first;
}

readers.push_back(&rit->second);
}

it = m_resources.emplace(
name,
std::make_shared<Resource>(*this, name, readers)).first;
}

auto it(m_resources.find(name));
if (it == m_resources.end())
SharedResource resource(it->second);

lock.unlock();

for (TimedReader* reader : resource->readers())
{
if (auto resource = Resource::create(*this, name))
const auto name(reader->name());
if (m_auth)
{
const auto code(m_auth->check(name, req));
if (!ok(code))
{
throw HttpError(code, "Authorization failure: " + name);
}
}

if (!reader->get())
{
it = m_resources.insert(std::make_pair(name, resource)).first;
throw HttpError(
HttpStatusCode::client_error_not_found,
"Not found: " + name);
}
else return SharedResource();
}
it->second.touch();
return it->second.get();

return resource;
}

} // namespace greyhound
Expand Down

0 comments on commit 3818d78

Please sign in to comment.