Skip to content

Commit

Permalink
src: add support for weak_ptr
Browse files Browse the repository at this point in the history
Allow a weak_ptr to be passed to each callback along with a normal
pointer. This will make it easier to not accidentally hold on to
resources when juggling multiple threads.

Many tests were duplicated into test/*-wp.cc just to make sure the
weak_ptr API has test coverage.

PR-URL: #31
Co-authored-by: Santiago Gimeno <[email protected]>
  • Loading branch information
trevnorris and santigimeno committed Feb 28, 2024
1 parent 8d56c39 commit d4f4c64
Show file tree
Hide file tree
Showing 15 changed files with 4,650 additions and 3 deletions.
561 changes: 561 additions & 0 deletions include/nsuv-inl.h

Large diffs are not rendered by default.

143 changes: 140 additions & 3 deletions include/nsuv.h

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions test/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ constexpr char kTestPipename3[] = "/tmp/uv-test-sock3";

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))

// Because nsuv has a void* overload C++ doesn't know how to properly cast from
// a std::shared_ptr to std::weak_ptr implicitly. Create a macro so we can do it
// inline.
// NOLINTNEXTLINE
#define TO_WEAK(a) (std::weak_ptr<typename std::remove_pointer<decltype((a).get())>::type>(a))

// TODO(trevnorris): This is temporary while libuv tests are being ported. A
// more permanent solution should be made.
#define container_of(ptr, type, member) \
Expand Down
102 changes: 102 additions & 0 deletions test/test-addrinfo-wp.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include "../include/nsuv-inl.h"
#include "./catch.hpp"
#include "./helpers.h"

using nsuv::ns_addrinfo;

static const char* invalid_name = "xyzzy.xyzzy.xyzzy.";
static const char* valid_name = "localhost";

static std::string* my_data_ptr = nullptr;

static void gettaddrinfo_failure_cb(ns_addrinfo* info,
int status,
std::weak_ptr<size_t> d) {
auto sp = d.lock();
ASSERT(sp);
ASSERT_EQ(42, *sp);
ASSERT_GT(0, status);
ASSERT_NULL(info->info());
}

TEST_CASE("invalid_get_async_wp", "[addrinfo]") {
std::shared_ptr<size_t> sp = std::make_shared<size_t>(42);
ns_addrinfo info;

ASSERT_EQ(0, info.get(uv_default_loop(),
gettaddrinfo_failure_cb,
invalid_name,
nullptr,
nullptr,
TO_WEAK(sp)));

ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT));

make_valgrind_happy();
}

static void gettaddrinfo_success_cb(ns_addrinfo* info,
int status,
std::weak_ptr<size_t> d) {
auto sp = d.lock();
ASSERT(sp);
ASSERT_EQ(42, *sp);
ASSERT_EQ(0, status);
ASSERT(info->info());
}

TEST_CASE("valid_get_async_wp", "[addrinfo]") {
std::shared_ptr<size_t> sp = std::make_shared<size_t>(42);
ns_addrinfo info;

ASSERT_EQ(0, info.get(uv_default_loop(),
gettaddrinfo_success_cb,
valid_name,
nullptr,
nullptr,
TO_WEAK(sp)));

ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT));

make_valgrind_happy();
}

static void gettaddrinfo_void_data_cb(ns_addrinfo* info,
int status,
std::weak_ptr<std::string> d) {
auto data = d.lock();
ASSERT_EQ(0, status);
ASSERT(info->info());
ASSERT(data);
ASSERT_EQ(data.get(), my_data_ptr);
}

TEST_CASE("valid_get_async_void_data_wp", "[addrinfo]") {
auto my_data = std::make_shared<std::string>("my_data");
std::weak_ptr<std::string> wp = my_data;
ns_addrinfo info;
struct addrinfo hints;

my_data_ptr = my_data.get();

memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
hints.ai_protocol = 0; /* Any protocol */
hints.ai_canonname = nullptr;
hints.ai_addr = nullptr;
hints.ai_next = nullptr;

ASSERT_EQ(0, info.get(uv_default_loop(),
gettaddrinfo_void_data_cb,
valid_name,
nullptr,
&hints,
wp));

ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT));

my_data_ptr = nullptr;
make_valgrind_happy();
}
93 changes: 93 additions & 0 deletions test/test-async-wp.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "../include/nsuv-inl.h"
#include "./catch.hpp"
#include "./helpers.h"

#include <atomic>

using nsuv::ns_thread;
using nsuv::ns_async;
using nsuv::ns_mutex;
using nsuv::ns_prepare;

struct resources {
ns_thread* thread;
ns_async* async;
ns_mutex* mutex;
ns_prepare* prepare;
};

static std::atomic<int> async_cb_called;
static int prepare_cb_called;
static int close_cb_called;


static void thread_cb(ns_thread*, std::weak_ptr<resources> wp) {
auto res = wp.lock();
ASSERT(res);
for (;;) {
res->mutex->lock();
res->mutex->unlock();
if (async_cb_called == 3) {
break;
}
CHECK(0 == res->async->send());
uv_sleep(0);
}
}


template <class H_T>
static void close_cb(H_T* handle) {
CHECK(handle != nullptr);
close_cb_called++;
}


static void async_cb(ns_async* handle, std::weak_ptr<resources> wp) {
auto res = wp.lock();
ASSERT(res);
CHECK(handle == res->async);
res->mutex->lock();
res->mutex->unlock();
if (++async_cb_called == 3) {
res->async->close(close_cb);
res->prepare->close(close_cb);
}
}


static void prepare_cb(ns_prepare* handle, std::weak_ptr<resources> wp) {
auto res = wp.lock();
ASSERT(res);
if (prepare_cb_called++)
return;
CHECK(handle == res->prepare);
CHECK(0 == res->thread->create(thread_cb, wp));
res->mutex->unlock();
}


TEST_CASE("async_operations_wp", "[async]") {
ns_thread thread;
ns_async async;
ns_mutex mutex;
ns_prepare prepare;
std::shared_ptr<resources> sp(
new resources{ &thread, &async, &mutex, &prepare });
std::weak_ptr<resources> res = sp;

ASSERT_EQ(0, prepare.init(uv_default_loop()));
ASSERT_EQ(0, prepare.start(prepare_cb, res));
ASSERT_EQ(0, async.init(uv_default_loop(), async_cb, res));
ASSERT_EQ(0, mutex.init());

mutex.lock();

ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT));
ASSERT_LT(0, prepare_cb_called);
ASSERT_EQ(3, async_cb_called);
ASSERT_EQ(2, close_cb_called);
ASSERT_EQ(0, thread.join());

make_valgrind_happy();
}
Loading

0 comments on commit d4f4c64

Please sign in to comment.