mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
lws_set_timer_usecs: change to usec resolution
This replaces the existing, unreleased lws_set_timer(wsi, secs) with lws_set_timer_usecs(wsi, usecs). wsi with a timer waiting are added to a linked-list sorted by the timer trigger time. 1) poll() timeout (ie, poll wait) is trimmed to the nearest ms of the first waiting timer if the default poll wait is longer than the interval until the first waiting timer. The linked-list of waiting timers is checked every entry and exit from poll()... if no timers waiting or none reached their time this costs almost nothing. 2) libuv: the earliest hrtimer is checked after every IO, again this is costing nothing if the list head is NULL. If the case there are hrtimers on the list, it costs a getimeofday (a VDSO in linux) and more only if any of the timers have fired. In addition on entry to libuv idle, if there are any waiting hrtimers on the list, a libuv timer is used to force a wake in case we stay idle (the libuv timer has ms resolution). 3) libev: not implemented 4) libevent: not implemented Warnings are logged in the api is used on an event backend without support. Patches welcome to add support similarly to libuv.
This commit is contained in:
parent
9cf641dece
commit
1820212724
8 changed files with 360 additions and 122 deletions
|
@ -30,6 +30,24 @@ lws_feature_status_libuv(struct lws_context_creation_info *info)
|
|||
lwsl_info("libuv support compiled in but disabled\n");
|
||||
}
|
||||
|
||||
static void
|
||||
lws_uv_hrtimer_cb(uv_timer_t *timer
|
||||
#if UV_VERSION_MAJOR == 0
|
||||
, int status
|
||||
#endif
|
||||
)
|
||||
{
|
||||
struct lws_context_per_thread *pt = lws_container_of(timer,
|
||||
struct lws_context_per_thread, uv_hrtimer);
|
||||
lws_usec_t us;
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
us = __lws_hrtimer_service(pt);
|
||||
if (us != LWS_HRTIMER_NOWAIT)
|
||||
uv_timer_start(&pt->uv_hrtimer, lws_uv_hrtimer_cb, us / 1000, 0);
|
||||
lws_pt_unlock(pt);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_uv_idle(uv_idle_t *handle
|
||||
#if UV_VERSION_MAJOR == 0
|
||||
|
@ -39,6 +57,7 @@ lws_uv_idle(uv_idle_t *handle
|
|||
{
|
||||
struct lws_context_per_thread *pt = lws_container_of(handle,
|
||||
struct lws_context_per_thread, uv_idle);
|
||||
lws_usec_t us;
|
||||
|
||||
/*
|
||||
* is there anybody with pending stuff that needs service forcing?
|
||||
|
@ -52,6 +71,14 @@ lws_uv_idle(uv_idle_t *handle
|
|||
return;
|
||||
}
|
||||
|
||||
/* account for hrtimer */
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
us = __lws_hrtimer_service(pt);
|
||||
if (us != LWS_HRTIMER_NOWAIT)
|
||||
uv_timer_start(&pt->uv_hrtimer, lws_uv_hrtimer_cb, us / 1000, 0);
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
/* there is nobody who needs service forcing, shut down idle */
|
||||
uv_idle_stop(handle);
|
||||
}
|
||||
|
@ -63,6 +90,7 @@ lws_io_cb(uv_poll_t *watcher, int status, int revents)
|
|||
struct lws_io_watcher, uv_watcher);
|
||||
struct lws *wsi = lws_container_of(lws_io, struct lws, w_read);
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
struct lws_pollfd eventfd;
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
|
@ -97,7 +125,11 @@ lws_io_cb(uv_poll_t *watcher, int status, int revents)
|
|||
}
|
||||
lws_service_fd(context, &eventfd);
|
||||
|
||||
uv_idle_start(&context->pt[(int)wsi->tsi].uv_idle, lws_uv_idle);
|
||||
lws_pt_lock(pt, __func__);
|
||||
__lws_hrtimer_service(pt);
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
uv_idle_start(&pt->uv_idle, lws_uv_idle);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
|
@ -247,6 +279,7 @@ lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi)
|
|||
uv_timer_init(pt->io_loop_uv, &pt->uv_timeout_watcher);
|
||||
uv_timer_start(&pt->uv_timeout_watcher, lws_uv_timeout_cb,
|
||||
10, 1000);
|
||||
uv_timer_init(pt->io_loop_uv, &pt->uv_hrtimer);
|
||||
}
|
||||
|
||||
return status;
|
||||
|
@ -299,6 +332,8 @@ lws_libuv_destroyloop(struct lws_context *context, int tsi)
|
|||
|
||||
uv_timer_stop(&pt->uv_timeout_watcher);
|
||||
uv_close((uv_handle_t *)&pt->uv_timeout_watcher, lws_uv_close_cb);
|
||||
uv_timer_stop(&pt->uv_hrtimer);
|
||||
uv_close((uv_handle_t *)&pt->uv_hrtimer, lws_uv_close_cb);
|
||||
|
||||
uv_idle_stop(&pt->uv_idle);
|
||||
uv_close((uv_handle_t *)&pt->uv_idle, lws_uv_close_cb);
|
||||
|
|
|
@ -132,27 +132,80 @@ __lws_free_wsi(struct lws *wsi)
|
|||
lws_free(wsi);
|
||||
}
|
||||
|
||||
int
|
||||
lws_should_be_on_timeout_list(struct lws *wsi)
|
||||
void
|
||||
lws_dll_add_front(struct lws_dll *d, struct lws_dll *phead)
|
||||
{
|
||||
return wsi->timer_active || wsi->pending_timeout;
|
||||
if (d->prev)
|
||||
return;
|
||||
|
||||
/* our next guy is current first guy */
|
||||
d->next = phead->next;
|
||||
/* if there is a next guy, set his prev ptr to our next ptr */
|
||||
if (d->next)
|
||||
d->next->prev = d;
|
||||
/* our prev ptr is first ptr */
|
||||
d->prev = phead;
|
||||
/* set the first guy to be us */
|
||||
phead->next = d;
|
||||
}
|
||||
|
||||
/* situation is:
|
||||
*
|
||||
* HEAD: struct lws_dll * = &entry1
|
||||
*
|
||||
* Entry 1: struct lws_dll .pprev = &HEAD , .next = Entry 2
|
||||
* Entry 2: struct lws_dll .pprev = &entry1 , .next = &entry2
|
||||
* Entry 3: struct lws_dll .pprev = &entry2 , .next = NULL
|
||||
*
|
||||
* Delete Entry1:
|
||||
*
|
||||
* - HEAD = &entry2
|
||||
* - Entry2: .pprev = &HEAD, .next = &entry3
|
||||
* - Entry3: .pprev = &entry2, .next = NULL
|
||||
*
|
||||
* Delete Entry2:
|
||||
*
|
||||
* - HEAD = &entry1
|
||||
* - Entry1: .pprev = &HEAD, .next = &entry3
|
||||
* - Entry3: .pprev = &entry1, .next = NULL
|
||||
*
|
||||
* Delete Entry3:
|
||||
*
|
||||
* - HEAD = &entry1
|
||||
* - Entry1: .pprev = &HEAD, .next = &entry2
|
||||
* - Entry2: .pprev = &entry1, .next = NULL
|
||||
*
|
||||
*/
|
||||
|
||||
void
|
||||
lws_dll_remove(struct lws_dll *d)
|
||||
{
|
||||
if (!d->prev) /* ie, not part of the list */
|
||||
return;
|
||||
|
||||
/*
|
||||
* remove us
|
||||
*
|
||||
* USp <-> us <-> USn --> USp <-> USn
|
||||
*/
|
||||
|
||||
/* if we have a next guy, set his prev to our prev */
|
||||
if (d->next)
|
||||
d->next->prev = d->prev;
|
||||
|
||||
/* set our prev guy to our next guy instead of us */
|
||||
if (d->prev)
|
||||
d->prev->next = d->next;
|
||||
|
||||
/* we're out of the list, we should not point anywhere any more */
|
||||
d->prev = NULL;
|
||||
d->next = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
__lws_remove_from_timeout_list(struct lws *wsi)
|
||||
{
|
||||
if (!wsi->timeout_list_prev) /* ie, not part of the list */
|
||||
return;
|
||||
|
||||
/* if we have a next guy, set his prev to our prev */
|
||||
if (wsi->timeout_list)
|
||||
wsi->timeout_list->timeout_list_prev = wsi->timeout_list_prev;
|
||||
/* set our prev guy to our next guy instead of us */
|
||||
*wsi->timeout_list_prev = wsi->timeout_list;
|
||||
|
||||
/* we're out of the list, we should not point anywhere any more */
|
||||
wsi->timeout_list_prev = NULL;
|
||||
wsi->timeout_list = NULL;
|
||||
lws_dll_lws_remove(&wsi->dll_timeout);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -161,85 +214,169 @@ 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);
|
||||
}
|
||||
|
||||
static void
|
||||
__lws_add_to_timeout_list(struct lws *wsi)
|
||||
void
|
||||
lws_dll_dump(struct lws_dll_lws *head, const char *title)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
int n = 0;
|
||||
|
||||
if (wsi->timeout_list_prev)
|
||||
return;
|
||||
(void)n;
|
||||
lwsl_notice("%s: %s (head.next %p)\n", __func__, title, head->next);
|
||||
|
||||
/* our next guy is current first guy */
|
||||
wsi->timeout_list = pt->timeout_list;
|
||||
/* if there is a next guy, set his prev ptr to our next ptr */
|
||||
if (wsi->timeout_list)
|
||||
wsi->timeout_list->timeout_list_prev = &wsi->timeout_list;
|
||||
/* our prev ptr is first ptr */
|
||||
wsi->timeout_list_prev = &pt->timeout_list;
|
||||
/* set the first guy to be us */
|
||||
*wsi->timeout_list_prev = wsi;
|
||||
lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, head->next) {
|
||||
struct lws *wsi = lws_container_of(d, struct lws, dll_hrtimer);
|
||||
|
||||
(void)wsi;
|
||||
|
||||
lwsl_notice(" %d: wsi %p: %llu\n", n++, wsi,
|
||||
(unsigned long long)wsi->pending_timer);
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
}
|
||||
|
||||
void
|
||||
__lws_set_timer(struct lws *wsi, int secs)
|
||||
__lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs)
|
||||
{
|
||||
// struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
time_t now;
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
struct lws_dll_lws *dd = &pt->dll_head_hrtimer;
|
||||
struct timeval now;
|
||||
struct lws *wsi1;
|
||||
int bef = 0;
|
||||
|
||||
if (secs < 0) {
|
||||
wsi->timer_active = 0;
|
||||
if (LWS_LIBEV_ENABLED(wsi->context))
|
||||
lwsl_warn("%s: lws hrtimer not implemented for libev\n", __func__);
|
||||
if (LWS_LIBEVENT_ENABLED(wsi->context))
|
||||
lwsl_warn("%s: lws hrtimer not implemented for libevent\n", __func__);
|
||||
|
||||
if (!lws_should_be_on_timeout_list(wsi))
|
||||
__lws_remove_from_timeout_list(wsi);
|
||||
lws_dll_lws_remove(&wsi->dll_hrtimer);
|
||||
|
||||
if (usecs == LWS_SET_TIMER_USEC_CANCEL)
|
||||
return;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
wsi->pending_timer = ((now.tv_sec * 1000000ll) + now.tv_usec) + usecs;
|
||||
|
||||
/*
|
||||
* we sort the hrtimer list with the earliest timeout first
|
||||
*/
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
|
||||
pt->dll_head_hrtimer.next) {
|
||||
dd = d;
|
||||
wsi1 = lws_container_of(d, struct lws, dll_hrtimer);
|
||||
|
||||
if (wsi1->pending_timer >= wsi->pending_timer) {
|
||||
/* d, dprev's next, is >= our time */
|
||||
bef = 1;
|
||||
break;
|
||||
}
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
|
||||
if (bef) {
|
||||
/*
|
||||
* we go before dd
|
||||
* DDp <-> DD <-> DDn --> DDp <-> us <-> DD <-> DDn
|
||||
*/
|
||||
/* we point forward to dd */
|
||||
wsi->dll_hrtimer.next = dd;
|
||||
/* we point back to what dd used to point back to */
|
||||
wsi->dll_hrtimer.prev = dd->prev;
|
||||
/* DDp points forward to us now */
|
||||
dd->prev->next = &wsi->dll_hrtimer;
|
||||
/* DD points back to us now */
|
||||
dd->prev = &wsi->dll_hrtimer;
|
||||
} else {
|
||||
/*
|
||||
* we go after dd
|
||||
* DDp <-> DD <-> DDn --> DDp <-> DD <-> us <-> DDn
|
||||
*/
|
||||
/* we point forward to what dd used to point forward to */
|
||||
wsi->dll_hrtimer.next = dd->next;
|
||||
/* we point back to dd */
|
||||
wsi->dll_hrtimer.prev = dd;
|
||||
/* DDn points back to us */
|
||||
if (dd->next)
|
||||
dd->next->prev = &wsi->dll_hrtimer;
|
||||
/* DD points forward to us */
|
||||
dd->next = &wsi->dll_hrtimer;
|
||||
}
|
||||
|
||||
time(&now);
|
||||
|
||||
wsi->pending_timer_limit = secs;
|
||||
wsi->pending_timer_set = now;
|
||||
|
||||
if (!wsi->timer_active) {
|
||||
wsi->timer_active = 1;
|
||||
if (!wsi->pending_timeout)
|
||||
__lws_add_to_timeout_list(wsi);
|
||||
}
|
||||
// lws_dll_dump(&pt->dll_head_hrtimer, "after set_timer_usec");
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_set_timer(struct lws *wsi, int secs)
|
||||
lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs)
|
||||
{
|
||||
//struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
__lws_set_timer_usecs(wsi, usecs);
|
||||
}
|
||||
|
||||
// lws_pt_lock(pt, __func__);
|
||||
__lws_set_timer(wsi, secs);
|
||||
// lws_pt_unlock(pt);
|
||||
lws_usec_t
|
||||
__lws_hrtimer_service(struct lws_context_per_thread *pt)
|
||||
{
|
||||
struct timeval now;
|
||||
struct lws *wsi;
|
||||
lws_usec_t t;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
t = (now.tv_sec * 1000000ll) + now.tv_usec;
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
|
||||
pt->dll_head_hrtimer.next) {
|
||||
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 (!pt->dll_head_hrtimer.next)
|
||||
return LWS_HRTIMER_NOWAIT;
|
||||
|
||||
wsi = lws_container_of(pt->dll_head_hrtimer.next, struct lws, dll_hrtimer);
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
t = (now.tv_sec * 1000000ll) + now.tv_usec;
|
||||
|
||||
if (wsi->pending_timer < t)
|
||||
return 0;
|
||||
|
||||
return wsi->pending_timer - 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];
|
||||
time_t now;
|
||||
|
||||
time(&now);
|
||||
|
||||
if (reason)
|
||||
__lws_add_to_timeout_list(wsi);
|
||||
|
||||
lwsl_debug("%s: %p: %d secs\n", __func__, wsi, secs);
|
||||
wsi->pending_timeout_limit = secs;
|
||||
wsi->pending_timeout_set = now;
|
||||
wsi->pending_timeout = reason;
|
||||
|
||||
if (!reason && !lws_should_be_on_timeout_list(wsi))
|
||||
__lws_remove_from_timeout_list(wsi);
|
||||
if (!reason)
|
||||
lws_dll_lws_remove(&wsi->dll_timeout);
|
||||
else
|
||||
lws_dll_lws_add_front(&wsi->dll_timeout, &pt->dll_head_timeout);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
|
@ -255,9 +392,7 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
|
|||
}
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
|
||||
__lws_set_timeout(wsi, reason, secs);
|
||||
|
||||
lws_pt_unlock(pt);
|
||||
}
|
||||
|
||||
|
@ -784,6 +919,7 @@ just_kill_connection:
|
|||
*/
|
||||
__lws_ssl_remove_wsi_from_buffered_list(wsi);
|
||||
__lws_remove_from_timeout_list(wsi);
|
||||
lws_dll_lws_remove(&wsi->dll_hrtimer);
|
||||
|
||||
/* checking return redundant since we anyway close */
|
||||
if (wsi->desc.sockfd != LWS_SOCK_INVALID)
|
||||
|
|
|
@ -426,12 +426,13 @@ lwsl_visible(int level);
|
|||
#define lws_container_of(P,T,M) ((T *)((char *)(P) - offsetof(T, M)))
|
||||
#endif
|
||||
|
||||
|
||||
struct lws;
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||
#endif
|
||||
|
||||
typedef int64_t lws_usec_t;
|
||||
|
||||
/* api change list for user code to test against */
|
||||
|
||||
#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_ARG
|
||||
|
@ -1452,11 +1453,12 @@ enum lws_callback_reasons {
|
|||
* from the pvo for the cert renewal, non-NULL in the array index
|
||||
* means use that pointer instead for the index. */
|
||||
LWS_CALLBACK_TIMER = 73,
|
||||
/**< When the time elapsed after a call to lws_set_timer(wsi, secs)
|
||||
/**< When the time elapsed after a call to lws_set_timer_usecs(wsi, usecs)
|
||||
* is up, the wsi will get one of these callbacks. The deadline
|
||||
* can be continuously extended into the future by later calls
|
||||
* to lws_set_timer() before the deadline expires, or cancelled by
|
||||
* lws_set_timer(wsi, -1); */
|
||||
* to lws_set_timer_usecs() before the deadline expires, or cancelled by
|
||||
* lws_set_timer_usecs(wsi, -1); See the note on lws_set_timer_usecs()
|
||||
* about which event loops are supported. */
|
||||
LWS_CALLBACK_VHOST_CERT_UPDATE = 74,
|
||||
/**< When a vhost TLS cert is being updated, progress is
|
||||
* reported to the vhost in question here, including completion
|
||||
|
@ -4587,20 +4589,38 @@ enum pending_timeout {
|
|||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
|
||||
|
||||
#define LWS_SET_TIMER_USEC_CANCEL ((lws_usec_t)-1ll)
|
||||
#define LWS_USEC_PER_SEC (1000000ll)
|
||||
|
||||
/**
|
||||
* lws_set_timer() - schedules a callback on the wsi in the future
|
||||
* lws_set_timer_usecs() - schedules a callback on the wsi in the future
|
||||
*
|
||||
* \param wsi: Websocket connection instance
|
||||
* \param secs: -1 removes any existing scheduled callback, otherwise the
|
||||
* number of seconds in the future the callback will occur at.
|
||||
* \param usecs: LWS_SET_TIMER_USEC_CANCEL removes any existing scheduled
|
||||
* callback, otherwise number of microseconds in the future
|
||||
* the callback will occur at.
|
||||
*
|
||||
* When the deadline expires, the wsi will get a callback of type
|
||||
* NOTE: event loop support for this:
|
||||
*
|
||||
* default poll() loop: yes
|
||||
* libuv event loop: yes
|
||||
* libev: not implemented (patch welcome)
|
||||
* libevent: not implemented (patch welcome)
|
||||
*
|
||||
* After the deadline expires, the wsi will get a callback of type
|
||||
* LWS_CALLBACK_TIMER and the timer is exhausted. The deadline may be
|
||||
* continuously deferred by further calls to lws_set_timer() with a later
|
||||
* deadline, or cancelled by lws_set_timer(wsi, -1)
|
||||
* continuously deferred by further calls to lws_set_timer_usecs() with a later
|
||||
* deadline, or cancelled by lws_set_timer_usecs(wsi, -1).
|
||||
*
|
||||
* If the timer should repeat, lws_set_timer_usecs() must be called again from
|
||||
* LWS_CALLBACK_TIMER.
|
||||
*
|
||||
* Accuracy depends on the platform and the load on the event loop or system...
|
||||
* all that's guaranteed is the callback will come after the requested wait
|
||||
* period.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_set_timer(struct lws *wsi, int secs);
|
||||
lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs);
|
||||
|
||||
/*
|
||||
* lws_timed_callback_vh_protocol() - calls back a protocol on a vhost after
|
||||
|
@ -5383,6 +5403,60 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
|
|||
} lws_end_foreach_llp(___ppss, ___m_list); \
|
||||
}
|
||||
|
||||
/*
|
||||
* doubly linked-list
|
||||
*/
|
||||
|
||||
struct lws_dll { /* abstract */
|
||||
struct lws_dll *prev;
|
||||
struct lws_dll *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* these all point to the composed list objects... you have to use the
|
||||
* lws_container_of() helper to recover the start of the containing struct
|
||||
*/
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_dll_add_front(struct lws_dll *d, struct lws_dll *phead);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_dll_remove(struct lws_dll *d);
|
||||
|
||||
struct lws_dll_lws { /* typed as struct lws * */
|
||||
struct lws_dll_lws *prev;
|
||||
struct lws_dll_lws *next;
|
||||
};
|
||||
|
||||
static inline void
|
||||
lws_dll_lws_add_front(struct lws_dll_lws *_a, struct lws_dll_lws *_head)
|
||||
{
|
||||
lws_dll_add_front((struct lws_dll *)_a, (struct lws_dll *)_head);
|
||||
}
|
||||
|
||||
static inline void
|
||||
lws_dll_lws_remove(struct lws_dll_lws *_a)
|
||||
{
|
||||
lws_dll_remove((struct lws_dll *)_a);
|
||||
}
|
||||
|
||||
/*
|
||||
* these are safe against the current container object getting deleted,
|
||||
* since the hold his next in a temp and go to that next. ___tmp is
|
||||
* the temp.
|
||||
*/
|
||||
|
||||
#define lws_start_foreach_dll_safe(___type, ___it, ___tmp, ___start) \
|
||||
{ \
|
||||
___type ___it = ___start; \
|
||||
while (___it) { \
|
||||
___type ___tmp = (___it)->next;
|
||||
|
||||
#define lws_end_foreach_dll_safe(___it, ___tmp) \
|
||||
___it = ___tmp; \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* lws_ptr_diff(): helper to report distance between pointers as an int
|
||||
*
|
||||
|
|
|
@ -200,6 +200,15 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
timeout_ms = 0;
|
||||
}
|
||||
|
||||
if (timeout_ms) {
|
||||
lws_pt_lock(pt, __func__);
|
||||
/* don't stay in poll wait longer than next hr timeout */
|
||||
lws_usec_t t = __lws_hrtimer_service(pt);
|
||||
if (timeout_ms * 1000 > t)
|
||||
timeout_ms = t / 1000;
|
||||
lws_pt_unlock(pt);
|
||||
}
|
||||
|
||||
vpt->inside_poll = 1;
|
||||
lws_memory_barrier();
|
||||
n = poll(pt->fds, pt->fds_count, timeout_ms);
|
||||
|
@ -239,6 +248,10 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
vpt->foreign_pfd_list = NULL;
|
||||
lws_memory_barrier();
|
||||
|
||||
/* we have come out of a poll wait... check the hrtimer list */
|
||||
|
||||
__lws_hrtimer_service(pt);
|
||||
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
|
|
|
@ -804,6 +804,8 @@ struct allocated_headers {
|
|||
uint8_t /* enum lws_token_indexes */ parser_state;
|
||||
};
|
||||
|
||||
#define LWS_HRTIMER_NOWAIT (0x7fffffffffffffffll)
|
||||
|
||||
/*
|
||||
* so we can have n connections being serviced simultaneously,
|
||||
* these things need to be isolated per-thread.
|
||||
|
@ -818,7 +820,8 @@ struct lws_context_per_thread {
|
|||
volatile struct lws_foreign_thread_pollfd * volatile foreign_pfd_list;
|
||||
struct lws *rx_draining_ext_list;
|
||||
struct lws *tx_draining_ext_list;
|
||||
struct lws *timeout_list;
|
||||
struct lws_dll_lws dll_head_timeout;
|
||||
struct lws_dll_lws dll_head_hrtimer;
|
||||
#if defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
|
||||
struct lws_context *context;
|
||||
#endif
|
||||
|
@ -842,6 +845,7 @@ struct lws_context_per_thread {
|
|||
uv_loop_t *io_loop_uv;
|
||||
uv_signal_t signals[8];
|
||||
uv_timer_t uv_timeout_watcher;
|
||||
uv_timer_t uv_hrtimer;
|
||||
uv_idle_t uv_idle;
|
||||
#endif
|
||||
#if defined(LWS_WITH_LIBEVENT)
|
||||
|
@ -1870,8 +1874,8 @@ struct lws {
|
|||
const struct lws_protocols *protocol;
|
||||
struct lws **same_vh_protocol_prev, *same_vh_protocol_next;
|
||||
/* we get on the list if either the timeout or the timer is valid */
|
||||
struct lws *timeout_list;
|
||||
struct lws **timeout_list_prev;
|
||||
struct lws_dll_lws dll_timeout;
|
||||
struct lws_dll_lws dll_hrtimer;
|
||||
#if defined(LWS_WITH_PEER_LIMITS)
|
||||
struct lws_peer *peer;
|
||||
#endif
|
||||
|
@ -1911,8 +1915,9 @@ struct lws {
|
|||
uint64_t accept_start_us;
|
||||
#endif
|
||||
#endif
|
||||
lws_usec_t pending_timer;
|
||||
|
||||
time_t pending_timeout_set;
|
||||
time_t pending_timer_set;
|
||||
|
||||
/* ints */
|
||||
int position_in_fds_table;
|
||||
|
@ -1957,8 +1962,6 @@ struct lws {
|
|||
unsigned int handling_404;
|
||||
|
||||
unsigned int could_have_pending:1; /* detect back-to-back writes */
|
||||
|
||||
unsigned int timer_active:1;
|
||||
unsigned int outer_will_close:1;
|
||||
|
||||
#ifdef LWS_WITH_ACCESS_LOG
|
||||
|
@ -1990,7 +1993,6 @@ struct lws {
|
|||
unsigned short c_port;
|
||||
#endif
|
||||
unsigned short pending_timeout_limit;
|
||||
unsigned short pending_timer_limit;
|
||||
|
||||
uint8_t state; /* enum lws_connection_states */
|
||||
uint8_t mode; /* enum connection_mode */
|
||||
|
@ -2052,9 +2054,6 @@ __remove_wsi_socket_from_fds(struct lws *wsi);
|
|||
LWS_EXTERN int
|
||||
lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len);
|
||||
|
||||
LWS_EXTERN int
|
||||
lws_should_be_on_timeout_list(struct lws *wsi);
|
||||
|
||||
#ifndef LWS_LATENCY
|
||||
static inline void
|
||||
lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
|
||||
|
@ -2786,6 +2785,10 @@ lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer,
|
|||
|
||||
void
|
||||
__lws_remove_from_timeout_list(struct lws *wsi);
|
||||
|
||||
lws_usec_t
|
||||
__lws_hrtimer_service(struct lws_context_per_thread *pt);
|
||||
|
||||
void
|
||||
__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
|
||||
int
|
||||
|
|
|
@ -682,36 +682,11 @@ __lws_service_timeout_check(struct lws *wsi, time_t sec)
|
|||
if (lws_ext_cb_active(wsi, LWS_EXT_CB_1HZ, NULL, sec) < 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* is there a timer callback we should be doing?
|
||||
*/
|
||||
|
||||
if (wsi->timer_active &&
|
||||
lws_compare_time_t(wsi->context, sec, wsi->pending_timer_set) >
|
||||
wsi->pending_timer_limit) {
|
||||
wsi->timer_active = 0;
|
||||
|
||||
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");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!lws_should_be_on_timeout_list(wsi)) {
|
||||
__lws_remove_from_timeout_list(wsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* if we went beyond the allowed time, kill the
|
||||
* connection
|
||||
*/
|
||||
if (wsi->pending_timeout &&
|
||||
if (wsi->dll_timeout.prev &&
|
||||
lws_compare_time_t(wsi->context, sec, wsi->pending_timeout_set) >
|
||||
wsi->pending_timeout_limit) {
|
||||
|
||||
|
@ -1110,12 +1085,16 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
|
|||
struct allocated_headers *ah;
|
||||
struct lws_tokens eff_buf;
|
||||
unsigned int pending = 0;
|
||||
struct lws *wsi, *wsi1;
|
||||
struct lws *wsi;
|
||||
char draining_flow = 0;
|
||||
int timed_out = 0;
|
||||
time_t now;
|
||||
int n = 0, m;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
struct lws *wsi1;
|
||||
#endif
|
||||
|
||||
if (!context->protocol_init_done)
|
||||
if (lws_protocol_init(context))
|
||||
return -1;
|
||||
|
@ -1190,10 +1169,10 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
|
|||
*/
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
wsi = context->pt[tsi].timeout_list;
|
||||
while (wsi) {
|
||||
/* we have to take copies, because he may be deleted */
|
||||
wsi1 = wsi->timeout_list;
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
|
||||
context->pt[tsi].dll_head_timeout.next) {
|
||||
wsi = lws_container_of(d, struct lws, dll_timeout);
|
||||
tmp_fd = wsi->desc.sockfd;
|
||||
if (__lws_service_timeout_check(wsi, now)) {
|
||||
/* he did time out... */
|
||||
|
@ -1202,8 +1181,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
|
|||
timed_out = 1;
|
||||
/* he's gone, no need to mark as handled */
|
||||
}
|
||||
wsi = wsi1;
|
||||
}
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
|
||||
/*
|
||||
* Phase 2: double-check active ah timeouts independent of wsi
|
||||
|
|
|
@ -91,8 +91,7 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
pss->number = 0;
|
||||
/* just to test the timer api */
|
||||
// lws_set_timer(wsi, 3);
|
||||
lws_set_timer_usecs(wsi, 3 * LWS_USEC_PER_SEC);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
|
@ -118,8 +117,8 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
break;
|
||||
|
||||
case LWS_CALLBACK_TIMER:
|
||||
lwsl_notice("%s: LWS_CALLBACK_TIMER\n", __func__);
|
||||
lws_set_timer(wsi, 3);
|
||||
lwsl_info("%s: LWS_CALLBACK_TIMER: %p\n", __func__, wsi);
|
||||
lws_set_timer_usecs(wsi, 3 * LWS_USEC_PER_SEC);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -36,7 +36,7 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
case LWS_CALLBACK_ESTABLISHED:
|
||||
pss->number = 0;
|
||||
/* just to test the timer api */
|
||||
lws_set_timer(wsi, 3);
|
||||
lws_set_timer_usecs(wsi, 3 * LWS_USEC_PER_SEC);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
|
@ -66,8 +66,8 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
break;
|
||||
|
||||
case LWS_CALLBACK_TIMER:
|
||||
lwsl_notice("%s: LWS_CALLBACK_TIMER\n", __func__);
|
||||
lws_set_timer(wsi, 3);
|
||||
lwsl_notice("%s: LWS_CALLBACK_TIMER: %p\n", __func__, wsi);
|
||||
lws_set_timer_usecs(wsi, 3 * LWS_USEC_PER_SEC);
|
||||
break;
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Reference in a new issue