2015-06-23 01:49:14 +02:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <atomic>
|
|
|
|
|
|
|
|
|
|
#include "sync.hh"
|
2016-03-09 14:30:13 +01:00
|
|
|
|
#include "types.hh"
|
|
|
|
|
|
|
|
|
|
namespace nix {
|
|
|
|
|
|
|
|
|
|
MakeError(NoTokens, Error)
|
2015-06-23 01:49:14 +02:00
|
|
|
|
|
|
|
|
|
/* This class hands out tokens. There are only ‘maxTokens’ tokens
|
2016-03-09 14:30:13 +01:00
|
|
|
|
available. Calling get(N) will return a Token object, representing
|
|
|
|
|
ownership of N tokens. If the requested number of tokens is
|
|
|
|
|
unavailable, get() will sleep until another thread returns a
|
|
|
|
|
token. */
|
2015-06-23 01:49:14 +02:00
|
|
|
|
|
|
|
|
|
class TokenServer
|
|
|
|
|
{
|
2016-03-09 14:30:13 +01:00
|
|
|
|
const size_t maxTokens;
|
2015-06-23 01:49:14 +02:00
|
|
|
|
|
2016-03-09 14:30:13 +01:00
|
|
|
|
Sync<size_t> inUse{0};
|
2016-02-24 14:04:31 +01:00
|
|
|
|
std::condition_variable wakeup;
|
2015-06-23 01:49:14 +02:00
|
|
|
|
|
|
|
|
|
public:
|
2016-03-09 14:30:13 +01:00
|
|
|
|
TokenServer(size_t maxTokens) : maxTokens(maxTokens) { }
|
2015-06-23 01:49:14 +02:00
|
|
|
|
|
|
|
|
|
class Token
|
|
|
|
|
{
|
|
|
|
|
friend TokenServer;
|
|
|
|
|
|
|
|
|
|
TokenServer * ts;
|
|
|
|
|
|
2016-03-09 14:30:13 +01:00
|
|
|
|
size_t tokens;
|
|
|
|
|
|
2015-06-23 01:49:14 +02:00
|
|
|
|
bool acquired = false;
|
|
|
|
|
|
2016-03-09 14:30:13 +01:00
|
|
|
|
Token(TokenServer * ts, size_t tokens, unsigned int timeout)
|
|
|
|
|
: ts(ts), tokens(tokens)
|
2015-06-23 01:49:14 +02:00
|
|
|
|
{
|
2016-03-09 14:30:13 +01:00
|
|
|
|
if (tokens >= ts->maxTokens)
|
2016-03-09 16:59:35 +01:00
|
|
|
|
throw NoTokens(format("requesting more tokens (%d) than exist (%d)") % tokens % ts->maxTokens);
|
2016-11-16 17:46:00 +01:00
|
|
|
|
debug("acquiring %d tokens", tokens);
|
2016-03-09 14:30:13 +01:00
|
|
|
|
auto inUse(ts->inUse.lock());
|
|
|
|
|
while (*inUse + tokens > ts->maxTokens)
|
2015-06-23 01:49:14 +02:00
|
|
|
|
if (timeout) {
|
2016-03-09 14:30:13 +01:00
|
|
|
|
if (!inUse.wait_for(ts->wakeup, std::chrono::seconds(timeout),
|
|
|
|
|
[&]() { return *inUse + tokens <= ts->maxTokens; }))
|
2015-06-23 01:49:14 +02:00
|
|
|
|
return;
|
|
|
|
|
} else
|
2016-03-09 14:30:13 +01:00
|
|
|
|
inUse.wait(ts->wakeup);
|
|
|
|
|
*inUse += tokens;
|
2015-06-23 01:49:14 +02:00
|
|
|
|
acquired = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
2016-11-16 17:46:00 +01:00
|
|
|
|
Token(Token && t) : ts(t.ts), tokens(t.tokens), acquired(t.acquired)
|
|
|
|
|
{
|
|
|
|
|
t.ts = 0;
|
|
|
|
|
t.acquired = false;
|
|
|
|
|
}
|
2015-06-23 01:49:14 +02:00
|
|
|
|
Token(const Token & l) = delete;
|
|
|
|
|
|
|
|
|
|
~Token()
|
|
|
|
|
{
|
|
|
|
|
if (!ts || !acquired) return;
|
2016-11-16 17:46:00 +01:00
|
|
|
|
give_back(tokens);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool operator ()() { return acquired; }
|
|
|
|
|
|
|
|
|
|
void give_back(size_t t)
|
|
|
|
|
{
|
|
|
|
|
debug("returning %d tokens", t);
|
|
|
|
|
if (!t) return;
|
|
|
|
|
assert(acquired);
|
|
|
|
|
assert(t <= tokens);
|
2015-06-23 01:49:14 +02:00
|
|
|
|
{
|
2016-03-09 14:30:13 +01:00
|
|
|
|
auto inUse(ts->inUse.lock());
|
2016-11-16 17:46:00 +01:00
|
|
|
|
assert(*inUse >= t);
|
|
|
|
|
*inUse -= t;
|
|
|
|
|
tokens -= t;
|
2015-06-23 01:49:14 +02:00
|
|
|
|
}
|
2016-11-16 17:46:00 +01:00
|
|
|
|
// FIXME: inefficient. Should wake up waiters that can
|
|
|
|
|
// proceed now.
|
|
|
|
|
ts->wakeup.notify_all();
|
2015-06-23 01:49:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
2016-03-09 14:30:13 +01:00
|
|
|
|
Token get(size_t tokens = 1, unsigned int timeout = 0)
|
2015-06-23 01:49:14 +02:00
|
|
|
|
{
|
2016-03-09 14:30:13 +01:00
|
|
|
|
return Token(this, tokens, timeout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t currentUse()
|
|
|
|
|
{
|
|
|
|
|
auto inUse_(inUse.lock());
|
|
|
|
|
return *inUse_;
|
2015-06-23 01:49:14 +02:00
|
|
|
|
}
|
|
|
|
|
};
|
2016-03-09 14:30:13 +01:00
|
|
|
|
|
|
|
|
|
}
|