{"id":20105038,"url":"https://github.com/misyltoad/libjobs","last_synced_at":"2025-10-29T03:13:18.587Z","repository":{"id":135969091,"uuid":"286452070","full_name":"misyltoad/libjobs","owner":"misyltoad","description":"A cross-platform, header-only compute-shader-styled job dispatching API written in C++.","archived":false,"fork":false,"pushed_at":"2020-08-19T23:50:50.000Z","size":14,"stargazers_count":21,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-09T10:45:40.232Z","etag":null,"topics":["asynchronous","compute-shader","dispatching","job","thread","threading","work"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"zlib","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/misyltoad.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-08-10T11:06:39.000Z","updated_at":"2025-01-04T10:58:17.000Z","dependencies_parsed_at":null,"dependency_job_id":"6e3404f4-3f5b-4c5a-b72d-744cec4bbbb6","html_url":"https://github.com/misyltoad/libjobs","commit_stats":null,"previous_names":["misyltoad/libjobs"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misyltoad%2Flibjobs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misyltoad%2Flibjobs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misyltoad%2Flibjobs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misyltoad%2Flibjobs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/misyltoad","download_url":"https://codeload.github.com/misyltoad/libjobs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252657152,"owners_count":21783781,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asynchronous","compute-shader","dispatching","job","thread","threading","work"],"created_at":"2024-11-13T17:45:40.799Z","updated_at":"2025-10-29T03:13:13.565Z","avatar_url":"https://github.com/misyltoad.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# libjobs\n\nA cross-platform, header-only compute-shader-styled job dispatching API written in C++.\n\n## Features\n\n* A compute-shader-styled job dispatching API\n  * Dispatch `x` number of jobs across `y` number of threads\n    * Local Invocation Index\n    * Local Invocation Count\n    * Global Invocation Index\n    * Workgroup Id\n    * Shared Memory\n      * Across a workgroup\n    * (You already know the Global Invocation Count yourself, so you can capture it)\n* Execute a single function asynchronously\n* Easily wait for a single job or multiple set of jobs to complete\n* Fire and forget functions or dispatches\n* Jobs are lambda functions with capture support\n* Entirely thread safe api\n* Help with work on this thread while you wait with `JobRunner::waitAndWork`\n* Scope safety\n  * When a `JobRunner` goes out of scope, all pending jobs will be completed before shutting down\n    * This is done without tracking jobs/watchers inside the `JobRunner`\n* Cross platform thread API\n  * Thread naming\n  * Thread priorities\n  * Thread yielding\n  * Thread pinning\n    * All of this is configurable and supported by the job runner\n    * Exposed for use on normal `std::thread`s also in `libjobs::platform`\n\n## License\n\nlibjobs is licensed under zlib/libpng.\n\n## Requirements\n\n**libjobs** *requires* a compiler with support for C++20.\n\n## How to use\n\nClone or download this repository, and include the `libjobs.h` header into your project.\n\nAll functions/classes/etc in the library are in the `libjobs` namespace.\n\n## Examples\n\nCreate a ``libjobs::JobRunner`` like so:\n```cpp\nlibjobs::JobRunner\u003c256\u003e runner(libjobs::platform::threadCount() - 1);\n```\nwhere 256 = the maximum number of queued jobs,\nand the first argument represents the maximum number of threads the runner can use.\n\nIn this case we leave the main thread free.\n\n### Throwaway async!\n```cpp\n// Throwaway async!\nrunner.execute([](libjobs::JobContext\u0026\u0026 ctx) -\u003e bool {\n  printf(\"Asynchronous frog!\\n\");\n  return true;\n});\n```\nWill output:\n``Asynchronous frog!``\n\n### Counting to 100!\n```cpp\nlibjobs::JobWatcher watcher;\nstd::atomic\u003cuint32_t\u003e counter = 0;\nrunner.dispatch(watcher, [\u0026counter](libjobs::JobContext\u0026\u0026 ctx) -\u003e bool {\n  counter++;\n  return true;\n}, 100, runner.threadCount());\nwatcher.wait();\nprintf(\"All %u of us counted to %u!\\n\", runner.threadCount(), counter.load());\n```\nWill output:\n``All 23 of us counted to 100!`` (where 23 == the number of threads on the JobRunner)\n\n### Progress counting!\n```cpp\nlibjobs::JobWatcher watcher;\nstd::atomic\u003cuint32_t\u003e counter = 0;\nconst uint32_t globalInvocationCount = 100;\nrunner.dispatch(watcher, [\u0026counter](libjobs::JobContext\u0026\u0026 ctx) -\u003e bool {\n  using namespace std::chrono_literals;\n  std::this_thread::sleep_for(10ms);\n  counter++;\n  return true;\n}, globalInvocationCount, runner.threadCount());\n\nuint32_t lastCounter = std::numeric_limits\u003cuint32_t\u003e::max();\nwhile (watcher.busy()) {\n  uint32_t counterValue = counter.load();\n  if (lastCounter != counterValue)\n    printf(\"Loading: %u/%u complete!\\n\", counterValue, globalInvocationCount);\n  lastCounter = counterValue;\n  libjobs::platform::yield();\n}\nuint32_t counterValue = counter.load();\nif (lastCounter != counterValue)\n  printf(\"Loading: %u/%u complete!\\n\", counterValue, globalInvocationCount);\n```\nWill output:\n```\nLoading: 0/100 complete!\nLoading: 1/100 complete!\nLoading: 2/100 complete!\nLoading: 3/100 complete!\nLoading: 4/100 complete!\nLoading: 5/100 complete!\nLoading: 6/100 complete!\nLoading: 7/100 complete!\nLoading: 8/100 complete!\n[...]\nLoading: 97/100 complete!\nLoading: 98/100 complete!\nLoading: 99/100 complete!\nLoading: 100/100 complete!\n```\n\n### Cool context information!\n\n```cpp\nstd::mutex waitingMutex;\nbool printedExample = false;\nlibjobs::JobWatcher watcher;\nconst uint32_t globalInvocationCount = 128;\nrunner.dispatch(watcher, [\u0026printedExample, \u0026waitingMutex, globalInvocationCount](libjobs::JobContext\u0026\u0026 ctx) -\u003e bool {\n  std::unique_lock lock(waitingMutex);\n  if (printedExample)\n    return true;\n\n  printedExample = true;\n  printf(\"localInvocationIndex:  %u\\n\", ctx.localInvocationIndex);\n  printf(\"localInvocationCount:  %u\\n\", ctx.localInvocationCount);\n  printf(\"globalInvocationIndex: %u\\n\", ctx.globalInvocationIndex);\n  // You pass in the global invocation count (jobCount) yourself,\n  // so we don't expose this in the context, but you can easily\n  // just capture it.\n  printf(\"globalInvocationCount: %u\\n\", globalInvocationCount);\n  printf(\"workgroupId:           %u\\n\", ctx.workgroupId);\n  printf(\"sharedMemory:          %p\\n\", ctx.sharedMemory);\n  return true;\n}, globalInvocationCount, runner.threadCount(), 128);\nwatcher.wait();\n```\nWill output:\n```\nlocalInvocationIndex:  0\nlocalInvocationCount:  23\nglobalInvocationIndex: 0\nglobalInvocationCount: 128\nworkgroupId:           0\nsharedMemory:          0x7ff997372cc0\n```\nwhere 23 is the number of threads on the job runner that we passed in for the group size.\n\n*(note: do not assume `groupSize == localInvocationCount`, it is not guaranteed if the `groupSize` does not divide into the `jobCount`)*\n\n### Scope safety!\n\n```cpp\n// This job will be completed and waited upon when the\n// runner goes out of scope.\nprintf(\"Running a job that waits a bit and waiting on its destruction.\\n\");\nrunner.dispatch([](libjobs::JobContext\u0026\u0026 ctx) -\u003e bool {\n  using namespace std::chrono_literals;\n  std::this_thread::sleep_for(2s);\n  printf(\"I finished waiting, goodbye!\\n\");\n  return true;\n}, 1, 1);\nreturn 0;\n```\nWill output:\n```\nRunning a job that waits a bit and waiting on its destruction.\n[ 2 seconds of time... ]\nI finished waiting, goodbye!\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmisyltoad%2Flibjobs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmisyltoad%2Flibjobs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmisyltoad%2Flibjobs/lists"}