Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/g41797/mailbox
Zig Mailbox is convenient inter-thread communication mechanizm.
https://github.com/g41797/mailbox
actor-model channel-on-steroids fan-in fan-out inter-thread-communication mailbox thread-safe zig zig-library zig-package
Last synced: about 23 hours ago
JSON representation
Zig Mailbox is convenient inter-thread communication mechanizm.
- Host: GitHub
- URL: https://github.com/g41797/mailbox
- Owner: g41797
- License: mit
- Created: 2024-07-31T05:20:32.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2024-10-22T16:32:17.000Z (3 months ago)
- Last Synced: 2024-11-17T16:31:11.497Z (2 months ago)
- Topics: actor-model, channel-on-steroids, fan-in, fan-out, inter-thread-communication, mailbox, thread-safe, zig, zig-library, zig-package
- Language: Zig
- Homepage:
- Size: 366 KB
- Stars: 34
- Watchers: 2
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-zig - g41797/mailbox - thread communication mechanizm. (Multithreading / Utility)
README
![](_logo/mailboxes.png)
# Mailbox - old new way of inter-thread communication.
[![CI](https://github.com/g41797/yazq/actions/workflows/ci.yml/badge.svg)](https://github.com/g41797/yazq/actions/workflows/ci.yml)
## A bit of history, a bit of theory
Mailboxes are one of the fundamental parts of the [actor model originated in **1973**](https://en.wikipedia.org/wiki/Actor_model):
> An actor is an object that carries out its actions in response to communications it receives.
> Through the mailbox mechanism, actors can decouple the reception of a message from its elaboration.
> A mailbox is nothing more than the data structure (FIFO) that holds messages.I first encountered MailBox in the late 80s while working on a real-time system:
> "A **mailbox** is object that can be used for inter-task
communication. When task A wants to send an object to task B, task A
must send the object to the mailbox, and task B must visit the mailbox,
where, if an object isn't there, it has the option of *waiting for any
desired length of time*..."
> **iRMX 86™ NUCLEUS REFERENCE MANUAL** _Copyright @ 1980, 1981 Intel Corporation.Since than I have used it in:
| OS | Language(s) |
|:-----------:|:-----------:|
| iRMX | *PL/M-86* |
| AIX | *C* |
| Windows | *C++/C#* |
| Linux | *Go* |**Now it's Zig time!!!**
## Why?
If your thread runs in "Fire and Forget" mode, you don't need Mailbox.
But in real multithreaded applications, threads communicate with each other as
members of a work team.**Mailbox** provides a convenient and simple inter-thread communication:
- thread safe
- asynchronous
- non-blocking
- cancelable
- no own allocations
- unbounded
- fan-out/fan-in
## Example of usage - 'Echo'
```zig
// Mbx is Mailbox with usize letter(data)
const Mbx = mailbox.MailBox(usize);// Echo - runs on own thread
// It has two mailboxes
// "TO" and "FROM" - from the client point of the view
// Receives letter via 'TO' mailbox
// Replies letter without change (echo) to "FROM" mailbox
const Echo = struct {
const Self = @This();to: Mbx = undefined,
from: Mbx = undefined,
thread: Thread = undefined,// Mailboxes creation and start of the thread
// Pay attention, that client code does not use
// any thread "API" - all embedded within Echo
pub fn start(echo: *Self) void {
echo.to = .{};
echo.from = .{};
echo.thread = std.Thread.spawn(.{}, run, .{echo}) catch unreachable;
}// Echo thread function
fn run(echo: *Self) void {
// Main loop:
while (true) {
// Receive - exit from the thread if mailbox was closed
const envelope = echo.to.receive(100000000) catch break;
// Reply to the client
// Exit from the thread if mailbox was closed
_ = echo.from.send(envelope) catch break;
}
}// Wait exit from the thread
pub fn waitFinish(echo: *Self) void {
echo.thread.join();
}// Close mailboxes
// As result Echo should stop processing
// and exit from the thread.
pub fn stop(echo: *Self) !void {
_ = echo.to.close();
_ = echo.from.close();
}
};var echo = try std.testing.allocator.create(Echo);
// Start Echo(on own thread)
echo.start();
defer echo.stop();defer {
// Wait finish of Echo
echo.waitFinish();
std.testing.allocator.destroy(echo);
}// because nothing was send to 'TO' mailbox, nothing should be received
// from 'FROM' mailbox
try testing.expectError(error.Timeout, echo.from.receive(100));// Create wrapper for the data
const envl = try std.testing.allocator.create(Mbx.Envelope);
defer std.testing.allocator.destroy(envl);// Send/Receive loop
for (0..6) |indx| {
// Set value for send [0-5]
envl.letter = indx;// Send to 'TO' mailbox
try echo.to.send(envl);// Wait received data from OUT mailbox
const back = echo.from.receive(1000000);if (back) |val| {
// Expected value == index [0-5]
try testing.expect(val.letter == indx);
} else |_| {
try testing.expect(false);
}
}
```## Boring details
Mailbox of *[]const u8* 'Letters':
```zig
const Rumors = mailbox.MailBox([]const u8);
const rmrsMbx : Rumors = .{};
```**Envelope** is a wrapper of actual user defined type **Letter**.
```zig
pub const Envelope = struct {
prev: ?*Envelope = null,
next: ?*Envelope = null,
letter: Letter,
};
```
In fact Mailbox is a queue(FIFO) of Envelope(s).MailBox supports following operations:
- send *Envelope* to MailBox (*enqueue*) and wakeup waiting receiver(s)
- receive *Envelope* from Mailbox (*dequeue*) with time-out
- close Mailbox:
- disables further operations
- first close returns List of non-processed *Envelope(s)* for free/reuse etc.Feel free to suggest improvements in doc and code.
## License
[MIT](LICENSE)## Installation
You finally got to installation!### Submodules
Create folder *'deps'* under *'src'* and mailbox submodule:
```bash
mkdif src/deps
git submodule add https://github.com/g41797/mailbox src/deps/mailbox
```
Import mailbox:
```zig
const mailbox = @import("deps/mailbox/src/mailbox.zig");
```
Use mailbox:
```zig
const MsgBlock = struct {
len: usize = undefined,
buff: [1024]u8 = undefined,
};const Msgs = mailbox.MailBox(MsgBlock);
var msgs: Msgs = .{};
...................
_ = msgs.close();
```Periodically update submodule(s):
```bash
git submodule update --remote
```### Package Manager
With an existing Zig project, adding Mailbox to it is easy:
1. Add mailbox to your `build.zig.zon`
2. Add mailbox to your `build.zig`To add mailbox to `build.zig.zon` simply run the following in your terminal:
```sh
cd my-example-project
zig fetch --save=mailbox git+https://github.com/g41797/mailbox
```and in your `build.zig.zon` you should find a new dependency like:
```zig
.{
.name = "My example project",
.version = "0.0.1",.dependencies = .{
.mailbox = .{
.url = "git+https://github.com/g41797/mailbox#3f794f34f5d859e7090c608da998f3b8856f8329",
.hash = "122068e7811ec1bfc2a81c9250078dd5dafa9dca4eb3f1910191ba060585526f03fe",
},
},
.paths = .{
"",
},
}
```Then, in your `build.zig`'s `build` function, add the following before
`b.installArtifact(exe)`:```zig
const mailbox = b.dependency("mailbox", .{
.target = target,
.optimize = optimize,
});exe.root_module.addImport("mailbox", mailbox.module("mailbox"));
```From then on, you can use the Mailbox package in your project.
## Last warning
First rule of multithreading:
>**If you can do without multithreading - do without.**
*Powered by* [![clion](_logo/CLion_icon.png)][refclion]
[refclion]: https://www.jetbrains.com/clion/