Add support for abstract unix socket addresses
[platform/upstream/glib.git] / gio / gthreadedsocketservice.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright © 2009 Codethink Limited
4  * Copyright © 2009 Red Hat, Inc
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published
8  * by the Free Software Foundation; either version 2 of the licence or (at
9  * your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General
17  * Public License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *
21  * Authors: Ryan Lortie <desrt@desrt.ca>
22  *          Alexander Larsson <alexl@redhat.com>
23  */
24
25 /**
26  * SECTION: gthreadedsocketservice
27  * @title: GThreadedSocketService
28  * @short_description: a threaded #GSocketService
29  * @see_also: #GSocketService.
30  *
31  * A #GThreadedSocketService is a simple subclass of #GSocketService
32  * that handles incoming connections by creating a worker thread and
33  * dispatching the connection to it by emitting the ::run signal in
34  * the new thread.
35  *
36  * The signal handler may perform blocking IO and need not return
37  * until the connection is closed.
38  *
39  * The service is implemented using a thread pool, so there is a
40  * limited amount of threads availible to serve incomming requests.
41  * The service automatically stops the #GSocketService from accepting
42  * new connections when all threads are busy.
43  *
44  * As with #GSocketService, you may connect to ::run, or subclass and
45  * override the default handler.
46  */
47
48 #include "config.h"
49 #include "gsocketconnection.h"
50 #include "gthreadedsocketservice.h"
51
52 #include "gio-marshal.h"
53
54 #include "gioalias.h"
55
56 static guint g_threaded_socket_service_run_signal;
57
58 G_DEFINE_TYPE (GThreadedSocketService,
59                g_threaded_socket_service,
60                G_TYPE_SOCKET_SERVICE);
61
62 G_LOCK_DEFINE_STATIC(job_count);
63
64 struct _GThreadedSocketServicePrivate
65 {
66   GThreadPool *thread_pool;
67   int max_threads;
68   gint job_count;
69 };
70
71 typedef struct
72 {
73   GThreadedSocketService *service;
74   GSocketConnection *connection;
75   GObject *source_object;
76 } GThreadedSocketServiceData;
77
78 static void
79 g_threaded_socket_service_func (gpointer _data,
80                                 gpointer user_data)
81 {
82   GThreadedSocketService *threaded = user_data;
83   GThreadedSocketServiceData *data = _data;
84   gboolean result;
85
86   g_signal_emit (data->service, g_threaded_socket_service_run_signal,
87                  0, data->connection, data->source_object, &result);
88
89   g_object_unref (data->service);
90   g_object_unref (data->connection);
91   if (data->source_object)
92     g_object_unref (data->source_object);
93   g_slice_free (GThreadedSocketServiceData, data);
94
95   G_LOCK (job_count);
96   if (threaded->priv->job_count-- == threaded->priv->max_threads)
97     g_socket_service_start (G_SOCKET_SERVICE (threaded));
98   G_UNLOCK (job_count);
99 }
100
101 static gboolean
102 g_threaded_socket_service_incoming (GSocketService    *service,
103                                     GSocketConnection *connection,
104                                     GObject           *source_object)
105 {
106   GThreadedSocketService *threaded;
107   GThreadedSocketServiceData *data;
108
109   threaded = G_THREADED_SOCKET_SERVICE (service);
110
111   data = g_slice_new (GThreadedSocketServiceData);
112   data->service = g_object_ref (service);
113   data->connection = g_object_ref (connection);
114   if (source_object)
115     data->source_object = g_object_ref (source_object);
116   else
117     data->source_object = NULL;
118
119   G_LOCK (job_count);
120   if (++threaded->priv->job_count == threaded->priv->max_threads)
121     g_socket_service_stop (service);
122   G_UNLOCK (job_count);
123
124   g_thread_pool_push (threaded->priv->thread_pool, data, NULL);
125
126
127
128   return FALSE;
129 }
130
131 static void
132 g_threaded_socket_service_init (GThreadedSocketService *service)
133 {
134   service->priv = G_TYPE_INSTANCE_GET_PRIVATE (service,
135                                                G_TYPE_THREADED_SOCKET_SERVICE,
136                                                GThreadedSocketServicePrivate);
137   service->priv->max_threads = 10;
138 }
139
140 static void
141 g_threaded_socket_service_constructed (GObject *object)
142 {
143   GThreadedSocketService *service = G_THREADED_SOCKET_SERVICE (object);
144
145   service->priv->thread_pool =
146     g_thread_pool_new  (g_threaded_socket_service_func,
147                         service,
148                         service->priv->max_threads,
149                         FALSE,
150                         NULL);
151 }
152
153
154 static void
155 g_threaded_socket_service_finalize (GObject *object)
156 {
157   GThreadedSocketService *service = G_THREADED_SOCKET_SERVICE (object);
158
159   g_object_unref (service->priv->thread_pool);
160
161   G_OBJECT_CLASS (g_threaded_socket_service_parent_class)
162     ->finalize (object);
163 }
164
165
166 static void
167 g_threaded_socket_service_class_init (GThreadedSocketServiceClass *class)
168 {
169   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
170   GSocketServiceClass *ss_class = &class->parent_class;
171
172   g_type_class_add_private (class, sizeof (GThreadedSocketServicePrivate));
173
174   gobject_class->constructed = g_threaded_socket_service_constructed;
175   gobject_class->finalize = g_threaded_socket_service_finalize;
176
177   ss_class->incoming = g_threaded_socket_service_incoming;
178
179   /**
180    * GThreadedSocketService::run:
181    * @service: the #GThreadedSocketService.
182    * @connection: a new #GSocketConnection object.
183    * @source_object: the source_object passed to g_socket_listener_add_address().
184    * @returns: %TRUE if @connection has been handled.
185    *
186    * The ::run signal is emitted in a worker thread in response to an
187    * incoming connection.  This thread is dedicated to handling
188    * @connection and may perform blocking IO.  The signal handler need
189    * not return until the connection is closed.
190    *
191    * If %TRUE is returned then no other handlers are called.
192    **/
193   g_threaded_socket_service_run_signal =
194     g_signal_new ("run", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST,
195                   G_STRUCT_OFFSET (GThreadedSocketServiceClass, run),
196                   g_signal_accumulator_true_handled, NULL,
197                   _gio_marshal_BOOLEAN__OBJECT_OBJECT, G_TYPE_BOOLEAN,
198                   2, G_TYPE_SOCKET_CONNECTION, G_TYPE_OBJECT);
199 }
200
201 /**
202  * g_threaded_socket_service_new:
203  * @returns: a new #GSocketService.
204  * @max_threads: the maximal number of threads to execute concurrently
205  *   handling incomming clients, -1 means no limit
206  *
207  * Creates a new #GThreadedSocketService with no listeners.  Listeners
208  * must be added with g_socket_service_add_listeners().
209  **/
210 GSocketService *
211 g_threaded_socket_service_new (int max_threads)
212 {
213   return g_object_new (G_TYPE_THREADED_SOCKET_SERVICE, NULL);
214 }
215
216 #define __G_THREADED_SOCKET_SERVICE_C__
217 #include "gioaliasdef.c"