Skip to content

Commit 7e20e38

Browse files
committed
Open imported documents preemptively
1 parent a0ab11c commit 7e20e38

File tree

1 file changed

+88
-7
lines changed

1 file changed

+88
-7
lines changed

src/DocumentStore.zig

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ lsp_capabilities: struct {
3535
supports_semantic_tokens_refresh: bool = false,
3636
supports_inlay_hints_refresh: bool = false,
3737
} = .{},
38+
currently_loading_uris: std.StringArrayHashMapUnmanaged(void) = .empty,
39+
wait_for_currently_loading_uri: std.Thread.Condition = .{},
3840

3941
pub const Uri = []const u8;
4042

@@ -647,6 +649,9 @@ pub fn deinit(self: *DocumentStore) void {
647649
}
648650
self.trigram_stores.deinit(self.allocator);
649651

652+
std.debug.assert(self.currently_loading_uris.count() == 0);
653+
self.currently_loading_uris.deinit(self.allocator);
654+
650655
if (supports_build_system) {
651656
for (self.build_files.values()) |build_file| {
652657
build_file.deinit(self.allocator);
@@ -725,10 +730,35 @@ pub fn getOrLoadHandle(store: *DocumentStore, uri: Uri) ?*Handle {
725730
const tracy_zone = tracy.trace(@src());
726731
defer tracy_zone.end();
727732

728-
if (store.getHandle(uri)) |handle| return handle;
733+
{
734+
store.lock.lock();
735+
defer store.lock.unlock();
729736

730-
const file_contents = store.readUri(uri) orelse return null;
737+
while (true) {
738+
if (store.handles.get(uri)) |handle| return handle;
739+
740+
const gop = store.currently_loading_uris.getOrPutValue(
741+
store.allocator,
742+
uri,
743+
{},
744+
) catch return null;
745+
746+
if (!gop.found_existing) {
747+
break;
748+
}
749+
750+
var mutex: std.Thread.Mutex = .{};
751+
752+
mutex.lock();
753+
defer mutex.unlock();
754+
755+
store.lock.unlock();
756+
store.wait_for_currently_loading_uri.wait(&mutex);
757+
store.lock.lock();
758+
}
759+
}
731760

761+
const file_contents = store.readUri(uri) orelse return null;
732762
return store.createAndStoreDocument(uri, file_contents) catch |err| {
733763
log.err("failed to store document '{s}': {}", .{ uri, err });
734764
return null;
@@ -831,6 +861,8 @@ pub fn openDocument(self: *DocumentStore, uri: Uri, text: []const u8) error{OutO
831861
}
832862
gop.value_ptr.* = handle_ptr;
833863

864+
try self.loadDependencies(handle_ptr.import_uris.items);
865+
834866
if (isBuildFile(uri)) {
835867
log.debug("Opened document '{s}' (build file)", .{uri});
836868
} else {
@@ -885,6 +917,7 @@ pub fn refreshDocument(self: *DocumentStore, uri: Uri, new_text: [:0]const u8) !
885917
try handle.setSource(new_text);
886918
handle.import_uris = try self.collectImportUris(handle);
887919
handle.cimports = try collectCIncludes(self.allocator, handle.tree);
920+
try self.loadDependencies(handle.import_uris.items);
888921
}
889922

890923
/// Removes a document from the store, unless said document is currently open and
@@ -1551,13 +1584,17 @@ fn createAndStoreDocument(self: *DocumentStore, uri: Uri, text: [:0]const u8) er
15511584
const gop = blk: {
15521585
self.lock.lock();
15531586
defer self.lock.unlock();
1554-
break :blk try self.handles.getOrPutValue(self.allocator, handle_ptr.uri, handle_ptr);
1587+
1588+
const gop = try self.handles.getOrPutValue(self.allocator, handle_ptr.uri, handle_ptr);
1589+
std.debug.assert(!gop.found_existing);
1590+
1591+
std.debug.assert(self.currently_loading_uris.swapRemove(uri));
1592+
self.wait_for_currently_loading_uri.broadcast();
1593+
1594+
break :blk gop;
15551595
};
15561596

1557-
if (gop.found_existing) {
1558-
handle_ptr.deinit();
1559-
self.allocator.destroy(handle_ptr);
1560-
}
1597+
try self.loadDependencies(handle_ptr.import_uris.items);
15611598

15621599
if (isBuildFile(gop.value_ptr.*.uri)) {
15631600
log.debug("Opened document '{s}' (build file)", .{gop.value_ptr.*.uri});
@@ -1568,6 +1605,50 @@ fn createAndStoreDocument(self: *DocumentStore, uri: Uri, text: [:0]const u8) er
15681605
return gop.value_ptr.*;
15691606
}
15701607

1608+
fn loadDependencies(store: *DocumentStore, import_uris: []const []const u8) !void {
1609+
var not_currently_loading_uris: std.ArrayListUnmanaged([]const u8) =
1610+
try .initCapacity(store.allocator, import_uris.len);
1611+
defer {
1612+
not_currently_loading_uris.deinit(store.allocator);
1613+
}
1614+
errdefer {
1615+
for (not_currently_loading_uris.items) |duped_import_uri| {
1616+
store.allocator.free(duped_import_uri);
1617+
}
1618+
}
1619+
1620+
{
1621+
store.lock.lockShared();
1622+
defer store.lock.unlockShared();
1623+
1624+
for (import_uris) |import_uri| {
1625+
if (!store.handles.contains(import_uri) and
1626+
!store.currently_loading_uris.contains(import_uri))
1627+
{
1628+
not_currently_loading_uris.appendAssumeCapacity(
1629+
try store.allocator.dupe(u8, import_uri),
1630+
);
1631+
}
1632+
}
1633+
}
1634+
1635+
errdefer comptime unreachable;
1636+
1637+
const S = struct {
1638+
fn getOrLoadHandleVoid(s: *DocumentStore, duped_import_uri: Uri) void {
1639+
_ = s.getOrLoadHandle(duped_import_uri);
1640+
defer s.allocator.free(duped_import_uri);
1641+
}
1642+
};
1643+
1644+
for (not_currently_loading_uris.items) |duped_import_uri| {
1645+
store.thread_pool.spawn(S.getOrLoadHandleVoid, .{ store, duped_import_uri }) catch {
1646+
defer store.allocator.free(duped_import_uri);
1647+
_ = store.getOrLoadHandle(duped_import_uri);
1648+
};
1649+
}
1650+
}
1651+
15711652
/// Caller owns returned memory.
15721653
/// **Thread safe** takes a shared lock
15731654
fn collectImportUris(self: *DocumentStore, handle: *Handle) error{OutOfMemory}!std.ArrayListUnmanaged(Uri) {

0 commit comments

Comments
 (0)