diff --git a/Applications/Makefile.common b/Applications/Makefile.common index af49961f1af..6707d000106 100755 --- a/Applications/Makefile.common +++ b/Applications/Makefile.common @@ -3,7 +3,7 @@ DEFINES += -DUSERLAND all: $(APP) $(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: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/Kernel/makeall.sh b/Kernel/makeall.sh index e44f75db078..b21ee80acf0 100755 --- a/Kernel/makeall.sh +++ b/Kernel/makeall.sh @@ -19,10 +19,11 @@ build_targets="" build_targets="$build_targets ../DevTools/IPCCompiler" 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/LibCore" 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_targets="$build_targets ../Servers/AudioServer" diff --git a/Libraries/LibThread/BackgroundAction.cpp b/Libraries/LibThread/BackgroundAction.cpp new file mode 100644 index 00000000000..0f1ef2046a1 --- /dev/null +++ b/Libraries/LibThread/BackgroundAction.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +static CLockable>>* s_all_actions; +static LibThread::Thread* s_background_thread; + +static int background_thread_func() +{ + while (true) { + Function 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>>(); + s_background_thread = new LibThread::Thread(background_thread_func); + s_background_thread->set_name("Background thread"); + s_background_thread->start(); +} + +CLockable>>& 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; +} diff --git a/Libraries/LibThread/BackgroundAction.h b/Libraries/LibThread/BackgroundAction.h new file mode 100644 index 00000000000..965ec7b0bf5 --- /dev/null +++ b/Libraries/LibThread/BackgroundAction.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace LibThread { + +template +class BackgroundAction; + +class BackgroundActionBase { + template + friend class BackgroundAction; + +private: + BackgroundActionBase() {} + + static CLockable>>& all_actions(); + static Thread& background_thread(); +}; + +template +class BackgroundAction final : public CObject + , public RefCounted> + , private BackgroundActionBase { + + C_OBJECT(BackgroundAction); + +public: + static NonnullRefPtr> create( + Function action, + Function on_complete = nullptr + ) + { + return adopt(*new BackgroundAction(move(action), move(on_complete))); + } + + virtual ~BackgroundAction() {} + +private: + + BackgroundAction(Function action, Function 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([this](CObject&) { + m_on_complete(m_result.release_value()); + this->deref(); + })); + CEventLoop::main().wake(); + } else + this->deref(); + }); + } + + Function m_action; + Function m_on_complete; + Optional m_result; +}; + +} diff --git a/Libraries/LibThread/Makefile b/Libraries/LibThread/Makefile new file mode 100644 index 00000000000..bc548c208a7 --- /dev/null +++ b/Libraries/LibThread/Makefile @@ -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 diff --git a/Libraries/LibThread/Thread.cpp b/Libraries/LibThread/Thread.cpp new file mode 100644 index 00000000000..de5296f353b --- /dev/null +++ b/Libraries/LibThread/Thread.cpp @@ -0,0 +1,39 @@ +#include +#include + +LibThread::Thread::Thread(Function 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(arg); + int exit_code = self->m_action(); + self->m_tid = -1; + exit_thread(exit_code); + return exit_code; + }, static_cast(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); +} diff --git a/Libraries/LibThread/Thread.h b/Libraries/LibThread/Thread.h new file mode 100644 index 00000000000..d9562da10f7 --- /dev/null +++ b/Libraries/LibThread/Thread.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace LibThread { + +class Thread final : public CObject { + C_OBJECT(Thread); + +public: + explicit Thread(Function action); + virtual ~Thread(); + + void start(); + void quit(int code = 0); + +private: + Function m_action; + int m_tid { -1 }; +}; + +} diff --git a/Makefile.common b/Makefile.common index e8e973920dc..f53fd05d03e 100644 --- a/Makefile.common +++ b/Makefile.common @@ -22,6 +22,7 @@ LDFLAGS = \ -L$(SERENITY_BASE_DIR)/Libraries/LibM \ -L$(SERENITY_BASE_DIR)/Libraries/LibDraw \ -L$(SERENITY_BASE_DIR)/Libraries/LibGUI \ + -L$(SERENITY_BASE_DIR)/Libraries/LibThread \ -L$(SERENITY_BASE_DIR)/Libraries/LibVT \ -L$(SERENITY_BASE_DIR)/Libraries/LibAudio diff --git a/Userland/Makefile b/Userland/Makefile index 955eb07470d..ab46369c961 100644 --- a/Userland/Makefile +++ b/Userland/Makefile @@ -19,7 +19,7 @@ clean: $(APPS) : % : %.o $(OBJS) @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 @echo "CXX $<"