Merge "res_pjsip: Add serialized scheduler (res_pjsip/pjsip_scheduler.c)" into 13

This commit is contained in:
Joshua Colp
2016-04-27 10:12:31 -05:00
committed by Gerrit Code Review
6 changed files with 1291 additions and 9 deletions

View File

@@ -19,6 +19,7 @@
#include "asterisk/compat.h"
#include "asterisk/lock.h"
#include "asterisk/inline_api.h"
/*! \file
* \ref AstObj2
@@ -638,6 +639,46 @@ int __ao2_trylock(void *a, enum ao2_lock_req lock_how, const char *file, const c
void *ao2_object_get_lockaddr(void *obj);
/*!
* \brief Increment reference count on an object and lock it
* \since 13.9.0
*
* \param[in] obj A pointer to the ao2 object
* \retval 0 The object is not an ao2 object or wasn't locked successfully
* \retval 1 The object's reference count was incremented and was locked
*/
AST_INLINE_API(
int ao2_ref_and_lock(void *obj),
{
ao2_ref(obj, +1);
if (ao2_lock(obj)) {
ao2_ref(obj, -1);
return 0;
}
return 1;
}
)
/*!
* \brief Unlock an object and decrement its reference count
* \since 13.9.0
*
* \param[in] obj A pointer to the ao2 object
* \retval 0 The object is not an ao2 object or wasn't unlocked successfully
* \retval 1 The object was unlocked and it's reference count was decremented
*/
AST_INLINE_API(
int ao2_unlock_and_unref(void *obj),
{
if (ao2_unlock(obj)) {
return 0;
}
ao2_ref(obj, -1);
return 1;
}
)
/*! Global ao2 object holder structure. */
struct ao2_global_obj {
/*! Access lock to the held ao2 object. */
@@ -1985,4 +2026,97 @@ void ao2_iterator_cleanup(struct ao2_iterator *iter);
*/
int ao2_iterator_count(struct ao2_iterator *iter);
/*!
* \brief Creates a hash function for a structure string field.
* \param stype The structure type
* \param field The string field in the structure to hash
*
* AO2_STRING_FIELD_HASH_CB(mystruct, myfield) will produce a function
* named mystruct_hash_fn which hashes mystruct->myfield.
*/
#define AO2_STRING_FIELD_HASH_FN(stype, field) \
static int stype ## _hash_fn(const void *obj, const int flags) \
{ \
const struct stype *object = obj; \
const char *key; \
switch (flags & OBJ_SEARCH_MASK) { \
case OBJ_SEARCH_KEY: \
key = obj; \
break; \
case OBJ_SEARCH_OBJECT: \
key = object->field; \
break; \
default: \
ast_assert(0); \
return 0; \
} \
return ast_str_hash(key); \
}
/*!
* \brief Creates a compare function for a structure string field.
* \param stype The structure type
* \param field The string field in the structure to compare
*
* AO2_STRING_FIELD_CMP_FN(mystruct, myfield) will produce a function
* named mystruct_cmp_fn which compares mystruct->myfield.
*/
#define AO2_STRING_FIELD_CMP_FN(stype, field) \
static int stype ## _cmp_fn(void *obj, void *arg, int flags) \
{ \
const struct stype *object_left = obj, *object_right = arg; \
const char *right_key = arg; \
int cmp; \
switch (flags & OBJ_SEARCH_MASK) { \
case OBJ_SEARCH_OBJECT: \
right_key = object_right->field; \
case OBJ_SEARCH_KEY: \
cmp = strcmp(object_left->field, right_key); \
break; \
case OBJ_SEARCH_PARTIAL_KEY: \
cmp = strncmp(object_left->field, right_key, strlen(right_key)); \
break; \
default: \
cmp = 0; \
break; \
} \
if (cmp) { \
return 0; \
} \
return CMP_MATCH; \
}
/*!
* \brief Creates a sort function for a structure string field.
* \param stype The structure type
* \param field The string field in the structure to compare
*
* AO2_STRING_FIELD_SORT_FN(mystruct, myfield) will produce a function
* named mystruct_sort_fn which compares mystruct->myfield.
*/
#define AO2_STRING_FIELD_SORT_FN(stype, field) \
static int stype ## _sort_fn(const void *obj, const void *arg, int flags) \
{ \
const struct stype *object_left = obj; \
const struct stype *object_right = arg; \
const char *right_key = arg; \
int cmp; \
\
switch (flags & OBJ_SEARCH_MASK) { \
case OBJ_SEARCH_OBJECT: \
right_key = object_right->field; \
/* Fall through */ \
case OBJ_SEARCH_KEY: \
cmp = strcmp(object_left->field, right_key); \
break; \
case OBJ_SEARCH_PARTIAL_KEY: \
cmp = strncmp(object_left->field, right_key, strlen(right_key)); \
break; \
default: \
cmp = 0; \
break; \
} \
return cmp; \
}
#endif /* _ASTERISK_ASTOBJ2_H */

View File

@@ -19,6 +19,13 @@
#ifndef _RES_PJSIP_H
#define _RES_PJSIP_H
#include <pjsip.h>
/* Needed for SUBSCRIBE, NOTIFY, and PUBLISH method definitions */
#include <pjsip_simple.h>
#include <pjsip/sip_transaction.h>
#include <pj/timer.h>
#include <pjlib.h>
#include "asterisk/stringfields.h"
/* Needed for struct ast_sockaddr */
#include "asterisk/netsock2.h"
@@ -1174,8 +1181,9 @@ struct ast_sip_auth *ast_sip_get_artificial_auth(void);
*/
struct ast_sip_endpoint *ast_sip_get_artificial_endpoint(void);
/*!
* \page Threading model for SIP
/*! \defgroup pjsip_threading PJSIP Threading Model
* @{
* \page PJSIP PJSIP Threading Model
*
* There are three major types of threads that SIP will have to deal with:
* \li Asterisk threads
@@ -1224,6 +1232,19 @@ struct ast_sip_endpoint *ast_sip_get_artificial_endpoint(void);
* previous tasks pushed with the same serializer have completed. For more information
* on serializers and the benefits they provide, see \ref ast_threadpool_serializer
*
* \par Scheduler
*
* Some situations require that a task run periodically or at a future time. Normally
* the ast_sched functionality would be used but ast_sched only uses 1 thread for all
* tasks and that thread isn't registered with PJLIB and therefore can't do any PJSIP
* related work.
*
* ast_sip_sched uses ast_sched only as a scheduled queue. When a task is ready to run,
* it's pushed to a Serializer to be invoked asynchronously by a Servant. This ensures
* that the task is executed in a PJLIB registered thread and allows the ast_sched thread
* to immediately continue processing the queue. The Serializer used by ast_sip_sched
* is one of your choosing or a random one from the res_pjsip pool if you don't choose one.
*
* \note
*
* Do not make assumptions about individual threads based on a corresponding serializer.
@@ -1232,6 +1253,8 @@ struct ast_sip_endpoint *ast_sip_get_artificial_endpoint(void);
* tasks, even though they are all guaranteed to be executed in sequence.
*/
typedef int (*ast_sip_task)(void *user_data);
/*!
* \brief Create a new serializer for SIP tasks
*
@@ -1368,6 +1391,214 @@ int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*si
*/
int ast_sip_thread_is_servant(void);
/*!
* \brief Task flags for the res_pjsip scheduler
*
* The default is AST_SIP_SCHED_TASK_FIXED
* | AST_SIP_SCHED_TASK_DATA_NOT_AO2
* | AST_SIP_SCHED_TASK_DATA_NO_CLEANUP
* | AST_SIP_SCHED_TASK_PERIODIC
*/
enum ast_sip_scheduler_task_flags {
/*!
* The defaults
*/
AST_SIP_SCHED_TASK_DEFAULTS = (0 << 0),
/*!
* Run at a fixed interval.
* Stop scheduling if the callback returns 0.
* Any other value is ignored.
*/
AST_SIP_SCHED_TASK_FIXED = (0 << 0),
/*!
* Run at a variable interval.
* Stop scheduling if the callback returns 0.
* Any other return value is used as the new interval.
*/
AST_SIP_SCHED_TASK_VARIABLE = (1 << 0),
/*!
* The task data is not an AO2 object.
*/
AST_SIP_SCHED_TASK_DATA_NOT_AO2 = (0 << 1),
/*!
* The task data is an AO2 object.
* A reference count will be held by the scheduler until
* after the task has run for the final time (if ever).
*/
AST_SIP_SCHED_TASK_DATA_AO2 = (1 << 1),
/*!
* Don't take any cleanup action on the data
*/
AST_SIP_SCHED_TASK_DATA_NO_CLEANUP = (0 << 3),
/*!
* If AST_SIP_SCHED_TASK_DATA_AO2 is set, decrement the reference count
* otherwise call ast_free on it.
*/
AST_SIP_SCHED_TASK_DATA_FREE = ( 1 << 3 ),
/*! \brief AST_SIP_SCHED_TASK_PERIODIC
* The task is scheduled at multiples of interval
* \see Interval
*/
AST_SIP_SCHED_TASK_PERIODIC = (0 << 4),
/*! \brief AST_SIP_SCHED_TASK_DELAY
* The next invocation of the task is at last finish + interval
* \see Interval
*/
AST_SIP_SCHED_TASK_DELAY = (1 << 4),
};
/*!
* \brief Scheduler task data structure
*/
struct ast_sip_sched_task;
/*!
* \brief Schedule a task to run in the res_pjsip thread pool
* \since 13.9.0
*
* \param serializer The serializer to use. If NULL, don't use a serializer (see note below)
* \param interval The invocation interval in milliseconds (see note below)
* \param sip_task The task to invoke
* \param name An optional name to associate with the task
* \param task_data Optional data to pass to the task
* \param flags One of enum ast_sip_scheduler_task_type
*
* \returns Pointer to \ref ast_sip_sched_task ao2 object which must be dereferenced when done.
*
* \paragraph Serialization
*
* Specifying a serializer guarantees serialized execution but NOT specifying a serializer
* may still result in tasks being effectively serialized if the thread pool is busy.
* The point of the serializer BTW is not to prevent parallel executions of the SAME task.
* That happens automatically (see below). It's to prevent the task from running at the same
* time as other work using the same serializer, whether or not it's being run by the scheduler.
*
* \paragraph Interval
*
* The interval is used to calculate the next time the task should run. There are two models.
*
* \ref AST_SIP_SCHED_TASK_PERIODIC specifies that the invocations of the task occur at the
* specific interval. That is, every \ref "interval" milliseconds, regardless of how long the task
* takes. If the task takes longer than \ref interval, it will be scheduled at the next available
* multiple of \ref interval. For exmaple: If the task has an interval of 60 seconds and the task
* takes 70 seconds, the next invocation will happen at 120 seconds.
*
* \ref AST_SIP_SCHED_TASK_DELAY specifies that the next invocation of the task should start
* at \ref interval milliseconds after the current invocation has finished.
*
*/
struct ast_sip_sched_task *ast_sip_schedule_task(struct ast_taskprocessor *serializer,
int interval, ast_sip_task sip_task, char *name, void *task_data,
enum ast_sip_scheduler_task_flags flags);
/*!
* \brief Cancels the next invocation of a task
* \since 13.9.0
*
* \param schtd The task structure pointer
* \retval 0 Success
* \retval -1 Failure
* \note Only cancels future invocations not the currently running invocation.
*/
int ast_sip_sched_task_cancel(struct ast_sip_sched_task *schtd);
/*!
* \brief Cancels the next invocation of a task by name
* \since 13.9.0
*
* \param name The task name
* \retval 0 Success
* \retval -1 Failure
* \note Only cancels future invocations not the currently running invocation.
*/
int ast_sip_sched_task_cancel_by_name(const char *name);
/*!
* \brief Gets the last start and end times of the task
* \since 13.9.0
*
* \param schtd The task structure pointer
* \param[out] when_queued Pointer to a timeval structure to contain the time when queued
* \param[out] last_start Pointer to a timeval structure to contain the time when last started
* \param[out] last_end Pointer to a timeval structure to contain the time when last ended
* \retval 0 Success
* \retval -1 Failure
* \note Any of the pointers can be NULL if you don't need them.
*/
int ast_sip_sched_task_get_times(struct ast_sip_sched_task *schtd,
struct timeval *when_queued, struct timeval *last_start, struct timeval *last_end);
/*!
* \brief Gets the last start and end times of the task by name
* \since 13.9.0
*
* \param name The task name
* \param[out] when_queued Pointer to a timeval structure to contain the time when queued
* \param[out] last_start Pointer to a timeval structure to contain the time when last started
* \param[out] last_end Pointer to a timeval structure to contain the time when last ended
* \retval 0 Success
* \retval -1 Failure
* \note Any of the pointers can be NULL if you don't need them.
*/
int ast_sip_sched_task_get_times_by_name(const char *name,
struct timeval *when_queued, struct timeval *last_start, struct timeval *last_end);
/*!
* \brief Gets the number of milliseconds until the next invocation
* \since 13.9.0
*
* \param schtd The task structure pointer
* \return The number of milliseconds until the next invocation or -1 if the task isn't scheduled
*/
int ast_sip_sched_task_get_next_run(struct ast_sip_sched_task *schtd);
/*!
* \brief Gets the number of milliseconds until the next invocation
* \since 13.9.0
*
* \param name The task name
* \return The number of milliseconds until the next invocation or -1 if the task isn't scheduled
*/
int ast_sip_sched_task_get_next_run_by_name(const char *name);
/*!
* \brief Checks if the task is currently running
* \since 13.9.0
*
* \param schtd The task structure pointer
* \retval 0 not running
* \retval 1 running
*/
int ast_sip_sched_is_task_running(struct ast_sip_sched_task *schtd);
/*!
* \brief Checks if the task is currently running
* \since 13.9.0
*
* \param name The task name
* \retval 0 not running or not found
* \retval 1 running
*/
int ast_sip_sched_is_task_running_by_name(const char *name);
/*!
* \brief Gets the task name
* \since 13.9.0
*
* \param schtd The task structure pointer
* \retval 0 success
* \retval 1 failure
*/
int ast_sip_sched_task_get_name(struct ast_sip_sched_task *schtd, char *name, size_t maxlen);
/*!
* @}
*/
/*!
* \brief SIP body description
*