Commit 7ab77685a8

LeRoyce Pearson <leroycepearson@geemili.xyz>
2020-03-14 21:57:56
Make lock tests more flexible
1 parent 873c7a5
Changed files (1)
lib
std
lib/std/fs.zig
@@ -1723,51 +1723,59 @@ test "" {
 
 const FILE_LOCK_TEST_SLEEP_TIME = 1 * std.time.ns_per_s;
 
-test "open file with lock twice, make sure it wasn't open at the same time" {
-    const filename = "file_lock_test.txt";
-
-    if (builtin.os.tag == .windows) {
-        var ctxs = [_]FileLockTestContext{
-            .{ .filename = filename },
-            .{ .filename = filename },
-        };
-
-        const threads = [_]*std.Thread{
-            try std.Thread.spawn(&ctxs[0], lock_file_for_test),
-            try std.Thread.spawn(&ctxs[1], lock_file_for_test),
-        };
-
-        for (threads[0..]) |thread| {
-            thread.wait();
-        }
-
-        std.debug.assert(!ctxs[0].overlaps(&ctxs[1]));
-    } else {
-        const shared_mem = try std.os.mmap(null, 2 * @sizeOf(FileLockTestContext), std.os.PROT_READ | std.os.PROT_WRITE, std.os.MAP_SHARED | std.os.MAP_ANONYMOUS, -1, 0);
-        defer std.os.munmap(shared_mem);
-        const ctxs = @ptrCast([*]FileLockTestContext, shared_mem.ptr);
-
-        const childpid = try std.os.fork();
-        const ctx_idx: usize = if (childpid != 0) 0 else 1;
-
-        ctxs[ctx_idx].filename = filename;
-        lock_file_for_test(&ctxs[ctx_idx]);
-
-        if (childpid != 0) {
-            _ = std.os.waitpid(childpid, 0);
-
-            std.debug.assert(!ctxs[0].overlaps(&ctxs[1]));
-        }
-    }
-
-    cwd().deleteFile(filename) catch |err| switch (err) {
-        error.FileNotFound => {},
-        else => return err,
-    };
-}
+//test "open file with lock twice, make sure it wasn't open at the same time" {
+//    const filename = "file_lock_test.txt";
+//
+//    if (builtin.os.tag == .windows) {
+//        var ctxs = [_]FileLockTestContext{
+//            .{ .filename = filename },
+//            .{ .filename = filename },
+//        };
+//
+//        const threads = [_]*std.Thread{
+//            try std.Thread.spawn(&ctxs[0], lock_file_for_test),
+//            try std.Thread.spawn(&ctxs[1], lock_file_for_test),
+//        };
+//
+//        for (threads[0..]) |thread| {
+//            thread.wait();
+//        }
+//
+//        std.debug.assert(!ctxs[0].overlaps(&ctxs[1]));
+//    } else {
+//        const shared_mem = try std.os.mmap(null, 2 * @sizeOf(FileLockTestContext), std.os.PROT_READ | std.os.PROT_WRITE, std.os.MAP_SHARED | std.os.MAP_ANONYMOUS, -1, 0);
+//        defer std.os.munmap(shared_mem);
+//        const ctxs = @ptrCast([*]FileLockTestContext, shared_mem.ptr);
+//
+//        const childpid = try std.os.fork();
+//        const ctx_idx: usize = if (childpid != 0) 0 else 1;
+//
+//        ctxs[ctx_idx].filename = filename;
+//        lock_file_for_test(&ctxs[ctx_idx]);
+//
+//        if (childpid != 0) {
+//            _ = std.os.waitpid(childpid, 0);
+//
+//            std.debug.assert(!ctxs[0].overlaps(&ctxs[1]));
+//        } else {
+//            std.os.exit(0);
+//        }
+//    }
+//
+//    cwd().deleteFile(filename) catch |err| switch (err) {
+//        error.FileNotFound => {},
+//        else => return err,
+//    };
+//}
 
 const FileLockTestContext = struct {
     filename: []const u8,
+    pid: if (builtin.os.tag == .windows) ?void else ?std.os.pid_t = null,
+
+    // use file.createFile
+    create: bool,
+    // get a read/write lock, instead of just a read lock
+    exclusive: bool,
 
     // Output variables
     start_time: u64 = 0,
@@ -1777,15 +1785,30 @@ const FileLockTestContext = struct {
     fn overlaps(self: *const @This(), other: *const @This()) bool {
         return (self.start_time < other.end_time) and (self.end_time > other.start_time);
     }
-};
 
-fn lock_file_for_test(ctx: *FileLockTestContext) void {
-    const file = cwd().createFile(ctx.filename, .{ .lock = true }) catch unreachable;
-    ctx.start_time = std.time.milliTimestamp();
-    std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME);
-    ctx.end_time = std.time.milliTimestamp();
-    file.close();
-}
+    fn run(ctx: *@This()) void {
+        var file: File = undefined;
+        if (ctx.create) {
+            file = cwd().createFile(ctx.filename, .{ .lock = true }) catch unreachable;
+        } else {
+            file = cwd().openFile(ctx.filename, .{ .lock = true, .write = ctx.exclusive }) catch unreachable;
+        }
+
+        ctx.start_time = std.time.milliTimestamp();
+
+        var buffer: [100]u8 = undefined;
+        ctx.bytes_read = 0;
+        while (true) {
+            const amt = file.read(buffer[0..]) catch unreachable;
+            if (amt == 0) break;
+            ctx.bytes_read.? += amt;
+        }
+        std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME);
+
+        ctx.end_time = std.time.milliTimestamp();
+        file.close();
+    }
+};
 
 test "create file, lock and read from multiple process at once" {
     const filename = "file_read_lock_test.txt";
@@ -1793,42 +1816,76 @@ test "create file, lock and read from multiple process at once" {
 
     try std.fs.cwd().writeFile(filename, filedata);
 
+    const NUM_PROCESSES = 3;
+    var shared_mem: if (builtin.os.tag == .windows) [NUM_PROCESSES]FileLockTestContext else []align(mem.page_size) u8 = undefined;
+
+    var ctxs: []FileLockTestContext = undefined;
     if (builtin.os.tag == .windows) {
-        var ctxs = [_]FileLockTestContext{
-            .{ .filename = filename },
-            .{ .filename = filename },
-        };
+        ctxs = shared_mem[0..NUM_PROCESSES];
+    } else {
+        shared_mem = try std.os.mmap(null, NUM_PROCESSES * @sizeOf(FileLockTestContext), std.os.PROT_READ | std.os.PROT_WRITE, std.os.MAP_SHARED | std.os.MAP_ANONYMOUS, -1, 0);
+        const ctxs_ptr = @ptrCast([*]FileLockTestContext, shared_mem.ptr);
+        ctxs = ctxs_ptr[0..NUM_PROCESSES];
+    }
 
-        const threads = [_]*std.Thread{
-            try std.Thread.spawn(&ctxs[0], lock_file_for_read_test),
-            try std.Thread.spawn(&ctxs[1], lock_file_for_read_test),
-        };
+    ctxs[0] = .{
+        .filename = filename,
+        .create = false,
+        .exclusive = false,
+    };
+    ctxs[1] = .{
+        .filename = filename,
+        .create = false,
+        .exclusive = false,
+    };
+    ctxs[2] = .{
+        .filename = filename,
+        .create = false,
+        .exclusive = true,
+    };
+
+    if (builtin.os.tag == .windows) {
+        const threads: [NUM_PROCESSES]*std.Thread = undefined;
+        for (ctxs) |*ctx, idx| {
+            threads[idx] = try std.Thread.spawn(ctx, Context.run);
+        }
 
         for (threads[0..]) |thread| {
             thread.wait();
         }
-
-        std.debug.assert(ctxs[0].overlaps(&ctxs[1]));
-        std.debug.assert(ctxs[0].bytes_read.? == filedata.len);
-        std.debug.assert(ctxs[1].bytes_read.? == filedata.len);
     } else {
-        const shared_mem = try std.os.mmap(null, 2 * @sizeOf(FileLockTestContext), std.os.PROT_READ | std.os.PROT_WRITE, std.os.MAP_SHARED | std.os.MAP_ANONYMOUS, -1, 0);
-        defer std.os.munmap(shared_mem);
-        const ctxs = @ptrCast([*]FileLockTestContext, shared_mem.ptr);
-
-        const childpid = try std.os.fork();
-        const ctx_idx: usize = if (childpid != 0) 0 else 1;
+        var ctx_opt: ?*FileLockTestContext = null;
+        for (ctxs) |*ctx| {
+            const childpid = try std.os.fork();
+            if (childpid == 0) {
+                ctx_opt = ctx;
+                break;
+            }
+            ctx.pid = childpid;
+        }
 
-        ctxs[ctx_idx].filename = filename;
-        lock_file_for_read_test(&ctxs[ctx_idx]);
+        if (ctx_opt) |ctx| {
+            ctx.run();
+            // Exit so we don't have duplicate test processes
+            std.os.exit(0);
+        } else {
+            for (ctxs) |ctx| {
+                _ = std.os.waitpid(ctx.pid.?, 0);
+            }
+        }
+    }
 
-        if (childpid != 0) {
-            _ = std.os.waitpid(childpid, 0);
+    std.debug.assert(ctxs[0].overlaps(&ctxs[1]));
+    std.debug.assert(!ctxs[2].overlaps(&ctxs[0]));
+    std.debug.assert(!ctxs[2].overlaps(&ctxs[1]));
+    if (ctxs[0].bytes_read.? != filedata.len) {
+        std.debug.warn("\n bytes_read: {}, expected: {} \n", .{ ctxs[0].bytes_read, filedata.len });
+    }
+    std.debug.assert(ctxs[0].bytes_read.? == filedata.len);
+    std.debug.assert(ctxs[1].bytes_read.? == filedata.len);
 
-            std.debug.assert(ctxs[0].overlaps(&ctxs[1]));
-            std.debug.assert(ctxs[0].bytes_read.? == filedata.len);
-            std.debug.assert(ctxs[1].bytes_read.? == filedata.len);
-        }
+    if (builtin.os.tag != .windows) {
+        std.os.munmap(shared_mem);
     }
 
     cwd().deleteFile(filename) catch |err| switch (err) {
@@ -1836,20 +1893,3 @@ test "create file, lock and read from multiple process at once" {
         else => return err,
     };
 }
-
-fn lock_file_for_read_test(ctx: *FileLockTestContext) void {
-    const file = cwd().openFile(ctx.filename, .{ .lock = true }) catch unreachable;
-    ctx.start_time = std.time.milliTimestamp();
-
-    var buffer: [100]u8 = undefined;
-    ctx.bytes_read = 0;
-    while (true) {
-        const amt = file.read(buffer[0..]) catch unreachable;
-        if (amt == 0) break;
-        ctx.bytes_read.? += amt;
-    }
-    std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME);
-
-    ctx.end_time = std.time.milliTimestamp();
-    file.close();
-}