diff --git a/media-proxy/include/mesh/concurrency.h b/media-proxy/include/mesh/concurrency.h index 78fba982c..7c3f910e7 100644 --- a/media-proxy/include/mesh/concurrency.h +++ b/media-proxy/include/mesh/concurrency.h @@ -50,11 +50,11 @@ class Context { Context(Context& parent); Context(Context& parent, std::chrono::milliseconds timeout_ms); - Context *parent; - thread::Channel *ch; + Context *parent = nullptr; + thread::Channel *ch = nullptr; std::future async_cb; std::chrono::milliseconds timeout_ms; - std::unique_ptr>> cb; + std::unique_ptr>> cb = nullptr; friend Context& Background(); friend class WithCancel; diff --git a/media-proxy/src/mesh/concurrency.cc b/media-proxy/src/mesh/concurrency.cc index ee87d1306..c9b84ccfa 100644 --- a/media-proxy/src/mesh/concurrency.cc +++ b/media-proxy/src/mesh/concurrency.cc @@ -12,9 +12,6 @@ namespace mesh { namespace context { Context::Context() : ss(std::stop_source()), - parent(nullptr), - cb(nullptr), - ch(new thread::Channel(1)), timeout_ms(std::chrono::milliseconds(0)) { } @@ -55,9 +52,11 @@ Context& Context::operator=(Context&& other) noexcept { ss = std::stop_source(); parent = other.parent; - cb = std::make_unique>>( - parent->ss.get_token(), [this] { cancel(); } - ); + if (parent) { + cb = std::make_unique>>( + parent->ss.get_token(), [this] { cancel(); } + ); + } timeout_ms = other.timeout_ms; @@ -71,6 +70,10 @@ Context& Context::operator=(Context&& other) noexcept { }); } + if (ch) { + delete ch; + } + ch = other.ch; other.ch = nullptr; } diff --git a/media-proxy/tests/mesh_context_tests.cc b/media-proxy/tests/mesh_context_tests.cc new file mode 100644 index 000000000..fe56f28d6 --- /dev/null +++ b/media-proxy/tests/mesh_context_tests.cc @@ -0,0 +1,51 @@ +#include +#include "mesh/concurrency.h" + +using namespace mesh; + +class test_class { + public: + context::Context _ctx; + + void init(context::Context &ctx) { _ctx = context::WithCancel(ctx); } +}; + +/** + * Test designed to detect memory leaks in the Context class. + * + * Run the test with Valgrind to detect memory leaks. + * valgrind --leak-check=full ./media_proxy_unit_tests --gtest_filter=*Context* + */ +TEST(MeshContext, constructor) { + auto ctx = context::WithCancel(context::Background()); + test_class t; + t.init(ctx); + { + auto ctx2 = context::WithCancel(ctx); + t.init(ctx2); + mesh::thread::Sleep(ctx2, std::chrono::milliseconds(10)); + ctx2.cancel(); + } + { + context::Context c1; + c1 = context::Context(); + } + { + context::Context c1 = context::WithTimeout(ctx, std::chrono::milliseconds(10)); + context::Context c2 = context::WithTimeout(c1, std::chrono::milliseconds(20)); + context::Context c3 = context::WithTimeout(c2, std::chrono::milliseconds(30)); + context::Context c4 = context::WithTimeout(c3, std::chrono::milliseconds(40)); + context::Context c5 = context::WithTimeout(c4, std::chrono::milliseconds(50)); + context::Context c6 = context::WithTimeout(c5, std::chrono::milliseconds(60)); + mesh::thread::Sleep(c6, std::chrono::milliseconds(10)); + } + { + context::Context c1 = context::WithTimeout(ctx, std::chrono::milliseconds(60)); + context::Context c2 = context::WithTimeout(c1, std::chrono::milliseconds(50)); + context::Context c3 = context::WithTimeout(c2, std::chrono::milliseconds(40)); + context::Context c4 = context::WithTimeout(c3, std::chrono::milliseconds(30)); + context::Context c5 = context::WithTimeout(c4, std::chrono::milliseconds(20)); + context::Context c6 = context::WithTimeout(c5, std::chrono::milliseconds(10)); + mesh::thread::Sleep(c6, std::chrono::milliseconds(10)); + } +}