📄 su_root.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_root.c * OS-independent synchronization interface. * @internal * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * * @date Created: Tue Sep 14 15:51:04 1999 ppessi */#include "config.h"#include <stdlib.h>#include <assert.h>#include <stdio.h>#include <string.h>#include <errno.h>#include "sofia-sip/su.h"#if SU_HAVE_PTHREADS#include <pthread.h>#endifstruct su_root_s;#define SU_ROOT_MAGIC_T struct su_root_magic_s#define SU_WAKEUP_ARG_T struct su_wakeup_arg_s#define SU_TIMER_ARG_T struct su_timer_arg_s#include "su_port.h"#include "sofia-sip/su_alloc.h"/**@ingroup su_wait * * @page su_root_t Tasks and root objects * * A task is the basic execution unit for the Sofia event-driven programming * model. According to the model, the program can ask that the event loop * invokes a callback function when a certain event occurs. Such events * include @ref su_root_register "I/O activity", @ref su_timer_t "timers" or * a @ref su_msg_t "message" from other task. The event loop is run with * function su_root_run() or su_root_step(). * * Root object gives access to the task control. The root object represents * the task to the code running within task. Through the root, the task code * can access its context object (magic) and thread-synchronization features * like wait objects, timers, and messages. * * When a message is sent between tasks, a task reference #su_task_r is used * to reprent the task address. Reference counting is used to make sure that * the task references stay valid. * * The public API contains following functions: * - su_root_create() [Do not call from cloned task] * - su_root_destroy() [Do not call from cloned task] * - su_root_magic() * - su_root_register() * - su_root_deregister() * - su_root_unregister() * - su_root_threading() * - su_root_run() [Do not call from cloned task] * - su_root_break() [Do not call from cloned task] * - su_root_step() [Do not call from cloned task] * - su_root_task() * * New tasks can be created via su_clone_start() function. *//**@ingroup su_wait * * @page su_root_register Registering Wait Objects * * When application expects I/O events, it can create a wait object and * register it, a callback function and a context pointer to the #su_root_t * object using the su_root_register() function. Whenever the wait object * receives an event, the registered @link ::su_wakeup_f callback function * @endlink is invoked. * * When successful, the su_root_register() returns an small non-negative * integer representing the registration. The registration can be * manipulated with su_root_eventmask() function, for instance, when sending * through a socket block, the application can add SU_WAIT_OUT event to the * mask. * * The registration can be removed using su_root_deregister() function. *//**@ingroup su_wait * * Contains hint of number of sockets supported by su_root_t */ int su_root_size_hint = 64;/* ========================================================================= * Tasks */su_task_r const su_task_null = SU_TASK_R_INIT;#define SU_TASK_ZAP(t, f) \ while (t->sut_port) { \ su_port_decref(t->sut_port, #f); t->sut_port = NULL; break; }#define SU_TASK_ZAPP(t, f) \ do { if (t->sut_port) { \ su_port_decref(t->sut_port, #f); t->sut_port = NULL; } \ t->sut_root = NULL; } while(0)/** * Initialize a task handle with su_task_null. * * @param task task handle * * @return A reference to the initialized task handle. */_su_task_r su_task_init(su_task_r task){ assert(task); memset(task, 0, sizeof(task)); return task;}/** * Destroy a task handle * * @param task task handle */void su_task_deinit(su_task_r task){ assert(task); SU_TASK_ZAP(task, su_task_deinit); task->sut_root = NULL;}/** * Create a new task handle. * * @param task task reference * @param root pointer to root object * @param port pointer to port object * * @return New task handle. */_su_task_r su_task_new(su_task_r task, su_root_t *root, su_port_t *port){ assert(task); task->sut_root = root; if ((task->sut_port = port)) { su_port_incref(port, "su_task_new"); } return task;}/** * Duplicates a task handle. * * @param dst destination task reference * @param src source task reference */void su_task_copy(su_task_r dst, su_task_r const src){ su_port_t *port; assert(src); assert(dst); SU_TASK_ZAP(dst, su_task_copy); port = src->sut_port; if (port) { su_port_incref(port, "su_task_copy"); } dst[0] = src[0];}#define SU_TASK_COPY(d, s, by) (void)((d)[0]=(s)[0], \ (s)->sut_port?(void)su_port_incref(s->sut_port, #by):(void)0)/** * Moves a task handle. * * @param dst destination task reference * @param src source task reference */void su_task_move(su_task_r dst, su_task_r src){ SU_TASK_ZAP(dst, su_task_move); dst[0] = src[0]; src->sut_port = 0; src->sut_root = 0;}/** * Compare two tasks with each other. * * @param a First task * @param b Second task * * @retval negative number, if a < b * @retval positive number, if a > b * @retval 0, if a == b. */int su_task_cmp(su_task_r const a, su_task_r const b){ intptr_t retval = (char *)a->sut_port - (char *)b->sut_port; if (retval == 0) retval = (char *)a->sut_root - (char *)b->sut_root; if (sizeof(retval) != sizeof(int)) { if (retval < 0) retval = -1; else if (retval > 0) retval = 1; } return (int)retval;}/** * Tests if a task is running. * * @param task task handle * * @retval true (nonzero) if task is not stopped, * @retval zero if it is null or stopped. */int su_task_is_running(su_task_r const task){ return task && task->sut_port && task->sut_root;}/** @internal * Attach a root object to the task handle. * * @param self task handle * @param root pointer to the root object * * @retval 0 if successful, * @retval -1 otherwise. */int su_task_attach(su_task_r self, su_root_t *root){ if (self->sut_port) { self->sut_root = root; return 0; } else return -1;}/** * Get root pointer attached to a task handle. * * @param self task handle * * @return * A pointer to root object attached to the task handle, or NULL if no root * object has been attached. */su_root_t *su_task_root(su_task_r const self){ if (self->sut_port) return self->sut_root; else return NULL;}#if 0/** @internal * Detach a root pointer from task handle. * @bug Not used anymore. */int su_task_detach(su_task_r self){ self->sut_root = NULL; return 0;}#endif/** * Return the timer list associated with given task. * * @param task task handle * * @return A timer list of the task. If there are no timers, it returns * NULL. */su_timer_queue_t *su_task_timers(su_task_r const task){ return task->sut_port ? su_port_timers(task->sut_port) : NULL;}/** Execute the @a function by @a task thread. * * @retval 0 if successful * @retval -1 upon an error */int su_task_execute(su_task_r const task, int (*function)(void *), void *arg, int *return_value){ int dummy; if (function == NULL) return (errno = EFAULT), -1; if (return_value == NULL) return_value = &dummy; if (!su_port_own_thread(task->sut_port)) { return su_port_execute(task, function, arg, return_value); } else { int value = function(arg); if (return_value) *return_value = value; return 0; }}_su_task_r su_task_new(su_task_r task, su_root_t *root, su_port_t *port);int su_task_attach(su_task_r self, su_root_t *root);int su_task_detach(su_task_r self);int su_timer_reset_all(su_timer_t **t0, su_task_r);/* Note that is *not* necessary same as su_root_t, * as su_root_t can be extended */#define sur_port sur_task->sut_port#define sur_root sur_task->sut_root#define SU_ROOT_OWN_THREAD(r) (r->sur_port && su_port_own_thread(r->sur_port))/** Create a reactor object. * * Allocate and initialize the instance of su_root_t. * * @param magic pointer to user data * * @return A pointer to allocated su_root_t instance, NULL on error. */su_root_t *su_root_create(su_root_magic_t *magic){ return su_root_create_with_port(magic, su_port_create());}/**@internal * * Create a reactor object using given message port. * * Allocate and initialize the instance of su_root_t. Note that this * function always uses a reference to su_port_t, even when creating the * root fails. * * @param magic pointer to user data * @param port pointer to a message port * * @return A pointer to allocated su_root_t instance, NULL on error. */su_root_t *su_root_create_with_port(su_root_magic_t *magic, su_port_t *port){ su_root_t *self; if (!port) return NULL; self = su_salloc(su_port_home(port), sizeof(struct su_root_s)); if (self) { self->sur_magic = magic;#if SU_HAVE_PTHREADS self->sur_threading = SU_HAVE_PTHREADS;#else self->sur_threading = 0;#endif /* This one creates a new reference to port */ su_task_new(self->sur_task, self, port); /* ... so we zap the old one below */ } su_port_decref(port, "su_root_create_with_port"); return self;}/** Destroy a root object. * * Stop and free an instance of su_root_t * * @param self pointer to a root object. */void su_root_destroy(su_root_t *self){ su_port_t *port; int unregistered, reset; if (!self) return; assert(SU_ROOT_OWN_THREAD(self)); self->sur_deiniting = 1; if (self->sur_deinit) { su_root_deinit_f deinit = self->sur_deinit; su_root_magic_t *magic = self->sur_magic; self->sur_deinit = NULL; deinit(self, magic); } port = self->sur_port; assert(port); unregistered = su_port_unregister_all(port, self); reset = su_timer_reset_all(su_task_timers(self->sur_task), self->sur_task); if (unregistered || reset) SU_DEBUG_1(("su_root_destroy: " "%u registered waits, %u timers\n", unregistered, reset)); SU_TASK_ZAP(self->sur_parent, su_root_destroy); su_free(su_port_home(port), self); su_port_decref(port, "su_root_destroy");}/** Get instance name. * * @param self pointer to a root object * * @return Instance name (e.g., "epoll", "devpoll", "select"). * * @NEW_1_12_6. */char const *su_root_name(su_root_t *self){ if (!self) return (void)(errno = EFAULT), NULL; assert(self->sur_port); return su_port_name(self->sur_task->sut_port);}/** Set the context pointer. * * Set the context pointer (magic) of a root object. * * @param self pointer to a root object * @param magic pointer to user data * * @retval 0 when successful, * @retval -1 upon error. */int su_root_set_magic(su_root_t *self, su_root_magic_t *magic){ if (self == NULL) return (void)(errno = EFAULT), -1; assert(SU_ROOT_OWN_THREAD(self)); self->sur_magic = magic; return 0;}/** Set threading option. * * Controls whether su_clone_start() creates a new thread. * * @param self pointer to a root object * @param enable if true, enable threading, if false, disable threading * * @return True if threading is enabled. */int su_root_threading(su_root_t *self, int enable){ if (self == NULL) return (void)(errno = EFAULT), -1; assert(SU_ROOT_OWN_THREAD(self));#if SU_HAVE_PTHREADS self->sur_threading = enable = enable != 0; return enable;#else return 0;#endif}/** Get context pointer. * * The function su_root_magic() returns the user context pointer that was * given input to su_root_create() or su_root_set_magic(). * * @param self pointer to a root object * * @return A pointer to user data */su_root_magic_t *su_root_magic(su_root_t *self){ if (!self) return (void)(errno = EFAULT), NULL; return self->sur_magic;}/** Get a GSource */struct _GSource *su_root_gsource(su_root_t *self){ if (!self) return (void)(errno = EFAULT), NULL; assert(self->sur_port); return su_port_gsource(self->sur_port);}/** Register a su_wait_t object. * * The function su_root_register() registers a su_wait_t object. The wait * object, a callback function and a argument are stored to the root * object. The callback function is called, when the wait object is * signaled. * * Please note if identical wait objects are inserted, only first one is * ever signalled. * * @param self pointer to root object * @param wait pointer to wait object * @param callback callback function pointer * @param arg argument given to callback function when it is invoked * @param priority relative priority of the wait object * (0 is normal, 1 important, 2 realtime) * * @return Nonzero index of the wait object, or -1 upon an error. */int su_root_register(su_root_t *self, su_wait_t *wait, su_wakeup_f callback, su_wakeup_arg_t *arg, int priority){ if (!self || !wait) return (void)(errno = EFAULT), -1; assert(self->sur_port); return su_port_register(self->sur_port, self, wait, callback, arg, priority);}/** Unregister a su_wait_t object. * * The function su_root_unregister() unregisters a su_wait_t object. The * wait object, a callback function and a argument are removed from the * root object. * * @param self pointer to root object * @param wait pointer to wait object * @param callback callback function pointer (may be NULL) * @param arg argument given to callback function when it is invoked * (may be NULL) * * @return Nonzero index of the wait object, or -1 upon an error. */int su_root_unregister(su_root_t *self, su_wait_t *wait, su_wakeup_f callback, /* XXX - ignored */ su_wakeup_arg_t *arg){ if (!self || !wait) return (void)(errno = EFAULT), -1; assert(self->sur_port); return su_port_unregister(self->sur_port, self, wait, callback, arg);}/** Remove a su_wait_t registration. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -