esp8266-std/ESP8266_RTOS_SDK/third_party/nopoll/nopoll_loop.c

270 lines
8.0 KiB
C
Raw Normal View History

2018-11-23 01:43:17 +00:00
/*
* LibNoPoll: A websocket library
* Copyright (C) 2013 Advanced Software Production Line, S.L.
*
* This program 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 program 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 program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* You may find a copy of the license under this software is released
* at COPYING file. This is LGPL software: you are welcome to develop
* proprietary applications using this library without any royalty or
* fee but returning back any change, improvement or addition in the
* form of source code, project image, documentation patches, etc.
*
* For commercial support on build Websocket enabled solutions
* contact us:
*
* Postal address:
* Advanced Software Production Line, S.L.
* Edificio Alius A, Oficina 102,
* C/ Antonio Suarez 10,
* Alcalá de Henares 28802 Madrid
* Spain
*
* Email address:
* info@aspl.es - http://www.aspl.es/nopoll
*/
#include <nopoll_loop.h>
#include <nopoll_private.h>
/**
* \defgroup nopoll_loop noPoll Loop: basic support to create a watching loop for WebSocket listeners
*/
/**
* \addtogroup nopoll_loop
* @{
*/
/**
* @internal Function used by nopoll_loop_wait to register all
* connections into the io waiting object.
*/
nopoll_bool nopoll_loop_register (noPollCtx * ctx, noPollConn * conn, noPollPtr user_data)
{
/* do not add connections that aren't working */
if (! nopoll_conn_is_ok (conn)) {
/* remove this connection from registry */
nopoll_ctx_unregister_conn (ctx, conn);
return nopoll_false; /* keep foreach, don't stop */
}
/* register the connection socket */
/* nopoll_log (ctx, NOPOLL_LEVEL_DEBUG, "Adding socket id: %d", conn->session);*/
if (! ctx->io_engine->addto (conn->session, ctx, conn, ctx->io_engine->io_object)) {
/* remove this connection from registry */
nopoll_ctx_unregister_conn (ctx, conn);
nopoll_log (ctx, NOPOLL_LEVEL_WARNING, "Failed to add socket %d to the watching set", conn->session);
}
return nopoll_false; /* keep foreach, don't stop */
}
/**
* @internal Function used to handle incoming data from from the
* connection and to notify this data on the connection.
*/
void nopoll_loop_process_data (noPollCtx * ctx, noPollConn * conn)
{
noPollMsg * msg;
/* call to get messages from the connection */
msg = nopoll_conn_get_msg (conn);
if (msg == NULL)
return;
/* found message, notify it */
if (conn->on_msg)
conn->on_msg (ctx, conn, msg, conn->on_msg_data);
else if (ctx->on_msg)
ctx->on_msg (ctx, conn, msg, ctx->on_msg_data);
/* release message */
nopoll_msg_unref (msg);
return;
}
/**
* @internal Function used to detected which connections has something
* interesting to be notified.
*
*/
nopoll_bool nopoll_loop_process (noPollCtx * ctx, noPollConn * conn, noPollPtr user_data)
{
int * conn_changed = (int *) user_data;
/* check if the connection have something to notify */
if (ctx->io_engine->isset (ctx, conn->session, ctx->io_engine->io_object)) {
/* call to notify action according to role */
switch (conn->role) {
case NOPOLL_ROLE_CLIENT:
case NOPOLL_ROLE_LISTENER:
/* received data, notify */
nopoll_loop_process_data (ctx, conn);
break;
case NOPOLL_ROLE_MAIN_LISTENER:
/* call to handle */
nopoll_conn_accept (ctx, conn);
break;
default:
nopoll_log (ctx, NOPOLL_LEVEL_CRITICAL, "Found connection with unknown role, closing and dropping");
nopoll_conn_shutdown (conn);
break;
}
/* reduce connection changed */
(*conn_changed)--;
} /* end if */
return (*conn_changed) == 0;
}
/**
* @internal Function used to init internal io wait mechanism...
*
* @param ctx The noPoll context to be initialized if it wasn't
*/
void nopoll_loop_init (noPollCtx * ctx)
{
if (ctx == NULL)
return;
/* grab the mutex for the following check */
if (ctx->io_engine == NULL) {
ctx->io_engine = nopoll_io_get_engine (ctx, NOPOLL_IO_ENGINE_DEFAULT);
if (ctx->io_engine == NULL) {
nopoll_log (ctx, NOPOLL_LEVEL_CRITICAL, "Failed to create IO wait engine, unable to implement wait call");
return;
}
} /* end if */
/* release the mutex */
return;
}
/**
* @brief Flag to stop the current loop implemented (if any) on the provided context.
*
* @param ctx The context where the loop is being done, and wanted to
* be stopped.
*
*/
void nopoll_loop_stop (noPollCtx * ctx)
{
if (! ctx)
return;
ctx->keep_looping = nopoll_false;
return;
} /* end if */
/**
* @brief Allows to implement a wait over all connections registered
* under the provided context during the provided timeout until
* something is detected meaningful to the user, calling to the action
* handler defined, optionally receving the user data pointer.
*
* @param ctx The context object where the wait will be implemented.
*
* @param timeout The timeout to wait for changes. If no changes
* happens, the function returns. The function will block the caller
* until a call to \ref nopoll_loop_stop is done in the case timeout
* passed is 0.
*
* @return The function returns 0 when finished or -2 in the case ctx
* is NULL or timeout is negative.
*/
int nopoll_loop_wait (noPollCtx * ctx, long timeout)
{
struct timeval start;
struct timeval stop;
struct timeval diff;
long ellapsed;
int wait_status;
nopoll_return_val_if_fail (ctx, ctx, -2);
nopoll_return_val_if_fail (ctx, timeout >= 0, -2);
/* call to init io engine */
nopoll_loop_init (ctx);
/* get as reference current time */
if (timeout > 0)
#if defined(NOPOLL_OS_WIN32)
nopoll_win32_gettimeofday (&start, NULL);
#else
gettimeofday (&start, NULL);
#endif
/* set to keep looping everything this function is called */
ctx->keep_looping = nopoll_true;
while (ctx->keep_looping) {
/* ok, now implement wait operation */
ctx->io_engine->clear (ctx, ctx->io_engine->io_object);
/* add all connections */
/* nopoll_log (ctx, NOPOLL_LEVEL_DEBUG, "Adding connections to watch: %d", ctx->conn_num); */
nopoll_ctx_foreach_conn (ctx, nopoll_loop_register, NULL);
/* if (errno == EBADF) { */
/* detected some descriptor not properly
* working, try to check them */
/* nopoll_ctx_foreach_conn (ctx, nopoll_loop_clean_descriptors, NULL); */
/* nopoll_log (ctx, NOPOLL_LEVEL_CRITICAL, "Found some descriptor is not valid (errno==%d)", errno);
continue; */
/* } */ /* end if */
/* implement wait operation */
/* nopoll_log (ctx, NOPOLL_LEVEL_DEBUG, "Waiting for changes into %d connections", ctx->conn_num); */
wait_status = ctx->io_engine->wait (ctx, ctx->io_engine->io_object);
/* nopoll_log (ctx, NOPOLL_LEVEL_DEBUG, "Waiting finished with result %d", wait_status); */
if (wait_status == -1) {
nopoll_log (ctx, NOPOLL_LEVEL_CRITICAL, "Received error from wait operation, error code was: %d", errno);
break;
} /* end if */
/* check how many connections changed and restart */
if (wait_status > 0) {
/* check and call for connections with something
* interesting */
nopoll_ctx_foreach_conn (ctx, nopoll_loop_process, &wait_status);
}
/* check to stop wait operation */
if (timeout > 0) {
#if defined(NOPOLL_OS_WIN32)
nopoll_win32_gettimeofday (&stop, NULL);
#else
gettimeofday (&stop, NULL);
#endif
nopoll_timeval_substract (&stop, &start, &diff);
ellapsed = (diff.tv_sec * 1000000) + diff.tv_usec;
if (ellapsed > timeout)
break;
} /* end if */
} /* end while */
/* release engine */
nopoll_io_release_engine (ctx->io_engine);
ctx->io_engine = NULL;
return 0;
}
/* @} */