X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=wui_thread.c;fp=wui_thread.c;h=0db43c8829a41ba06a239ad0eddfe6917597be81;hb=7e9794277b619bb232ffc874ee7efa4ead8ddb8e;hp=0000000000000000000000000000000000000000;hpb=7389cc56d30d68a5ca9c57bbf691f4bf05d1a2f9;p=ovirt-viewer.git diff --git a/wui_thread.c b/wui_thread.c new file mode 100644 index 0000000..0db43c8 --- /dev/null +++ b/wui_thread.c @@ -0,0 +1,389 @@ +/* ovirt viewer console application + * Copyright (C) 2008 Red Hat Inc. + * Written by Richard W.M. Jones + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include +#include + +#include + +#include "internal.h" + +/* Private functions. */ +static gpointer wui_thread (gpointer data); +static void wui_thread_send_quit (void); +static gboolean do_connect (void); +static gboolean do_login (void); +static void refresh_vm_list (void); + +/* Messages (main thread -> WUI thread only). + * + * These are passed by reference. They are allocated by the sender + * (ie. the main thread) and freed by the receiver (ie. the WUI thread). + */ +enum message_type { + QUIT, /* Tell the WUI thread to quit cleanly. */ + CONNECT, /* Tell to connect (just fetch the URI). */ + DISCONNECT, /* Tell to disconnect, forget state. */ + LOGIN, /* Tell to login, and pass credentials. */ + REFRESH_VM_LIST, /* Tell to refresh the VM list right away. */ +}; + +struct message { + enum message_type type; + char *str1; + char *str2; +}; + +/* Start the WUI thread. See main() for explanation of the threading model. */ +static GThread *wui_gthread = NULL; +static GAsyncQueue *wui_thread_queue = NULL; + +void +start_wui_thread (void) +{ + GError *error = NULL; + + DEBUG ("starting the WUI thread"); + + /* Create the message queue for main -> WUI thread communications. */ + wui_thread_queue = g_async_queue_new (); + + wui_gthread = g_thread_create (wui_thread, wui_thread_queue, TRUE, &error); + if (error) { + g_print ("%s\n", error->message); + g_error_free (error); + exit (1); + } +} + +void +stop_wui_thread (void) +{ + DEBUG ("stopping the WUI thread"); + assert (wui_gthread); + + /* Send a quit message then wait for the WUI thread to join. + * + * This "nice" shutdown could cause the UI to hang for an + * indefinite period (eg. if the WUI thread is engaged in some + * long connection or download from the remote server). But + * I want to keep it this way for the moment so that we can + * diagnose problems with the WUI thread. + * + * (This could be solved with some sort of interruptible + * join, but glib doesn't support that AFAICT). + */ + wui_thread_send_quit (); + (void) g_thread_join (wui_gthread); + g_async_queue_unref (wui_thread_queue); + wui_gthread = NULL; +} + +/* Send the quit message to the WUI thread. */ +static void +wui_thread_send_quit (void) +{ + struct message *msg; + + msg = g_new (struct message, 1); + msg->type = QUIT; + g_async_queue_push (wui_thread_queue, msg); +} + +/* Send the connect message to the WUI thread. */ +void +wui_thread_send_connect (const char *uri) +{ + struct message *msg; + + msg = g_new (struct message, 1); + msg->type = CONNECT; + msg->str1 = g_strdup (uri); + g_async_queue_push (wui_thread_queue, msg); +} + +/* Send the disconnect message to the WUI thread. */ +void +wui_thread_send_disconnect (void) +{ + struct message *msg; + + msg = g_new (struct message, 1); + msg->type = DISCONNECT; + g_async_queue_push (wui_thread_queue, msg); +} + +/* Send the login message to the WUI thread. */ +void +wui_thread_send_login (const char *username, const char *password) +{ + struct message *msg; + + msg = g_new (struct message, 1); + msg->type = LOGIN; + msg->str1 = g_strdup (username); + msg->str2 = g_strdup (password); + g_async_queue_push (wui_thread_queue, msg); +} + +/* Send the refresh VM list message to the WUI thread. */ +void +wui_thread_send_refresh_vm_list (void) +{ + struct message *msg; + + msg = g_new (struct message, 1); + msg->type = REFRESH_VM_LIST; + g_async_queue_push (wui_thread_queue, msg); +} + +/* The current state. + * + * For safety, the main thread must lock this before reading, and the + * WUI thread must lock this before writing. However the WUI thread + * does not need to lock before reading, because no other thread + * can modify it. + */ +static gboolean connected = FALSE; +static gboolean logged_in = FALSE; +static gboolean busy = FALSE; +static GStaticMutex state_mutex; + +static void set_connected (gboolean); +static void set_logged_in (gboolean); +static void set_busy (gboolean); + +/* The private state of the WUI thread. */ +static int secs_between_refresh = 60; +static char *uri = NULL; +static char *username = NULL; +static char *password = NULL; + +static gboolean process_message (struct message *); + +/* The WUI thread. See main() above for explanation of + * the threading model. + */ +static gpointer +wui_thread (gpointer _queue) +{ + GAsyncQueue *queue = (GAsyncQueue *) _queue; + gboolean quit = FALSE; + GTimeVal tv; + gpointer _msg; + struct message *msg; + + DEBUG ("WUI thread starting up"); + + g_async_queue_ref (queue); + + /* In the thread's loop we check for new instructions from the main + * thread and carry them out. Also, if we are connected and logged + * in then we periodically recheck the list of VMs. + */ + while (!quit) { + set_busy (FALSE); + + if (logged_in) { + g_get_current_time (&tv); + g_time_val_add (&tv, secs_between_refresh * 1000000); + _msg = g_async_queue_timed_pop (queue, &tv); + } else + _msg = g_async_queue_pop (queue); + + set_busy (TRUE); + + msg = (struct message *) _msg; + + if (msg) { + DEBUG ("received message %d", msg->type); + quit = process_message (msg); + /* Don't free any strings in the message - we've saved them. */ + g_free (msg); + } else { + /* No message, must have got a timeout instead, which means + * we are logged in and we should refresh the list of VMs. + * Note it's not an error if we temporarily lose contact + * with the WUI. + */ + refresh_vm_list (); + } + } + + DEBUG ("WUI thread shutting down cleanly"); + + g_async_queue_unref (queue); + g_thread_exit (NULL); + return NULL; /* not reached? */ +} + +/* The WUI thread calls this to safely update the state variables. + * This also updates elements in the UI by setting idle callbacks + * which are executed in the context of the main thread. + */ +static void +set_connected (gboolean new_connected) +{ + g_static_mutex_lock (&state_mutex); + connected = new_connected; + g_static_mutex_unlock (&state_mutex); +} + +static void +set_logged_in (gboolean new_logged_in) +{ + g_static_mutex_lock (&state_mutex); + logged_in = new_logged_in; + g_static_mutex_unlock (&state_mutex); +} + +static void +set_busy (gboolean new_busy) +{ + g_static_mutex_lock (&state_mutex); + busy = new_busy; + g_static_mutex_unlock (&state_mutex); +} + +/* The main thread should call these functions to get the WUI thread's + * current state. + */ +gboolean +wui_thread_is_connected (void) +{ + gboolean ret; + + g_static_mutex_lock (&state_mutex); + ret = connected; + g_static_mutex_unlock (&state_mutex); + return ret; +} + +gboolean +wui_thread_is_logged_in (void) +{ + gboolean ret; + + g_static_mutex_lock (&state_mutex); + ret = logged_in; + g_static_mutex_unlock (&state_mutex); + return ret; +} + +gboolean +wui_thread_is_busy (void) +{ + gboolean ret; + + g_static_mutex_lock (&state_mutex); + ret = busy; + g_static_mutex_unlock (&state_mutex); + return ret; +} + +/* Process a message from the main thread. */ +static gboolean +process_message (struct message *msg) +{ + switch (msg->type) { + case QUIT: + if (uri) g_free (uri); + if (username) g_free (username); + if (password) g_free (password); + set_connected (FALSE); + set_logged_in (FALSE); + return 1; + + case CONNECT: + if (uri) g_free (uri); + uri = msg->str1; + + if (do_connect ()) { + set_connected (TRUE); + } else { + set_connected (FALSE); + set_logged_in (FALSE); + } + break; + + case DISCONNECT: + /* This just forgets the state. REST is connectionless. */ + if (uri) g_free (uri); + if (username) g_free (username); + if (password) g_free (password); + set_connected (FALSE); + set_logged_in (FALSE); + break; + + case LOGIN: + if (username) g_free (username); + username = msg->str1; + if (password) g_free (password); + password = msg->str2; + + if (connected) { + if (do_login ()) { + set_logged_in (TRUE); + refresh_vm_list (); + } else { + set_logged_in (FALSE); + } + } + break; + + case REFRESH_VM_LIST: + refresh_vm_list (); + secs_between_refresh = 60; + break; + + default: + DEBUG ("unknown message type %d", msg->type); + abort (); + } + + return 0; +} + +/* Try to connect to the current URI. Returns true on success. */ +static gboolean +do_connect (void) +{ + DEBUG ("connecting to uri %s", uri); + return FALSE; +} + +/* Try to login to URI/login with the username and password given. + * Returns true on success. + */ +static gboolean +do_login (void) +{ + DEBUG ("logging in with username %s, password *****", username); + return FALSE; +} + +/* Refresh the list of VMs. */ +static void +refresh_vm_list (void) +{ + DEBUG ("refreshing list of VMs"); +}