call_end

    • Pl chevron_right

      Christian Hergert: Directory Listings with Foundry

      news.movim.eu / PlanetGnome • 21 September • 3 minutes

    I took a different approach to directory listings in Foundry. They use GListModel as the interface but behind the scenes it is implemented with futures and and fibers.

    A primary use case for a directory listing is the project tree of an IDE. Since we use GtkListView for efficient trees in GTK it we expose a GListModel . Each item in the directory is represented as a FoundryDirectoryItem which acts just a bit like a specialized GFileInfo . It contains information about all the attributes requested in the directory listing.

    You can also request some other information that is not traditionally available via GFileInfo . You can request attributes that will be populated by the version control system such as if the file is modified or should be ignored.

    Use a GtkFilterListModel to look at specific properties or attributes. Sort them quickly using GtkSortListModel . All of this makes implementing a project tree browser very straight forward.

    Reining in the Complexity

    One of the most complex things in writing a directory listing is managing updates to the directory. To manage some level of correctness here Foundry does it with a fiber in the following ordering:

    • Start a file monitor on the directory and start queing up changes
    • Enumerate children in the directory and add to internal list
    • Start processing monitor events, starting with the backlog

    Performance

    There are a couple tricks to balancing the performance of new items being added and items being removed. We want both to be similarly well performing.

    To do this, new items are always placed at the end of the list. We don’t care about sorting here because that will be dealt with at a higher layer (in the GtkSortListModel ). That keeps adding new items quite fast because we can quickly access the end of the list. This saves us a lot of time sorting the tree just for the removal.

    But if you aren’t sorting the items, how do you make removals quick? Doesn’t that become O(n) ?

    Well not quite. If we keep a secondary index (in this case a simple GHashTable ) then we can store a key (the file’s name) which points to a stable pointer (a GSequenceIter ). That lookup is O(1) and the removal from a GSequence on average are O(log n) .

    Big O notation is often meaningless when you’re talking about different systems. So let’s be real for a moment. Your callback from GListModel::items-changed() can have a huge impact on performance compared to the internal data structures here.

    Reference Counting and Fibers

    When doing this with a fiber attention must be taken to avoid over referencing the object or you risk never disposing/finalizing.

    One way out of that mess is to use a GWeakRef and only request the object when it is truly necessary. That way, you can make your fiber cancel when the directory listing is disposed. In turn cleanup your monitor and other resources are automatically cleaned up.

    I do this by creating a future that will resolve when there is more work to be done. Then I release my self object followed by awaiting the future. At that point I’ll either resolve or reject from an error (including the fiber being cancelled).

    If I need self again because I got an event, it’s just a g_weak_ref_get() away.

    Future-based File Monitors

    I’m not a huge fan of “signal based” APIs like GFileMonitor so internally FoundryFileMonitor does something a bit different. It still uses GFileMonitor internally for outstanding portability, but the exposed API uses DexFuture .

    This allows you to call when you want to get the next event (which may already be queued). Call foundry_file_monotor_next() to get the next event and the future will resolve when an event has been received. This makes more complex awaiting operations feasible too (such as monitoring multiple directories for the next change).

    foundry-directory-listing.c , foundry-directory-item.c , and foundry-file-monitor.c .