nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 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, see <http://www.gnu.org/licenses/>. |
||
18 | * |
||
19 | * Authors: Ryan Lortie <desrt@desrt.ca> |
||
20 | * Alexander Larsson <alexl@redhat.com> |
||
21 | */ |
||
22 | |||
23 | /** |
||
24 | * SECTION:gthreadedsocketservice |
||
25 | * @title: GThreadedSocketService |
||
26 | * @short_description: A threaded GSocketService |
||
27 | * @include: gio/gio.h |
||
28 | * @see_also: #GSocketService. |
||
29 | * |
||
30 | * A #GThreadedSocketService is a simple subclass of #GSocketService |
||
31 | * that handles incoming connections by creating a worker thread and |
||
32 | * dispatching the connection to it by emitting the |
||
33 | * #GThreadedSocketService::run signal in the new thread. |
||
34 | * |
||
35 | * The signal handler may perform blocking IO and need not return |
||
36 | * until the connection is closed. |
||
37 | * |
||
38 | * The service is implemented using a thread pool, so there is a |
||
39 | * limited amount of threads available to serve incoming requests. |
||
40 | * The service automatically stops the #GSocketService from accepting |
||
41 | * new connections when all threads are busy. |
||
42 | * |
||
43 | * As with #GSocketService, you may connect to #GThreadedSocketService::run, |
||
44 | * or subclass and override the default handler. |
||
45 | */ |
||
46 | |||
47 | #include "config.h" |
||
48 | #include "gsocketconnection.h" |
||
49 | #include "gthreadedsocketservice.h" |
||
50 | #include "glibintl.h" |
||
51 | |||
52 | struct _GThreadedSocketServicePrivate |
||
53 | { |
||
54 | GThreadPool *thread_pool; |
||
55 | int max_threads; |
||
56 | gint job_count; |
||
57 | }; |
||
58 | |||
59 | static guint g_threaded_socket_service_run_signal; |
||
60 | |||
61 | G_DEFINE_TYPE_WITH_PRIVATE (GThreadedSocketService, |
||
62 | g_threaded_socket_service, |
||
63 | G_TYPE_SOCKET_SERVICE) |
||
64 | |||
65 | enum |
||
66 | { |
||
67 | PROP_0, |
||
68 | PROP_MAX_THREADS |
||
69 | }; |
||
70 | |||
71 | G_LOCK_DEFINE_STATIC(job_count); |
||
72 | |||
73 | typedef struct |
||
74 | { |
||
75 | GSocketConnection *connection; |
||
76 | GObject *source_object; |
||
77 | } GThreadedSocketServiceData; |
||
78 | |||
79 | static void |
||
80 | g_threaded_socket_service_func (gpointer _data, |
||
81 | gpointer user_data) |
||
82 | { |
||
83 | GThreadedSocketService *threaded = user_data; |
||
84 | GThreadedSocketServiceData *data = _data; |
||
85 | gboolean result; |
||
86 | |||
87 | g_signal_emit (threaded, g_threaded_socket_service_run_signal, |
||
88 | 0, data->connection, data->source_object, &result); |
||
89 | |||
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 | g_object_unref (threaded); |
||
101 | } |
||
102 | |||
103 | static gboolean |
||
104 | g_threaded_socket_service_incoming (GSocketService *service, |
||
105 | GSocketConnection *connection, |
||
106 | GObject *source_object) |
||
107 | { |
||
108 | GThreadedSocketService *threaded; |
||
109 | GThreadedSocketServiceData *data; |
||
110 | |||
111 | threaded = G_THREADED_SOCKET_SERVICE (service); |
||
112 | |||
113 | data = g_slice_new (GThreadedSocketServiceData); |
||
114 | |||
115 | /* Ref the socket service for the thread */ |
||
116 | g_object_ref (service); |
||
117 | |||
118 | data->connection = g_object_ref (connection); |
||
119 | if (source_object) |
||
120 | data->source_object = g_object_ref (source_object); |
||
121 | else |
||
122 | data->source_object = NULL; |
||
123 | |||
124 | G_LOCK (job_count); |
||
125 | if (++threaded->priv->job_count == threaded->priv->max_threads) |
||
126 | g_socket_service_stop (service); |
||
127 | G_UNLOCK (job_count); |
||
128 | |||
129 | g_thread_pool_push (threaded->priv->thread_pool, data, NULL); |
||
130 | |||
131 | |||
132 | |||
133 | return FALSE; |
||
134 | } |
||
135 | |||
136 | static void |
||
137 | g_threaded_socket_service_init (GThreadedSocketService *service) |
||
138 | { |
||
139 | service->priv = g_threaded_socket_service_get_instance_private (service); |
||
140 | service->priv->max_threads = 10; |
||
141 | } |
||
142 | |||
143 | static void |
||
144 | g_threaded_socket_service_constructed (GObject *object) |
||
145 | { |
||
146 | GThreadedSocketService *service = G_THREADED_SOCKET_SERVICE (object); |
||
147 | |||
148 | service->priv->thread_pool = |
||
149 | g_thread_pool_new (g_threaded_socket_service_func, |
||
150 | service, |
||
151 | service->priv->max_threads, |
||
152 | FALSE, |
||
153 | NULL); |
||
154 | } |
||
155 | |||
156 | |||
157 | static void |
||
158 | g_threaded_socket_service_finalize (GObject *object) |
||
159 | { |
||
160 | GThreadedSocketService *service = G_THREADED_SOCKET_SERVICE (object); |
||
161 | |||
162 | g_thread_pool_free (service->priv->thread_pool, FALSE, FALSE); |
||
163 | |||
164 | G_OBJECT_CLASS (g_threaded_socket_service_parent_class) |
||
165 | ->finalize (object); |
||
166 | } |
||
167 | |||
168 | static void |
||
169 | g_threaded_socket_service_get_property (GObject *object, |
||
170 | guint prop_id, |
||
171 | GValue *value, |
||
172 | GParamSpec *pspec) |
||
173 | { |
||
174 | GThreadedSocketService *service = G_THREADED_SOCKET_SERVICE (object); |
||
175 | |||
176 | switch (prop_id) |
||
177 | { |
||
178 | case PROP_MAX_THREADS: |
||
179 | g_value_set_int (value, service->priv->max_threads); |
||
180 | break; |
||
181 | |||
182 | default: |
||
183 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
||
184 | } |
||
185 | } |
||
186 | |||
187 | static void |
||
188 | g_threaded_socket_service_set_property (GObject *object, |
||
189 | guint prop_id, |
||
190 | const GValue *value, |
||
191 | GParamSpec *pspec) |
||
192 | { |
||
193 | GThreadedSocketService *service = G_THREADED_SOCKET_SERVICE (object); |
||
194 | |||
195 | switch (prop_id) |
||
196 | { |
||
197 | case PROP_MAX_THREADS: |
||
198 | service->priv->max_threads = g_value_get_int (value); |
||
199 | break; |
||
200 | |||
201 | default: |
||
202 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
||
203 | } |
||
204 | } |
||
205 | |||
206 | |||
207 | static void |
||
208 | g_threaded_socket_service_class_init (GThreadedSocketServiceClass *class) |
||
209 | { |
||
210 | GObjectClass *gobject_class = G_OBJECT_CLASS (class); |
||
211 | GSocketServiceClass *ss_class = &class->parent_class; |
||
212 | |||
213 | gobject_class->constructed = g_threaded_socket_service_constructed; |
||
214 | gobject_class->finalize = g_threaded_socket_service_finalize; |
||
215 | gobject_class->set_property = g_threaded_socket_service_set_property; |
||
216 | gobject_class->get_property = g_threaded_socket_service_get_property; |
||
217 | |||
218 | ss_class->incoming = g_threaded_socket_service_incoming; |
||
219 | |||
220 | /** |
||
221 | * GThreadedSocketService::run: |
||
222 | * @service: the #GThreadedSocketService. |
||
223 | * @connection: a new #GSocketConnection object. |
||
224 | * @source_object: the source_object passed to g_socket_listener_add_address(). |
||
225 | * |
||
226 | * The ::run signal is emitted in a worker thread in response to an |
||
227 | * incoming connection. This thread is dedicated to handling |
||
228 | * @connection and may perform blocking IO. The signal handler need |
||
229 | * not return until the connection is closed. |
||
230 | * |
||
231 | * Returns: %TRUE to stop further signal handlers from being called |
||
232 | */ |
||
233 | g_threaded_socket_service_run_signal = |
||
234 | g_signal_new (I_("run"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, |
||
235 | G_STRUCT_OFFSET (GThreadedSocketServiceClass, run), |
||
236 | g_signal_accumulator_true_handled, NULL, |
||
237 | NULL, G_TYPE_BOOLEAN, |
||
238 | 2, G_TYPE_SOCKET_CONNECTION, G_TYPE_OBJECT); |
||
239 | |||
240 | g_object_class_install_property (gobject_class, PROP_MAX_THREADS, |
||
241 | g_param_spec_int ("max-threads", |
||
242 | P_("Max threads"), |
||
243 | P_("The max number of threads handling clients for this service"), |
||
244 | -1, |
||
245 | G_MAXINT, |
||
246 | 10, |
||
247 | G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
||
248 | } |
||
249 | |||
250 | /** |
||
251 | * g_threaded_socket_service_new: |
||
252 | * @max_threads: the maximal number of threads to execute concurrently |
||
253 | * handling incoming clients, -1 means no limit |
||
254 | * |
||
255 | * Creates a new #GThreadedSocketService with no listeners. Listeners |
||
256 | * must be added with one of the #GSocketListener "add" methods. |
||
257 | * |
||
258 | * Returns: a new #GSocketService. |
||
259 | * |
||
260 | * Since: 2.22 |
||
261 | */ |
||
262 | GSocketService * |
||
263 | g_threaded_socket_service_new (int max_threads) |
||
264 | { |
||
265 | return g_object_new (G_TYPE_THREADED_SOCKET_SERVICE, |
||
266 | "max-threads", max_threads, |
||
267 | NULL); |
||
268 | } |