{"id":13671810,"url":"https://github.com/drewcrawford/kiruna","last_synced_at":"2026-03-08T12:39:19.867Z","repository":{"id":46271675,"uuid":"407710244","full_name":"drewcrawford/kiruna","owner":"drewcrawford","description":"An experimental, tiny, fast, and simple async executor and IO library for Rust","archived":false,"fork":false,"pushed_at":"2024-10-23T21:04:34.000Z","size":541,"stargazers_count":3,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-12-01T10:06:58.175Z","etag":null,"topics":["async","asyncio","executor","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/drewcrawford.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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":"2021-09-18T00:08:22.000Z","updated_at":"2024-10-23T21:04:37.000Z","dependencies_parsed_at":"2024-11-11T09:32:25.664Z","dependency_job_id":"d3199fac-4918-4ca1-842e-2e672a3e90a0","html_url":"https://github.com/drewcrawford/kiruna","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/drewcrawford%2Fkiruna","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drewcrawford%2Fkiruna/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drewcrawford%2Fkiruna/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drewcrawford%2Fkiruna/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/drewcrawford","download_url":"https://codeload.github.com/drewcrawford/kiruna/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228370991,"owners_count":17909389,"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","executor","rust"],"created_at":"2024-08-02T09:01:19.340Z","updated_at":"2026-03-08T12:39:14.846Z","avatar_url":"https://github.com/drewcrawford.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"![logo](art/logo.png)\n\nKiruna is an experimental, tiny, fast, and simple async executor and IO library.  Kiruna is an answer to these questions:\n\n1.  ~~What if Rust had *EVEN MOAR* async runtimes than it does already?~~\n2.  What's missing for writing interactive desktop/mobile applications?\n3.  What if you 'stepped into' some Future implementation and found 1-2 small files you could read, understand, debug, and optimize?\n4.  What if we just call some API from the operating system?\n5.  What if users provided a bit more information that could fix performance issues before they happen?\n6.  ~~What if you are a hipster and too cool to use tokio?~~\n\nFor more details on the rationale and design principles, read the manifesto.\n\nKiruna is also a remote town in the arctic circle.  Programs that use it will be cold, beautiful, and isolated from more popular async runtimes.\n\n# Practicalities\n\n* Kiruna is an experimental/research quality.  But I am depending on it from real projects so I don't intend it to be a toy library.\n* Many features are missing, partly implemented or still being designed.  Some APIs will change.\n* macOS and Windows are my primary focus.  Linux is planned but not implemented, iOS might happen if I end up needing it\n* Free for noncommerial or 'small commercial' use, commercial licensing is available.\n\n# Cargo features\n\nKiruna is \"batteries included but off by default\".  When you build kiruna you build basically nothing,\nto do something you need to turn it on.  Table of features:\n\n* `io_stream` implements the `io::stream` personality.\n* `io_seek` implements the `io::seek` personality.\n* `sync` implements the default single-threaded `sync::Executor`.  Note that individual operations generally have some 'other' way to\n  do concurrency, so this is the right default for small to medium workloads.\n* `crate::test` is the [world's smallest async 'runtime'](https://github.com/drewcrawford/kiruna/blob/f516f2ad8f493577b0fd2a6f2feef8bde35a8a30/src/test.rs#L23), which polls your futures in a busyloop.\n  This is very silly but surprisingly great for use in tests.  It compiles quickly, has no startup time and is badly-behaved generally,\n  which flushes out unusual bugs in a unit test.\n* `join` implements some join functions like in the [futures](https://crates.io/crates/futures) crate.  But they compile faster\n  and support other behaviors like 'fail fast' that are interesting to applications\n* `futures` is a grab bag of convenient APIs for futures.\n* `tpc`: An experimental multicore thread-per-core executor.  Suitable for CPU-bound work or non-blocking IO.\n* `block_party`: Creates futures out of blocking calls.  An experimental multicore threadpool for IO-bound work.\n\n\n\nComing someday:\n* Additional priorities\n\nOut of scope:\n* Lesser-used IO, like multiprocess communication.\n  * See [command-rs](https://github.com/drewcrawford/command-rs) for a process implementation\n* High-level IO, like TLS or HTTP\n\n# Kiruna manifesto\n\n## Interactive apps\n\nRuntimes like tokio and even most alternatives like smol are thinking about servers.  Their assumption is you have a bajillion requests, and the more requests per second\nthe better they are.\n\nIn an interactive application, you have 15 tasks, perhaps 2 of which are realtime.  Success means \"not interrupting the realtime tasks\",\nwhich might be *delaying* outstanding work.  For that reason, many async libraries that exist are at loggerheads with application use.\n\nThere are some cool solutions to this problem like [thin_main_loop](https://github.com/diwic/thin_main_loop).  However, they tend to view\nGUI problems as a separate \"thing\".  To some extent they are, to some extent though it creates distance with more 'mainline' async Rust.\n\nThe whole problem is resolved by assigning every task a *priority*.  Tasks with the same priority target maximum throughput,\nso that's the same as in serverland.  Tasks with different priorities target not interrupting a higher-priority task.\n\nOf course, the key difference of this design is it requires the application developer to specify the priority of the task.  This\nimplicitly means that any use of Kiruna expects more options and configuration than other async runtimes.\n\n## More information\n\nThis illustrates an important principle of Kiruna's design, which is 'ask the developer what they want to do'.  Reading a file\nisn't just a question of file descriptors, it is a question of semantics and intent, because opening a document and caching data\nshould have different task priorities.\n\nAnother dimension of difference is in types of files.  If you are reading from a socket, there is nothing for it\nbesides copying all the data into memory which has such complications as residency, buffer sizes, and deallocations that\nultimately someone has to manage.  Whereas with an ordinary file, any modern OS can [memory-map](https://en.wikipedia.org/wiki/Memory-mapped_I/O) that file\nfor reading which is usually way cheaper and simpler.\n\nBut even though the application programmer knows whether they're reading from a file or a socket, many async IO\nlibraries do not.  If they assume the common denominator, the missed optimizations contribute to the common\nobservation that using async might make code *slower*. Alternatively, if libraries perform checks or heuristics,\nthere's also a runtime cost for that, and the increased complexity of maintaining that code.  And if the application\nworks with an owned buffer, the optimization might be moot entirely.\n\nIn Kiruna, IO operations have 'personalities' that distinguish various cases with a zero-cost abstraction and\nhelps the application developer understand what is going on, and will naturally choose the right API.  It's a bit like\n[C-CALLER-CONTROL](https://rust-lang.github.io/api-guidelines/flexibility.html#c-caller-control) principle\nof making functions that need owned data have owned data parameters.  It makes the performance easier to reason about,\nat the cost of some additional API.\n\nThe tradeoff is you have to tell Kiruna more information just to 'read a file' than you\nwould in another library.\n\n## Small implementations\n\nAsync implementations can be quite complicated, and many libraries create shared, reusable components to manage this complexity.\nFor example, they may introduce a \"reactor\", and this reactor may have various \"backends\" that vary by platform.  While\nthese designs improve consistency internally in large codebases like tokio, these concepts are distant from 'reading a file' which is allegedly\nwhy we entered the async tarpit to begin with.  The result is that opening the hood is much harder for application developers\nthan it could be.\n\nIn Kiruna, the \"async read function on macOS\" *looks* like it does that. Here it is:\n\n```rust\n    ///Performs a single read.\n    ///\n    /// In practice, this function reads 0 bytes if the stream is closed.\n    fn once\u003c'a, O: Into\u003cOSReadOptions\u003c'a\u003e\u003e\u003e(\u0026self, os_read_options: O) -\u003e impl Future\u003cOutput=Result\u003cManaged,OSError\u003e\u003e {\n        let (continuation, completion) = blocksr::continuation::Continuation::\u003c(),_\u003e::new();\n        read_completion(dispatch_fd_t::new(self.fd), usize::MAX, os_read_options.into().queue, |data,err| {\n            if err==0 {\n                completion.complete(Ok(Managed::retain(data)))\n            }\n            else {\n                completion.complete(Err(OSError(err)))\n            }\n        });\n        continuation\n    }\n```\n\nAdmittedly there are a few more `\u003c\u003e` in there than I'd like, but you don't need a degree in reactorology to profile this code or\nset a breakpoint.  You might compare this to some [similar implementation in tokio](https://github.com/tokio-rs/tokio/blob/1073f6e8be93837803704e770c7c54ddb4dcde27/tokio-util/src/lib.rs#L106)\n(at least, I think they're similar, but I'm not certain.) I don't mean tokio code is bad in any way, I just mean it's not obvious\nwhat it does exactly, which is not what I want when debugging my application.\n\nThe design principle in Kiruna is that we start with the problem, which is reading a file, and then \"*just write code that does that*\".\n\nIf it's that easy, you might well ask why other libraries don't have that design.  The tradeoff is that there are a lot of tiny functions in Kiruna\nand they lack consistency in their implementations.  Personally I think this is a feature, since memory-mapped IO is\ndifferent than other kinds and it deserves a different implementation.  The result is you might encounter\nmany more implementation differences, particularly across different OS.  Please file bugs.\n\nIn limited cases, such as for read/write on the same OS and personality, Kiruna has some code reuse, but it is quite\nnarrow in scope and only has to worry about a very small set of situations within a single platform so it's easy\nto jump into and see what it does.\n\n## OS leverage\n\nWhen you have a \"big reactor\" design, porting your library to an OS becomes all about porting the reactor, which ultimately calls\nsome very low-level kernel call regardless of read/write, file/socket, etc.\nFor example, [kqueue](https://en.wikipedia.org/wiki/Kqueue) or [epoll](https://man7.org/linux/man-pages/man7/epoll.7.html).\n\nHowever, kqueue is [not anywhere close to the preferred way to do IO on macOS](https://news.ycombinator.com/item?id=12687257), that is [dispatch_read](https://developer.apple.com/documentation/dispatch/1388933-dispatch_read).\nSimilarly, `epoll` is not the preferred way\nto do IO on linux, that is [io_uring](https://kernel.dk/io_uring.pdf).  Because these APIs are all very different from each other,\nand the reactor should be the same everywhere, it's quite difficult for reactors to adopt them.\n\nHowever, because Kiruna is built around many little functions that \"just read a file on macOS\", they can \"just\" call the OS's preferred\nAPI to read files on macOS.  In fact, one function can call `kqueue` and another might call `dispatch`, which is great for experimentation,\nprofiling, debugging, and incrementally adopting OS innovation.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrewcrawford%2Fkiruna","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdrewcrawford%2Fkiruna","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrewcrawford%2Fkiruna/lists"}