#producer consumer patterns
1 messages · Page 1 of 1 (latest)
Working version
const std = @import("std");
const ProdCons = struct {
const Self = @This();
const QT = std.PriorityQueue(u32, void, order);
x: u32 = 0,
s: std.Thread.Semaphore = .{ .permits = 0 },
q: QT = QT.init(std.heap.page_allocator, {}),
c: std.Thread = undefined,
pub fn init() !Self {
return Self{};
}
fn order(_: void, a: u32, b: u32) std.math.Order {
return std.math.order(a, b);
}
pub fn producer(self: *Self, a: u32) void {
std.debug.print("producer posts\n", .{});
self.q.add(a) catch unreachable;
self.s.post();
}
fn consumer(self: *Self) void {
while (true) {
self.s.wait();
const a = self.q.remove();
std.debug.print("consumer consooms\n", .{});
self.x += a;
std.debug.print("x: {}\n", .{self.x});
}
}
};
pub fn main() !void {
var pc = try ProdCons.init();
const c = try std.Thread.spawn(.{}, ProdCons.consumer, .{&pc});
c.detach();
std.debug.print("Hello, World!\n", .{});
for (0..10) |i| {
std.time.sleep(@as(usize, @intFromFloat(0.05 * std.time.ns_per_s)) * i);
pc.producer(@intCast(i));
}
std.time.sleep(0.25 * std.time.ns_per_s);
}
Buggy version
const std = @import("std");
const ProdCons = struct {
const Self = @This();
const QT = std.PriorityQueue(u32, void, order);
x: u32 = 0,
s: std.Thread.Semaphore = .{ .permits = 0 },
q: QT = QT.init(std.heap.page_allocator, {}),
c: std.Thread = undefined,
pub fn init() !Self {
var pc = Self{};
pc.c = try std.Thread.spawn(.{}, ProdCons.consumer, .{&pc});
return pc;
}
fn order(_: void, a: u32, b: u32) std.math.Order {
return std.math.order(a, b);
}
pub fn producer(self: *Self, a: u32) void {
std.debug.print("producer posts\n", .{});
self.q.add(a) catch unreachable;
self.s.post();
}
fn consumer(self: *Self) void {
while (true) {
self.s.wait();
const a = self.q.remove();
std.debug.print("consumer consooms\n", .{});
self.x += a;
std.debug.print("x: {}\n", .{self.x});
}
}
};
pub fn main() !void {
var pc = try ProdCons.init();
std.debug.print("Hello, World!\n", .{});
for (0..10) |i| {
std.time.sleep(@as(usize, @intFromFloat(0.05 * std.time.ns_per_s)) * i);
pc.producer(@intCast(i));
}
std.time.sleep(0.25 * std.time.ns_per_s);
}
itll only work sometimes as PriorityQueue isnt thread safe. You need to access it around something like a Mutex.
in the second version, a pointer to the pc local variable is passed in, but then that local variable is destroyed when the function returns. It needs to stay alive & pinned while the thread is running
Is the semaphor I have not enough to make it thread safe?
not for multiple items. Heres an invalid interleaving:
producer:
queue.push()
sema.post()
consumer:
sema.wait() woken up
preempted during queue.pop(), queue temporarily in invalid state
producer:
queue.push() on invalid queue
Ah I see. Yeah it makes sense now. Though for the second point, how do you keep it alive and pinned? Is it just that I need to put it on the heap?
putting it on the heap is one way. Keeping the variable alive while the thread is spawned & until its joined / stops using it (like in version1) is another