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

Overview

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
You might also like...
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

Design and Implementation of kernel level threads for xv6 operating system. Adding system call related to threading environment in xv6 along with userland threading library with one to one mapping and semaphore implementation as synchronisation primitive
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

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

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

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

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

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

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

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

    opened by LordSk 2
Owner
Mikulas Florek
I'm the best
Mikulas Florek
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

Sepehr Taghdisian 35 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.

Hyoung Min Suh 12 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

Google 1.5k 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);

null 390 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

Yossi Kreinin 279 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

Andrea Guzzo 392 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

Eyal Rozenberg 548 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,

Intel Corporation 18 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

Yasser Asmi 46 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. Features C++ 11 Minimal Source Code Header-only No external depe

Dihara Wijetunga 27 Oct 28, 2022