Commit e40e2640 authored by Pekka Pessi's avatar Pekka Pessi

su_port.c etc: refactored su_port implementation.

Basic su_port.c implementation using pthreads and poll() is now divided into
three modules: su_base_port.c contains base implementation with su_base_*()
functions, su_pthread_port.c contains pthread-specific implementation and
su_poll_port() contains poll()/epoll()-specific parts. The decomposed
su_port allows implementations on different platforms and usages to share
code as far as possible.

This patch also introduces configure option --disable-poll-port which should
be used on systems with emulated poll, like older *BSD derivatives such as
OS X. Note however that su_select_poll.c is not completed yet.

darcs-hash:20070126155634-65a35-09612536f51f7cd14c33ba42278bc9ed2cee4144.gz
parent 8a5472ff
......@@ -29,6 +29,11 @@ libsofia-sip-ua:
- Added SIP header Refer-Sub and related functions
- Added <sofia-sip/sip_extra.h> include file
- Added auc_info() function (sofia-sip/auth_client.h)
- Added alternative implementations to event reactor object (su_port_t,
referenced by su_root_t) that can be changed at runtime
- Internal semantics of su_port_t reference counting have changed:
now su_port_create() has one reference, and su_root_create_with_port()
uses it reference
- This release is ABI/API compatible with applications linked against
any 1.12.x release. However, applications built against this release won't
work against an older library. The ABI has been tested with the nua module
......
......@@ -77,7 +77,9 @@ libsu_la_SOURCES = \
su_alloc.c su_alloc_lock.c su_strdup.c su_sprintf.c \
su_strlst.c su_vector.c \
su_time.c su_time0.c \
su_wait.c su_root.c su_timer.c su_port.c su_port.h \
su_wait.c su_root.c su_timer.c \
su_port.c su_port.h \
su_base_port.c su_pthread_port.c su_poll_port.c su_select_port.c \
su_localinfo.c \
su_os_nw.c \
su_taglist.c su_tag.c su_tag_io.c \
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* 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_pthread_port.c
*
* OS-Independent Socket Syncronization Interface with pthreads
*
* This implements #su_msg_t message passing functionality using pthreads.
*
* @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"
#include <stdlib.h>
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#define su_pthread_port_s su_port_s
#include "sofia-sip/su.h"
#include "su_port.h"
#include "sofia-sip/su_alloc.h"
#if 1
#define PORT_LOCK_DEBUG(x) ((void)0)
#else
#define PORT_LOCK_DEBUG(x) printf x
#endif
#if HAVE_SOCKETPAIR
#define SU_MBOX_SEND 1
#else
#define SU_MBOX_SEND 0
#endif
/** @internal Message box wakeup function. */
static int su_mbox_port_wakeup(su_root_magic_t *magic, /* NULL */
su_wait_t *w,
su_wakeup_arg_t *arg)
{
char buf[32];
su_socket_t socket = *(su_socket_t*)arg;
su_wait_events(w, socket);
recv(socket, buf, sizeof(buf), 0);
return 0;
}
/**@internal
*
* Initializes a message port. It creates a mailbox used to wake up the
* thread waiting on the port if needed. Currently, the mailbox is a
* socketpair or an UDP socket connected to itself.
*/
int su_pthread_port_init(su_port_t *self, su_port_vtable_t const *vtable)
{
SU_DEBUG_9(("su_pthread_port_init(%p, %p) called\n", self, vtable));
if (su_base_port_init(self, vtable) == 0) {
int af;
su_socket_t mb = INVALID_SOCKET;
su_wait_t wait[1] = { SU_WAIT_INIT };
char const *why;
self->sup_tid = pthread_self();
pthread_mutex_init(self->sup_mutex, NULL);
#if HAVE_SOCKETPAIR
#if defined(AF_LOCAL)
af = AF_LOCAL;
#else
af = AF_UNIX;
#endif
if (socketpair(af, SOCK_STREAM, 0, self->sup_mbox) == -1) {
why = "socketpair"; goto error;
}
mb = self->sup_mbox[0];
su_setblocking(self->sup_mbox[1], 0);
#else
{
struct sockaddr_in sin = { sizeof(struct sockaddr_in), 0 };
socklen_t sinsize = sizeof sin;
struct sockaddr *sa = (struct sockaddr *)&sin;
af = PF_INET;
self->sup_mbox[0] = mb = su_socket(af, SOCK_DGRAM, IPPROTO_UDP);
if (mb == INVALID_SOCKET) {
why = "socket"; goto error;
}
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* 127.1 */
/* Get a port for us */
if (bind(mb, sa, sizeof sin) == -1) {
why = "bind"; goto error;
}
if (getsockname(mb, sa, &sinsize) == -1) {
why = "getsockname"; goto error;
}
if (connect(mb, sa, sinsize) == -1) {
why = "connect"; goto error;
}
}
#endif
su_setblocking(mb, 0);
if (su_wait_create(wait, mb, SU_WAIT_IN) == -1) {
why = "su_wait_create";
goto error;
}
self->sup_mbox_index = su_port_register(self, NULL, wait,
su_mbox_port_wakeup,
(void *)self->sup_mbox, 0);
if (self->sup_mbox_index <= 0) {
why = "su_port_register";
su_wait_destroy(wait);
goto error;
}
SU_DEBUG_9(("%s() returns %d\n", "su_pthread_port_init", 0));
return 0;
error:
su_log("%s: %s: %s\n",
"su_pthread_port_init", why, su_strerror(su_errno()));
su_pthread_port_deinit(self);
}
SU_DEBUG_9(("%s() returns %d\n", "su_pthread_port_init", -1));
return -1;
}
/** @internal Deinit a base implementation of port. */
void su_pthread_port_deinit(su_port_t *self)
{
assert(self);
if (self->sup_mbox_index > 0)
su_port_deregister(self, self->sup_mbox_index);
self->sup_mbox_index = 0;
if (self->sup_mbox[0] && self->sup_mbox[0] != INVALID_SOCKET)
su_close(self->sup_mbox[0]); self->sup_mbox[0] = INVALID_SOCKET;
#if HAVE_SOCKETPAIR
if (self->sup_mbox[1] && self->sup_mbox[1] != INVALID_SOCKET)
su_close(self->sup_mbox[1]); self->sup_mbox[1] = INVALID_SOCKET;
#endif
su_base_port_deinit(self);
}
void su_pthread_port_lock(su_port_t *self, char const *who)
{
PORT_LOCK_DEBUG(("%p at %s locking(%p)...",
(void *)pthread_self(), who, self));
pthread_mutex_lock(self->sup_mutex);
PORT_LOCK_DEBUG((" ...%p at %s locked(%p)...",
(void *)pthread_self(), who, self));
}
void su_pthread_port_unlock(su_port_t *self, char const *who)
{
pthread_mutex_unlock(self->sup_mutex);
PORT_LOCK_DEBUG((" ...%p at %s unlocked(%p)\n",
(void *)pthread_self(), who, self));
}
/** @internal Send a message to the port. */
int su_pthread_port_send(su_port_t *self, su_msg_r rmsg)
{
int wakeup = su_base_port_send(self, rmsg);
if (wakeup < 0)
return -1;
if (wakeup == 0)
return 0;
assert(self->sup_mbox[SU_MBOX_SEND] != INVALID_SOCKET);
if (send(self->sup_mbox[SU_MBOX_SEND], "X", 1, 0) == -1) {
#if HAVE_SOCKETPAIR
if (su_errno() != EWOULDBLOCK)
#endif
su_perror("su_msg_send: send()");
}
return 0;
}
/** @internal
* Checks if the calling thread owns the port object.
*
* @param self pointer to a port object
*
* @retval true (nonzero) if the calling thread owns the port,
* @retval false (zero) otherwise.
*/
int su_pthread_port_own_thread(su_port_t const *self)
{
return self == NULL ||
pthread_equal(self->sup_tid, pthread_self());
}
......@@ -136,11 +136,11 @@ 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; }
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; } \
su_port_decref(t->sut_port, #f); t->sut_port = NULL; } \
t->sut_root = NULL; } while(0)
/**
......@@ -186,7 +186,7 @@ _su_task_r su_task_new(su_task_r task, su_root_t *root, su_port_t *port)
task->sut_root = root;
if ((task->sut_port = port)) {
SU_PORT_INCREF(port, su_task_new);
su_port_incref(port, "su_task_new");
}
return task;
}
......@@ -207,14 +207,14 @@ void su_task_copy(su_task_r dst, su_task_r const src)
port = src->sut_port;
if (port) {
SU_PORT_INCREF(port, su_task_copy);
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)
(s)->sut_port?(void)su_port_incref(s->sut_port, #by):(void)0)
/**
* Moves a task handle.
......@@ -242,8 +242,10 @@ void su_task_move(su_task_r dst, su_task_r src)
*/
int su_task_cmp(su_task_r const a, su_task_r const b)
{
intptr_t retval = a->sut_port - b->sut_port;
retval = retval ? retval : (char *)a->sut_root - (char *)b->sut_root;
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)
......@@ -486,9 +488,13 @@ su_root_t *su_root_create(su_root_magic_t *magic)
return su_root_create_with_port(magic, su_port_create());
}
/** Create a reactor object using given message port.
/**@internal
*
* Allocate and initialize the instance of su_root_t.
* 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
......@@ -512,11 +518,13 @@ su_root_t *su_root_create_with_port(su_root_magic_t *magic,
#else
self->sur_threading = 0;
#endif
/* This one creates a new reference to port */
su_task_new(self->sur_task, self, port);
} else {
su_port_decref(port, "su_root_create");
/* ... so we zap the old one below */
}
su_port_decref(port, "su_root_create_with_port");
return self;
}
......@@ -852,12 +860,20 @@ su_duration_t su_root_sleep(su_root_t *self, su_duration_t duration)
int su_root_yield(su_root_t *self)
{
if (self && self->sur_task[0].sut_port) {
su_port_t *port = self->sur_task[0].sut_port;
su_virtual_port_t *port = (su_virtual_port_t *)self->sur_task[0].sut_port;
/* Make sure we have su_port_wait_events extension */
if (port->sup_vtable->su_vtable_size >=
offsetof(su_port_vtable_t, su_port_wait_events)
&& port->sup_vtable->su_port_wait_events)
return port->sup_vtable->
su_port_wait_events(self->sur_task[0].sut_port, 0);
/* Make sure we have su_port_yield extension */
if (port->sup_vtable->su_vtable_size >=
offsetof(su_port_vtable_t, su_port_yield)
&& port->sup_vtable->su_port_yield)
return port->sup_vtable->su_port_yield(port);
return port->sup_vtable->
su_port_yield(self->sur_task[0].sut_port);
}
errno = EINVAL;
return -1;
......@@ -1016,10 +1032,9 @@ static void *su_clone_main(void *varg)
if (!port)
pthread_exit(NULL);
su_port_threadsafe(port);
SU_PORT_INCREF(port, su_clone_main);
/* Change task ownership */
SU_PORT_INCREF(self->sur_task->sut_port = port, su_clone_main);
su_port_incref(self->sur_task->sut_port = port, "su_clone_main");
self->sur_task->sut_root = self;
if (su_msg_create(arg->clone,
......@@ -1057,7 +1072,7 @@ static void *su_clone_main(void *varg)
su_root_destroy(self); /* Cleanup root */
SU_PORT_ZAPREF(port, su_clone_main);
su_port_zapref(port, "su_clone_main");
#if SU_HAVE_WINSOCK
su_deinit();
......@@ -1405,12 +1420,9 @@ int su_msg_create(su_msg_r rmsg,
su_msg_f wakeup,
isize_t size)
{
su_port_t *port = to->sut_port;
su_msg_t *msg;
SU_PORT_LOCK(port, su_msg_create);
msg = su_zalloc(NULL /*port->sup_home*/, sizeof(*msg) + size);
SU_PORT_UNLOCK(port, su_msg_create);
msg = su_zalloc(NULL, sizeof(*msg) + size);
if (msg) {
msg->sum_size = sizeof(*msg) + size;
......@@ -1522,14 +1534,14 @@ void su_msg_destroy(su_msg_r rmsg)
if (rmsg[0]) {
/* su_port_t *port = rmsg[0]->sum_to->sut_port; */
/* SU_PORT_INCREF(port, su_msg_destroy); */
/* su_port_incref(port, "su_msg_destroy"); */
SU_TASK_ZAP(rmsg[0]->sum_to, su_msg_destroy);
SU_TASK_ZAP(rmsg[0]->sum_from, su_msg_destroy);
su_free(NULL /* port->sup_home */, rmsg[0]);
/* SU_PORT_UNLOCK(port, su_msg_destroy); */
/* SU_PORT_DECREF(port, su_msg_destroy); */
/* su_port_decref(port, "su_msg_destroy"); */
}
rmsg[0] = NULL;
......
This diff is collapsed.
......@@ -47,7 +47,7 @@ int tstflags;
#define TSTFLAGS tstflags
#include <sofia-sip/tstdef.h>
char const *name = "su_torture";
char const *name = "torture_su";
static int test_sockaddr(void);
......
......@@ -78,8 +78,8 @@ static int test_alloc(void)
d0 = d1a = d1 = d2 = d3 = 0;
h0->p = &d0; h1->p = &d1a;
TEST(su_home_desctructor(h0->home, exdestructor), 0);
TEST(su_home_desctructor(h1->home, exdestructor), 0);
TEST(su_home_destructor(h0->home, exdestructor), 0);
TEST(su_home_destructor(h1->home, exdestructor), 0);
TEST_1(h2 = su_home_ref(h0->home));
su_home_unref(h0->home);
......@@ -95,7 +95,7 @@ static int test_alloc(void)
TEST(d1a, destructed_once);
TEST_1(h1 = su_home_clone(h0->home, sizeof(*h1)));
TEST(su_home_desctructor(h1->home, exdestructor), 0);
TEST(su_home_destructor(h1->home, exdestructor), 0);
h1->p = &d1;
for (i = 0; i < 128; i++)
......
......@@ -24,7 +24,7 @@
/**
* @file torture_su_port.c
* @brief Test su_port interface
* @brief Test su_poll_port interface
*
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
* @date Created: Wed Mar 10 17:05:23 2004 ppessi
......@@ -39,7 +39,7 @@ struct su_root_magic_s;
#define SU_ROOT_MAGIC_T struct su_root_magic_s
#include "su_port.c"
#include "su_poll_port.c"
#if HAVE_FUNC
#elif HAVE_FUNCTION
......@@ -56,7 +56,7 @@ int tstflags;
char const *name = "torture_su_port";
int N0 = SU_HAVE_MBOX, N = 128, I = 128 + 1;
int const N0 = SU_MBOX_SIZE > 0, N = 128, I = 129;
int test_sup_indices(su_port_t const *port)
{
......@@ -144,7 +144,7 @@ int test_wakeup(su_port_t *port, su_root_magic_t *magic)
su_perror("getsockname"), exit(1);
if (su_sendto(magic->sockets[1], "X", 1, 0, su, sulen) < 0)
su_perror("su_sendto"), exit(1);
n = su_port_wait_events(port, 100);
n = su_poll_port_wait_events(port, 100);
if (n != 1)
return 1;
if (magic->error)
......@@ -188,9 +188,10 @@ int test_register(void)
su_root_size_hint = 16;
TEST_1(port = su_port_create());
TEST_1(port = su_poll_port_create());
TEST(su_port_threadsafe(port), 0);
SU_PORT_INCREF(port, __func__);
/* Before 1.12.4 su_port_create() had reference count 0 after creation */
/* su_port_incref(port, "test_register"); */
TEST_1(test_sup_indices(port));
......@@ -292,7 +293,7 @@ int test_register(void)
TEST(su_port_deregister(port, reg[i]), -1);
}
TEST_VOID(su_port_decref(port, 1, __func__));
TEST_VOID(su_port_decref(port, __func__));
END();
}
......
......@@ -457,6 +457,20 @@ fi
SAC_REPLACE_FUNCS([memmem memccpy memspn memcspn strcasestr strtoull \
inet_ntop inet_pton])
# ===========================================================================
# Check how to implement su_port
# ===========================================================================
AC_ARG_ENABLE(poll-port,
[ --disable-poll-port disable su_poll_port (enabled)
Use this option in systems emulating poll
with select], , enable_poll_port=yes)
if test $enable_poll_port = yes ; then
if test $ac_cv_func_poll = yes ; then
AC_DEFINE([HAVE_POLL_PORT], 1, [Define to 1 if you use poll in su_port.])
fi
fi
# ===========================================================================
# Check pthread_rwlock_unlock()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment