1 /*
2 * GUPnP Simple IGD abstraction
3 *
4 * Copyright 2008 Collabora Ltd.
5 * @author: Olivier Crete <olivier.crete@collabora.co.uk>
6 * Copyright 2008 Nokia Corp.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23
24 /**
25 * SECTION:gupnp-simple-igd-thread
26 * @short_description: Threaded wrapper for GUPnPSimpleIgd
27 *
28 * This wraps a #GUPnPSimpleIgd into a thread so that it can be used without
29 * having a #GMainLoop running.
30 */
31
32
33 #ifdef HAVE_CONFIG_H
34 # include <config.h>
35 #endif
36
37 #include "gupnp-simple-igd-thread.h"
38 #include "gupnp-simple-igd-priv.h"
39
40
41 /**
42 * GUPnPSimpleIgdThreadClass:
43 *
44 * The Raw UDP component transmitter class
45 */
46
47 struct _GUPnPSimpleIgdThreadClass
48 {
49 GUPnPSimpleIgdClass parent_class;
50
51 /*virtual functions */
52 /*< private >*/
53 };
54
55 struct thread_data
56 {
57 gint refcount;
58
59 GMutex mutex;
60
61 GMainContext *context;
62 GMainLoop *loop;
63 gboolean all_mappings_deleted;
64
65 GUPnPSimpleIgdThread *self;
66 };
67
68 struct _GUPnPSimpleIgdThreadPrivate
69 {
70 GThread *thread;
71 GMainContext *context;
72
73 /* Protected by mutex inside thread_data*/
74 gboolean can_dispose;
75 GCond can_dispose_cond;
76
77 struct thread_data *thread_data;
78
79 GPtrArray *add_remove_port_datas;
80 };
81
82
83 #define GUPNP_SIMPLE_IGD_THREAD_GET_PRIVATE(o) \
84 (G_TYPE_INSTANCE_GET_PRIVATE ((o), GUPNP_TYPE_SIMPLE_IGD_THREAD, \
85 GUPnPSimpleIgdThreadPrivate))
86
87 #define GUPNP_SIMPLE_IGD_THREAD_LOCK(o) \
88 g_mutex_lock (&(o)->priv->thread_data->mutex)
89 #define GUPNP_SIMPLE_IGD_THREAD_UNLOCK(o) \
90 g_mutex_unlock (&(o)->priv->thread_data->mutex)
91
92
93 G_DEFINE_TYPE_WITH_CODE (GUPnPSimpleIgdThread, gupnp_simple_igd_thread,
94 GUPNP_TYPE_SIMPLE_IGD, G_ADD_PRIVATE (GUPnPSimpleIgdThread));
95
96 static void gupnp_simple_igd_thread_constructed (GObject *object);
97 static GObject *gupnp_simple_igd_thread_constructor (GType type,
98 guint n_props,
99 GObjectConstructParam *props);
100 static void gupnp_simple_igd_thread_dispose (GObject *object);
101 static void gupnp_simple_igd_thread_finalize (GObject *object);
102
103 static void gupnp_simple_igd_thread_add_port (GUPnPSimpleIgd *self,
104 const gchar *protocol,
105 guint16 external_port,
106 const gchar *local_ip,
107 guint16 local_port,
108 guint32 lease_duration,
109 const gchar *description);
110 static void gupnp_simple_igd_thread_remove_port (GUPnPSimpleIgd *self,
111 const gchar *protocol,
112 guint external_port);
113 static void gupnp_simple_igd_thread_remove_port_local (GUPnPSimpleIgd *self,
114 const gchar *protocol,
115 const gchar *local_ip,
116 guint16 local_port);
117
118
119 struct AddRemovePortData {
120 GMutex mutex;
121 GUPnPSimpleIgdThread *self G_GNUC_MAY_ALIAS; /* protected by mutex */
122 gchar *protocol;
123 guint16 external_port;
124 gchar *local_ip;
125 guint16 local_port;
126 guint32 lease_duration;
127 gchar *description;
128 };
129
130
131 static void
gupnp_simple_igd_thread_class_init(GUPnPSimpleIgdThreadClass * klass)132 gupnp_simple_igd_thread_class_init (GUPnPSimpleIgdThreadClass *klass)
133 {
134 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
135 GUPnPSimpleIgdClass *simple_igd_class = GUPNP_SIMPLE_IGD_CLASS (klass);
136
137 gobject_class->constructed = gupnp_simple_igd_thread_constructed;
138 gobject_class->constructor = gupnp_simple_igd_thread_constructor;
139 gobject_class->dispose = gupnp_simple_igd_thread_dispose;
140 gobject_class->finalize = gupnp_simple_igd_thread_finalize;
141
142 simple_igd_class->add_port = gupnp_simple_igd_thread_add_port;
143 simple_igd_class->remove_port = gupnp_simple_igd_thread_remove_port;
144 simple_igd_class->remove_port_local =
145 gupnp_simple_igd_thread_remove_port_local;
146 }
147
148
149 static void
gupnp_simple_igd_thread_init(GUPnPSimpleIgdThread * self)150 gupnp_simple_igd_thread_init (GUPnPSimpleIgdThread *self)
151 {
152 self->priv = gupnp_simple_igd_thread_get_instance_private (self);
153
154 self->priv->context = g_main_context_new ();
155 g_cond_init (&self->priv->can_dispose_cond);
156
157 self->priv->add_remove_port_datas = g_ptr_array_new ();
158 }
159
160 static gboolean
delete_all_mappings(gpointer user_data)161 delete_all_mappings (gpointer user_data)
162 {
163 GUPnPSimpleIgdThread *self = user_data;
164 gboolean can_dispose;
165
166 can_dispose = gupnp_simple_igd_delete_all_mappings (GUPNP_SIMPLE_IGD (self));
167
168 GUPNP_SIMPLE_IGD_THREAD_LOCK (self);
169 self->priv->can_dispose |= can_dispose;
170 self->priv->thread_data->all_mappings_deleted = TRUE;
171 GUPNP_SIMPLE_IGD_THREAD_UNLOCK (self);
172
173 g_cond_broadcast (&self->priv->can_dispose_cond);
174
175 return FALSE;
176 }
177
178 static gboolean
stop_loop(GUPnPSimpleIgdThread * self)179 stop_loop (GUPnPSimpleIgdThread *self)
180 {
181 GUPNP_SIMPLE_IGD_THREAD_LOCK (self);
182 if (self->priv->thread_data->loop)
183 g_main_loop_quit (self->priv->thread_data->loop);
184 GUPNP_SIMPLE_IGD_THREAD_UNLOCK (self);
185
186 return FALSE;
187 }
188
189 static void
gupnp_simple_igd_thread_dispose(GObject * object)190 gupnp_simple_igd_thread_dispose (GObject *object)
191 {
192 GUPnPSimpleIgdThread *self = GUPNP_SIMPLE_IGD_THREAD_CAST (object);
193
194 GUPNP_SIMPLE_IGD_THREAD_LOCK (self);
195 while (self->priv->add_remove_port_datas->len)
196 {
197 struct AddRemovePortData *data =
198 g_ptr_array_remove_index (self->priv->add_remove_port_datas, 0);
199 g_mutex_lock (&data->mutex);
200 data->self = NULL;
201 g_mutex_unlock (&data->mutex);
202 }
203
204 if (g_thread_self () == self->priv->thread)
205 {
206 GUPNP_SIMPLE_IGD_THREAD_UNLOCK (self);
207
208 if (!gupnp_simple_igd_delete_all_mappings (GUPNP_SIMPLE_IGD (self)))
209 return;
210
211 GUPNP_SIMPLE_IGD_THREAD_LOCK (self);
212 if (self->priv->thread_data->loop)
213 g_main_loop_quit (self->priv->thread_data->loop);
214 GUPNP_SIMPLE_IGD_THREAD_UNLOCK (self);
215
216 if (self->priv->thread_data->loop != NULL) {
217 self->priv->thread_data->self = g_object_ref (self);
218 return;
219 }
220 }
221 else if (self->priv->thread)
222 {
223 GSource *delete_all_src;
224
225 delete_all_src = g_idle_source_new ();
226 g_source_set_priority (delete_all_src, G_PRIORITY_HIGH);
227 g_source_set_callback (delete_all_src, delete_all_mappings,
228 g_object_ref (self),
229 g_object_unref);
230 g_source_attach (delete_all_src, self->priv->context);
231 g_source_unref (delete_all_src);
232
233 while (!self->priv->thread_data->all_mappings_deleted)
234 g_cond_wait (&self->priv->can_dispose_cond,
235 &self->priv->thread_data->mutex);
236
237 if (!self->priv->can_dispose)
238 {
239 GUPNP_SIMPLE_IGD_THREAD_UNLOCK (self);
240 return;
241 }
242
243 if (self->priv->thread_data->loop)
244 {
245 GSource *src = g_idle_source_new ();
246
247 g_source_set_callback (src, (GSourceFunc) stop_loop, self, NULL);
248 g_source_attach (src, self->priv->context);
249 g_source_unref (src);
250
251 if (self->priv->thread_data->loop)
252 g_main_loop_quit (self->priv->thread_data->loop);
253 }
254 GUPNP_SIMPLE_IGD_THREAD_UNLOCK (self);
255
256 g_thread_join (self->priv->thread);
257 self->priv->thread = NULL;
258 }
259
260 G_OBJECT_CLASS (gupnp_simple_igd_thread_parent_class)->dispose (object);
261 }
262
263 static void
thread_data_dec(struct thread_data * data)264 thread_data_dec (struct thread_data *data)
265 {
266 if (g_atomic_int_dec_and_test (&data->refcount))
267 {
268 g_mutex_clear (&data->mutex);
269 g_main_context_unref (data->context);
270 g_slice_free (struct thread_data, data);
271 }
272 }
273
274 static void
gupnp_simple_igd_thread_finalize(GObject * object)275 gupnp_simple_igd_thread_finalize (GObject *object)
276 {
277 GUPnPSimpleIgdThread *self = GUPNP_SIMPLE_IGD_THREAD_CAST (object);
278
279 g_main_context_unref (self->priv->context);
280 g_cond_clear (&self->priv->can_dispose_cond);
281
282 g_ptr_array_free (self->priv->add_remove_port_datas, TRUE);
283
284 thread_data_dec (self->priv->thread_data);
285
286 G_OBJECT_CLASS (gupnp_simple_igd_thread_parent_class)->finalize (object);
287 }
288
289 static gpointer
thread_func(gpointer dat)290 thread_func (gpointer dat)
291 {
292 struct thread_data *data = dat;
293 GMainLoop *loop = g_main_loop_new (data->context, FALSE);
294
295 g_main_context_push_thread_default (data->context);
296
297 g_mutex_lock (&data->mutex);
298 data->loop = loop;
299 g_mutex_unlock (&data->mutex);
300
301 g_main_loop_run (loop);
302
303 g_mutex_lock (&data->mutex);
304 data->loop = NULL;
305 data->all_mappings_deleted = TRUE;
306 g_mutex_unlock (&data->mutex);
307
308 g_main_loop_unref (loop);
309
310 if (data->self)
311 g_object_unref (data->self);
312
313 g_main_context_pop_thread_default (data->context);
314 thread_data_dec (data);
315
316 return NULL;
317 }
318
319 static GObject *
gupnp_simple_igd_thread_constructor(GType type,guint n_props,GObjectConstructParam * props)320 gupnp_simple_igd_thread_constructor (GType type,
321 guint n_props,
322 GObjectConstructParam *props)
323 {
324 GObject *obj;
325
326 return G_OBJECT_CLASS (gupnp_simple_igd_thread_parent_class)->constructor (
327 type, n_props, props);
328
329 return obj;
330 }
331
332 static void
gupnp_simple_igd_thread_constructed(GObject * object)333 gupnp_simple_igd_thread_constructed (GObject *object)
334 {
335 GUPnPSimpleIgdThread *self = GUPNP_SIMPLE_IGD_THREAD_CAST (object);
336 struct thread_data *data = g_slice_new0 (struct thread_data);
337
338 g_main_context_push_thread_default (self->priv->context);
339 if (G_OBJECT_CLASS (gupnp_simple_igd_thread_parent_class)->constructed)
340 G_OBJECT_CLASS (gupnp_simple_igd_thread_parent_class)->constructed (object);
341 g_main_context_pop_thread_default (self->priv->context);
342
343 g_atomic_int_set (&data->refcount, 2);
344
345 self->priv->thread_data = data;
346
347 g_mutex_init (&data->mutex);
348 g_main_context_ref (self->priv->context);
349 data->context = self->priv->context;
350
351 self->priv->thread = g_thread_new ("gupnp-igd-thread", thread_func, data);
352 g_return_if_fail (self->priv->thread);
353 }
354
355 static gboolean
add_port_idle_func(gpointer user_data)356 add_port_idle_func (gpointer user_data)
357 {
358 struct AddRemovePortData *data = user_data;
359 GUPnPSimpleIgdClass *klass =
360 GUPNP_SIMPLE_IGD_CLASS (gupnp_simple_igd_thread_parent_class);
361 GUPnPSimpleIgdThread *self;
362
363 g_mutex_lock (&data->mutex);
364 self = data->self;
365 if (self)
366 g_object_ref (self);
367 g_mutex_unlock (&data->mutex);
368 if (!self)
369 return FALSE;
370
371 if (klass->add_port)
372 klass->add_port (GUPNP_SIMPLE_IGD (self), data->protocol,
373 data->external_port, data->local_ip, data->local_port,
374 data->lease_duration,
375 data->description);
376
377 g_object_unref (self);
378
379 return FALSE;
380 }
381
382
383 static gboolean
remove_port_idle_func(gpointer user_data)384 remove_port_idle_func (gpointer user_data)
385 {
386 struct AddRemovePortData *data = user_data;
387 GUPnPSimpleIgdClass *klass =
388 GUPNP_SIMPLE_IGD_CLASS (gupnp_simple_igd_thread_parent_class);
389 GUPnPSimpleIgdThread *self;
390
391 g_mutex_lock (&data->mutex);
392 self = data->self;
393 if (self)
394 g_object_ref (self);
395 g_mutex_unlock (&data->mutex);
396 if (!self)
397 return FALSE;
398
399 if (klass->remove_port)
400 klass->remove_port (GUPNP_SIMPLE_IGD (self), data->protocol,
401 data->external_port);
402
403 g_object_unref (self);
404
405 return FALSE;
406 }
407
408 static gboolean
remove_port_local_idle_func(gpointer user_data)409 remove_port_local_idle_func (gpointer user_data)
410 {
411 struct AddRemovePortData *data = user_data;
412 GUPnPSimpleIgdClass *klass =
413 GUPNP_SIMPLE_IGD_CLASS (gupnp_simple_igd_thread_parent_class);
414 GUPnPSimpleIgdThread *self;
415
416 g_mutex_lock (&data->mutex);
417 self = data->self;
418 if (self)
419 g_object_ref (self);
420 g_mutex_unlock (&data->mutex);
421 if (!self)
422 return FALSE;
423
424 if (klass->remove_port_local)
425 klass->remove_port_local (GUPNP_SIMPLE_IGD (self), data->protocol,
426 data->local_ip, data->local_port);
427
428 g_object_unref (self);
429
430 return FALSE;
431 }
432
433 static void
free_add_remove_port_data(gpointer user_data)434 free_add_remove_port_data (gpointer user_data)
435 {
436 struct AddRemovePortData *data = user_data;
437 GUPnPSimpleIgdThread *self;
438
439 g_mutex_lock (&data->mutex);
440 self = data->self;
441 data->self = NULL;
442 if (self)
443 g_object_ref (self);
444 g_mutex_unlock (&data->mutex);
445 if (self)
446 {
447 GUPNP_SIMPLE_IGD_THREAD_LOCK (self);
448 g_ptr_array_remove_fast (self->priv->add_remove_port_datas, data);
449 GUPNP_SIMPLE_IGD_THREAD_UNLOCK (self);
450 g_object_unref (self);
451 }
452
453 g_free (data->protocol);
454 g_free (data->local_ip);
455 g_free (data->description);
456
457 g_mutex_clear (&data->mutex);
458
459 g_slice_free (struct AddRemovePortData, data);
460 }
461
462 static void
gupnp_simple_igd_thread_add_port(GUPnPSimpleIgd * self,const gchar * protocol,guint16 external_port,const gchar * local_ip,guint16 local_port,guint32 lease_duration,const gchar * description)463 gupnp_simple_igd_thread_add_port (GUPnPSimpleIgd *self,
464 const gchar *protocol,
465 guint16 external_port,
466 const gchar *local_ip,
467 guint16 local_port,
468 guint32 lease_duration,
469 const gchar *description)
470 {
471 GUPnPSimpleIgdThread *realself = GUPNP_SIMPLE_IGD_THREAD (self);
472 struct AddRemovePortData *data = g_slice_new0 (struct AddRemovePortData);
473 GSource *source;
474
475 g_mutex_init (&data->mutex);
476 data->self = realself;
477 data->protocol = g_strdup (protocol);
478 data->external_port = external_port;
479 data->local_ip = g_strdup (local_ip);
480 data->local_port = local_port;
481 data->lease_duration = lease_duration;
482 data->description = g_strdup (description);
483 GUPNP_SIMPLE_IGD_THREAD_LOCK (realself);
484 g_ptr_array_add (realself->priv->add_remove_port_datas, data);
485 GUPNP_SIMPLE_IGD_THREAD_UNLOCK (realself);
486
487 source = g_idle_source_new ();
488 g_source_set_callback (source, add_port_idle_func, data,
489 free_add_remove_port_data);
490 g_source_set_priority (source, G_PRIORITY_DEFAULT);
491 g_source_attach (source, realself->priv->context);
492 g_source_unref (source);
493 g_main_context_wakeup (realself->priv->context);
494 }
495
496 static void
gupnp_simple_igd_thread_remove_port(GUPnPSimpleIgd * self,const gchar * protocol,guint external_port)497 gupnp_simple_igd_thread_remove_port (GUPnPSimpleIgd *self,
498 const gchar *protocol,
499 guint external_port)
500 {
501 GUPnPSimpleIgdThread *realself = GUPNP_SIMPLE_IGD_THREAD (self);
502 struct AddRemovePortData *data = g_slice_new0 (struct AddRemovePortData);
503 GSource *source;
504
505 g_mutex_init (&data->mutex);
506 data->self = realself;
507 data->protocol = g_strdup (protocol);
508 data->external_port = external_port;
509 GUPNP_SIMPLE_IGD_THREAD_LOCK (realself);
510 g_ptr_array_add (realself->priv->add_remove_port_datas, data);
511 GUPNP_SIMPLE_IGD_THREAD_UNLOCK (realself);
512
513 source = g_idle_source_new ();
514 g_source_set_callback (source, remove_port_idle_func, data,
515 free_add_remove_port_data);
516 g_source_set_priority (source, G_PRIORITY_DEFAULT);
517 g_source_attach (source, realself->priv->context);
518 g_source_unref (source);
519 g_main_context_wakeup (realself->priv->context);
520 }
521
522 static void
gupnp_simple_igd_thread_remove_port_local(GUPnPSimpleIgd * self,const gchar * protocol,const gchar * local_ip,guint16 local_port)523 gupnp_simple_igd_thread_remove_port_local (GUPnPSimpleIgd *self,
524 const gchar *protocol,
525 const gchar *local_ip,
526 guint16 local_port)
527 {
528 GUPnPSimpleIgdThread *realself = GUPNP_SIMPLE_IGD_THREAD (self);
529 struct AddRemovePortData *data = g_slice_new0 (struct AddRemovePortData);
530 GSource *source;
531
532 g_mutex_init (&data->mutex);
533 data->self = realself;
534 data->protocol = g_strdup (protocol);
535 data->local_ip = g_strdup (local_ip);
536 data->local_port = local_port;
537 GUPNP_SIMPLE_IGD_THREAD_LOCK (realself);
538 g_ptr_array_add (realself->priv->add_remove_port_datas, data);
539 GUPNP_SIMPLE_IGD_THREAD_UNLOCK (realself);
540
541 source = g_idle_source_new ();
542 g_source_set_callback (source, remove_port_local_idle_func, data,
543 free_add_remove_port_data);
544 g_source_set_priority (source, G_PRIORITY_DEFAULT);
545 g_source_attach (source, realself->priv->context);
546 g_source_unref (source);
547 g_main_context_wakeup (realself->priv->context);
548 }
549
550 /**
551 * gupnp_simple_igd_thread_new:
552 *
553 * Creates a new #GUPnPSimpleIgdThread
554 *
555 * Returns: the new #GUPnPSimpleIgdThread
556 */
557
558 GUPnPSimpleIgdThread *
gupnp_simple_igd_thread_new()559 gupnp_simple_igd_thread_new ()
560 {
561 return g_object_new (GUPNP_TYPE_SIMPLE_IGD_THREAD, NULL);
562 }
563