2006-12-21 01:30:28 -05:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*
|
2008-12-16 13:05:22 -05:00
|
|
|
* @CFILE su_wait.c
|
2006-12-21 01:30:28 -05:00
|
|
|
* Implementation of OS-independent socket synchronization interface.
|
|
|
|
*
|
|
|
|
* This looks like nth reincarnation of "reactor". It implements the
|
|
|
|
* (poll()/select()/WaitForMultipleObjects()) functionality.
|
|
|
|
*
|
|
|
|
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
|
2007-04-14 22:03:41 -04:00
|
|
|
* @author Martti Mela <Martti.Mela@nokia.com>
|
2006-12-21 01:30:28 -05:00
|
|
|
* @date Created: Tue Sep 14 15:51:04 1999 ppessi
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#define SU_INTERNAL_P su_root_t *
|
|
|
|
|
|
|
|
#include "sofia-sip/su.h"
|
|
|
|
#include "sofia-sip/su_wait.h"
|
|
|
|
#include "sofia-sip/su_alloc.h"
|
|
|
|
|
|
|
|
/**@defgroup su_wait Syncronization and Threading
|
|
|
|
* @brief Syncronization and threading interface.
|
|
|
|
*
|
|
|
|
* The Sofia utility library provides simple OS-independent synchronization
|
|
|
|
* interface. The synchronization interface contains primitives for managing
|
|
|
|
* events, messages, timers and threads.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**@ingroup su_wait
|
|
|
|
* @defgroup su_root_ex Example and test code for syncronization and threads
|
|
|
|
*
|
|
|
|
* Example programs demonstrate the su syncronization and threading
|
|
|
|
* primitives.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/**@ingroup su_wait
|
|
|
|
*
|
|
|
|
* @page su_wait_t Wait objects
|
|
|
|
*
|
|
|
|
* Wait objects are used to signal I/O events to the process.
|
|
|
|
* The events are as follows:
|
|
|
|
*
|
|
|
|
* - SU_WAIT_IN - incoming data is available on socket
|
|
|
|
* - SU_WAIT_OUT - data can be sent on socket
|
|
|
|
* - SU_WAIT_ERR - an error occurred on socket
|
|
|
|
* - SU_WAIT_HUP - the socket connection was closed
|
|
|
|
* - SU_WAIT_ACCEPT - a listening socket accepted a new connection attempt
|
|
|
|
*
|
|
|
|
* It is possible to combine several events with |, binary or operator.
|
|
|
|
*
|
|
|
|
* The wait objects can be managed with functions as follows:
|
|
|
|
* - su_wait_create()
|
|
|
|
* - su_wait_destroy()
|
|
|
|
* - su_wait()
|
|
|
|
* - su_wait_events()
|
|
|
|
* - su_wait_mask()
|
|
|
|
*
|
|
|
|
* @note
|
|
|
|
* In Unix, the wait object is @c struct @c poll. The structure contains a
|
|
|
|
* file descriptor, a mask describing expected events, and a mask
|
|
|
|
* containing the occurred events after calling @c su_wait(), ie. poll().
|
|
|
|
*
|
|
|
|
* @note
|
|
|
|
* In Windows, the wait object is a @c HANDLE (a descriptor of a Windows
|
|
|
|
* kernel entity).
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**Initialize a wait object.
|
|
|
|
*
|
|
|
|
* The function su_wait_init initializes a memory area of a su_wait_t
|
|
|
|
* object.
|
|
|
|
*/
|
|
|
|
void su_wait_init(su_wait_t dst[1])
|
|
|
|
{
|
|
|
|
su_wait_t const src = SU_WAIT_INIT;
|
|
|
|
*dst = src;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**Create a wait object.
|
|
|
|
*
|
|
|
|
* The function su_wait_create() creates a new su_wait_t object for an @a
|
|
|
|
* socket, with given @a events. The new wait object is assigned to the @a
|
|
|
|
* newwait parameter.
|
|
|
|
*
|
|
|
|
* There can be only one wait object per socket. (This is a limitation or
|
|
|
|
* feature of WinSock interface; the limitation is not enforced on other
|
|
|
|
* platforms).
|
|
|
|
*
|
|
|
|
* As a side-effect the socket is put into non-blocking mode when wait
|
|
|
|
* object is created.
|
2008-12-16 13:05:22 -05:00
|
|
|
*
|
2006-12-21 01:30:28 -05:00
|
|
|
* @param newwait the newly created wait object (output)
|
|
|
|
* @param socket socket
|
|
|
|
* @param events mask for events that can signal this wait object
|
2008-12-16 13:05:22 -05:00
|
|
|
*
|
2006-12-21 01:30:28 -05:00
|
|
|
* @retval 0 if the call was successful,
|
2008-12-16 13:05:22 -05:00
|
|
|
* @retval -1 upon an error.
|
2006-12-21 01:30:28 -05:00
|
|
|
*/
|
|
|
|
int su_wait_create(su_wait_t *newwait, su_socket_t socket, int events)
|
|
|
|
{
|
|
|
|
#if SU_HAVE_WINSOCK
|
|
|
|
HANDLE h = WSACreateEvent();
|
|
|
|
|
|
|
|
if (newwait == NULL || events == 0 || socket == INVALID_SOCKET) {
|
|
|
|
su_seterrno(WSAEINVAL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*newwait = 0;
|
|
|
|
|
|
|
|
if (WSAEventSelect(socket, h, events) != 0) {
|
|
|
|
int error = su_errno();
|
|
|
|
WSACloseEvent(h);
|
|
|
|
su_seterrno(error);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*newwait = h;
|
|
|
|
|
2007-04-14 22:03:41 -04:00
|
|
|
#elif SU_HAVE_POLL || HAVE_SELECT
|
2006-12-21 01:30:28 -05:00
|
|
|
int mode;
|
|
|
|
|
|
|
|
if (newwait == NULL || events == 0 || socket == INVALID_SOCKET) {
|
|
|
|
su_seterrno(EINVAL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
mode = fcntl(socket, F_GETFL, 0);
|
|
|
|
if (mode < 0)
|
|
|
|
return -1;
|
|
|
|
mode |= O_NDELAY | O_NONBLOCK;
|
|
|
|
if (fcntl(socket, F_SETFL, mode) < 0)
|
|
|
|
return -1;
|
2008-12-16 13:05:22 -05:00
|
|
|
|
2006-12-21 01:30:28 -05:00
|
|
|
newwait->fd = socket;
|
|
|
|
newwait->events = events;
|
|
|
|
newwait->revents = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Destroy a wait object.
|
|
|
|
*
|
|
|
|
* The function su_wait_destroy() destroys a su_wait_t object.
|
|
|
|
*
|
2008-12-16 13:05:22 -05:00
|
|
|
* @param waitobj pointer to wait object
|
2006-12-21 01:30:28 -05:00
|
|
|
*
|
|
|
|
* @retval 0 when successful,
|
|
|
|
* @retval -1 upon an error.
|
|
|
|
*/
|
|
|
|
int su_wait_destroy(su_wait_t *waitobj)
|
|
|
|
{
|
|
|
|
#if SU_HAVE_WINSOCK
|
2008-05-26 20:17:34 -04:00
|
|
|
su_wait_t w0 = NULL;
|
2008-05-25 11:13:10 -04:00
|
|
|
assert(waitobj != NULL);
|
2008-05-26 20:17:34 -04:00
|
|
|
if (*waitobj) {
|
2006-12-21 01:30:28 -05:00
|
|
|
WSACloseEvent(*waitobj);
|
2008-05-26 20:17:34 -04:00
|
|
|
*waitobj = w0;
|
|
|
|
}
|
2007-04-14 22:03:41 -04:00
|
|
|
#else
|
2006-12-21 01:30:28 -05:00
|
|
|
su_wait_t w0 = { INVALID_SOCKET, 0, 0 };
|
|
|
|
assert(waitobj != NULL);
|
2008-05-26 20:17:34 -04:00
|
|
|
if (waitobj) {
|
|
|
|
*waitobj = w0;
|
|
|
|
}
|
2008-05-25 11:13:10 -04:00
|
|
|
#endif
|
2006-12-21 01:30:28 -05:00
|
|
|
return waitobj ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**Wait for multiple events.
|
|
|
|
*
|
|
|
|
* The function su_wait() blocks until an event specified by wait objects in
|
|
|
|
* @a wait array. If @a timeout is not SU_WAIT_FOREVER, a timeout occurs
|
|
|
|
* after @a timeout milliseconds.
|
2008-12-16 13:05:22 -05:00
|
|
|
*
|
2006-12-21 01:30:28 -05:00
|
|
|
* In Unix, this is @c poll() or @c select().
|
2008-12-16 13:05:22 -05:00
|
|
|
*
|
2006-12-21 01:30:28 -05:00
|
|
|
* In Windows, this is @c WSAWaitForMultipleEvents().
|
2008-12-16 13:05:22 -05:00
|
|
|
*
|
2006-12-21 01:30:28 -05:00
|
|
|
* @param waits array of wait objects
|
|
|
|
* @param n number of wait objects in array waits
|
|
|
|
* @param timeout timeout in milliseconds
|
|
|
|
*
|
|
|
|
* @retval Index of the signaled wait object, if any,
|
|
|
|
* @retval SU_WAIT_TIMEOUT if timeout occurred, or
|
|
|
|
* @retval -1 upon an error.
|
|
|
|
*/
|
|
|
|
int su_wait(su_wait_t waits[], unsigned n, su_duration_t timeout)
|
|
|
|
{
|
|
|
|
#if SU_HAVE_WINSOCK
|
|
|
|
DWORD i;
|
|
|
|
|
|
|
|
if (n > 0)
|
|
|
|
i = WSAWaitForMultipleEvents(n, waits, FALSE, timeout, FALSE);
|
|
|
|
else
|
|
|
|
return Sleep(timeout), SU_WAIT_TIMEOUT;
|
|
|
|
|
|
|
|
if (i == WSA_WAIT_TIMEOUT)
|
|
|
|
return SU_WAIT_TIMEOUT;
|
|
|
|
else if (i == WSA_WAIT_FAILED)
|
|
|
|
return SOCKET_ERROR;
|
|
|
|
else
|
|
|
|
return i;
|
|
|
|
|
2007-04-14 22:03:41 -04:00
|
|
|
#elif SU_HAVE_POLL || HAVE_SELECT
|
2006-12-21 01:30:28 -05:00
|
|
|
for (;;) {
|
|
|
|
int i = poll(waits, n, timeout);
|
|
|
|
|
|
|
|
if (i == 0)
|
|
|
|
return SU_WAIT_TIMEOUT;
|
|
|
|
|
|
|
|
if (i > 0) {
|
|
|
|
unsigned j;
|
|
|
|
for (j = 0; j < n; j++) {
|
|
|
|
if (waits[j].revents)
|
|
|
|
return j;
|
|
|
|
}
|
|
|
|
}
|
2008-12-16 13:05:22 -05:00
|
|
|
|
2006-12-21 01:30:28 -05:00
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return -1;
|
2008-12-16 13:05:22 -05:00
|
|
|
}
|
2006-12-21 01:30:28 -05:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get events.
|
2008-12-16 13:05:22 -05:00
|
|
|
*
|
2006-12-21 01:30:28 -05:00
|
|
|
* The function su_wait_events() returns an mask describing events occurred.
|
|
|
|
*
|
2008-12-16 13:05:22 -05:00
|
|
|
* @param waitobj pointer to wait object
|
2006-12-21 01:30:28 -05:00
|
|
|
* @param s socket
|
|
|
|
*
|
|
|
|
* @return Binary mask describing the events.
|
|
|
|
*/
|
|
|
|
int su_wait_events(su_wait_t *waitobj, su_socket_t s)
|
|
|
|
{
|
|
|
|
#if SU_HAVE_WINSOCK
|
|
|
|
WSANETWORKEVENTS net_events;
|
|
|
|
|
|
|
|
if (WSAEnumNetworkEvents(s, *waitobj, &net_events) != 0)
|
|
|
|
return SOCKET_ERROR;
|
|
|
|
|
|
|
|
return net_events.lNetworkEvents;
|
|
|
|
|
2007-04-14 22:03:41 -04:00
|
|
|
#elif SU_HAVE_POLL || HAVE_SELECT
|
2006-12-21 01:30:28 -05:00
|
|
|
/* poll(e, 1, 0); */
|
|
|
|
return waitobj->revents;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Set event mask.
|
2008-12-16 13:05:22 -05:00
|
|
|
*
|
2006-12-21 01:30:28 -05:00
|
|
|
* The function su_wait_mask() sets the mask describing events that can
|
|
|
|
* signal the wait object.
|
|
|
|
*
|
2008-12-16 13:05:22 -05:00
|
|
|
* @param waitobj pointer to wait object
|
2006-12-21 01:30:28 -05:00
|
|
|
* @param s socket
|
|
|
|
* @param events new event mask
|
|
|
|
*
|
2008-12-16 13:05:22 -05:00
|
|
|
* @retval 0 when successful,
|
2006-12-21 01:30:28 -05:00
|
|
|
* @retval -1 upon an error.
|
|
|
|
*/
|
|
|
|
int su_wait_mask(su_wait_t *waitobj, su_socket_t s, int events)
|
|
|
|
{
|
|
|
|
#if SU_HAVE_WINSOCK
|
|
|
|
HANDLE e = *waitobj;
|
|
|
|
|
|
|
|
if (WSAEventSelect(s, e, events) != 0) {
|
|
|
|
int error = WSAGetLastError();
|
|
|
|
WSACloseEvent(e);
|
|
|
|
WSASetLastError(error);
|
|
|
|
return -1;
|
|
|
|
}
|
2007-04-14 22:03:41 -04:00
|
|
|
|
|
|
|
#elif SU_HAVE_POLL || HAVE_SELECT
|
2006-12-21 01:30:28 -05:00
|
|
|
waitobj->fd = s;
|
|
|
|
waitobj->events = events;
|
|
|
|
waitobj->revents = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|