Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions Libraries/LibCore/EventLoop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ EventLoop::EventLoop()

EventLoop::~EventLoop()
{
if (m_weak)
m_weak->revoke();
if (!event_loop_stack().is_empty() && &event_loop_stack().last() == this) {
event_loop_stack().take_last();
}
Expand All @@ -61,6 +63,14 @@ EventLoop& EventLoop::current()
return event_loop_stack().last();
}

NonnullRefPtr<EventLoopWeak> EventLoop::current_weak()
{
auto& event_loop = current();
if (!event_loop.m_weak)
event_loop.m_weak = NonnullRefPtr(NonnullRefPtr<EventLoopWeak>::Adopt, *new (nothrow) EventLoopWeak(event_loop));
return *event_loop.m_weak;
}

void EventLoop::quit(int code)
{
ThreadEventQueue::current().cancel_all_pending_jobs();
Expand Down Expand Up @@ -152,4 +162,54 @@ void deferred_invoke(Function<void()> invokee)
EventLoop::current().deferred_invoke(move(invokee));
}

EventLoopWeak::EventLoopWeak(EventLoop& event_loop)
: m_event_loop(&event_loop)
{
}

void EventLoopWeak::revoke()
{
m_lock.lock_write();
m_event_loop = nullptr;
m_lock.unlock();
}

EventLoopLockedReference EventLoopWeak::take()
{
return EventLoopLockedReference(*this);
}

EventLoopLockedReference::EventLoopLockedReference(EventLoopWeak& event_loop_weak)
{
event_loop_weak.m_lock.lock_read();
m_event_loop_weak = &event_loop_weak;
}

EventLoopLockedReference::~EventLoopLockedReference()
{
m_event_loop_weak->m_lock.unlock();
}

bool EventLoopLockedReference::is_alive() const
{
return m_event_loop_weak->m_event_loop != nullptr;
}

EventLoopLockedReference::operator bool() const
{
return is_alive();
}

EventLoop* EventLoopLockedReference::operator*() const
{
VERIFY(is_alive());
return m_event_loop_weak->m_event_loop;
}

EventLoop* EventLoopLockedReference::operator->() const
{
VERIFY(is_alive());
return m_event_loop_weak->m_event_loop;
}

}
40 changes: 40 additions & 0 deletions Libraries/LibCore/EventLoop.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#pragma once

#include <AK/AtomicRefCounted.h>
#include <AK/Forward.h>
#include <AK/Function.h>
#include <AK/Noncopyable.h>
Expand All @@ -16,11 +17,13 @@
#include <AK/Time.h>
#include <LibCore/Event.h>
#include <LibCore/Forward.h>
#include <LibThreading/RWLock.h>

namespace Core {

class EventLoopImplementation;
class ThreadEventQueue;
class EventLoopWeak;

// The event loop enables asynchronous (not parallel or multi-threaded) computing by efficiently handling events from various sources.
// Event loops are most important for GUI programs, where the various GUI updates and action callbacks run on the EventLoop,
Expand Down Expand Up @@ -89,13 +92,50 @@ class EventLoop {

static bool is_running();
static EventLoop& current();
static NonnullRefPtr<EventLoopWeak> current_weak();

EventLoopImplementation& impl() { return *m_impl; }

private:
NonnullOwnPtr<EventLoopImplementation> m_impl;
RefPtr<EventLoopWeak> m_weak;
} SWIFT_UNSAFE_REFERENCE;

class EventLoopLockedReference;

class EventLoopWeak : public AtomicRefCounted<EventLoopWeak> {
public:
EventLoopLockedReference take();

private:
friend class EventLoop;
friend class EventLoopLockedReference;

EventLoopWeak(EventLoop&);

void revoke();

EventLoop* m_event_loop;
Threading::RWLock m_lock;
};

class EventLoopLockedReference {
public:
~EventLoopLockedReference();

bool is_alive() const;
operator bool() const;
EventLoop* operator*() const;
EventLoop* operator->() const;

private:
friend class EventLoopWeak;

EventLoopLockedReference(EventLoopWeak&);

EventLoopWeak* m_event_loop_weak;
};

void deferred_invoke(ESCAPING Function<void()>);

}
1 change: 1 addition & 0 deletions Libraries/LibCore/Forward.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class DeferredInvocationContext;
class ElapsedTimer;
class Event;
class EventLoop;
class EventLoopWeak;
class EventReceiver;
class File;
class LocalServer;
Expand Down
12 changes: 7 additions & 5 deletions Libraries/LibThreading/BackgroundAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,23 +76,26 @@ class BackgroundAction final
if (on_error.has_value())
m_on_error = on_error.release_value();

enqueue_work([self = NonnullRefPtr(*this), promise = move(promise), origin_event_loop = &Core::EventLoop::current()]() mutable {
enqueue_work([self = NonnullRefPtr(*this), promise = move(promise), origin_event_loop = Core::EventLoop::current_weak()]() mutable {
auto result = self->m_action(*self);

// The event loop cancels the promise when it exits.
self->m_canceled |= promise->is_rejected();

// All of our work was successful and we weren't cancelled; resolve the event loop's promise.
auto strong_event_loop = origin_event_loop->take();
if (!strong_event_loop.is_alive())
return;

if (!self->m_canceled && !result.is_error()) {
self->m_result = result.release_value();

// If there is no completion callback, we don't rely on the user keeping around the event loop.
if (self->m_on_complete) {
origin_event_loop->deferred_invoke([self, promise = move(promise)] {
strong_event_loop->deferred_invoke([self, promise = move(promise)] {
// Our promise's resolution function will never error.
(void)promise->resolve(*self);
});
origin_event_loop->wake();
}
} else {
// We were either unsuccessful or cancelled (in which case there is no error).
Expand All @@ -103,10 +106,9 @@ class BackgroundAction final
promise->reject(Error::from_errno(ECANCELED));

if (!self->m_canceled && self->m_on_error) {
origin_event_loop->deferred_invoke([self, error = move(error)]() mutable {
strong_event_loop->deferred_invoke([self, error = move(error)]() mutable {
self->m_on_error(move(error));
});
origin_event_loop->wake();
} else if (self->m_on_error) {
self->m_on_error(move(error));
}
Expand Down
Loading