From be33f3f50b7358bbad9e16bf730fac2ab3c4886b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 28 Aug 2015 16:46:20 +0200 Subject: [PATCH] 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. --- src/httprpc.cpp | 22 +++++++--------------- src/httpserver.cpp | 33 ++++++++------------------------- src/httpserver.h | 14 +++++++------- src/qt/rpcconsole.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ src/qt/rpcconsole.h | 2 ++ src/rpcserver.cpp | 2 +- src/rpcserver.h | 4 ++-- 7 files changed, 67 insertions(+), 50 deletions(-) diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 570beadc5..98ac750bb 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -19,24 +19,16 @@ class HTTPRPCTimer : public RPCTimerBase { public: - HTTPRPCTimer(struct event_base* eventBase, boost::function& func, int64_t seconds) : ev(eventBase, false, new Handler(func)) + HTTPRPCTimer(struct event_base* eventBase, boost::function& 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); } private: HTTPEvent ev; - - class Handler : public HTTPClosure - { - public: - Handler(const boost::function& func) : func(func) - { - } - private: - boost::function func; - void operator()() { func(); } - }; }; class HTTPRPCTimerInterface : public RPCTimerInterface @@ -49,9 +41,9 @@ public: { return "HTTP"; } - RPCTimerBase* NewTimer(boost::function& func, int64_t seconds) + RPCTimerBase* NewTimer(boost::function& func, int64_t millis) { - return new HTTPRPCTimer(base, func, seconds); + return new HTTPRPCTimer(base, func, millis); } private: struct event_base* base; diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 89366b2e4..13f870567 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -412,18 +412,15 @@ struct event_base* EventBase() static void httpevent_callback_fn(evutil_socket_t, short, void* data) { - // Static handler simply passes through execution flow to _handle method - ((HTTPEvent*)data)->_handle(); + // Static handler: simply call inner handler + HTTPEvent *self = ((HTTPEvent*)data); + self->handler(); + if (self->deleteWhenTriggered) + delete self; } -void HTTPEvent::_handle() -{ - (*handler)(); - if (deleteWhenTriggered) - delete this; -} - -HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, HTTPClosure* handler) : deleteWhenTriggered(deleteWhenTriggered), handler(handler) +HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const boost::function& handler): + deleteWhenTriggered(deleteWhenTriggered), handler(handler) { ev = event_new(base, -1, 0, httpevent_callback_fn, this); 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, * 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) { assert(!replySent && req); @@ -518,7 +501,7 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) assert(evb); evbuffer_add(evb, strReply.data(), strReply.size()); 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); replySent = true; req = 0; // transferred back to main thread diff --git a/src/httpserver.h b/src/httpserver.h index c6a780419..648e8b6f8 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -117,8 +117,11 @@ public: class HTTPEvent { public: - /** Create a new event */ - HTTPEvent(struct event_base* base, bool deleteWhenTriggered, HTTPClosure* handler); + /** Create a new event. + * 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& handler); ~HTTPEvent(); /** Trigger the event. If tv is 0, trigger it immediately. Otherwise trigger it after @@ -126,13 +129,10 @@ public: */ void trigger(struct timeval* tv); - /** Internal function for handling, do not call directly */ - void _handle(); - -private: bool deleteWhenTriggered; + boost::function handler; +private: struct event* ev; - boost::scoped_ptr handler; }; #endif // BITCOIN_HTTPSERVER_H diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index d9d4f1d0e..b742a47c9 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #if QT_VERSION < 0x050000 #include @@ -66,6 +67,40 @@ Q_SIGNALS: 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& 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 func; +}; + +class QtRPCTimerInterface: public RPCTimerInterface +{ +public: + ~QtRPCTimerInterface() {} + const char *Name() { return "Qt"; } + RPCTimerBase* NewTimer(boost::function& func, int64_t millis) + { + return new QtRPCTimerBase(func, millis); + } +}; + + #include "rpcconsole.moc" /** @@ -232,6 +267,9 @@ RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) : ui->label_berkeleyDBVersion->hide(); ui->berkeleyDBVersion->hide(); #endif + // Register RPC timer interface + rpcTimerInterface = new QtRPCTimerInterface(); + RPCRegisterTimerInterface(rpcTimerInterface); startExecutor(); setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); @@ -246,6 +284,8 @@ RPCConsole::~RPCConsole() { GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this); Q_EMIT stopExecutor(); + RPCUnregisterTimerInterface(rpcTimerInterface); + delete rpcTimerInterface; delete ui; } diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index b94efee84..1409fca52 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -14,6 +14,7 @@ class ClientModel; class PlatformStyle; +class RPCTimerInterface; namespace Ui { class RPCConsole; @@ -108,6 +109,7 @@ private: NodeId cachedNodeid; QMenu *contextMenu; const PlatformStyle *platformStyle; + RPCTimerInterface *rpcTimerInterface; }; #endif // BITCOIN_QT_RPCCONSOLE_H diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 5d7e2125e..b831d3d3b 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -562,7 +562,7 @@ void RPCRunLater(const std::string& name, boost::function func, int6 deadlineTimers.erase(name); RPCTimerInterface* timerInterface = timerInterfaces[0]; 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; diff --git a/src/rpcserver.h b/src/rpcserver.h index ac821d5b5..83cc37918 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -92,12 +92,12 @@ public: /** Implementation name */ virtual const char *Name() = 0; /** 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. * 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. */ - virtual RPCTimerBase* NewTimer(boost::function&, int64_t) = 0; + virtual RPCTimerBase* NewTimer(boost::function& func, int64_t millis) = 0; }; /** Register factory function for timers */