{"id":17187174,"url":"https://github.com/domfarolino/base","last_synced_at":"2025-04-13T18:38:08.470Z","repository":{"id":64945250,"uuid":"576087352","full_name":"domfarolino/base","owner":"domfarolino","description":"🧱 A simple threading and scheduling library with I/O support","archived":false,"fork":false,"pushed_at":"2023-05-22T02:15:08.000Z","size":58,"stargazers_count":6,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-27T09:21:25.501Z","etag":null,"topics":["async","asyncio","concurrency","multithreading","thread-safety"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/domfarolino.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-12-09T01:23:33.000Z","updated_at":"2024-11-21T10:35:52.000Z","dependencies_parsed_at":"2022-12-19T07:50:57.530Z","dependency_job_id":null,"html_url":"https://github.com/domfarolino/base","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domfarolino%2Fbase","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domfarolino%2Fbase/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domfarolino%2Fbase/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domfarolino%2Fbase/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/domfarolino","download_url":"https://codeload.github.com/domfarolino/base/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248328074,"owners_count":21085257,"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":["async","asyncio","concurrency","multithreading","thread-safety"],"created_at":"2024-10-15T01:05:35.774Z","updated_at":"2025-04-13T18:38:08.450Z","avatar_url":"https://github.com/domfarolino.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `//base` 🧱\n\n![ci-shield](https://github.com/domfarolino/base/actions/workflows/workflow.yml/badge.svg)\n\nBase is a cross-platform threading and task scheduling library written in C++.\nIt provides a lightweight event loop implementation to support asynchronous\nprogramming patterns, and the ability to perform platform-independent I/O.\n\nIt was designed based on pieces of Chromium's [`//base`] directory, which is a\ncollection of threading, task scheduling, synchronization, process management,\nplatform-independent I/O primitives, and much more.\n\nThis library was originally written in the [domfarolino/browser] repository, and\nlater pulled out into this repository to be a more general dependency for other\nprojects.\n\n----\n\n### Table of contents\n\n- [Supported platforms](#supported-platforms)\n- [API reference](#api-reference)\n  - [Task scheduling](#task-scheduling)\n    - [`TaskLoop`](#taskloop)\n    - [`TaskRunner`](#taskrunner)\n    - [Thread-local \u0026 process-global scheduling handles](#thread-local--process-global-scheduling-handles)\n  - [Threading](#threading)\n    - [`Thread`](#thread)\n    - [`SimpleThread`](#simplethread)\n    - [`ThreadChecker`](#threadchecker)\n  - [Synchronization](#synchronization)\n    - [`ConditionVariable`](#conditionvariable)\n    - [`Mutex`](#mutex)\n  - [Callbacks](#callbacks)\n    - [`OnceClosure` / `BindOnce`](#onceclosure--bindonce)\n  - [Miscellaneous](#miscellaneous)\n    - [`CHECK` variants](#check-variants)\n- [Building and running the tests](#building-and-running-the-tests)\n  - [Debugging](#debugging)\n\n----\n\n\n## Supported platforms\n\n![Linux](./assets/linux.svg)\n![macOS](./assets/apple.svg)\n\nIn progress:\n\n![Windows](./assets/windows.svg)\n\n\n## API reference\n\n### Task scheduling\n\n#### `TaskLoop`\n\n`base::TaskLoop` is the abstract base class that defines the API for a standard\nevent loop that runs on a thread. It has three variants:\n - `TaskLoopForUI`\n - `TaskLoopForIO`\n - `TaskLoopForWorker`\n\nTaskLoops primarily process \"tasks\", which are just function closures\n(specifically `base::OnceClosure`s).\n\n`TaskLoopForWorker` is the simplest implementation, only supporting the ability\nto post and execute tasks asynchronously; it doesn't support any\nplatform-specific UI or I/O functionality. The `TaskLoopForUI` and\n`TaskLoopForIO` variants are strict supersets of `TaskLoopForWorker`, supporting\nthe same task posting functionality as the worker flavor, but with additional\nplatform-specific UI and I/O listening capabilities respectively[^1].\n\nTaskLoops can be created with one of the two static creation methods:\n - `CreateUnbound(ThreadType)`\n - `Create(ThreadType)`\n\nBoth of these take a `base::ThreadType` that describe one of the variants above,\nand create and return the corresponding task loop implementation. The difference\nis that `CreateUnbound()` does not \"bind\" the loop to the current thread-local\nand process-global scheduling handles (see that section below), while the\n`Create()` method does, and therefore **must** be called on the thread that the\ntask loop will actually run on. The reason these exist is so that when you spin\nup a new thread, the intiator thread can create an unbound task loop (which can\nimmediately start queueing tasks) which is later bound on the new physical\nthread.\n\nTo actually run a task loop, you simply call `Run()`. This must be called on the\nthread that the loop is bound to, and will synchronously spin the loop, running\ntasks as they come in and performantly waiting when there are no more tasks to\nprocess.\n\nThe `Quit()` method can be called from any thread, and prompts the loop to quit\nright away or after the currently-running task ends if any such task exists.\n\n`QuitWhenIdle()` is similar to `Quit()`, but will quit the loop as soon as there\nare no more tasks to execute, stopping it from waiting indefinitely for more\ntasks.\n\n`GetTaskRunner()` returns a `std::shared_ptr\u003cTaskRunner\u003e` associated with a task\nloop; it is used to post tasks to the current loop, and it is safe to do so even\nif the loop has been destroyed. This is the mechanism with which you should post\nall tasks.\n\n#### `TaskRunner`\n\n`base::TaskRunner` is the mechanism with which you post tasks to `TaskLoop`s. It\nhas a single method `PostTask()` which does just that.\n\nYou typically hold a `std::shared_ptr\u003cTaskRunner\u003e` which has a weak reference to\nthe associated task loop, and therefore it is safe to call `PostTask()` even if\nthe underlying task loop has been destroyed. `TaskRunner`s are cheap and thread\nsafe, which means you can:\n - Have multiple `TaskRunner`s associated with a single `TaskLoop`, each living\n   on their own thread and posting tasks whenever they want [✅ recommended]\n - Have a single task runner accessed from various threads, posting tasks\n   whenever they want [supported, but weird... not recommended]\n\n#### Thread-local \u0026 process-global scheduling handles\n\nThe base library has a number of thread-local and process-global handles\naccessible through static global functions.\n\n**TODO**: finish this and the rest of the API reference.\n\n### Threading\n\n#### `Thread`\n\n`base::Thread` is the principal API with which you create a new physical thread\nwith a running `TaskLoop`. This\n\n#### `SimpleThread`\n#### `ThreadChecker`\n\n### Synchronization\n\n#### `ConditionVariable`\n#### `Mutex`\n\n### Callbacks\n\n#### `OnceClosure` / `BindOnce`\n\n### Miscellaneous\n\n#### `CHECK` variants\n\n\n## Building and running the tests\n\nWith the repository downloaded, to build and run the examples, run:\n\n```sh\n$ bazel build examples\n$ ./bazel-bin/examples/\u003cfoo\u003e\n```\n\nTo run the tests, run one of the following:\n\n```sh\n$ bazel test base/base_tests\n```\n\nor...\n\n\n```sh\n$ bazel build base/base_tests\n$ ./bazel-bin/base/base_tests\n```\n\n### Debugging\n\nThe base library is built with debugging symbols by default (see\n[`.bazelrc`](.bazelrc)). To debug a failing test or other internals with `lldb`,\nrun:\n\n```sh\n$ bazel build base/base_tests\n$ lldb ./bazel-bin/base/base_tests\n# Set breakpoints\n$ br s -n TaskLoopForIO::Run\n$ br s -f task_loop_for_io_test.cc -l \u003cline_number\u003e\n$ run --gtest_filter=\"TaskLoopForIOTestBase.BasicSocketReading\"\n```\n\n[^1]: Right now since `//base` is in an early development phase, no\nplatform-specific UI work has been implemented, and `TaskLoopForUI` is just an\nalias for `TaskLoopForWorker`. Once the base library supports platform-specific\nUI integration, that should change.\n\n[`//base`]: https://source.chromium.org/chromium/chromium/src/+/main:base/\n[domfarolino/browser]: https://github.com/domfarolino/browser\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdomfarolino%2Fbase","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdomfarolino%2Fbase","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdomfarolino%2Fbase/lists"}