Implement RPCTimerHandler for Qt RPC console

Implement RPCTimerHandler for Qt RPC console, so that `walletpassphrase`
works with GUI and `-server=0`.

Also simplify HTTPEvent-related code by using boost::function directly.
This commit is contained in:
Wladimir J. van der Laan 2015-08-28 16:46:20 +02:00
parent 57d85d9bee
commit be33f3f50b
7 changed files with 67 additions and 50 deletions

View File

@ -19,24 +19,16 @@
class HTTPRPCTimer : public RPCTimerBase class HTTPRPCTimer : public RPCTimerBase
{ {
public: public:
HTTPRPCTimer(struct event_base* eventBase, boost::function<void(void)>& func, int64_t seconds) : ev(eventBase, false, new Handler(func)) HTTPRPCTimer(struct event_base* eventBase, boost::function<void(void)>& func, int64_t millis) :
ev(eventBase, false, func)
{ {
struct timeval tv = {seconds, 0}; struct timeval tv;
tv.tv_sec = millis/1000;
tv.tv_usec = (millis%1000)*1000;
ev.trigger(&tv); ev.trigger(&tv);
} }
private: private:
HTTPEvent ev; HTTPEvent ev;
class Handler : public HTTPClosure
{
public:
Handler(const boost::function<void(void)>& func) : func(func)
{
}
private:
boost::function<void(void)> func;
void operator()() { func(); }
};
}; };
class HTTPRPCTimerInterface : public RPCTimerInterface class HTTPRPCTimerInterface : public RPCTimerInterface
@ -49,9 +41,9 @@ public:
{ {
return "HTTP"; return "HTTP";
} }
RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t seconds) RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t millis)
{ {
return new HTTPRPCTimer(base, func, seconds); return new HTTPRPCTimer(base, func, millis);
} }
private: private:
struct event_base* base; struct event_base* base;

View File

@ -412,18 +412,15 @@ struct event_base* EventBase()
static void httpevent_callback_fn(evutil_socket_t, short, void* data) static void httpevent_callback_fn(evutil_socket_t, short, void* data)
{ {
// Static handler simply passes through execution flow to _handle method // Static handler: simply call inner handler
((HTTPEvent*)data)->_handle(); HTTPEvent *self = ((HTTPEvent*)data);
self->handler();
if (self->deleteWhenTriggered)
delete self;
} }
void HTTPEvent::_handle() HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const boost::function<void(void)>& handler):
{ deleteWhenTriggered(deleteWhenTriggered), handler(handler)
(*handler)();
if (deleteWhenTriggered)
delete this;
}
HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, HTTPClosure* handler) : deleteWhenTriggered(deleteWhenTriggered), handler(handler)
{ {
ev = event_new(base, -1, 0, httpevent_callback_fn, this); ev = event_new(base, -1, 0, httpevent_callback_fn, this);
assert(ev); assert(ev);
@ -496,20 +493,6 @@ void HTTPRequest::WriteHeader(const std::string& hdr, const std::string& value)
* Replies must be sent in the main loop in the main http thread, * Replies must be sent in the main loop in the main http thread,
* this cannot be done from worker threads. * this cannot be done from worker threads.
*/ */
struct HTTPSendReplyHandler : HTTPClosure {
public:
HTTPSendReplyHandler(struct evhttp_request* req, int nStatus) : req(req), nStatus(nStatus)
{
}
void operator()()
{
evhttp_send_reply(req, nStatus, NULL, NULL);
}
private:
struct evhttp_request* req;
int nStatus;
};
void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
{ {
assert(!replySent && req); assert(!replySent && req);
@ -518,7 +501,7 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
assert(evb); assert(evb);
evbuffer_add(evb, strReply.data(), strReply.size()); evbuffer_add(evb, strReply.data(), strReply.size());
HTTPEvent* ev = new HTTPEvent(eventBase, true, HTTPEvent* ev = new HTTPEvent(eventBase, true,
new HTTPSendReplyHandler(req, nStatus)); boost::bind(evhttp_send_reply, req, nStatus, (const char*)NULL, (struct evbuffer *)NULL));
ev->trigger(0); ev->trigger(0);
replySent = true; replySent = true;
req = 0; // transferred back to main thread req = 0; // transferred back to main thread

View File

@ -117,8 +117,11 @@ public:
class HTTPEvent class HTTPEvent
{ {
public: public:
/** Create a new event */ /** Create a new event.
HTTPEvent(struct event_base* base, bool deleteWhenTriggered, HTTPClosure* handler); * deleteWhenTriggered deletes this event object after the event is triggered (and the handler called)
* handler is the handler to call when the event is triggered.
*/
HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const boost::function<void(void)>& handler);
~HTTPEvent(); ~HTTPEvent();
/** Trigger the event. If tv is 0, trigger it immediately. Otherwise trigger it after /** Trigger the event. If tv is 0, trigger it immediately. Otherwise trigger it after
@ -126,13 +129,10 @@ public:
*/ */
void trigger(struct timeval* tv); void trigger(struct timeval* tv);
/** Internal function for handling, do not call directly */
void _handle();
private:
bool deleteWhenTriggered; bool deleteWhenTriggered;
boost::function<void(void)> handler;
private:
struct event* ev; struct event* ev;
boost::scoped_ptr<HTTPClosure> handler;
}; };
#endif // BITCOIN_HTTPSERVER_H #endif // BITCOIN_HTTPSERVER_H

View File

@ -28,6 +28,7 @@
#include <QScrollBar> #include <QScrollBar>
#include <QThread> #include <QThread>
#include <QTime> #include <QTime>
#include <QTimer>
#if QT_VERSION < 0x050000 #if QT_VERSION < 0x050000
#include <QUrl> #include <QUrl>
@ -66,6 +67,40 @@ Q_SIGNALS:
void reply(int category, const QString &command); void reply(int category, const QString &command);
}; };
/** Class for handling RPC timers
* (used for e.g. re-locking the wallet after a timeout)
*/
class QtRPCTimerBase: public QObject, public RPCTimerBase
{
Q_OBJECT
public:
QtRPCTimerBase(boost::function<void(void)>& func, int64_t millis):
func(func)
{
timer.setSingleShot(true);
connect(&timer, SIGNAL(timeout()), this, SLOT(timeout()));
timer.start(millis);
}
~QtRPCTimerBase() {}
private Q_SLOTS:
void timeout() { func(); }
private:
QTimer timer;
boost::function<void(void)> func;
};
class QtRPCTimerInterface: public RPCTimerInterface
{
public:
~QtRPCTimerInterface() {}
const char *Name() { return "Qt"; }
RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t millis)
{
return new QtRPCTimerBase(func, millis);
}
};
#include "rpcconsole.moc" #include "rpcconsole.moc"
/** /**
@ -232,6 +267,9 @@ RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) :
ui->label_berkeleyDBVersion->hide(); ui->label_berkeleyDBVersion->hide();
ui->berkeleyDBVersion->hide(); ui->berkeleyDBVersion->hide();
#endif #endif
// Register RPC timer interface
rpcTimerInterface = new QtRPCTimerInterface();
RPCRegisterTimerInterface(rpcTimerInterface);
startExecutor(); startExecutor();
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
@ -246,6 +284,8 @@ RPCConsole::~RPCConsole()
{ {
GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this); GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this);
Q_EMIT stopExecutor(); Q_EMIT stopExecutor();
RPCUnregisterTimerInterface(rpcTimerInterface);
delete rpcTimerInterface;
delete ui; delete ui;
} }

View File

@ -14,6 +14,7 @@
class ClientModel; class ClientModel;
class PlatformStyle; class PlatformStyle;
class RPCTimerInterface;
namespace Ui { namespace Ui {
class RPCConsole; class RPCConsole;
@ -108,6 +109,7 @@ private:
NodeId cachedNodeid; NodeId cachedNodeid;
QMenu *contextMenu; QMenu *contextMenu;
const PlatformStyle *platformStyle; const PlatformStyle *platformStyle;
RPCTimerInterface *rpcTimerInterface;
}; };
#endif // BITCOIN_QT_RPCCONSOLE_H #endif // BITCOIN_QT_RPCCONSOLE_H

View File

@ -562,7 +562,7 @@ void RPCRunLater(const std::string& name, boost::function<void(void)> func, int6
deadlineTimers.erase(name); deadlineTimers.erase(name);
RPCTimerInterface* timerInterface = timerInterfaces[0]; RPCTimerInterface* timerInterface = timerInterfaces[0];
LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
deadlineTimers.insert(std::make_pair(name, timerInterface->NewTimer(func, nSeconds))); deadlineTimers.insert(std::make_pair(name, timerInterface->NewTimer(func, nSeconds*1000)));
} }
const CRPCTable tableRPC; const CRPCTable tableRPC;

View File

@ -92,12 +92,12 @@ public:
/** Implementation name */ /** Implementation name */
virtual const char *Name() = 0; virtual const char *Name() = 0;
/** Factory function for timers. /** Factory function for timers.
* RPC will call the function to create a timer that will call func in *seconds* seconds. * RPC will call the function to create a timer that will call func in *millis* milliseconds.
* @note As the RPC mechanism is backend-neutral, it can use different implementations of timers. * @note As the RPC mechanism is backend-neutral, it can use different implementations of timers.
* This is needed to cope with the case in which there is no HTTP server, but * This is needed to cope with the case in which there is no HTTP server, but
* only GUI RPC console, and to break the dependency of pcserver on httprpc. * only GUI RPC console, and to break the dependency of pcserver on httprpc.
*/ */
virtual RPCTimerBase* NewTimer(boost::function<void(void)>&, int64_t) = 0; virtual RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t millis) = 0;
}; };
/** Register factory function for timers */ /** Register factory function for timers */