//! A scratch directory owned by one harness run (dst, smoke). A fixed name
//! let two concurrent runs from one checkout delete or collide with each
//! other's workspace; a name carrying the pid and a random suffix, created
//! exclusively, cannot. A directory that held the old fixed name is left
//! alone: it may belong to an older run still in flight.
const std = @import("std");
const builtin = @import("builtin");
/// Room for a parent path plus `-<pid>-<16 hex>`.
pub const max_path = 4096;
/// Create `<prefix>-<pid>-<16 hex>` under `dir` and return its path in
/// `buf`. `prefix` may carry parent components; the parent must exist.
pub fn create(io: std.Io, dir: std.Io.Dir, buf: *[max_path]u8, prefix: []const u8) ![]const u8 {
const pid = getpid();
var attempt: u32 = 0;
while (true) : (attempt += 1) {
var suffix: [8]u8 = undefined;
io.random(&suffix);
const path = try std.fmt.bufPrint(buf, "{s}-{d}-{x}", .{ prefix, pid, suffix });
dir.createDir(io, path, .default_dir) catch |e| switch (e) {
// Another run won this name; draw again. Bounded so a broken
// random source fails loudly instead of spinning.
error.PathAlreadyExists => if (attempt < 16) continue else return e,
else => return e,
};
return path;
}
}
fn getpid() i64 {
return if (builtin.os.tag == .linux) std.os.linux.getpid() else std.c.getpid();
}
test "create makes distinct directories under one prefix" {
const io = std.testing.io;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
var buf_a: [max_path]u8 = undefined;
var buf_b: [max_path]u8 = undefined;
const a = try create(io, tmp.dir, &buf_a, "ws");
const b = try create(io, tmp.dir, &buf_b, "ws");
try std.testing.expect(!std.mem.eql(u8, a, b));
try std.testing.expect(std.mem.startsWith(u8, a, "ws-"));
var da = try tmp.dir.openDir(io, a, .{});
da.close(io);
var db = try tmp.dir.openDir(io, b, .{});
db.close(io);
}