freeswitch/libs/sofia-sip/libsofia-sip-ua/su/su_port.c
Michael Jerris 853936abd3 a little update to the sofia-sip library
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@4939 d0543943-73ff-0310-b7d9-9358b9ac24b2
2007-04-15 02:03:41 +00:00

462 lines
14 KiB
C

/*
* This file is part of the Sofia-SIP package
*
* Copyright (C) 2005 Nokia Corporation.
*
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
/**@ingroup su_wait
* @CFILE su_port.c
*
* OS-Independent Socket Syncronization Interface.
*
* This looks like nth reincarnation of "reactor". It implements the
* poll/select/WaitForMultipleObjects and message passing functionality.
* This is virtual implementation:
*
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
* @author Kai Vehmanen <kai.vehmanen@nokia.com>
*
* @date Created: Tue Sep 14 15:51:04 1999 ppessi
*/
#include "config.h"
#define SU_CLONE_T su_msg_t
#define su_port_s su_virtual_port_s
#include "su_port.h"
#include <string.h>
#include <stdlib.h>
/** Create the default su_port_t implementation. */
su_port_t *su_default_port_create(void)
{
#if HAVE_EPOLL
return su_epoll_port_create();
#elif HAVE_KQUEUE
return su_kqueue_port_create();
#elif HAVE_SYS_DEVPOLL_H
return su_devpoll_port_create();
#elif HAVE_POLL_PORT
return su_poll_port_create();
#elif HAVE_WIN32
return su_wsaevent_port_create();
#elif HAVE_SELECT
return su_select_port_create();
#else
return NULL;
#endif
}
int su_default_clone_start(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit)
{
#if HAVE_EPOLL
return su_epoll_clone_start(parent, return_clone, magic, init, deinit);
#elif HAVE_KQUEUE
return su_kqueue_clone_start(parent, return_clone, magic, init, deinit);
#elif HAVE_SYS_DEVPOLL_H
return su_devpoll_clone_start(parent, return_clone, magic, init, deinit);
#elif HAVE_POLL_PORT
return su_poll_clone_start(parent, return_clone, magic, init, deinit);
#elif HAVE_WIN32
return su_wsaevent_clone_start(parent, return_clone, magic, init, deinit);
#elif HAVE_SELECT
return su_select_clone_start(parent, return_clone, magic, init, deinit);
#else
errno = ENOSYS;
return -1;
#endif
}
static su_port_create_f *preferred_su_port_create;
static su_clone_start_f *preferred_su_clone_start;
/** Explicitly set the preferred su_port_t implementation.
*
* @sa su_epoll_port_create(), su_poll_port_create(), su_select_port_create()
*/
void su_port_prefer(su_port_create_f *create,
su_clone_start_f *start)
{
if (create) preferred_su_port_create = create;
if (start) preferred_su_clone_start = start;
}
void su_port_set_system_preferences(char const *name)
{
su_port_create_f *create = preferred_su_port_create;
su_clone_start_f *start = preferred_su_clone_start;
if (name == NULL)
;
#if HAVE_EPOLL
else if (strcmp(name, "epoll") == 0) {
create = su_epoll_port_create;
start = su_epoll_clone_start;
}
#endif
#if HAVE_KQUEUE
else if (strcmp(name, "kqueue") == 0) {
create = su_kqueue_port_create;
start = su_kqueue_clone_start;
}
#endif
#if HAVE_SYS_DEVPOLL_H
else if (strcmp(name, "devpoll") == 0) {
create = su_devpoll_port_create;
start = su_devpoll_clone_start;
}
#endif
#if HAVE_POLL_PORT
else if (strcmp(name, "poll") == 0) {
create = su_poll_port_create;
start = su_poll_clone_start;
}
#endif
#if HAVE_WIN32
else if (strcasecmp(name, "wsaevent") == 0) {
create = su_wsaevent_port_create;
start = su_wsaevent_clone_start;
}
#elif HAVE_SELECT
else if (strcmp(name, "select") == 0) {
create = su_select_port_create;
start = su_select_clone_start;
}
#endif
if (create == NULL)
create = su_default_port_create;
if (!preferred_su_port_create ||
preferred_su_port_create == su_default_port_create)
preferred_su_port_create = create;
if (start == NULL)
start = su_default_clone_start;
if (!preferred_su_clone_start ||
preferred_su_clone_start == su_default_clone_start)
preferred_su_clone_start = start;
}
/** Create the preferred su_port_t implementation. */
su_port_t *su_port_create(void)
{
if (preferred_su_port_create == NULL)
su_port_set_system_preferences(getenv("SU_PORT"));
return preferred_su_port_create();
}
/** Return name of the su_port_t instance. */
char const *su_port_name(su_port_t const *port)
{
return port->sup_vtable->su_port_name(port);
}
/* ========================================================================
* su_clone_t
*/
/**@ingroup su_wait
*
* @page su_clone_t Clone Objects
*
* The process may be divided into many tasks via cloning. Several tasks may
* run in context of one thread, or each task may be run by its own thread.
* However, only a single thread can execute code within a task. There can
* be a 1-to-N mapping from thread to tasks. Thus, software using tasks can
* be executed by multiple threads in a multithreaded environment and by a
* single thread in a singlethreaded environment.
*
* The clones are useful for handling tasks that can be executed by a
* separate threads, but which do not block excessively. When threads are
* not available or they are not needed, clones can also be run in a
* single-threaded mode. Running in single-threaded mode is especially
* useful while debugging.
*
* A clone task is created with function su_clone_start(). Each clone has
* its own root object (su_root_t), which holds a context pointer
* (su_root_magic_t *). The context object can be different from that of
* parent task.
*
* When a clone is started, the clone initialization function is called. The
* initialization function should do whatever initialization there is to be
* performed, register I/O events and timers, and then return. If the
* initialization is successful, the clone task reverts to run the event
* loop and invoking the event callbacks until its parent stops it by
* calling su_clone_wait() which invokes the deinit function. The clone task
* is destroyed when the deinit function returns.
*
* The public API consists of following functions:
* - su_clone_start()
* - su_clone_task()
* - su_clone_wait()
* - su_clone_forget()
*
* @note
* There is only one event loop for each thread which can be shared by
* multiple clone tasks. Therefore, the clone tasks can not explicitly run
* or step the event loop, but they are limited to event callbacks. A clone
* task may not call su_root_break(), su_root_run() or su_root_step().
*/
static int su_root_init_nothing(su_root_t *root, su_root_magic_t *magic)
{
return 0;
}
static void su_root_deinit_nothing(su_root_t *root, su_root_magic_t *magic)
{
}
/** Start a clone task.
*
* Allocate and initialize a sub-task. Depending on the su_root_threading()
* settings, a separate thread may be created to execute the sub-task. The
* sub-task is represented by clone handle to the rest of the application.
* The function su_clone_start() returns the clone handle in @a
* return_clone. The clone handle is used to communicate with the newly
* created clone task using messages.
*
* A new #su_root_t object is created for the sub-task with the @a magic as
* the root context pointer. Because the sub-task may or may not have its
* own thread, all its activity must be scheduled via this root object. In
* other words, the sub-task can be schedule
* -# I/O events with su_root_register()
* -# timers with su_timer_set(), su_timer_set_at() or su_timer_run()
* -# messages with su_msg_send().
*
* Messages can also be used to pass information between tasks or threads.
*
* In multi-threaded implementation, su_clone_start() launches a new thread,
* and the initialization routine is executed by this newly created thread.
* The calling thread blocks until the initialization routine completes. If
* the initialization routine returns #su_success (0), the sub-task is
* considered to be created successfully. After the successful
* initialization, the sub-task continues to execeute the function
* su_root_run().
*
* In single-threaded implementations, just a new root object is created.
* The initialization routine is called directly from su_clone_start().
*
* If the initalization function @a init fails, the sub-task (either the
* newly created thread or the current thread executing the su_clone_start()
* function) calls the deinitialization function, and su_clone_start()
* returns NULL.
*
* @param parent root to be cloned
* @param return_clone reference to a clone [OUT]
* @param magic pointer to user data
* @param init initialization function
* @param deinit deinitialization function
*
* @return 0 if successfull, -1 upon an error.
*
* @note Earlier documentation mentioned that @a parent could be NULL. That
* feature has never been implemented, however.
*
* @sa su_root_threading(), su_clone_task(), su_clone_stop(), su_clone_wait(),
* su_clone_forget().
*/
int su_clone_start(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit)
{
su_port_vtable_t const *svp;
if (init == NULL)
init = su_root_init_nothing;
if (deinit == NULL)
deinit = su_root_deinit_nothing;
if (parent == NULL || parent->sur_threading) {
if (preferred_su_clone_start == NULL)
su_port_set_system_preferences(getenv("SU_PORT"));
return preferred_su_clone_start(parent, return_clone, magic, init, deinit);
}
svp = parent->sur_task->sut_port->sup_vtable;
if (svp->su_port_start_shared == NULL)
return su_seterrno(EINVAL);
/* Return a task sharing the same port. */
return svp->su_port_start_shared(parent, return_clone, magic, init, deinit);
}
/** Get reference to a clone task.
*
* @param clone Clone pointer
*
* @return A reference to the task structure of the clone.
*/
_su_task_r su_clone_task(su_clone_r clone)
{
return su_msg_to(clone);
}
/**Forget the clone.
*
* Normally, the clone task executes until it is stopped. If the parent
* task does not need to stop the task, it can "forget" the clone. The
* clone exits independently of the parent task.
*
* @param rclone Reference to the clone.
*/
void su_clone_forget(su_clone_r rclone)
{
su_msg_destroy(rclone);
}
/** Stop the clone.
*
* This can used only if clone task has sent no report messages (messages
* with delivery report sent back to clone).
*
* @deprecated. Use su_clone_wait().
*/
void su_clone_stop(su_clone_r rclone)
{
su_msg_send(rclone);
}
/** Stop a clone and wait until it is has completed.
*
* The function su_clone_wait() is used to stop the clone task and wait
* until it has cleaned up. The clone task is destroyed asynchronously. The
* parent sends a message to clone, clone deinitializes itself and then
* replies. After the reply message is received by the parent, it will send
* a third message back to clone.
*
* The parent destroy all messages to or from clone task before calling
* su_clone_wait(). The parent task may not send any messages to the clone
* after calling su_clone_wait(). The su_clone_wait() function blocks until
* the cloned task is destroyed. During that time, the parent task must be
* prepared to process all the messages sent by clone task. This includes
* all the messages sent by clone before destroy the message reached the
* clone.
*/
void su_clone_wait(su_root_t *root, su_clone_r rclone)
{
if (rclone[0]) {
assert(root == NULL || root == su_msg_from(rclone)->sut_root);
su_port_wait(rclone);
}
}
/** Pause a clone.
*
* Obtain an exclusive lock on clone's private data.
*
* @retval 0 if successful (and clone is paused)
* @retval -1 upon an error
*
* @deprecated Never implemented.
*/
int su_clone_pause(su_clone_r rclone)
{
#if 0
su_root_t *cloneroot = su_task_root(su_msg_to(rclone));
if (!cloneroot)
return (errno = EFAULT), -1;
if (SU_ROOT_OWN_THREAD(cloneroot))
/* We own it already */
return 0;
return su_port_pause(cloneroot->sur_port);
#else
return errno = ENOSYS, -1;
#endif
}
/** Resume a clone.
*
* Give up an exclusive lock on clone's private data.
*
* @retval 0 if successful (and clone is resumed)
* @retval -1 upon an error
*
* @deprecated Never implemented.
*/
int su_clone_resume(su_clone_r rclone)
{
#if 0
su_root_t *cloneroot = su_task_root(su_msg_to(rclone));
if (!cloneroot)
return (errno = EFAULT), -1;
if (SU_ROOT_OWN_THREAD(cloneroot))
/* We cannot give it away */
return 0;
return su_port_resume(cloneroot->sur_port);
#else
return errno = ENOSYS, -1;
#endif
}
void su_port_wait(su_clone_r rclone)
{
su_port_t *cloneport;
cloneport = su_msg_to(rclone)->sut_port;
cloneport->sup_vtable->su_port_wait(rclone);
}
int su_port_execute(su_task_r const task,
int (*function)(void *), void *arg,
int *return_value)
{
if (!task->sut_port->sup_vtable->su_port_execute)
return errno = ENOSYS, -1;
return task->sut_port->sup_vtable->
su_port_execute(task, function, arg, return_value);
}
#if notyet && nomore
int su_port_pause(su_port_t *self)
{
assert(self->sup_vtable->su_port_pause);
return self->sup_vtable->su_port_pause(self);
}
int su_port_resume(su_port_t *self)
{
assert(self->sup_vtable->su_port_resume);
return self->sup_vtable->su_port_resume(self);
}
#endif