summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ck-vt-monitor.c226
1 files changed, 174 insertions, 52 deletions
diff --git a/src/ck-vt-monitor.c b/src/ck-vt-monitor.c
index 59958d5..a0b5cf0 100644
--- a/src/ck-vt-monitor.c
+++ b/src/ck-vt-monitor.c
@@ -57,6 +57,9 @@ struct CkVtMonitorPrivate
int vfd;
GHashTable *vt_thread_hash;
guint active_num;
+
+ GAsyncQueue *event_queue;
+ guint process_queue_id;
};
enum {
@@ -74,9 +77,12 @@ static void ck_vt_monitor_class_init (CkVtMonitorClass *klass);
static void ck_vt_monitor_init (CkVtMonitor *vt_monitor);
static void ck_vt_monitor_finalize (GObject *object);
+static void vt_add_watches (CkVtMonitor *vt_monitor);
+
G_DEFINE_TYPE (CkVtMonitor, ck_vt_monitor, G_TYPE_OBJECT)
-static void watch_vts (CkVtMonitor *vt_monitor);
+G_LOCK_DEFINE_STATIC (hash_lock);
+G_LOCK_DEFINE_STATIC (schedule_lock);
static gpointer vt_object = NULL;
@@ -143,13 +149,16 @@ change_active_num (CkVtMonitor *vt_monitor,
{
if (vt_monitor->priv->active_num != num) {
+ ck_debug ("Changing active VT: %d", num);
vt_monitor->priv->active_num = num;
- g_hash_table_remove (vt_monitor->priv->vt_thread_hash, GUINT_TO_POINTER (num));
- watch_vts (vt_monitor);
+ /* add a watch to every vt without a thread */
+ vt_add_watches (vt_monitor);
g_signal_emit (vt_monitor, signals[ACTIVE_CHANGED], 0, num);
+ } else {
+ ck_debug ("VT activated but already active: %d", num);
}
}
@@ -158,16 +167,89 @@ typedef struct {
CkVtMonitor *vt_monitor;
} ThreadData;
-static gboolean
-vt_activated (ThreadData *data)
+typedef struct {
+ gint32 num;
+} EventData;
+
+static void
+thread_data_free (ThreadData *data)
{
- change_active_num (data->vt_monitor, data->num);
+ if (data == NULL) {
+ return;
+ }
g_free (data);
+}
+
+static void
+event_data_free (EventData *data)
+{
+ if (data == NULL) {
+ return;
+ }
+
+ g_free (data);
+}
+
+static gboolean
+process_queue (CkVtMonitor *vt_monitor)
+{
+ int i;
+ int queue_length;
+ EventData *data;
+ EventData *d;
+
+ g_async_queue_lock (vt_monitor->priv->event_queue);
+
+ ck_debug ("Processing VT event queue");
+
+ queue_length = g_async_queue_length_unlocked (vt_monitor->priv->event_queue);
+ data = NULL;
+
+ G_LOCK (hash_lock);
+
+ /* compress events in the queue */
+ for (i = 0; i < queue_length; i++) {
+ d = g_async_queue_try_pop_unlocked (vt_monitor->priv->event_queue);
+ if (d == NULL) {
+ continue;
+
+ }
+
+ if (data != NULL) {
+ ck_debug ("Compressing queue; skipping event for VT %d", data->num);
+ event_data_free (data);
+ }
+
+ data = d;
+ }
+
+ G_UNLOCK (hash_lock);
+
+ if (data != NULL) {
+ change_active_num (vt_monitor, data->num);
+ event_data_free (data);
+ }
+
+ G_LOCK (schedule_lock);
+ vt_monitor->priv->process_queue_id = 0;
+ G_UNLOCK (schedule_lock);
+
+ g_async_queue_unlock (vt_monitor->priv->event_queue);
return FALSE;
}
+static void
+schedule_process_queue (CkVtMonitor *vt_monitor)
+{
+ G_LOCK (schedule_lock);
+ if (vt_monitor->priv->process_queue_id == 0) {
+ vt_monitor->priv->process_queue_id = g_idle_add ((GSourceFunc)process_queue, vt_monitor);
+ }
+ G_UNLOCK (schedule_lock);
+}
+
static void *
vt_thread_start (ThreadData *data)
{
@@ -182,7 +264,7 @@ vt_thread_start (ThreadData *data)
ck_debug ("VT_WAITACTIVE for vt %d", num);
ret = ioctl (vt_monitor->priv->vfd, VT_WAITACTIVE, num);
- ck_debug ("VT_WAITACTIVE returned %d", ret);
+ ck_debug ("VT_WAITACTIVE for vt %d returned %d", num, ret);
if (ret == ERROR) {
@@ -202,52 +284,67 @@ vt_thread_start (ThreadData *data)
}
g_free (data);
-
- if (vt_monitor->priv->vt_thread_hash != NULL) {
- g_hash_table_remove (vt_monitor->priv->vt_thread_hash, GUINT_TO_POINTER (num));
- }
} else {
- g_idle_add ((GSourceFunc)vt_activated, data);
+ EventData *event;
+
+ /* add event to queue */
+ event = g_new0 (EventData, 1);
+ event->num = num;
+ ck_debug ("Pushing activation event for VT %d onto queue", num);
+
+ g_async_queue_push (vt_monitor->priv->event_queue, event);
+
+ /* schedule processing of queue */
+ schedule_process_queue (vt_monitor);
}
+ G_LOCK (hash_lock);
+ if (vt_monitor->priv->vt_thread_hash != NULL) {
+ g_hash_table_remove (vt_monitor->priv->vt_thread_hash, GUINT_TO_POINTER (num));
+ }
+ G_UNLOCK (hash_lock);
+
g_thread_exit (NULL);
+ thread_data_free (data);
return NULL;
}
-static guint
-get_active_native (CkVtMonitor *vt_monitor)
+static void
+vt_add_watch_unlocked (CkVtMonitor *vt_monitor,
+ gint32 num)
{
- int ret;
- struct vt_stat stat;
+ GThread *thread;
+ GError *error;
+ ThreadData *data;
+ gpointer id;
- ret = ioctl (vt_monitor->priv->vfd, VT_GETSTATE, &stat);
- if (ret == ERROR) {
- perror ("ioctl VT_GETSTATE");
- return -1;
- }
+ data = g_new0 (ThreadData, 1);
+ data->num = num;
+ data->vt_monitor = vt_monitor;
- {
- int i;
+ ck_debug ("Creating thread for vt %d", num);
- ck_debug ("Current VT: tty%d", stat.v_active);
- for (i = 1; i <= 16; i++) {
- gboolean is_on;
- is_on = stat.v_state & (1 << i);
+ id = GINT_TO_POINTER (num);
- ck_debug ("VT %d:%s", i, is_on ? "on" : "off");
- }
+ error = NULL;
+ thread = g_thread_create_full ((GThreadFunc)vt_thread_start, data, 65536, FALSE, TRUE, G_THREAD_PRIORITY_NORMAL, &error);
+ if (thread == NULL) {
+ ck_debug ("Unable to create thread: %s", error->message);
+ g_error_free (error);
+ } else {
+ g_hash_table_insert (vt_monitor->priv->vt_thread_hash, id, thread);
}
-
- return stat.v_active;
}
static void
-watch_vts (CkVtMonitor *vt_monitor)
+vt_add_watches (CkVtMonitor *vt_monitor)
{
int i;
gint32 current_num;
+ G_LOCK (hash_lock);
+
current_num = vt_monitor->priv->active_num;
for (i = 1; i < MAX_NR_CONSOLES; i++) {
@@ -259,27 +356,41 @@ watch_vts (CkVtMonitor *vt_monitor)
}
id = GINT_TO_POINTER (i);
+
+ /* add a watch to all other VTs that don't have threads */
if (g_hash_table_lookup (vt_monitor->priv->vt_thread_hash, id) == NULL) {
- GThread *thread;
- GError *error;
- ThreadData *data;
-
- data = g_new0 (ThreadData, 1);
- data->num = i;
- data->vt_monitor = vt_monitor;
-
- ck_debug ("Creating thread for vt %d", i);
-
- error = NULL;
- thread = g_thread_create_full ((GThreadFunc)vt_thread_start, data, 65536, FALSE, TRUE, G_THREAD_PRIORITY_NORMAL, &error);
- if (thread == NULL) {
- ck_debug ("Unable to create thread: %s", error->message);
- g_error_free (error);
- } else {
- g_hash_table_insert (vt_monitor->priv->vt_thread_hash, id, thread);
- }
+ vt_add_watch_unlocked (vt_monitor, i);
}
}
+
+ G_UNLOCK (hash_lock);
+}
+
+static guint
+get_active_native (CkVtMonitor *vt_monitor)
+{
+ int ret;
+ struct vt_stat stat;
+
+ ret = ioctl (vt_monitor->priv->vfd, VT_GETSTATE, &stat);
+ if (ret == ERROR) {
+ perror ("ioctl VT_GETSTATE");
+ return -1;
+ }
+
+ {
+ int i;
+
+ ck_debug ("Current VT: tty%d", stat.v_active);
+ for (i = 1; i <= 16; i++) {
+ gboolean is_on;
+ is_on = stat.v_state & (1 << i);
+
+ ck_debug ("VT %d:%s", i, is_on ? "on" : "off");
+ }
+ }
+
+ return stat.v_active;
}
static void
@@ -317,12 +428,13 @@ ck_vt_monitor_init (CkVtMonitor *vt_monitor)
g_critical ("Unable to open console: %s", g_strerror (errno));
}
+ vt_monitor->priv->event_queue = g_async_queue_new ();
vt_monitor->priv->vfd = fd;
vt_monitor->priv->vt_thread_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
vt_monitor->priv->active_num = get_active_native (vt_monitor);
- watch_vts (vt_monitor);
+ vt_add_watches (vt_monitor);
}
static void
@@ -337,7 +449,17 @@ ck_vt_monitor_finalize (GObject *object)
g_return_if_fail (vt_monitor->priv != NULL);
- g_hash_table_destroy (vt_monitor->priv->vt_thread_hash);
+ if (vt_monitor->priv->process_queue_id > 0) {
+ g_source_remove (vt_monitor->priv->process_queue_id);
+ }
+
+ if (vt_monitor->priv->event_queue != NULL) {
+ g_async_queue_unref (vt_monitor->priv->event_queue);
+ }
+
+ if (vt_monitor->priv->vt_thread_hash != NULL) {
+ g_hash_table_destroy (vt_monitor->priv->vt_thread_hash);
+ }
close (vt_monitor->priv->vfd);