mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-25 14:05:15 +00:00
LibThread: Introduce a new threading library
This library is meant to provide C++-style wrappers over lower level APIs such as syscalls and pthread_* functions, as well as utilities for easily running pieces of logic on different threads.
This commit is contained in:
parent
d5f3487203
commit
e1a6f8a27d
Notes:
sideshowbarker
2024-07-19 12:31:27 +09:00
Author: https://github.com/bugaevc Commit: https://github.com/SerenityOS/serenity/commit/e1a6f8a27d7 Pull-request: https://github.com/SerenityOS/serenity/pull/486 Reviewed-by: https://github.com/awesomekling
9 changed files with 211 additions and 3 deletions
|
@ -3,7 +3,7 @@ DEFINES += -DUSERLAND
|
||||||
all: $(APP)
|
all: $(APP)
|
||||||
|
|
||||||
$(APP): $(OBJS)
|
$(APP): $(OBJS)
|
||||||
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lgui -ldraw -laudio -lipc -lvt -lpcidb -lcore -lc
|
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lgui -ldraw -laudio -lipc -lthread -lvt -lpcidb -lcore -lc
|
||||||
|
|
||||||
.cpp.o:
|
.cpp.o:
|
||||||
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
|
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
|
||||||
|
|
|
@ -19,10 +19,11 @@ build_targets=""
|
||||||
build_targets="$build_targets ../DevTools/IPCCompiler"
|
build_targets="$build_targets ../DevTools/IPCCompiler"
|
||||||
build_targets="$build_targets ../DevTools/FormCompiler"
|
build_targets="$build_targets ../DevTools/FormCompiler"
|
||||||
|
|
||||||
# Build LibC, LibCore and LibIPC before IPC servers, since they depend on it.
|
# Build LibC, LibCore, LibIPC and LibThread before IPC servers, since they depend on them.
|
||||||
build_targets="$build_targets ../Libraries/LibC"
|
build_targets="$build_targets ../Libraries/LibC"
|
||||||
build_targets="$build_targets ../Libraries/LibCore"
|
build_targets="$build_targets ../Libraries/LibCore"
|
||||||
build_targets="$build_targets ../Libraries/LibIPC"
|
build_targets="$build_targets ../Libraries/LibIPC"
|
||||||
|
build_targets="$build_targets ../Libraries/LibThread"
|
||||||
|
|
||||||
# Build IPC servers before their client code to ensure the IPC definitions are available.
|
# Build IPC servers before their client code to ensure the IPC definitions are available.
|
||||||
build_targets="$build_targets ../Servers/AudioServer"
|
build_targets="$build_targets ../Servers/AudioServer"
|
||||||
|
|
48
Libraries/LibThread/BackgroundAction.cpp
Normal file
48
Libraries/LibThread/BackgroundAction.cpp
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#include <LibThread/BackgroundAction.h>
|
||||||
|
#include <LibThread/Thread.h>
|
||||||
|
#include <LibCore/CLock.h>
|
||||||
|
#include <AK/Queue.h>
|
||||||
|
|
||||||
|
static CLockable<Queue<Function<void()>>>* s_all_actions;
|
||||||
|
static LibThread::Thread* s_background_thread;
|
||||||
|
|
||||||
|
static int background_thread_func()
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
Function<void()> work_item;
|
||||||
|
{
|
||||||
|
LOCKER(s_all_actions->lock());
|
||||||
|
|
||||||
|
if (!s_all_actions->resource().is_empty())
|
||||||
|
work_item = s_all_actions->resource().dequeue();
|
||||||
|
}
|
||||||
|
if (work_item)
|
||||||
|
work_item();
|
||||||
|
else
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init()
|
||||||
|
{
|
||||||
|
s_all_actions = new CLockable<Queue<Function<void()>>>();
|
||||||
|
s_background_thread = new LibThread::Thread(background_thread_func);
|
||||||
|
s_background_thread->set_name("Background thread");
|
||||||
|
s_background_thread->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
CLockable<Queue<Function<void()>>>& LibThread::BackgroundActionBase::all_actions()
|
||||||
|
{
|
||||||
|
if (s_all_actions == nullptr)
|
||||||
|
init();
|
||||||
|
return *s_all_actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
LibThread::Thread& LibThread::BackgroundActionBase::background_thread()
|
||||||
|
{
|
||||||
|
if (s_background_thread == nullptr)
|
||||||
|
init();
|
||||||
|
return *s_background_thread;
|
||||||
|
}
|
75
Libraries/LibThread/BackgroundAction.h
Normal file
75
Libraries/LibThread/BackgroundAction.h
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Function.h>
|
||||||
|
#include <AK/NonnullRefPtr.h>
|
||||||
|
#include <AK/Optional.h>
|
||||||
|
#include <AK/Queue.h>
|
||||||
|
#include <AK/RefCounted.h>
|
||||||
|
#include <LibCore/CEventLoop.h>
|
||||||
|
#include <LibCore/CLock.h>
|
||||||
|
#include <LibCore/CObject.h>
|
||||||
|
#include <LibThread/Thread.h>
|
||||||
|
|
||||||
|
namespace LibThread {
|
||||||
|
|
||||||
|
template<typename Result>
|
||||||
|
class BackgroundAction;
|
||||||
|
|
||||||
|
class BackgroundActionBase {
|
||||||
|
template<typename Result>
|
||||||
|
friend class BackgroundAction;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BackgroundActionBase() {}
|
||||||
|
|
||||||
|
static CLockable<Queue<Function<void()>>>& all_actions();
|
||||||
|
static Thread& background_thread();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Result>
|
||||||
|
class BackgroundAction final : public CObject
|
||||||
|
, public RefCounted<BackgroundAction<Result>>
|
||||||
|
, private BackgroundActionBase {
|
||||||
|
|
||||||
|
C_OBJECT(BackgroundAction);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static NonnullRefPtr<BackgroundAction<Result>> create(
|
||||||
|
Function<Result()> action,
|
||||||
|
Function<void(Result)> on_complete = nullptr
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return adopt(*new BackgroundAction(move(action), move(on_complete)));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~BackgroundAction() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
BackgroundAction(Function<Result()> action, Function<void(Result)> on_complete)
|
||||||
|
: CObject(background_thread())
|
||||||
|
, m_action(move(action))
|
||||||
|
, m_on_complete(move(on_complete))
|
||||||
|
{
|
||||||
|
LOCKER(all_actions().lock());
|
||||||
|
|
||||||
|
this->ref();
|
||||||
|
all_actions().resource().enqueue([this] {
|
||||||
|
m_result = m_action();
|
||||||
|
if (m_on_complete) {
|
||||||
|
CEventLoop::main().post_event(*this, make<CDeferredInvocationEvent>([this](CObject&) {
|
||||||
|
m_on_complete(m_result.release_value());
|
||||||
|
this->deref();
|
||||||
|
}));
|
||||||
|
CEventLoop::main().wake();
|
||||||
|
} else
|
||||||
|
this->deref();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Function<Result()> m_action;
|
||||||
|
Function<void(Result)> m_on_complete;
|
||||||
|
Optional<Result> m_result;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
21
Libraries/LibThread/Makefile
Normal file
21
Libraries/LibThread/Makefile
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
include ../../Makefile.common
|
||||||
|
|
||||||
|
OBJS = \
|
||||||
|
Thread.o \
|
||||||
|
BackgroundAction.o
|
||||||
|
|
||||||
|
LIBRARY = libthread.a
|
||||||
|
DEFINES += -DUSERLAND
|
||||||
|
|
||||||
|
all: $(LIBRARY)
|
||||||
|
|
||||||
|
$(LIBRARY): $(OBJS)
|
||||||
|
@echo "LIB $@"; $(AR) rcs $@ $(OBJS) $(LIBS)
|
||||||
|
|
||||||
|
.cpp.o:
|
||||||
|
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
|
-include $(OBJS:%.o=%.d)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@echo "CLEAN"; rm -f $(LIBRARY) $(OBJS) *.d
|
39
Libraries/LibThread/Thread.cpp
Normal file
39
Libraries/LibThread/Thread.cpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#include <LibThread/Thread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
LibThread::Thread::Thread(Function<int()> action)
|
||||||
|
: CObject(nullptr)
|
||||||
|
, m_action(move(action))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LibThread::Thread::~Thread()
|
||||||
|
{
|
||||||
|
if (m_tid != -1) {
|
||||||
|
dbg() << "trying to destroy a running thread!";
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibThread::Thread::start()
|
||||||
|
{
|
||||||
|
int rc = create_thread([](void* arg) {
|
||||||
|
Thread* self = static_cast<Thread*>(arg);
|
||||||
|
int exit_code = self->m_action();
|
||||||
|
self->m_tid = -1;
|
||||||
|
exit_thread(exit_code);
|
||||||
|
return exit_code;
|
||||||
|
}, static_cast<void*>(this));
|
||||||
|
|
||||||
|
ASSERT(rc > 0);
|
||||||
|
dbg() << "Started a thread, tid = " << rc;
|
||||||
|
m_tid = rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibThread::Thread::quit(int code)
|
||||||
|
{
|
||||||
|
ASSERT(m_tid == gettid());
|
||||||
|
|
||||||
|
m_tid = -1;
|
||||||
|
exit_thread(code);
|
||||||
|
}
|
23
Libraries/LibThread/Thread.h
Normal file
23
Libraries/LibThread/Thread.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Function.h>
|
||||||
|
#include <LibCore/CObject.h>
|
||||||
|
|
||||||
|
namespace LibThread {
|
||||||
|
|
||||||
|
class Thread final : public CObject {
|
||||||
|
C_OBJECT(Thread);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Thread(Function<int()> action);
|
||||||
|
virtual ~Thread();
|
||||||
|
|
||||||
|
void start();
|
||||||
|
void quit(int code = 0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Function<int()> m_action;
|
||||||
|
int m_tid { -1 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ LDFLAGS = \
|
||||||
-L$(SERENITY_BASE_DIR)/Libraries/LibM \
|
-L$(SERENITY_BASE_DIR)/Libraries/LibM \
|
||||||
-L$(SERENITY_BASE_DIR)/Libraries/LibDraw \
|
-L$(SERENITY_BASE_DIR)/Libraries/LibDraw \
|
||||||
-L$(SERENITY_BASE_DIR)/Libraries/LibGUI \
|
-L$(SERENITY_BASE_DIR)/Libraries/LibGUI \
|
||||||
|
-L$(SERENITY_BASE_DIR)/Libraries/LibThread \
|
||||||
-L$(SERENITY_BASE_DIR)/Libraries/LibVT \
|
-L$(SERENITY_BASE_DIR)/Libraries/LibVT \
|
||||||
-L$(SERENITY_BASE_DIR)/Libraries/LibAudio
|
-L$(SERENITY_BASE_DIR)/Libraries/LibAudio
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ clean:
|
||||||
|
|
||||||
$(APPS) : % : %.o $(OBJS)
|
$(APPS) : % : %.o $(OBJS)
|
||||||
@echo "LD $@"
|
@echo "LD $@"
|
||||||
@$(LD) -o $@ $(LDFLAGS) $< -lc -lgui -ldraw -laudio -lipc -lcore -lpcidb
|
@$(LD) -o $@ $(LDFLAGS) $< -lc -lgui -ldraw -laudio -lipc -lthread -lcore -lpcidb
|
||||||
|
|
||||||
%.o: %.cpp
|
%.o: %.cpp
|
||||||
@echo "CXX $<"
|
@echo "CXX $<"
|
||||||
|
|
Loading…
Add table
Reference in a new issue