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 45ec3ce369 lws_dll: upgrade all instances to lws_dll2
lws_dll2 removes the downsides of lws_dll and adds new features like a
running member count and explicit owner type... it's cleaner and more
robust (eg, nodes know their owner, so they can casually switch between
list owners and remove themselves without the code knowing the owner).

This deprecates lws_dll, but since it's public it allows it to continue
to be built for 4.0 release if you give cmake LWS_WITH_DEPRECATED_LWS_DLL.

All remaining internal users of lws_dll are migrated to lws_dll2.
2019-08-08 16:58:55 +01:00

220 lines
5.6 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->dll_timeout);
}
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_dll2_remove(&wsi->dll_hrtimer);
if (us == LWS_SET_TIMER_USEC_CANCEL)
return;
wsi->pending_timer = lws_now_usecs() + us;
/*
* we sort the hrtimer list with the earliest timeout first
*/
lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp,
pt->dll_hrtimer_head.head) {
struct lws *w = lws_container_of(p, struct lws, dll_hrtimer);
assert(w->pending_timer); /* shouldn't be on the list otherwise */
if (w->pending_timer >= wsi->pending_timer) {
/* drop us in before this guy */
lws_dll2_add_before(&wsi->dll_hrtimer, &w->dll_hrtimer);
return;
}
} lws_end_foreach_dll_safe(p, tp);
/*
* Either nobody on the list yet to compare him to, or he's the
* longest timeout... stick him at the tail end
*/
lws_dll2_add_tail(&wsi->dll_hrtimer, &pt->dll_hrtimer_head);
}
LWS_VISIBLE void
lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs)
{
__lws_set_timer_usecs(wsi, usecs);
}
/* 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)
{
struct lws *wsi;
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
lws_dll2_get_head(&pt->dll_hrtimer_head)) {
wsi = lws_container_of(d, struct lws, dll_hrtimer);
/*
* if we met one in the future, we are done, because the list
* is sorted by time in the future.
*/
if (wsi->pending_timer > t)
break;
lws_set_timer_usecs(wsi, LWS_SET_TIMER_USEC_CANCEL);
/* it's time for the timer to be serviced */
if (wsi->protocol &&
wsi->protocol->callback(wsi, LWS_CALLBACK_TIMER,
wsi->user_space, NULL, 0))
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"timer cb errored");
} lws_end_foreach_dll_safe(d, d1);
/* return an estimate how many us until next timer hit */
if (!lws_dll2_get_head(&pt->dll_hrtimer_head))
return 0; /* there is nothing pending */
wsi = lws_container_of(lws_dll2_get_head(&pt->dll_hrtimer_head),
struct lws, dll_hrtimer);
t = lws_now_usecs();
if (wsi->pending_timer <= t) /* in the past */
return 1;
return wsi->pending_timer - t; /* at least 1 */
}
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];
time_t now;
time(&now);
lwsl_debug("%s: %p: %d secs, reason %d\n", __func__, wsi, secs, reason);
wsi->pending_timeout_limit = secs;
wsi->pending_timeout_set = now;
wsi->pending_timeout = reason;
lws_dll2_remove(&wsi->dll_timeout);
if (!reason)
return;
lws_dll2_add_head(&wsi->dll_timeout, &pt->dll_timeout_owner);
}
LWS_VISIBLE 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];
// lwsl_info("%s: %p: %d %d\n", __func__, wsi, reason, secs);
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);
}
/* 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_secs() + 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;
}