Expirementation with a new way to finish threads. Currently not working.
This commit is contained in:
+84
-85
@@ -14,8 +14,9 @@ const file = @import("inode_data/file.zig");
|
||||
const misc = @import("inode_data/misc.zig");
|
||||
const DataReader = @import("util/data.zig");
|
||||
const ThreadedDataReader = @import("util/data_threaded.zig");
|
||||
const InodeFinish = @import("util/inode_finish.zig");
|
||||
const FinishUnion = InodeFinish.FinishUnion;
|
||||
const MetadataReader = @import("util/metadata.zig");
|
||||
const XattrTable = @import("xattr.zig");
|
||||
|
||||
pub const Ref = packed struct {
|
||||
block_offset: u16,
|
||||
@@ -155,7 +156,7 @@ fn entriesFromData(alloc: std.mem.Allocator, archive: Archive, data: anytype) ![
|
||||
return DirEntry.readDir(alloc, &meta.interface, data.size);
|
||||
}
|
||||
|
||||
/// Returns the xattr index for the given inode. If the inode isn't an extended variant or doesn't have any, the returned value is the max u32 value (0xFFFFFFFF)
|
||||
/// Returns the xattr index for the given inode. If the inode isn't an extended variant or doesn't have any, the u32 max is returned (0xFFFFFFFF).
|
||||
pub fn xattrIdx(self: Inode) u32 {
|
||||
return switch (self.data) {
|
||||
.ext_dir => |d| d.xattr_id,
|
||||
@@ -167,7 +168,9 @@ pub fn xattrIdx(self: Inode) u32 {
|
||||
};
|
||||
}
|
||||
|
||||
inline fn setPermissionAndXattr(self: Inode, alloc: std.mem.Allocator, archive: *Archive, fil: std.fs.File, options: ExtractionOptions) !void {
|
||||
/// Applies the Inode's metadata to the given File.
|
||||
/// Mod time is always set, but permissions and xattrs are set based on the given ExtractionOptions.
|
||||
pub fn setMetadata(self: Inode, alloc: std.mem.Allocator, archive: *Archive, fil: std.fs.File, options: ExtractionOptions) !void {
|
||||
const time = @as(i128, self.hdr.mod_time) * 1000000000;
|
||||
try fil.updateTimes(time, time);
|
||||
if (!options.ignore_permissions) {
|
||||
@@ -223,7 +226,7 @@ pub fn extractTo(self: Inode, alloc: std.mem.Allocator, archive: *Archive, path:
|
||||
|
||||
var fil = try std.fs.cwd().openFile(path, .{});
|
||||
defer fil.close();
|
||||
try self.setPermissionAndXattr(alloc, archive, fil, options);
|
||||
try self.setMetadata(alloc, archive, fil, options);
|
||||
},
|
||||
.file, .ext_file => try self.extractRegFile(alloc, archive, path, options),
|
||||
.symlink, .ext_symlink => try self.extractSymlink(path),
|
||||
@@ -231,46 +234,6 @@ pub fn extractTo(self: Inode, alloc: std.mem.Allocator, archive: *Archive, path:
|
||||
}
|
||||
}
|
||||
|
||||
const Parent = struct {
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
inode: Inode,
|
||||
path: []const u8,
|
||||
archive: *Archive,
|
||||
options: ExtractionOptions,
|
||||
|
||||
wg: WaitGroup = .{},
|
||||
mut: Mutex = .{},
|
||||
|
||||
fn create(alloc: std.mem.Allocator, inode: Inode, path: []const u8, archive: *Archive, options: ExtractionOptions, dir_size: usize) !*Parent {
|
||||
const out = try alloc.create(Parent);
|
||||
errdefer alloc.destroy(out);
|
||||
out.* = .{
|
||||
.alloc = alloc,
|
||||
|
||||
.inode = inode,
|
||||
.path = path,
|
||||
.archive = archive,
|
||||
.options = options,
|
||||
};
|
||||
out.wg.startMany(dir_size);
|
||||
return out;
|
||||
}
|
||||
|
||||
fn finish(p: *Parent) !void {
|
||||
p.mut.lock();
|
||||
{
|
||||
defer p.mut.unlock();
|
||||
p.wg.finish();
|
||||
if (!p.wg.isDone()) return;
|
||||
}
|
||||
defer p.alloc.destroy(p);
|
||||
var fil = try std.fs.cwd().openFile(p.path, .{});
|
||||
defer fil.close();
|
||||
try p.inode.setPermissionAndXattr(p.alloc, p.archive, fil, p.options);
|
||||
}
|
||||
};
|
||||
|
||||
/// Extract the inode to the given path. Multi-threaded.
|
||||
/// Functions identically to extractTo on all but regular files and directories.
|
||||
fn extractToThreaded(self: Inode, allocator: std.mem.Allocator, archive: *Archive, path: []const u8, options: ExtractionOptions) !void {
|
||||
@@ -294,13 +257,13 @@ fn extractToThreaded(self: Inode, allocator: std.mem.Allocator, archive: *Archiv
|
||||
var out_err: ?anyerror = null;
|
||||
|
||||
wg.start();
|
||||
self.extractThread(alloc, archive, path, options, &wg, &pool, &out_err, null);
|
||||
self.extractThread(alloc, archive, path, options, .{ .wg = &wg }, &pool, &out_err);
|
||||
pool.waitAndWork(&wg);
|
||||
if (out_err != null) return out_err.?;
|
||||
|
||||
var fil = try std.fs.cwd().openFile(path, .{});
|
||||
defer fil.close();
|
||||
try self.setPermissionAndXattr(alloc, archive, fil, options);
|
||||
try self.setMetadata(alloc, archive, fil, options);
|
||||
},
|
||||
.file, .ext_file => {
|
||||
var pool: Pool = undefined;
|
||||
@@ -314,11 +277,17 @@ fn extractToThreaded(self: Inode, allocator: std.mem.Allocator, archive: *Archiv
|
||||
var thread_alloc: std.heap.ThreadSafeAllocator = .{ .child_allocator = arena_alloc.allocator() };
|
||||
const alloc = thread_alloc.allocator();
|
||||
|
||||
try self.extractRegFileThreaded(alloc, archive, path, options, &pool);
|
||||
var wg: WaitGroup = .{};
|
||||
var out_err: ?anyerror = null;
|
||||
|
||||
self.extractThread(alloc, archive, path, options, .{ .wg = &wg }, &pool, &out_err);
|
||||
pool.waitAndWork(&wg);
|
||||
|
||||
if (out_err != null) return out_err.?;
|
||||
|
||||
var fil = try std.fs.cwd().openFile(path, .{});
|
||||
defer fil.close();
|
||||
try self.setPermissionAndXattr(alloc, archive, fil, options);
|
||||
try self.setMetadata(alloc, archive, fil, options);
|
||||
},
|
||||
.symlink, .ext_symlink => try self.extractSymlink(path),
|
||||
else => try self.extractDevice(allocator, archive, path, options),
|
||||
@@ -331,13 +300,12 @@ fn extractThreadEntry(
|
||||
archive: *Archive,
|
||||
path: []const u8,
|
||||
options: ExtractionOptions,
|
||||
wg: *WaitGroup,
|
||||
finish: FinishUnion,
|
||||
pool: *Pool,
|
||||
out_err: *?anyerror,
|
||||
parent: ?*Parent,
|
||||
) void {
|
||||
var new_path = alloc.alloc(u8, path.len + entry.name.len + 1) catch |err| {
|
||||
wg.finish();
|
||||
finish.finish();
|
||||
out_err.* = err;
|
||||
return;
|
||||
};
|
||||
@@ -346,10 +314,10 @@ fn extractThreadEntry(
|
||||
new_path[path.len] = '/';
|
||||
var inode = readFromEntry(alloc, archive, entry) catch |err| {
|
||||
out_err.* = err;
|
||||
wg.finish();
|
||||
finish.finish();
|
||||
return;
|
||||
};
|
||||
inode.extractThread(alloc, archive, new_path, options, wg, pool, out_err, parent);
|
||||
inode.extractThread(alloc, archive, new_path, options, finish, pool, out_err);
|
||||
}
|
||||
|
||||
/// Extract threadedly the inode to the path.
|
||||
@@ -359,21 +327,13 @@ fn extractThread(
|
||||
archive: *Archive,
|
||||
path: []const u8,
|
||||
options: ExtractionOptions,
|
||||
wg: *WaitGroup,
|
||||
finish: FinishUnion,
|
||||
pool: *Pool,
|
||||
out_err: *?anyerror,
|
||||
parent: ?*Parent,
|
||||
) void {
|
||||
if (options.verbose)
|
||||
options.verbose_writer.?.print("Extracting inode #{} to {s}\n", .{ self.hdr.num, path }) catch {};
|
||||
defer {
|
||||
if (parent != null) parent.?.finish() catch |err| {
|
||||
if (options.verbose)
|
||||
options.verbose_writer.?.print("Error setting folder permission to {s}: {}\n", .{ path, err }) catch {};
|
||||
out_err.* = err;
|
||||
};
|
||||
wg.finish();
|
||||
}
|
||||
defer finish.finish();
|
||||
if (out_err.* != null) return;
|
||||
switch (self.hdr.inode_type) {
|
||||
.dir, .ext_dir => {
|
||||
@@ -390,15 +350,35 @@ fn extractThread(
|
||||
out_err.* = err;
|
||||
return;
|
||||
};
|
||||
const p = Parent.create(alloc, self, path, archive, options, entries.len) catch |err| {
|
||||
const fin = InodeFinish.create(
|
||||
alloc,
|
||||
self,
|
||||
path,
|
||||
archive,
|
||||
options,
|
||||
finish,
|
||||
out_err,
|
||||
null,
|
||||
entries.len,
|
||||
) catch |err| {
|
||||
if (options.verbose)
|
||||
options.verbose_writer.?.print("Error allocating memory\n", .{}) catch {};
|
||||
out_err.* = err;
|
||||
return;
|
||||
};
|
||||
wg.startMany(entries.len);
|
||||
// defer files.deinit(alloc); We don't need to do this due to ArenaAllocator
|
||||
for (entries) |entry| {
|
||||
if (entry.inode_type == .dir) {
|
||||
extractThreadEntry(entry, alloc, archive, path, options, wg, pool, out_err, p);
|
||||
extractThreadEntry(
|
||||
entry,
|
||||
alloc,
|
||||
archive,
|
||||
path,
|
||||
options,
|
||||
.{ .fin = fin },
|
||||
pool,
|
||||
out_err,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
pool.spawn(
|
||||
@@ -409,13 +389,12 @@ fn extractThread(
|
||||
archive,
|
||||
path,
|
||||
options,
|
||||
wg,
|
||||
FinishUnion{ .fin = fin },
|
||||
pool,
|
||||
out_err,
|
||||
p,
|
||||
},
|
||||
) catch |err| {
|
||||
wg.finish();
|
||||
fin.finish();
|
||||
if (options.verbose)
|
||||
options.verbose_writer.?.print("Error starting extraction thread: {}\n", .{err}) catch {};
|
||||
out_err.* = err;
|
||||
@@ -424,10 +403,42 @@ fn extractThread(
|
||||
}
|
||||
},
|
||||
.file, .ext_file => {
|
||||
self.extractRegFileThreaded(alloc, archive, path, options, pool) catch |err| {
|
||||
const fil = std.fs.cwd().createFile(path, .{}) catch |err| {
|
||||
if (options.verbose)
|
||||
options.verbose_writer.?.print("Error extracting file inode #{} to {s}: {}\n", .{ self.hdr.num, path, err }) catch {};
|
||||
options.verbose_writer.?.print("Error creating {s}: {}\n", .{ path, err }) catch {};
|
||||
out_err.* = err;
|
||||
return;
|
||||
};
|
||||
var data = self.threadedDataReader(alloc, archive) catch |err| {
|
||||
if (options.verbose)
|
||||
options.verbose_writer.?.print(
|
||||
"Error creating data reader for inode #{} (extracting to {s}): {}\n",
|
||||
.{ self.hdr.num, path, err },
|
||||
) catch {};
|
||||
out_err.* = err;
|
||||
return;
|
||||
};
|
||||
const fin = InodeFinish.create(
|
||||
alloc,
|
||||
self,
|
||||
path,
|
||||
archive,
|
||||
options,
|
||||
finish,
|
||||
out_err,
|
||||
fil,
|
||||
data.num_blocks,
|
||||
) catch |err| {
|
||||
if (options.verbose)
|
||||
options.verbose_writer.?.print("Error allocating memory\n", .{}) catch {};
|
||||
out_err.* = err;
|
||||
return;
|
||||
};
|
||||
data.extractThreaded(fil, pool, fin) catch |err| {
|
||||
if (options.verbose)
|
||||
options.verbose_writer.?.print("Error spawning threads: {}\n", .{err}) catch {};
|
||||
out_err.* = err;
|
||||
return;
|
||||
};
|
||||
},
|
||||
.symlink, .ext_symlink => {
|
||||
@@ -459,19 +470,7 @@ fn extractRegFile(self: Inode, alloc: std.mem.Allocator, archive: *Archive, path
|
||||
_ = try dat_rdr.interface.streamRemaining(&wrt.interface);
|
||||
try wrt.interface.flush();
|
||||
|
||||
try self.setPermissionAndXattr(alloc, archive, fil, options);
|
||||
}
|
||||
/// Extract the inode file contents to the given path threadedly.
|
||||
/// pool is used to spawn threads.
|
||||
///
|
||||
/// Assumes the inode is a file or ext_file type.
|
||||
fn extractRegFileThreaded(self: Inode, alloc: std.mem.Allocator, archive: *Archive, path: []const u8, options: ExtractionOptions, pool: *Pool) !void {
|
||||
var fil = try std.fs.cwd().createFile(path, .{});
|
||||
defer fil.close();
|
||||
var data = try self.threadedDataReader(alloc, archive);
|
||||
try data.extractThreaded(fil, pool);
|
||||
|
||||
try self.setPermissionAndXattr(alloc, archive, fil, options);
|
||||
try self.setMetadata(alloc, archive, fil, options);
|
||||
}
|
||||
/// Creates the symlink described by the inode.
|
||||
///
|
||||
@@ -533,5 +532,5 @@ fn extractDevice(self: Inode, alloc: std.mem.Allocator, archive: *Archive, path:
|
||||
}
|
||||
var fil = try std.fs.cwd().openFile(path, .{});
|
||||
defer fil.close();
|
||||
try self.setPermissionAndXattr(alloc, archive, fil, options);
|
||||
try self.setMetadata(alloc, archive, fil, options);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user