Skip to content

Commit cfe41d7

Browse files
committed
Merge #9387: [Refactor] RAII of libevent stuff using unique ptrs with deleters
05a55a6 Added EVENT_CFLAGS to test makefile to explicitly include libevent headers. (Karl-Johan Alm) 280a559 Added some simple tests for the RAII-style events. (Karl-Johan Alm) 7f7f102 Switched bitcoin-cli.cpp to use RAII unique pointers with deleters. (Karl-Johan Alm) e5534d2 Added std::unique_ptr<> wrappers with deleters for libevent modules. (Karl-Johan Alm)
2 parents c4b7d4f + 05a55a6 commit cfe41d7

File tree

5 files changed

+160
-22
lines changed

5 files changed

+160
-22
lines changed

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ BITCOIN_CORE_H = \
135135
support/allocators/secure.h \
136136
support/allocators/zeroafterfree.h \
137137
support/cleanse.h \
138+
support/events.h \
138139
support/lockedpool.h \
139140
sync.h \
140141
threadsafety.h \

src/Makefile.test.include

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ BITCOIN_TESTS =\
8888
test/policyestimator_tests.cpp \
8989
test/pow_tests.cpp \
9090
test/prevector_tests.cpp \
91+
test/raii_event_tests.cpp \
9192
test/reverselock_tests.cpp \
9293
test/rpc_tests.cpp \
9394
test/sanity_tests.cpp \
@@ -123,9 +124,9 @@ BITCOIN_TESTS += \
123124
endif
124125

125126
test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
126-
test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS)
127+
test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS)
127128
test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
128-
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1)
129+
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS)
129130
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
130131
if ENABLE_WALLET
131132
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)

src/bitcoin-cli.cpp

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@
1717
#include <boost/filesystem/operations.hpp>
1818
#include <stdio.h>
1919

20-
#include <event2/event.h>
21-
#include <event2/http.h>
2220
#include <event2/buffer.h>
2321
#include <event2/keyvalq_struct.h>
22+
#include "support/events.h"
2423

2524
#include <univalue.h>
2625

@@ -190,23 +189,19 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params)
190189
std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT);
191190
int port = GetArg("-rpcport", BaseParams().RPCPort());
192191

193-
// Create event base
194-
struct event_base *base = event_base_new(); // TODO RAII
195-
if (!base)
196-
throw std::runtime_error("cannot create event_base");
192+
// Obtain event base
193+
raii_event_base base = obtain_event_base();
197194

198195
// Synchronously look up hostname
199-
struct evhttp_connection *evcon = evhttp_connection_base_new(base, NULL, host.c_str(), port); // TODO RAII
200-
if (evcon == NULL)
201-
throw std::runtime_error("create connection failed");
202-
evhttp_connection_set_timeout(evcon, GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));
196+
raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
197+
evhttp_connection_set_timeout(evcon.get(), GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));
203198

204199
HTTPReply response;
205-
struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII
200+
raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
206201
if (req == NULL)
207202
throw std::runtime_error("create http request failed");
208203
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
209-
evhttp_request_set_error_cb(req, http_error_cb);
204+
evhttp_request_set_error_cb(req.get(), http_error_cb);
210205
#endif
211206

212207
// Get credentials
@@ -223,28 +218,25 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params)
223218
strRPCUserColonPass = GetArg("-rpcuser", "") + ":" + GetArg("-rpcpassword", "");
224219
}
225220

226-
struct evkeyvalq *output_headers = evhttp_request_get_output_headers(req);
221+
struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
227222
assert(output_headers);
228223
evhttp_add_header(output_headers, "Host", host.c_str());
229224
evhttp_add_header(output_headers, "Connection", "close");
230225
evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
231226

232227
// Attach request data
233228
std::string strRequest = JSONRPCRequestObj(strMethod, params, 1).write() + "\n";
234-
struct evbuffer * output_buffer = evhttp_request_get_output_buffer(req);
229+
struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
235230
assert(output_buffer);
236231
evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
237232

238-
int r = evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/");
233+
int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, "/");
234+
req.release(); // ownership moved to evcon in above call
239235
if (r != 0) {
240-
evhttp_connection_free(evcon);
241-
event_base_free(base);
242236
throw CConnectionFailed("send http request failed");
243237
}
244238

245-
event_base_dispatch(base);
246-
evhttp_connection_free(evcon);
247-
event_base_free(base);
239+
event_base_dispatch(base.get());
248240

249241
if (response.status == 0)
250242
throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error));

src/support/events.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (c) 2016 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_SUPPORT_EVENTS_H
6+
#define BITCOIN_SUPPORT_EVENTS_H
7+
8+
#include <ios>
9+
#include <memory>
10+
11+
#include <event2/event.h>
12+
#include <event2/http.h>
13+
14+
#define MAKE_RAII(type) \
15+
/* deleter */\
16+
struct type##_deleter {\
17+
void operator()(struct type* ob) {\
18+
type##_free(ob);\
19+
}\
20+
};\
21+
/* unique ptr typedef */\
22+
typedef std::unique_ptr<struct type, type##_deleter> raii_##type
23+
24+
MAKE_RAII(event_base);
25+
MAKE_RAII(event);
26+
MAKE_RAII(evhttp);
27+
MAKE_RAII(evhttp_request);
28+
MAKE_RAII(evhttp_connection);
29+
30+
raii_event_base obtain_event_base() {
31+
auto result = raii_event_base(event_base_new());
32+
if (!result.get())
33+
throw std::runtime_error("cannot create event_base");
34+
return result;
35+
}
36+
37+
raii_event obtain_event(struct event_base* base, evutil_socket_t s, short events, event_callback_fn cb, void* arg) {
38+
return raii_event(event_new(base, s, events, cb, arg));
39+
}
40+
41+
raii_evhttp obtain_evhttp(struct event_base* base) {
42+
return raii_evhttp(evhttp_new(base));
43+
}
44+
45+
raii_evhttp_request obtain_evhttp_request(void(*cb)(struct evhttp_request *, void *), void *arg) {
46+
return raii_evhttp_request(evhttp_request_new(cb, arg));
47+
}
48+
49+
raii_evhttp_connection obtain_evhttp_connection_base(struct event_base* base, std::string host, uint16_t port) {
50+
auto result = raii_evhttp_connection(evhttp_connection_base_new(base, NULL, host.c_str(), port));
51+
if (!result.get())
52+
throw std::runtime_error("create connection failed");
53+
return result;
54+
}
55+
56+
#endif // BITCOIN_SUPPORT_EVENTS_H

src/test/raii_event_tests.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright (c) 2016 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <event2/event.h>
6+
#include <map>
7+
#include <stdlib.h>
8+
9+
#include "support/events.h"
10+
11+
#include "test/test_bitcoin.h"
12+
13+
#include <vector>
14+
15+
#include <boost/test/unit_test.hpp>
16+
17+
static std::map<void*, short> tags;
18+
static std::map<void*, uint16_t> orders;
19+
static uint16_t tagSequence = 0;
20+
21+
static void* tag_malloc(size_t sz) {
22+
void* mem = malloc(sz);
23+
if (!mem) return mem;
24+
tags[mem]++;
25+
orders[mem] = tagSequence++;
26+
return mem;
27+
}
28+
29+
static void tag_free(void* mem) {
30+
tags[mem]--;
31+
orders[mem] = tagSequence++;
32+
free(mem);
33+
}
34+
35+
BOOST_FIXTURE_TEST_SUITE(raii_event_tests, BasicTestingSetup)
36+
37+
BOOST_AUTO_TEST_CASE(raii_event_creation)
38+
{
39+
event_set_mem_functions(tag_malloc, realloc, tag_free);
40+
41+
void* base_ptr = NULL;
42+
{
43+
auto base = obtain_event_base();
44+
base_ptr = (void*)base.get();
45+
BOOST_CHECK(tags[base_ptr] == 1);
46+
}
47+
BOOST_CHECK(tags[base_ptr] == 0);
48+
49+
void* event_ptr = NULL;
50+
{
51+
auto base = obtain_event_base();
52+
auto event = obtain_event(base.get(), -1, 0, NULL, NULL);
53+
54+
base_ptr = (void*)base.get();
55+
event_ptr = (void*)event.get();
56+
57+
BOOST_CHECK(tags[base_ptr] == 1);
58+
BOOST_CHECK(tags[event_ptr] == 1);
59+
}
60+
BOOST_CHECK(tags[base_ptr] == 0);
61+
BOOST_CHECK(tags[event_ptr] == 0);
62+
63+
event_set_mem_functions(malloc, realloc, free);
64+
}
65+
66+
BOOST_AUTO_TEST_CASE(raii_event_order)
67+
{
68+
event_set_mem_functions(tag_malloc, realloc, tag_free);
69+
70+
void* base_ptr = NULL;
71+
void* event_ptr = NULL;
72+
{
73+
auto base = obtain_event_base();
74+
auto event = obtain_event(base.get(), -1, 0, NULL, NULL);
75+
76+
base_ptr = (void*)base.get();
77+
event_ptr = (void*)event.get();
78+
79+
// base should have allocated before event
80+
BOOST_CHECK(orders[base_ptr] < orders[event_ptr]);
81+
}
82+
// base should be freed after event
83+
BOOST_CHECK(orders[base_ptr] > orders[event_ptr]);
84+
85+
event_set_mem_functions(malloc, realloc, free);
86+
}
87+
88+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)