For the last couple of weeks I’ve been re-writing the existing PackageKit-glib bindings. The old library was beginning to limit what we could do with client applications. The biggest problems were:
- A lot of the dbus calls are sync, which slowed down application startup and user input “snappyness”
- A lot of the methods were not cancellable
- It’s very hard to add details and update details about a set of packages, without caching all the data in the client
- Having to “reset” a single heavyweight client instance before doing each operation (mitigated in some respects with PkClientPool)
As the days have progressed, I’ve slowly add more functionality to the packagekit-glib2 library, with the aim of removing the original glib library for 0.6.x. After two weeks of hacking, I’ve deprecated the glib1 library, with everything in the PackageKit git master tree is now using glib2. Overall, working with everything asynchronous works really well. The GUI clients benefit the most, although it makes the text clients much simpler too:
[hughsie@hughsie-laptop client]$ wc -l pk-console*.c
One of the main benefits is that the “dance” (the multiple transactions) can be done asynchronously, and completely hidden from the client. To do this, I’ve got the following classes:
- PkControl: For getting properties on the main interface, and doing methods such as GetTid
- PkClient: For scheduling a single transaction
- PkResults: For storing the completed results of the transaction (packages, update-details, all the compound objects)
- PkProgress: For storing current progress, for passing to async clients
- PkTask: For managing the “dance”, for instance, requeuing transaction for simulate, gpg-keys, eulas and for trusted. It superclasses PkClient.
Now, PkTask on it’s own isn’t very interesting, as it fails at every step of the dance by default. PkTask does however have klass methods that can be overridden by classes implementing PkTask. There’s an example I made for the “make check” functionality called PkTaskWrapper which basically accepts each step of the dance without asking the user. More interestingly, there’s a class called PkTaskText that implements PkTask and handles all the callbacks using console commands like fgets. That’s what text stuff like pkcon and pk-debuginfo-install are now using, and why they are a lot smaller now.
This means that pkcon can create an instance of PkTaskText, and all the interactions are done for it in the PkTaskText object. The actual interaction is hidden from pkcon, and all it gets is the progress callback as the transactions are scheduled. And because PkTaskText can be used as a PkTask, it can also be used as a PkClient, so the methods that are not wrapped can be used with a single object. It also means that other clients can just use PkTaskText and get all the complex interaction stuff handled automatically.
Of course, in gnome-packagekit, it will make things much cleaner as each PkClient can be converted to use an async GpkTask, which will handle all the interactions. So, instead of having all the scary logic about what methods to send in response to different signals and return codes in multiple places, we can just derive from PkTask and do them all in one place. Of course, moving from a 80% sync library to a 99% async library is going to be hard for projects that make heavy use of libpackagekit-glib. The most obvious example is gnome-packagekit, and so far I’m already about 80% they way through. Diffstat reports “59 files changed, 6311 insertions(+), 8684 deletions(-)” with the majority of porting work just switching code to use the async variants. When it at least compiles I’ll push it to git master, but until then it lives in the glib2 git branch.
The new code is much cleaner, more debuggable and most of all supportable, so when 0.6.x is released (a few months away) we can remove a metric ton of code (~20000 LOC) from the server. In preparation we’ve written a porting guide, although it’ll get more love when the design for packagekit-glib2 is set in stone.