1.\" $NetBSD: timeout.9,v 1.2 1996/06/23 22:32:34 pk Exp $ 2.\" 3.\" Copyright (c) 1996 The NetBSD Foundation, Inc. 4.\" All rights reserved. 5.\" 6.\" This code is derived from software contributed to The NetBSD Foundation 7.\" by Paul Kranenburg. 8.\" 9.\" Redistribution and use in source and binary forms, with or without 10.\" modification, are permitted provided that the following conditions 11.\" are met: 12.\" 1. Redistributions of source code must retain the above copyright 13.\" notice, this list of conditions and the following disclaimer. 14.\" 2. Redistributions in binary form must reproduce the above copyright 15.\" notice, this list of conditions and the following disclaimer in the 16.\" documentation and/or other materials provided with the distribution. 17.\" 18.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 22.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28.\" POSSIBILITY OF SUCH DAMAGE. 29.\" 30.\" $FreeBSD$ 31.\" 32.Dd October 8, 2014 33.Dt TIMEOUT 9 34.Os 35.Sh NAME 36.Nm callout_active , 37.Nm callout_deactivate , 38.Nm callout_drain , 39.Nm callout_handle_init , 40.Nm callout_init , 41.Nm callout_init_mtx , 42.Nm callout_init_rm , 43.Nm callout_init_rw , 44.Nm callout_pending , 45.Nm callout_reset , 46.Nm callout_reset_curcpu , 47.Nm callout_reset_on , 48.Nm callout_reset_sbt , 49.Nm callout_reset_sbt_curcpu , 50.Nm callout_reset_sbt_on , 51.Nm callout_schedule , 52.Nm callout_schedule_curcpu , 53.Nm callout_schedule_on , 54.Nm callout_schedule_sbt , 55.Nm callout_schedule_sbt_curcpu , 56.Nm callout_schedule_sbt_on , 57.Nm callout_stop , 58.Nm timeout , 59.Nm untimeout 60.Nd execute a function after a specified length of time 61.Sh SYNOPSIS 62.In sys/types.h 63.In sys/systm.h 64.Bd -literal 65typedef void timeout_t (void *); 66.Ed 67.Ft int 68.Fn callout_active "struct callout *c" 69.Ft void 70.Fn callout_deactivate "struct callout *c" 71.Ft int 72.Fn callout_drain "struct callout *c" 73.Ft void 74.Fn callout_handle_init "struct callout_handle *handle" 75.Bd -literal 76struct callout_handle handle = CALLOUT_HANDLE_INITIALIZER(&handle); 77.Ed 78.Ft void 79.Fn callout_init "struct callout *c" "int mpsafe" 80.Ft void 81.Fn callout_init_mtx "struct callout *c" "struct mtx *mtx" "int flags" 82.Ft void 83.Fn callout_init_rm "struct callout *c" "struct rmlock *rm" "int flags" 84.Ft void 85.Fn callout_init_rw "struct callout *c" "struct rwlock *rw" "int flags" 86.Ft int 87.Fn callout_pending "struct callout *c" 88.Ft int 89.Fn callout_reset "struct callout *c" "int ticks" "timeout_t *func" "void *arg" 90.Ft int 91.Fn callout_reset_curcpu "struct callout *c" "int ticks" "timeout_t *func" \ 92"void *arg" 93.Ft int 94.Fn callout_reset_on "struct callout *c" "int ticks" "timeout_t *func" \ 95"void *arg" "int cpu" 96.Ft int 97.Fn callout_reset_sbt "struct callout *c" "sbintime_t sbt" \ 98"sbintime_t pr" "timeout_t *func" "void *arg" "int flags" 99.Ft int 100.Fn callout_reset_sbt_curcpu "struct callout *c" "sbintime_t sbt" \ 101"sbintime_t pr" "timeout_t *func" "void *arg" "int flags" 102.Ft int 103.Fn callout_reset_sbt_on "struct callout *c" "sbintime_t sbt" \ 104"sbintime_t pr" "timeout_t *func" "void *arg" "int cpu" "int flags" 105.Ft int 106.Fn callout_schedule "struct callout *c" "int ticks" 107.Ft int 108.Fn callout_schedule_curcpu "struct callout *c" "int ticks" 109.Ft int 110.Fn callout_schedule_on "struct callout *c" "int ticks" "int cpu" 111.Ft int 112.Fn callout_schedule_sbt "struct callout *c" "sbintime_t sbt" \ 113"sbintime_t pr" "int flags" 114.Ft int 115.Fn callout_schedule_sbt_curcpu "struct callout *c" "sbintime_t sbt" \ 116"sbintime_t pr" "int flags" 117.Ft int 118.Fn callout_schedule_sbt_on "struct callout *c" "sbintime_t sbt" \ 119"sbintime_t pr" "int cpu" "int flags" 120.Ft int 121.Fn callout_stop "struct callout *c" 122.Ft struct callout_handle 123.Fn timeout "timeout_t *func" "void *arg" "int ticks" 124.Ft void 125.Fn untimeout "timeout_t *func" "void *arg" "struct callout_handle handle" 126.Sh DESCRIPTION 127The 128.Nm callout 129API is used to schedule a call to an arbitrary function at a specific 130time in the future. 131Consumers of this API are required to allocate a callout structure 132.Pq struct callout 133for each pending function invocation. 134This structure stores state about the pending function invocation including 135the function to be called and the time at which the function should be invoked. 136Pending function calls can be cancelled or rescheduled to a different time. 137In addition, 138a callout structure may be reused to schedule a new function call after a 139scheduled call is completed. 140.Pp 141Callouts only provide a single-shot mode. 142If a consumer requires a periodic timer, 143it must explicitly reschedule each function call. 144This is normally done by rescheduling the subsequent call within the called 145function. 146.Pp 147Callout functions must not sleep. 148They may not acquire sleepable locks, 149wait on condition variables, 150perform blocking allocation requests, 151or invoke any other action that might sleep. 152.Pp 153Each callout structure must be initialized by 154.Fn callout_init , 155.Fn callout_init_mtx , 156.Fn callout_init_rm , 157or 158.Fn callout_init_rw 159before it is passed to any of the other callout functions. 160The 161.Fn callout_init 162function initializes a callout structure in 163.Fa c 164that is not associated with a specific lock. 165If the 166.Fa mpsafe 167argument is zero, 168the callout structure is not considered to be 169.Dq multi-processor safe ; 170and the Giant lock will be acquired before calling the callout function 171and released when the callout function returns. 172.Pp 173The 174.Fn callout_init_mtx , 175.Fn callout_init_rm , 176and 177.Fn callout_init_rw 178functions initialize a callout structure in 179.Fa c 180that is associated with a specific lock. 181The lock is specified by the 182.Fa mtx , 183.Fa rm , 184or 185.Fa rw 186parameter. 187The associated lock must be held while stopping or rescheduling the 188callout. 189The callout subsystem acquires the associated lock before calling the 190callout function and releases it after the function returns. 191If the callout was cancelled while the callout subsystem waited for the 192associated lock, 193the callout function is not called, 194and the associated lock is released. 195This ensures that stopping or rescheduling the callout will abort any 196previously scheduled invocation. 197.Pp 198Only regular mutexes may be used with 199.Fn callout_init_mtx ; 200spin mutexes are not supported. 201A sleepable read-mostly lock 202.Po 203one initialized with the 204.Dv RM_SLEEPABLE 205flag 206.Pc 207may not be used with 208.Fn callout_init_rm . 209Similarly, other sleepable lock types such as 210.Xr sx 9 211and 212.Xr lockmgr 9 213cannot be used with callouts because sleeping is not permitted in 214the callout subsystem. 215.Pp 216These 217.Fa flags 218may be specified for 219.Fn callout_init_mtx , 220.Fn callout_init_rm , 221or 222.Fn callout_init_rw : 223.Bl -tag -width ".Dv CALLOUT_RETURNUNLOCKED" 224.It Dv CALLOUT_RETURNUNLOCKED 225The callout function will release the associated lock itself, 226so the callout subsystem should not attempt to unlock it 227after the callout function returns. 228.It Dv CALLOUT_SHAREDLOCK 229The lock is only acquired in read mode when running the callout handler. 230This flag is ignored by 231.Fn callout_init_mtx . 232.El 233.Pp 234The function 235.Fn callout_stop 236cancels a callout 237.Fa c 238if it is currently pending. 239If the callout is pending, then 240.Fn callout_stop 241returns a non-zero value. 242If the callout is not set, 243has already been serviced, 244or is currently being serviced, 245then zero will be returned. 246If the callout has an associated lock, 247then that lock must be held when this function is called. 248.Pp 249The function 250.Fn callout_drain 251is identical to 252.Fn callout_stop 253except that it will wait for the callout 254.Fa c 255to complete if it is already in progress. 256This function MUST NOT be called while holding any 257locks on which the callout might block, or deadlock will result. 258Note that if the callout subsystem has already begun processing this 259callout, then the callout function may be invoked before 260.Fn callout_drain 261returns. 262However, the callout subsystem does guarantee that the callout will be 263fully stopped before 264.Fn callout_drain 265returns. 266.Pp 267The 268.Fn callout_reset 269and 270.Fn callout_schedule 271function families schedule a future function invocation for callout 272.Fa c . 273If 274.Fa c 275already has a pending callout, 276it is cancelled before the new invocation is scheduled. 277These functions return a non-zero value if a pending callout was cancelled 278and zero if there was no pending callout. 279If the callout has an associated lock, 280then that lock must be held when any of these functions are called. 281.Pp 282The time at which the callout function will be invoked is determined by 283either the 284.Fa ticks 285argument or the 286.Fa sbt , 287.Fa pr , 288and 289.Fa flags 290arguments. 291When 292.Fa ticks 293is used, 294the callout is scheduled to execute after 295.Fa ticks Ns No /hz 296seconds. 297Non-positive values of 298.Fa ticks 299are silently converted to the value 300.Sq 1 . 301.Pp 302The 303.Fa sbt , 304.Fa pr , 305and 306.Fa flags 307arguments provide more control over the scheduled time including 308support for higher resolution times, 309specifying the precision of the scheduled time, 310and setting an absolute deadline instead of a relative timeout. 311The callout is scheduled to execute in a time window which begins at 312the time specified in 313.Fa sbt 314and extends for the amount of time specified in 315.Fa pr . 316If 317.Fa sbt 318specifies a time in the past, 319the window is adjusted to start at the current time. 320A non-zero value for 321.Fa pr 322allows the callout subsystem to coalesce callouts scheduled close to each 323other into fewer timer interrupts, 324reducing processing overhead and power consumption. 325These 326.Fa flags 327may be specified to adjust the interpretation of 328.Fa sbt 329and 330.Fa pr : 331.Bl -tag -width ".Dv C_DIRECT_EXEC" 332.It Dv C_ABSOLUTE 333Handle the 334.Fa sbt 335argument as an absolute time since boot. 336By default, 337.Fa sbt 338is treated as a relative amount of time, 339similar to 340.Fa ticks . 341.It Dv C_DIRECT_EXEC 342Run the handler directly from hardware interrupt context instead of from the 343softclock thread. 344This reduces latency and overhead, but puts more constraints on the callout 345function. 346Callout functions run in this context may use only spin mutexes for locking 347and should be as small as possible because they run with absolute priority. 348.It Fn C_PREL 349Specifies relative event time precision as binary logarithm of time interval 350divided by acceptable time deviation: 1 -- 1/2, 2 -- 1/4, etc. 351Note that the larger of 352.Fa pr 353or this value is used as the length of the time window. 354Smaller values 355.Pq which result in larger time intervals 356allow the callout subsystem to aggregate more events in one timer interrupt. 357.It Dv C_HARDCLOCK 358Align the timeouts to 359.Fn hardclock 360calls if possible. 361.El 362.Pp 363The 364.Fn callout_reset 365functions accept a 366.Fa func 367argument which identifies the function to be called when the time expires. 368It must be a pointer to a function that takes a single 369.Fa void * 370argument. 371Upon invocation, 372.Fa func 373will receive 374.Fa arg 375as its only argument. 376The 377.Fn callout_schedule 378functions reuse the 379.Fa func 380and 381.Fa arg 382arguments from the previous callout. 383Note that one of the 384.Fn callout_reset 385functions must always be called to initialize 386.Fa func 387and 388.Fa arg 389before one of the 390.Fn callout_schedule 391functions can be used. 392.Pp 393The callout subsystem provides a softclock thread for each CPU in the system. 394Callouts are assigned to a single CPU and are executed by the softclock thread 395for that CPU. 396Initially, 397callouts are assigned to CPU 0. 398The 399.Fn callout_reset_on , 400.Fn callout_reset_sbt_on , 401.Fn callout_schedule_on 402and 403.Fn callout_schedule_sbt_on 404functions assign the callout to CPU 405.Fa cpu . 406The 407.Fn callout_reset_curcpu , 408.Fn callout_reset_sbt_curpu , 409.Fn callout_schedule_curcpu 410and 411.Fn callout_schedule_sbt_curcpu 412functions assign the callout to the current CPU. 413The 414.Fn callout_reset , 415.Fn callout_reset_sbt , 416.Fn callout_schedule 417and 418.Fn callout_schedule_sbt 419functions schedule the callout to execute in the softclock thread of the CPU 420to which it is currently assigned. 421.Pp 422Softclock threads are not pinned to their respective CPUs by default. 423The softclock thread for CPU 0 can be pinned to CPU 0 by setting the 424.Va kern.pin_default_swi 425loader tunable to a non-zero value. 426Softclock threads for CPUs other than zero can be pinned to their 427respective CPUs by setting the 428.Va kern.pin_pcpu_swi 429loader tunable to a non-zero value. 430.Pp 431The macros 432.Fn callout_pending , 433.Fn callout_active 434and 435.Fn callout_deactivate 436provide access to the current state of the callout. 437The 438.Fn callout_pending 439macro checks whether a callout is 440.Em pending ; 441a callout is considered 442.Em pending 443when a timeout has been set but the time has not yet arrived. 444Note that once the timeout time arrives and the callout subsystem 445starts to process this callout, 446.Fn callout_pending 447will return 448.Dv FALSE 449even though the callout function may not have finished 450.Pq or even begun 451executing. 452The 453.Fn callout_active 454macro checks whether a callout is marked as 455.Em active , 456and the 457.Fn callout_deactivate 458macro clears the callout's 459.Em active 460flag. 461The callout subsystem marks a callout as 462.Em active 463when a timeout is set and it clears the 464.Em active 465flag in 466.Fn callout_stop 467and 468.Fn callout_drain , 469but it 470.Em does not 471clear it when a callout expires normally via the execution of the 472callout function. 473.Ss "Avoiding Race Conditions" 474The callout subsystem invokes callout functions from its own thread 475context. 476Without some kind of synchronization, 477it is possible that a callout 478function will be invoked concurrently with an attempt to stop or reset 479the callout by another thread. 480In particular, since callout functions typically acquire a lock as 481their first action, the callout function may have already been invoked, 482but is blocked waiting for that lock at the time that another thread 483tries to reset or stop the callout. 484.Pp 485There are three main techniques for addressing these 486synchronization concerns. 487The first approach is preferred as it is the simplest: 488.Bl -enum -offset indent 489.It 490Callouts can be associated with a specific lock when they are initialized 491by 492.Fn callout_init_mtx , 493.Fn callout_init_rm , 494or 495.Fn callout_init_rw . 496When a callout is associated with a lock, 497the callout subsystem acquires the lock before the callout function is 498invoked. 499This allows the callout subsystem to transparently handle races between 500callout cancellation, 501scheduling, 502and execution. 503Note that the associated lock must be acquired before calling 504.Fn callout_stop 505or one of the 506.Fn callout_reset 507or 508.Fn callout_schedule 509functions to provide this safety. 510.Pp 511A callout initialized via 512.Fn callout_init 513with 514.Fa mpsafe 515set to zero is implicitly associated with the 516.Va Giant 517mutex. 518If 519.Va Giant 520is held when cancelling or rescheduling the callout, 521then its use will prevent races with the callout function. 522.It 523The return value from 524.Fn callout_stop 525.Po 526or the 527.Fn callout_reset 528and 529.Fn callout_schedule 530function families 531.Pc 532indicates whether or not the callout was removed. 533If it is known that the callout was set and the callout function has 534not yet executed, then a return value of 535.Dv FALSE 536indicates that the callout function is about to be called. 537For example: 538.Bd -literal -offset indent 539if (sc->sc_flags & SCFLG_CALLOUT_RUNNING) { 540 if (callout_stop(&sc->sc_callout)) { 541 sc->sc_flags &= ~SCFLG_CALLOUT_RUNNING; 542 /* successfully stopped */ 543 } else { 544 /* 545 * callout has expired and callout 546 * function is about to be executed 547 */ 548 } 549} 550.Ed 551.It 552The 553.Fn callout_pending , 554.Fn callout_active 555and 556.Fn callout_deactivate 557macros can be used together to work around the race conditions. 558When a callout's timeout is set, the callout subsystem marks the 559callout as both 560.Em active 561and 562.Em pending . 563When the timeout time arrives, the callout subsystem begins processing 564the callout by first clearing the 565.Em pending 566flag. 567It then invokes the callout function without changing the 568.Em active 569flag, and does not clear the 570.Em active 571flag even after the callout function returns. 572The mechanism described here requires the callout function itself to 573clear the 574.Em active 575flag using the 576.Fn callout_deactivate 577macro. 578The 579.Fn callout_stop 580and 581.Fn callout_drain 582functions always clear both the 583.Em active 584and 585.Em pending 586flags before returning. 587.Pp 588The callout function should first check the 589.Em pending 590flag and return without action if 591.Fn callout_pending 592returns 593.Dv TRUE . 594This indicates that the callout was rescheduled using 595.Fn callout_reset 596just before the callout function was invoked. 597If 598.Fn callout_active 599returns 600.Dv FALSE 601then the callout function should also return without action. 602This indicates that the callout has been stopped. 603Finally, the callout function should call 604.Fn callout_deactivate 605to clear the 606.Em active 607flag. 608For example: 609.Bd -literal -offset indent 610mtx_lock(&sc->sc_mtx); 611if (callout_pending(&sc->sc_callout)) { 612 /* callout was reset */ 613 mtx_unlock(&sc->sc_mtx); 614 return; 615} 616if (!callout_active(&sc->sc_callout)) { 617 /* callout was stopped */ 618 mtx_unlock(&sc->sc_mtx); 619 return; 620} 621callout_deactivate(&sc->sc_callout); 622/* rest of callout function */ 623.Ed 624.Pp 625Together with appropriate synchronization, such as the mutex used above, 626this approach permits the 627.Fn callout_stop 628and 629.Fn callout_reset 630functions to be used at any time without races. 631For example: 632.Bd -literal -offset indent 633mtx_lock(&sc->sc_mtx); 634callout_stop(&sc->sc_callout); 635/* The callout is effectively stopped now. */ 636.Ed 637.Pp 638If the callout is still pending then these functions operate normally, 639but if processing of the callout has already begun then the tests in 640the callout function cause it to return without further action. 641Synchronization between the callout function and other code ensures that 642stopping or resetting the callout will never be attempted while the 643callout function is past the 644.Fn callout_deactivate 645call. 646.Pp 647The above technique additionally ensures that the 648.Em active 649flag always reflects whether the callout is effectively enabled or 650disabled. 651If 652.Fn callout_active 653returns false, then the callout is effectively disabled, since even if 654the callout subsystem is actually just about to invoke the callout 655function, the callout function will return without action. 656.El 657.Pp 658There is one final race condition that must be considered when a 659callout is being stopped for the last time. 660In this case it may not be safe to let the callout function itself 661detect that the callout was stopped, since it may need to access 662data objects that have already been destroyed or recycled. 663To ensure that the callout is completely finished, a call to 664.Fn callout_drain 665should be used. 666In particular, 667a callout should always be drained prior to destroying its associated lock 668or releasing the storage for the callout structure. 669.Sh LEGACY API 670.Bf Sy 671The functions below are a legacy API that will be removed in a future release. 672New code should not use these routines. 673.Ef 674.Pp 675The function 676.Fn timeout 677schedules a call to the function given by the argument 678.Fa func 679to take place after 680.Fa ticks Ns No /hz 681seconds. 682Non-positive values of 683.Fa ticks 684are silently converted to the value 685.Sq 1 . 686.Fa func 687should be a pointer to a function that takes a 688.Fa void * 689argument. 690Upon invocation, 691.Fa func 692will receive 693.Fa arg 694as its only argument. 695The return value from 696.Fn timeout 697is a 698.Ft struct callout_handle 699which can be used in conjunction with the 700.Fn untimeout 701function to request that a scheduled timeout be canceled. 702.Pp 703The function 704.Fn callout_handle_init 705can be used to initialize a handle to a state which will cause 706any calls to 707.Fn untimeout 708with that handle to return with no side 709effects. 710.Pp 711Assigning a callout handle the value of 712.Fn CALLOUT_HANDLE_INITIALIZER 713performs the same function as 714.Fn callout_handle_init 715and is provided for use on statically declared or global callout handles. 716.Pp 717The function 718.Fn untimeout 719cancels the timeout associated with 720.Fa handle 721using the 722.Fa func 723and 724.Fa arg 725arguments to validate the handle. 726If the handle does not correspond to a timeout with 727the function 728.Fa func 729taking the argument 730.Fa arg 731no action is taken. 732.Fa handle 733must be initialized by a previous call to 734.Fn timeout , 735.Fn callout_handle_init , 736or assigned the value of 737.Fn CALLOUT_HANDLE_INITIALIZER "&handle" 738before being passed to 739.Fn untimeout . 740The behavior of calling 741.Fn untimeout 742with an uninitialized handle 743is undefined. 744.Pp 745As handles are recycled by the system, it is possible (although unlikely) 746that a handle from one invocation of 747.Fn timeout 748may match the handle of another invocation of 749.Fn timeout 750if both calls used the same function pointer and argument, and the first 751timeout is expired or canceled before the second call. 752The timeout facility offers O(1) running time for 753.Fn timeout 754and 755.Fn untimeout . 756Timeouts are executed from 757.Fn softclock 758with the 759.Va Giant 760lock held. 761Thus they are protected from re-entrancy. 762.Sh RETURN VALUES 763The 764.Fn callout_active 765macro returns the state of a callout's 766.Em active 767flag. 768.Pp 769The 770.Fn callout_pending 771macro returns the state of a callout's 772.Em pending 773flag. 774.Pp 775The 776.Fn callout_reset 777and 778.Fn callout_schedule 779function families return non-zero if the callout was pending before the new 780function invocation was scheduled. 781.Pp 782The 783.Fn callout_stop 784and 785.Fn callout_drain 786functions return non-zero if the callout was still pending when it was 787called or zero otherwise. 788The 789.Fn timeout 790function returns a 791.Ft struct callout_handle 792that can be passed to 793.Fn untimeout . 794.Sh HISTORY 795The current timeout and untimeout routines are based on the work of 796.An Adam M. Costello 797and 798.An George Varghese , 799published in a technical report entitled 800.%T "Redesigning the BSD Callout and Timer Facilities" 801and modified slightly for inclusion in 802.Fx 803by 804.An Justin T. Gibbs . 805The original work on the data structures used in this implementation 806was published by 807.An G. Varghese 808and 809.An A. Lauck 810in the paper 811.%T "Hashed and Hierarchical Timing Wheels: Data Structures for the Efficient Implementation of a Timer Facility" 812in the 813.%B "Proceedings of the 11th ACM Annual Symposium on Operating Systems Principles" . 814The current implementation replaces the long standing 815.Bx 816linked list 817callout mechanism which offered O(n) insertion and removal running time 818but did not generate or require handles for untimeout operations. 819