~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjmedia/src/pjmedia/event.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: event.c 3905 2011-12-09 05:15:39Z ming $ */
 
2
/* 
 
3
 * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 
18
 */
 
19
#include <pjmedia/event.h>
 
20
#include <pjmedia/errno.h>
 
21
#include <pj/assert.h>
 
22
#include <pj/list.h>
 
23
#include <pj/log.h>
 
24
#include <pj/os.h>
 
25
#include <pj/pool.h>
 
26
#include <pj/string.h>
 
27
 
 
28
#define THIS_FILE       "event.c"
 
29
 
 
30
#define MAX_EVENTS 16
 
31
 
 
32
typedef struct esub esub;
 
33
 
 
34
struct esub
 
35
{
 
36
    PJ_DECL_LIST_MEMBER(esub);
 
37
 
 
38
    pjmedia_event_cb    *cb;
 
39
    void                *user_data;
 
40
    void                *epub;
 
41
};
 
42
 
 
43
typedef struct event_queue
 
44
{
 
45
    pjmedia_event   events[MAX_EVENTS]; /**< array of events.           */
 
46
    int             head, tail;
 
47
    pj_bool_t       is_full;
 
48
} event_queue;
 
49
 
 
50
struct pjmedia_event_mgr
 
51
{
 
52
    pj_pool_t      *pool;
 
53
    pj_thread_t    *thread;             /**< worker thread.             */
 
54
    pj_bool_t       is_quitting;
 
55
    pj_sem_t       *sem;
 
56
    pj_mutex_t     *mutex;
 
57
    event_queue     ev_queue;
 
58
    event_queue    *pub_ev_queue;       /**< publish() event queue.     */
 
59
    esub            esub_list;          /**< list of subscribers.       */
 
60
    esub            free_esub_list;     /**< list of subscribers.       */
 
61
    esub           *th_next_sub,        /**< worker thread's next sub.  */
 
62
                   *pub_next_sub;       /**< publish() next sub.        */
 
63
};
 
64
 
 
65
static pjmedia_event_mgr *event_manager_instance;
 
66
 
 
67
static pj_status_t event_queue_add_event(event_queue* ev_queue,
 
68
                                         pjmedia_event *event)
 
69
{
 
70
    if (ev_queue->is_full) {
 
71
        char ev_name[5];
 
72
 
 
73
        /* This event will be ignored. */
 
74
        PJ_LOG(4, (THIS_FILE, "Lost event %s from publisher [0x%p] "
 
75
                              "due to full queue.",
 
76
                              pjmedia_fourcc_name(event->type, ev_name),
 
77
                              event->epub));
 
78
 
 
79
        return PJ_ETOOMANY;
 
80
    }
 
81
 
 
82
    pj_memcpy(&ev_queue->events[ev_queue->tail], event, sizeof(*event));
 
83
    ev_queue->tail = (ev_queue->tail + 1) % MAX_EVENTS;
 
84
    if (ev_queue->tail == ev_queue->head)
 
85
        ev_queue->is_full = PJ_TRUE;
 
86
 
 
87
    return PJ_SUCCESS;
 
88
}
 
89
 
 
90
static pj_status_t event_mgr_distribute_events(pjmedia_event_mgr *mgr,
 
91
                                               event_queue *ev_queue,
 
92
                                               esub **next_sub,
 
93
                                               pj_bool_t rls_lock)
 
94
{
 
95
    pj_status_t err = PJ_SUCCESS;
 
96
    esub * sub = mgr->esub_list.next;
 
97
    pjmedia_event *ev = &ev_queue->events[ev_queue->head];
 
98
 
 
99
    while (sub != &mgr->esub_list) {
 
100
        *next_sub = sub->next;
 
101
 
 
102
        /* Check if the subscriber is interested in 
 
103
         * receiving the event from the publisher.
 
104
         */
 
105
        if (sub->epub == ev->epub || !sub->epub) {
 
106
            pjmedia_event_cb *cb = sub->cb;
 
107
            void *user_data = sub->user_data;
 
108
            pj_status_t status;
 
109
            
 
110
            if (rls_lock)
 
111
                pj_mutex_unlock(mgr->mutex);
 
112
 
 
113
            status = (*cb)(ev, user_data);
 
114
            if (status != PJ_SUCCESS && err == PJ_SUCCESS)
 
115
                err = status;
 
116
 
 
117
            if (rls_lock)
 
118
                pj_mutex_lock(mgr->mutex);
 
119
        }
 
120
        sub = *next_sub;
 
121
    }
 
122
    *next_sub = NULL;
 
123
 
 
124
    ev_queue->head = (ev_queue->head + 1) % MAX_EVENTS;
 
125
    ev_queue->is_full = PJ_FALSE;
 
126
 
 
127
    return err;
 
128
}
 
129
 
 
130
/* Event worker thread function. */
 
131
static int event_worker_thread(void *arg)
 
132
{
 
133
    pjmedia_event_mgr *mgr = (pjmedia_event_mgr *)arg;
 
134
 
 
135
    while (1) {
 
136
        /* Wait until there is an event. */
 
137
        pj_sem_wait(mgr->sem);
 
138
 
 
139
        if (mgr->is_quitting)
 
140
            break;
 
141
 
 
142
        pj_mutex_lock(mgr->mutex);
 
143
        event_mgr_distribute_events(mgr, &mgr->ev_queue,
 
144
                                    &mgr->th_next_sub, PJ_TRUE);
 
145
        pj_mutex_unlock(mgr->mutex);
 
146
    }
 
147
 
 
148
    return 0;
 
149
}
 
150
 
 
151
PJ_DEF(pj_status_t) pjmedia_event_mgr_create(pj_pool_t *pool,
 
152
                                             unsigned options,
 
153
                                             pjmedia_event_mgr **p_mgr)
 
154
{
 
155
    pjmedia_event_mgr *mgr;
 
156
    pj_status_t status;
 
157
 
 
158
    mgr = PJ_POOL_ZALLOC_T(pool, pjmedia_event_mgr);
 
159
    mgr->pool = pj_pool_create(pool->factory, "evt mgr", 500, 500, NULL);
 
160
    pj_list_init(&mgr->esub_list);
 
161
    pj_list_init(&mgr->free_esub_list);
 
162
 
 
163
    if (!(options & PJMEDIA_EVENT_MGR_NO_THREAD)) {
 
164
        status = pj_sem_create(mgr->pool, "ev_sem", 0, MAX_EVENTS + 1,
 
165
                               &mgr->sem);
 
166
        if (status != PJ_SUCCESS)
 
167
            return status;
 
168
 
 
169
        status = pj_thread_create(mgr->pool, "ev_thread",
 
170
                                  &event_worker_thread,
 
171
                                  mgr, 0, 0, &mgr->thread);
 
172
        if (status != PJ_SUCCESS) {
 
173
            pjmedia_event_mgr_destroy(mgr);
 
174
            return status;
 
175
        }
 
176
    }
 
177
 
 
178
    status = pj_mutex_create_recursive(mgr->pool, "ev_mutex", &mgr->mutex);
 
179
    if (status != PJ_SUCCESS) {
 
180
        pjmedia_event_mgr_destroy(mgr);
 
181
        return status;
 
182
    }
 
183
 
 
184
    if (!event_manager_instance)
 
185
        event_manager_instance = mgr;
 
186
 
 
187
    if (p_mgr)
 
188
        *p_mgr = mgr;
 
189
 
 
190
    return PJ_SUCCESS;
 
191
}
 
192
 
 
193
PJ_DEF(pjmedia_event_mgr*) pjmedia_event_mgr_instance(void)
 
194
{
 
195
    return event_manager_instance;
 
196
}
 
197
 
 
198
PJ_DEF(void) pjmedia_event_mgr_set_instance(pjmedia_event_mgr *mgr)
 
199
{
 
200
    event_manager_instance = mgr;
 
201
}
 
202
 
 
203
PJ_DEF(void) pjmedia_event_mgr_destroy(pjmedia_event_mgr *mgr)
 
204
{
 
205
    if (!mgr) mgr = pjmedia_event_mgr_instance();
 
206
    PJ_ASSERT_ON_FAIL(mgr != NULL, return);
 
207
 
 
208
    if (mgr->thread) {
 
209
        mgr->is_quitting = PJ_TRUE;
 
210
        pj_sem_post(mgr->sem);
 
211
        pj_thread_join(mgr->thread);
 
212
    }
 
213
 
 
214
    if (mgr->sem) {
 
215
        pj_sem_destroy(mgr->sem);
 
216
        mgr->sem = NULL;
 
217
    }
 
218
 
 
219
    if (mgr->mutex) {
 
220
        pj_mutex_destroy(mgr->mutex);
 
221
        mgr->mutex = NULL;
 
222
    }
 
223
 
 
224
    if (mgr->pool)
 
225
        pj_pool_release(mgr->pool);
 
226
 
 
227
    if (event_manager_instance == mgr)
 
228
        event_manager_instance = NULL;
 
229
}
 
230
 
 
231
PJ_DEF(void) pjmedia_event_init( pjmedia_event *event,
 
232
                                 pjmedia_event_type type,
 
233
                                 const pj_timestamp *ts,
 
234
                                 const void *src)
 
235
{
 
236
    pj_bzero(event, sizeof(*event));
 
237
    event->type = type;
 
238
    if (ts)
 
239
        event->timestamp.u64 = ts->u64;
 
240
    event->epub = event->src = src;
 
241
}
 
242
 
 
243
PJ_DEF(pj_status_t) pjmedia_event_subscribe( pjmedia_event_mgr *mgr,
 
244
                                             pjmedia_event_cb *cb,
 
245
                                             void *user_data,
 
246
                                             void *epub)
 
247
{
 
248
    esub *sub;
 
249
 
 
250
    PJ_ASSERT_RETURN(cb, PJ_EINVAL);
 
251
 
 
252
    if (!mgr) mgr = pjmedia_event_mgr_instance();
 
253
    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
 
254
 
 
255
    pj_mutex_lock(mgr->mutex);
 
256
    /* Check whether callback function with the same user data is already
 
257
     * subscribed to the publisher. This is to prevent the callback function
 
258
     * receiving the same event from the same publisher more than once.
 
259
     */
 
260
    sub = mgr->esub_list.next;
 
261
    while (sub != &mgr->esub_list) {
 
262
        esub *next = sub->next;
 
263
        if (sub->cb == cb && sub->user_data == user_data &&
 
264
            sub->epub == epub)
 
265
        {
 
266
            pj_mutex_unlock(mgr->mutex);
 
267
            return PJ_SUCCESS;
 
268
        }
 
269
        sub = next;
 
270
    }
 
271
 
 
272
    if (mgr->free_esub_list.next != &mgr->free_esub_list) {
 
273
        sub = mgr->free_esub_list.next;
 
274
        pj_list_erase(sub);
 
275
    } else
 
276
        sub = PJ_POOL_ZALLOC_T(mgr->pool, esub);
 
277
    sub->cb = cb;
 
278
    sub->user_data = user_data;
 
279
    sub->epub = epub;
 
280
    pj_list_push_back(&mgr->esub_list, sub);
 
281
    pj_mutex_unlock(mgr->mutex);
 
282
 
 
283
    return PJ_SUCCESS;
 
284
}
 
285
 
 
286
PJ_DEF(pj_status_t)
 
287
pjmedia_event_unsubscribe(pjmedia_event_mgr *mgr,
 
288
                          pjmedia_event_cb *cb,
 
289
                          void *user_data,
 
290
                          void *epub)
 
291
{
 
292
    esub *sub;
 
293
 
 
294
    PJ_ASSERT_RETURN(cb, PJ_EINVAL);
 
295
 
 
296
    if (!mgr) mgr = pjmedia_event_mgr_instance();
 
297
    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
 
298
 
 
299
    pj_mutex_lock(mgr->mutex);
 
300
    sub = mgr->esub_list.next;
 
301
    while (sub != &mgr->esub_list) {
 
302
        esub *next = sub->next;
 
303
        if (sub->cb == cb && (sub->user_data == user_data || !user_data) &&
 
304
            (sub->epub == epub || !epub))
 
305
        {
 
306
            /* If the worker thread or pjmedia_event_publish() API is
 
307
             * in the process of distributing events, make sure that
 
308
             * its pointer to the next subscriber stays valid.
 
309
             */
 
310
            if (mgr->th_next_sub == sub)
 
311
                mgr->th_next_sub = sub->next;
 
312
            if (mgr->pub_next_sub == sub)
 
313
                mgr->pub_next_sub = sub->next;
 
314
            pj_list_erase(sub);
 
315
            pj_list_push_back(&mgr->free_esub_list, sub);
 
316
            if (user_data && epub)
 
317
                break;
 
318
        }
 
319
        sub = next;
 
320
    }
 
321
    pj_mutex_unlock(mgr->mutex);
 
322
 
 
323
    return PJ_SUCCESS;
 
324
}
 
325
 
 
326
PJ_DEF(pj_status_t) pjmedia_event_publish( pjmedia_event_mgr *mgr,
 
327
                                           void *epub,
 
328
                                           pjmedia_event *event,
 
329
                                           pjmedia_event_publish_flag flag)
 
330
{
 
331
    pj_status_t err = PJ_SUCCESS;
 
332
 
 
333
    PJ_ASSERT_RETURN(epub && event, PJ_EINVAL);
 
334
 
 
335
    if (!mgr) mgr = pjmedia_event_mgr_instance();
 
336
    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
 
337
 
 
338
    event->epub = epub;
 
339
 
 
340
    pj_mutex_lock(mgr->mutex);
 
341
    if (flag & PJMEDIA_EVENT_PUBLISH_POST_EVENT) {
 
342
        if (event_queue_add_event(&mgr->ev_queue, event) == PJ_SUCCESS)
 
343
            pj_sem_post(mgr->sem);
 
344
    } else {
 
345
        /* For nested pjmedia_event_publish() calls, i.e. calling publish()
 
346
         * inside the subscriber's callback, the function will only add
 
347
         * the event to the event queue of the first publish() call. It
 
348
         * is the first publish() call that will be responsible to
 
349
         * distribute the events.
 
350
         */
 
351
        if (mgr->pub_ev_queue) {
 
352
            event_queue_add_event(mgr->pub_ev_queue, event);
 
353
        } else {
 
354
            static event_queue ev_queue;
 
355
            pj_status_t status;
 
356
 
 
357
            ev_queue.head = ev_queue.tail = 0;
 
358
            ev_queue.is_full = PJ_FALSE;
 
359
            mgr->pub_ev_queue = &ev_queue;
 
360
 
 
361
            event_queue_add_event(mgr->pub_ev_queue, event);
 
362
 
 
363
            do {
 
364
                status = event_mgr_distribute_events(mgr, mgr->pub_ev_queue,
 
365
                                                     &mgr->pub_next_sub,
 
366
                                                     PJ_FALSE);
 
367
                if (status != PJ_SUCCESS && err == PJ_SUCCESS)
 
368
                    err = status;
 
369
            } while(ev_queue.head != ev_queue.tail || ev_queue.is_full);
 
370
 
 
371
            mgr->pub_ev_queue = NULL;
 
372
        }
 
373
    }
 
374
    pj_mutex_unlock(mgr->mutex);
 
375
 
 
376
    return err;
 
377
}