1
0
Fork 0
mirror of https://github.com/zigzap/zap.git synced 2025-10-20 15:14:08 +00:00

saner mem management, thread-safety

This commit is contained in:
Rene Schallner 2023-01-19 20:34:47 +01:00
parent 50c64e0f90
commit 1f8e8ac0e4
5 changed files with 107 additions and 38 deletions

View file

@ -11,6 +11,9 @@ var alloc: std.mem.Allocator = undefined;
var endpoint: zap.SimpleEndpoint = undefined; var endpoint: zap.SimpleEndpoint = undefined;
var users: Users = undefined; var users: Users = undefined;
// 100MB of json buffer
var jsonbuf: [100 * 1024 * 1024]u8 = undefined;
pub fn init( pub fn init(
a: std.mem.Allocator, a: std.mem.Allocator,
user_path: []const u8, user_path: []const u8,
@ -53,7 +56,7 @@ fn getUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
} }
if (userIdFromPath(path)) |id| { if (userIdFromPath(path)) |id| {
if (users.get(id)) |user| { if (users.get(id)) |user| {
if (zap.stringify(user, .{})) |json| { if (zap.stringifyBuf(&jsonbuf, user, .{})) |json| {
_ = r.sendJson(json); _ = r.sendJson(json);
} }
} }
@ -67,7 +70,7 @@ fn listUsers(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
if (users.list(&l)) {} else |_| { if (users.list(&l)) {} else |_| {
return; return;
} }
if (zap.stringifyArrayList(User, &l, .{})) |maybe_json| { if (zap.stringifyArrayListBuf(&jsonbuf, User, &l, .{})) |maybe_json| {
if (maybe_json) |json| { if (maybe_json) |json| {
_ = r.sendJson(json); _ = r.sendJson(json);
} }
@ -88,7 +91,7 @@ fn postUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
if (maybe_user) |u| { if (maybe_user) |u| {
defer std.json.parseFree(User, u, .{ .allocator = alloc }); defer std.json.parseFree(User, u, .{ .allocator = alloc });
if (users.addByName(u.first_name, u.last_name)) |id| { if (users.addByName(u.first_name, u.last_name)) |id| {
if (zap.stringify(.{ .status = "OK", .id = id }, .{})) |json| { if (zap.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| {
_ = r.sendJson(json); _ = r.sendJson(json);
} }
} else |_| { } else |_| {
@ -117,14 +120,14 @@ fn putUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
.{ .allocator = alloc }, .{ .allocator = alloc },
); );
if (users.update(id, u.first_name, u.last_name)) { if (users.update(id, u.first_name, u.last_name)) {
if (zap.stringify(.{ if (zap.stringifyBuf(&jsonbuf, .{
.status = "OK", .status = "OK",
.id = id, .id = id,
}, .{})) |json| { }, .{})) |json| {
_ = r.sendJson(json); _ = r.sendJson(json);
} }
} else { } else {
if (zap.stringify(.{ if (zap.stringifyBuf(&jsonbuf, .{
.status = "ERROR", .status = "ERROR",
.id = id, .id = id,
}, .{})) |json| { }, .{})) |json| {
@ -143,11 +146,11 @@ fn deleteUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
if (r.path) |path| { if (r.path) |path| {
if (userIdFromPath(path)) |id| { if (userIdFromPath(path)) |id| {
if (users.delete(id)) { if (users.delete(id)) {
if (zap.stringify(.{ .status = "OK", .id = id }, .{})) |json| { if (zap.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| {
_ = r.sendJson(json); _ = r.sendJson(json);
} }
} else { } else {
if (zap.stringify(.{ if (zap.stringifyBuf(&jsonbuf, .{
.status = "ERROR", .status = "ERROR",
.id = id, .id = id,
}, .{})) |json| { }, .{})) |json| {

View file

@ -3,7 +3,10 @@ const zap = @import("zap");
const Endpoint = @import("endpoint.zig"); const Endpoint = @import("endpoint.zig");
pub fn main() !void { pub fn main() !void {
const allocator = std.heap.page_allocator; var gpa = std.heap.GeneralPurposeAllocator(.{
.thread_safe = true,
}){};
var allocator = gpa.allocator();
// setup listener // setup listener
var listener = zap.SimpleEndpointListener.init( var listener = zap.SimpleEndpointListener.init(
allocator, allocator,

View file

@ -32,10 +32,7 @@ pub fn init(a: std.mem.Allocator) Self {
// the request will be freed (and its mem reused by facilio) when it's // the request will be freed (and its mem reused by facilio) when it's
// completed, so we take copies of the names // completed, so we take copies of the names
pub fn addByName(self: *Self, first: ?[]const u8, last: ?[]const u8) !usize { pub fn addByName(self: *Self, first: ?[]const u8, last: ?[]const u8) !usize {
// TODO: get rid of the temp allocation here var user: InternalUser = undefined;
var temp = try self.alloc.alloc(InternalUser, 1);
defer self.alloc.free(temp);
var user = temp[0];
user.firstnamelen = 0; user.firstnamelen = 0;
user.lastnamelen = 0; user.lastnamelen = 0;
if (first) |firstname| { if (first) |firstname| {
@ -66,7 +63,7 @@ pub fn delete(self: *Self, id: usize) bool {
pub fn get(self: *Self, id: usize) ?User { pub fn get(self: *Self, id: usize) ?User {
// we don't care about locking here, as our usage-pattern is unlikely to // we don't care about locking here, as our usage-pattern is unlikely to
// get a user by id that is not known yet // get a user by id that is not known yet
if (self.users.get(id)) |pUser| { if (self.users.getPtr(id)) |pUser| {
return .{ return .{
.id = pUser.id, .id = pUser.id,
.first_name = pUser.firstnamebuf[0..pUser.firstnamelen], .first_name = pUser.firstnamebuf[0..pUser.firstnamelen],
@ -83,11 +80,9 @@ pub fn update(
last: ?[]const u8, last: ?[]const u8,
) bool { ) bool {
// we don't care about locking here // we don't care about locking here
var user: ?InternalUser = self.users.get(id); if (self.users.getPtr(id)) |pUser| {
// we got a copy apparently, so we need to put again pUser.firstnamelen = 0;
if (user) |*pUser| { pUser.lastnamelen = 0;
pUser.*.firstnamelen = 0;
pUser.*.lastnamelen = 0;
if (first) |firstname| { if (first) |firstname| {
std.mem.copy(u8, pUser.firstnamebuf[0..], firstname); std.mem.copy(u8, pUser.firstnamebuf[0..], firstname);
pUser.firstnamelen = firstname.len; pUser.firstnamelen = firstname.len;
@ -96,12 +91,6 @@ pub fn update(
std.mem.copy(u8, pUser.lastnamebuf[0..], lastname); std.mem.copy(u8, pUser.lastnamebuf[0..], lastname);
pUser.lastnamelen = lastname.len; pUser.lastnamelen = lastname.len;
} }
_ = self.users.remove(id);
if (self.users.put(id, pUser.*)) {
return true;
} else |_| {
return false;
}
} }
return false; return false;
} }

View file

@ -18,8 +18,9 @@ fn on_request(r: zap.SimpleRequest) void {
const user_id: usize = @intCast(usize, the_path[6] - 0x30); const user_id: usize = @intCast(usize, the_path[6] - 0x30);
const user = users.get(user_id); const user = users.get(user_id);
var buf: [100]u8 = undefined;
var json_to_send: []const u8 = undefined; var json_to_send: []const u8 = undefined;
if (stringify(user, .{})) |json| { if (zap.stringifyBuf(&buf, user, .{})) |json| {
json_to_send = json; json_to_send = json;
} else { } else {
json_to_send = "null"; json_to_send = "null";
@ -39,17 +40,6 @@ fn setupUserData(a: std.mem.Allocator) !void {
try users.put(2, .{ .first_name = "Your", .last_name = "Mom" }); try users.put(2, .{ .first_name = "Your", .last_name = "Mom" });
} }
var buf: [100]u8 = undefined;
fn stringify(value: anytype, options: std.json.StringifyOptions) ?[]const u8 {
var fba = std.heap.FixedBufferAllocator.init(&buf);
var string = std.ArrayList(u8).init(fba.allocator());
if (std.json.stringify(value, options, string.writer())) {
return string.items;
} else |_| { // error
return null;
}
}
pub fn main() !void { pub fn main() !void {
var a = std.heap.page_allocator; var a = std.heap.page_allocator;
try setupUserData(a); try setupUserData(a);

View file

@ -6,8 +6,15 @@ const std = @import("std");
// 1MB JSON buffer // 1MB JSON buffer
var jsonbuf: [1024 * 1024]u8 = undefined; var jsonbuf: [1024 * 1024]u8 = undefined;
var mutex: std.Thread.Mutex = .{};
pub fn stringify(value: anytype, options: std.json.StringifyOptions) ?[]const u8 { /// use default 1MB buffer, mutex-protected
pub fn stringify(
value: anytype,
options: std.json.StringifyOptions,
) ?[]const u8 {
mutex.lock();
defer mutex.unlock();
var fba = std.heap.FixedBufferAllocator.init(&jsonbuf); var fba = std.heap.FixedBufferAllocator.init(&jsonbuf);
var string = std.ArrayList(u8).init(fba.allocator()); var string = std.ArrayList(u8).init(fba.allocator());
if (std.json.stringify(value, options, string.writer())) { if (std.json.stringify(value, options, string.writer())) {
@ -17,7 +24,14 @@ pub fn stringify(value: anytype, options: std.json.StringifyOptions) ?[]const u8
} }
} }
pub fn stringifyArrayList(comptime T: anytype, list: *std.ArrayList(T), options: std.json.StringifyOptions) !?[]const u8 { /// use default 1MB buffer, mutex-protected
pub fn stringifyArrayList(
comptime T: anytype,
list: *std.ArrayList(T),
options: std.json.StringifyOptions,
) !?[]const u8 {
mutex.lock();
defer mutex.unlock();
var fba = std.heap.FixedBufferAllocator.init(&jsonbuf); var fba = std.heap.FixedBufferAllocator.init(&jsonbuf);
var string = std.ArrayList(u8).init(fba.allocator()); var string = std.ArrayList(u8).init(fba.allocator());
var writer = string.writer(); var writer = string.writer();
@ -31,3 +45,73 @@ pub fn stringifyArrayList(comptime T: anytype, list: *std.ArrayList(T), options:
try writer.writeByte(']'); try writer.writeByte(']');
return string.items; return string.items;
} }
/// provide your own buf, NOT mutex-protected!
pub fn stringifyBuf(
buffer: []u8,
value: anytype,
options: std.json.StringifyOptions,
) ?[]const u8 {
var fba = std.heap.FixedBufferAllocator.init(buffer);
var string = std.ArrayList(u8).init(fba.allocator());
if (std.json.stringify(value, options, string.writer())) {
return string.items;
} else |_| { // error
return null;
}
}
/// provide your own buf, NOT mutex-protected
pub fn stringifyArrayListBuf(
buffer: []u8,
comptime T: anytype,
list: *std.ArrayList(T),
options: std.json.StringifyOptions,
) !?[]const u8 {
var fba = std.heap.FixedBufferAllocator.init(buffer);
var string = std.ArrayList(u8).init(fba.allocator());
var writer = string.writer();
try writer.writeByte('[');
var first: bool = true;
for (list.items) |user| {
if (!first) try writer.writeByte(',');
first = false;
try std.json.stringify(user, options, string.writer());
}
try writer.writeByte(']');
return string.items;
}
/// provide your own allocator, NOT mutex-protected
pub fn stringifyAlloc(
a: std.mem.Allocator,
value: anytype,
options: std.json.StringifyOptions,
) ?[]const u8 {
var string = std.ArrayList(u8).init(a);
if (std.json.stringify(value, options, string.writer())) {
return string.items;
} else |_| { // error
return null;
}
}
/// provide your own allocator, NOT mutex-protected
pub fn stringifyArrayListAlloc(
a: std.mem.Allocator,
comptime T: anytype,
list: *std.ArrayList(T),
options: std.json.StringifyOptions,
) !?[]const u8 {
var string = std.ArrayList(u8).init(a);
var writer = string.writer();
try writer.writeByte('[');
var first: bool = true;
for (list.items) |user| {
if (!first) try writer.writeByte(',');
first = false;
try std.json.stringify(user, options, string.writer());
}
try writer.writeByte(']');
return string.items;
}