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:
Sergey Bugaev 2019-08-25 18:55:56 +03:00 committed by Andreas Kling
parent d5f3487203
commit e1a6f8a27d
Notes: sideshowbarker 2024-07-19 12:31:27 +09:00
9 changed files with 211 additions and 3 deletions

View file

@ -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 $<

View file

@ -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"

View 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;
}

View 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;
};
}

View 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

View 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);
}

View 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 };
};
}

View file

@ -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

View file

@ -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 $<"