Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
zig-cache/
zig-out/
.zig-cache
zig-out/
61 changes: 41 additions & 20 deletions build.zig
Original file line number Diff line number Diff line change
@@ -1,36 +1,57 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});
const local_module = b.addModule("antiphony", .{
.root_source_file = .{ .path = "src/antiphony.zig" },
});
const s2s_dep = b.dependency("s2s", .{});
const s2s_mod = s2s_dep.module("s2s");
local_module.addImport("s2s",s2s_mod );

const linux_example = b.addExecutable(.{
.name = "socketpair-example",
.root_source_file = .{
.path = "examples/linux.zig",
},
.optimize = mode,
// Main antiphony module
const antiphony_mod = b.addModule("antiphony", .{
.root_source_file = b.path("src/antiphony.zig"),
.target = target,
.optimize = mode,
});
linux_example.root_module.addImport("antiphony", local_module);

// Add s2s dependency
const s2s_mod = b.dependency("s2s", .{}).module("s2s");
antiphony_mod.addImport("s2s", s2s_mod);

// Linux example
const linux_example = b.addExecutable(.{
.name = "linux-socketpair-example",
.root_module = b.createModule(.{
.root_source_file = b.path("examples/linux.zig"),
.optimize = mode,
.target = target,
.imports = &.{.{ .name = "antiphony", .module = antiphony_mod }},
}),
});
b.installArtifact(linux_example);

// macOS example
const macos_example = b.addExecutable(.{
.name = "macos-socketpair-example",
.root_module = b.createModule(.{
.root_source_file = b.path("examples/macos.zig"),
.optimize = mode,
.target = target,
.imports = &.{.{ .name = "antiphony", .module = antiphony_mod }},
}),
});
b.installArtifact(macos_example);

// Run steps for examples
const run_linux = b.addRunArtifact(linux_example);
const run_linux_step = b.step("run-linux", "Run the Linux socketpair example");
run_linux_step.dependOn(&run_linux.step);

const run_macos = b.addRunArtifact(macos_example);
const run_macos_step = b.step("run-macos", "Run the macOS machport example");
run_macos_step.dependOn(&run_macos.step);

// Tests
const main_tests = b.addTest(.{
.name = "antiphony",
.root_source_file = .{
.path = "src/antiphony.zig"
},
.target = target,
.optimize = mode,
.root_module = antiphony_mod,
});
main_tests.root_module.addImport("s2s", s2s_mod);

Expand Down
7 changes: 4 additions & 3 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
.{
.name = "antiphony",
.name = .antiphony,
.version = "0.0.0",
.dependencies = .{
.s2s = .{
.url = "https://github.com/ziglibs/s2s/archive/b30205d5e9204899fb6d0fdf28d00ed4d18fe9c9.tar.gz",
.hash = "12202c39c98f05041f1052c268132669dbfcda87e4dbb0353cd84a6070924c8ac0e3",
.url = "git+https://github.com/burakssen/s2s#c48757afb221c003156e143787326c8e80875ee1",
.hash = "s2s-0.0.1-qt8ajZ98AACx_APAb4QftlxP9Ap3O6-lNejjB3YttSc1",
},
},
.paths = .{
Expand All @@ -14,4 +14,5 @@
//"LICENSE",
//"README.md",
},
.fingerprint = 0xc9c0a5e5ff3f18f0,
}
168 changes: 168 additions & 0 deletions examples/macos.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
const std = @import("std");
const rpc = @import("antiphony");

const CreateError = error{ OutOfMemory, UnknownCounter };
const UsageError = error{ OutOfMemory, UnknownCounter };

// Define our RPC service via function signatures.
const RcpDefinition = rpc.CreateDefinition(.{
.host = .{
.createCounter = fn () CreateError!u32,
.destroyCounter = fn (u32) void,
.increment = fn (u32, u32) UsageError!u32,
.getCount = fn (u32) UsageError!u32,
},
.client = .{
.signalError = fn (msg: []const u8) void,
},
});

fn clientImplementation(file: std.fs.File) !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();

const ClientImpl = struct {
pub fn signalError(msg: []const u8) void {
std.log.err("remote error: {s}", .{msg});
}
};

const EndPoint = RcpDefinition.ClientEndPoint(ClientImpl);

var reader_buffer: [1024]u8 = undefined;
var file_reader = file.reader(&reader_buffer);

var writer_buffer: [1024]u8 = undefined;
var file_writer = file.writer(&writer_buffer);

var end_point = EndPoint.init(
gpa.allocator(),
&file_reader.interface,
&file_writer.interface,
);
defer end_point.destroy();

var impl = ClientImpl{};
std.debug.print("Client connecting to host\n", .{});
try end_point.connect(&impl); // establish RPC handshake
std.debug.print("Client connected to host\n", .{});
const handle = try end_point.invoke("createCounter", .{});

std.log.info("first increment: {}", .{try end_point.invoke("increment", .{ handle, 5 })});
std.log.info("second increment: {}", .{try end_point.invoke("increment", .{ handle, 3 })});
std.log.info("third increment: {}", .{try end_point.invoke("increment", .{ handle, 7 })});
std.log.info("final count: {}", .{try end_point.invoke("getCount", .{handle})});

try end_point.invoke("destroyCounter", .{handle});

_ = end_point.invoke("getCount", .{handle}) catch |err| std.log.info("error while calling getCount() with invalid handle: {s}", .{@errorName(err)});

try end_point.shutdown();
}

fn hostImplementation(file: std.fs.File) !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();

const HostImpl = struct {
const Self = @This();

const EndPoint = RcpDefinition.HostEndPoint(Self);

allocator: std.mem.Allocator,
counters: std.ArrayList(?u32),
end_point: *EndPoint,

fn sendErrorMessage(self: Self, msg: []const u8) void {
self.end_point.invoke("signalError", .{msg}) catch |err| std.log.err("failed to send error message: {s}", .{@errorName(err)});
}

pub fn createCounter(self: *Self) CreateError!u32 {
const index: u32 = @truncate(self.counters.items.len);
try self.counters.append(self.allocator, 0);
return index;
}
pub fn destroyCounter(self: *Self, handle: u32) void {
if (handle >= self.counters.items.len) {
self.sendErrorMessage("unknown counter");
return;
}
self.counters.items[handle] = null;
}
pub fn increment(self: *Self, handle: u32, count: u32) UsageError!u32 {
if (handle >= self.counters.items.len) {
self.sendErrorMessage("This counter does not exist!");
return error.UnknownCounter;
}
if (self.counters.items[handle]) |*counter| {
const previous = counter.*;
counter.* +%= count;
return previous;
} else {
self.sendErrorMessage("This counter was already deleted!");
return error.UnknownCounter;
}
}
pub fn getCount(self: *Self, handle: u32) UsageError!u32 {
if (handle >= self.counters.items.len) {
self.sendErrorMessage("This counter does not exist!");
return error.UnknownCounter;
}
if (self.counters.items[handle]) |value| {
return value;
} else {
self.sendErrorMessage("This counter was already deleted!");
return error.UnknownCounter;
}
}
};

var reader_buffer: [1024]u8 = undefined;
var file_reader = file.reader(&reader_buffer);

var writer_buffer: [1024]u8 = undefined;
var file_writer = file.writer(&writer_buffer);

var end_point = HostImpl.EndPoint.init(
gpa.allocator(), // we need some basic session management
&file_reader.interface, // data input
&file_writer.interface, // data output
);
defer end_point.destroy();

var impl = HostImpl{
.allocator = gpa.allocator(),
.counters = .empty,
.end_point = &end_point,
};
defer impl.counters.deinit(gpa.allocator());

try end_point.connect(&impl); // establish RPC handshake

try end_point.acceptCalls(); // blocks until client exits.
}

// This main function just creates a socket pair and hands them off to two threads that perform some RPC calls.
pub fn main() !void {
const c = std.c;
var sockets: [2]c.fd_t = undefined;

// Use C socketpair which works on macOS
if (c.socketpair(c.AF.UNIX, c.SOCK.STREAM, 0, &sockets) != 0) {
return error.SocketPairError;
}

var socket_a = std.fs.File{ .handle = sockets[0] };
defer socket_a.close();

var socket_b = std.fs.File{ .handle = sockets[1] };
defer socket_b.close();

{
var client_thread = try std.Thread.spawn(.{}, clientImplementation, .{socket_a});
defer client_thread.join();

var host_thread = try std.Thread.spawn(.{}, hostImplementation, .{socket_b});
defer host_thread.join();
}
}
Loading