diff --git a/src/libexpr/tests/coro-gc.cc b/src/libexpr/tests/coro-gc.cc new file mode 100644 index 00000000000..6023b9b5e55 --- /dev/null +++ b/src/libexpr/tests/coro-gc.cc @@ -0,0 +1,99 @@ +#include +#if HAVE_BOEHMGC +#include +#endif + +#include "eval.hh" +#include "serialise.hh" + + +#define guard_gc(x) GC_register_finalizer((void*)x, finalizer, x##_collected, nullptr, nullptr) + + +namespace nix { +#if HAVE_BOEHMGC + static bool* uncollectable_bool() { + bool* res = (bool*)GC_MALLOC_UNCOLLECTABLE(1); + *res = false; + return res; + } + + static void finalizer(void *obj, void *data) { + //printf("finalizer: obj %p data %p\n", obj, data); + *((bool*)data) = true; + } + + // Generate 2 objects, discard one, run gc, + // see if one got collected and the other didn't + static void testFinalizerCalls() { + bool* do_collect_collected = uncollectable_bool(); + bool* dont_collect_collected = uncollectable_bool(); + { + volatile void* do_collect = GC_MALLOC_ATOMIC(128); + guard_gc(do_collect); + } + volatile void* dont_collect = GC_MALLOC_ATOMIC(128); + guard_gc(dont_collect); + GC_gcollect(); + GC_invoke_finalizers(); + + ASSERT_TRUE(*do_collect_collected); + ASSERT_FALSE(*dont_collect_collected); + ASSERT_NE(nullptr, dont_collect); + } + + // This test tests that boehm handles coroutine stacks correctly + TEST(CoroGC, CoroutineStackNotGCd) { + initGC(); + testFinalizerCalls(); + + bool* dont_collect_collected = uncollectable_bool(); + bool* do_collect_collected = uncollectable_bool(); + + volatile void* dont_collect = GC_MALLOC_ATOMIC(128); + guard_gc(dont_collect); + { + volatile void* do_collect = GC_MALLOC_ATOMIC(128); + guard_gc(do_collect); + } + + auto source = sinkToSource([&](Sink& sink) { + testFinalizerCalls(); + + bool* dont_collect_inner_collected = uncollectable_bool(); + bool* do_collect_inner_collected = uncollectable_bool(); + + volatile void* dont_collect_inner = GC_MALLOC_ATOMIC(128); + guard_gc(dont_collect_inner); + { + volatile void* do_collect_inner = GC_MALLOC_ATOMIC(128); + guard_gc(do_collect_inner); + } + // pass control to main + writeString("foo", sink); + + ASSERT_TRUE(*do_collect_inner_collected); + ASSERT_FALSE(*dont_collect_inner_collected); + ASSERT_NE(nullptr, dont_collect_inner); + + // pass control to main + writeString("bar", sink); + }); + + // pass control to coroutine + std::string foo = readString(*source); + ASSERT_EQ(foo, "foo"); + + GC_gcollect(); + GC_invoke_finalizers(); + + // pass control to coroutine + std::string bar = readString(*source); + ASSERT_EQ(bar, "bar"); + + ASSERT_FALSE(*dont_collect_collected); + ASSERT_TRUE(*do_collect_collected); + ASSERT_NE(nullptr, dont_collect); + } +#endif +} diff --git a/src/libexpr/tests/local.mk b/src/libexpr/tests/local.mk index 3e5504f716a..c36d21bfeae 100644 --- a/src/libexpr/tests/local.mk +++ b/src/libexpr/tests/local.mk @@ -16,4 +16,4 @@ libexpr-tests_CXXFLAGS += -I src/libexpr -I src/libutil -I src/libstore -I src/l libexpr-tests_LIBS = libstore-tests libutils-tests libexpr libutil libstore libfetchers -libexpr-tests_LDFLAGS := $(GTEST_LIBS) -lgmock +libexpr-tests_LDFLAGS := $(GTEST_LIBS) -lgmock -lboost_context