1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-30 00:00:16 +01:00
libwebsockets/lib/core-net/wsi-timeout.c
Andy Green 3c12fd72e8 unify us sorted waits
There are quite a few linked-lists of things that want events after
some period.  This introduces a type binding an lws_dll2 for the
list and a lws_usec_t for the duration.

The wsi timeouts, the hrtimer and the sequencer timeouts are converted
to use these, also in the common event wait calculation.
2019-08-08 22:39:47 +01:00

240 lines
6.5 KiB
C

/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.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:
* version 2.1 of the License.
*
* 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 Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "core/private.h"
void
__lws_remove_from_timeout_list(struct lws *wsi)
{
lws_dll2_remove(&wsi->sul_timeout.list);
}
void
lws_remove_from_timeout_list(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
lws_pt_lock(pt, __func__);
__lws_remove_from_timeout_list(wsi);
lws_pt_unlock(pt);
}
void
__lws_set_timer_usecs(struct lws *wsi, lws_usec_t us)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
__lws_sul_insert(&pt->dll_hrtimer_owner, &wsi->sul_hrtimer, us);
}
LWS_VISIBLE void
lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs)
{
__lws_set_timer_usecs(wsi, usecs);
}
static void
lws_hrtimer_sul_check_cb(lws_sorted_usec_list_t *sul)
{
struct lws *wsi = lws_container_of(sul, struct lws, sul_hrtimer);
if (wsi->protocol &&
wsi->protocol->callback(wsi, LWS_CALLBACK_TIMER,
wsi->user_space, NULL, 0))
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"hrtimer cb errored");
}
/* return 0 if nothing pending, or the number of us before the next event */
lws_usec_t
__lws_hrtimer_service(struct lws_context_per_thread *pt, lws_usec_t t)
{
return __lws_sul_check(&pt->dll_hrtimer_owner,
lws_hrtimer_sul_check_cb, t);
}
void
__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
__lws_sul_insert(&pt->dll_timeout_owner, &wsi->sul_timeout,
((lws_usec_t)secs) * LWS_US_PER_SEC);
lwsl_debug("%s: %p: %d secs, reason %d\n", __func__, wsi, secs, reason);
wsi->pending_timeout = reason;
}
void
lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
if (!secs) {
lws_remove_from_timeout_list(wsi);
return;
}
if (secs == LWS_TO_KILL_SYNC) {
lws_remove_from_timeout_list(wsi);
lwsl_debug("synchronously killing %p\n", wsi);
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"to sync kill");
return;
}
if (secs == LWS_TO_KILL_ASYNC)
secs = 0;
lws_pt_lock(pt, __func__);
__lws_set_timeout(wsi, reason, secs);
lws_pt_unlock(pt);
}
void
lws_set_timeout_us(struct lws *wsi, enum pending_timeout reason, lws_usec_t us)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
if (!us) {
lws_remove_from_timeout_list(wsi);
return;
}
lws_pt_lock(pt, __func__);
__lws_sul_insert(&pt->dll_timeout_owner, &wsi->sul_timeout, us);
lwsl_debug("%s: %p: %llu us, reason %d\n", __func__, wsi,
(unsigned long long)us, reason);
wsi->pending_timeout = reason;
lws_pt_unlock(pt);
}
static void
lws_wsitimeout_sul_check_cb(lws_sorted_usec_list_t *sul)
{
struct lws *wsi = lws_container_of(sul, struct lws, sul_timeout);
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_TIMEOUTS, 1);
/* no need to log normal idle keepalive timeout */
// if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE)
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
lwsl_info("wsi %p: TIMEDOUT WAITING on %d "
"(did hdr %d, ah %p, wl %d\n",
(void *)wsi, wsi->pending_timeout,
wsi->hdr_parsing_completed, wsi->http.ah,
pt->http.ah_wait_list_length);
#if defined(LWS_WITH_CGI)
if (wsi->http.cgi)
lwsl_notice("CGI timeout: %s\n", wsi->http.cgi->summary);
#endif
#else
lwsl_info("wsi %p: TIMEDOUT WAITING on %d ", (void *)wsi,
wsi->pending_timeout);
#endif
/* cgi timeout */
if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE)
/*
* Since he failed a timeout, he already had a chance to
* do something and was unable to... that includes
* situations like half closed connections. So process
* this "failed timeout" close as a violent death and
* don't try to do protocol cleanup like flush partials.
*/
wsi->socket_is_permanently_unusable = 1;
if (lwsi_state(wsi) == LRS_WAITING_SSL && wsi->protocol)
wsi->protocol->callback(wsi,
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
wsi->user_space,
(void *)"Timed out waiting SSL", 21);
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "timeout");
}
/* return 0 if nothing pending, or the number of us before the next event */
lws_usec_t
__lws_wsitimeout_service(struct lws_context_per_thread *pt, lws_usec_t t)
{
return __lws_sul_check(&pt->dll_timeout_owner,
lws_wsitimeout_sul_check_cb, t);
}
/* requires context + vh lock */
int
__lws_timed_callback_remove(struct lws_vhost *vh, struct lws_timed_vh_protocol *p)
{
lws_start_foreach_llp(struct lws_timed_vh_protocol **, pt,
vh->timed_vh_protocol_list) {
if (*pt == p) {
*pt = p->next;
lws_free(p);
return 0;
}
} lws_end_foreach_llp(pt, next);
return 1;
}
LWS_VISIBLE LWS_EXTERN int
lws_timed_callback_vh_protocol(struct lws_vhost *vh,
const struct lws_protocols *prot, int reason,
int secs)
{
struct lws_timed_vh_protocol *p = (struct lws_timed_vh_protocol *)
lws_malloc(sizeof(*p), "timed_vh");
if (!p)
return 1;
p->tsi_req = lws_pthread_self_to_tsi(vh->context);
if (p->tsi_req < 0) /* not called from a service thread --> tsi 0 */
p->tsi_req = 0;
lws_context_lock(vh->context, __func__); /* context ----------------- */
p->protocol = prot;
p->reason = reason;
p->time = (lws_now_usecs() / LWS_US_PER_SEC) + secs;
// lwsl_notice("%s: %s.%s %d\n", __func__, vh->name, prot->name, secs);
lws_vhost_lock(vh); /* vhost ---------------------------------------- */
p->next = vh->timed_vh_protocol_list;
vh->timed_vh_protocol_list = p;
lws_vhost_unlock(vh); /* -------------------------------------- vhost */
lws_context_unlock(vh->context); /* ------------------------- context */
return 0;
}