| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  | /* GIO - GLib Input, Output and Streaming Library
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright © 2009 Codethink Limited | 
					
						
							|  |  |  |  * Copyright © 2009 Red Hat, Inc | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU Lesser General Public License as published | 
					
						
							|  |  |  |  * by the Free Software Foundation; either version 2 of the licence or (at | 
					
						
							|  |  |  |  * your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This library 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 | 
					
						
							|  |  |  |  * Lesser General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU Lesser General | 
					
						
							|  |  |  |  * Public License along with this library; if not, write to the | 
					
						
							|  |  |  |  * Free Software Foundation, Inc., 59 Temple Place, Suite 330, | 
					
						
							|  |  |  |  * Boston, MA 02111-1307, USA. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Authors: Ryan Lortie <desrt@desrt.ca> | 
					
						
							|  |  |  |  *          Alexander Larsson <alexl@redhat.com> | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2011-02-01 16:17:23 -02:00
										 |  |  |  * SECTION:gthreadedsocketservice | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  |  * @title: GThreadedSocketService | 
					
						
							| 
									
										
										
										
											2009-05-28 00:30:21 -04:00
										 |  |  |  * @short_description: A threaded GSocketService | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  |  * @see_also: #GSocketService. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * A #GThreadedSocketService is a simple subclass of #GSocketService | 
					
						
							|  |  |  |  * that handles incoming connections by creating a worker thread and | 
					
						
							| 
									
										
										
										
											2011-06-04 18:44:44 -04:00
										 |  |  |  * dispatching the connection to it by emitting the | 
					
						
							|  |  |  |  * #GThreadedSocketService::run signal in the new thread. | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  |  * | 
					
						
							|  |  |  |  * The signal handler may perform blocking IO and need not return | 
					
						
							|  |  |  |  * until the connection is closed. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The service is implemented using a thread pool, so there is a | 
					
						
							| 
									
										
										
										
											2011-04-20 19:08:06 +02:00
										 |  |  |  * limited amount of threads available to serve incoming requests. | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  |  * The service automatically stops the #GSocketService from accepting | 
					
						
							|  |  |  |  * new connections when all threads are busy. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2011-06-04 18:44:44 -04:00
										 |  |  |  * As with #GSocketService, you may connect to #GThreadedSocketService::run, | 
					
						
							| 
									
										
										
										
											2009-05-27 18:20:08 -04:00
										 |  |  |  * or subclass and override the default handler. | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "config.h"
 | 
					
						
							|  |  |  | #include "gsocketconnection.h"
 | 
					
						
							|  |  |  | #include "gthreadedsocketservice.h"
 | 
					
						
							| 
									
										
										
										
											2009-05-20 11:28:27 +02:00
										 |  |  | #include "glibintl.h"
 | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static guint g_threaded_socket_service_run_signal; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | G_DEFINE_TYPE (GThreadedSocketService, | 
					
						
							| 
									
										
										
										
											2009-05-20 11:28:27 +02:00
										 |  |  | 	       g_threaded_socket_service, | 
					
						
							|  |  |  | 	       G_TYPE_SOCKET_SERVICE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   PROP_0, | 
					
						
							|  |  |  |   PROP_MAX_THREADS | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | G_LOCK_DEFINE_STATIC(job_count); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct _GThreadedSocketServicePrivate | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   GThreadPool *thread_pool; | 
					
						
							|  |  |  |   int max_threads; | 
					
						
							|  |  |  |   gint job_count; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   GThreadedSocketService *service; | 
					
						
							|  |  |  |   GSocketConnection *connection; | 
					
						
							|  |  |  |   GObject *source_object; | 
					
						
							|  |  |  | } GThreadedSocketServiceData; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | g_threaded_socket_service_func (gpointer _data, | 
					
						
							|  |  |  | 				gpointer user_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   GThreadedSocketService *threaded = user_data; | 
					
						
							|  |  |  |   GThreadedSocketServiceData *data = _data; | 
					
						
							|  |  |  |   gboolean result; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   g_signal_emit (data->service, g_threaded_socket_service_run_signal, | 
					
						
							|  |  |  |                  0, data->connection, data->source_object, &result); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   g_object_unref (data->service); | 
					
						
							|  |  |  |   g_object_unref (data->connection); | 
					
						
							|  |  |  |   if (data->source_object) | 
					
						
							|  |  |  |     g_object_unref (data->source_object); | 
					
						
							|  |  |  |   g_slice_free (GThreadedSocketServiceData, data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   G_LOCK (job_count); | 
					
						
							|  |  |  |   if (threaded->priv->job_count-- == threaded->priv->max_threads) | 
					
						
							|  |  |  |     g_socket_service_start (G_SOCKET_SERVICE (threaded)); | 
					
						
							|  |  |  |   G_UNLOCK (job_count); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static gboolean | 
					
						
							|  |  |  | g_threaded_socket_service_incoming (GSocketService    *service, | 
					
						
							|  |  |  |                                     GSocketConnection *connection, | 
					
						
							|  |  |  |                                     GObject           *source_object) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   GThreadedSocketService *threaded; | 
					
						
							|  |  |  |   GThreadedSocketServiceData *data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   threaded = G_THREADED_SOCKET_SERVICE (service); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   data = g_slice_new (GThreadedSocketServiceData); | 
					
						
							|  |  |  |   data->service = g_object_ref (service); | 
					
						
							|  |  |  |   data->connection = g_object_ref (connection); | 
					
						
							|  |  |  |   if (source_object) | 
					
						
							|  |  |  |     data->source_object = g_object_ref (source_object); | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     data->source_object = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   G_LOCK (job_count); | 
					
						
							|  |  |  |   if (++threaded->priv->job_count == threaded->priv->max_threads) | 
					
						
							|  |  |  |     g_socket_service_stop (service); | 
					
						
							|  |  |  |   G_UNLOCK (job_count); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   g_thread_pool_push (threaded->priv->thread_pool, data, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return FALSE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | g_threaded_socket_service_init (GThreadedSocketService *service) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   service->priv = G_TYPE_INSTANCE_GET_PRIVATE (service, | 
					
						
							|  |  |  | 					       G_TYPE_THREADED_SOCKET_SERVICE, | 
					
						
							|  |  |  | 					       GThreadedSocketServicePrivate); | 
					
						
							|  |  |  |   service->priv->max_threads = 10; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | g_threaded_socket_service_constructed (GObject *object) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   GThreadedSocketService *service = G_THREADED_SOCKET_SERVICE (object); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   service->priv->thread_pool = | 
					
						
							|  |  |  |     g_thread_pool_new  (g_threaded_socket_service_func, | 
					
						
							|  |  |  | 			service, | 
					
						
							|  |  |  | 			service->priv->max_threads, | 
					
						
							|  |  |  | 			FALSE, | 
					
						
							|  |  |  | 			NULL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | g_threaded_socket_service_finalize (GObject *object) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   GThreadedSocketService *service = G_THREADED_SOCKET_SERVICE (object); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-30 08:23:46 +02:00
										 |  |  |   g_thread_pool_free (service->priv->thread_pool, FALSE, TRUE); | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   G_OBJECT_CLASS (g_threaded_socket_service_parent_class) | 
					
						
							|  |  |  |     ->finalize (object); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-20 11:28:27 +02:00
										 |  |  | static void | 
					
						
							|  |  |  | g_threaded_socket_service_get_property (GObject    *object, | 
					
						
							|  |  |  | 					guint       prop_id, | 
					
						
							|  |  |  | 					GValue     *value, | 
					
						
							|  |  |  | 					GParamSpec *pspec) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   GThreadedSocketService *service = G_THREADED_SOCKET_SERVICE (object); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   switch (prop_id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       case PROP_MAX_THREADS: | 
					
						
							|  |  |  | 	g_value_set_int (value, service->priv->max_threads); | 
					
						
							|  |  |  | 	break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       default: | 
					
						
							|  |  |  | 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | g_threaded_socket_service_set_property (GObject      *object, | 
					
						
							|  |  |  | 					guint         prop_id, | 
					
						
							|  |  |  | 					const GValue *value, | 
					
						
							|  |  |  | 					GParamSpec   *pspec) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   GThreadedSocketService *service = G_THREADED_SOCKET_SERVICE (object); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   switch (prop_id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       case PROP_MAX_THREADS: | 
					
						
							|  |  |  | 	service->priv->max_threads = g_value_get_int (value); | 
					
						
							|  |  |  | 	break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       default: | 
					
						
							|  |  |  | 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | g_threaded_socket_service_class_init (GThreadedSocketServiceClass *class) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   GObjectClass *gobject_class = G_OBJECT_CLASS (class); | 
					
						
							|  |  |  |   GSocketServiceClass *ss_class = &class->parent_class; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   g_type_class_add_private (class, sizeof (GThreadedSocketServicePrivate)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   gobject_class->constructed = g_threaded_socket_service_constructed; | 
					
						
							|  |  |  |   gobject_class->finalize = g_threaded_socket_service_finalize; | 
					
						
							| 
									
										
										
										
											2009-05-20 11:28:27 +02:00
										 |  |  |   gobject_class->set_property = g_threaded_socket_service_set_property; | 
					
						
							|  |  |  |   gobject_class->get_property = g_threaded_socket_service_get_property; | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   ss_class->incoming = g_threaded_socket_service_incoming; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /**
 | 
					
						
							|  |  |  |    * GThreadedSocketService::run: | 
					
						
							|  |  |  |    * @service: the #GThreadedSocketService. | 
					
						
							|  |  |  |    * @connection: a new #GSocketConnection object. | 
					
						
							|  |  |  |    * @source_object: the source_object passed to g_socket_listener_add_address(). | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * The ::run signal is emitted in a worker thread in response to an | 
					
						
							| 
									
										
										
										
											2009-05-27 18:20:08 -04:00
										 |  |  |    * incoming connection. This thread is dedicated to handling | 
					
						
							|  |  |  |    * @connection and may perform blocking IO. The signal handler need | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  |    * not return until the connection is closed. | 
					
						
							|  |  |  |    * | 
					
						
							| 
									
										
										
										
											2011-04-20 19:08:06 +02:00
										 |  |  |    * Returns: %TRUE to stop further signal handlers from being called | 
					
						
							| 
									
										
										
										
											2009-05-27 18:20:08 -04:00
										 |  |  |    */ | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  |   g_threaded_socket_service_run_signal = | 
					
						
							|  |  |  |     g_signal_new ("run", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, | 
					
						
							| 
									
										
										
										
											2009-05-20 11:28:27 +02:00
										 |  |  | 		  G_STRUCT_OFFSET (GThreadedSocketServiceClass, run), | 
					
						
							|  |  |  | 		  g_signal_accumulator_true_handled, NULL, | 
					
						
							| 
									
										
										
										
											2011-07-19 14:18:10 -03:00
										 |  |  | 		  NULL, G_TYPE_BOOLEAN, | 
					
						
							| 
									
										
										
										
											2009-05-20 11:28:27 +02:00
										 |  |  | 		  2, G_TYPE_SOCKET_CONNECTION, G_TYPE_OBJECT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   g_object_class_install_property (gobject_class, PROP_MAX_THREADS, | 
					
						
							|  |  |  | 				   g_param_spec_int ("max-threads", | 
					
						
							|  |  |  | 						     P_("Max threads"), | 
					
						
							|  |  |  | 						     P_("The max number of threads handling clients for this service"), | 
					
						
							|  |  |  | 						     -1, | 
					
						
							|  |  |  | 						     G_MAXINT, | 
					
						
							|  |  |  | 						     10, | 
					
						
							|  |  |  | 						     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * g_threaded_socket_service_new: | 
					
						
							| 
									
										
										
										
											2009-05-18 08:47:10 +02:00
										 |  |  |  * @max_threads: the maximal number of threads to execute concurrently | 
					
						
							| 
									
										
										
										
											2009-05-27 18:20:08 -04:00
										 |  |  |  *   handling incoming clients, -1 means no limit | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2009-05-27 18:20:08 -04:00
										 |  |  |  * Creates a new #GThreadedSocketService with no listeners. Listeners | 
					
						
							| 
									
										
										
										
											2011-06-06 10:22:40 -04:00
										 |  |  |  * must be added with one of the #GSocketListener "add" methods. | 
					
						
							| 
									
										
										
										
											2009-05-27 18:20:08 -04:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Returns: a new #GSocketService. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Since: 2.22 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  | GSocketService * | 
					
						
							| 
									
										
										
										
											2009-05-18 08:47:10 +02:00
										 |  |  | g_threaded_socket_service_new (int max_threads) | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-20 11:28:27 +02:00
										 |  |  |   return g_object_new (G_TYPE_THREADED_SOCKET_SERVICE, | 
					
						
							|  |  |  | 		       "max-threads", max_threads, | 
					
						
							|  |  |  | 		       NULL); | 
					
						
							| 
									
										
										
										
											2009-05-15 21:26:24 +02:00
										 |  |  | } |