1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<link rel="stylesheet" href="style.css" type="text/css">
<meta content="text/html; charset=iso-8859-1" http-equiv="Content-Type">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="Start" href="index.html">
<link rel="previous" href="Equeue_intro.html">
<link rel="next" href="Netcamlbox.html">
<link rel="Up" href="index.html">
<link title="Index of types" rel=Appendix href="index_types.html">
<link title="Index of extensions" rel=Appendix href="index_extensions.html">
<link title="Index of exceptions" rel=Appendix href="index_exceptions.html">
<link title="Index of values" rel=Appendix href="index_values.html">
<link title="Index of class attributes" rel=Appendix href="index_attributes.html">
<link title="Index of class methods" rel=Appendix href="index_methods.html">
<link title="Index of classes" rel=Appendix href="index_classes.html">
<link title="Index of class types" rel=Appendix href="index_class_types.html">
<link title="Index of modules" rel=Appendix href="index_modules.html">
<link title="Index of module types" rel=Appendix href="index_module_types.html">
<link title="Uq_gtk" rel="Chapter" href="Uq_gtk.html">
<link title="Uq_tcl" rel="Chapter" href="Uq_tcl.html">
<link title="Equeue" rel="Chapter" href="Equeue.html">
<link title="Unixqueue" rel="Chapter" href="Unixqueue.html">
<link title="Unixqueue_pollset" rel="Chapter" href="Unixqueue_pollset.html">
<link title="Unixqueue_select" rel="Chapter" href="Unixqueue_select.html">
<link title="Uq_resolver" rel="Chapter" href="Uq_resolver.html">
<link title="Uq_engines" rel="Chapter" href="Uq_engines.html">
<link title="Uq_multiplex" rel="Chapter" href="Uq_multiplex.html">
<link title="Uq_transfer" rel="Chapter" href="Uq_transfer.html">
<link title="Uq_socks5" rel="Chapter" href="Uq_socks5.html">
<link title="Uq_io" rel="Chapter" href="Uq_io.html">
<link title="Uq_lwt" rel="Chapter" href="Uq_lwt.html">
<link title="Uq_libevent" rel="Chapter" href="Uq_libevent.html">
<link title="Uq_mt" rel="Chapter" href="Uq_mt.html">
<link title="Uq_client" rel="Chapter" href="Uq_client.html">
<link title="Uq_server" rel="Chapter" href="Uq_server.html">
<link title="Uq_datagram" rel="Chapter" href="Uq_datagram.html">
<link title="Uq_engines_compat" rel="Chapter" href="Uq_engines_compat.html">
<link title="Equeue_intro" rel="Chapter" href="Equeue_intro.html">
<link title="Equeue_howto" rel="Chapter" href="Equeue_howto.html">
<link title="Netcamlbox" rel="Chapter" href="Netcamlbox.html">
<link title="Netcgi_apache" rel="Chapter" href="Netcgi_apache.html">
<link title="Netcgi_modtpl" rel="Chapter" href="Netcgi_modtpl.html">
<link title="Netcgi_plex" rel="Chapter" href="Netcgi_plex.html">
<link title="Netcgi_common" rel="Chapter" href="Netcgi_common.html">
<link title="Netcgi" rel="Chapter" href="Netcgi.html">
<link title="Netcgi_ajp" rel="Chapter" href="Netcgi_ajp.html">
<link title="Netcgi_scgi" rel="Chapter" href="Netcgi_scgi.html">
<link title="Netcgi_cgi" rel="Chapter" href="Netcgi_cgi.html">
<link title="Netcgi_fcgi" rel="Chapter" href="Netcgi_fcgi.html">
<link title="Netcgi_dbi" rel="Chapter" href="Netcgi_dbi.html">
<link title="Netcgi1_compat" rel="Chapter" href="Netcgi1_compat.html">
<link title="Netcgi_test" rel="Chapter" href="Netcgi_test.html">
<link title="Netcgi_porting" rel="Chapter" href="Netcgi_porting.html">
<link title="Nethttp_client_conncache" rel="Chapter" href="Nethttp_client_conncache.html">
<link title="Nethttp_client" rel="Chapter" href="Nethttp_client.html">
<link title="Nettelnet_client" rel="Chapter" href="Nettelnet_client.html">
<link title="Netftp_data_endpoint" rel="Chapter" href="Netftp_data_endpoint.html">
<link title="Netftp_client" rel="Chapter" href="Netftp_client.html">
<link title="Nethttp_fs" rel="Chapter" href="Nethttp_fs.html">
<link title="Netftp_fs" rel="Chapter" href="Netftp_fs.html">
<link title="Netsmtp" rel="Chapter" href="Netsmtp.html">
<link title="Netpop" rel="Chapter" href="Netpop.html">
<link title="Netldap" rel="Chapter" href="Netldap.html">
<link title="Netclient_tut" rel="Chapter" href="Netclient_tut.html">
<link title="Netgss_bindings" rel="Chapter" href="Netgss_bindings.html">
<link title="Netgss" rel="Chapter" href="Netgss.html">
<link title="Nethttpd_types" rel="Chapter" href="Nethttpd_types.html">
<link title="Nethttpd_kernel" rel="Chapter" href="Nethttpd_kernel.html">
<link title="Nethttpd_reactor" rel="Chapter" href="Nethttpd_reactor.html">
<link title="Nethttpd_engine" rel="Chapter" href="Nethttpd_engine.html">
<link title="Nethttpd_services" rel="Chapter" href="Nethttpd_services.html">
<link title="Nethttpd_plex" rel="Chapter" href="Nethttpd_plex.html">
<link title="Nethttpd_util" rel="Chapter" href="Nethttpd_util.html">
<link title="Nethttpd_intro" rel="Chapter" href="Nethttpd_intro.html">
<link title="Netmcore" rel="Chapter" href="Netmcore.html">
<link title="Netmcore_camlbox" rel="Chapter" href="Netmcore_camlbox.html">
<link title="Netmcore_mempool" rel="Chapter" href="Netmcore_mempool.html">
<link title="Netmcore_heap" rel="Chapter" href="Netmcore_heap.html">
<link title="Netmcore_ref" rel="Chapter" href="Netmcore_ref.html">
<link title="Netmcore_array" rel="Chapter" href="Netmcore_array.html">
<link title="Netmcore_sem" rel="Chapter" href="Netmcore_sem.html">
<link title="Netmcore_mutex" rel="Chapter" href="Netmcore_mutex.html">
<link title="Netmcore_condition" rel="Chapter" href="Netmcore_condition.html">
<link title="Netmcore_queue" rel="Chapter" href="Netmcore_queue.html">
<link title="Netmcore_buffer" rel="Chapter" href="Netmcore_buffer.html">
<link title="Netmcore_matrix" rel="Chapter" href="Netmcore_matrix.html">
<link title="Netmcore_hashtbl" rel="Chapter" href="Netmcore_hashtbl.html">
<link title="Netmcore_process" rel="Chapter" href="Netmcore_process.html">
<link title="Netmcore_tut" rel="Chapter" href="Netmcore_tut.html">
<link title="Netmcore_basics" rel="Chapter" href="Netmcore_basics.html">
<link title="Netplex_types" rel="Chapter" href="Netplex_types.html">
<link title="Netplex_mp" rel="Chapter" href="Netplex_mp.html">
<link title="Netplex_mt" rel="Chapter" href="Netplex_mt.html">
<link title="Netplex_log" rel="Chapter" href="Netplex_log.html">
<link title="Netplex_controller" rel="Chapter" href="Netplex_controller.html">
<link title="Netplex_container" rel="Chapter" href="Netplex_container.html">
<link title="Netplex_sockserv" rel="Chapter" href="Netplex_sockserv.html">
<link title="Netplex_workload" rel="Chapter" href="Netplex_workload.html">
<link title="Netplex_main" rel="Chapter" href="Netplex_main.html">
<link title="Netplex_config" rel="Chapter" href="Netplex_config.html">
<link title="Netplex_kit" rel="Chapter" href="Netplex_kit.html">
<link title="Rpc_netplex" rel="Chapter" href="Rpc_netplex.html">
<link title="Netplex_cenv" rel="Chapter" href="Netplex_cenv.html">
<link title="Netplex_semaphore" rel="Chapter" href="Netplex_semaphore.html">
<link title="Netplex_sharedvar" rel="Chapter" href="Netplex_sharedvar.html">
<link title="Netplex_mutex" rel="Chapter" href="Netplex_mutex.html">
<link title="Netplex_encap" rel="Chapter" href="Netplex_encap.html">
<link title="Netplex_mbox" rel="Chapter" href="Netplex_mbox.html">
<link title="Netplex_internal" rel="Chapter" href="Netplex_internal.html">
<link title="Netplex_intro" rel="Chapter" href="Netplex_intro.html">
<link title="Netplex_advanced" rel="Chapter" href="Netplex_advanced.html">
<link title="Netplex_admin" rel="Chapter" href="Netplex_admin.html">
<link title="Netshm" rel="Chapter" href="Netshm.html">
<link title="Netshm_data" rel="Chapter" href="Netshm_data.html">
<link title="Netshm_hashtbl" rel="Chapter" href="Netshm_hashtbl.html">
<link title="Netshm_array" rel="Chapter" href="Netshm_array.html">
<link title="Netshm_intro" rel="Chapter" href="Netshm_intro.html">
<link title="Netstring_pcre" rel="Chapter" href="Netstring_pcre.html">
<link title="Netconversion" rel="Chapter" href="Netconversion.html">
<link title="Netchannels" rel="Chapter" href="Netchannels.html">
<link title="Netstream" rel="Chapter" href="Netstream.html">
<link title="Netmime_string" rel="Chapter" href="Netmime_string.html">
<link title="Netmime" rel="Chapter" href="Netmime.html">
<link title="Netsendmail" rel="Chapter" href="Netsendmail.html">
<link title="Neturl" rel="Chapter" href="Neturl.html">
<link title="Netaddress" rel="Chapter" href="Netaddress.html">
<link title="Netbuffer" rel="Chapter" href="Netbuffer.html">
<link title="Netmime_header" rel="Chapter" href="Netmime_header.html">
<link title="Netmime_channels" rel="Chapter" href="Netmime_channels.html">
<link title="Neturl_ldap" rel="Chapter" href="Neturl_ldap.html">
<link title="Netdate" rel="Chapter" href="Netdate.html">
<link title="Netencoding" rel="Chapter" href="Netencoding.html">
<link title="Netulex" rel="Chapter" href="Netulex.html">
<link title="Netaccel" rel="Chapter" href="Netaccel.html">
<link title="Netaccel_link" rel="Chapter" href="Netaccel_link.html">
<link title="Nethtml" rel="Chapter" href="Nethtml.html">
<link title="Netstring_str" rel="Chapter" href="Netstring_str.html">
<link title="Netmappings" rel="Chapter" href="Netmappings.html">
<link title="Netaux" rel="Chapter" href="Netaux.html">
<link title="Nethttp" rel="Chapter" href="Nethttp.html">
<link title="Netpagebuffer" rel="Chapter" href="Netpagebuffer.html">
<link title="Netfs" rel="Chapter" href="Netfs.html">
<link title="Netglob" rel="Chapter" href="Netglob.html">
<link title="Netauth" rel="Chapter" href="Netauth.html">
<link title="Netsockaddr" rel="Chapter" href="Netsockaddr.html">
<link title="Netnumber" rel="Chapter" href="Netnumber.html">
<link title="Netxdr_mstring" rel="Chapter" href="Netxdr_mstring.html">
<link title="Netxdr" rel="Chapter" href="Netxdr.html">
<link title="Netcompression" rel="Chapter" href="Netcompression.html">
<link title="Netunichar" rel="Chapter" href="Netunichar.html">
<link title="Netasn1" rel="Chapter" href="Netasn1.html">
<link title="Netasn1_encode" rel="Chapter" href="Netasn1_encode.html">
<link title="Netoid" rel="Chapter" href="Netoid.html">
<link title="Netstring_tstring" rel="Chapter" href="Netstring_tstring.html">
<link title="Netdn" rel="Chapter" href="Netdn.html">
<link title="Netx509" rel="Chapter" href="Netx509.html">
<link title="Netascii_armor" rel="Chapter" href="Netascii_armor.html">
<link title="Nettls_support" rel="Chapter" href="Nettls_support.html">
<link title="Netmech_scram" rel="Chapter" href="Netmech_scram.html">
<link title="Netmech_scram_gssapi" rel="Chapter" href="Netmech_scram_gssapi.html">
<link title="Netmech_scram_sasl" rel="Chapter" href="Netmech_scram_sasl.html">
<link title="Netmech_scram_http" rel="Chapter" href="Netmech_scram_http.html">
<link title="Netgssapi_support" rel="Chapter" href="Netgssapi_support.html">
<link title="Netgssapi_auth" rel="Chapter" href="Netgssapi_auth.html">
<link title="Netchannels_crypto" rel="Chapter" href="Netchannels_crypto.html">
<link title="Netx509_pubkey" rel="Chapter" href="Netx509_pubkey.html">
<link title="Netx509_pubkey_crypto" rel="Chapter" href="Netx509_pubkey_crypto.html">
<link title="Netsaslprep" rel="Chapter" href="Netsaslprep.html">
<link title="Netmech_plain_sasl" rel="Chapter" href="Netmech_plain_sasl.html">
<link title="Netmech_crammd5_sasl" rel="Chapter" href="Netmech_crammd5_sasl.html">
<link title="Netmech_digest_sasl" rel="Chapter" href="Netmech_digest_sasl.html">
<link title="Netmech_digest_http" rel="Chapter" href="Netmech_digest_http.html">
<link title="Netmech_krb5_sasl" rel="Chapter" href="Netmech_krb5_sasl.html">
<link title="Netmech_gs2_sasl" rel="Chapter" href="Netmech_gs2_sasl.html">
<link title="Netmech_spnego_http" rel="Chapter" href="Netmech_spnego_http.html">
<link title="Netchannels_tut" rel="Chapter" href="Netchannels_tut.html">
<link title="Netmime_tut" rel="Chapter" href="Netmime_tut.html">
<link title="Netsendmail_tut" rel="Chapter" href="Netsendmail_tut.html">
<link title="Netulex_tut" rel="Chapter" href="Netulex_tut.html">
<link title="Neturl_tut" rel="Chapter" href="Neturl_tut.html">
<link title="Netsys" rel="Chapter" href="Netsys.html">
<link title="Netsys_posix" rel="Chapter" href="Netsys_posix.html">
<link title="Netsys_pollset" rel="Chapter" href="Netsys_pollset.html">
<link title="Netlog" rel="Chapter" href="Netlog.html">
<link title="Netexn" rel="Chapter" href="Netexn.html">
<link title="Netsys_win32" rel="Chapter" href="Netsys_win32.html">
<link title="Netsys_pollset_posix" rel="Chapter" href="Netsys_pollset_posix.html">
<link title="Netsys_pollset_win32" rel="Chapter" href="Netsys_pollset_win32.html">
<link title="Netsys_pollset_generic" rel="Chapter" href="Netsys_pollset_generic.html">
<link title="Netsys_signal" rel="Chapter" href="Netsys_signal.html">
<link title="Netsys_oothr" rel="Chapter" href="Netsys_oothr.html">
<link title="Netsys_xdr" rel="Chapter" href="Netsys_xdr.html">
<link title="Netsys_rng" rel="Chapter" href="Netsys_rng.html">
<link title="Netsys_crypto_types" rel="Chapter" href="Netsys_crypto_types.html">
<link title="Netsys_types" rel="Chapter" href="Netsys_types.html">
<link title="Netsys_mem" rel="Chapter" href="Netsys_mem.html">
<link title="Netsys_tmp" rel="Chapter" href="Netsys_tmp.html">
<link title="Netsys_sem" rel="Chapter" href="Netsys_sem.html">
<link title="Netsys_pmanage" rel="Chapter" href="Netsys_pmanage.html">
<link title="Netsys_crypto" rel="Chapter" href="Netsys_crypto.html">
<link title="Netsys_tls" rel="Chapter" href="Netsys_tls.html">
<link title="Netsys_ciphers" rel="Chapter" href="Netsys_ciphers.html">
<link title="Netsys_digests" rel="Chapter" href="Netsys_digests.html">
<link title="Netsys_crypto_modes" rel="Chapter" href="Netsys_crypto_modes.html">
<link title="Netsys_gssapi" rel="Chapter" href="Netsys_gssapi.html">
<link title="Netsys_sasl_types" rel="Chapter" href="Netsys_sasl_types.html">
<link title="Netsys_sasl" rel="Chapter" href="Netsys_sasl.html">
<link title="Netsys_polypipe" rel="Chapter" href="Netsys_polypipe.html">
<link title="Netsys_polysocket" rel="Chapter" href="Netsys_polysocket.html">
<link title="Netsys_global" rel="Chapter" href="Netsys_global.html">
<link title="Nettls_gnutls_bindings" rel="Chapter" href="Nettls_gnutls_bindings.html">
<link title="Nettls_nettle_bindings" rel="Chapter" href="Nettls_nettle_bindings.html">
<link title="Nettls_gnutls" rel="Chapter" href="Nettls_gnutls.html">
<link title="Netunidata" rel="Chapter" href="Netunidata.html">
<link title="Netgzip" rel="Chapter" href="Netgzip.html">
<link title="Rpc_auth_local" rel="Chapter" href="Rpc_auth_local.html">
<link title="Rpc_xti_client" rel="Chapter" href="Rpc_xti_client.html">
<link title="Rpc" rel="Chapter" href="Rpc.html">
<link title="Rpc_program" rel="Chapter" href="Rpc_program.html">
<link title="Rpc_util" rel="Chapter" href="Rpc_util.html">
<link title="Rpc_portmapper_aux" rel="Chapter" href="Rpc_portmapper_aux.html">
<link title="Rpc_packer" rel="Chapter" href="Rpc_packer.html">
<link title="Rpc_transport" rel="Chapter" href="Rpc_transport.html">
<link title="Rpc_client" rel="Chapter" href="Rpc_client.html">
<link title="Rpc_simple_client" rel="Chapter" href="Rpc_simple_client.html">
<link title="Rpc_portmapper_clnt" rel="Chapter" href="Rpc_portmapper_clnt.html">
<link title="Rpc_portmapper" rel="Chapter" href="Rpc_portmapper.html">
<link title="Rpc_server" rel="Chapter" href="Rpc_server.html">
<link title="Rpc_auth_sys" rel="Chapter" href="Rpc_auth_sys.html">
<link title="Rpc_auth_gssapi" rel="Chapter" href="Rpc_auth_gssapi.html">
<link title="Rpc_proxy" rel="Chapter" href="Rpc_proxy.html">
<link title="Rpc_intro" rel="Chapter" href="Rpc_intro.html">
<link title="Rpc_mapping_ref" rel="Chapter" href="Rpc_mapping_ref.html">
<link title="Rpc_intro_gss" rel="Chapter" href="Rpc_intro_gss.html">
<link title="Shell_sys" rel="Chapter" href="Shell_sys.html">
<link title="Shell" rel="Chapter" href="Shell.html">
<link title="Shell_uq" rel="Chapter" href="Shell_uq.html">
<link title="Shell_fs" rel="Chapter" href="Shell_fs.html">
<link title="Shell_intro" rel="Chapter" href="Shell_intro.html">
<link title="Intro" rel="Chapter" href="Intro.html">
<link title="Platform" rel="Chapter" href="Platform.html">
<link title="Foreword" rel="Chapter" href="Foreword.html">
<link title="Ipv6" rel="Chapter" href="Ipv6.html">
<link title="Regexp" rel="Chapter" href="Regexp.html">
<link title="Tls" rel="Chapter" href="Tls.html">
<link title="Crypto" rel="Chapter" href="Crypto.html">
<link title="Authentication" rel="Chapter" href="Authentication.html">
<link title="Credentials" rel="Chapter" href="Credentials.html">
<link title="Gssapi" rel="Chapter" href="Gssapi.html">
<link title="Ocamlnet4" rel="Chapter" href="Ocamlnet4.html">
<link title="Get" rel="Chapter" href="Get.html"><title>Ocamlnet 4 Reference Manual : Equeue_howto</title>
</head>
<body>
<div class="navbar"><a class="pre" href="Equeue_intro.html" title="Equeue_intro">Previous</a>
<a class="up" href="index.html" title="Index">Up</a>
<a class="post" href="Netcamlbox.html" title="Netcamlbox">Next</a>
</div>
<h1>Equeue_howto</h1>
<div class="info-desc">
<h2 id="1_TheEqueueUnixqueueandEnginesHOWTO">The Equeue, Unixqueue, and Engines HOWTO</h2>
<p>This document tries to explain briefly what you can do with the
Equeue library. It is more superficial than <a href="Equeue_intro.html"><code class="code">Equeue_intro</code></a>, and
gives some recipes how to do things.</p>
<p>Contents:</p>
<ul>
<li><code class="code">Equeue_howto.about</code></li>
<li><code class="code">Equeue_howto.esys</code></li>
<li><code class="code">Equeue_howto.engines</code></li>
<li><code class="code">Equeue_howto.overview</code></li>
<li><code class="code">Equeue_howto.tricks</code></li>
<li><code class="code">Equeue_howto.lwt</code></li>
</ul>
<h3 id="about">What is this about?</h3>
<p>We talk here about a form of concurrent programming, sometimes called
lightweight threading or cooperative threading. As for all concurrency
mechanisms, the ultimate goal is to do several things in
parallel. This type, however, focuses especially on I/O, because the
points where the execution threads can be switched are usually the
points where a thread needs to stop for waiting on an I/O resource.</p>
<p>There is no preemption: When normal OCaml code is executed, there is
no possibility to switch the thread. First when the control is
returned to the Equeue library, a different thread can be selected for
execution.</p>
<p>There is also no parallelism: All execution happens in the context
of the process or kernel thread running the code. It is only possible
to use one CPU core.</p>
<p>Note that LWT is another popular (and younger) implementation of the
same idea for OCaml. It is possible to use LWT together with Ocamlnet,
but there are restrictions. We'll explain this later in this article.</p>
<h3 id="esys">What are event systems?</h3>
<p>You will often see the type</p>
<pre class="codepre"><code class="code"> Unixqueue.event_system </code></pre>
<p>in signatures of functions. An event system bundles all resources that
are needed to run cooperative threads, like watched file descriptors,
handlers for file events, and timers. It is the common anchor point
for all activities that will happen concurrently:</p>
<ul>
<li>If you define several cooperative threads for the same <code class="code">event_system</code>,
it is possible to run them concurrently.</li>
<li>You can have any number of <code class="code">event_system</code> objects in your program.
However, once you attach cooperative threads to different event
systems, they cannot run together anymore.</li>
<li>Having several event systems makes nevertheless sense in a number
of scenarios. For example, you could write a library function that
will do a number of I/O actions concurrently, but when all I/O
is finished, the function returns normally (and stops any concurrent
execution). In this case you would use a local event system that exists
for the lifetime of this function only.</li>
<li>A more extreme model is to use only one event system for the whole
program. This, however, means that the whole program must follow
a programming style that is compatible with events.</li>
</ul>
<p>The <code class="code">event_system</code> object is usually passed on from one function call
to the next. There is no global event system. (NB. If you develop for
Netplex, there is a pseudo-global event system for every Netplex container.
But this just means that you can define your own global event system if
you need it for your application.)</p>
<p><b>How to create an event system:</b></p>
<pre class="codepre"><code class="code">let esys = Unixqueue.standard_event_system()
</code></pre>
<p>An older name of the same is <code class="code">Unixqueue.create_unix_event_system</code>.</p>
<p>There is also a second implementation which uses accelerated poll interfaces
if provided by the operating system (e.g. epoll on Linux):</p>
<pre class="codepre"><code class="code">let esys = Unixqueue.performance_event_system()
</code></pre>
<p>This, however, is only an advantage if you have hundreds of file
descriptors to observe.</p>
<p><b>How to attach actions to event systems:</b></p>
<p>The abstraction of event systems defines an API allowing one to interact
with it. This is available in the <a href="Unixqueue.html"><code class="code">Unixqueue</code></a> module. Normally, however,
you don't use this module directly, because it is <i>very low-level</i>.</p>
<p>So, let's look directly at high-level interfaces. For example, the
<a href="Nethttp_client.html"><code class="code">Nethttp_client</code></a> uses event systems internally, and one can also control
this aspect of it. When creating an <a href="Nethttp_client.pipeline-c.html"><code class="code">Nethttp_client.pipeline</code></a> object,
just set the event system to the one you want to use. This attaches
the whole HTTP protocol interpreter implemented by this object to the
event system:</p>
<pre class="codepre"><code class="code">let pipeline = new Nethttp_client.pipeline
let () = pipeline # set_event_system esys
</code></pre>
<p>Note that you can attach other pipelines and even unrelated, other I/O
actions to the same event system. This just means, as mentioned above,
that these actions are done concurrently.</p>
<p>The HTTP pipeline is initially empty, i.e. it does nothing. Before something
can happen, you need to program it, i.e. add tasks to do. For example:</p>
<pre class="codepre"><code class="code">pipeline # add_with_callback
(new Nethttp_client.get "http://caml.inria.fr/")
(fun get -> ...)
</code></pre>
<p>The <code class="code">add_with_callback</code> method adds the HTTP task to run to the internal
queue. Also, there is a callback function which gets invoked when the
task is done.</p>
<p>If you enter the shown OCaml code into a toploop, you will notice that
no I/O occurs so far. Adding something to the internal task queue does not
yet trigger that it is executed. This is meant as a feature of all
Equeue-based concurrency: You have the chance to set the machinery up
before it starts running.</p>
<p>This example showed how to deal with HTTP clients. What about other
network protocols? The scheme is always the same: The event system object
needs to be passed down to the protocol interpreter, either directly
at creation time, or directly after that.</p>
<p><b>How to run event systems</b></p>
<p>The remaining question is now how to start the execution after everything
is set up. This is normally done with</p>
<pre class="codepre"><code class="code">Unixqueue.run esys
</code></pre>
<p>This single statement starts whatever action was previously configured,
and returns first when the action is completely finished. In our
example this means it covers the whole HTTP GET protocol.</p>
<p>It is allowed to modify the scene while something is already happening.
For example, you could download a second HTTP file when the first is
done:</p>
<pre class="codepre"><code class="code">pipeline # add_with_callback
(new Nethttp_client.get "http://caml.inria.fr/")
(fun get1 ->
pipeline # add_with_callback
(new Nethttp_client.get "http://www.camlcity.org/")
(fun get2 -> ...)
)
</code></pre>
<p>These "in-place" modifications of what to do are not only allowed at
points like the shown where a part of the action is already complete,
but at any point in time. For example, you can define a timer that
starts the other action, no matter at which point of execution the
running action currently is:</p>
<pre class="codepre"><code class="code">pipeline # add_with_callback
(new Nethttp_client.get "http://caml.inria.fr/")
(fun get1 -> ...);
let g = Unixqueue.new_group esys
Unixqueue.once esys g time
(fun () ->
pipeline # add_with_callback
(new Nethttp_client.get "http://www.camlcity.org/")
(fun get2 -> ...)
)
</code></pre>
<p>After <code class="code">time</code> seconds the second download is started. (NB. What is the
purpose of <code class="code">g</code>? A Unixqueue group can be used for cancelling all actions
associated to the group. In this case for cancelling the timer.)</p>
<p>The HTTP client provides an API style where the completion of an
action is indicated to the user via a callback. This style is easy to
use for beginners, but it has a drawback: There is no uniform way how
to compose more elementary actions to more complex actions. Such
composition is possible as shown in the example, but it is always an
ad-hoc solution.</p>
<p><b>Recursion is your friend</b></p>
<p>Let's have a look at such an ad-hoc composition: Assumed we have
a list of URLs we want to download them with high concurrency.</p>
<p>Idea 1: We just add all URLs to the same pipeline, as in</p>
<pre class="codepre"><code class="code">let count = ref 0
List.iter
(fun url ->
pipeline # add_with_callback
(new Nethttp_client.get url)
(fun get ->
decr count;
if !count = 0 then ... (* do some followup action here *)
);
incr count
)
list
</code></pre>
<p>and then run the <code class="code">esys</code>. This works, but the "intelligence" of the
HTTP pipeline object is only limited. If there are several files to
download from the same server, the pipeline is able to manage to use
only a limited number of connections to do this, and to serialize the
requests over these connections. There is, however, no built-in
mechanism that would limit the number of servers to contact at
once. If you had one million different servers in this list, the
pipeline would try to download from all servers concurrently. Of
course, this will fail (lacking system resources).</p>
<p>Idea 2: We only add a limited number of URLs at a time.</p>
<pre class="codepre"><code class="code">let count = ref 0
let count_max = 10
let list = ref list
let rec maybe_next() =
if !count < count_max then (
match !list with
| [] -> ()
| url :: list' ->
pipeline # add_with_callback
(new Nethttp_client.get url)
(fun get ->
decr count;
maybe_next();
if !count = 0 then ... (* followup action *)
);
incr count;
list := list';
maybe_next()
)
</code></pre>
<p>We use here recursion to encode the repetitive algorithm. This is the
mechanism of choice, because we need to continue the loop from the
callback function (an imperative construct could not do so).</p>
<p>Note that recursive calls from callback functions do not fill up the
stack, so you could do this endlessly without risking a stack
overflow.</p>
<p><b>Trap: do not mix synchronous and event-based APIs</b></p>
<p>Many protocol interpreters provide both styles of APIs: Conventional
synchronous APIs, and event-based APIs. The question is whether one
can mix them.</p>
<p>This is not possible, because the synchronous API is normally derived
from the event-based API by setting up a one-time action in the event
system and then running the event system. If you mixed the APIs,
it would occur that a running event system is again tried to be run.
This is forbidden, though, and will cause that an exception is thrown.</p>
<p>So: Use the same instance of the protocol interpreter either in a
synchronous way, or in an event-based way, but do not do both.</p>
<h3 id="engines">What are engines?</h3>
<p>As we have seen, callbacks are a common way to notify the caller about
state changes. However, callbacks are too primitive to allow
systematic composition of actions. The abstraction of engines has been
developed to fill this gap. As a first approximation, imagine an
engine as a wrapped callback interface: a machinery which is executing
something concurrently until the result is available, with the
possibility of notifying users of the result.</p>
<p>Continuing the HTTP example, there is also an engine-based version of
adding a request to the pipeline:</p>
<pre class="codepre"><code class="code">let e = pipeline # add_e (new Nethttp_client.get url)
</code></pre>
<p>This is the same as <code class="code">add_with_callback</code> only that the delivery mechanism
is different.</p>
<p>It is possible to attach a callback to an engine:</p>
<pre class="codepre"><code class="code">Uq_engines.when_state
~is_done:(fun () -> ...)
~is_error:(fun ex -> ...)
e
</code></pre>
<p>The first function is called when the HTTP request could be processed
successfully, and the second one when a fatal error occurs (with <code class="code">ex</code>
being the exception). Using <a href="Uq_engines.html#VALwhen_state"><code class="code">Uq_engines.when_state</code></a>, every engine-style
interface can be turned into a callback-style interface. Of course, this
is not the primary idea of this abstraction, but this possibility means
that we can go to the lower level of callbacks whenever needed.</p>
<p>The three most important composition operators are available in
<a href="Uq_engines.Operators.html"><code class="code">Uq_engines.Operators</code></a>. It is suggested that you <code class="code">open</code> this module,
and use the operators directly:</p>
<ul>
<li>Sequence: With
<pre class="codepre"><code class="code"> e1 ++ (fun r1 -> e2) </code></pre>
the engine <code class="code">e1</code> is executed first, and when it has computed the result
<code class="code">r1</code>, the engine <code class="code">e2</code> is started. The result of <code class="code">++</code> is again an engine,
so it is possible to concatenate several expressions:
<pre class="codepre"><code class="code"> e1 ++ (fun r1 -> e2) ++ (fun r2 -> e3) ++ ... </code></pre>
One can also set the parentheses differently if the previous results
are needed later:
<pre class="codepre"><code class="code"> e1 ++ (fun r1 -> e2 ++ (fun r2 -> e3 ++ ... )) </code></pre>
The <code class="code">++</code> operator is also available as normal function:
<a href="Uq_engines.html#VALseq_engine"><code class="code">Uq_engines.seq_engine</code></a></li>
<li>Mapping: With
<pre class="codepre"><code class="code"> e1 >> (fun st1 -> st2) </code></pre>
one can map the final state of <code class="code">e1</code> to a different state. The state
of the engine is either the computed value, the resulting exception,
or the tag that the engine was aborted:
<pre class="codepre"><code class="code"> e1 >>
(function
| `Done v -> ...
| `Error ex -> ...
| `Aborted -> ...
)
</code></pre>
As you can also have access to exceptions, this construction can be
used to catch exceptions, and to transform them into normal values:
<pre class="codepre"><code class="code"> e1 >>
(function
| `Done s -> `Done(Some s)
| `Error Not_found -> `Done None
| `Error ex -> `Error ex
| `Aborted -> `Aborted
)
</code></pre></li>
<li>Values as engines: If you just want to encapsulate an already existing
value <code class="code">v</code> into an engine, use
<pre class="codepre"><code class="code"> eps_e (`Done v) esys </code></pre>
or more generally
<pre class="codepre"><code class="code"> eps_e st esys </code></pre>
to encapsulate any state. The <code class="code">eps_e</code> makes an engine out of a value
by pretending that the value is computed in a single step (the
epsilon step).</li>
</ul>
<p>Using this, the above example of downloading two files, one after the
other, looks like:</p>
<pre class="codepre"><code class="code">let e =
pipeline # add_e
(new Nethttp_client.get "http://caml.inria.fr/")
++ (fun get1 ->
pipeline # add_e
(new Nethttp_client.get "http://www.camlcity.org/")
++ (fun get2 ->
...;
eps_e (`Done()) pipeline#event_system
)
)
</code></pre>
<p>Note that the final result is here just <code class="code">()</code>, and it is transformed with
<code class="code">eps_e</code> into an engine-compatible shape.</p>
<p><b>Getting results out of an engine-based algorithm</b></p>
<p>As engines use event systems internally, the constructed complex
engine (like <code class="code">e</code> in the previous example) is not immediately started,
but first when the event system runs (unless the event system is
already running). So you still finally need</p>
<pre class="codepre"><code class="code">Unixqueue.run esys
</code></pre>
<p>to fire up the prepared engines, and to wait for the result.</p>
<p>You may wonder how to access the result. In the previous example, the
result was just <code class="code">()</code>, so there is no interest in knowing it. But you
could also just return what you have got, as in</p>
<pre class="codepre"><code class="code">let e =
pipeline # add_e
(new Nethttp_client.get "http://caml.inria.fr/")
++ (fun get1 ->
pipeline # add_e
(new Nethttp_client.get "http://www.camlcity.org/")
++ (fun get2 ->
eps_e (`Done(get1, get2)) pipeline#event_system
)
)
</code></pre>
<p>and the question is how to get the pair <code class="code">(get1,get2)</code> with the
downloaded files after <code class="code">e</code> is finished. This is in deed very simple -
after <code class="code">Unixqueue.run</code> returns, you can check for result values:</p>
<pre class="codepre"><code class="code">let st = e # state
</code></pre>
<p>Here, <code class="code">st</code> can again have the values</p>
<ul>
<li><code class="code">`Done x</code> if the engine has a final value <code class="code">x</code> (here our pair)</li>
<li><code class="code">`Error e</code> if the engine execution ended in an exception <code class="code">e</code></li>
<li><code class="code">`Aborted</code> if the engine was articially stopped</li>
</ul>
<p>but also</p>
<ul>
<li><code class="code">`Working n</code> if the engine is not yet done, and <code class="code">n</code> is an integer
indicating the number of computation steps</li>
</ul>
<p>The <code class="code">`Working</code> state is only visible if you query the state directly with
<code class="code">state</code> but not in the <code class="code">>></code> operator.</p>
<p><b>Forking and joining concurrent threads built with engines</b></p>
<p>By concatenating elementary engines with <code class="code">++</code> you basically create an
execution thread. As we are talking here about concurrent programming,
the question is how to fork a new thread off of an existing one, and
how to join again with the created thread once it is finished.</p>
<p>Forking is very simple: Just have several expressions, e.g.</p>
<pre class="codepre"><code class="code">let e1 = <expression using ++>
let e2 = <expression using ++>
</code></pre>
<p>Now <code class="code">e1</code> and <code class="code">e2</code> run concurrently.</p>
<p>For joining, use the function <a href="Uq_engines.html#VALsync_engine"><code class="code">Uq_engines.sync_engine</code></a>:</p>
<pre class="codepre"><code class="code">let e_joined =
Uq_engines.sync_engine e1 e2
</code></pre>
<p>The engine <code class="code">e_joined</code> is first finished when both <code class="code">e1</code> and <code class="code">e2</code> are
finished. The result value of <code class="code">e_joined</code> is the pair of the results of
<code class="code">e1</code> and <code class="code">e2</code>, e.g.</p>
<pre class="codepre"><code class="code">e_joined ++ (fun (r1,r2) -> ...)
</code></pre>
<p>There is also a version of <code class="code">sync_engine</code> which can join any number of
engines: <a href="Uq_engines.html#VALmsync_engine"><code class="code">Uq_engines.msync_engine</code></a>. We use it in the engine-based version
of the download example:</p>
<pre class="codepre"><code class="code">let count_max = 10
let download_e list =
let list = ref list in
let rec download_thread_e() =
match !list with
| [] -> eps_e (`Done ()) esys
| url :: list' ->
list := list';
pipeline # add_e (new Nethttp_client.get url)
++ (fun get ->
download_thread_e()
) in
let rec fork_e k =
if k < count_max then
let e = download_thread_e() in
e :: fork_e (k+1)
else
[] in
let e_list = fork_e 0 in
Uq_engines.msync_engine
e_list
(fun _ () -> ())
()
esys
</code></pre>
<p>The function <code class="code">download_thread_e</code> downloads documents sequentially from the
HTTP servers. The URLs are fetched from the variable <code class="code">list</code>. In order to
get concurrency, <code class="code">count_max</code> of these threads are started by the
<code class="code">fork_e</code> recursion. The result is <code class="code">e_list</code>, a list of all concurrently
running engines. Finally, <code class="code">msync_engine</code> is used to wait until all of these
threads are finished. <code class="code">msync_engine</code> works like a fold operator, and
aggregates the result values via the argument function. Here, the threads
only return <code class="code">()</code> as results, so aggregation is pointless.</p>
<h3 id="overview">Overview of library functions for engines</h3>
<p><b>In <a href="Uq_engines.html"><code class="code">Uq_engines</code></a>:</b></p>
<p>This module contains the basic definition of engines, <a href="Uq_engines.engine-c.html"><code class="code">Uq_engines.engine</code></a>,
plus a number of combinators for engines:</p>
<ul>
<li>Sequence: <a href="Uq_engines.html#VALseq_engine"><code class="code">Uq_engines.seq_engine</code></a>, also backing the <code class="code">++</code> operator</li>
<li>Mapping: <a href="Uq_engines.html#VALmap_engine"><code class="code">Uq_engines.map_engine</code></a> and <a href="Uq_engines.html#VALfmap_engine"><code class="code">Uq_engines.fmap_engine</code></a>. The
latter is backing the <code class="code">>></code> operator</li>
<li>Waiting on event: <a href="Uq_engines.html#VALsignal_engine"><code class="code">Uq_engines.signal_engine</code></a>. The engine stops and
waits until a <code class="code">signal</code> function is called.</li>
<li>Error handling: <a href="Uq_engines.html#VALmeta_engine"><code class="code">Uq_engines.meta_engine</code></a>. Errors are lifted into the
normal value space.</li>
<li>Streaming: <a href="Uq_engines.html#VALstream_seq_engine"><code class="code">Uq_engines.stream_seq_engine</code></a>. Folding over a stream of
values (<code class="code">Stream</code> module), and evaluation of the fold function as engine.</li>
<li>Joining: <a href="Uq_engines.html#VALsync_engine"><code class="code">Uq_engines.sync_engine</code></a> and <a href="Uq_engines.html#VALmsync_engine"><code class="code">Uq_engines.msync_engine</code></a></li>
<li>Delays: <a href="Uq_engines.html#VALdelay_engine"><code class="code">Uq_engines.delay_engine</code></a> suspends the execution n seconds</li>
<li>Timeouts: <a href="Uq_engines.html#VALtimeout_engine"><code class="code">Uq_engines.timeout_engine</code></a> gives an engine a maximum time
for computations, and if the time is exceeded the engine is aborted.</li>
<li>Automatic serialization:
<a href="Uq_engines.html#VALserializer"><code class="code">Uq_engines.serializer</code></a> forces that an engine function
is serialized - the next engine can first start when the previous is
finished. <a href="Uq_engines.html#VALprioritizer"><code class="code">Uq_engines.prioritizer</code></a> is an advanced version where the
waiting engines can be prioritized.</li>
<li>Cache (lazy evaluation): <a href="Uq_engines.html#VALcache"><code class="code">Uq_engines.cache</code></a> obtains a lazily computed
value by running an engine.</li>
</ul>
<p>Also, the module defines basic I/O engines:</p>
<ul>
<li>File descriptor polling: <a href="Uq_engines.poll_engine-c.html"><code class="code">Uq_engines.poll_engine</code></a> waits until a
file descriptor is ready for an I/O operation</li>
<li>Input and output: <a href="Uq_engines.input_engine-c.html"><code class="code">Uq_engines.input_engine</code></a> and <a href="Uq_engines.output_engine-c.html"><code class="code">Uq_engines.output_engine</code></a>
can be used to define I/O engines by wrapping <code class="code">read</code> and <code class="code">write</code></li>
</ul>
<p><b>In <a href="Uq_client.html"><code class="code">Uq_client</code></a>:</b></p>
<ul>
<li>Connect a client: <a href="Uq_client.html#VALconnect_e"><code class="code">Uq_client.connect_e</code></a> creates a new socket and
connects it to a server</li>
</ul>
<p><b>In <a href="Uq_server.html"><code class="code">Uq_server</code></a>:</b></p>
<ul>
<li>Define a server: <a href="Uq_server.html#VALlistener"><code class="code">Uq_server.listener</code></a> creates a server socket
and allows it to process the accepted connections with engines.</li>
</ul>
<p><b>In <a href="Uq_io.html"><code class="code">Uq_io</code></a>:</b></p>
<p>This module contains functions for buffered I/O:</p>
<ul>
<li>Read data: <a href="Uq_io.html#VALinput_e"><code class="code">Uq_io.input_e</code></a> reads data from a device</li>
<li>Read data with fixed length: <a href="Uq_io.html#VALreally_input_e"><code class="code">Uq_io.really_input_e</code></a></li>
<li>Read data line by line: <a href="Uq_io.html#VALinput_line_e"><code class="code">Uq_io.input_line_e</code></a> and <a href="Uq_io.html#VALinput_lines_e"><code class="code">Uq_io.input_lines_e</code></a></li>
<li>Write data: <a href="Uq_io.html#VALoutput_e"><code class="code">Uq_io.output_e</code></a> writes data to a device</li>
<li>Write data with fixed length: <a href="Uq_io.html#VALreally_output_e"><code class="code">Uq_io.really_output_e</code></a>. There are also
variants for writing strings and buffers</li>
<li>Write EOF: <a href="Uq_io.html#VALwrite_eof_e"><code class="code">Uq_io.write_eof_e</code></a></li>
<li>Copy data between devices: <a href="Uq_io.html#VALcopy_e"><code class="code">Uq_io.copy_e</code></a></li>
</ul>
<p>A "device" is a file descriptor, a multiplex controller (see
<a href="Uq_engines.multiplex_controller-c.html"><code class="code">Uq_engines.multiplex_controller</code></a>), or an asynchronous channel.
The definition of devices is extensible.</p>
<p><b>In <a href="Uq_transfer.html"><code class="code">Uq_transfer</code></a>:</b></p>
<ul>
<li>Copying data: <a href="Uq_transfer.copier-c.html"><code class="code">Uq_transfer.copier</code></a> copies data between file descriptors
without looking at the data</li>
</ul>
<p><b>Other basics:</b></p>
<ul>
<li>Name resolution (e.g. DNS): <a href="Uq_resolver.html"><code class="code">Uq_resolver</code></a></li>
<li>SOCKS proxies: <a href="Uq_socks5.html"><code class="code">Uq_socks5</code></a></li>
<li>Using event systems together with kernel threads: <a href="Uq_mt.html"><code class="code">Uq_mt</code></a></li>
</ul>
<p><b>Protocol interpreters:</b></p>
<ul>
<li>RPC clients: <a href="Rpc_client.html"><code class="code">Rpc_client</code></a></li>
<li>RPC servers: <a href="Rpc_server.html"><code class="code">Rpc_server</code></a></li>
<li>HTTP clients: <a href="Nethttp_client.html"><code class="code">Nethttp_client</code></a></li>
<li>HTTP servers: <a href="Nethttpd_engine.html"><code class="code">Nethttpd_engine</code></a></li>
<li>FTP clients: <a href="Netftp_client.html"><code class="code">Netftp_client</code></a></li>
<li>Telnet clients: <a href="Nettelnet_client.html"><code class="code">Nettelnet_client</code></a></li>
</ul>
<p><b>Definition hierarchy</b></p>
<p>We have, top-to-bottom:</p>
<ul>
<li>Engines are the top-level abstraction. Essentially, they "only" provide
a notification mechanism for operations over event systems, but
exactly this make them the easily composable units that are most useful
for constructing algorithms.</li>
<li>Event systems are simply a uniform interface for event loops, and events
can be queued up in user space (<code class="code">Equeue</code>).</li>
<li>Pollsets (<a href="Netsys_pollset.html"><code class="code">Netsys_pollset</code></a>) are event loops, i.e. it is waited for
file descriptor conditions.</li>
<li>Kernel interface (<code class="code">select</code>, <code class="code">poll</code>, <code class="code">epoll</code> etc.)</li>
</ul>
<h3 id="tricks">Various tricks</h3>
<p><b>Aborting engines:</b> Unlike other types of threads, cooperative threads
can be aborted at any time. Use the <code class="code">abort</code> method:</p>
<pre class="codepre"><code class="code"> e # abort() </code></pre>
<p>The engine <code class="code">e</code> will then enter the <code class="code">`Aborted</code> state as soon as possible.
Often this will happen immediately, but there are also engines where this
takes some time.</p>
<p><b>Exceptions:</b> There are several ways to signal exceptions. First, the
orderly way:</p>
<pre class="codepre"><code class="code"> eps_e (`Error x) esys </code></pre>
<p>This creates an engine that represents an exception as final state. Because
exceptions cannot always handled that cleanly, the basic combinators like
<code class="code">++</code> always catch exceptions, and represent these exceptions in their final
state. For example:</p>
<pre class="codepre"><code class="code">e1 ++ (fun r1 -> raise x)
</code></pre>
<p>The <code class="code">++</code> operator catches the exception, and the state transitions to
<code class="code">`Error x</code> (just as the <code class="code">eps_e</code> example would do).</p>
<p>Note that this behavior relates to engines only. If you program event
systems directly, there will be no automatic exception handling. For
example</p>
<pre class="codepre"><code class="code">Unixqueue.once esys g 1.0 (fun () -> raise x)
</code></pre>
<p>does not catch <code class="code">x</code> in any way. The effect is that the exception falls
through to the caller, which is always <code class="code">Unixqueue.run</code>.</p>
<p><b>How to jump out of event processing:</b> The just mentioned way of raising
an exception can be used to leave event processing. Just define</p>
<pre class="codepre"><code class="code"> exception Esys_exit </code></pre>
<p>and throw it like</p>
<pre class="codepre"><code class="code">Unixqueue.once esys g 0.0 (fun () -> raise Esys_exit)
</code></pre>
<p>and catch it like</p>
<pre class="codepre"><code class="code">try
Unixqueue.run esys
with
| Esys_exit -> ...
</code></pre>
<p>This works always, even if the call of <code class="code">Unixqueue.once</code> is inside an
engine expression.</p>
<h3 id="lwt">Engines and LWT</h3>
<p>Users who are familiar with LWT will certainly recognize many operations -
although they often have another name. (You may wonder why both implementations
exist - well, a longer story, but essentially Ocamlnet has older roots,
and at a certain state of the development it only knew event systems but not
yet engines. The LWT developers saw this, and found this insufficient, and
developed LWT. Unfortunately, they did not base their new library on Ocamlnet,
but chose to reimplement the event loop core. In the meantime, the Ocamlnet
development fixed the deficiencies in their library. Now we have two good
libraries for the same range of problems.)</p>
<p>Compare:</p>
<ul>
<li>Wrap values: <code class="code">eps_e</code> vs. <code class="code">Lwt.return</code> and <code class="code">Lwt.fail</code></li>
<li>Waiting: <a href="Uq_engines.html#VALsignal_engine"><code class="code">Uq_engines.signal_engine</code></a> vs. <code class="code">Lwt.wait</code></li>
<li>Sequences: <code class="code">Uq_engines.seq_engines</code> vs. <code class="code">Lwt.bind</code></li>
<li>Joining: <a href="Uq_engines.html#VALsync_engine"><code class="code">Uq_engines.sync_engine</code></a> vs. <code class="code">Lwt.join</code>, <code class="code">Lwt.choose</code>, and
<code class="code">Lwt.pick</code></li>
<li>Run until finished: <a href="Unixqueue.html#VALrun"><code class="code">Unixqueue.run</code></a> vs. <code class="code">Lwt_main.run</code></li>
</ul>
<p>You should, however, be aware that there are some differences between
the libraries:</p>
<ul>
<li>Delayed vs immediate execution:
The LWT threads are immediately started, and run until they sleep.
First at this point, you need to call <code class="code">Lwt_main.run</code>. In contrast,
Equeue prefers to first start at <code class="code">Unixqueue.run</code> time.</li>
<li>LWT allows it to create Lwt threads which are already terminated
(<code class="code">Lwt.return</code>).
In Equeue we don't do - <code class="code">eps_e</code> creates an engine which will terminate
as soon as possible and which results in a constant value. Effectively,
this means that <code class="code">eps_e</code> is seen as point where threads can be switched,
whereas this is not the case for <code class="code">Lwt.return</code>. This gives more
opportunities for switching, but there are also more subtle consequences,
like who is the caller of suspended functions. In Equeue it is always
the core of the event loop, whereas this is not uniform in LWT.</li>
<li>In Equeue all engines can be aborted, whereas in LWT a special
abstraction needs to be used.</li>
<li>In LWT there can be only one active event loop at a time, whereas
in Equeue there can be as many <code class="code">event_system</code> objects as needed. This
is mainly a restriction for programs using kernel threads: In Equeue,
every thread can have its own <code class="code">event_system</code>, but doing the same in
LWT is impossible (incompatibility with kernel threads on this level).</li>
</ul>
<p>The remaining question is how to use both facilities in the same program
(provided it is not multi-threaded, which rules LWT out). There is now
the library <a href="Uq_lwt.html"><code class="code">Uq_lwt</code></a> helping here.</p>
<p>The idea is to replace the event loop built into LWT by the Ocamlnet
event loop. This is done for a given <code class="code">esys</code> by</p>
<pre class="codepre"><code class="code">class lwt_engine esys =
object
inherit Lwt_engine.abstract
inherit Uq_lwt.lwt_backend esys
end
Lwt_engine.set (new lwt_engine esys)
</code></pre>
<p>Additionally, it is required that you now always call <code class="code">Lwt_main.run</code> for
starting event-based programs, and not <code class="code">Unixqueue.run esys</code>. The latter
would not work for technical reasons. Of course, this means that the
main program will be LWT.</p>
<p><b>How to call engines from LWT threads</b>: Assumed you are in some
LWT code, and want to call an engine-based function
<code class="code">f : 'a -> 'b engine</code>. This can be achieved by first simply calling
the function, and then using an LWT primitive to LWT-wait until the
engine is done:</p>
<pre class="codepre"><code class="code">let call_thread f x =
let e = f x in
let waiter, condition = Lwt.wait() in
Uq_engines.when_state
~is_done:(fun r -> Lwt.wakeup condition r)
~is_error:(fun x -> Lwt.wakeup_exn condition x)
e;
waiter
</code></pre>
<p><b>How to call LWT threads from engines:</b> The reverse for a function
<code class="code">f : 'a -> 'b Lwt.thread</code>:</p>
<pre class="codepre"><code class="code">let call_e f x =
let thr = f x in
let waiter, signal = Uq_engines.signal_engine() in
Lwt.on_success thr (fun r -> signal (`Done r));
Lwt.on_failure thr (fun x -> signal (`Error x));
waiter
</code></pre></div>
</body></html>
|