Skip to content

Commit 8bbf413

Browse files
committed
refactor scheduling of build on save jobs
1 parent 9bbd0ba commit 8bbf413

File tree

2 files changed

+82
-63
lines changed

2 files changed

+82
-63
lines changed

src/Server.zig

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,7 @@ pub fn updateConfiguration(
924924
if (!std.process.can_spawn) break :blk;
925925

926926
for (server.document_store.build_files.keys()) |build_file_uri| {
927-
try server.document_store.invalidateBuildFile(build_file_uri);
927+
server.document_store.invalidateBuildFile(build_file_uri);
928928
}
929929
}
930930

@@ -934,12 +934,7 @@ pub fn updateConfiguration(
934934
}
935935
server.document_store.cimports.clearAndFree(server.document_store.allocator);
936936

937-
if (std.process.can_spawn and
938-
server.config.enable_build_on_save != false and
939-
server.client_capabilities.supports_publish_diagnostics)
940-
{
941-
try server.thread_pool.spawn(runBuildOnSave, .{server});
942-
}
937+
server.scheduleBuildOnSave();
943938
}
944939

945940
if (server.status == .initialized) {
@@ -1340,15 +1335,10 @@ fn saveDocumentHandler(server: *Server, arena: std.mem.Allocator, notification:
13401335
const uri = notification.textDocument.uri;
13411336

13421337
if (std.process.can_spawn and DocumentStore.isBuildFile(uri)) {
1343-
try server.document_store.invalidateBuildFile(uri);
1338+
server.document_store.invalidateBuildFile(uri);
13441339
}
13451340

1346-
if (std.process.can_spawn and
1347-
server.config.enable_build_on_save != false and
1348-
server.client_capabilities.supports_publish_diagnostics)
1349-
{
1350-
try server.thread_pool.spawn(runBuildOnSave, .{server});
1351-
}
1341+
server.scheduleBuildOnSave();
13521342

13531343
if (server.getAutofixMode() == .on_save) {
13541344
const handle = server.document_store.getHandle(uri) orelse return;
@@ -1647,33 +1637,59 @@ fn selectionRangeHandler(server: *Server, arena: std.mem.Allocator, request: typ
16471637
return try selection_range.generateSelectionRanges(arena, handle, request.positions, server.offset_encoding);
16481638
}
16491639

1650-
fn runBuildOnSave(server: *Server) void {
1651-
comptime std.debug.assert(std.process.can_spawn);
1640+
fn scheduleBuildOnSave(server: *Server) void {
1641+
if (!std.process.can_spawn) return;
1642+
if (server.config.enable_build_on_save != false) return;
1643+
if (!server.client_capabilities.supports_publish_diagnostics) return;
16521644

1653-
// TODO data-race on server.workspaces
16541645
for (server.workspaces.items) |*workspace| {
1655-
const was_build_on_save_running = workspace.is_build_on_save_running.swap(true, .acq_rel);
1656-
if (was_build_on_save_running) continue;
1657-
defer workspace.is_build_on_save_running.store(false, .release);
1658-
1659-
var arena_allocator = std.heap.ArenaAllocator.init(server.allocator);
1660-
defer arena_allocator.deinit();
1661-
var diagnostic_set = std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(types.Diagnostic)){};
1662-
diagnostics_gen.generateBuildOnSaveDiagnostics(server, workspace.uri, arena_allocator.allocator(), &diagnostic_set) catch |err| {
1663-
log.err("failed to run build on save on {s}: {}", .{ workspace.uri, err });
1664-
return;
1665-
};
1666-
1667-
for (diagnostic_set.keys(), diagnostic_set.values()) |document_uri, diagnostics| {
1668-
const json_message = server.sendToClientNotification("textDocument/publishDiagnostics", .{
1669-
.uri = document_uri,
1670-
.diagnostics = diagnostics.items,
1671-
}) catch return;
1672-
server.allocator.free(json_message);
1646+
if (zig_builtin.single_threaded) {
1647+
server.runBuildOnSave(workspace);
1648+
} else {
1649+
// TODO race-condition: `server.workspaces` may be modified
1650+
server.thread_pool.spawn(runBuildOnSave, .{ server, workspace }) catch {
1651+
server.runBuildOnSave(workspace);
1652+
};
16731653
}
16741654
}
16751655
}
16761656

1657+
fn runBuildOnSave(server: *Server, workspace: *Workspace) void {
1658+
comptime std.debug.assert(std.process.can_spawn);
1659+
1660+
const was_build_on_save_running = workspace.is_build_on_save_running.swap(true, .acq_rel);
1661+
if (was_build_on_save_running) return;
1662+
1663+
defer workspace.is_build_on_save_running.store(false, .release);
1664+
1665+
var diagnostic_set: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(types.Diagnostic)) = .{};
1666+
1667+
var arena_allocator = std.heap.ArenaAllocator.init(server.allocator);
1668+
defer arena_allocator.deinit();
1669+
1670+
diagnostics_gen.generateBuildOnSaveDiagnostics(
1671+
server.allocator,
1672+
&server.document_store,
1673+
// TODO data-race on server.config
1674+
server.config,
1675+
workspace.uri,
1676+
arena_allocator.allocator(),
1677+
&diagnostic_set,
1678+
) catch |err| {
1679+
log.err("failed to run build on save on {s}: {}", .{ workspace.uri, err });
1680+
return;
1681+
};
1682+
1683+
for (diagnostic_set.keys(), diagnostic_set.values()) |document_uri, diagnostics| {
1684+
std.debug.assert(server.client_capabilities.supports_publish_diagnostics);
1685+
const json_message = server.sendToClientNotification("textDocument/publishDiagnostics", .{
1686+
.uri = document_uri,
1687+
.diagnostics = diagnostics.items,
1688+
}) catch return;
1689+
server.allocator.free(json_message);
1690+
}
1691+
}
1692+
16771693
const HandledRequestParams = union(enum) {
16781694
initialize: types.InitializeParams,
16791695
shutdown,
@@ -1786,7 +1802,7 @@ pub fn create(allocator: std.mem.Allocator) !*Server {
17861802
} else {
17871803
try server.thread_pool.init(.{
17881804
.allocator = allocator,
1789-
.n_jobs = 4, // what is a good value here?
1805+
.n_jobs = @min(4, std.Thread.getCpuCount() catch 1), // what is a good value here?
17901806
});
17911807
server.document_store.thread_pool = &server.thread_pool;
17921808
}

src/features/diagnostics.zig

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const Ast = std.zig.Ast;
55
const log = std.log.scoped(.zls_diag);
66

77
const Server = @import("../Server.zig");
8+
const Config = @import("../Config.zig");
89
const DocumentStore = @import("../DocumentStore.zig");
910
const types = @import("lsp").types;
1011
const Analyser = @import("../analysis.zig");
@@ -191,7 +192,9 @@ pub fn generateDiagnostics(server: *Server, arena: std.mem.Allocator, handle: *D
191192
}
192193

193194
pub fn generateBuildOnSaveDiagnostics(
194-
server: *Server,
195+
allocator: std.mem.Allocator,
196+
store: *DocumentStore,
197+
config: Config,
195198
workspace_uri: types.URI,
196199
arena: std.mem.Allocator,
197200
diagnostics: *std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(types.Diagnostic)),
@@ -200,19 +203,19 @@ pub fn generateBuildOnSaveDiagnostics(
200203
defer tracy_zone.end();
201204
comptime std.debug.assert(std.process.can_spawn);
202205

203-
const zig_exe_path = server.config.zig_exe_path orelse return;
204-
const zig_lib_path = server.config.zig_lib_path orelse return;
206+
const zig_exe_path = config.zig_exe_path orelse return;
207+
const zig_lib_path = config.zig_lib_path orelse return;
205208

206-
const workspace_path = URI.parse(server.allocator, workspace_uri) catch |err| {
209+
const workspace_path = URI.parse(allocator, workspace_uri) catch |err| {
207210
log.err("failed to parse invalid uri '{s}': {}", .{ workspace_uri, err });
208211
return;
209212
};
210-
defer server.allocator.free(workspace_path);
213+
defer allocator.free(workspace_path);
211214

212215
std.debug.assert(std.fs.path.isAbsolute(workspace_path));
213216

214-
const build_zig_path = try std.fs.path.join(server.allocator, &.{ workspace_path, "build.zig" });
215-
defer server.allocator.free(build_zig_path);
217+
const build_zig_path = try std.fs.path.join(allocator, &.{ workspace_path, "build.zig" });
218+
defer allocator.free(build_zig_path);
216219

217220
std.fs.accessAbsolute(build_zig_path, .{}) catch |err| switch (err) {
218221
error.FileNotFound => return,
@@ -222,8 +225,8 @@ pub fn generateBuildOnSaveDiagnostics(
222225
},
223226
};
224227

225-
const build_zig_uri = try URI.fromPath(server.allocator, build_zig_path);
226-
defer server.allocator.free(build_zig_uri);
228+
const build_zig_uri = try URI.fromPath(allocator, build_zig_path);
229+
defer allocator.free(build_zig_uri);
227230

228231
const base_args = &[_][]const u8{
229232
zig_exe_path,
@@ -235,21 +238,21 @@ pub fn generateBuildOnSaveDiagnostics(
235238
"none",
236239
};
237240

238-
var argv = try std.ArrayListUnmanaged([]const u8).initCapacity(arena, base_args.len + server.config.build_on_save_args.len);
241+
var argv = try std.ArrayListUnmanaged([]const u8).initCapacity(arena, base_args.len + config.build_on_save_args.len);
239242
defer argv.deinit(arena);
240243
argv.appendSliceAssumeCapacity(base_args);
241-
argv.appendSliceAssumeCapacity(server.config.build_on_save_args);
244+
argv.appendSliceAssumeCapacity(config.build_on_save_args);
242245

243-
const has_explicit_steps = for (server.config.build_on_save_args) |extra_arg| {
246+
const has_explicit_steps = for (config.build_on_save_args) |extra_arg| {
244247
if (!std.mem.startsWith(u8, extra_arg, "-")) break true;
245248
} else false;
246249

247250
var has_check_step: bool = false;
248251

249252
blk: {
250-
server.document_store.lock.lockShared();
251-
defer server.document_store.lock.unlockShared();
252-
const build_file = server.document_store.build_files.get(build_zig_uri) orelse break :blk;
253+
store.lock.lockShared();
254+
defer store.lock.unlockShared();
255+
const build_file = store.build_files.get(build_zig_path) orelse break :blk;
253256

254257
no_build_config: {
255258
const build_associated_config = build_file.build_associated_config orelse break :no_build_config;
@@ -263,9 +266,9 @@ pub fn generateBuildOnSaveDiagnostics(
263266

264267
no_check: {
265268
if (has_explicit_steps) break :no_check;
266-
const config = build_file.tryLockConfig() orelse break :no_check;
269+
const build_config = build_file.tryLockConfig() orelse break :no_check;
267270
defer build_file.unlockConfig();
268-
for (config.top_level_steps) |tls| {
271+
for (build_config.top_level_steps) |tls| {
269272
if (std.mem.eql(u8, tls, "check")) {
270273
has_check_step = true;
271274
break;
@@ -274,7 +277,7 @@ pub fn generateBuildOnSaveDiagnostics(
274277
}
275278
}
276279

277-
if (!(server.config.enable_build_on_save orelse has_check_step)) {
280+
if (!(config.enable_build_on_save orelse has_check_step)) {
278281
return;
279282
}
280283

@@ -283,30 +286,30 @@ pub fn generateBuildOnSaveDiagnostics(
283286
try argv.append(arena, "check");
284287
}
285288

286-
const extra_args_joined = try std.mem.join(server.allocator, " ", argv.items[base_args.len..]);
287-
defer server.allocator.free(extra_args_joined);
289+
const extra_args_joined = try std.mem.join(allocator, " ", argv.items[base_args.len..]);
290+
defer allocator.free(extra_args_joined);
288291

289292
log.info("Running build-on-save: {s} ({s})", .{ build_zig_uri, extra_args_joined });
290293

291294
const result = std.process.Child.run(.{
292-
.allocator = server.allocator,
295+
.allocator = allocator,
293296
.argv = argv.items,
294297
.cwd = workspace_path,
295298
.max_output_bytes = 1024 * 1024,
296299
}) catch |err| {
297-
const joined = std.mem.join(server.allocator, " ", argv.items) catch return;
298-
defer server.allocator.free(joined);
300+
const joined = std.mem.join(allocator, " ", argv.items) catch return;
301+
defer allocator.free(joined);
299302
log.err("failed zig build command:\n{s}\nerror:{}\n", .{ joined, err });
300303
return err;
301304
};
302-
defer server.allocator.free(result.stdout);
303-
defer server.allocator.free(result.stderr);
305+
defer allocator.free(result.stdout);
306+
defer allocator.free(result.stderr);
304307

305308
switch (result.term) {
306309
.Exited => |code| if (code == 0) return,
307310
else => {
308-
const joined = std.mem.join(server.allocator, " ", argv.items) catch return;
309-
defer server.allocator.free(joined);
311+
const joined = std.mem.join(allocator, " ", argv.items) catch return;
312+
defer allocator.free(joined);
310313
log.err("failed zig build command:\n{s}\nstderr:{s}\n\n", .{ joined, result.stderr });
311314
},
312315
}

0 commit comments

Comments
 (0)