Commit cffd9d9c authored by Zander Brown's avatar Zander Brown Committed by Zander Brown
Browse files

watcher: use GTree for log(n) lookups

parent f324d4cb
......@@ -129,8 +129,8 @@ kgx_application_finalize (GObject *object)
g_clear_object (&self->desktop_interface);
g_ptr_array_unref (self->watching);
g_ptr_array_unref (self->children);
g_clear_pointer (&self->watching, g_tree_unref);
g_clear_pointer (&self->children, g_tree_unref);
G_OBJECT_CLASS (kgx_application_parent_class)->finalize (object);
}
......@@ -157,63 +157,83 @@ kgx_application_activate (GApplication *app)
#if HAS_GTOP
static gboolean
watch_is_for_process (struct ProcessWatch *watch,
KgxProcess *process)
handle_watch_iter (gpointer pid,
gpointer val,
gpointer user_data)
{
return kgx_process_get_pid (watch->process) == kgx_process_get_pid (process);
KgxProcess *process = val;
KgxApplication *self = user_data;
GPid parent = kgx_process_get_parent (process);
struct ProcessWatch *watch = NULL;
watch = g_tree_lookup (self->watching, GINT_TO_POINTER (parent));
// There are far more processes on the system than there are children
// of watches, thus lookup are unlikly
if (G_UNLIKELY (watch != NULL)) {
if (!g_tree_lookup (self->children, pid)) {
struct ProcessWatch *child_watch = g_new (struct ProcessWatch, 1);
child_watch->process = g_rc_box_acquire (process);
child_watch->window = g_object_ref (watch->window);
g_debug ("Hello %i!", GPOINTER_TO_INT (pid));
g_tree_insert (self->children, pid, child_watch);
}
kgx_window_push_child (watch->window, process);
}
return FALSE;
}
struct RemoveDead {
GTree *plist;
GPtrArray *dead;
};
static gboolean
process_is_watched_by (KgxProcess *process,
struct ProcessWatch *watch)
remove_dead (gpointer pid,
gpointer val,
gpointer user_data)
{
return kgx_process_get_pid (watch->process) == kgx_process_get_pid (process);
struct RemoveDead *data = user_data;
struct ProcessWatch *watch = val;
if (!g_tree_lookup (data->plist, pid)) {
g_debug ("%i marked as dead", GPOINTER_TO_INT (pid));
kgx_window_pop_child (watch->window, watch->process);
g_ptr_array_add (data->dead, pid);
}
return FALSE;
}
static gboolean
watch (gpointer data)
{
KgxApplication *self = KGX_APPLICATION (data);
g_autoptr (GPtrArray) plist = NULL;
g_autoptr (GTree) plist = NULL;
struct RemoveDead dead;
plist = kgx_process_get_list ();
for (int i = 0; i < self->watching->len; i++) {
struct ProcessWatch *watch = g_ptr_array_index (self->watching, i);
for (int j = 0; j < plist->len; j++) {
g_autoptr (KgxProcess) parent = NULL;
KgxProcess *curr = g_ptr_array_index (plist, j);
parent = kgx_process_get_parent (curr);
if (kgx_process_get_pid (parent) == kgx_process_get_pid (watch->process)) {
if (!g_ptr_array_find_with_equal_func (self->children, curr, (GEqualFunc) watch_is_for_process, NULL)) {
struct ProcessWatch *child_watch = g_new(struct ProcessWatch, 1);
g_tree_foreach (plist, handle_watch_iter, self);
child_watch->process = g_rc_box_acquire (curr);
child_watch->window = g_object_ref (watch->window);
dead.plist = plist;
dead.dead = g_ptr_array_new_full (1, NULL);
// g_debug ("Hello %s!", exec);
g_tree_foreach (self->children, remove_dead, &dead);
g_ptr_array_add (self->children, child_watch);
}
kgx_window_push_child (watch->window, curr);
}
}
// We can't modify self->chilren whilst walking it
for (int i = 0; i < dead.dead->len; i++) {
g_tree_remove (self->children, g_ptr_array_index (dead.dead, i));
}
for (int i = 0; i < self->children->len; i++) {
struct ProcessWatch *child_watch = g_ptr_array_index (self->children, i);
if (!g_ptr_array_find_with_equal_func (plist, child_watch, (GEqualFunc) process_is_watched_by, NULL)) {
g_debug ("Bye %s!", kgx_process_get_exec (child_watch->process));
kgx_window_pop_child (child_watch->window, child_watch->process);
g_ptr_array_remove_index (self->children, i);
i--;
}
}
g_ptr_array_unref (dead.dead);
return G_SOURCE_CONTINUE;
}
......@@ -544,8 +564,14 @@ kgx_application_init (KgxApplication *self)
G_CALLBACK (font_changed),
self);
self->watching = g_ptr_array_new_with_free_func ((GDestroyNotify) clear_watch);
self->children = g_ptr_array_new_with_free_func ((GDestroyNotify) clear_watch);
self->watching = g_tree_new_full (kgx_pid_cmp,
NULL,
NULL,
(GDestroyNotify) clear_watch);
self->children = g_tree_new_full (kgx_pid_cmp,
NULL,
NULL,
(GDestroyNotify) clear_watch);
self->active = 0;
self->timeout = 0;
......@@ -578,14 +604,7 @@ kgx_application_add_watch (KgxApplication *self,
g_return_if_fail (KGX_IS_WINDOW (watch->window));
g_ptr_array_add (self->watching, watch);
}
static gboolean
watch_is_for_pid (struct ProcessWatch *watch,
gpointer pid)
{
return kgx_process_get_pid (watch->process) == GPOINTER_TO_INT (pid);
g_tree_insert (self->watching, GINT_TO_POINTER (pid), watch);
}
/**
......@@ -599,15 +618,10 @@ void
kgx_application_remove_watch (KgxApplication *self,
GPid pid)
{
guint idx = 0;
g_return_if_fail (KGX_IS_APPLICATION (self));
if (g_ptr_array_find_with_equal_func (self->watching,
GINT_TO_POINTER (pid),
(GEqualFunc) watch_is_for_pid,
&idx)) {
g_ptr_array_remove_index_fast (self->watching, idx);
if (G_LIKELY (g_tree_lookup (self->watching, GINT_TO_POINTER (pid)))) {
g_tree_remove (self->watching, GINT_TO_POINTER (pid));
g_debug ("Stopped watching %i", pid);
} else {
g_warning ("Unknown process %i", pid);
......
......@@ -58,8 +58,8 @@ struct ProcessWatch {
* @theme: the colour palette in use
* @scale: the font scaling used
* @desktop_interface: the #GSettings storing the system monospace font
* @watching: (element-type ProcessWatch): the shells running in windows
* @children: (element-type ProcessWatch): the processes running in shells
* @watching: ~ (element-type GLib.Pid ProcessWatch) the shells running in windows
* @children: ~ (element-type GLib.Pid ProcessWatch) the processes running in shells
* @active: counter of #KgxWindow's with #GtkWindow:is-active = %TRUE,
* obviously this should only ever be 1 or but we can't be certain
* @timeout: the current #GSource id of the watcher
......@@ -78,8 +78,8 @@ struct _KgxApplication
GSettings *desktop_interface;
GPtrArray *watching;
GPtrArray *children;
GTree *watching;
GTree *children;
guint timeout;
int active;
......
......@@ -157,18 +157,21 @@ kgx_process_get_is_root (KgxProcess *self)
*
* Get information about the processes parent
*
* Returns: the parent, free with kgx_process_unref()
* Note a previous version (0.1.0) returned a #KgxProcess, this has been
* changed in favour of lazy loading
*
* Returns: the parent #GPid
*
* Stability: Private
*
* Since: 0.1.0
*/
inline KgxProcess *
inline GPid
kgx_process_get_parent (KgxProcess *self)
{
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (self != NULL, 0);
return kgx_process_new (self->parent);
return self->parent;
}
/**
......@@ -199,31 +202,64 @@ kgx_process_get_exec (KgxProcess *self)
}
/**
* kgx_process_get_list:
* kgx_pid_cmp:
* @a: the first #GPid
* @b: the second #GPid
* @data: unused
*
* Implementation of #GCompareDataFunc for comparing #GPid encoded with
* GINT_TO_POINTER()
*
* Returns: difference between @a and @b
*
* Stability: Private
*
* Since: 0.2.0
*/
int
kgx_pid_cmp (gconstpointer a, gconstpointer b, gpointer data)
{
return a - b;
}
/**
* kgx_process_get_list: (skip)
*
* Get the list of running processes
*
* Returns: (transfer full) (element-type Kgx.Process): List of processes free with g_ptr_array_unref()
* Note: This originally (0.1.0) returned #GPtrArray but now returns #GTree
* for faster lookup
*
* Note: (skip) due to
* https://gitlab.gnome.org/GNOME/gobject-introspection/issues/310
*
* #GTree is map of #GPid -> #KgxProcess
*
* Returns: ~ (transfer full) (element-type GLib.Pid Kgx.Process)
* List of processes, free with g_tree_unref()
*
* Stability: Private
*
* Since: 0.1.0
*/
GPtrArray *
GTree *
kgx_process_get_list (void)
{
glibtop_proclist pid_list;
g_autofree GPid *pids = NULL;
GPtrArray *list = NULL;
GTree *list = NULL;
list = g_ptr_array_new_with_free_func ((GDestroyNotify) kgx_process_unref);
list = g_tree_new_full (kgx_pid_cmp,
NULL,
NULL,
(GDestroyNotify) kgx_process_unref);
pids = glibtop_get_proclist (&pid_list, GLIBTOP_KERN_PROC_ALL, 0);
g_return_val_if_fail (pids != NULL, NULL);
for (int i = 0; i < pid_list.number; i++) {
g_ptr_array_add (list, kgx_process_new (pids[i]));
g_tree_insert (list, GINT_TO_POINTER (pids[i]), kgx_process_new (pids[i]));
}
return list;
......
......@@ -34,17 +34,21 @@ typedef struct _KgxProcess KgxProcess;
* need to be #if HAS_GTOP
*/
#if HAS_GTOP
GPtrArray *kgx_process_get_list (void);
GTree *kgx_process_get_list (void);
KgxProcess *kgx_process_new (GPid pid);
GPid kgx_process_get_pid (KgxProcess *self);
gint32 kgx_process_get_uid (KgxProcess *self);
gboolean kgx_process_get_is_root (KgxProcess *self);
KgxProcess *kgx_process_get_parent (KgxProcess *self);
GPid kgx_process_get_parent (KgxProcess *self);
const char *kgx_process_get_exec (KgxProcess *self);
#endif
GType kgx_process_get_type (void);
void kgx_process_unref (KgxProcess *self);
int kgx_pid_cmp (gconstpointer a,
gconstpointer b,
gpointer data);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (KgxProcess, kgx_process_unref)
G_END_DECLS
......@@ -900,7 +900,7 @@ push_type (GHashTable *table,
{
g_hash_table_insert (table,
GINT_TO_POINTER (pid),
g_rc_box_acquire (process));
process != NULL ? g_rc_box_acquire (process) : NULL);
g_debug ("Now %i %s", g_hash_table_size (table), class_name);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment