Back to index

unity  6.0.0
ubus-server.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
00002 /*
00003  * u-bus-server.c
00004  * Copyright (C) 2010 Canonical, Ltd.
00005  *
00006  * This library is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU Lesser General Public License
00008  * version 3.0 as published by the Free Software Foundation.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU Lesser General Public License version 3.0 for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public
00016  * License along with this library. If not, see
00017  * <http://www.gnu.org/licenses/>.
00018  *
00019  * Authored by Gordon Allott <gord.allott@canonical.com>
00020  */
00021 
00022 #include "ubus-server.h"
00023 #include <string.h>
00024 #include <stdlib.h>
00025 #include <glib.h>
00026 
00027 #define UBUS_SERVER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\
00028   UBUS_TYPE_SERVER, \
00029   UBusServerPrivate))
00030 
00031 struct _UBusServerPrivate
00032 {
00033   GHashTable*   message_interest_table;
00034   GHashTable*   dispatch_table;
00035 
00036   GQueue*       message_queue;
00037   GStringChunk* message_names;
00038 
00039   guint         id_sequencial_number;
00040   gboolean      message_pump_queued;
00041 };
00042 
00043 
00044 G_DEFINE_TYPE(UBusServer, ubus_server, G_TYPE_INITIALLY_UNOWNED);
00045 
00046 struct _UBusDispatchInfo
00047 {
00048   guint         id;
00049   UBusCallback  callback;
00050   gchar*        message;
00051   gpointer      user_data;
00052 };
00053 typedef struct _UBusDispatchInfo UBusDispatchInfo;
00054 
00055 UBusDispatchInfo*
00056 ubus_dispatch_info_new(UBusServer*   server,
00057                        const gchar*  message,
00058                        UBusCallback  callback,
00059                        gpointer      user_data)
00060 {
00061   g_return_val_if_fail(UBUS_IS_SERVER(server), NULL);
00062   UBusServerPrivate* priv = server->priv;
00063   UBusDispatchInfo* info;
00064 
00065   if (priv->id_sequencial_number < 1)
00066   {
00067     g_critical(G_STRLOC ": ID's are overflowing");
00068   }
00069 
00070   info = g_slice_new(UBusDispatchInfo);
00071   info->id = priv->id_sequencial_number++;
00072   info->callback = callback;
00073   info->message = g_string_chunk_insert_const(priv->message_names, message);
00074   info->user_data = user_data;
00075 
00076   return info;
00077 }
00078 
00079 void
00080 ubus_dispatch_info_free(UBusDispatchInfo* info)
00081 {
00082   g_slice_free(UBusDispatchInfo, info);
00083 }
00084 
00085 struct _UBusMessageInfo
00086 {
00087   gchar*     message;
00088   GVariant*  data;
00089 };
00090 
00091 typedef struct _UBusMessageInfo UBusMessageInfo;
00092 
00093 /*
00094  * If @data is floating the constructed message info will
00095  * assume ownership of the ref.
00096  *
00097  * The message member of the UBusMessageInfo struct is managed
00098  * by the UBusServer owning the message. This is done to have
00099  * "interned" strings representing the message names.
00100  *
00101  * Technically the interning is done with g_string_chunk_insert_const().
00102  * This not only gives us imporved memory management, but also allows
00103  * us to compare message names with direct pointer comparison.
00104  */
00105 static UBusMessageInfo*
00106 ubus_message_info_new(GVariant* data)
00107 {
00108   UBusMessageInfo* info;
00109 
00110   info = g_slice_new0(UBusMessageInfo);
00111   info->data = data;
00112 
00113   if (data != NULL)
00114     g_variant_ref_sink(data);
00115 
00116   return info;
00117 }
00118 
00119 static void
00120 ubus_message_info_free(UBusMessageInfo* info)
00121 {
00122   if (info->data != NULL)
00123   {
00124     g_variant_unref(info->data);
00125     info->data = NULL;
00126   }
00127 
00128   g_slice_free(UBusMessageInfo, info);
00129 }
00130 
00131 static void
00132 ubus_server_init(UBusServer* server)
00133 {
00134   UBusServerPrivate* priv;
00135 
00136   priv = server->priv = UBUS_SERVER_GET_PRIVATE(server);
00137 
00138   /* message_interest_table holds the message/DispatchInfo relationship
00139    * We can use g_direct_* hash functions because we are interning all
00140    * message names in our GStringChunk
00141    */
00142   priv->message_interest_table = g_hash_table_new_full(g_direct_hash,
00143                                                        g_direct_equal,
00144                                                        NULL,
00145                                                        (GDestroyNotify) g_sequence_free);
00146   // dispatch_table holds the individial id/DispatchInfo pairs
00147   priv->dispatch_table = g_hash_table_new_full(g_direct_hash,
00148                                                g_direct_equal,
00149                                                NULL,
00150                                                (GDestroyNotify) ubus_dispatch_info_free);
00151 
00152   // for anyone thats wondering (hi kamstrup!), there are two hash tables so
00153   // that lookups are fast when sending messages and removing handlers
00154 
00155   priv->message_queue = g_queue_new();
00156   priv->message_names = g_string_chunk_new(512);
00157   priv->id_sequencial_number = 1;
00158 }
00159 
00160 static void
00161 ubus_server_finalize(GObject* object)
00162 {
00163   UBusServer*        server;
00164   UBusServerPrivate* priv;
00165 
00166   server = UBUS_SERVER(object);
00167   priv = server->priv;
00168 
00169   g_hash_table_destroy(priv->message_interest_table);
00170   g_hash_table_destroy(priv->dispatch_table);
00171 
00172   UBusMessageInfo* info = (UBusMessageInfo*)g_queue_pop_tail(priv->message_queue);
00173   for (; info != NULL; info = (UBusMessageInfo*)g_queue_pop_tail(priv->message_queue))
00174   {
00175     ubus_message_info_free(info);
00176   }
00177 
00178   g_queue_free(priv->message_queue);
00179   g_string_chunk_free(priv->message_names);
00180 
00181   G_OBJECT_CLASS(ubus_server_parent_class)->finalize(object);
00182 }
00183 
00184 static void
00185 ubus_server_class_init(UBusServerClass* klass)
00186 {
00187   GObjectClass* object_class = G_OBJECT_CLASS(klass);
00188   g_type_class_add_private(klass, sizeof(UBusServerPrivate));
00189   object_class->finalize = ubus_server_finalize;
00190 }
00191 
00192 UBusServer*
00193 ubus_server_get_default()
00194 {
00195   UBusServer* server;
00196   static gsize singleton;
00197 
00198   // Ensure GType has been initialized
00199   g_type_init();
00200 
00201   if (g_once_init_enter(&singleton))
00202   {
00203     server = (UBusServer*)g_object_new(UBUS_TYPE_SERVER, NULL);
00204     g_object_ref_sink(server);
00205     g_once_init_leave(&singleton, (gsize) server);
00206   }
00207 
00208   // we actually just want to hold our own reference and not let anything
00209   // else reference us, because we never want to lose that reference, we are
00210   // only allowed to initalise once
00211   return (UBusServer*)singleton;
00212 }
00213 
00214 guint
00215 ubus_server_register_interest(UBusServer*   server,
00216                               const gchar*  message,
00217                               UBusCallback  callback,
00218                               gpointer      user_data)
00219 {
00220   UBusServerPrivate* priv;
00221   GSequence*         dispatch_list;
00222   gchar*             interned_message;
00223   UBusDispatchInfo*  info;
00224 
00225   g_return_val_if_fail(UBUS_IS_SERVER(server), 0);
00226   g_return_val_if_fail(message != NULL, 0);
00227 
00228   priv = server->priv;
00229   interned_message = g_string_chunk_insert_const(priv->message_names, message);
00230   dispatch_list = (GSequence*)g_hash_table_lookup(priv->message_interest_table,
00231                                                   interned_message);
00232 
00233   if (dispatch_list == NULL)
00234   {
00235     // not had this message before so add a new entry to the message_interest table
00236     dispatch_list = g_sequence_new(NULL);  // we use a sequence because its a stable pointer
00237     g_hash_table_insert(priv->message_interest_table,
00238                         interned_message,
00239                         dispatch_list);
00240   }
00241 
00242   // add the callback to the dispatch table
00243   info = ubus_dispatch_info_new(server, message, callback, user_data);
00244   g_hash_table_insert(priv->dispatch_table, GUINT_TO_POINTER(info->id), info);
00245 
00246   // add the dispatch info to the dispatch list in the message interest table
00247   g_sequence_append(dispatch_list, info);
00248 
00249   return info->id;
00250 }
00251 
00252 static gboolean
00253 ubus_server_pump_message_queue(UBusServer* server)
00254 {
00255   g_return_val_if_fail(UBUS_IS_SERVER(server), FALSE);
00256   UBusServerPrivate* priv = server->priv;
00257   UBusMessageInfo* info;
00258 
00259   priv->message_pump_queued = FALSE;
00260 
00261   // loop through each message queued and call the dispatch functions associated
00262   // with it. something in the back of my mind says it would be quicker in some
00263   // situations to sort the queue first so that duplicate messages can re-use
00264   // the same dispatch_list lookups.. but thats a specific case.
00265 
00266   info = (UBusMessageInfo*)g_queue_pop_tail(priv->message_queue);
00267   for (; info != NULL; info = (UBusMessageInfo*)g_queue_pop_tail(priv->message_queue))
00268   {
00269     GSequence* dispatch_list;
00270     dispatch_list = (GSequence*)g_hash_table_lookup(priv->message_interest_table,
00271                                                     info->message);
00272 
00273     if (dispatch_list == NULL)
00274     {
00275       ubus_message_info_free(info);
00276       continue; // no handlers for this message
00277     }
00278 
00279     GSequenceIter* iter = g_sequence_get_begin_iter(dispatch_list);
00280     GSequenceIter* end = g_sequence_get_end_iter(dispatch_list);
00281 
00282     while (iter != end)
00283     {
00284       GSequenceIter* next = g_sequence_iter_next(iter);
00285       UBusDispatchInfo* dispatch_info = (UBusDispatchInfo*)g_sequence_get(iter);
00286       UBusCallback callback = dispatch_info->callback;
00287 
00288       (*callback)(info->data, dispatch_info->user_data);
00289 
00290       iter = next;
00291     }
00292 
00293     ubus_message_info_free(info);
00294   }
00295 
00296   return FALSE;
00297 }
00298 
00299 static void
00300 ubus_server_queue_message_pump(UBusServer* server)
00301 {
00302   UBusServerPrivate* priv;
00303 
00304   g_return_if_fail(UBUS_IS_SERVER(server));
00305 
00306   priv = server->priv;
00307   if (priv->message_pump_queued)
00308     return;
00309 
00310   g_idle_add((GSourceFunc)ubus_server_pump_message_queue, server);
00311   priv->message_pump_queued = TRUE;
00312 }
00313 
00314 void
00315 ubus_server_send_message(UBusServer*  server,
00316                          const gchar* message,
00317                          GVariant*    data)
00318 {
00319   UBusServerPrivate* priv;
00320   UBusMessageInfo*   message_info;
00321 
00322   g_return_if_fail(UBUS_IS_SERVER(server));
00323   g_return_if_fail(message != NULL);
00324 
00325   priv = server->priv;
00326   message_info = ubus_message_info_new(data);
00327   message_info->message = g_string_chunk_insert_const(priv->message_names,
00328                                                       message);
00329 
00330   g_queue_push_head(priv->message_queue, message_info);
00331   ubus_server_queue_message_pump(server);
00332 }
00333 
00334 void
00335 ubus_server_unregister_interest(UBusServer* server, guint handle)
00336 {
00337   UBusServerPrivate* priv;
00338   GSequence*         dispatch_list;
00339   UBusDispatchInfo*  info;
00340 
00341   g_return_if_fail(UBUS_IS_SERVER(server));
00342   g_return_if_fail(handle > 0);
00343 
00344   priv = server->priv;
00345   info = (UBusDispatchInfo*)g_hash_table_lookup(priv->dispatch_table, GUINT_TO_POINTER(handle));
00346 
00347   if (info == NULL)
00348   {
00349     g_warning(G_STRLOC ": Handle %u does not exist", handle);
00350     return;
00351   }
00352 
00353   // now the slightly sucky bit, we have to remove from our message-interest
00354   // table, but we can only find it by itterating through a sequence
00355   // but this is not so bad because we know *which* sequence its in
00356 
00357   dispatch_list = (GSequence*)g_hash_table_lookup(priv->message_interest_table,
00358                                                   info->message);
00359 
00360   if (dispatch_list == NULL)
00361   {
00362     g_critical(G_STRLOC ": Handle exists but not dispatch list, ubus has "\
00363                "become unstable");
00364     return;
00365   }
00366 
00367   GSequenceIter* iter = g_sequence_get_begin_iter(dispatch_list);
00368   GSequenceIter* end = g_sequence_get_end_iter(dispatch_list);
00369   while (iter != end)
00370   {
00371     GSequenceIter* next = g_sequence_iter_next(iter);
00372     UBusDispatchInfo* info_test = (UBusDispatchInfo*)g_sequence_get(iter);
00373 
00374     if (info_test->id == handle)
00375     {
00376       g_sequence_remove(iter);
00377     }
00378 
00379     iter = next;
00380   }
00381 
00382   if (g_sequence_get_length(dispatch_list) == 0)
00383   {
00384     // free the key/value pair
00385     g_hash_table_remove(priv->message_interest_table, info->message);
00386   }
00387 
00388   // finally remove the dispatch_table hash table.
00389   g_hash_table_remove(priv->dispatch_table, &handle);
00390 
00391 }
00392 
00393 void
00394 ubus_server_force_message_pump(UBusServer* server)
00395 {
00396   ubus_server_pump_message_queue(server);
00397 }