diff --git a/src/lib/fcitx-utils/eventdispatcher.h b/src/lib/fcitx-utils/eventdispatcher.h index d9f3b29c..6e4619b3 100644 --- a/src/lib/fcitx-utils/eventdispatcher.h +++ b/src/lib/fcitx-utils/eventdispatcher.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "fcitxutils_export.h" namespace fcitx { @@ -43,6 +44,7 @@ class FCITXUTILS_EXPORT EventDispatcher { * thread from event loop. */ void detach(); + /** * A thread-safe function to schedule a functor to be call from event loop. * @@ -54,6 +56,33 @@ class FCITXUTILS_EXPORT EventDispatcher { */ void schedule(std::function functor); + /** + * A helper function that allows to only invoke certain function if the + * reference is still valid. + * + * If context object is not valid when calling scheduleWithContext, it won't + * be scheduled at all. + * + * @param context the context object. + * @param functor function to be scheduled + * + * @since 5.1.8 + */ + template + void scheduleWithContext(TrackableObjectReference context, + std::function functor) { + if (!context.isValid()) { + return; + } + + schedule( + [context = std::move(context), functor = std::move(functor)]() { + if (context.isValid()) { + functor(); + } + }); + } + /** * Return the currently attached event loop * diff --git a/test/testeventdispatcher.cpp b/test/testeventdispatcher.cpp index b9a70251..36891b93 100644 --- a/test/testeventdispatcher.cpp +++ b/test/testeventdispatcher.cpp @@ -7,9 +7,11 @@ #include #include #include +#include #include "fcitx-utils/event.h" #include "fcitx-utils/eventdispatcher.h" #include "fcitx-utils/log.h" +#include "fcitx-utils/trackableobject.h" using namespace fcitx; @@ -38,6 +40,22 @@ void basicTest() { thread.join(); } +void testOrder() { + EventLoop loop; + EventDispatcher dispatcher; + dispatcher.attach(&loop); + std::vector value; + for (int i = 0; i < 100; i++) { + dispatcher.schedule([i, &value]() { value.push_back(i); }); + } + dispatcher.schedule([&loop]() { loop.exit(); }); + loop.exec(); + FCITX_ASSERT(value.size() == 100); + for (int i = 0; i < 100; i++) { + FCITX_ASSERT(i == value[i]) << i << " " << value[i]; + } +} + void recursiveSchedule() { EventDispatcher dispatcher; EventLoop loop; @@ -59,8 +77,35 @@ void recursiveSchedule() { FCITX_ASSERT(counter == 100); } +class TestObject : public TrackableObject {}; + +void withContext() { + EventDispatcher dispatcher; + EventLoop loop; + dispatcher.attach(&loop); + bool called = false; + bool invalidCalled = false; + TestObject validObject; + { + TestObject invalidObject; + dispatcher.scheduleWithContext(validObject.watch(), + [&called]() { called = true; }); + dispatcher.scheduleWithContext( + invalidObject.watch(), + [&invalidCalled]() { invalidCalled = true; }); + } + + dispatcher.schedule([&loop]() { loop.exit(); }); + loop.exec(); + + FCITX_ASSERT(called); + FCITX_ASSERT(!invalidCalled); +} + int main() { basicTest(); + testOrder(); recursiveSchedule(); + withContext(); return 0; }