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) ...@@ -129,8 +129,8 @@ kgx_application_finalize (GObject *object)
g_clear_object (&self->desktop_interface); g_clear_object (&self->desktop_interface);
g_ptr_array_unref (self->watching); g_clear_pointer (&self->watching, g_tree_unref);
g_ptr_array_unref (self->children); g_clear_pointer (&self->children, g_tree_unref);
G_OBJECT_CLASS (kgx_application_parent_class)->finalize (object); G_OBJECT_CLASS (kgx_application_parent_class)->finalize (object);
} }
...@@ -157,63 +157,83 @@ kgx_application_activate (GApplication *app) ...@@ -157,63 +157,83 @@ kgx_application_activate (GApplication *app)
#if HAS_GTOP #if HAS_GTOP
static gboolean static gboolean
watch_is_for_process (struct ProcessWatch *watch, handle_watch_iter (gpointer pid,
KgxProcess *process) 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 static gboolean
process_is_watched_by (KgxProcess *process, remove_dead (gpointer pid,
struct ProcessWatch *watch) 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 static gboolean
watch (gpointer data) watch (gpointer data)
{ {
KgxApplication *self = KGX_APPLICATION (data); KgxApplication *self = KGX_APPLICATION (data);
g_autoptr (GPtrArray) plist = NULL; g_autoptr (GTree) plist = NULL;
struct RemoveDead dead;
plist = kgx_process_get_list (); plist = kgx_process_get_list ();
for (int i = 0; i < self->watching->len; i++) { g_tree_foreach (plist, handle_watch_iter, self);
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);
child_watch->process = g_rc_box_acquire (curr); dead.plist = plist;
child_watch->window = g_object_ref (watch->window); 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); // 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));
kgx_window_push_child (watch->window, curr);
}
}
} }
for (int i = 0; i < self->children->len; i++) { g_ptr_array_unref (dead.dead);
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--;
}
}
return G_SOURCE_CONTINUE; return G_SOURCE_CONTINUE;
} }
...@@ -544,8 +564,14 @@ kgx_application_init (KgxApplication *self) ...@@ -544,8 +564,14 @@ kgx_application_init (KgxApplication *self)
G_CALLBACK (font_changed), G_CALLBACK (font_changed),
self); self);
self->watching = g_ptr_array_new_with_free_func ((GDestroyNotify) clear_watch); self->watching = g_tree_new_full (kgx_pid_cmp,
self->children = g_ptr_array_new_with_free_func ((GDestroyNotify) clear_watch); NULL,
NULL,
(GDestroyNotify) clear_watch);
self->children = g_tree_new_full (kgx_pid_cmp,
NULL,
NULL,
(GDestroyNotify) clear_watch);
self->active = 0; self->active = 0;
self->timeout = 0; self->timeout = 0;
...@@ -578,14 +604,7 @@ kgx_application_add_watch (KgxApplication *self, ...@@ -578,14 +604,7 @@ kgx_application_add_watch (KgxApplication *self,
g_return_if_fail (KGX_IS_WINDOW (watch->window)); g_return_if_fail (KGX_IS_WINDOW (watch->window));
g_ptr_array_add (self->watching, watch); g_tree_insert (self->watching, GINT_TO_POINTER (pid), watch);
}
static gboolean
watch_is_for_pid (struct ProcessWatch *watch,
gpointer pid)
{
return kgx_process_get_pid (watch->process) == GPOINTER_TO_INT (pid);
} }
/** /**
...@@ -599,15 +618,10 @@ void ...@@ -599,15 +618,10 @@ void
kgx_application_remove_watch (KgxApplication *self, kgx_application_remove_watch (KgxApplication *self,
GPid pid) GPid pid)
{ {
guint idx = 0;
g_return_if_fail (KGX_IS_APPLICATION (self)); g_return_if_fail (KGX_IS_APPLICATION (self));
if (g_ptr_array_find_with_equal_func (self->watching, if (G_LIKELY (g_tree_lookup (self->watching, GINT_TO_POINTER (pid)))) {
GINT_TO_POINTER (pid), g_tree_remove (self->watching, GINT_TO_POINTER (pid));
(GEqualFunc) watch_is_for_pid,
&idx)) {
g_ptr_array_remove_index_fast (self->watching, idx);
g_debug ("Stopped watching %i", pid); g_debug ("Stopped watching %i", pid);
} else { } else {
g_warning ("Unknown process %i", pid); g_warning ("Unknown process %i", pid);
......
...@@ -58,8 +58,8 @@ struct ProcessWatch { ...@@ -58,8 +58,8 @@ struct ProcessWatch {
* @theme: the colour palette in use * @theme: the colour palette in use
* @scale: the font scaling used * @scale: the font scaling used
* @desktop_interface: the #GSettings storing the system monospace font * @desktop_interface: the #GSettings storing the system monospace font
* @watching: (element-type ProcessWatch): the shells running in windows * @watching: ~ (element-type GLib.Pid ProcessWatch) the shells running in windows
* @children: (element-type ProcessWatch): the processes running in shells * @children: ~ (element-type GLib.Pid ProcessWatch) the processes running in shells
* @active: counter of #KgxWindow's with #GtkWindow:is-active = %TRUE, * @active: counter of #KgxWindow's with #GtkWindow:is-active = %TRUE,
* obviously this should only ever be 1 or but we can't be certain * obviously this should only ever be 1 or but we can't be certain
* @timeout: the current #GSource id of the watcher * @timeout: the current #GSource id of the watcher
...@@ -78,8 +78,8 @@ struct _KgxApplication ...@@ -78,8 +78,8 @@ struct _KgxApplication
GSettings *desktop_interface; GSettings *desktop_interface;
GPtrArray *watching; GTree *watching;
GPtrArray *children; GTree *children;
guint timeout; guint timeout;
int active; int active;
......
...@@ -157,18 +157,21 @@ kgx_process_get_is_root (KgxProcess *self) ...@@ -157,18 +157,21 @@ kgx_process_get_is_root (KgxProcess *self)
* *
* Get information about the processes parent * 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 * Stability: Private
* *
* Since: 0.1.0 * Since: 0.1.0
*/ */
inline KgxProcess * inline GPid
kgx_process_get_parent (KgxProcess *self) 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) ...@@ -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 * 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 * Stability: Private
* *
* Since: 0.1.0 * Since: 0.1.0
*/ */
GPtrArray * GTree *
kgx_process_get_list (void) kgx_process_get_list (void)
{ {
glibtop_proclist pid_list; glibtop_proclist pid_list;
g_autofree GPid *pids = NULL; 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); pids = glibtop_get_proclist (&pid_list, GLIBTOP_KERN_PROC_ALL, 0);
g_return_val_if_fail (pids != NULL, NULL); g_return_val_if_fail (pids != NULL, NULL);
for (int i = 0; i < pid_list.number; i++) { 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; return list;
......
...@@ -34,17 +34,21 @@ typedef struct _KgxProcess KgxProcess; ...@@ -34,17 +34,21 @@ typedef struct _KgxProcess KgxProcess;
* need to be #if HAS_GTOP * need to be #if HAS_GTOP
*/ */
#if HAS_GTOP #if HAS_GTOP
GPtrArray *kgx_process_get_list (void); GTree *kgx_process_get_list (void);
KgxProcess *kgx_process_new (GPid pid); KgxProcess *kgx_process_new (GPid pid);
GPid kgx_process_get_pid (KgxProcess *self); GPid kgx_process_get_pid (KgxProcess *self);
gint32 kgx_process_get_uid (KgxProcess *self); gint32 kgx_process_get_uid (KgxProcess *self);
gboolean kgx_process_get_is_root (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); const char *kgx_process_get_exec (KgxProcess *self);
#endif #endif
GType kgx_process_get_type (void); GType kgx_process_get_type (void);
void kgx_process_unref (KgxProcess *self); 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_DEFINE_AUTOPTR_CLEANUP_FUNC (KgxProcess, kgx_process_unref)
G_END_DECLS G_END_DECLS
...@@ -900,7 +900,7 @@ push_type (GHashTable *table, ...@@ -900,7 +900,7 @@ push_type (GHashTable *table,
{ {
g_hash_table_insert (table, g_hash_table_insert (table,
GINT_TO_POINTER (pid), 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); 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