1 /* 2 3 silcfsm.h 4 5 Author: Pekka Riikonen <priikone@silcnet.org> 6 7 Copyright (C) 2005, 2006, 2007 Pekka Riikonen 8 9 The contents of this file are subject to one of the Licenses specified 10 in the COPYING file; You may not use this file except in compliance 11 with the License. 12 13 The software distributed under the License is distributed on an "AS IS" 14 basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY 15 KIND, either expressed or implied. See the COPYING file for more 16 information. 17 18 */ 19 20 /****h* silcutil/SILC Finite State Machine 21 * 22 * DESCRIPTION 23 * 24 * SILC FSM Interface implements a finite state machine. The FSM can be 25 * used to implement all kinds of machines and protocols. The FSM supports 26 * also threads and can be synchronized by using mutex locks. The FSM 27 * also supports real system threads. It is possible to create new FSM 28 * thread and then execute in real system thread, if platform supports 29 * threads. 30 * 31 * The FSM provides also asynchronous events that can be used to wait for 32 * some events or states to occur. The FSM events may be used as condition 33 * variables and signallers. The FSM events can safely be used in FSM 34 * threads that are executed in real system threads. 35 * 36 * To synchronize machines that use FSM threads that are executed in real 37 * system threads the SILC Mutex API (silcmutex.h) may be used. Normal 38 * multi-threaded coding conventions apply when programming with real FSM 39 * threads. If the FSM threads are not real system threads, synchronization 40 * is not required. 41 * 42 ***/ 43 44 #ifndef SILCFSM_H 45 #define SILCFSM_H 46 47 /****s* silcutil/SilcFSMAPI/SilcFSM 48 * 49 * NAME 50 * 51 * typedef struct SilcFSMObject *SilcFSM; 52 * 53 * DESCRIPTION 54 * 55 * The actual FSM context and is allocated with silc_fsm_alloc and 56 * given as argument to all silc_fsm_* functions. It is freed by 57 * silc_fsm_free function. It is also possible to use pre-allocated 58 * FSM context by using SilcFSMStruct instead of SilcFSM. 59 * 60 ***/ 61 typedef struct SilcFSMObject *SilcFSM; 62 63 /****s* silcutil/SilcFSMAPI/SilcFSMStruct 64 * 65 * NAME 66 * 67 * typedef struct SilcFSMObject SilcFSMStruct; 68 * 69 * DESCRIPTION 70 * 71 * The actual FSM context and can be used as pre-allocated FSM context, 72 * instead of SilcFSM context. This context is initialized with the 73 * silc_fsm_init function. It need not be uninitialized. 74 * 75 ***/ 76 typedef struct SilcFSMObject SilcFSMStruct; 77 78 /****s* silcutil/SilcFSMAPI/SilcFSMThread 79 * 80 * NAME 81 * 82 * typedef struct SilcFSMObject *SilcFSMThread; 83 * 84 * DESCRIPTION 85 * 86 * FSM thread context. The SILC FSM supports threads, virtual machine 87 * threads (inside FSM) and actual real system threads if platorm 88 * supports them. In a complex machine certain complex operations may 89 * be desired to execute in a thread. The SilcFSMThread is allocated 90 * by silc_fsm_thread_alloc and feed by silc_fsm_free. It is also 91 * possible to use pre-allocated thread by using SilcFSMThreadStruct 92 * instead of SilcFSMThread. 93 * 94 ***/ 95 typedef struct SilcFSMObject *SilcFSMThread; 96 97 /****s* silcutil/SilcFSMAPI/SilcFSMThreadStruct 98 * 99 * NAME 100 * 101 * typedef struct SilcFSMObject SilcFSMThreadStruct; 102 * 103 * DESCRIPTION 104 * 105 * FSM thread context and can be used as a pre-allocated FSM thread context, 106 * instead of SilcFSMThread context. This context is initialized with the 107 * silc_fsm_thread_init function. It need not be uninitialized. 108 * 109 ***/ 110 typedef struct SilcFSMObject SilcFSMThreadStruct; 111 112 /****d* silcutil/SilcFSMAPI/SILC_FSM_CONTINUE 113 * 114 * NAME 115 * 116 * #define SILC_FSM_CONTINUE ... 117 * 118 * DESCRIPTION 119 * 120 * Moves to next state synchronously. This type is used is returned 121 * from state functions to immediately move to next state. 122 * 123 * EXAMPLE 124 * 125 * SILC_FSM_STATE(silc_foo_state) 126 * { 127 * ... 128 * 129 * // Move to next state now 130 * silc_fsm_next(fsm, silc_foo_next_state); 131 * return SILC_FSM_CONTINUE; 132 * } 133 * 134 ***/ 135 #if defined(SILC_DEBUG) 136 #define SILC_FSM_CONTINUE \ 137 fsm->next_state(fsm, fsm->fsm_context, fsm->state_context); 138 #else 139 #define SILC_FSM_CONTINUE SILC_FSM_ST_CONTINUE; 140 #endif /* SILC_DEBUG */ 141 142 /****d* silcutil/SilcFSMAPI/SILC_FSM_YIELD 143 * 144 * NAME 145 * 146 * #define SILC_FSM_YIELD ... 147 * 148 * DESCRIPTION 149 * 150 * Moves to next state through the machine scheduler. Other threads 151 * running in the machine will get running time with SILC_FSM_YIELD. 152 * When using real threads, using SILC_FSM_YIELD is usually unnecessary. 153 * This type is returned in the state function. 154 * 155 ***/ 156 #define SILC_FSM_YIELD SILC_FSM_ST_YIELD; 157 158 /****d* silcutil/SilcFSMAPI/SILC_FSM_WAIT 159 * 160 * NAME 161 * 162 * #define SILC_FSM_WAIT ... 163 * 164 * DESCRIPTION 165 * 166 * Suspends the machine or thread until it is awaken. This is used 167 * when asynchronous call is made or timer is set, or something else 168 * that requires waiting. This type is returned in the state function. 169 * 170 ***/ 171 #define SILC_FSM_WAIT SILC_FSM_ST_WAIT 172 173 /****d* silcutil/SilcFSMAPI/SILC_FSM_FINISH 174 * 175 * NAME 176 * 177 * #define SILC_FSM_FINISH ... 178 * 179 * DESCRIPTION 180 * 181 * Finishes the machine or thread and calls its destructor, if defined. 182 * If the machine is finished when it has running threads the machine 183 * will fatally fail. User must always finish the threads before 184 * finishing the machine. This type is returned in the state function. 185 * 186 ***/ 187 #define SILC_FSM_FINISH SILC_FSM_ST_FINISH 188 189 /****f* silcutil/SilcFSMAPI/SilcFSMDestructor 190 * 191 * SYNOPSIS 192 * 193 * typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context, 194 * void *destructor_context); 195 * 196 * DESCRIPTION 197 * 198 * The destructor callback that was set in silc_fsm_alloc or in 199 * silc_fsm_init function. It will be called when a state function 200 * returns SILC_FSM_FINISH. This function will be called through 201 * the scheduler; it will not be called immediately after the state 202 * function returns SILC_FSM_FINISH, but will be called later. The 203 * `fsm' can be freed in this function. 204 * 205 ***/ 206 typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context, 207 void *destructor_context); 208 209 /****f* silcutil/SilcFSMAPI/SilcFSMThreadDestructor 210 * 211 * SYNOPSIS 212 * 213 * typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread, 214 * void *thread_context, 215 * void *destructor_context); 216 * 217 * DESCRIPTION 218 * 219 * The destructor callback that was set in silc_fsm_thread_alloc or in 220 * silc_fsm_thread_init function. It will be called when a state function 221 * returns SILC_FSM_FINISH. This function will be called through the 222 * scheduler; it will not be called immediately after the state function 223 * returns SILC_FSM_FINISH, but will be called later. The `thread' can 224 * be freed in this function. 225 * 226 * NOTES 227 * 228 * Even if the `thread' was executed in real system thread, this callback 229 * is always received in the main machine thread, not in the created 230 * thread. 231 * 232 ***/ 233 typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread, 234 void *thread_context, 235 void *destructor_context); 236 237 /****d* silcutil/SilcFSMAPI/SILC_FSM_STATE 238 * 239 * NAME 240 * 241 * #define SILC_FSM_STATE(name) 242 * 243 * DESCRIPTION 244 * 245 * This macro is used to declare an FSM state function. The `fsm' is 246 * the SilcFSM or SilcFSMThread context, the `fsm_context' is the context 247 * given as argument to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_init, 248 * or silc_fsm_thread_alloc function. The `state_context' is the optional 249 * state specific context set with silc_fsm_set_state_context function. 250 * 251 * SOURCE 252 */ 253 #define SILC_FSM_STATE(name) \ 254 int name(struct SilcFSMObject *fsm, void *fsm_context, void *state_context) 255 /***/ 256 257 /* State function callback */ 258 typedef int (*SilcFSMStateCallback)(struct SilcFSMObject *fsm, 259 void *fsm_context, 260 void *state_context); 261 262 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL 263 * 264 * NAME 265 * 266 * SILC_FSM_CALL(function) 267 * 268 * DESCRIPTION 269 * 270 * Macro used to call asynchronous calls from state function. If the 271 * call is not really asynchronous then this will cause the machine to 272 * directly proceed to next state. If the call is truly asynchronous 273 * then this will set the machine to wait state. The silc_fsm_next 274 * must be called before this macro, so that the next state is set. 275 * 276 * NOTES 277 * 278 * The state function returns in this macro. 279 * 280 * EXAMPLE 281 * 282 * // Simple example 283 * silc_fsm_next(fsm, some_next_state); 284 * SILC_FSM_CALL(silc_some_async_call(server, some_callback, context)); 285 * 286 * // More complex example 287 * silc_fsm_next(fsm, some_next_state); 288 * SILC_FSM_CALL((some_context->operation = 289 * silc_some_async_call(server, some_callback, context))); 290 * 291 ***/ 292 #define SILC_FSM_CALL(function) \ 293 do { \ 294 SILC_VERIFY(!silc_fsm_set_call(fsm, TRUE)); \ 295 function; \ 296 if (!silc_fsm_set_call(fsm, FALSE)) \ 297 return SILC_FSM_CONTINUE; \ 298 return SILC_FSM_WAIT; \ 299 } while(0) 300 301 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE 302 * 303 * NAME 304 * 305 * SILC_FSM_CALL_CONTINUE(fsm) 306 * 307 * DESCRIPTION 308 * 309 * Macro used to proceed after asynchornous call. This is called in the 310 * callback of the asynchronous call to continue in the state machine. 311 * 312 * EXAMPLE 313 * 314 * void some_callback(void *context) { 315 * SilcFSM fsm = context; 316 * ... 317 * // Continue to the next state 318 * SILC_FSM_CALL_CONTINUE(fsm); 319 * } 320 * 321 ***/ 322 #define SILC_FSM_CALL_CONTINUE(fsm) \ 323 do { \ 324 if (!silc_fsm_set_call(fsm, FALSE)) \ 325 silc_fsm_continue(fsm); \ 326 } while(0) 327 328 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE_SYNC 329 * 330 * NAME 331 * 332 * SILC_FSM_CALL_CONTINUE_SYNC(fsm) 333 * 334 * DESCRIPTION 335 * 336 * Macro used to proceed after asynchornous call. This is called in the 337 * callback of the asynchronous call to continue in the state machine. 338 * This continues to the next state synchronously, not through the 339 * scheduler. 340 * 341 * EXAMPLE 342 * 343 * void some_callback(void *context) { 344 * SilcFSM fsm = context; 345 * ... 346 * // Continue to the next state immediately 347 * SILC_FSM_CALL_CONTINUE_SYNC(fsm); 348 * } 349 * 350 ***/ 351 #define SILC_FSM_CALL_CONTINUE_SYNC(fsm) \ 352 do { \ 353 if (!silc_fsm_set_call(fsm, FALSE)) \ 354 silc_fsm_continue_sync(fsm); \ 355 } while(0) 356 357 /****d* silcutil/SilcFSMAPI/SILC_FSM_THREAD_WAIT 358 * 359 * NAME 360 * 361 * SILC_FSM_THREAD_WAIT(thread) 362 * 363 * DESCRIPTION 364 * 365 * Macro used to wait for the `thread' to terminate. The machine or 366 * thread will be suspended while it is waiting for the thread to 367 * terminate. The machine or thread will continue once the waited 368 * thread has terminated. 369 * 370 * NOTES 371 * 372 * The state function returns in this macro. 373 * 374 * This macro is the only way to safely make sure that the thread has 375 * terminated by the time FSM continues from the waiting state. Using 376 * FSM events to signal from the thread before SILC_FSM_FINISH is returned 377 * works with normal FSM threads, but especially with real system threads 378 * it does not guarantee that the FSM won't continue before the thread has 379 * actually terminated. Usually this is not a problem, but it can be a 380 * problem if the FSM is waiting to be freed. In this case using this 381 * macro is strongly recommended. 382 * 383 ***/ 384 #define SILC_FSM_THREAD_WAIT(thread) \ 385 do { \ 386 silc_fsm_thread_wait(fsm, thread); \ 387 return SILC_FSM_WAIT; \ 388 } while(0) 389 390 /****f* silcutil/SilcFSMAPI/silc_fsm_alloc 391 * 392 * SYNOPSIS 393 * 394 * SilcFSM silc_fsm_alloc(void *fsm_context, 395 * SilcFSMDestructor destructor, 396 * void *destructor_context, 397 * SilcSchedule schedule); 398 * 399 * DESCRIPTION 400 * 401 * Allocates SILC Finite State Machine context. The `destructor' with 402 * `destructor_context' will be called when the machines finishes. The 403 * caller must free the returned context with silc_fsm_free. The 404 * `fsm_context' is delivered to every FSM state function. The `schedule' 405 * is the caller's scheduler and the FSM will be run in the scheduler. 406 * 407 * EXAMPLE 408 * 409 * SilcAsyncOperation silc_async_call(Callback callback, void *cb_context) 410 * { 411 * SilcAsyncOperation op; 412 * SilcFSM fsm; 413 * ... 414 * 415 * // Allocate async operation so that caller can control us, like abort 416 * op = silc_async_alloc(silc_async_call_abort, NULL, ourcontext); 417 * 418 * // Start FSM 419 * fsm = silc_fsm_alloc(ourcontext, fsm_destructor, ourcontext, 420 * schedule); 421 * silc_fsm_start(fsm, first_state); 422 * ... 423 * 424 * // Return async operation for upper layer 425 * return op; 426 * } 427 * 428 ***/ 429 SilcFSM silc_fsm_alloc(void *fsm_context, 430 SilcFSMDestructor destructor, 431 void *destructor_context, 432 SilcSchedule schedule); 433 434 /****f* silcutil/SilcFSMAPI/silc_fsm_init 435 * 436 * SYNOPSIS 437 * 438 * SilcBool silc_fsm_init(SilcFSM fsm, 439 * void *fsm_context, 440 * SilcFSMDestructor destructor, 441 * void *destructor_context, 442 * SilcSchedule schedule); 443 * 444 * DESCRIPTION 445 * 446 * Initializes a pre-allocated SilcFSM context. This call is equivalent 447 * to silc_fsm_alloc except that this takes the pre-allocated context 448 * as argument. The silc_fsm_free must not be called if this was called. 449 * Returns TRUE if the initialization is Ok or FALSE if error occurred. 450 * This function does not allocate any memory. The `schedule' is the 451 * caller's scheduler and the FSM will be run in the scheduler. 452 * 453 * EXAMPLE 454 * 455 * SilcFSMStruct fsm; 456 * 457 * silc_fsm_init(&fsm, application, fsm_destructor, application, schedule); 458 * silc_fsm_start(&fsm, first_state); 459 * 460 ***/ 461 SilcBool silc_fsm_init(SilcFSM fsm, 462 void *fsm_context, 463 SilcFSMDestructor destructor, 464 void *destructor_context, 465 SilcSchedule schedule); 466 467 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_alloc 468 * 469 * SYNOPSIS 470 * 471 * SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm, 472 * void *thread_context, 473 * SilcFSMThreadDestructor destructor, 474 * void *destructor_context, 475 * SilcBool real_thread); 476 * 477 * DESCRIPTION 478 * 479 * Allocates FSM thread context. The thread will be executed in the 480 * FSM machine indicated by `fsm'. The caller must free the returned 481 * thread context with silc_fsm_free. If the 'real_thread' is TRUE 482 * then the thread will actually be executed in real thread, if platform 483 * supports them. The `thread_context' is delivered to every state 484 * function in the thread. 485 * 486 * NOTES 487 * 488 * If the system does not support threads, then this function will revert 489 * back to normal FSM threads. 490 * 491 * If the `real_thread' is TRUE then FSM will allocate new SilcSchedule 492 * for the FSM thread. If you need scheduler in the real thread it is 493 * strongly recommended that you use the SilcSchedule that is allocated 494 * for the thread. You can retrieve the SilcSchedule from the thread 495 * using silc_fsm_get_schedule function. Note that, the allocated 496 * SilcSchedule will become invalid after the thread finishes. 497 * 498 * If `real_thread' is FALSE the silc_fsm_get_schedule will return 499 * the SilcSchedule that was originally given to silc_fsm_alloc or 500 * silc_fsm_init. 501 * 502 * EXAMPLE 503 * 504 * SILC_FSM_STATE(silc_foo_state) 505 * { 506 * SilcFSMThread thread; 507 * ... 508 * 509 * // Execute the route lookup in thread 510 * thread = silc_fsm_thread_alloc(fsm, fsm_context, NULL, NULL, FALSE); 511 * silc_fsm_start(thread, silc_route_lookup_start); 512 * 513 * // Wait here for the thread to terminate. Set the state where to go 514 * // after the thread has terminated. 515 * silc_fsm_next(fsm, silc_foo_route_lookup_finished); 516 * SILC_FSM_THREAD_WAIT(thread); 517 * } 518 * 519 ***/ 520 SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm, 521 void *thread_context, 522 SilcFSMThreadDestructor destructor, 523 void *destructor_context, 524 SilcBool real_thread); 525 526 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_init 527 * 528 * SYNOPSIS 529 * 530 * void silc_fsm_thread_init(SilcFSMThread thread, 531 * SilcFSM fsm, 532 * void *thread_context, 533 * SilcFSMThreadDestructor destructor, 534 * void *destructor_context, 535 * SilcBool real_thread); 536 * 537 * DESCRIPTION 538 * 539 * Initializes a pre-allocated SilcFSMThread context. This call is 540 * equivalent to silc_fsm_thread_alloc except that this takes the 541 * pre-allocated context as argument. The silc_fsm_free must not be 542 * called if this was called. If the `real_thread' is TRUE then the 543 * thread will actually be executed in real thread, if platform supports 544 * them. 545 * 546 * NOTES 547 * 548 * See the notes from the silc_fsm_thread_alloc. 549 * 550 * EXAMPLE 551 * 552 * SilcFSMThreadStruct thread; 553 * 554 * silc_fsm_thread_init(&thread, fsm, application, NULL, NULL, FALSE); 555 * silc_fsm_start(&thread, first_state); 556 * 557 ***/ 558 void silc_fsm_thread_init(SilcFSMThread thread, 559 SilcFSM fsm, 560 void *thread_context, 561 SilcFSMThreadDestructor destructor, 562 void *destructor_context, 563 SilcBool real_thread); 564 565 /****f* silcutil/SilcFSMAPI/silc_fsm_free 566 * 567 * SYNOPSIS 568 * 569 * void silc_fsm_free(void *fsm); 570 * 571 * DESCRIPTION 572 * 573 * Free the SILC FSM context that was allocated with silc_fsm_alloc, 574 * or free the SILC FSM thread context that was allocated with 575 * silc_fsm_thread_alloc. This function is used with both SilcFSM 576 * and SilcFSMThread contexts. 577 * 578 * NOTES 579 * 580 * When freeing FSM, it must not have any active threads. 581 * 582 ***/ 583 void silc_fsm_free(void *fsm); 584 585 /****f* silcutil/SilcFSMAPI/silc_fsm_start 586 * 587 * SYNOPSIS 588 * 589 * void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state); 590 * 591 * DESCRIPTION 592 * 593 * This function must be called after the SILC FSM context was created. 594 * This actually starts the state machine. Note that, the machine is 595 * started later after this function returns. The `start_state' is the 596 * state where the machine or thread is started. This function is used 597 * with both SilcFSM and SilcFSMThread contexts. 598 * 599 * EXAMPLE 600 * 601 * SilcFSM fsm; 602 * 603 * fsm = silc_fsm_alloc(context, destructor, context, schedule); 604 * silc_fsm_start(fsm, first_state); 605 * 606 ***/ 607 void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state); 608 609 /****f* silcutil/SilcFSMAPI/silc_fsm_start_sync 610 * 611 * SYNOPSIS 612 * 613 * void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state); 614 * 615 * DESCRIPTION 616 * 617 * This function is same as silc_fsm_start, except that the FSM will 618 * be started immediately inside this function. After this function 619 * returns the `start_state' has already been executed. If the machine 620 * is completely synchronous (no waiting used in the machine) then 621 * the machine will have finished once this function returns. Also 622 * note that if the machine is completely synchronous the destructor 623 * will also be called from inside this function. This function is used 624 * with both SilcFSM and SilcFSMThread contexts. 625 * 626 ***/ 627 void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state); 628 629 /****f* silcutil/SilcFSMAPI/silc_fsm_next 630 * 631 * SYNOPSIS 632 * 633 * void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state); 634 * 635 * DESCRIPTION 636 * 637 * Set the next state to be executed. If the state function that 638 * call this function returns SILC_FSM_CONTINUE, the `next_state' 639 * will be executed immediately. If it returns SILC_FSM_YIELD it 640 * yields the thread and the `next_state' will be run after other 641 * threads have run first. This function must always be used to set 642 * the next state in the machine or thread. This function is used 643 * with both SilcFSM and SilcFSMThread contexts. 644 * 645 * EXAMPLE 646 * 647 * // Move to next state 648 * silc_fsm_next(fsm, next_state); 649 * return SILC_FSM_CONTINUE; 650 * 651 ***/ 652 void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state); 653 654 /****f* silcutil/SilcFSMAPI/silc_fsm_next_later 655 * 656 * SYNOPSIS 657 * 658 * void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state, 659 * SilcUInt32 seconds, SilcUInt32 useconds); 660 * 661 * DESCRIPTION 662 * 663 * Set the next state to be executed later, at the specified time. 664 * The SILC_FSM_WAIT must be returned in the state function if this 665 * function is called. If any other state is returned machine operation 666 * is undefined. The machine or thread will move to `next_state' after 667 * the specified timeout. This function is used with both SilcFSM and 668 * SilcFSMThread contexts. 669 * 670 * NOTES 671 * 672 * If both `seconds' and `useconds' are 0, the effect is same as calling 673 * silc_fsm_next function, and SILC_FSM_CONTINUE must be returned. 674 * 675 * If silc_fsm_continue or silc_fsm_continue_sync is called while the 676 * machine or thread is in SILC_FSM_WAIT state the timeout is automatically 677 * canceled and the state moves to the next state. 678 * 679 * EXAMPLE 680 * 681 * // Move to next state after 10 seconds 682 * silc_fsm_next_later(fsm, next_state, 10, 0); 683 * return SILC_FSM_WAIT; 684 * 685 ***/ 686 void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state, 687 SilcUInt32 seconds, SilcUInt32 useconds); 688 689 /****f* silcutil/SilcFSMAPI/silc_fsm_continue 690 * 691 * SYNOPSIS 692 * 693 * void silc_fsm_continue(void *fsm); 694 * 695 * DESCRIPTION 696 * 697 * Continues in the state machine from a SILC_FSM_WAIT state. This can 698 * be called from outside waiting FSM to continue to the next state. 699 * This function can be used instead of SILC_FSM_CALL_CONTINUE macro 700 * in case the SILC_FSM_CALL was not used. This must not be used if 701 * SILC_FSM_CALL was used. This function is used with both SilcFSM and 702 * SilcFSMThread contexts. 703 * 704 ***/ 705 void silc_fsm_continue(void *fsm); 706 707 /****f* silcutil/SilcFSMAPI/silc_fsm_continue_sync 708 * 709 * SYNOPSIS 710 * 711 * void silc_fsm_continue_sync(void *fsm); 712 * 713 * DESCRIPTION 714 * 715 * Continues immediately in the state machine from a SILC_FSM_WAIT state. 716 * This can be called from outside waiting FSM to immediately continue to 717 * the next state. This function can be used instead of the 718 * SILC_FSM_CALL_CONTINUE_SYNC macro in case the SILC_FSM_CALL was not used. 719 * This must not be used if SILC_FSM_CALL was used. This function is used 720 * with both SilcFSM and SilcFSMThread contexts. 721 * 722 ***/ 723 void silc_fsm_continue_sync(void *fsm); 724 725 /****f* silcutil/SilcFSMAPI/silc_fsm_finish 726 * 727 * SYNOPSIS 728 * 729 * void silc_fsm_finish(void *fsm); 730 * 731 * DESCRIPTION 732 * 733 * Finishes the `fsm'. This function may be used in case the FSM 734 * needs to be finished outside FSM states. Usually FSM is finished 735 * by returning SILC_FSM_FINISH from the state, but if this is not 736 * possible this function may be called. This function is used with 737 * both SilcFSM and SilcFSMThread contexts. 738 * 739 * If the `fsm' is a machine and it has running threads, the machine 740 * will fatally fail. The caller must first finish the threads and 741 * then the machine. 742 * 743 ***/ 744 void silc_fsm_finish(void *fsm); 745 746 /****f* silcutil/SilcFSMAPI/silc_fsm_set_context 747 * 748 * SYNOPSIS 749 * 750 * void silc_fsm_set_context(void *fsm, void *fsm_context); 751 * 752 * DESCRIPTION 753 * 754 * Set new context for the `fsm'. This function can be used to change 755 * the context inside the `fsm', if needed. This function is used with 756 * both SilcFSM and SilcFSMThread contexts. The context is the 757 * `fsm_context' in the state function (SILC_FSM_STATE). 758 * 759 ***/ 760 void silc_fsm_set_context(void *fsm, void *fsm_context); 761 762 /****f* silcutil/SilcFSMAPI/silc_fsm_get_context 763 * 764 * SYNOPSIS 765 * 766 * void *silc_fsm_get_context(void *fsm); 767 * 768 * DESCRIPTION 769 * 770 * Returns the context associated with the `fsm'. It is the context that 771 * was given to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_alloc or 772 * silc_fsm_thread_init. This function is used with both SilcFSM and 773 * SilcFSMThread contexts. 774 * 775 ***/ 776 void *silc_fsm_get_context(void *fsm); 777 778 /****f* silcutil/SilcFSMAPI/silc_fsm_set_state_context 779 * 780 * SYNOPSIS 781 * 782 * void silc_fsm_set_state_context(void *fsm, void *state_context); 783 * 784 * DESCRIPTION 785 * 786 * Set's a state specific context for the `fsm'. This function can be 787 * used to change the state context inside the `fsm', if needed. This 788 * function is used with both SilcFSM and SilcFSMThread contexts. The 789 * context is the `state_context' in the state function (SILC_FSM_STATE). 790 * 791 ***/ 792 void silc_fsm_set_state_context(void *fsm, void *state_context); 793 794 /****f* silcutil/SilcFSMAPI/silc_fsm_get_state_context 795 * 796 * SYNOPSIS 797 * 798 * void *silc_fsm_get_state_context(void *fsm); 799 * 800 * DESCRIPTION 801 * 802 * Returns the state context associated with the `fsm'. It is the context 803 * that was set with silc_fsm_set_state_context function. This function 804 * is used with both SilcFSM and SilcFSMThread contexts. 805 * 806 ***/ 807 void *silc_fsm_get_state_context(void *fsm); 808 809 /****f* silcutil/SilcFSMAPI/silc_fsm_get_schedule 810 * 811 * SYNOPSIS 812 * 813 * SilcSchedule silc_fsm_get_schedule(void *fsm); 814 * 815 * DESCRIPTION 816 * 817 * Returns the SilcSchedule that has been associated with the `fsm'. 818 * If caller needs scheduler it may retrieve it with this function. This 819 * function is used with both SilcFSM and SilcFSMThread contexts. 820 * 821 * If the `fsm' is thread and real system threads are being used, and this 822 * is called from the thread, it will return the SilcSchedule that was 823 * allocated by the FSM for the thread. It is strongly recommended to 824 * use this SilcSchedule if you are using real threads, and you need 825 * scheduler in the thread. Note that, once the thread finishes the 826 * returned SilcSchedule becomes invalid. 827 * 828 * In other times this returns the SilcSchedule pointer that was given 829 * to silc_fsm_alloc or silc_fsm_init. 830 * 831 ***/ 832 SilcSchedule silc_fsm_get_schedule(void *fsm); 833 834 /****f* silcutil/SilcFSMAPI/silc_fsm_get_machine 835 * 836 * SYNOPSIS 837 * 838 * SilcFSM silc_fsm_get_machine(SilcFSMThread thread); 839 * 840 * DESCRIPTION 841 * 842 * Returns the machine from the FSM thread indicated by `thread'. 843 * 844 ***/ 845 SilcFSM silc_fsm_get_machine(SilcFSMThread thread); 846 847 /****f* silcutil/SilcFSMAPI/silc_fsm_is_started 848 * 849 * SYNOPSIS 850 * 851 * SilcBool silc_fsm_is_started(void *fsm); 852 * 853 * DESCRIPTION 854 * 855 * Returns TRUE if the machine or thread `fsm' has been started and has 856 * not been finished yet. This function is used with both SilcFSM and 857 * SilcFSMThread contexts. 858 * 859 ***/ 860 SilcBool silc_fsm_is_started(void *fsm); 861 862 /* FSM Events */ 863 864 /****s* silcutil/SilcFSMAPI/SilcFSMEvent 865 * 866 * NAME 867 * 868 * typedef struct SilcFSMEventObject *SilcFSMEvent; 869 * 870 * DESCRIPTION 871 * 872 * The FSM event context allocated with silc_fsm_event_alloc. The 873 * caller must free it with silc_fsm_event_free. It is also possible 874 * to use pre-allocated SilcFSMEventStruct instead of SilcFSMEvent context. 875 * 876 ***/ 877 typedef struct SilcFSMEventObject *SilcFSMEvent; 878 879 /****s* silcutil/SilcFSMAPI/SilcFSMEventStruct 880 * 881 * NAME 882 * 883 * typedef struct SilcFSMEventObject SilcFSMEventStruct; 884 * 885 * DESCRIPTION 886 * 887 * The FSM event context that can be used as pre-allocated context. 888 * It is initialized with silc_fsm_event_init. It need not be 889 * uninitialized. 890 * 891 ***/ 892 typedef struct SilcFSMEventObject SilcFSMEventStruct; 893 894 /****f* silcutil/SilcFSMAPI/silc_fsm_event_alloc 895 * 896 * SYNOPSIS 897 * 898 * SilcFSMEvent silc_fsm_event_alloc(SilcFSM fsm); 899 * 900 * DESCRIPTION 901 * 902 * Allocates asynchronous FSM event. FSM events are asynchronous events 903 * that can be waited and signalled. They can be used as condition 904 * variables and signallers. They can be used for example to wait that 905 * some event happens, some thread moves to a specific state or similar. 906 * The FSM Events may also be used in FSM threads that are executed in 907 * real system threads. It is safe to wait and signal the event from 908 * threads. 909 * 910 * Use the macros SILC_FSM_EVENT_WAIT and SILC_FSM_EVENT_TIMEDWAIT to wait 911 * for the event. Use the SILC_FSM_EVENT_SIGNAL macro to signal all the 912 * waiters. 913 * 914 ***/ 915 SilcFSMEvent silc_fsm_event_alloc(SilcFSM fsm); 916 917 /****f* silcutil/SilcFSMAPI/silc_fsm_event_init 918 * 919 * SYNOPSIS 920 * 921 * void silc_fsm_event_init(SilcFSMEvent event, SilcFSM fsm); 922 * 923 * DESCRIPTION 924 * 925 * Initializes a pre-allocates FSM event context. This call is 926 * equivalent to silc_fsm_event_alloc except this use the pre-allocated 927 * context. This fuction does not allocate any memory. 928 * 929 ***/ 930 void silc_fsm_event_init(SilcFSMEvent event, SilcFSM fsm); 931 932 /****f* silcutil/SilcFSMAPI/silc_fsm_event_free 933 * 934 * SYNOPSIS 935 * 936 * void silc_fsm_event_free(SilcFSMEvent event); 937 * 938 * DESCRIPTION 939 * 940 * Free the event allocated by silc_fsm_event_alloc function. 941 * 942 ***/ 943 void silc_fsm_event_free(SilcFSMEvent event); 944 945 /****d* silcutil/SilcFSMAPI/SILC_FSM_EVENT_WAIT 946 * 947 * NAME 948 * 949 * SILC_FSM_EVENT_WAIT(event) 950 * 951 * DESCRIPTION 952 * 953 * Macro used to wait for the `event' to be signalled. The machine 954 * or thread will be suspended while it is waiting for the event. 955 * This macro can only be used in FSM state functions. When the 956 * event is signalled the FSM will re-enter the current state (or 957 * state that was set with silc_fsm_next before waiting). 958 * 959 * EXAMPLE 960 * 961 * // Signalling example 962 * ctx->async_event = silc_fsm_event_alloc(fsm); 963 * ... 964 * 965 * SILC_FSM_STATE(silc_foo_state) 966 * { 967 * ... 968 * 969 * // Wait here for async call to complete 970 * SILC_FSM_EVENT_WAIT(ctx->async_event); 971 * 972 * // Async call completed 973 * if (ctx->async_success == FALSE) 974 * fatal(error); 975 * ... 976 * } 977 * 978 ***/ 979 #define SILC_FSM_EVENT_WAIT(event) \ 980 do { \ 981 if (silc_fsm_event_wait(event, fsm) == 0) \ 982 return SILC_FSM_WAIT; \ 983 } while(0) 984 985 /****d* silcutil/SilcFSMAPI/SILC_FSM_EVENT_TIMEDWAIT 986 * 987 * NAME 988 * 989 * SILC_FSM_EVENT_TIMEDWAIT(event, seconds, useconds, timedout) 990 * 991 * DESCRIPTION 992 * 993 * Macro used to wait for the `event' to be signalled, or until 994 * the timeout specified by `seconds' and `useconds' has elapsed. If 995 * the timeout occurs before the event is signalled, the machine 996 * will wakeup. The `timedout' is SilcBool pointer and if it is 997 * non-NULL indication of whether timeout occurred or not is saved to 998 * the pointer. This macro can only be used in FSM state functions. 999 * When the event is signalled or timedout the FSM will re-enter 1000 * the current state (or state that was set with silc_fsm_next before 1001 * waiting). 1002 * 1003 * EXAMPLE 1004 * 1005 * SILC_FSM_STATE(silc_foo_state) 1006 * { 1007 * SilcBool timedout; 1008 * ... 1009 * 1010 * // Wait here for async call to complete, or 10 seconds for timeout 1011 * SILC_FSM_EVENT_TIMEDWAIT(ctx->async_event, 10, 0, &timedout); 1012 * 1013 * // See if timeout occurred 1014 * if (timedout == TRUE) 1015 * fatal(error); 1016 * 1017 * // Async call completed 1018 * if (ctx->async_success == FALSE) 1019 * fatal(error); 1020 * ... 1021 * } 1022 * 1023 ***/ 1024 #define SILC_FSM_EVENT_TIMEDWAIT(event, seconds, useconds, ret_to) \ 1025 do { \ 1026 if (silc_fsm_event_timedwait(event, fsm, seconds, useconds, ret_to) == 0) \ 1027 return SILC_FSM_WAIT; \ 1028 } while(0) 1029 1030 /****f* silcutil/SilcFSMAPI/SILC_FSM_EVENT_SIGNAL 1031 * 1032 * SYNOPSIS 1033 * 1034 * SILC_FSM_EVENT_SIGNAL(event) 1035 * 1036 * DESCRIPTION 1037 * 1038 * Signals the `event' and awakens everybody that are waiting for this 1039 * event. This macro never blocks. It can be safely called at any place 1040 * in state function and in asynchronous callbacks or other functions. 1041 * 1042 * EXAMPLE 1043 * 1044 * SILC_FSM_STATE(silc_foo_async_completion) 1045 * { 1046 * ... 1047 * 1048 * // Notify all waiters 1049 * ctx->async_success = TRUE; 1050 * SILC_FSM_EVENT_SIGNAL(ctx->async_event); 1051 * ... 1052 * } 1053 * 1054 ***/ 1055 #define SILC_FSM_EVENT_SIGNAL(event) \ 1056 do { \ 1057 silc_fsm_event_signal(event); \ 1058 } while(0) 1059 1060 #include "silcfsm_i.h" 1061 1062 #endif /* SILCFSM_H */ 1063