Lucy job system - Fiber-based job system with extremely simple API

Lucy Job System

This is outdated compared to Lumix Engine. Use that instead.

Fiber-based job system with extremely simple API.

It's a standalone version of job system used in Lumix Engine. Only Windows is supported now, although the Lumix Engine version has Linux support, I have to copy it here yet.

Demo

Create a solution with create_solution_vs2017.bat. The solution is in build/tmp/. Open the solution, compile and run.

#include "lucy.h"
#include <condition_variable>
#include <cstdio>
#include <mutex>


std::mutex g_mutex;
std::condition_variable g_finished;
std::mutex g_finished_mutex;


void print(const char* msg)
{
	std::lock_guard lock(g_mutex);
	printf(msg);
	printf("\n");
}


void jobB(void*)
{
	print("B begin");
	print("B end");
}


void jobA(void*)
{
	print("A begin");
	lucy::SignalHandle finished = lucy::INVALID_HANDLE;
	for(int i = 0; i < 10; ++i) {
		lucy::run(nullptr, jobB, &finished);
	}
	lucy::wait(finished);
	print("A end");
	g_finished.notify_all();
}


int main(int argc, char* argv[])
{
	lucy::init();
	lucy::run(nullptr, jobA, nullptr);
	std::unique_lock<std::mutex> lck(g_finished_mutex);
	g_finished.wait(lck);
	lucy::shutdown();
}

Integration

Just put lucy.h and lucy.cpp in your project and you are good to go.

Docs

See src/main.cpp for an example.

Initialization / shutdown

lucy::init() must be called before any other function from lucy namespace lucy::shutdown() call this when you don't want to run any more jobs. Make sure all jobs are finished before calling this, since it does not wait for jobs to finish.

Jobs

Job is a function pointer with associated void data pointer. When job is executed, the function is called and the data pointer is passed as a parameter to this function.

lucy::run push a job to global queue

int value = 42;
lucy::run(&value, [](void* data){
	printf("%d", *(int*)data);
}, nullptr);

This prints 42. Eventually, after the job is finished, a signal can be triggered, see signals for more information.

Signals

lucy::SignalHandle signal = lucy::INVALID_HANDLE;
lucy::wait(signal); // does not wait, since signal is invalid
lucy::incSignal(&signal);
lucy::wait(signal); // wait until someone calls lucy::decSignal, execute other jobs in the meantime
lucy::SignalHandle signal = lucy::INVALID_HANDLE;
for (int i = 0; i < N; ++i) {
	lucy::incSignal(&signal);
}
lucy::wait(signal); // wait until lucy::decSignal is called N-times

If a signal is passed to lucy::run (3rd parameter), then the signal is automatically incremented. It is decremented once all the job is finished.

lucy::SignalHandle finished = lucy::INVALID_HANDLE;
for(int i = 0; i < 10; ++i) {
	lucy::run(nullptr, job_fn, &finished);
}
lucy::wait(finished); // wait for all 10 jobs to finish

There's no need to destroy signals, it's "garbage collected".

Signals can be used as preconditions to run a job. It means a job starts only after the signal is signalled:

lucy::SignalHandle precondition = getPrecondition();
lucy::runEx(nullptr, job_fn, nullptr, precondition, lucy::ANY_WORKER);

is functionally equivalent to:

void job_fn(void*) { 
	lucy::wait(precondition);
	dowork();
}
lucy::run(nullptr, job_fn, nullptr);

However, the lucy::runEx version has better performance.

Finally, a job can be pinned to specific worker thread. This is useful for calling APIs which must be called from the same thread, e.g. OpenGL functions or WinAPI UI functions.

lucy::runEx(nullptr, job_fn, nullptr, lucy::INVALID_HANDLE, 3); // run on worker thread 3
Owner
Mikulas Florek
I'm the best
Mikulas Florek
Similar Resources

Operating system project - implementing scheduling algorithms and some system calls for XV6 OS

About XV6 xv6 is a modern reimplementation of Sixth Edition Unix in ANSI C for multiprocessor x86 and RISC-V systems. It was created for pedagogical p

Dec 22, 2022

Simple example for running code on VPU from Linux

VPU-example Simple example for running code on VPU from Linux Toggling GPIO2 on a Raspberry Pi, see code.asm Based on https://github.com/ali1234/vcpok

Aug 2, 2022

Laughably simple Actor concurrency framework for C++20

Light Actor Framework Concurrency is a breeze. Also a nightmare, if you ever used synchronization techniques. Mostly a nightmare, though. This tiny li

Dec 27, 2022

An ultra-simple thread pool implementation for running void() functions in multiple worker threads

An ultra-simple thread pool implementation for running void() functions in multiple worker threads

void_thread_pool.cpp © 2021 Dr Sebastien Sikora. [email protected] Updated 06/11/2021. What is it? void_thread_pool.cpp is an ultra-simple

Nov 19, 2021

ThreadPool - A simple C++11 Thread Pool implementation

ThreadPool A simple C++11 Thread Pool implementation. Basic usage: // create thread pool with 4 worker threads ThreadPool pool(4); // enqueue and sto

Jan 7, 2023

Parallel-util - Simple header-only implementation of "parallel for" and "parallel map" for C++11

parallel-util A single-header implementation of parallel_for, parallel_map, and parallel_exec using C++11. This library is based on multi-threading on

Jun 24, 2022

A library for enabling task-based multi-threading. It allows execution of task graphs with arbitrary dependencies.

Fiber Tasking Lib This is a library for enabling task-based multi-threading. It allows execution of task graphs with arbitrary dependencies. Dependenc

Dec 30, 2022

OpenCL based GPU accelerated SPH fluid simulation library

libclsph An OpenCL based GPU accelerated SPH fluid simulation library Can I see it in action? Demo #1 Demo #2 Why? Libclsph was created to explore the

Jul 27, 2022
Comments
  • lucy::wait() needs a worker

    lucy::wait() needs a worker

    First, thank you for sharing your work. I just so happened to want a job system for my project and thought I would start by testing yours.

    So in my project, I have 2 threads dedicated to certain tasks and the remaining would be workers. In one of the dedicated thread, I create a few jobs to be run:

    lucy::SignalHandle finished = lucy::INVALID_HANDLE;
    for (int i = 0; i < 10; ++i) {
        lucy::run(nullptr, jobB, &finished);
    }
    lucy::wait(finished);
    

    lucy::wait() calls getWorker() which is null since we did not create any worker on this thread. I have attached a simple example (based on your main.cpp example code) if you want to reproduce the bug.

    main.zip

Termite-jobs - Fast, multiplatform fiber based job dispatcher based on Naughty Dogs' GDC2015 talk.

NOTE This library is obsolete and may contain bugs. For maintained version checkout sx library. until I rip it from there and make a proper single-hea

Jan 9, 2022
A easy to use multithreading thread pool library for C. It is a handy stream like job scheduler with an automatic garbage collector. This is a multithreaded job scheduler for non I/O bound computation.

A easy to use multithreading thread pool library for C. It is a handy stream-like job scheduler with an automatic garbage collector for non I/O bound computation.

Jun 4, 2022
A hybrid thread / fiber task scheduler written in C++ 11

Marl Marl is a hybrid thread / fiber task scheduler written in C++ 11. About Marl is a C++ 11 library that provides a fluent interface for running tas

Jan 4, 2023
Coroutine - C++11 single .h asymmetric coroutine implementation via ucontext / fiber

C++11 single .h asymmetric coroutine implementation API in namespace coroutine: routine_t create(std::function<void()> f); void destroy(routine_t id);

Dec 20, 2022
checkedthreads: no race condition goes unnoticed! Simple API, automatic load balancing, Valgrind-based checking

checkedthreads checkedthreads is a fork-join parallelism framework for C and C++ providing: Automated race detection using debugging schedulers and Va

Nov 4, 2022
Simple and fast C library implementing a thread-safe API to manage hash-tables, linked lists, lock-free ring buffers and queues

libhl C library implementing a set of APIs to efficiently manage some basic data structures such as : hashtables, linked lists, queues, trees, ringbuf

Dec 3, 2022
Thin C++-flavored wrappers for the CUDA Runtime API

cuda-api-wrappers: Thin C++-flavored wrappers for the CUDA runtime API Branch Build Status: Master | Development: nVIDIA's Runtime API for CUDA is int

Dec 28, 2022
OOX: Out-of-Order Executor library. Yet another approach to efficient and scalable tasking API and task scheduling.

OOX Out-of-Order Executor library. Yet another approach to efficient and scalable tasking API and task scheduling. Try it Requirements: Install cmake,

Oct 25, 2022
Lev - Lightweight C++ wrapper for LibEvent 2 API

lev Lightweight C++ wrapper for LibEvent 2 API LibEvent is a great library. It uses a C interface which is well designed but has a learning curve. Thi

Sep 15, 2022
DwThreadPool - A simple, header-only, dependency-free, C++ 11 based ThreadPool library.
DwThreadPool - A simple, header-only, dependency-free, C++ 11 based ThreadPool library.

dwThreadPool A simple, header-only, dependency-free, C++ 11 based ThreadPool library. Features C++ 11 Minimal Source Code Header-only No external depe

Oct 28, 2022