File: Netmcore_basics.html

package info (click to toggle)
ocamlnet 4.1.9-7
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 54,024 kB
  • sloc: ml: 151,939; ansic: 11,071; sh: 2,003; makefile: 1,310
file content (871 lines) | stat: -rw-r--r-- 46,796 bytes parent folder | download | duplicates (4)
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
<!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="Netmcore_tut.html">
<link rel="next" href="Netplex_types.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 : Netmcore_basics</title>
</head>
<body>
<div class="navbar"><a class="pre" href="Netmcore_tut.html" title="Netmcore_tut">Previous</a>
&nbsp;<a class="up" href="index.html" title="Index">Up</a>
&nbsp;<a class="post" href="Netplex_types.html" title="Netplex_types">Next</a>
</div>
<h1>Netmcore_basics</h1>
<div class="info-desc">
<h2 id="1_NetmulticoreBasics">Netmulticore Basics</h2>
<p><b>Contents</b></p>

<ul>
<li><code class="code">Netmcore_basics.processes</code></li>
<li><code class="code">Netmcore_basics.startstop</code></li>
<li><code class="code">Netmcore_basics.primitive</code></li>
<li><code class="code">Netmcore_basics.ipc</code></li>
<li><code class="code">Netmcore_basics.msgpassing</code></li>
<li><code class="code">Netmcore_basics.goon</code></li>
</ul>
<p>The intention of this chapter is to give an overview over the basic
mechanisms of Netmulticore. The focus is here on the <i>safe</i>
mechanisms, i.e. those that cannot crash the program if used the wrong
way. Generally, however, Netmulticore also provides unsafe programming
elements, mostly to maximize performance. The unsafe elements are well
explained in <a href="Netmcore_tut.html"><code class="code">Netmcore_tut</code></a>.</p>

<h3 id="processes">Processes</h3>
<p>Netmulticore uses subprocesses as workers. This has the advantage that
the workers are really independent from each other. Especially, every
worker does its own memory management (e.g. a worker won't stop
another worker when it does a garbage collection run). The downside
is, of course, that it is harder to exchange data between the workers
(especially compared with multithreading).</p>

<p><div class="remark">
Remember that multithreading in OCaml is restricted so that at most
only one CPU core can be utilized. Implementing the workers with
full processes is so far the only way to run algorithms on more than
one core.
</div></p>

<p>There is a fixed process hierarchy. When Netmulticore starts up, the
current process becomes the master process, and the workers will be
children of this master:</p>

<pre class="codepre"><code class="code">master
  |
  +-- worker1
  |
  +-- worker2
  |
  +-- worker3
</code></pre>
<p>It is possible to create new workers at any time. A worker can create
another worker, and Netmulticore creates then a new child as fork of
the master (really, this works, and is implemented by forwarding the
creation request from the worker to the master). Netmulticore never
forks a worker from a worker directly - this would create deep process
hierarchies, and these tend to become unmanageable.</p>

<p>In the same way, it is also possible to wait for the termination of
a worker (join). Any worker can join any other worker.</p>

<p>All "payload work" must be really done by the workers, and not by the
master. The master remains mostly idle for the time of the
Netmulticore job, and only provides auxiliary services for things that
must be done in the master (e.g. organizing a fork). Ideally, the
master process is kept as lean as possible, i.e. nothing is stored
there, because the new workers are copies of the master image, and
copying is cheapest when the image is as small as possible.</p>

<h3 id="startstop">Starting up, shutting down</h3>
<p>At startup time, Netmulticore starts one special worker, the <i>first
process</i>. This is the only worker that is not started by another
worker, but directly by the process playing the role of the master.</p>

<p>When all workers have terminated, the Netmulticore job is done. At
this point, the master process is alone again. It is now possible
to query for the result of other worker processes, especially of the
first process.</p>

<p>Let's look at a simple example (the "hello world" of Netmulticore):</p>

<pre class="codepre"><code class="code">let computation (x,y) =
  x +. y

let computation_fork, computation_join =
  Netmcore_process.def_process computation

let first_process() =
  Netmcore_process.start computation_fork (3.0,4.0)

let extract_result _ pid =
  Netmcore_process.join_nowait computation_join pid

let () =
  let sum_opt =
    Netmcore.run
      ~socket_directory:"/tmp/netmcore"
      ~first_process
      ~extract_result
      () in
  match sum_opt with
    | None -&gt; printf "Error\n"
    | Some sum -&gt; printf "Sum: %f\n" sum
</code></pre>
<p>The function to be run in a worker process is here <code class="code">computation</code>. With
<a href="Netmcore_process.html#VALdef_process"><code class="code">Netmcore_process.def_process</code></a> we define this function as designated
process. Note that this needs to happen before the first worker is
started, and because of this, it is normally done in global context
(i.e. not from a function body, but directly at module level).  The
master needs to know all functions that can be used as worker
processes in advance (remember that all forks are managed by the
master).  <code class="code">def_process</code> returns a pair of "fork point" and "join
point". These are abstract descriptors needed for forking and joining,
respectively.</p>

<p>With <a href="Netmcore_process.html#VALstart"><code class="code">Netmcore_process.start</code></a> a new worker is started. The arguments
are the "fork point" (which implicitly names the worker function), and
the argument passed to the worker. The process identifier is returned.
In this example, we have only one worker, and that takes over the role
of the first process. (N.B. The pid is not directly the identifier
used by the operating system, but an internally managed identifier.)</p>

<p>As mentioned, the Netmulticore job is done when all workers have
finished their tasks. Normally, you call <a href="Netmcore_process.html#VALjoin"><code class="code">Netmcore_process.join</code></a> to
get the result of a worker. However, <code class="code">join</code> also blocks the execution
(i.e. it waits until the worker is done). In the master process, blocking
is generally not allowed, and hence we have to use here the variant of
<code class="code">join</code> that does not wait, <a href="Netmcore_process.html#VALjoin_nowait"><code class="code">Netmcore_process.join_nowait</code></a>. As we
already know that the workers are finished, and we only want to get the
result value, this is no problem here.</p>

<p>For joining, we pass the so-called "join point" to this function -
basically, this tells <code class="code">join_nowait</code> which result to retrieve.</p>

<p>The callback <code class="code">extract_result</code> is invoked when the workers are already
done, but before Netmulticore officially finishes its work. This is
the last moment when workers can be joined. The result of <code class="code">extract_result</code>
is the result of <a href="Netmcore.html#VALrun"><code class="code">Netmcore.run</code></a>.</p>

<p><div class="remark">
History: 
The function <a href="Netmcore.html#VALrun"><code class="code">Netmcore.run</code></a> is new in OCamlnet-3.6.3. In older versions
there was only <a href="Netmcore.html#VALstartup"><code class="code">Netmcore.startup</code></a> without providing any way to pass
results back to the caller. Note that <a href="Netmcore.html#VALjoin_nowait"><code class="code">Netmcore.join_nowait</code></a> was also
added in this release. If you find code in the Internet, it often
contains workarounds for these limitations present in older Ocamlnet versions.
</div></p>

<p>A few more remarks:</p>
<ul>
<li>The <code class="code">socket_directory</code> is used for Unix domain sockets, and for
   temporary files Netmulticore may need. Typical locations are
   <code class="code">/tmp</code> or <code class="code">/var/run</code>. The path name of this directory must not be
   too iong (there is a limit of around 100 chars in total). The name
   can also be generated. You can delete the directory after use.</li>
<li>Worker parameters (here the float pair (3.0,4.0) and the sum) are
   passed by serializing the values with the <code class="code">Marshal</code> module. So far,
   functional values, objects, lazy values, and exceptions cannot be
   marshalled.</li>
<li>Essentially, Netmulticore uses the functionality provided by
   Netplex to start and stop processes, and also for the communication
   between worker and master. Because of this, the Netplex infrastructure
   is fully available. For instance, you can use the functions of
   <a href="Netplex_cenv.html"><code class="code">Netplex_cenv</code></a>, e.g. for logging, or manipulating the Netplex
   container. The <code class="code">netplex-admin</code> utility (see <code class="code">Netplex_admin.admin</code>)
   can be used to query the process state from the command line. 
   It is not only possible
   to use Netplex from Netmulticore, but also the other way round:
   A network server implemented with Netplex can start Netmulticore
   workers (just call <a href="Netmcore_process.html#VALstart"><code class="code">Netmcore_process.start</code></a> when you need one) - 
   the only requirement
   and initialization for this is that <a href="Netmcore.html#VALadd_plugins"><code class="code">Netmcore.add_plugins</code></a> must
   have been called at Netplex startup time. We'll look at the possible
   interactions with Netplex closer below.</li>
</ul>
<h3 id="primitive">Primitive parallelization</h3>
<p>In the most simple scenario, a few workers are started at the same
time, and compute the result in parallel. When the workers are done,
it is expected that every worker has computed some part result, and
it is only required to retrieve it, and to combine it with the other
part results. Note that <a href="Netmcore.html#VALrun"><code class="code">Netmcore.run</code></a> can only start <i>one</i>
worker, so we need to start the real workers on our own from the
single first process:</p>

<pre class="codepre"><code class="code">let n = &lt;number of workers&gt;

let computation (i,arg) =
  &lt;compute something from arg and return it&gt;

let computation_fork, computation_join =
  Netmcore_process.def_process computation

let manage arg =
  let pids =
    List.map
      (fun i -&gt; Netmcore_process.start computation_fork (i,arg))
      (Array.to_list (Array.initialize n (fun i -&gt; i))) in
  let results =
    List.map
      (fun pid -&gt;
         match Netmcore_process.join computation_join pid with
           | None -&gt; failwith "No result after error"
           | Some r -&gt; r
      )
      pids in
  &lt;reduce the results to a single one&gt;

let manage_fork, manage_join =
  Netmcore_process.def_process manage

let first_process arg =
  Netmcore_process.start manage_fork arg

let extract_result _ pid =
  Netmcore_process.join_nowait manage_join pid

&lt;call Netmcore.run as above&gt;
</code></pre>
<p>Here, <code class="code">manage</code> takes over the role of the first process that starts the
real workers, and waits until the workers are done. Note that we use
<a href="Netmcore_process.html#VALjoin"><code class="code">Netmcore_process.join</code></a> here, and no longer <code class="code">join_nowait</code>, because it is
essential to wait for the termination of the workers.</p>

<p>Some remarks:</p>

<ul>
<li>Starting and terminating processes are relatively expensive operations.
   This scheme is only well-suited if the parallelized computation takes
   really long (e.g. several seconds at least)</li>
<li>The workers do not communicate directly with each other in this scheme.
   The workers just get an argument to process, and deliver some part
   result. In between, there is no opportunity for data exchange.</li>
<li>The workers are created at the time they receive their arguments.
   There is no option for doing some per-worker initialization before
   the invocation. Of course, one can do some global initialization in
   the master, because the worker processes are created as copies of the
   master process, and inherit any global data prepared there.</li>
<li>Every worker gets an individual copy of the arguments. Copying is not
   a free operation, although quite cheap. For certain algorithms,
   copying per worker is already relatively expensive, and decreases
   the performance noticeably (e.g. this can be observed for sorting
   algorithms).</li>
</ul>
<h3 id="ipc">Using Netplex IPC mechanisms</h3>
<p>As noted, the Netplex library is the basis on which Netmulticore
provides more advanced features. Let's have a quick glance at the
mechanisms Netplex defines:</p>

<ul>
<li><a href="Netplex_sharedvar.html"><code class="code">Netplex_sharedvar</code></a> is a simple way for storing values in a common
   place so that all workers can access them</li>
<li><a href="Netplex_mutex.html"><code class="code">Netplex_mutex</code></a> defines primitives for mutual exclusion</li>
<li><a href="Netplex_semaphore.html"><code class="code">Netplex_semaphore</code></a> defines semaphores</li>
<li><a href="Netplex_mbox.html"><code class="code">Netplex_mbox</code></a> is a simple system for message passing. See the
   next section for details.</li>
</ul>
<p>Generally, the Netplex mechanisms are implemented on top of RPC
messaging with Unix domain sockets. The master process serves as the
controlling instance of the primitives, i.e. the worker sends a
message to the master with a request like "lock the mutex", and the
master implements the logic, eventually notifying the worker that the
lock has been acquired. This type of IPC primitives is relatively
slow, but also robust ("uncrashable"), and does not need special
prerequisites like shared memory. (Note that there are also very fast
IPC primitives in Netmulticore that use shared memory for
communication, and which are described in <a href="Netmcore_tut.html"><code class="code">Netmcore_tut</code></a>. These are,
however, a lot more complicated to use than the simple ones defined
here, and not well suited as starting point for exploring
parallelization options in OCaml.)</p>

<h4 id="3_Initialization">Initialization</h4>
<p>The Netplex mechanisms need to be initialized at two times:</p>

<ul>
<li>They need to be added as <i>plugins</i> to the Netplex controller.
    Such a plugin attaches some new behavior to the Netplex routines
    running in the master process. This type of initialization is
    done once per Netplex controller, and as Netmulticore creates
    a new controller per <a href="Netmcore.html#VALrun"><code class="code">Netmcore.run</code></a> call, this is required
    for every such call.

    The plugin is defined in the respective module (i.e.
    <a href="Netplex_mutex.html#VALplugin"><code class="code">Netplex_mutex.plugin</code></a>, <a href="Netplex_sharedvar.html#VALplugin"><code class="code">Netplex_sharedvar.plugin</code></a>, and
    <a href="Netplex_semaphore.html#VALplugin"><code class="code">Netplex_semaphore.plugin</code></a>). You just need to add it to the
    controller with code like

    <pre class="codepre"><code class="code">  Netmcore.run
    ...
    ~init_ctrl:(fun ctrl -&gt; ctrl # add_plugin Netplex_mutex.plugin)
    ...
    </code></pre>

    Adding a plugin several times is a no-op.
 </li>
<li>Of course, you can manage several objects per mechanism (i.e.
    several mutexes/variables/semaphores). Each object needs to
    be created. Note that you cannot do this in the scope of the
    master process! A good point in time to do this is at the
    beginning of the first worker process, before any further worker is
    launched. (Alternatively, it is also possible to create the
    objects when they are used first. This is a bit more complicated,
    and not covered by this tutorial.)

    The objects are normally identified by strings. For example,
    a semaphore could be created as

    <pre class="codepre"><code class="code">    ignore(Netplex_semaphore.create "my_sem" 5L)
    </code></pre>

    The initial value would be 5. The semaphore is now available for all
    workers in the same <a href="Netmcore.html#VALrun"><code class="code">Netmcore.run</code></a> session under this name. The
    return value of <a href="Netplex_semaphore.html#VALcreate"><code class="code">Netplex_semaphore.create</code></a> says whether the semaphore
    was created (true). Otherwise, the semaphore existed already.
 </li>
</ul>

<p>There is normally no need to delete the objects when you are done with
them. The objects are bound to the lifetime of the Netplex controller,
and this ends anyway when <a href="Netmcore.html#VALrun"><code class="code">Netmcore.run</code></a> returns.</p>

<h4 id="3_Objectinitializationforsemaphores">Object initialization for semaphores</h4>
<p>As mentioned, the function to call is <a href="Netplex_semaphore.html#VALcreate"><code class="code">Netplex_semaphore.create</code></a>:</p>

<pre class="codepre"><code class="code">let success =
  Netplex_semaphore.create name initial_value
</code></pre>
<p>Remember that semaphores are counters with non-negative values, and
hence <code class="code">initial_value</code> is the initial counter value.</p>

<h4 id="3_Objectinitializationformutexes">Object initialization for mutexes</h4>
<p>There is no need to create mutexes - these are implicitly created
(in unlocked state) when they are used for the first time, i.e.
when doing</p>

<pre class="codepre"><code class="code">let mutex_handle =
  Netplex_mutex.access name
</code></pre>
<p>(Now call <a href="Netplex_mutex.html#VALlock"><code class="code">Netplex_mutex.lock</code></a> or <a href="Netplex_mutex.html#VALunlock"><code class="code">Netplex_mutex.unlock</code></a> to work with
the mutex.)</p>

<h4 id="3_Objectinitializationforsharedvariables">Object initialization for shared variables</h4>
<p>For <a href="Netplex_sharedvar.html"><code class="code">Netplex_sharedvar</code></a> variables, the creation looks like</p>

<pre class="codepre"><code class="code">let success =
  Netplex_sharedvar.create_var ~enc:true "my_var"
</code></pre>
<p>We pass here <code class="code">enc:true</code> which is required when we want to use the
<a href="Netplex_sharedvar.Make_var_type.html"><code class="code">Netplex_sharedvar.Make_var_type</code></a> functor for getting easy and
safe access. This works like this: Define</p>

<pre class="codepre"><code class="code">module Var_foo =
  Netplex_sharedvar.Make_var_type(struct type t = foo end)
</code></pre>
<p>in global context to get the well-typed accessor functions</p>

<pre class="codepre"><code class="code">let value = Var_foo.get "my_var"
</code></pre>
<p>and</p>

<pre class="codepre"><code class="code">Var_foo.set "my_var" new_value
</code></pre>
<p>As noted, this works only when setting <code class="code">enc:true</code> at creation time.</p>

<h4 id="3_Operation">Operation</h4>
<p>Generally, the access to the Netplex synchronization objects is
restricted to the lifetime of the Netplex controller (i.e. the
duration of <a href="Netmcore.html#VALrun"><code class="code">Netmcore.run</code></a>), and the objects can only be accessed
from worker processes (or better, from any Netplex container, as
workers are implemented by containers). It is not possible to interact
with the objects from the master process (although there are a few
exceptions from this rule, e.g. you can read (but not write) the value
of a shared variable also from the master, and the last opportunity is
even in the <code class="code">extract_result</code> callback of <a href="Netmcore.html#VALrun"><code class="code">Netmcore.run</code></a>).</p>

<p>Every operation is isolated from concurrent operations of the same
type. For example, when two workers set the same shared variable with
<code class="code">Var_foo.set "my_var"</code>, there is no risk that the two calls interact
in a bad way and cause a crash. Netplex implicitly serializes such
calls, and one of the two calls is executed before the other.</p>

<p>For this reason, it is normally not necessary to proctect a single
shared variable with a mutex. You need mutexes first when you need
to synchronize several variables, or a variable and a semaphore.</p>

<p>Overview of the operations (see linked pages for details):</p>

<ul>
<li>Semaphores:
<ul>
<li><a href="Netplex_semaphore.html#VALincrement"><code class="code">Netplex_semaphore.increment</code></a> ("post")</li>
<li><a href="Netplex_semaphore.html#VALdecrement"><code class="code">Netplex_semaphore.decrement</code></a> ("wait")</li>
<li><a href="Netplex_semaphore.html#VALget"><code class="code">Netplex_semaphore.get</code></a></li>
</ul>

  </li>
<li>Mutexes:
<ul>
<li><a href="Netplex_mutex.html#VALlock"><code class="code">Netplex_mutex.lock</code></a></li>
<li><a href="Netplex_mutex.html#VALunlock"><code class="code">Netplex_mutex.unlock</code></a></li>
</ul>

  </li>
<li>Shared variables:
<ul>
<li>After defining the accessor module with
       <a href="Netplex_sharedvar.Make_var_type.html"><code class="code">Netplex_sharedvar.Make_var_type</code></a>, just use
       the <code class="code">get</code> and <code class="code">set</code> functions in this module</li>
</ul>

  </li>
</ul>

<h3 id="msgpassing">Message passing</h3>
<p>Message passing means that a worker installs a message box, and waits
for the arrival of messages from other workers. Messages can be arbitrary
OCaml values provided these can be marshalled. The message boxes 
implemented by <a href="Netplex_mbox.html"><code class="code">Netplex_mbox</code></a> have only space for one message at a time,
so the message senders will have to wait until the box is free.</p>

<p><div class="remark">
There are other implementations of message boxes in OCamlnet:
<a href="Netcamlbox.html"><code class="code">Netcamlbox</code></a> provides very fast boxes that store the messages in
shared memory. The caveat is that
the size of the messages is limited. Another option is <a href="Netmcore_queue.html"><code class="code">Netmcore_queue</code></a>
which is a shared value queue which can be easily extended to support
full message box functionality. 
Both alternatives do not run on every operating system, though (but Linux
and OS X are supported).
</div></p>

<p>Preparations: First, the required plugin needs to be installed in
the Netplex controller. Again, use code like</p>

<pre class="codepre"><code class="code">  Netmcore.run
    ...
    ~init_ctrl:(fun ctrl -&gt; ctrl # add_plugin Netplex_mbox.plugin)
    ...
</code></pre>
<p>Second, create the mailbox module. This is very similar to
<a href="Netplex_sharedvar.html"><code class="code">Netplex_sharedvar</code></a>, e.g.:</p>

<pre class="codepre"><code class="code">module Mbox_foo =
  Netplex_mbox.Make_mbox_type(struct type t = foo end)
</code></pre>
<p>Remember that this needs to happen in global context (i.e. don't
do it in a local module).</p>

<p>Now create the mailbox (in a worker):</p>

<pre class="codepre"><code class="code">let mbox = Mbox_foo.create "mybox"
</code></pre>
<p>If the box already exists, it is just opened, so you can use <code class="code">create</code>
to get a handle for the message box in all workers accessing it.
Sending a message <code class="code">msg</code> of type <code class="code">foo</code> is as easy as</p>

<pre class="codepre"><code class="code">Mbox_foo.send mbox msg
</code></pre>
<p>and receiving one is possible with</p>

<pre class="codepre"><code class="code">let msg = Mbox_foo.receive mbox
</code></pre>
<p>which also waits for the arrival of the message. Remember that all
three functions, <code class="code">create</code>, <code class="code">send</code>, and <code class="code">receive</code> can only be called
from worker context.</p>

<h4 id="3_ExampleTaskqueue">Example: Task queue</h4>
<p>In this example, we want to parallelize a list of tasks which can be
independently run on any worker. The idea is that every worker
provides a message box where a special process, the supervisor, sends
the task descriptions to.  If all tasks are done, the supervisor sends a
termination request instead:</p>

<pre class="codepre"><code class="code">type worker_msg =
  | Task of task
  | Term_request
</code></pre>
<p>The supervisor has no idea by itself which worker is busy and which one
would be free for another task. Because of this, the supervisor installs
another message box, and the worker sends a message when it is idle
and requests another task:</p>

<pre class="codepre"><code class="code">type supervisor_msg =
  | Task_request of int
</code></pre>
<p>The integer argument is the index of the requesting worker.</p>

<p>This arrangement will result in a "ping pong game": When a worker is free
it sends <code class="code">Task_request</code> to the supervisor, which in turn will send the
next <code class="code">Task</code> to the requesting worker, or <code class="code">Term_request</code> if the list
is already empty. The interesting property is that no process actively
monitors another process - instead, all processes just wait for messages
and react on these.</p>

<p>The definitions of the mailbox modules:</p>

<pre class="codepre"><code class="code">module Mbox_worker =
  Netplex_mbox.Make_mbox_type(struct type t = worker_msg end)

module Mbox_supervisor =
  Netplex_mbox.Make_mbox_type(struct type t = supervisor_msg end)
</code></pre>
<h4 id="3_Theimplementationoftheworkers">The implementation of the workers</h4>
<p>The workers wait for the arrival of messages from the supervisor in a
loop, and react on incoming tasks. The loop is left when the
termination request arrives.</p>

<pre class="codepre"><code class="code">let worker_main w =
  (* where w is the index of the worker, 0..n-1 *)
  let wrk_mbox_name = sprintf "Worker_%d" w in
  let wrk_mbox = Mbox_worker.create wrk_mbox_name in
  let op_mbox = Mbox_supervisor.create "Supervisor" in
  let cont = ref true in
  while !cont do
    (* request a new task *)
    Mbox_supervisor.send op_mbox (Task_request w);
    (* wait for task *)
    match Mbox_worker.receive wrk_mbox with
      | Task t -&gt;
           (* do here the task *)
           ...
      | Term_request -&gt;
           cont := false
  done;
  ()

let worker_main_fork, worker_main_join =
  Netmcore_process.def_process worker_main
</code></pre>
<h4 id="3_Theimplementationofthesupervisor">The implementation of the supervisor</h4>
<p>The supervisor starts the worker processes, and also joins them
at the end.</p>

<p>There is a queue of messages to send to the workers, <code class="code">q</code>. When a
worker requests another task, the next prepared message is sent.
At the end of <code class="code">q</code> there are as many <code class="code">Term_request</code> messages as
needed to ensure that all workers will terminate.</p>

<p>Note that this version does not collect any results from the workers.
There could be extra <code class="code">Task_result</code> messages for this purpose (emitted
by the workers and interpreted by the supervisor).</p>

<pre class="codepre"><code class="code">let supervisor_main arg =
  let ((num_workers : int), (tasks : task list)) = arg in
  let op_mbox = Mbox_supervisor.create "Supervisor" in
  let q = Queue.create() in
  List.iter (fun t -&gt; Queue.add q (Task t)) tasks;
  let workers =
    Array.init
      num_workers
      (fun i -&gt; Netmcore_process.start worker_main_fork i) in
  Array.iteri
    (fun i _ -&gt; Queue.add q Term_request)
    workers;
  let wrk_mbox_names = 
    Array.mapi
      (fun i _ -&gt; sprintf "Worker_%d" i)
      workers in
  let wrk_mboxes =
    Array.map
      (fun name -&gt; Mbox_worker.create name)
      wrk_mbox_names in
  while not(Queue.is_empty q) do
    (* wait for request *)
    match Mbox_supervisor.receive op_mbox with
      | Task_request r -&gt;
          let msg = Queue.take q in
          let wrk_mbox = wrk_mboxes.(r) in
          Mbox_worker.send wrk_mbox msg;
  done;
  Array.iter
    (fun pid -&gt;
       Netmcore_process.join worker_main_join pid
    )
    workers

let supervisor_main_fork, supervisor_main_join =
  Netmcore_process.def_process supervisor_main
</code></pre>
<h4 id="3_Amainprogram">A main program</h4>
<p>The main program just starts the supervisor, and waits for its
termination:</p>

<pre class="codepre"><code class="code">let main tasks =
  let sum_opt =
    Netmcore.run
      ~socket_directory:"/tmp/netmcore"
      ~first_process:(fun () -&gt;
                        Netmcore_process.start supervisor_main_fork tasks
                     )
      ~extract_result:(fun _ pid -&gt;
                        Netmcore_process.join_nowait supervisor_main_join pid
                      )
      () in
  match sum_opt with
    | None -&gt; printf "Error\n"
    | Some () -&gt; printf "Tasks completed\n"
</code></pre>
<h3 id="goon">Where to go on from here</h3>
<p>If you want to write an Internet server, and you need Netmulticore
for managing some workload processes, you should next try to understand
Netplex in more detail. Netplex is a generic process manager with special
support for server processes. As pointed out before, Netmulticore is just
an extension of Netplex, so both libraries can be easily used together.
Read more about Netplex in: <a href="Netplex_intro.html"><code class="code">Netplex_intro</code></a>, <a href="Netplex_advanced.html"><code class="code">Netplex_advanced</code></a>,
<a href="Netplex_admin.html"><code class="code">Netplex_admin</code></a>.</p>

<p>If your focus is on the acceleration of your multicore program, the
next page to read is <a href="Netmcore_tut.html"><code class="code">Netmcore_tut</code></a>. This page explains the parts of
Netmulticore that use shared memory. In particular, the worker processes
are enabled to access shared heaps containing OCaml values. The heaps
are read/write, which is so far unique in the OCaml world. This allows
you to represent shared data, e.g. as queues, hashtables, or arrays.
The downside of these mechanisms is that unsafe and low-level OCaml
features are used, comparable to writing a wrapper for a C function.</p>
</div>
</body></html>