Changeset 20396
- Timestamp:
- 06/29/07 17:38:02 (1 year ago)
- Files:
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
juggler/branches/2.2/modules/vapor/doc/programmer.guide/programmer.guide.xml
r19951 r20396 44 44 45 45 <para>Internally, VPR wraps platform-specific APIs such as BSD sockets, 46 POSIX threads, and Win32 overlapped I/O. Depending upon how it is47 compiled, it may also wrap the <ulink46 POSIX threads, Win32 threads, and Win32 overlapped I/O. Depending upon 47 how it is compiled, it may also wrap the <ulink 48 48 url="http://www.mozilla.org/projects/nspr/index.html">Netscape Portable 49 49 Runtime</ulink> (<glossterm linkend="gloss.nspr">NSPR</glossterm>), … … 739 739 <para>The serial port abstraction is handled differently than the 740 740 other I/O abstraction components. We wrap two serial port 741 interfaces: termios and Win32 overlapped I/O<footnote> 742 <para>This is the only Win32-native code in VPR. All other 743 Win32 interfaces are handled by NSPR.</para> 744 </footnote>. Because NSPR does not provide a serial port layer, 745 we have to allow the termios to be used with NSPR on UNIX-based 746 platforms. While this makes the implementation a little clumsy and 747 the build system a little more complicated, it has little if any 748 impact on users. The point of the abstraction is to hide the 749 low-level details to provide a consistent interface across 750 platforms.</para> 741 interfaces: termios and Win32 overlapped I/O. Because NSPR does 742 not provide a serial port layer, we have to allow the termios to 743 be used with NSPR on UNIX-based platforms. While this makes the 744 implementation a little clumsy and the build system a little more 745 complicated, it has little if any impact on users. The point of 746 the abstraction is to hide the low-level details to provide a 747 consistent interface across platforms.</para> 751 748 </section> 752 749 </chapter> … … 1254 1251 <programlisting>thread->resume();</programlisting> 1255 1252 1256 <para>On successful completion, both methods return 1257 <constant>vpr::ReturnStatus::Succeed</constant>. If the 1258 operation could not be performed for some reason, 1259 <constant>vpr::ReturnStatus::Fail</constant> is returned to 1260 indicate error status.</para> 1253 <para>If something goes wrong when suspending or resuming, 1254 <exceptionname>vpr::IllegalArgumentException</exceptionname><indexterm> 1255 <primary>exceptions</primary> 1256 1257 <secondary>vpr::IllegalArgumentException</secondary> 1258 </indexterm> is thrown. Otherwise, these methods return 1259 nothing upon successful completion.</para> 1261 1260 </section> 1262 1261 … … 1300 1299 thread->setPrio(prio);</programlisting> 1301 1300 1302 <para>On successful completion, both methods return 1303 <constant>vpr::ReturnStatus::Succeed</constant>. If the 1304 operation could not be performed for some reason, 1305 <constant>vpr::ReturnStatus::Fail</constant> is returned to 1306 indicate error status.</para> 1301 <para>If something goes wrong when querying or changing the 1302 priority of the thread, 1303 <exceptionname>vpr::IllegalArgumentException</exceptionname><indexterm> 1304 <primary>exceptions</primary> 1305 1306 <secondary>vpr::IllegalArgumentException</secondary> 1307 </indexterm> is thrown. Otherwise, these methods return 1308 nothing upon successful completion.</para> 1307 1309 </section> 1308 1310 … … 1346 1348 operating system, and the thread is expected to handle it 1347 1349 properly. This version of the <methodname>kill()</methodname> 1348 method returns <constant>vpr::ReturnStatus::Succeed</constant> 1349 if the signal is sent successfully. Otherwise, 1350 <constant>vpr::ReturnStatus::Fail</constant> is returned to 1351 indicate that an error occurred.</para> 1350 method throws 1351 <exceptionname>vpr::IllegalArgumentException</exceptionname><indexterm> 1352 <primary>exceptions</primary> 1353 1354 <secondary>vpr::IllegalArgumentException</secondary> 1355 </indexterm> if an error occurs. Otherwise, this method 1356 returns nothing upon successful compltion.</para> 1352 1357 </section> 1353 1358 … … 1453 1458 around Netscape Portable Runtime threads</para> 1454 1459 </listitem> 1460 1461 <listitem> 1462 <para><classname>vpr::ThreadWin32</classname>: A wrapper 1463 around Win32 threads</para> 1464 </listitem> 1455 1465 </itemizedlist> 1456 1466 … … 1471 1481 <classname>vpr::Thread</classname> interface. For example, if 1472 1482 compiling on Win32, the class 1473 <classname>vpr::Thread NSPR</classname> is1483 <classname>vpr::ThreadWin32</classname> is 1474 1484 <type>typedef</type>'d to be 1475 1485 <classname>vpr::Thread</classname>. Since the interface is … … 1492 1502 Portable Runtime</ulink> (NSPR) threads<indexterm> 1493 1503 <primary>NSPR</primary> 1494 </indexterm>. We have removed the Win32-specific threads 1495 because NSPR already supports that implementation. Further 1496 implementations may be removed in favor of using what NSPR 1497 offers. Doing this will offload much of our efforts onto the 1498 NSPR. NSPR threads do not support all the features we have, 1499 however, because they took the lowest-common-denominator 1500 approach. As with all technology, there is a trade-off in 1501 relieving some of our work load by using an existing 1502 cross-platform thread implementation: our interface becomes 1503 limited to what features that implementation provides. It 1504 remains to be seen exactly how much of VPR's threading 1505 subsystem will be removed, and those programmers who choose 1506 to use it should be careful to watch the mailing lists for 1507 discussions and announcements about changes.</para> 1504 </indexterm>. NSPR threads do not support all the 1505 features we have, however, because they took the 1506 lowest-common-denominator approach. As with all technology, 1507 there is a trade-off in relieving some of our work load by 1508 using an existing cross-platform thread implementation: our 1509 interface becomes limited to what features that 1510 implementation provides. It remains to be seen exactly how 1511 much of VPR's threading subsystem will be removed, and those 1512 programmers who choose to use it should be careful to watch 1513 the mailing lists for discussions and announcements about 1514 changes.</para> 1508 1515 </note> 1509 1516 </section> … … 2454 2461 <para>The semaphore <varname>sema</varname> now controls access 2455 2462 to only four resources.</para> 2463 2464 <para>Creation of a semaphore can fail, and if it does, the 2465 <classname>vpr::Semaphore</classname> constructor throws an 2466 exception of type 2467 <exceptionname>vpr::ResourceException</exceptionname><indexterm> 2468 <primary>exceptions</primary> 2469 2470 <secondary>vpr::ResourceException</secondary> 2471 </indexterm>. If creation of the semaphore succeeds, then 2472 lock and unlock operations on that semaphore are guaranteed to 2473 succeed.</para> 2456 2474 </section> 2457 2475 … … 2475 2493 blocking call, so if the semaphore's value is less than or 2476 2494 equal to 0, the thread requesting the lock will block until the 2477 semaphore's value is greater than 0. If the lock is acquired, 2478 <constant>vpr::ReturnStatus::Succeed</constant> is returned. If 2479 the attempt to lock the semaphore fails for some reason, 2480 <constant>vpr::ReturnStatus::Fail</constant> is 2481 returned.</para> 2495 semaphore's value is greater than 0. If the acquisition of the 2496 semaphore would cause a deadlock (because the thread already 2497 holds the semaphore lock), 2498 <exceptionname>vpr::DeadlockException</exceptionname><indexterm> 2499 <primary>exceptions</primary> 2500 2501 <secondary>vpr::DeadlockException</secondary> 2502 </indexterm> is thrown. Otherwise, the method is guaranteed 2503 to return when the lock is acquired.</para> 2482 2504 </section> 2483 2505 … … 2497 2519 <programlisting>sema.release();</programlisting> 2498 2520 2499 <para>If the locked semaphore is released successfully, 2500 <constant>vpr::ReturnStatus::Succeed</constant> is returned. 2501 Otherwise, <constant>vpr::ReturnStatus::Fail</constant> is 2502 returned.</para> 2521 <para>This method is guaranteed to return successfully.</para> 2503 2522 </section> 2504 2523 … … 2535 2554 <para><classname>vpr::SemaphoreNSPR</classname>: An 2536 2555 implementation of semaphores using NSPR primitives</para> 2556 </listitem> 2557 2558 <listitem> 2559 <para><classname>vpr::SemaphoreWin32</classname>: A 2560 wrapper around Win32 semaphores</para> 2537 2561 </listitem> 2538 2562 </itemizedlist> … … 2625 2649 <programlisting>vpr::Mutex mutex;</programlisting> 2626 2650 2627 <para>There is nothing more to say this time.</para> 2651 <para>Creation of a mutex can fail, and if it does, the 2652 <classname>vpr::Mutex</classname> constructor throws an 2653 exception of type 2654 <exceptionname>vpr::ResourceException</exceptionname><indexterm> 2655 <primary>exceptions</primary> 2656 2657 <secondary>vpr::ResourceException</secondary> 2658 </indexterm>. If creation of the mutex succeeds, then lock 2659 and unlock operations on that mutex are guaranteed to 2660 succeed.</para> 2628 2661 </section> 2629 2662 … … 2647 2680 blocking call, so if the mutex is already locked by another 2648 2681 thread, the thread requesting the lock will block until the 2649 mutex is released by the other thread. If the lock is acquired, 2650 <constant>vpr::ReturnStatus::Succeed</constant> is returned. If 2651 the attempt to lock the semaphore fails for some reason, 2652 <constant>vpr::ReturnStatus::Fail</constant> is 2653 returned.</para> 2682 mutex is released by the other thread. If the acquisition of 2683 the mutex would cause a deadlock (because the thread already 2684 holds the mutex), 2685 <exceptionname>vpr::DeadlockException</exceptionname><indexterm> 2686 <primary>exceptions</primary> 2687 2688 <secondary>vpr::DeadlockException</secondary> 2689 </indexterm> is thrown. Otherwise, the method is guaranteed 2690 to return when the lock is acquired.</para> 2654 2691 </section> 2655 2692 … … 2671 2708 as follows:</para> 2672 2709 2673 <programlisting>mutex.tryAcquire();</programlisting> 2674 2675 <para>If the mutex is locked, 2676 <constant>vpr::ReturnStatus::Succeed</constant> is returned. 2677 Otherwise, <constant>vpr::ReturnStatus::Fail</constant> is 2678 returned. The call does not block.</para> 2710 <programlisting>const bool locked = mutex.tryAcquire();</programlisting> 2711 2712 <para>If the mutex is locked, <constant>true</constant> is 2713 returned. Otherwise, <constant>false</constant> is returned. 2714 The call does not block.</para> 2679 2715 </section> 2680 2716 … … 2693 2729 follows:</para> 2694 2730 2695 <programlisting> int state= mutex.test();</programlisting>2731 <programlisting>const bool locked = mutex.test();</programlisting> 2696 2732 2697 2733 <para>If the mutex is <emphasis>not</emphasis> locked, 2698 <constant>vpr::ReturnStatus::Fail</constant> is returned. 2699 Otherwise, <constant>vpr::ReturnStatus::Succeed</constant> is 2700 returned.</para> 2734 <constant>false</constant> is returned. Otherwise, 2735 <constant>true</constant> is returned.</para> 2701 2736 </section> 2702 2737 … … 2716 2751 <programlisting>mutex.release();</programlisting> 2717 2752 2718 <para>If the locked mutex is released successfully, 2719 <constant>vpr::ReturnStatus::Succeed</constant> is returned. 2720 Otherwise, <constant>vpr::ReturnStatus::Fail</constant> is 2721 returned.</para> 2753 <para>This method is guaranteed to return successfully.</para> 2722 2754 </section> 2723 2755 … … 2756 2788 <para><classname>vpr::MutexNSPR</classname>: A wrapper 2757 2789 around NSPR mutexes</para> 2790 </listitem> 2791 2792 <listitem> 2793 <para><classname>vpr::MutexWin32</classname>: A wrapper 2794 around Win32 mutexes</para> 2758 2795 </listitem> 2759 2796 </itemizedlist> … … 2850 2887 <programlisting>vpr::CondVar cv;</programlisting> 2851 2888 2889 <para>Creation of a condition variable can fail, and if it 2890 does, the <classname>vpr::CondVar</classname> constructor 2891 throws an exception of type 2892 <exceptionname>vpr::ResourceException</exceptionname><indexterm> 2893 <primary>exceptions</primary> 2894 2895 <secondary>vpr::ResourceException</secondary> 2896 </indexterm>. If creation of the condition variable 2897 succeeds, then lock and unlock operations on that condition 2898 variable are guaranteed to succeed.</para> 2899 2852 2900 <para>In addition to the <classname>vpr::CondVar</classname> 2853 2901 instance, there is usually some associated variable whose value … … 2879 2927 locked by another thread, the thread requesting the lock will 2880 2928 block until the mutex is released by the other thread. If the 2881 lock is acquired, 2882 <constant>vpr::ReturnStatus::Succeed</constant> is returned. If 2883 the attempt to lock the semaphore fails for some reason, 2884 <constant>vpr::ReturnStatus::Fail</constant> is 2885 returned.</para> 2929 acquisition of the condition variable's lock would cause a 2930 deadlock (because the thread already holds the lock), 2931 <exceptionname>vpr::DeadlockException</exceptionname><indexterm> 2932 <primary>exceptions</primary> 2933 2934 <secondary>vpr::DeadlockException</secondary> 2935 </indexterm> is thrown. Otherwise, the method is guaranteed 2936 to return when the lock is acquired.</para> 2937 </section> 2938 2939 <section> 2940 <title>Attempting to Lock a Condition Variable</title> 2941 2942 <indexterm> 2943 <primary>vpr::CondVar</primary> 2944 2945 <secondary>locking</secondary> 2946 2947 <tertiary>without blocking</tertiary> 2948 </indexterm> 2949 2950 <para>If there is a need to lock a condition variable's mutex 2951 only when the call would <emphasis>not</emphasis> block, a 2952 method is provided to do this. It is called 2953 <methodname>tryAcquire()</methodname>, and it will not block if 2954 the mutex is already locked. It works as follows:</para> 2955 2956 <programlisting>const bool locked = cv.tryAcquire();</programlisting> 2957 2958 <para>If the condition variable's mutex is locked, 2959 <constant>true</constant> is returned. Otherwise, 2960 <constant>false</constant> is returned. The call does not 2961 block.</para> 2886 2962 </section> 2887 2963 … … 2901 2977 <programlisting>cv.release();</programlisting> 2902 2978 2903 <para>If the locked condition variable is released 2904 successfully, <constant>vpr::ReturnStatus::Succeed</constant> 2905 is returned. Otherwise, 2906 <constant>vpr::ReturnStatus::Fail</constant> is 2907 returned.</para> 2979 <para>This method is guaranteed to return successfully.</para> 2908 2980 2909 2981 <para>Prior to calling … … 3046 3118 the handler for the given set of signals with the operating 3047 3119 system.</para> 3048 3049 <para>At this time, the signal handling abstraction has not been put3050 into use in any Juggler Project code. As such, it is not well tested,3051 and it should be considered a work in progress.</para>3052 3120 </chapter> 3053 3121 </part> … … 3083 3151 actually uses instances of those classes. The use of sim sockets 3084 3152 versus real sockets is made when VPR is compiled, in the same way 3085 that the threading abstraction (NSPR versus POSIX versus SPROC) is3086 chosen at compile time. Ideally, user code should not have to3087 change at all to use sim sockets, thus making it possible to test3088 network algorithms with exactly the same code as would be used3089 w ith real sockets.</para>3153 that the threading abstraction (NSPR versus POSIX versus Win32 3154 versus SPROC) is chosen at compile time. Ideally, user code should 3155 not have to change at all to use sim sockets, thus making it 3156 possible to test network algorithms with exactly the same code as 3157 would be used with real sockets.</para> 3090 3158 3091 3159 <para>Of course, the real world is not ideal. While it is very … … 3252 3320 3253 3321 <para>A <firstterm>singleton</firstterm> is a common design pattern 3254 <xref linkend="ref.design.patterns" />. In VPR, the class 3322 <xref linkend="ref.design.patterns" />. VPR offers two ways to 3323 implement a singleton, both of which come from the header 3324 <filename>vpr/Util/Singleton.h</filename>. Libraries such as <ulink 3325 url="http://sourceforge.net/projects/loki-lib/">Loki</ulink> offer 3326 other approaches.</para> 3327 3328 <para>The first way of creating a singleton using VPR is to use the C 3329 preprocessor approach. In this case, we utilize two macros: 3330 <literal>vprSingletonHeader()</literal><indexterm> 3331 <primary>macros</primary> 3332 3333 <secondary>vprSingletonHeader</secondary> 3334 </indexterm> and <literal>vprSingletonImp()</literal><indexterm> 3335 <primary>macros</primary> 3336 3337 <secondary>vprSingletonImp</secondary> 3338 </indexterm>. One goes in the class declaration (in the header 3339 file), and the other goes with the class definition (in the 3340 <filename>.cpp</filename> file). The basic usage of these macros is 3341 shown in <xref linkend="example.vprSingletonHeader" /> and <xref 3342 linkend="example.vprSingletonImp" />.</para> 3343 3344 <example id="example.vprSingletonHeader"> 3345 <title>Use of <literal>vprSingletonHeader()</literal></title> 3346 3347 <programlisting>#include <boost/noncopyable.hpp> 3348 #include <vpr/Util/Singleton.h> 3349 3350 class MySingleton : boost::noncopyable 3351 { 3352 public: 3353 // Public operations ... 3354 void doSomething(); 3355 3356 private: 3357 // Prevent instantiation by user code. 3358 MySingleton() 3359 { 3360 // Some constructor actions ... 3361 } 3362 3363 // Bring in the singleton declaration pieces. 3364 vprSingletonHeader(MySingleton); 3365 };</programlisting> 3366 </example> 3367 3368 <example id="example.vprSingletonImp"> 3369 <title>Use of <literal>vprSingletonImp()</literal></title> 3370 3371 <programlisting>#include "MySingleton.h" 3372 3373 // Bring in the singleton definition pieces. 3374 vprSingletonImp(MySingleton); 3375 3376 void MySingleton::doSomething() 3377 { 3378 // Do something ... 3379 }</programlisting> 3380 </example> 3381 3382 <para>In some cases, an object may need to perform some 3383 initialization steps after being instantiated. In that case, use the 3384 macro <literal>vprSingletonHeaderWithInitFunc()</literal><indexterm> 3385 <primary>macros</primary> 3386 3387 <secondary>vprSingletonHeaderWithInitFunc</secondary> 3388 </indexterm> instead of <literal>vprSingletonHeader()</literal> 3389 and <literal>vprSingletonImpWithInitFunc()</literal><indexterm> 3390 <primary>macros</primary> 3391 3392 <secondary>vprSingletonImpWithInitFunc</secondary> 3393 </indexterm> instead of <literal>vprSingletonImp()</literal>. An 3394 example of this is shown in <xref 3395 linkend="example.vprSingletonHeaderWithInitFunc" /> and <xref 3396 linkend="example.vprSingletonImpWithInitFunc" />. The initialization 3397 method must take no parameters and return nothing.</para> 3398 3399 <example id="example.vprSingletonHeaderWithInitFunc"> 3400 <title>Use of 3401 <literal>vprSingletonHeaderWithInitFunc()</literal></title> 3402 3403 <programlisting>#include <boost/noncopyable.hpp> 3404 #include <vpr/Util/Singleton.h> 3405 3406 class MySingleton : boost::noncopyable 3407 { 3408 public: 3409 // Public operations ... 3410 void doSomething(); 3411 3412 private: 3413 // Prevent instantiation by user code. 3414 MySingleton() 3415 { 3416 // Some constructor actions ... 3417 } 3418 3419 void init() 3420 { 3421 // Perform initialization operations ... 3422 } 3423 3424 // Bring in the singleton declaration pieces. 3425 vprSingletonHeaderWithInitFunc(MySingleton, init); 3426 };</programlisting> 3427 </example> 3428 3429 <example id="example.vprSingletonImpWithInitFunc"> 3430 <title>Use of 3431 <literal>vprSingletonImpWithInitFunc()</literal></title> 3432 3433 <programlisting>#include "MySingleton.h" 3434 3435 // Bring in the singleton definition pieces. 3436 vprSingletonImp(MySingleton); 3437 3438 void MySingleton::doSomething() 3439 { 3440 // Do something ... 3441 }</programlisting> 3442 </example> 3443 3444 <para>In VPR 1.1 and beyond, the destructor for singleton objects is 3445 called when the application exits. This can result in some 3446 undesirable behavior if one singleton depends on another, thus 3447 meaning that the order in which they are deleted can make a 3448 difference. Without any specific ordering being imposed, singleton 3449 objects are deleted in the reverse order in which they were 3450 instantiated. Since it is not always possible to control the order of 3451 instantiation, it can be useful to control the order of destruction. 3452 This can be done by setting the <quote>lifetime</quote> or 3453 <quote>longevity</quote> of a singleton. The higher the longevity, 3454 the later that the singleton will be deleted when the application is 3455 exiting. This is accomplished using either 3456 <literal>vprSingletonImpLifetime()</literal><indexterm> 3457 <primary>macros</primary> 3458 3459 <secondary>vprSingletonImpLifetime</secondary> 3460 </indexterm> or 3461 <literal>vprSingletonImpLifetimeWithInitFunc()</literal><indexterm> 3462 <primary>macros</primary> 3463 3464 <secondary>vprSingletonImpLifetimeWithInitFunc</secondary> 3465 </indexterm>. The macro used must be paired with either 3466 <literal>vprSingletonHeader()</literal> or 3467 <literal>vprSingletonHeaderWithInitFunc()</literal>. Usage of 3468 <literal>vprSingletonImpLifetime()</literal> is shown in <xref 3469 linkend="example.vprSingletonImpLifetime" />.</para> 3470 3471 <example id="example.vprSingletonImpLifetime"> 3472 <title>Use of <literal>vprSingletonImpLifetime()</literal></title> 3473 3474 <programlisting>#include "MySingleton.h" 3475 3476 // Bring in the singleton definition pieces. Delete it early 3477 // in the application exit process. 3478 vprSingletonImpLifetime(MySingleton, 10); 3479 3480 void MySingleton::doSomething() 3481 { 3482 // Do something ... 3483 }</programlisting> 3484 </example> 3485 3486 <para>The other approach uses a template class called 3255 3487 <classname>vpr::Singleton<T><indexterm> 3256 3488 <primary>classes</primary> 3257 3489 3258 3490 <secondary>vpr::Singleton<T></secondary> 3259 </indexterm></classname> provides a template-based implementation 3260 of this pattern. An example of its use is as follows:</para> 3261 3262 <programlisting linenumbering="numbered">class MySingleton : public vpr::Singleton<MySingleton> 3491 </indexterm></classname>. An example of its use is shown in <xref 3492 linkend="example.vpr.Singleton.template" />.</para> 3493 3494 <example id="example.vpr.Singleton.template"> 3495 <title>Use of 3496 <classname>vpr::Singleton<T></classname></title> 3497 3498 <programlisting linenumbering="numbered">#include <boost/noncopyable.hpp> 3499 #include <vpr/Util/Singleton.h> 3500 3501 class MySingleton 3502 : public vpr::Singleton<MySingleton> 3503 , boost::noncopyable 3263 3504 { 3264 3505 public: … … 3278 3519 // Some constructor actions ... 3279 3520 } 3280 3281 // Prevent copying.3282 MySingleton(const MySingleton& o)3283 {3284 ;3285 }3286 3287 MySingleton& operator=(const MySingleton& o)3288 {3289 ;3290 }3291 3521 };</programlisting> 3292 3293 <para>Getting a reference to the singleton (and calling the 3522 </example> 3523 3524 <warning> 3525 <para>Singletons implemented using 3526 <classname>vpr::Singleton<T></classname> do not work across 3527 DLLs on <productname class="registered">Microsoft 3528 Windows</productname> or shared libraries 3529 (<filename>.dylib</filename> files) on Mac OS X. If the singleton 3530 is for internal use only, such as in the case of an application, 3531 then using <classname>vpr::Singleton<T></classname> is fine. 3532 Otherwise, the C preprocessor approach must be used.</para> 3533 </warning> 3534 3535 <para>In all singleton implementations, getting a reference to the 3536 singleton object (and calling the 3294 3537 <methodname>doSomething()</methodname> method) is then done using the 3295 3538 following syntax:</para> 3296 3539 3297 3540 <programlisting>MySingleton::instance()->doSomething();</programlisting> 3298 3299 <para>There is some old code in3300 <filename>vpr/Util/Singleton.h</filename> for a singleton3301 implementation based on macros defined by the C preprocessor. We3302 maintain this code for backwards compatibility, but we recommend that3303 new code use the template-based implementation described3304 above.</para>3305 3541 </chapter> 3306 3542 … … 3404 3640 termios serial I/O. When compiling on Windows, where only NSPR is 3405 3641 used, the domain specifies the use of native Win32 serial I/O and 3406 NSPR for everything else. Moreover, the simulated sockets can be 3407 mixed with any of the threading subsystems using this 3408 paradigm.</para> 3642 NSPR for socket I/O. Moreover, the simulated sockets can be mixed 3643 with any of the threading subsystems using this paradigm.</para> 3409 3644 </appendix> 3410 3645
