Skip to content

Commit 85f8b13

Browse files
committed
Add thread about to stop signal
1 parent 5d0ac93 commit 85f8b13

File tree

6 files changed

+106
-4
lines changed

6 files changed

+106
-4
lines changed

include/scratchcpp/iengine.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ class LIBSCRATCHCPP_EXPORT IEngine
113113
/*! Emits when rendering should occur. */
114114
virtual sigslot::signal<> &aboutToRender() = 0;
115115

116+
/*! Emits when a script is about to stop. */
117+
virtual sigslot::signal<VirtualMachine *> &threadAboutToStop() = 0;
118+
116119
/*! Returns true if the project is currently running. */
117120
virtual bool isRunning() const = 0;
118121

src/engine/internal/engine.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ void Engine::clear()
6363
for (auto monitor : m_monitors)
6464
m_monitorRemoved(monitor.get(), monitor->impl->iface);
6565

66+
for (auto thread : m_threads) {
67+
if (!thread->atEnd())
68+
m_threadAboutToStop(thread.get());
69+
}
70+
6671
m_sections.clear();
6772
m_targets.clear();
6873
m_broadcasts.clear();
@@ -328,6 +333,11 @@ void Engine::stop()
328333
} else {
329334
// If there isn't any active thread, it means the project was stopped from the outside
330335
// In this case all threads should be removed and the project should be considered stopped
336+
for (auto thread : m_threads) {
337+
if (!thread->atEnd())
338+
m_threadAboutToStop(thread.get());
339+
}
340+
331341
m_threads.clear();
332342
m_running = false;
333343
}
@@ -511,6 +521,11 @@ sigslot::signal<> &Engine::aboutToRender()
511521
return m_aboutToRedraw;
512522
}
513523

524+
sigslot::signal<VirtualMachine *> &Engine::threadAboutToStop()
525+
{
526+
return m_threadAboutToStop;
527+
}
528+
514529
std::vector<std::shared_ptr<VirtualMachine>> Engine::stepThreads()
515530
{
516531
// https://github.com/scratchfoundation/scratch-vm/blob/develop/src/engine/sequencer.js#L70-L173
@@ -548,8 +563,14 @@ std::vector<std::shared_ptr<VirtualMachine>> Engine::stepThreads()
548563
}
549564

550565
// Remove threads in m_threadsToStop
551-
for (auto thread : m_threadsToStop)
566+
for (auto thread : m_threadsToStop) {
567+
if (!thread->atEnd())
568+
m_threadAboutToStop(thread.get());
569+
552570
m_threads.erase(std::remove(m_threads.begin(), m_threads.end(), thread), m_threads.end());
571+
}
572+
573+
m_threadsToStop.clear();
553574

554575
// Remove inactive threads (and add them to doneThreads)
555576
m_threads.erase(
@@ -1658,6 +1679,10 @@ void Engine::stopThread(VirtualMachine *thread)
16581679
{
16591680
// https://github.com/scratchfoundation/scratch-vm/blob/f1aa92fad79af17d9dd1c41eeeadca099339a9f1/src/engine/runtime.js#L1667-L1672
16601681
assert(thread);
1682+
1683+
if (!thread->atEnd())
1684+
m_threadAboutToStop(thread);
1685+
16611686
thread->kill();
16621687
}
16631688

@@ -1668,6 +1693,9 @@ std::shared_ptr<VirtualMachine> Engine::restartThread(std::shared_ptr<VirtualMac
16681693
auto it = std::find(m_threads.begin(), m_threads.end(), thread);
16691694

16701695
if (it != m_threads.end()) {
1696+
if (!thread->atEnd())
1697+
m_threadAboutToStop(thread.get());
1698+
16711699
auto i = it - m_threads.begin();
16721700
m_threads[i] = newThread;
16731701
return newThread;

src/engine/internal/engine.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class Engine : public IEngine
5050
void stopEventLoop() override;
5151

5252
sigslot::signal<> &aboutToRender() override;
53+
sigslot::signal<VirtualMachine *> &threadAboutToStop() override;
5354

5455
bool isRunning() const override;
5556

@@ -266,6 +267,7 @@ class Engine : public IEngine
266267
bool m_running = false;
267268
bool m_redrawRequested = false;
268269
sigslot::signal<> m_aboutToRedraw;
270+
sigslot::signal<VirtualMachine *> m_threadAboutToStop;
269271
bool m_stopEventLoop = false;
270272
std::mutex m_stopEventLoopMutex;
271273

test/3_threads.sb3

908 Bytes
Binary file not shown.

test/engine/engine_test.cpp

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ class RedrawMock
4747
MOCK_METHOD(void, redraw, ());
4848
};
4949

50+
class ThreadAboutToStopMock
51+
{
52+
public:
53+
MOCK_METHOD(void, threadRemoved, (VirtualMachine *));
54+
};
55+
5056
class AddRemoveMonitorMock
5157
{
5258
public:
@@ -109,6 +115,44 @@ TEST(EngineTest, Clear)
109115
ASSERT_TRUE(engine.monitors().empty());
110116
}
111117

118+
TEST(EngineTest, ClearThreadAboutToStopSignal)
119+
{
120+
Project p("3_threads.sb3");
121+
ASSERT_TRUE(p.load());
122+
auto engine = p.engine();
123+
124+
engine->start();
125+
engine->step();
126+
127+
ThreadAboutToStopMock threadRemovedMock;
128+
EXPECT_CALL(threadRemovedMock, threadRemoved(_)).Times(3).WillRepeatedly(WithArgs<0>(Invoke([](VirtualMachine *vm) {
129+
ASSERT_TRUE(vm);
130+
ASSERT_FALSE(vm->atEnd());
131+
})));
132+
133+
engine->threadAboutToStop().connect(&ThreadAboutToStopMock::threadRemoved, &threadRemovedMock);
134+
engine->clear();
135+
}
136+
137+
TEST(EngineTest, StopThreadAboutToStopSignal)
138+
{
139+
Project p("3_threads.sb3");
140+
ASSERT_TRUE(p.load());
141+
auto engine = p.engine();
142+
143+
engine->start();
144+
engine->step();
145+
146+
ThreadAboutToStopMock threadRemovedMock;
147+
EXPECT_CALL(threadRemovedMock, threadRemoved(_)).Times(3).WillRepeatedly(WithArgs<0>(Invoke([](VirtualMachine *vm) {
148+
ASSERT_TRUE(vm);
149+
ASSERT_FALSE(vm->atEnd());
150+
})));
151+
152+
engine->threadAboutToStop().connect(&ThreadAboutToStopMock::threadRemoved, &threadRemovedMock);
153+
engine->stop();
154+
}
155+
112156
TEST(EngineTest, CompileAndExecuteMonitors)
113157
{
114158
Engine engine;
@@ -1840,10 +1884,18 @@ TEST(EngineTest, BroadcastsProject)
18401884
{
18411885
Project p("broadcasts.sb3");
18421886
ASSERT_TRUE(p.load());
1843-
p.run();
18441887

18451888
auto engine = p.engine();
1889+
1890+
ThreadAboutToStopMock threadRemovedMock;
1891+
EXPECT_CALL(threadRemovedMock, threadRemoved(_)).Times(21).WillRepeatedly(WithArgs<0>(Invoke([](VirtualMachine *vm) {
1892+
ASSERT_TRUE(vm);
1893+
ASSERT_FALSE(vm->atEnd());
1894+
})));
1895+
1896+
engine->threadAboutToStop().connect(&ThreadAboutToStopMock::threadRemoved, &threadRemovedMock);
18461897
engine->setFps(1000);
1898+
p.run();
18471899

18481900
Stage *stage = engine->stage();
18491901
ASSERT_TRUE(stage);
@@ -1880,10 +1932,18 @@ TEST(EngineTest, StopAllBypass)
18801932
{
18811933
Project p("stop_all_bypass.sb3");
18821934
ASSERT_TRUE(p.load());
1883-
p.run();
18841935

18851936
auto engine = p.engine();
18861937

1938+
ThreadAboutToStopMock threadRemovedMock;
1939+
EXPECT_CALL(threadRemovedMock, threadRemoved(_)).Times(2).WillRepeatedly(WithArgs<0>(Invoke([](VirtualMachine *vm) {
1940+
ASSERT_TRUE(vm);
1941+
ASSERT_FALSE(vm->atEnd());
1942+
})));
1943+
1944+
engine->threadAboutToStop().connect(&ThreadAboutToStopMock::threadRemoved, &threadRemovedMock);
1945+
p.run();
1946+
18871947
Stage *stage = engine->stage();
18881948
ASSERT_TRUE(stage);
18891949

@@ -1900,10 +1960,18 @@ TEST(EngineTest, StopOtherScriptsInSprite)
19001960
{
19011961
Project p("stop_other_scripts_in_sprite.sb3");
19021962
ASSERT_TRUE(p.load());
1903-
p.run();
19041963

19051964
auto engine = p.engine();
19061965

1966+
ThreadAboutToStopMock threadRemovedMock;
1967+
EXPECT_CALL(threadRemovedMock, threadRemoved(_)).Times(4).WillRepeatedly(WithArgs<0>(Invoke([](VirtualMachine *vm) {
1968+
ASSERT_TRUE(vm);
1969+
ASSERT_FALSE(vm->atEnd());
1970+
})));
1971+
1972+
engine->threadAboutToStop().connect(&ThreadAboutToStopMock::threadRemoved, &threadRemovedMock);
1973+
p.run();
1974+
19071975
Stage *stage = engine->stage();
19081976
ASSERT_TRUE(stage);
19091977

test/mocks/enginemock.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class EngineMock : public IEngine
3333
MOCK_METHOD(void, stopEventLoop, (), (override));
3434

3535
MOCK_METHOD(sigslot::signal<> &, aboutToRender, (), (override));
36+
MOCK_METHOD(sigslot::signal<VirtualMachine *> &, threadAboutToStop, (), (override));
3637

3738
MOCK_METHOD(bool, isRunning, (), (const, override));
3839

0 commit comments

Comments
 (0)