123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- /*
- * 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_wait.c
- * 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>
- * @author Martti Mela <Martti.Mela@nokia.com>
- * @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.
- *
- * @param newwait the newly created wait object (output)
- * @param socket socket
- * @param events mask for events that can signal this wait object
- *
- * @retval 0 if the call was successful,
- * @retval -1 upon an error.
- */
- 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;
- #elif SU_HAVE_POLL || HAVE_SELECT
- 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;
- 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.
- *
- * @param waitobj pointer to wait object
- *
- * @retval 0 when successful,
- * @retval -1 upon an error.
- */
- int su_wait_destroy(su_wait_t *waitobj)
- {
- #if SU_HAVE_WINSOCK
- su_wait_t w0 = NULL;
- assert(waitobj != NULL);
- if (*waitobj) {
- WSACloseEvent(*waitobj);
- *waitobj = w0;
- }
- #else
- su_wait_t w0 = { INVALID_SOCKET, 0, 0 };
- assert(waitobj != NULL);
- if (waitobj) {
- *waitobj = w0;
- }
- #endif
- 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.
- *
- * In Unix, this is @c poll() or @c select().
- *
- * In Windows, this is @c WSAWaitForMultipleEvents().
- *
- * @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) {
- #define WAIT_EVENT_BLOCK_SIZE WSA_MAXIMUM_WAIT_EVENTS
- /* Handle at most WAIT_EVENT_BLOCK_SIZE wait objects at a time */
- int blocks = (n + WAIT_EVENT_BLOCK_SIZE - 1) / WAIT_EVENT_BLOCK_SIZE;
- int block_index = 0;
- int first_wait_index = 0;
- int millisec_per_block = timeout / blocks;
- if (timeout > 0)
- millisec_per_block = max(1, millisec_per_block);
- i = WSA_WAIT_TIMEOUT;
- for(block_index = 0; block_index < blocks; block_index++,first_wait_index+=WAIT_EVENT_BLOCK_SIZE)
- {
- int remaining_blocks = n - block_index * WAIT_EVENT_BLOCK_SIZE;
- int waits_in_current_block = min( WAIT_EVENT_BLOCK_SIZE, remaining_blocks );
- i = WSAWaitForMultipleEvents(waits_in_current_block, waits + first_wait_index, FALSE, millisec_per_block, FALSE);
- if (i != WSA_WAIT_TIMEOUT) {
- /* Did not timeout, return something NOW, ignore remaining blocks */
- if (i != WSA_WAIT_FAILED) {
- /* Return the right index */
- i += first_wait_index;
- }
- break;
- }
- }
- } 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;
- #elif SU_HAVE_POLL || HAVE_SELECT
- 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;
- }
- }
- if (errno == EINTR)
- continue;
- return -1;
- }
- #endif
- }
- /** Get events.
- *
- * The function su_wait_events() returns an mask describing events occurred.
- *
- * @param waitobj pointer to wait object
- * @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;
- #elif SU_HAVE_POLL || HAVE_SELECT
- /* poll(e, 1, 0); */
- return waitobj->revents;
- #endif
- }
- /** Set event mask.
- *
- * The function su_wait_mask() sets the mask describing events that can
- * signal the wait object.
- *
- * @param waitobj pointer to wait object
- * @param s socket
- * @param events new event mask
- *
- * @retval 0 when successful,
- * @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;
- }
- #elif SU_HAVE_POLL || HAVE_SELECT
- waitobj->fd = s;
- waitobj->events = events;
- waitobj->revents = 0;
- #endif
- return 0;
- }
|