Skip to content

Commit b6d1e96

Browse files
committed
Fix LLVM coroutine memory leaks
1 parent 575bb3d commit b6d1e96

File tree

5 files changed

+66
-9
lines changed

5 files changed

+66
-9
lines changed

src/engine/internal/llvm/llvmexecutablecode.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,23 @@ bool LLVMExecutableCode::runPredicate(ExecutionContext *context)
8686
void LLVMExecutableCode::kill(ExecutionContext *context)
8787
{
8888
LLVMExecutionContext *ctx = getContext(context);
89-
ctx->setCoroutineHandle(nullptr);
89+
// NOTE: Do not destroy coroutine, it will be destroyed later
90+
// (really bad things happen when a coroutine destroys itself...)
91+
// The code is finished and the coroutine won't be used again until reset() is called
9092
ctx->setFinished(true);
9193
ctx->setPromise(nullptr);
9294
}
9395

9496
void LLVMExecutableCode::reset(ExecutionContext *context)
9597
{
9698
LLVMExecutionContext *ctx = getContext(context);
97-
ctx->setCoroutineHandle(nullptr);
99+
100+
if (ctx->coroutineHandle()) {
101+
// TODO: Can a script reset itself? (really bad things happen when a coroutine destroys itself...)
102+
m_ctx->destroyCoroutine(ctx->coroutineHandle());
103+
ctx->setCoroutineHandle(nullptr);
104+
}
105+
98106
ctx->setFinished(false);
99107
ctx->setPromise(nullptr);
100108
}
@@ -124,7 +132,7 @@ std::shared_ptr<ExecutionContext> LLVMExecutableCode::createExecutionContext(Thr
124132
}
125133

126134
m_resumeFunction = m_ctx->lookupFunction<ResumeFunctionType>(m_resumeFunctionName);
127-
return std::make_shared<LLVMExecutionContext>(thread);
135+
return std::make_shared<LLVMExecutionContext>(m_ctx, thread);
128136
}
129137

130138
LLVMExecutionContext *LLVMExecutableCode::getContext(ExecutionContext *context)

src/engine/internal/llvm/llvmexecutioncontext.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
11
// SPDX-License-Identifier: Apache-2.0
22

33
#include "llvmexecutioncontext.h"
4+
#include "llvmcompilercontext.h"
45

56
using namespace libscratchcpp;
67

7-
LLVMExecutionContext::LLVMExecutionContext(Thread *thread) :
8-
ExecutionContext(thread)
8+
LLVMExecutionContext::LLVMExecutionContext(LLVMCompilerContext *compilerCtx, Thread *thread) :
9+
ExecutionContext(thread),
10+
m_compilerCtx(compilerCtx)
911
{
1012
}
1113

14+
LLVMExecutionContext::~LLVMExecutionContext()
15+
{
16+
if (m_coroutineHandle) {
17+
if (m_compilerCtx) {
18+
m_compilerCtx->destroyCoroutine(m_coroutineHandle);
19+
m_coroutineHandle = nullptr;
20+
} else
21+
assert(false);
22+
}
23+
}
24+
1225
void *LLVMExecutionContext::coroutineHandle() const
1326
{
1427
return m_coroutineHandle;

src/engine/internal/llvm/llvmexecutioncontext.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
namespace libscratchcpp
88
{
99

10+
class LLVMCompilerContext;
11+
1012
class LLVMExecutionContext : public ExecutionContext
1113
{
1214
public:
13-
LLVMExecutionContext(Thread *thread);
15+
LLVMExecutionContext(LLVMCompilerContext *compilerCtx, Thread *thread);
16+
~LLVMExecutionContext();
1417

1518
void *coroutineHandle() const;
1619
void setCoroutineHandle(void *newCoroutineHandle);
@@ -19,6 +22,7 @@ class LLVMExecutionContext : public ExecutionContext
1922
void setFinished(bool newFinished);
2023

2124
private:
25+
LLVMCompilerContext *m_compilerCtx = nullptr;
2226
void *m_coroutineHandle = nullptr;
2327
bool m_finished = false;
2428
};

test/llvm/llvmcodebuilder_test.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,9 @@ class LLVMCodeBuilderTest : public testing::Test
7070
void createBuilder(Target *target, BlockPrototype *procedurePrototype, Compiler::CodeType codeType = Compiler::CodeType::Script)
7171
{
7272
if (m_contexts.find(target) == m_contexts.cend() || !target)
73-
m_contexts[target] = std::make_unique<LLVMCompilerContext>(&m_engine, target);
73+
m_contexts[target] = std::make_shared<LLVMCompilerContext>(&m_engine, target);
7474

75+
m_contextList.push_back(m_contexts[target]);
7576
m_builder = std::make_unique<LLVMCodeBuilder>(m_contexts[target].get(), procedurePrototype, codeType);
7677
}
7778

@@ -433,7 +434,8 @@ class LLVMCodeBuilderTest : public testing::Test
433434
ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes << v.toString() << quotes;
434435
};
435436

436-
std::unordered_map<Target *, std::unique_ptr<LLVMCompilerContext>> m_contexts;
437+
std::unordered_map<Target *, std::shared_ptr<LLVMCompilerContext>> m_contexts;
438+
std::vector<std::shared_ptr<LLVMCompilerContext>> m_contextList;
437439
std::unique_ptr<LLVMCodeBuilder> m_builder;
438440
std::shared_ptr<BlockPrototype> m_procedurePrototype;
439441
EngineMock m_engine;
@@ -2854,6 +2856,34 @@ TEST_F(LLVMCodeBuilderTest, Yield)
28542856
ASSERT_EQ(testing::internal::GetCapturedStdout(), expected2);
28552857
ASSERT_TRUE(code->isFinished(ctx.get()));
28562858

2859+
// Terminate unfinished coroutine
2860+
EXPECT_CALL(m_target, isStage()).Times(3);
2861+
testing::internal::CaptureStdout();
2862+
code->reset(ctx.get());
2863+
code->run(ctx.get());
2864+
ASSERT_EQ(testing::internal::GetCapturedStdout(), expected1);
2865+
ASSERT_FALSE(code->isFinished(ctx.get()));
2866+
2867+
code->kill(ctx.get());
2868+
ASSERT_TRUE(code->isFinished(ctx.get()));
2869+
2870+
// Reset unfinished coroutine
2871+
EXPECT_CALL(m_target, isStage()).Times(3);
2872+
testing::internal::CaptureStdout();
2873+
code->reset(ctx.get());
2874+
code->run(ctx.get());
2875+
ASSERT_EQ(testing::internal::GetCapturedStdout(), expected1);
2876+
ASSERT_FALSE(code->isFinished(ctx.get()));
2877+
2878+
code->reset(ctx.get());
2879+
2880+
EXPECT_CALL(m_target, isStage()).Times(3);
2881+
testing::internal::CaptureStdout();
2882+
code->reset(ctx.get());
2883+
code->run(ctx.get());
2884+
ASSERT_EQ(testing::internal::GetCapturedStdout(), expected1);
2885+
ASSERT_FALSE(code->isFinished(ctx.get())); // leave unfinished coroutine
2886+
28572887
// With warp
28582888
createBuilder(true);
28592889
build();
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
#include <scratchcpp/thread.h>
22
#include <engine/internal/llvm/llvmexecutioncontext.h>
3+
#include <engine/internal/llvm/llvmcompilercontext.h>
34
#include <gtest/gtest.h>
45

56
using namespace libscratchcpp;
67

78
TEST(LLVMExecutionContextTest, Constructor)
89
{
910
Thread thread(nullptr, nullptr, nullptr);
10-
LLVMExecutionContext ctx(&thread);
11+
LLVMCompilerContext compilerCtx(nullptr, nullptr);
12+
LLVMExecutionContext ctx(&compilerCtx, &thread);
1113
ASSERT_EQ(ctx.thread(), &thread);
1214
}

0 commit comments

Comments
 (0)