summaryrefslogtreecommitdiff
path: root/pjlib/include
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2013-02-21 11:18:36 +0000
committerBenny Prijono <bennylp@teluu.com>2013-02-21 11:18:36 +0000
commit9b8e0a5afe9cba0fd430e9642630bd465db9aefa (patch)
tree550cd346c99b144a79c76096dc75d6be2dfb3c31 /pjlib/include
parent99de12689e7c6b8117ee82ff8e3c17e5ec85418d (diff)
Fixed #1616: Implementation of Group lock and other foundation in PJLIB for fixing synchronization issues
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4359 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjlib/include')
-rw-r--r--pjlib/include/pj/activesock.h7
-rw-r--r--pjlib/include/pj/config.h16
-rw-r--r--pjlib/include/pj/errno.h5
-rw-r--r--pjlib/include/pj/ioqueue.h13
-rw-r--r--pjlib/include/pj/lock.h252
-rw-r--r--pjlib/include/pj/timer.h66
-rw-r--r--pjlib/include/pj/types.h3
7 files changed, 360 insertions, 2 deletions
diff --git a/pjlib/include/pj/activesock.h b/pjlib/include/pj/activesock.h
index 5a201864..11fa6589 100644
--- a/pjlib/include/pj/activesock.h
+++ b/pjlib/include/pj/activesock.h
@@ -174,6 +174,11 @@ typedef struct pj_activesock_cb
typedef struct pj_activesock_cfg
{
/**
+ * Optional group lock to be assigned to the ioqueue key.
+ */
+ pj_grp_lock_t *grp_lock;
+
+ /**
* Number of concurrent asynchronous operations that is to be supported
* by the active socket. This value only affects socket receive and
* accept operations -- the active socket will issue one or more
@@ -290,7 +295,6 @@ PJ_DECL(pj_status_t) pj_activesock_create_udp(pj_pool_t *pool,
pj_activesock_t **p_asock,
pj_sockaddr *bound_addr);
-
/**
* Close the active socket. This will unregister the socket from the
* ioqueue and ultimately close the socket.
@@ -548,6 +552,7 @@ PJ_DECL(pj_status_t) pj_activesock_start_connect(pj_activesock_t *asock,
const pj_sockaddr_t *remaddr,
int addr_len);
+
#endif /* PJ_HAS_TCP */
/**
diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h
index 2568214d..9b504c3a 100644
--- a/pjlib/include/pj/config.h
+++ b/pjlib/include/pj/config.h
@@ -488,6 +488,14 @@
/**
+ * Set this to 1 to enable debugging on the group lock. Default: 0
+ */
+#ifndef PJ_GRP_LOCK_DEBUG
+# define PJ_GRP_LOCK_DEBUG 0
+#endif
+
+
+/**
* Specify this as \a stack_size argument in #pj_thread_create() to specify
* that thread should use default stack size for the current platform.
*
@@ -1122,6 +1130,14 @@
#endif
/**
+ * Simulate race condition by sleeping the thread in strategic locations.
+ * Default: no!
+ */
+#ifndef PJ_RACE_ME
+# define PJ_RACE_ME(x)
+#endif
+
+/**
* Function attributes to inform that the function may throw exception.
*
* @param x The exception list, enclosed in parenthesis.
diff --git a/pjlib/include/pj/errno.h b/pjlib/include/pj/errno.h
index 66fea949..e9e403ce 100644
--- a/pjlib/include/pj/errno.h
+++ b/pjlib/include/pj/errno.h
@@ -422,6 +422,11 @@ PJ_DECL(pj_status_t) pj_register_strerror(pj_status_t start_code,
* Unsupported address family
*/
#define PJ_EAFNOTSUP (PJ_ERRNO_START_STATUS + 22)/* 70022 */
+/**
+ * @hideinitializer
+ * Object no longer exists
+ */
+#define PJ_EGONE (PJ_ERRNO_START_STATUS + 23)/* 70023 */
/** @} */ /* pj_errnum */
diff --git a/pjlib/include/pj/ioqueue.h b/pjlib/include/pj/ioqueue.h
index 40bb403c..1e983327 100644
--- a/pjlib/include/pj/ioqueue.h
+++ b/pjlib/include/pj/ioqueue.h
@@ -405,6 +405,19 @@ PJ_DECL(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
pj_ioqueue_key_t **key );
/**
+ * Variant of pj_ioqueue_register_sock() with additional group lock parameter.
+ * If group lock is set for the key, the key will add the reference counter
+ * when the socket is registered and decrease it when it is destroyed.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool,
+ pj_ioqueue_t *ioque,
+ pj_sock_t sock,
+ pj_grp_lock_t *grp_lock,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **key );
+
+/**
* Unregister from the I/O Queue framework. Caller must make sure that
* the key doesn't have any pending operations before calling this function,
* by calling #pj_ioqueue_is_pending() for all previously submitted
diff --git a/pjlib/include/pj/lock.h b/pjlib/include/pj/lock.h
index b8bdb0f9..b1a5d72e 100644
--- a/pjlib/include/pj/lock.h
+++ b/pjlib/include/pj/lock.h
@@ -147,6 +147,258 @@ PJ_DECL(pj_status_t) pj_lock_destroy( pj_lock_t *lock );
/** @} */
+
+/**
+ * @defgroup PJ_GRP_LOCK Group Lock
+ * @ingroup PJ_LOCK
+ * @{
+ *
+ * Group lock is a synchronization object to manage concurrency among members
+ * within the same logical group. Example of such groups are:
+ *
+ * - dialog, which has members such as the dialog itself, an invite session,
+ * and several transactions
+ * - ICE, which has members such as ICE stream transport, ICE session, STUN
+ * socket, TURN socket, and down to ioqueue key
+ *
+ * Group lock has three functions:
+ *
+ * - mutual exclusion: to protect resources from being accessed by more than
+ * one threads at the same time
+ * - session management: to make sure that the resource is not destroyed
+ * while others are still using or about to use it.
+ * - lock coordinator: to provide uniform lock ordering among more than one
+ * lock objects, which is necessary to avoid deadlock.
+ *
+ * The requirements of the group lock are:
+ *
+ * - must satisfy all the functions above
+ * - must allow members to join or leave the group (for example,
+ * transaction may be added or removed from a dialog)
+ * - must be able to synchronize with external lock (for example, a dialog
+ * lock must be able to sync itself with PJSUA lock)
+ *
+ * Please see https://trac.pjsip.org/repos/wiki/Group_Lock for more info.
+ */
+
+/**
+ * Settings for creating the group lock.
+ */
+typedef struct pj_grp_lock_config
+{
+ /**
+ * Creation flags, currently must be zero.
+ */
+ unsigned flags;
+
+} pj_grp_lock_config;
+
+
+/**
+ * Initialize the config with the default values.
+ *
+ * @param cfg The config to be initialized.
+ */
+PJ_DECL(void) pj_grp_lock_config_default(pj_grp_lock_config *cfg);
+
+/**
+ * Create a group lock object. Initially the group lock will have reference
+ * counter of one.
+ *
+ * @param pool The group lock only uses the pool parameter to get
+ * the pool factory, from which it will create its own
+ * pool.
+ * @param cfg Optional configuration.
+ * @param p_grp_lock Pointer to receive the newly created group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_create(pj_pool_t *pool,
+ const pj_grp_lock_config *cfg,
+ pj_grp_lock_t **p_grp_lock);
+
+/**
+ * Forcibly destroy the group lock, ignoring the reference counter value.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_destroy( pj_grp_lock_t *grp_lock);
+
+/**
+ * Move the contents of the old lock to the new lock and destroy the
+ * old lock.
+ *
+ * @param old_lock The old group lock to be destroyed.
+ * @param new_lock The new group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_replace(pj_grp_lock_t *old_lock,
+ pj_grp_lock_t *new_lock);
+
+/**
+ * Acquire lock on the specified group lock.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_acquire( pj_grp_lock_t *grp_lock);
+
+/**
+ * Acquire lock on the specified group lock if it is available, otherwise
+ * return immediately wihout waiting.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_tryacquire( pj_grp_lock_t *grp_lock);
+
+/**
+ * Release the previously held lock. This may cause the group lock
+ * to be destroyed if it is the last one to hold the reference counter.
+ * In that case, the function will return PJ_EGONE.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_release( pj_grp_lock_t *grp_lock);
+
+/**
+ * Add a destructor handler, to be called by the group lock when it is
+ * about to be destroyed.
+ *
+ * @param grp_lock The group lock.
+ * @param pool Pool to allocate memory for the handler.
+ * @param member A pointer to be passed to the handler.
+ * @param handler The destroy handler.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_add_handler(pj_grp_lock_t *grp_lock,
+ pj_pool_t *pool,
+ void *member,
+ void (*handler)(void *member));
+
+/**
+ * Remove previously registered handler. All parameters must be the same
+ * as when the handler was added.
+ *
+ * @param grp_lock The group lock.
+ * @param member A pointer to be passed to the handler.
+ * @param handler The destroy handler.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_del_handler(pj_grp_lock_t *grp_lock,
+ void *member,
+ void (*handler)(void *member));
+
+/**
+ * Increment reference counter to prevent the group lock grom being destroyed.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+#if !PJ_GRP_LOCK_DEBUG
+PJ_DECL(pj_status_t) pj_grp_lock_add_ref(pj_grp_lock_t *grp_lock);
+
+#define pj_grp_lock_add_ref_dbg(grp_lock, x, y) pj_grp_lock_add_ref(grp_lock)
+
+#else
+
+#define pj_grp_lock_add_ref(g) pj_grp_lock_add_ref_dbg(g, __FILE__, __LINE__)
+
+PJ_DECL(pj_status_t) pj_grp_lock_add_ref_dbg(pj_grp_lock_t *grp_lock,
+ const char *file,
+ int line);
+#endif
+
+/**
+ * Decrement the reference counter. When the counter value reaches zero, the
+ * group lock will be destroyed and all destructor handlers will be called.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+#if !PJ_GRP_LOCK_DEBUG
+PJ_DECL(pj_status_t) pj_grp_lock_dec_ref(pj_grp_lock_t *grp_lock);
+
+#define pj_grp_lock_dec_ref_dbg(grp_lock, x, y) pj_grp_lock_dec_ref(grp_lock)
+#else
+
+#define pj_grp_lock_dec_ref(g) pj_grp_lock_dec_ref_dbg(g, __FILE__, __LINE__)
+
+PJ_DECL(pj_status_t) pj_grp_lock_dec_ref_dbg(pj_grp_lock_t *grp_lock,
+ const char *file,
+ int line);
+
+#endif
+
+/**
+ * Get current reference count value. This normally is only used for
+ * debugging purpose.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return The reference count value.
+ */
+PJ_DECL(int) pj_grp_lock_get_ref(pj_grp_lock_t *grp_lock);
+
+
+/**
+ * Dump group lock info for debugging purpose. If group lock debugging is
+ * enabled (via PJ_GRP_LOCK_DEBUG) macro, this will print the group lock
+ * reference counter value along with the source file and line. If
+ * debugging is disabled, this will only print the reference counter.
+ *
+ * @param grp_lock The group lock.
+ */
+PJ_DECL(void) pj_grp_lock_dump(pj_grp_lock_t *grp_lock);
+
+
+/**
+ * Synchronize an external lock with the group lock, by adding it to the
+ * list of locks to be acquired by the group lock when the group lock is
+ * acquired.
+ *
+ * The ''pos'' argument specifies the lock order and also the relative
+ * position with regard to lock ordering against the group lock. Locks with
+ * lower ''pos'' value will be locked first, and those with negative value
+ * will be locked before the group lock (the group lock's ''pos'' value is
+ * zero).
+ *
+ * @param grp_lock The group lock.
+ * @param ext_lock The external lock
+ * @param pos The position.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_chain_lock(pj_grp_lock_t *grp_lock,
+ pj_lock_t *ext_lock,
+ int pos);
+
+/**
+ * Remove an external lock from group lock's list of synchronized locks.
+ *
+ * @param grp_lock The group lock.
+ * @param ext_lock The external lock
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_unchain_lock(pj_grp_lock_t *grp_lock,
+ pj_lock_t *ext_lock);
+
+
+/** @} */
+
+
PJ_END_DECL
diff --git a/pjlib/include/pj/timer.h b/pjlib/include/pj/timer.h
index c2182ce3..59149385 100644
--- a/pjlib/include/pj/timer.h
+++ b/pjlib/include/pj/timer.h
@@ -24,6 +24,7 @@
*/
#include <pj/types.h>
+#include <pj/lock.h>
PJ_BEGIN_DECL
@@ -118,6 +119,12 @@ typedef struct pj_timer_entry
*/
pj_time_val _timer_value;
+ /**
+ * Internal: the group lock used by this entry, set when
+ * pj_timer_heap_schedule_w_lock() is used.
+ */
+ pj_grp_lock_t *_grp_lock;
+
#if PJ_TIMER_DEBUG
const char *src_file;
int src_line;
@@ -229,7 +236,46 @@ PJ_DECL(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
#endif /* PJ_TIMER_DEBUG */
/**
- * Cancel a previously registered timer.
+ * Schedule a timer entry which will expire AFTER the specified delay, and
+ * increment the reference counter of the group lock while the timer entry
+ * is active. The group lock reference counter will automatically be released
+ * after the timer callback is called or when the timer is cancelled.
+ *
+ * @param ht The timer heap.
+ * @param entry The entry to be registered.
+ * @param id_val The value to be set to the "id" field of the timer entry
+ * once the timer is scheduled.
+ * @param delay The interval to expire.
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+#if PJ_TIMER_DEBUG
+# define pj_timer_heap_schedule_w_grp_lock(ht,e,d,id,g) \
+ pj_timer_heap_schedule_w_grp_lock_dbg(ht,e,d,id,g,__FILE__,__LINE__)
+
+ PJ_DECL(pj_status_t) pj_timer_heap_schedule_w_grp_lock_dbg(
+ pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay,
+ int id_val,
+ pj_grp_lock_t *grp_lock,
+ const char *src_file,
+ int src_line);
+#else
+PJ_DECL(pj_status_t) pj_timer_heap_schedule_w_grp_lock(
+ pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay,
+ int id_val,
+ pj_grp_lock_t *grp_lock);
+#endif /* PJ_TIMER_DEBUG */
+
+
+/**
+ * Cancel a previously registered timer. This will also decrement the
+ * reference counter of the group lock associated with the timer entry,
+ * if the entry was scheduled with one.
*
* @param ht The timer heap.
* @param entry The entry to be cancelled.
@@ -241,6 +287,24 @@ PJ_DECL(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
pj_timer_entry *entry);
/**
+ * Cancel only if the previously registered timer is active. This will
+ * also decrement the reference counter of the group lock associated
+ * with the timer entry, if the entry was scheduled with one. In any
+ * case, set the "id" to the specified value.
+ *
+ * @param ht The timer heap.
+ * @param entry The entry to be cancelled.
+ * @param id_val Value to be set to "id"
+ *
+ * @return The number of timer cancelled, which should be one if the
+ * entry has really been registered, or zero if no timer was
+ * cancelled.
+ */
+PJ_DECL(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ int id_val);
+
+/**
* Get the number of timer entries.
*
* @param ht The timer heap.
diff --git a/pjlib/include/pj/types.h b/pjlib/include/pj/types.h
index 9f05ce44..04e426b5 100644
--- a/pjlib/include/pj/types.h
+++ b/pjlib/include/pj/types.h
@@ -231,6 +231,9 @@ typedef struct pj_thread_t pj_thread_t;
/** Lock object. */
typedef struct pj_lock_t pj_lock_t;
+/** Group lock */
+typedef struct pj_grp_lock_t pj_grp_lock_t;
+
/** Mutex handle. */
typedef struct pj_mutex_t pj_mutex_t;