diff --git a/lib/event-libs/libuv.c b/lib/event-libs/libuv.c index 1bfc4655..e2a3b497 100644 --- a/lib/event-libs/libuv.c +++ b/lib/event-libs/libuv.c @@ -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); diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index c087c070..9ba3461c 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -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) diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 13744b58..210ff562 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -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 * diff --git a/lib/plat/lws-plat-unix.c b/lib/plat/lws-plat-unix.c index fe1fbfe6..4927b3da 100644 --- a/lib/plat/lws-plat-unix.c +++ b/lib/plat/lws-plat-unix.c @@ -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 diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 994d1054..02b22a2d 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -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 diff --git a/lib/service.c b/lib/service.c index 215424b0..122564da 100644 --- a/lib/service.c +++ b/lib/service.c @@ -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 diff --git a/plugins/protocol_dumb_increment.c b/plugins/protocol_dumb_increment.c index 258ac2b9..e935bf4a 100644 --- a/plugins/protocol_dumb_increment.c +++ b/plugins/protocol_dumb_increment.c @@ -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: diff --git a/test-apps/test-server-dumb-increment.c b/test-apps/test-server-dumb-increment.c index 5b58316c..36ade9d6 100644 --- a/test-apps/test-server-dumb-increment.c +++ b/test-apps/test-server-dumb-increment.c @@ -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; /*