Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-os by
lwip_timeouts.c
00001 /** 00002 * @file 00003 * Stack-internal timers implementation. 00004 * This file includes timer callbacks for stack-internal timers as well as 00005 * functions to set up or stop timers and check for expired timers. 00006 * 00007 */ 00008 00009 /* 00010 * Copyright (c) 2001-2004 Swedish Institute of Computer Science. 00011 * All rights reserved. 00012 * 00013 * Redistribution and use in source and binary forms, with or without modification, 00014 * are permitted provided that the following conditions are met: 00015 * 00016 * 1. Redistributions of source code must retain the above copyright notice, 00017 * this list of conditions and the following disclaimer. 00018 * 2. Redistributions in binary form must reproduce the above copyright notice, 00019 * this list of conditions and the following disclaimer in the documentation 00020 * and/or other materials provided with the distribution. 00021 * 3. The name of the author may not be used to endorse or promote products 00022 * derived from this software without specific prior written permission. 00023 * 00024 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 00025 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 00026 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 00027 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00028 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 00029 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00030 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00031 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 00032 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 00033 * OF SUCH DAMAGE. 00034 * 00035 * This file is part of the lwIP TCP/IP stack. 00036 * 00037 * Author: Adam Dunkels <adam@sics.se> 00038 * Simon Goldschmidt 00039 * 00040 */ 00041 00042 #include "lwip/opt.h" 00043 00044 #include "lwip/timeouts.h" 00045 #include "lwip/priv/tcp_priv.h" 00046 00047 #include "lwip/def.h" 00048 #include "lwip/memp.h" 00049 #include "lwip/priv/tcpip_priv.h" 00050 00051 #include "lwip/ip4_frag.h" 00052 #include "lwip/etharp.h" 00053 #include "lwip/dhcp.h" 00054 #include "lwip/autoip.h" 00055 #include "lwip/igmp.h" 00056 #include "lwip/dns.h" 00057 #include "lwip/nd6.h" 00058 #include "lwip/ip6_frag.h" 00059 #include "lwip/mld6.h" 00060 #include "lwip/sys.h" 00061 #include "lwip/pbuf.h" 00062 00063 #if LWIP_DEBUG_TIMERNAMES 00064 #define HANDLER(x) x, #x 00065 #else /* LWIP_DEBUG_TIMERNAMES */ 00066 #define HANDLER(x) x 00067 #endif /* LWIP_DEBUG_TIMERNAMES */ 00068 00069 /** This array contains all stack-internal cyclic timers. To get the number of 00070 * timers, use LWIP_ARRAYSIZE() */ 00071 const struct lwip_cyclic_timer lwip_cyclic_timers[] = { 00072 #if LWIP_TCP 00073 /* The TCP timer is a special case: it does not have to run always and 00074 is triggered to start from TCP using tcp_timer_needed() */ 00075 {TCP_TMR_INTERVAL, HANDLER(tcp_tmr)}, 00076 #endif /* LWIP_TCP */ 00077 #if LWIP_IPV4 00078 #if IP_REASSEMBLY 00079 {IP_TMR_INTERVAL, HANDLER(ip_reass_tmr)}, 00080 #endif /* IP_REASSEMBLY */ 00081 #if LWIP_ARP 00082 {ARP_TMR_INTERVAL, HANDLER(etharp_tmr)}, 00083 #endif /* LWIP_ARP */ 00084 #if LWIP_DHCP 00085 {DHCP_COARSE_TIMER_MSECS, HANDLER(dhcp_coarse_tmr)}, 00086 {DHCP_FINE_TIMER_MSECS, HANDLER(dhcp_fine_tmr)}, 00087 #endif /* LWIP_DHCP */ 00088 #if LWIP_AUTOIP 00089 {AUTOIP_TMR_INTERVAL, HANDLER(autoip_tmr)}, 00090 #endif /* LWIP_AUTOIP */ 00091 #if LWIP_IGMP 00092 {IGMP_TMR_INTERVAL, HANDLER(igmp_tmr)}, 00093 #endif /* LWIP_IGMP */ 00094 #endif /* LWIP_IPV4 */ 00095 #if LWIP_DNS 00096 {DNS_TMR_INTERVAL, HANDLER(dns_tmr)}, 00097 #endif /* LWIP_DNS */ 00098 #if LWIP_IPV6 00099 {ND6_TMR_INTERVAL, HANDLER(nd6_tmr)}, 00100 #if LWIP_IPV6_REASS 00101 {IP6_REASS_TMR_INTERVAL, HANDLER(ip6_reass_tmr)}, 00102 #endif /* LWIP_IPV6_REASS */ 00103 #if LWIP_IPV6_MLD 00104 {MLD6_TMR_INTERVAL, HANDLER(mld6_tmr)}, 00105 #endif /* LWIP_IPV6_MLD */ 00106 #endif /* LWIP_IPV6 */ 00107 }; 00108 00109 #if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM 00110 00111 /** The one and only timeout list */ 00112 static struct sys_timeo *next_timeout; 00113 static u32_t timeouts_last_time; 00114 00115 #if LWIP_TCP 00116 /** global variable that shows if the tcp timer is currently scheduled or not */ 00117 static int tcpip_tcp_timer_active; 00118 00119 /** 00120 * Timer callback function that calls tcp_tmr() and reschedules itself. 00121 * 00122 * @param arg unused argument 00123 */ 00124 static void 00125 tcpip_tcp_timer(void *arg) 00126 { 00127 LWIP_UNUSED_ARG(arg); 00128 00129 /* call TCP timer handler */ 00130 tcp_tmr(); 00131 /* timer still needed? */ 00132 if (tcp_active_pcbs || tcp_tw_pcbs) { 00133 /* restart timer */ 00134 sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); 00135 } else { 00136 /* disable timer */ 00137 tcpip_tcp_timer_active = 0; 00138 } 00139 } 00140 00141 /** 00142 * Called from TCP_REG when registering a new PCB: 00143 * the reason is to have the TCP timer only running when 00144 * there are active (or time-wait) PCBs. 00145 */ 00146 void 00147 tcp_timer_needed(void) 00148 { 00149 /* timer is off but needed again? */ 00150 if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) { 00151 /* enable and start timer */ 00152 tcpip_tcp_timer_active = 1; 00153 sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); 00154 } 00155 } 00156 #endif /* LWIP_TCP */ 00157 00158 /** 00159 * Timer callback function that calls mld6_tmr() and reschedules itself. 00160 * 00161 * @param arg unused argument 00162 */ 00163 static void 00164 cyclic_timer(void *arg) 00165 { 00166 const struct lwip_cyclic_timer* cyclic = (const struct lwip_cyclic_timer*)arg; 00167 #if LWIP_DEBUG_TIMERNAMES 00168 LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: %s()\n", cyclic->handler_name)); 00169 #endif 00170 cyclic->handler(); 00171 sys_timeout(cyclic->interval_ms, cyclic_timer, arg); 00172 } 00173 00174 /** Initialize this module */ 00175 void sys_timeouts_init(void) 00176 { 00177 size_t i; 00178 /* tcp_tmr() at index 0 is started on demand */ 00179 for (i = 1; i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++) { 00180 /* we have to cast via size_t to get rid of const warning 00181 (this is OK as cyclic_timer() casts back to const* */ 00182 sys_timeout(lwip_cyclic_timers[i].interval_ms, cyclic_timer, (void*)(size_t)&lwip_cyclic_timers[i]); 00183 } 00184 00185 /* Initialise timestamp for sys_check_timeouts */ 00186 timeouts_last_time = sys_now(); 00187 } 00188 00189 /** 00190 * Create a one-shot timer (aka timeout). Timeouts are processed in the 00191 * following cases: 00192 * - while waiting for a message using sys_timeouts_mbox_fetch() 00193 * - by calling sys_check_timeouts() (NO_SYS==1 only) 00194 * 00195 * @param msecs time in milliseconds after that the timer should expire 00196 * @param handler callback function to call when msecs have elapsed 00197 * @param arg argument to pass to the callback function 00198 */ 00199 #if LWIP_DEBUG_TIMERNAMES 00200 void 00201 sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name) 00202 #else /* LWIP_DEBUG_TIMERNAMES */ 00203 void 00204 sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) 00205 #endif /* LWIP_DEBUG_TIMERNAMES */ 00206 { 00207 struct sys_timeo *timeout, *t; 00208 u32_t now, diff; 00209 00210 timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT); 00211 if (timeout == NULL) { 00212 LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL); 00213 return; 00214 } 00215 00216 now = sys_now(); 00217 if (next_timeout == NULL) { 00218 diff = 0; 00219 timeouts_last_time = now; 00220 } else { 00221 diff = now - timeouts_last_time; 00222 } 00223 00224 timeout->next = NULL; 00225 timeout->h = handler; 00226 timeout->arg = arg; 00227 timeout->time = msecs + diff; 00228 #if LWIP_DEBUG_TIMERNAMES 00229 timeout->handler_name = handler_name; 00230 LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n", 00231 (void *)timeout, msecs, handler_name, (void *)arg)); 00232 #endif /* LWIP_DEBUG_TIMERNAMES */ 00233 00234 if (next_timeout == NULL) { 00235 next_timeout = timeout; 00236 return; 00237 } 00238 00239 if (next_timeout->time > msecs) { 00240 next_timeout->time -= msecs; 00241 timeout->next = next_timeout; 00242 next_timeout = timeout; 00243 } else { 00244 for (t = next_timeout; t != NULL; t = t->next) { 00245 timeout->time -= t->time; 00246 if (t->next == NULL || t->next->time > timeout->time) { 00247 if (t->next != NULL) { 00248 t->next->time -= timeout->time; 00249 } else if (timeout->time > msecs) { 00250 /* If this is the case, 'timeouts_last_time' and 'now' differs too much. 00251 This can be due to sys_check_timeouts() not being called at the right 00252 times, but also when stopping in a breakpoint. Anyway, let's assume 00253 this is not wanted, so add the first timer's time instead of 'diff' */ 00254 timeout->time = msecs + next_timeout->time; 00255 } 00256 timeout->next = t->next; 00257 t->next = timeout; 00258 break; 00259 } 00260 } 00261 } 00262 } 00263 00264 /** 00265 * Go through timeout list (for this task only) and remove the first matching 00266 * entry (subsequent entries remain untouched), even though the timeout has not 00267 * triggered yet. 00268 * 00269 * @param handler callback function that would be called by the timeout 00270 * @param arg callback argument that would be passed to handler 00271 */ 00272 void 00273 sys_untimeout(sys_timeout_handler handler, void *arg) 00274 { 00275 struct sys_timeo *prev_t, *t; 00276 00277 if (next_timeout == NULL) { 00278 return; 00279 } 00280 00281 for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { 00282 if ((t->h == handler) && (t->arg == arg)) { 00283 /* We have a match */ 00284 /* Unlink from previous in list */ 00285 if (prev_t == NULL) { 00286 next_timeout = t->next; 00287 } else { 00288 prev_t->next = t->next; 00289 } 00290 /* If not the last one, add time of this one back to next */ 00291 if (t->next != NULL) { 00292 t->next->time += t->time; 00293 } 00294 memp_free(MEMP_SYS_TIMEOUT, t); 00295 return; 00296 } 00297 } 00298 return; 00299 } 00300 00301 /** 00302 * @ingroup lwip_nosys 00303 * Handle timeouts for NO_SYS==1 (i.e. without using 00304 * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout 00305 * handler functions when timeouts expire. 00306 * 00307 * Must be called periodically from your main loop. 00308 */ 00309 #if !NO_SYS && !defined __DOXYGEN__ 00310 static 00311 #endif /* !NO_SYS */ 00312 void 00313 sys_check_timeouts(void) 00314 { 00315 if (next_timeout) { 00316 struct sys_timeo *tmptimeout; 00317 u32_t diff; 00318 sys_timeout_handler handler; 00319 void *arg; 00320 u8_t had_one; 00321 u32_t now; 00322 00323 now = sys_now(); 00324 /* this cares for wraparounds */ 00325 diff = now - timeouts_last_time; 00326 do { 00327 PBUF_CHECK_FREE_OOSEQ(); 00328 had_one = 0; 00329 tmptimeout = next_timeout; 00330 if (tmptimeout && (tmptimeout->time <= diff)) { 00331 /* timeout has expired */ 00332 had_one = 1; 00333 timeouts_last_time += tmptimeout->time; 00334 diff -= tmptimeout->time; 00335 next_timeout = tmptimeout->next; 00336 handler = tmptimeout->h; 00337 arg = tmptimeout->arg; 00338 #if LWIP_DEBUG_TIMERNAMES 00339 if (handler != NULL) { 00340 LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n", 00341 tmptimeout->handler_name, arg)); 00342 } 00343 #endif /* LWIP_DEBUG_TIMERNAMES */ 00344 memp_free(MEMP_SYS_TIMEOUT, tmptimeout); 00345 if (handler != NULL) { 00346 #if !NO_SYS 00347 /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the 00348 timeout handler function. */ 00349 LOCK_TCPIP_CORE(); 00350 #endif /* !NO_SYS */ 00351 handler(arg); 00352 #if !NO_SYS 00353 UNLOCK_TCPIP_CORE(); 00354 #endif /* !NO_SYS */ 00355 } 00356 LWIP_TCPIP_THREAD_ALIVE(); 00357 } 00358 /* repeat until all expired timers have been called */ 00359 } while (had_one); 00360 } 00361 } 00362 00363 #if NO_SYS 00364 /** Set back the timestamp of the last call to sys_check_timeouts() 00365 * This is necessary if sys_check_timeouts() hasn't been called for a long 00366 * time (e.g. while saving energy) to prevent all timer functions of that 00367 * period being called. 00368 */ 00369 void 00370 sys_restart_timeouts(void) 00371 { 00372 timeouts_last_time = sys_now(); 00373 } 00374 #endif /* NO_SYS */ 00375 00376 /** Return the time left before the next timeout is due. If no timeouts are 00377 * enqueued, returns 0xffffffff 00378 */ 00379 #if !NO_SYS 00380 static 00381 #endif /* !NO_SYS */ 00382 u32_t 00383 sys_timeouts_sleeptime(void) 00384 { 00385 u32_t diff; 00386 if (next_timeout == NULL) { 00387 return 0xffffffff; 00388 } 00389 diff = sys_now() - timeouts_last_time; 00390 if (diff > next_timeout->time) { 00391 return 0; 00392 } else { 00393 return next_timeout->time - diff; 00394 } 00395 } 00396 00397 #if !NO_SYS 00398 00399 /** 00400 * Wait (forever) for a message to arrive in an mbox. 00401 * While waiting, timeouts are processed. 00402 * 00403 * @param mbox the mbox to fetch the message from 00404 * @param msg the place to store the message 00405 */ 00406 void 00407 sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg) 00408 { 00409 u32_t sleeptime; 00410 00411 again: 00412 if (!next_timeout) { 00413 sys_arch_mbox_fetch(mbox, msg, 0); 00414 return; 00415 } 00416 00417 sleeptime = sys_timeouts_sleeptime(); 00418 if (sleeptime == 0 || sys_arch_mbox_fetch(mbox, msg, sleeptime) == SYS_ARCH_TIMEOUT) { 00419 /* If a SYS_ARCH_TIMEOUT value is returned, a timeout occurred 00420 before a message could be fetched. */ 00421 sys_check_timeouts(); 00422 /* We try again to fetch a message from the mbox. */ 00423 goto again; 00424 } 00425 } 00426 00427 #endif /* NO_SYS */ 00428 00429 #else /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */ 00430 /* Satisfy the TCP code which calls this function */ 00431 void 00432 tcp_timer_needed(void) 00433 { 00434 } 00435 #endif /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */
Generated on Tue Jul 12 2022 13:15:55 by
