summaryrefslogtreecommitdiffstats
path: root/nsprpub/pr
diff options
context:
space:
mode:
Diffstat (limited to 'nsprpub/pr')
-rw-r--r--nsprpub/pr/Makefile.in17
-rw-r--r--nsprpub/pr/include/Makefile.in27
-rw-r--r--nsprpub/pr/include/gencfg.c265
-rw-r--r--nsprpub/pr/include/md/Makefile.in42
-rw-r--r--nsprpub/pr/include/md/_aix.h225
-rw-r--r--nsprpub/pr/include/md/_aix32.cfg115
-rw-r--r--nsprpub/pr/include/md/_aix64.cfg116
-rw-r--r--nsprpub/pr/include/md/_bsdi.cfg170
-rw-r--r--nsprpub/pr/include/md/_bsdi.h181
-rw-r--r--nsprpub/pr/include/md/_darwin.cfg165
-rw-r--r--nsprpub/pr/include/md/_darwin.h308
-rw-r--r--nsprpub/pr/include/md/_freebsd.cfg594
-rw-r--r--nsprpub/pr/include/md/_freebsd.h255
-rw-r--r--nsprpub/pr/include/md/_hpux.h288
-rw-r--r--nsprpub/pr/include/md/_hpux32.cfg112
-rw-r--r--nsprpub/pr/include/md/_hpux64.cfg113
-rw-r--r--nsprpub/pr/include/md/_linux.cfg1365
-rw-r--r--nsprpub/pr/include/md/_linux.h724
-rw-r--r--nsprpub/pr/include/md/_netbsd.cfg351
-rw-r--r--nsprpub/pr/include/md/_netbsd.h230
-rw-r--r--nsprpub/pr/include/md/_nspr_pthread.h251
-rw-r--r--nsprpub/pr/include/md/_nto.cfg120
-rw-r--r--nsprpub/pr/include/md/_nto.h188
-rw-r--r--nsprpub/pr/include/md/_openbsd.cfg353
-rw-r--r--nsprpub/pr/include/md/_openbsd.h213
-rw-r--r--nsprpub/pr/include/md/_os2.cfg121
-rw-r--r--nsprpub/pr/include/md/_os2.h504
-rw-r--r--nsprpub/pr/include/md/_os2_errors.h128
-rw-r--r--nsprpub/pr/include/md/_pcos.h51
-rw-r--r--nsprpub/pr/include/md/_pth.h186
-rw-r--r--nsprpub/pr/include/md/_qnx.cfg64
-rw-r--r--nsprpub/pr/include/md/_qnx.h182
-rw-r--r--nsprpub/pr/include/md/_riscos.cfg111
-rw-r--r--nsprpub/pr/include/md/_riscos.h176
-rw-r--r--nsprpub/pr/include/md/_scoos.cfg108
-rw-r--r--nsprpub/pr/include/md/_scoos.h171
-rw-r--r--nsprpub/pr/include/md/_solaris.cfg171
-rw-r--r--nsprpub/pr/include/md/_solaris.h493
-rw-r--r--nsprpub/pr/include/md/_unix_errors.h139
-rw-r--r--nsprpub/pr/include/md/_unixos.h620
-rw-r--r--nsprpub/pr/include/md/_unixware.cfg108
-rw-r--r--nsprpub/pr/include/md/_unixware.h186
-rw-r--r--nsprpub/pr/include/md/_unixware7.cfg112
-rw-r--r--nsprpub/pr/include/md/_win32_errors.h122
-rw-r--r--nsprpub/pr/include/md/_win95.cfg321
-rw-r--r--nsprpub/pr/include/md/_win95.h549
-rw-r--r--nsprpub/pr/include/md/_winnt.cfg224
-rw-r--r--nsprpub/pr/include/md/_winnt.h599
-rw-r--r--nsprpub/pr/include/md/prosdep.h96
-rw-r--r--nsprpub/pr/include/nspr.h43
-rw-r--r--nsprpub/pr/include/obsolete/Makefile.in28
-rw-r--r--nsprpub/pr/include/obsolete/pralarm.h162
-rw-r--r--nsprpub/pr/include/obsolete/probslet.h149
-rw-r--r--nsprpub/pr/include/obsolete/protypes.h183
-rw-r--r--nsprpub/pr/include/obsolete/prsem.h64
-rw-r--r--nsprpub/pr/include/pratom.h198
-rw-r--r--nsprpub/pr/include/prbit.h152
-rw-r--r--nsprpub/pr/include/prclist.h108
-rw-r--r--nsprpub/pr/include/prcmon.h66
-rw-r--r--nsprpub/pr/include/prcountr.h525
-rw-r--r--nsprpub/pr/include/prcvar.h94
-rw-r--r--nsprpub/pr/include/prdtoa.h51
-rw-r--r--nsprpub/pr/include/prenv.h162
-rw-r--r--nsprpub/pr/include/prerr.h249
-rw-r--r--nsprpub/pr/include/prerror.h294
-rw-r--r--nsprpub/pr/include/prinet.h99
-rw-r--r--nsprpub/pr/include/prinit.h215
-rw-r--r--nsprpub/pr/include/prinrval.h146
-rw-r--r--nsprpub/pr/include/prio.h2016
-rw-r--r--nsprpub/pr/include/pripcsem.h101
-rw-r--r--nsprpub/pr/include/private/Makefile.in29
-rw-r--r--nsprpub/pr/include/private/pprio.h242
-rw-r--r--nsprpub/pr/include/private/pprmwait.h103
-rw-r--r--nsprpub/pr/include/private/pprthred.h311
-rw-r--r--nsprpub/pr/include/private/primpl.h2149
-rw-r--r--nsprpub/pr/include/private/prpriv.h16
-rw-r--r--nsprpub/pr/include/prlink.h230
-rw-r--r--nsprpub/pr/include/prlock.h109
-rw-r--r--nsprpub/pr/include/prlog.h226
-rw-r--r--nsprpub/pr/include/prlong.h403
-rw-r--r--nsprpub/pr/include/prmem.h126
-rw-r--r--nsprpub/pr/include/prmon.h96
-rw-r--r--nsprpub/pr/include/prmwait.h380
-rw-r--r--nsprpub/pr/include/prnetdb.h475
-rw-r--r--nsprpub/pr/include/prolock.h178
-rw-r--r--nsprpub/pr/include/prpdce.h86
-rw-r--r--nsprpub/pr/include/prprf.h122
-rw-r--r--nsprpub/pr/include/prproces.h86
-rw-r--r--nsprpub/pr/include/prrng.h75
-rw-r--r--nsprpub/pr/include/prrwlock.h88
-rw-r--r--nsprpub/pr/include/prshm.h257
-rw-r--r--nsprpub/pr/include/prshma.h239
-rw-r--r--nsprpub/pr/include/prsystem.h109
-rw-r--r--nsprpub/pr/include/prthread.h272
-rw-r--r--nsprpub/pr/include/prtime.h262
-rw-r--r--nsprpub/pr/include/prtpool.h83
-rw-r--r--nsprpub/pr/include/prtrace.h646
-rw-r--r--nsprpub/pr/include/prtypes.h561
-rwxr-xr-xnsprpub/pr/include/prvrsion.h105
-rw-r--r--nsprpub/pr/include/prwin16.h164
-rw-r--r--nsprpub/pr/src/Makefile.in355
-rw-r--r--nsprpub/pr/src/cplus/Makefile.in43
-rw-r--r--nsprpub/pr/src/cplus/rcascii.h143
-rw-r--r--nsprpub/pr/src/cplus/rcbase.cpp31
-rw-r--r--nsprpub/pr/src/cplus/rcbase.h55
-rw-r--r--nsprpub/pr/src/cplus/rccv.cpp70
-rw-r--r--nsprpub/pr/src/cplus/rccv.h64
-rw-r--r--nsprpub/pr/src/cplus/rcfileio.cpp247
-rw-r--r--nsprpub/pr/src/cplus/rcfileio.h131
-rw-r--r--nsprpub/pr/src/cplus/rcinrval.cpp37
-rw-r--r--nsprpub/pr/src/cplus/rcinrval.h191
-rw-r--r--nsprpub/pr/src/cplus/rcio.cpp14
-rw-r--r--nsprpub/pr/src/cplus/rcio.h117
-rw-r--r--nsprpub/pr/src/cplus/rclock.cpp42
-rw-r--r--nsprpub/pr/src/cplus/rclock.h74
-rw-r--r--nsprpub/pr/src/cplus/rcmon.h47
-rw-r--r--nsprpub/pr/src/cplus/rcnetdb.cpp233
-rw-r--r--nsprpub/pr/src/cplus/rcnetdb.h99
-rw-r--r--nsprpub/pr/src/cplus/rcnetio.cpp225
-rw-r--r--nsprpub/pr/src/cplus/rcnetio.h94
-rw-r--r--nsprpub/pr/src/cplus/rcthread.cpp221
-rw-r--r--nsprpub/pr/src/cplus/rcthread.h195
-rw-r--r--nsprpub/pr/src/cplus/rctime.cpp56
-rw-r--r--nsprpub/pr/src/cplus/rctime.h136
-rw-r--r--nsprpub/pr/src/cplus/tests/Makefile.in199
-rw-r--r--nsprpub/pr/src/cplus/tests/fileio.cpp33
-rw-r--r--nsprpub/pr/src/cplus/tests/interval.cpp101
-rw-r--r--nsprpub/pr/src/cplus/tests/ranfile.cpp449
-rw-r--r--nsprpub/pr/src/cplus/tests/switch.cpp248
-rw-r--r--nsprpub/pr/src/cplus/tests/thread.cpp110
-rw-r--r--nsprpub/pr/src/cplus/tests/time.cpp29
-rw-r--r--nsprpub/pr/src/cplus/tests/tpd.cpp353
-rw-r--r--nsprpub/pr/src/io/Makefile.in50
-rw-r--r--nsprpub/pr/src/io/prdir.c139
-rw-r--r--nsprpub/pr/src/io/prfdcach.c252
-rw-r--r--nsprpub/pr/src/io/prfile.c818
-rw-r--r--nsprpub/pr/src/io/prio.c257
-rw-r--r--nsprpub/pr/src/io/priometh.c607
-rw-r--r--nsprpub/pr/src/io/pripv6.c372
-rw-r--r--nsprpub/pr/src/io/prlayer.c785
-rw-r--r--nsprpub/pr/src/io/prlog.c572
-rw-r--r--nsprpub/pr/src/io/prmapopt.c530
-rw-r--r--nsprpub/pr/src/io/prmmap.c68
-rw-r--r--nsprpub/pr/src/io/prmwait.c1534
-rw-r--r--nsprpub/pr/src/io/prpolevt.c230
-rw-r--r--nsprpub/pr/src/io/prprf.c1316
-rw-r--r--nsprpub/pr/src/io/prscanf.c630
-rw-r--r--nsprpub/pr/src/io/prsocket.c2024
-rw-r--r--nsprpub/pr/src/io/prstdio.c74
-rw-r--r--nsprpub/pr/src/linking/Makefile.in31
-rw-r--r--nsprpub/pr/src/linking/prlink.c1142
-rw-r--r--nsprpub/pr/src/malloc/Makefile.in28
-rw-r--r--nsprpub/pr/src/malloc/prmalloc.c1200
-rw-r--r--nsprpub/pr/src/malloc/prmem.c678
-rw-r--r--nsprpub/pr/src/md/Makefile.in32
-rw-r--r--nsprpub/pr/src/md/os2/Makefile.in47
-rw-r--r--nsprpub/pr/src/md/os2/objs.mk27
-rw-r--r--nsprpub/pr/src/md/os2/os2_errors.c1095
-rw-r--r--nsprpub/pr/src/md/os2/os2cv.c334
-rw-r--r--nsprpub/pr/src/md/os2/os2emx.s82
-rw-r--r--nsprpub/pr/src/md/os2/os2gc.c58
-rw-r--r--nsprpub/pr/src/md/os2/os2inrval.c81
-rw-r--r--nsprpub/pr/src/md/os2/os2io.c968
-rw-r--r--nsprpub/pr/src/md/os2/os2misc.c620
-rw-r--r--nsprpub/pr/src/md/os2/os2poll.c361
-rw-r--r--nsprpub/pr/src/md/os2/os2rng.c83
-rw-r--r--nsprpub/pr/src/md/os2/os2sem.c63
-rw-r--r--nsprpub/pr/src/md/os2/os2sock.c669
-rw-r--r--nsprpub/pr/src/md/os2/os2thred.c353
-rw-r--r--nsprpub/pr/src/md/os2/os2vaclegacy.s45
-rw-r--r--nsprpub/pr/src/md/prosdep.c69
-rw-r--r--nsprpub/pr/src/md/unix/Makefile.in99
-rw-r--r--nsprpub/pr/src/md/unix/aix.c302
-rw-r--r--nsprpub/pr/src/md/unix/aixwrap.c33
-rw-r--r--nsprpub/pr/src/md/unix/bsdi.c87
-rw-r--r--nsprpub/pr/src/md/unix/darwin.c113
-rw-r--r--nsprpub/pr/src/md/unix/dgux.c77
-rw-r--r--nsprpub/pr/src/md/unix/freebsd.c87
-rw-r--r--nsprpub/pr/src/md/unix/hpux.c231
-rw-r--r--nsprpub/pr/src/md/unix/linux.c91
-rw-r--r--nsprpub/pr/src/md/unix/netbsd.c89
-rw-r--r--nsprpub/pr/src/md/unix/nto.c34
-rw-r--r--nsprpub/pr/src/md/unix/objs.mk31
-rw-r--r--nsprpub/pr/src/md/unix/openbsd.c89
-rw-r--r--nsprpub/pr/src/md/unix/os_AIX.s91
-rw-r--r--nsprpub/pr/src/md/unix/os_BSD_386_2.s42
-rw-r--r--nsprpub/pr/src/md/unix/os_Darwin.s13
-rw-r--r--nsprpub/pr/src/md/unix/os_Darwin_ppc.s68
-rw-r--r--nsprpub/pr/src/md/unix/os_Darwin_x86.s80
-rw-r--r--nsprpub/pr/src/md/unix/os_Darwin_x86_64.s67
-rw-r--r--nsprpub/pr/src/md/unix/os_HPUX.s25
-rw-r--r--nsprpub/pr/src/md/unix/os_HPUX_ia64.s80
-rw-r--r--nsprpub/pr/src/md/unix/os_Linux_ia64.s71
-rw-r--r--nsprpub/pr/src/md/unix/os_Linux_ppc.s75
-rw-r--r--nsprpub/pr/src/md/unix/os_Linux_x86.s85
-rw-r--r--nsprpub/pr/src/md/unix/os_Linux_x86_64.s74
-rw-r--r--nsprpub/pr/src/md/unix/os_SunOS_sparcv9.s173
-rw-r--r--nsprpub/pr/src/md/unix/os_SunOS_ultrasparc.s173
-rw-r--r--nsprpub/pr/src/md/unix/os_SunOS_x86.s126
-rw-r--r--nsprpub/pr/src/md/unix/os_SunOS_x86_64.s63
-rw-r--r--nsprpub/pr/src/md/unix/pthreads_user.c464
-rw-r--r--nsprpub/pr/src/md/unix/qnx.c70
-rw-r--r--nsprpub/pr/src/md/unix/riscos.c88
-rw-r--r--nsprpub/pr/src/md/unix/scoos.c150
-rw-r--r--nsprpub/pr/src/md/unix/solaris.c161
-rw-r--r--nsprpub/pr/src/md/unix/unix.c3744
-rw-r--r--nsprpub/pr/src/md/unix/unix_errors.c833
-rw-r--r--nsprpub/pr/src/md/unix/unixware.c559
-rw-r--r--nsprpub/pr/src/md/unix/uxpoll.c722
-rw-r--r--nsprpub/pr/src/md/unix/uxproces.c877
-rw-r--r--nsprpub/pr/src/md/unix/uxrng.c147
-rw-r--r--nsprpub/pr/src/md/unix/uxshm.c632
-rw-r--r--nsprpub/pr/src/md/unix/uxwrap.c512
-rw-r--r--nsprpub/pr/src/md/windows/Makefile.in72
-rw-r--r--nsprpub/pr/src/md/windows/ntdllmn.c57
-rw-r--r--nsprpub/pr/src/md/windows/ntgc.c98
-rw-r--r--nsprpub/pr/src/md/windows/ntinrval.c51
-rw-r--r--nsprpub/pr/src/md/windows/ntio.c4761
-rw-r--r--nsprpub/pr/src/md/windows/ntmisc.c1230
-rw-r--r--nsprpub/pr/src/md/windows/ntsec.c279
-rw-r--r--nsprpub/pr/src/md/windows/ntsem.c52
-rw-r--r--nsprpub/pr/src/md/windows/ntthread.c590
-rw-r--r--nsprpub/pr/src/md/windows/objs.mk48
-rw-r--r--nsprpub/pr/src/md/windows/w32ipcsem.c230
-rw-r--r--nsprpub/pr/src/md/windows/w32poll.c342
-rw-r--r--nsprpub/pr/src/md/windows/w32rng.c80
-rw-r--r--nsprpub/pr/src/md/windows/w32shm.c348
-rw-r--r--nsprpub/pr/src/md/windows/w95cv.c371
-rw-r--r--nsprpub/pr/src/md/windows/w95dllmain.c40
-rw-r--r--nsprpub/pr/src/md/windows/w95io.c1416
-rw-r--r--nsprpub/pr/src/md/windows/w95sock.c851
-rw-r--r--nsprpub/pr/src/md/windows/w95thred.c381
-rw-r--r--nsprpub/pr/src/md/windows/win32_errors.c541
-rw-r--r--nsprpub/pr/src/memory/Makefile.in29
-rw-r--r--nsprpub/pr/src/memory/prseg.c61
-rw-r--r--nsprpub/pr/src/memory/prshm.c128
-rw-r--r--nsprpub/pr/src/memory/prshma.c110
-rw-r--r--nsprpub/pr/src/misc/Makefile.in80
-rw-r--r--nsprpub/pr/src/misc/compile-et.pl108
-rw-r--r--nsprpub/pr/src/misc/dtoa.c4610
-rw-r--r--nsprpub/pr/src/misc/pralarm.c263
-rw-r--r--nsprpub/pr/src/misc/pratom.c384
-rw-r--r--nsprpub/pr/src/misc/praton.c217
-rw-r--r--nsprpub/pr/src/misc/prcountr.c483
-rw-r--r--nsprpub/pr/src/misc/prdtoa.c3697
-rw-r--r--nsprpub/pr/src/misc/prenv.c170
-rw-r--r--nsprpub/pr/src/misc/prerr.c97
-rw-r--r--nsprpub/pr/src/misc/prerr.et108
-rw-r--r--nsprpub/pr/src/misc/prerr.properties85
-rw-r--r--nsprpub/pr/src/misc/prerror.c78
-rw-r--r--nsprpub/pr/src/misc/prerrortable.c209
-rw-r--r--nsprpub/pr/src/misc/prinit.c857
-rw-r--r--nsprpub/pr/src/misc/prinrval.c122
-rw-r--r--nsprpub/pr/src/misc/pripc.c100
-rw-r--r--nsprpub/pr/src/misc/pripcsem.c102
-rw-r--r--nsprpub/pr/src/misc/prlog2.c27
-rw-r--r--nsprpub/pr/src/misc/prlong.c254
-rw-r--r--nsprpub/pr/src/misc/prnetdb.c2549
-rw-r--r--nsprpub/pr/src/misc/prolock.c56
-rw-r--r--nsprpub/pr/src/misc/prrng.c44
-rw-r--r--nsprpub/pr/src/misc/prsystem.c373
-rw-r--r--nsprpub/pr/src/misc/prthinfo.c211
-rw-r--r--nsprpub/pr/src/misc/prtime.c2150
-rw-r--r--nsprpub/pr/src/misc/prtpool.c1236
-rw-r--r--nsprpub/pr/src/misc/prtrace.c901
-rw-r--r--nsprpub/pr/src/nspr.def468
-rw-r--r--nsprpub/pr/src/nspr.rc69
-rw-r--r--nsprpub/pr/src/os2extra.def20
-rw-r--r--nsprpub/pr/src/prvrsion.c100
-rw-r--r--nsprpub/pr/src/pthreads/Makefile.in35
-rw-r--r--nsprpub/pr/src/pthreads/ptio.c5387
-rw-r--r--nsprpub/pr/src/pthreads/ptmisc.c45
-rw-r--r--nsprpub/pr/src/pthreads/ptsynch.c1295
-rw-r--r--nsprpub/pr/src/pthreads/ptthread.c1749
-rw-r--r--nsprpub/pr/src/threads/Makefile.in62
-rw-r--r--nsprpub/pr/src/threads/combined/Makefile.in40
-rw-r--r--nsprpub/pr/src/threads/combined/README62
-rw-r--r--nsprpub/pr/src/threads/combined/prucpu.c424
-rw-r--r--nsprpub/pr/src/threads/combined/prucv.c679
-rw-r--r--nsprpub/pr/src/threads/combined/prulock.c457
-rw-r--r--nsprpub/pr/src/threads/combined/prustack.c175
-rw-r--r--nsprpub/pr/src/threads/combined/pruthr.c1942
-rw-r--r--nsprpub/pr/src/threads/prcmon.c449
-rw-r--r--nsprpub/pr/src/threads/prcthr.c428
-rw-r--r--nsprpub/pr/src/threads/prdump.c125
-rw-r--r--nsprpub/pr/src/threads/prmon.c357
-rw-r--r--nsprpub/pr/src/threads/prrwlock.c498
-rw-r--r--nsprpub/pr/src/threads/prsem.c141
-rw-r--r--nsprpub/pr/src/threads/prtpd.c252
-rw-r--r--nsprpub/pr/tests/Makefile.in446
-rw-r--r--nsprpub/pr/tests/README.TXT404
-rw-r--r--nsprpub/pr/tests/abstract.c157
-rw-r--r--nsprpub/pr/tests/accept.c508
-rw-r--r--nsprpub/pr/tests/acceptread.c270
-rw-r--r--nsprpub/pr/tests/acceptreademu.c300
-rw-r--r--nsprpub/pr/tests/addrstr.c82
-rw-r--r--nsprpub/pr/tests/affinity.c84
-rw-r--r--nsprpub/pr/tests/alarm.c552
-rw-r--r--nsprpub/pr/tests/anonfm.c319
-rw-r--r--nsprpub/pr/tests/append.c146
-rw-r--r--nsprpub/pr/tests/atomic.c183
-rw-r--r--nsprpub/pr/tests/attach.c299
-rw-r--r--nsprpub/pr/tests/bigfile.c358
-rw-r--r--nsprpub/pr/tests/bigfile2.c100
-rw-r--r--nsprpub/pr/tests/bigfile3.c99
-rw-r--r--nsprpub/pr/tests/bug1test.c230
-rw-r--r--nsprpub/pr/tests/cleanup.c103
-rw-r--r--nsprpub/pr/tests/cltsrv.c1271
-rw-r--r--nsprpub/pr/tests/concur.c166
-rw-r--r--nsprpub/pr/tests/cvar.c309
-rw-r--r--nsprpub/pr/tests/cvar2.c976
-rw-r--r--nsprpub/pr/tests/dbmalloc.c339
-rw-r--r--nsprpub/pr/tests/dbmalloc1.c114
-rw-r--r--nsprpub/pr/tests/dceemu.c109
-rw-r--r--nsprpub/pr/tests/depend.c132
-rw-r--r--nsprpub/pr/tests/dll/Makefile.in77
-rw-r--r--nsprpub/pr/tests/dll/my.def26
-rw-r--r--nsprpub/pr/tests/dll/mygetval.c26
-rw-r--r--nsprpub/pr/tests/dll/mysetval.c13
-rw-r--r--nsprpub/pr/tests/dlltest.c216
-rw-r--r--nsprpub/pr/tests/dtoa.c213
-rw-r--r--nsprpub/pr/tests/env.c370
-rw-r--r--nsprpub/pr/tests/errcodes.c136
-rw-r--r--nsprpub/pr/tests/errset.c155
-rw-r--r--nsprpub/pr/tests/exit.c110
-rw-r--r--nsprpub/pr/tests/fdcach.c125
-rw-r--r--nsprpub/pr/tests/fileio.c214
-rw-r--r--nsprpub/pr/tests/foreign.c321
-rw-r--r--nsprpub/pr/tests/forktest.c254
-rw-r--r--nsprpub/pr/tests/formattm.c50
-rw-r--r--nsprpub/pr/tests/freeif.c47
-rw-r--r--nsprpub/pr/tests/fsync.c130
-rw-r--r--nsprpub/pr/tests/getai.c32
-rw-r--r--nsprpub/pr/tests/gethost.c267
-rw-r--r--nsprpub/pr/tests/getproto.c90
-rw-r--r--nsprpub/pr/tests/i2l.c103
-rw-r--r--nsprpub/pr/tests/initclk.c77
-rw-r--r--nsprpub/pr/tests/inrval.c217
-rw-r--r--nsprpub/pr/tests/instrumt.c478
-rw-r--r--nsprpub/pr/tests/intrio.c132
-rw-r--r--nsprpub/pr/tests/intrupt.c379
-rw-r--r--nsprpub/pr/tests/io_timeout.c269
-rw-r--r--nsprpub/pr/tests/io_timeoutk.c229
-rw-r--r--nsprpub/pr/tests/io_timeoutu.c230
-rw-r--r--nsprpub/pr/tests/ioconthr.c114
-rw-r--r--nsprpub/pr/tests/ipv6.c228
-rw-r--r--nsprpub/pr/tests/join.c264
-rw-r--r--nsprpub/pr/tests/joinkk.c174
-rw-r--r--nsprpub/pr/tests/joinku.c181
-rw-r--r--nsprpub/pr/tests/joinuk.c177
-rw-r--r--nsprpub/pr/tests/joinuu.c179
-rw-r--r--nsprpub/pr/tests/layer.c471
-rw-r--r--nsprpub/pr/tests/lazyinit.c108
-rw-r--r--nsprpub/pr/tests/libfilename.c101
-rw-r--r--nsprpub/pr/tests/lltest.c969
-rw-r--r--nsprpub/pr/tests/lock.c548
-rw-r--r--nsprpub/pr/tests/lockfile.c255
-rw-r--r--nsprpub/pr/tests/logfile.c38
-rw-r--r--nsprpub/pr/tests/logger.c127
-rw-r--r--nsprpub/pr/tests/makedir.c67
-rw-r--r--nsprpub/pr/tests/many_cv.c124
-rw-r--r--nsprpub/pr/tests/mbcs.c159
-rw-r--r--nsprpub/pr/tests/monref.c74
-rw-r--r--nsprpub/pr/tests/multiacc.c220
-rw-r--r--nsprpub/pr/tests/multiwait.c785
-rw-r--r--nsprpub/pr/tests/nameshm1.c586
-rw-r--r--nsprpub/pr/tests/nbconn.c531
-rw-r--r--nsprpub/pr/tests/nblayer.c778
-rw-r--r--nsprpub/pr/tests/nonblock.c223
-rw-r--r--nsprpub/pr/tests/ntioto.c319
-rw-r--r--nsprpub/pr/tests/ntoh.c93
-rw-r--r--nsprpub/pr/tests/obsints.c51
-rw-r--r--nsprpub/pr/tests/op_2long.c75
-rw-r--r--nsprpub/pr/tests/op_excl.c138
-rw-r--r--nsprpub/pr/tests/op_filnf.c51
-rw-r--r--nsprpub/pr/tests/op_filok.c48
-rw-r--r--nsprpub/pr/tests/op_noacc.c51
-rw-r--r--nsprpub/pr/tests/op_nofil.c56
-rw-r--r--nsprpub/pr/tests/openfile.c113
-rw-r--r--nsprpub/pr/tests/parent.c131
-rw-r--r--nsprpub/pr/tests/parsetm.c90
-rw-r--r--nsprpub/pr/tests/peek.c362
-rw-r--r--nsprpub/pr/tests/perf.c447
-rw-r--r--nsprpub/pr/tests/pipeping.c158
-rw-r--r--nsprpub/pr/tests/pipeping2.c160
-rw-r--r--nsprpub/pr/tests/pipepong.c60
-rw-r--r--nsprpub/pr/tests/pipepong2.c98
-rw-r--r--nsprpub/pr/tests/pipeself.c228
-rw-r--r--nsprpub/pr/tests/poll_er.c210
-rw-r--r--nsprpub/pr/tests/poll_nm.c355
-rw-r--r--nsprpub/pr/tests/poll_to.c209
-rw-r--r--nsprpub/pr/tests/pollable.c261
-rw-r--r--nsprpub/pr/tests/prfdbl.c29
-rw-r--r--nsprpub/pr/tests/prftest.c66
-rw-r--r--nsprpub/pr/tests/prftest1.c129
-rw-r--r--nsprpub/pr/tests/prftest2.c103
-rw-r--r--nsprpub/pr/tests/prfz.c85
-rw-r--r--nsprpub/pr/tests/primblok.c116
-rw-r--r--nsprpub/pr/tests/priotest.c202
-rw-r--r--nsprpub/pr/tests/provider.c1412
-rw-r--r--nsprpub/pr/tests/prpoll.c351
-rw-r--r--nsprpub/pr/tests/prpollml.c136
-rw-r--r--nsprpub/pr/tests/prselect.c368
-rw-r--r--nsprpub/pr/tests/prttools.h11
-rw-r--r--nsprpub/pr/tests/pushtop.c73
-rw-r--r--nsprpub/pr/tests/randseed.c137
-rw-r--r--nsprpub/pr/tests/ranfile.c433
-rw-r--r--nsprpub/pr/tests/reinit.c35
-rw-r--r--nsprpub/pr/tests/rmdir.c99
-rwxr-xr-xnsprpub/pr/tests/runtests.pl371
-rwxr-xr-xnsprpub/pr/tests/runtests.sh277
-rw-r--r--nsprpub/pr/tests/runy2ktests.ksh237
-rw-r--r--nsprpub/pr/tests/rwlockrank.c122
-rw-r--r--nsprpub/pr/tests/rwlocktest.c213
-rw-r--r--nsprpub/pr/tests/sel_spd.c534
-rw-r--r--nsprpub/pr/tests/selct_er.c200
-rw-r--r--nsprpub/pr/tests/selct_nm.c294
-rw-r--r--nsprpub/pr/tests/selct_to.c182
-rw-r--r--nsprpub/pr/tests/select2.c385
-rw-r--r--nsprpub/pr/tests/selintr.c49
-rw-r--r--nsprpub/pr/tests/sem.c222
-rw-r--r--nsprpub/pr/tests/sema.c165
-rw-r--r--nsprpub/pr/tests/semaerr.c124
-rw-r--r--nsprpub/pr/tests/semaerr1.c118
-rw-r--r--nsprpub/pr/tests/semaping.c187
-rw-r--r--nsprpub/pr/tests/semapong.c130
-rw-r--r--nsprpub/pr/tests/sendzlf.c214
-rw-r--r--nsprpub/pr/tests/server_test.c692
-rw-r--r--nsprpub/pr/tests/servr_kk.c659
-rw-r--r--nsprpub/pr/tests/servr_ku.c639
-rw-r--r--nsprpub/pr/tests/servr_uk.c640
-rw-r--r--nsprpub/pr/tests/servr_uu.c640
-rw-r--r--nsprpub/pr/tests/short_thread.c60
-rw-r--r--nsprpub/pr/tests/sigpipe.c94
-rw-r--r--nsprpub/pr/tests/sleep.c105
-rw-r--r--nsprpub/pr/tests/socket.c2286
-rw-r--r--nsprpub/pr/tests/sockopt.c183
-rw-r--r--nsprpub/pr/tests/sockping.c132
-rw-r--r--nsprpub/pr/tests/sockpong.c83
-rw-r--r--nsprpub/pr/tests/sprintf.c459
-rw-r--r--nsprpub/pr/tests/stack.c282
-rw-r--r--nsprpub/pr/tests/stat.c87
-rw-r--r--nsprpub/pr/tests/stdio.c51
-rw-r--r--nsprpub/pr/tests/str2addr.c50
-rw-r--r--nsprpub/pr/tests/strod.c76
-rw-r--r--nsprpub/pr/tests/suspend.c177
-rw-r--r--nsprpub/pr/tests/switch.c252
-rw-r--r--nsprpub/pr/tests/system.c56
-rw-r--r--nsprpub/pr/tests/testbit.c111
-rw-r--r--nsprpub/pr/tests/testfile.c963
-rw-r--r--nsprpub/pr/tests/threads.c211
-rw-r--r--nsprpub/pr/tests/thrpool_client.c349
-rw-r--r--nsprpub/pr/tests/thrpool_server.c579
-rw-r--r--nsprpub/pr/tests/thruput.c414
-rw-r--r--nsprpub/pr/tests/time.c177
-rw-r--r--nsprpub/pr/tests/timemac.c112
-rw-r--r--nsprpub/pr/tests/timetest.c823
-rw-r--r--nsprpub/pr/tests/tmoacc.c339
-rw-r--r--nsprpub/pr/tests/tmocon.c435
-rw-r--r--nsprpub/pr/tests/tpd.c308
-rw-r--r--nsprpub/pr/tests/udpsrv.c546
-rw-r--r--nsprpub/pr/tests/ut_ttools.h11
-rw-r--r--nsprpub/pr/tests/vercheck.c93
-rw-r--r--nsprpub/pr/tests/version.c94
-rw-r--r--nsprpub/pr/tests/writev.c225
-rw-r--r--nsprpub/pr/tests/xnotify.c429
-rw-r--r--nsprpub/pr/tests/y2k.c799
-rw-r--r--nsprpub/pr/tests/y2ktmo.c554
-rw-r--r--nsprpub/pr/tests/yield.c57
-rw-r--r--nsprpub/pr/tests/zerolen.c250
470 files changed, 154731 insertions, 0 deletions
diff --git a/nsprpub/pr/Makefile.in b/nsprpub/pr/Makefile.in
new file mode 100644
index 0000000000..f7af4023f1
--- /dev/null
+++ b/nsprpub/pr/Makefile.in
@@ -0,0 +1,17 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+DIRS = include src
+
+include $(topsrcdir)/config/rules.mk
diff --git a/nsprpub/pr/include/Makefile.in b/nsprpub/pr/include/Makefile.in
new file mode 100644
index 0000000000..9cc425a84f
--- /dev/null
+++ b/nsprpub/pr/include/Makefile.in
@@ -0,0 +1,27 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+DIRS = md private obsolete
+
+include $(topsrcdir)/config/config.mk
+
+HEADERS = $(wildcard $(srcdir)/*.h)
+
+RELEASE_HEADERS = $(HEADERS)
+RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR)
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(RELEASE_HEADERS)
+ $(INSTALL) -m 444 $(RELEASE_HEADERS) $(dist_includedir)
diff --git a/nsprpub/pr/include/gencfg.c b/nsprpub/pr/include/gencfg.c
new file mode 100644
index 0000000000..3a67e9113e
--- /dev/null
+++ b/nsprpub/pr/include/gencfg.c
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+
+#if defined(__sun)
+#ifndef SOLARIS
+error - SOLARIS is not defined
+#endif
+#endif
+
+#if defined(__hpux)
+#ifndef HPUX
+error - HPUX is not defined
+#endif
+#endif
+
+#if defined(__alpha)
+#if !(defined(_WIN32)) && !(defined(__linux)) && !(defined(__FreeBSD__))
+error - None of _WIN32, __linux, or __FreeBSD__ is defined
+#endif
+#endif
+
+#if defined(_IBMR2)
+#ifndef AIX
+error - AIX is not defined
+#endif
+#endif
+
+#if defined(linux)
+#ifndef LINUX
+error - LINUX is not defined
+#endif
+#endif
+
+#if defined(bsdi)
+#ifndef BSDI
+error - BSDI is not defined
+#endif
+#endif
+
+#if defined(M_UNIX)
+#ifndef SCO
+error - SCO is not defined
+#endif
+#endif
+#if !defined(M_UNIX) && defined(_USLC_)
+#ifndef UNIXWARE
+error - UNIXWARE is not defined
+#endif
+#endif
+
+#if defined(__APPLE__)
+#ifndef DARWIN
+error - DARWIN is not defined
+#endif
+#endif
+
+/************************************************************************/
+
+/* Generate cpucfg.h */
+
+#ifdef XP_PC
+#ifdef WIN32
+#define INT64 _PRInt64
+#else
+#define INT64 long
+#endif
+#else
+#if defined(HPUX) || defined(SCO) || defined(UNIXWARE)
+#define INT64 long
+#else
+#define INT64 long long
+#endif
+#endif
+
+struct align_short {
+ char c;
+ short a;
+};
+struct align_int {
+ char c;
+ int a;
+};
+struct align_long {
+ char c;
+ long a;
+};
+struct align_PRInt64 {
+ char c;
+ INT64 a;
+};
+struct align_fakelonglong {
+ char c;
+ struct {
+ long hi, lo;
+ } a;
+};
+struct align_float {
+ char c;
+ float a;
+};
+struct align_double {
+ char c;
+ double a;
+};
+struct align_pointer {
+ char c;
+ void *a;
+};
+
+#define ALIGN_OF(type) \
+ (((char*)&(((struct align_##type *)0)->a)) - ((char*)0))
+
+int bpb;
+
+/* Used if shell doesn't support redirection. By default, assume it does. */
+FILE *stream;
+
+static int Log2(int n)
+{
+ int log2 = 0;
+
+ if (n & (n-1)) {
+ log2++;
+ }
+ if (n >> 16) {
+ log2 += 16, n >>= 16;
+ }
+ if (n >> 8) {
+ log2 += 8, n >>= 8;
+ }
+ if (n >> 4) {
+ log2 += 4, n >>= 4;
+ }
+ if (n >> 2) {
+ log2 += 2, n >>= 2;
+ }
+ if (n >> 1) {
+ log2++;
+ }
+ return log2;
+}
+
+/* We assume that int's are 32 bits */
+static void do64(void)
+{
+ union {
+ int i;
+ char c[4];
+ } u;
+
+ u.i = 0x01020304;
+ if (u.c[0] == 0x01) {
+ fprintf(stream, "#undef IS_LITTLE_ENDIAN\n");
+ fprintf(stream, "#define IS_BIG_ENDIAN 1\n\n");
+ } else {
+ fprintf(stream, "#define IS_LITTLE_ENDIAN 1\n");
+ fprintf(stream, "#undef IS_BIG_ENDIAN\n\n");
+ }
+}
+
+static void do32(void)
+{
+ union {
+ long i;
+ char c[4];
+ } u;
+
+ u.i = 0x01020304;
+ if (u.c[0] == 0x01) {
+ fprintf(stream, "#undef IS_LITTLE_ENDIAN\n");
+ fprintf(stream, "#define IS_BIG_ENDIAN 1\n\n");
+ } else {
+ fprintf(stream, "#define IS_LITTLE_ENDIAN 1\n");
+ fprintf(stream, "#undef IS_BIG_ENDIAN\n\n");
+ }
+}
+
+/*
+** Concievably this could actually be used; but there is lots of code out
+** there with and's and shift's in it that assumes a byte is 8 bits, so
+** forget about porting THIS code to those non 8 bit byte machines.
+*/
+static void BitsPerByte(void)
+{
+ bpb = 8;
+}
+
+int main(int argc, char **argv)
+{
+ BitsPerByte();
+
+ /* If we got a command line argument, try to use it as the stream. */
+ ++argv;
+ if(*argv) {
+ if(!(stream = fopen ( *argv, "wt" ))) {
+ fprintf(stderr, "Could not write to output file %s.\n", *argv);
+ return 1;
+ }
+ } else {
+ stream = stdout;
+ }
+
+ fprintf(stream, "#ifndef nspr_cpucfg___\n");
+ fprintf(stream, "#define nspr_cpucfg___\n\n");
+
+ fprintf(stream, "/* AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n");
+
+ if (sizeof(long) == 8) {
+ do64();
+ } else {
+ do32();
+ }
+ fprintf(stream, "#define PR_BYTES_PER_BYTE %d\n", sizeof(char));
+ fprintf(stream, "#define PR_BYTES_PER_SHORT %d\n", sizeof(short));
+ fprintf(stream, "#define PR_BYTES_PER_INT %d\n", sizeof(int));
+ fprintf(stream, "#define PR_BYTES_PER_INT64 %d\n", 8);
+ fprintf(stream, "#define PR_BYTES_PER_LONG %d\n", sizeof(long));
+ fprintf(stream, "#define PR_BYTES_PER_FLOAT %d\n", sizeof(float));
+ fprintf(stream, "#define PR_BYTES_PER_DOUBLE %d\n\n", sizeof(double));
+
+ fprintf(stream, "#define PR_BITS_PER_BYTE %d\n", bpb);
+ fprintf(stream, "#define PR_BITS_PER_SHORT %d\n", bpb * sizeof(short));
+ fprintf(stream, "#define PR_BITS_PER_INT %d\n", bpb * sizeof(int));
+ fprintf(stream, "#define PR_BITS_PER_INT64 %d\n", bpb * 8);
+ fprintf(stream, "#define PR_BITS_PER_LONG %d\n", bpb * sizeof(long));
+ fprintf(stream, "#define PR_BITS_PER_FLOAT %d\n", bpb * sizeof(float));
+ fprintf(stream, "#define PR_BITS_PER_DOUBLE %d\n\n",
+ bpb * sizeof(double));
+
+ fprintf(stream, "#define PR_BITS_PER_BYTE_LOG2 %d\n", Log2(bpb));
+ fprintf(stream, "#define PR_BITS_PER_SHORT_LOG2 %d\n",
+ Log2(bpb * sizeof(short)));
+ fprintf(stream, "#define PR_BITS_PER_INT_LOG2 %d\n",
+ Log2(bpb * sizeof(int)));
+ fprintf(stream, "#define PR_BITS_PER_INT64_LOG2 %d\n", 6);
+ fprintf(stream, "#define PR_BITS_PER_LONG_LOG2 %d\n",
+ Log2(bpb * sizeof(long)));
+ fprintf(stream, "#define PR_BITS_PER_FLOAT_LOG2 %d\n",
+ Log2(bpb * sizeof(float)));
+ fprintf(stream, "#define PR_BITS_PER_DOUBLE_LOG2 %d\n\n",
+ Log2(bpb * sizeof(double)));
+
+ fprintf(stream, "#define PR_ALIGN_OF_SHORT %d\n", ALIGN_OF(short));
+ fprintf(stream, "#define PR_ALIGN_OF_INT %d\n", ALIGN_OF(int));
+ fprintf(stream, "#define PR_ALIGN_OF_LONG %d\n", ALIGN_OF(long));
+ if (sizeof(INT64) < 8) {
+ /* this machine doesn't actually support PRInt64's */
+ fprintf(stream, "#define PR_ALIGN_OF_INT64 %d\n",
+ ALIGN_OF(fakelonglong));
+ } else {
+ fprintf(stream, "#define PR_ALIGN_OF_INT64 %d\n", ALIGN_OF(PRInt64));
+ }
+ fprintf(stream, "#define PR_ALIGN_OF_FLOAT %d\n", ALIGN_OF(float));
+ fprintf(stream, "#define PR_ALIGN_OF_DOUBLE %d\n", ALIGN_OF(double));
+ fprintf(stream, "#define PR_ALIGN_OF_POINTER %d\n\n", ALIGN_OF(pointer));
+
+ fprintf(stream, "#endif /* nspr_cpucfg___ */\n");
+ fclose(stream);
+
+ return 0;
+}
diff --git a/nsprpub/pr/include/md/Makefile.in b/nsprpub/pr/include/md/Makefile.in
new file mode 100644
index 0000000000..383c092cf8
--- /dev/null
+++ b/nsprpub/pr/include/md/Makefile.in
@@ -0,0 +1,42 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+# The .cfg files need to be exported and installed to support
+# cross-compilation.
+CONFIGS = $(wildcard $(srcdir)/*.cfg)
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(MDCPUCFG_H)
+ $(INSTALL) -m 444 $(CONFIGS) $(dist_includedir)/md
+ $(INSTALL) -m 444 $(srcdir)/$(MDCPUCFG_H) $(dist_includedir)
+ mv -f $(dist_includedir)/$(MDCPUCFG_H) $(dist_includedir)/prcpucfg.h
+
+install::
+ $(NSINSTALL) -D $(DESTDIR)$(includedir)/md
+ $(NSINSTALL) -t -m 644 $(CONFIGS) $(DESTDIR)$(includedir)/md
+ $(NSINSTALL) -t -m 644 $(srcdir)/$(MDCPUCFG_H) $(DESTDIR)$(includedir)
+ mv -f $(DESTDIR)$(includedir)/$(MDCPUCFG_H) $(DESTDIR)$(includedir)/prcpucfg.h
+
+release:: export
+ @echo "Copying machine-dependent prcpucfg.h"
+ @if test -z "$(BUILD_NUMBER)"; then \
+ echo "BUILD_NUMBER must be defined"; \
+ false; \
+ fi
+ @if test ! -d $(RELEASE_INCLUDE_DIR); then \
+ rm -rf $(RELEASE_INCLUDE_DIR); \
+ $(NSINSTALL) -D $(RELEASE_INCLUDE_DIR);\
+ fi
+ cp $(srcdir)/$(MDCPUCFG_H) $(RELEASE_INCLUDE_DIR)/prcpucfg.h
diff --git a/nsprpub/pr/include/md/_aix.h b/nsprpub/pr/include/md/_aix.h
new file mode 100644
index 0000000000..c2794d3368
--- /dev/null
+++ b/nsprpub/pr/include/md/_aix.h
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_aix_defs_h___
+#define nspr_aix_defs_h___
+
+#include <sys/types.h>
+#if defined(_PR_PTHREADS) || defined(PTHREADS_USER)
+#include <pthread.h>
+#endif
+
+/*
+ * To pick up fd_set and the poll events.
+ */
+#include <sys/select.h>
+#include <sys/poll.h>
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "aix"
+#define _PR_SI_SYSNAME "AIX"
+#define _PR_SI_ARCHITECTURE "rs6000"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE (2*65536L)
+#define _MD_MINIMUM_STACK_SIZE (2*65536L)
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#define NEED_TIME_R
+#undef HAVE_STACK_GROWING_UP
+#undef HAVE_WEAK_IO_SYMBOLS
+#undef HAVE_WEAK_MALLOC_SYMBOLS
+#define HAVE_DLL
+#define USE_DLFCN
+#define _PR_HAVE_SOCKADDR_LEN
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+#ifdef _PR_INET6
+#define _PR_HAVE_INET_NTOP
+#define _PR_HAVE_GETHOSTBYNAME2
+#ifdef _AIX51 /* AIX 4.3.3 does not have AI_NUMERICHOST. */
+#define _PR_HAVE_GETADDRINFO
+#endif
+#endif
+#define _PR_HAVE_SYSV_SEMAPHORES
+#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY
+#define _PR_ACCEPT_INHERIT_NONBLOCK
+
+/* Timer operations */
+#if defined(AIX_TIMERS)
+#define _MD_INTERVAL_INIT()
+
+extern PRIntervalTime _MD_AixGetInterval(void);
+#define _MD_GET_INTERVAL _MD_AixGetInterval
+
+extern PRIntervalTime _MD_AixIntervalPerSec(void);
+#define _MD_INTERVAL_PER_SEC _MD_AixIntervalPerSec
+
+#else /* defined(AIX_TIMERS) */
+#define _MD_INTERVAL_USE_GTOD
+#endif /* defined(AIX_TIMERS) */
+
+#ifdef AIX_HAVE_ATOMIC_OP_H
+/* The atomic operations */
+#include <sys/atomic_op.h>
+#define _PR_HAVE_ATOMIC_OPS
+#ifndef IS_64
+#define _PR_HAVE_ATOMIC_CAS
+#endif
+#define _MD_INIT_ATOMIC()
+#define _MD_ATOMIC_INCREMENT(val) ((PRInt32)fetch_and_add((atomic_p)val, 1) + 1)
+#define _MD_ATOMIC_ADD(ptr, val) ((PRInt32)fetch_and_add((atomic_p)ptr, val) + val)
+#define _MD_ATOMIC_DECREMENT(val) ((PRInt32)fetch_and_add((atomic_p)val, -1) - 1)
+#define _MD_ATOMIC_SET(val, newval) _AIX_AtomicSet(val, newval)
+#endif /* AIX_HAVE_ATOMIC_OP_H */
+
+#define USE_SETJMP
+
+#include <setjmp.h>
+
+#define _MD_GET_SP(_t) (_t)->md.jb[3]
+#define _MD_SET_THR_SP(_t, _sp) ((_t)->md.jb[3] = (int) (_sp - 2 * 64))
+#define PR_NUM_GCREGS _JBLEN
+
+#define CONTEXT(_th) ((_th)->md.jb)
+#define SAVE_CONTEXT(_th) _setjmp(CONTEXT(_th))
+#define GOTO_CONTEXT(_th) _longjmp(CONTEXT(_th), 1)
+
+#ifdef PTHREADS_USER
+#include "_nspr_pthread.h"
+#else
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ *status = PR_TRUE; \
+ if (setjmp(CONTEXT(_thread))) { \
+ (*_main)(); \
+ } \
+ _MD_GET_SP(_thread) = (int) (_sp - 2 * 64); \
+ PR_END_MACRO
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!setjmp(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ longjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ jmp_buf jb;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#if !defined(_PR_PTHREADS)
+#define _MD_INIT_LOCKS()
+#endif
+
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+#endif /* PTHREADS_USER */
+
+#ifdef AIX_RENAME_SELECT
+#define _MD_SELECT select
+#define _MD_POLL poll
+#endif
+
+extern void _MD_aix_map_sendfile_error(int err);
+
+#endif /* nspr_aix_defs_h___ */
diff --git a/nsprpub/pr/include/md/_aix32.cfg b/nsprpub/pr/include/md/_aix32.cfg
new file mode 100644
index 0000000000..23a3f5125f
--- /dev/null
+++ b/nsprpub/pr/include/md/_aix32.cfg
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef AIX
+#define AIX
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_AF_INET6 24 /* same as AF_INET6 */
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+/* used by protypes.h only */
+#define _PR_AIX_HAVE_BSD_INT_TYPES
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_aix64.cfg b/nsprpub/pr/include/md/_aix64.cfg
new file mode 100644
index 0000000000..7efe896d1e
--- /dev/null
+++ b/nsprpub/pr/include/md/_aix64.cfg
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef AIX
+#define AIX
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 8
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_AF_INET6 24 /* same as AF_INET6 */
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+/* used by protypes.h only */
+#define _PR_AIX_HAVE_BSD_INT_TYPES
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_bsdi.cfg b/nsprpub/pr/include/md/_bsdi.cfg
new file mode 100644
index 0000000000..130cf820b0
--- /dev/null
+++ b/nsprpub/pr/include/md/_bsdi.cfg
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef BSDI
+#define BSDI
+#endif
+
+#define PR_AF_INET6 24 /* same as AF_INET6 */
+
+#if defined(__i386__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__sparc__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else
+
+#error "Unknown CPU architecture"
+
+#endif
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_bsdi.h b/nsprpub/pr/include/md/_bsdi.h
new file mode 100644
index 0000000000..99655dc3f2
--- /dev/null
+++ b/nsprpub/pr/include/md/_bsdi.h
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_bsdi_defs_h___
+#define nspr_bsdi_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#include <sys/param.h> /* for _BSDI_VERSION */
+
+#define PR_LINKER_ARCH "bsdi"
+#define _PR_SI_SYSNAME "BSDI"
+#if defined(__i386__)
+#define _PR_SI_ARCHITECTURE "x86"
+#elif defined(__sparc__)
+#define _PR_SI_ARCHITECTURE "sparc"
+#else
+#error "Unknown CPU architecture"
+#endif
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#define HAVE_BSD_FLOCK
+#define NEED_TIME_R
+#define _PR_HAVE_SOCKADDR_LEN
+#define _PR_NO_LARGE_FILES
+
+#define USE_SETJMP
+
+/* BSD/OS 4.3 and newer all have IPv6 support */
+#if _BSDI_VERSION >= 200105
+#define _PR_INET6
+#define _PR_HAVE_INET_NTOP
+#define _PR_HAVE_GETIPNODEBYNAME
+#define _PR_HAVE_GETIPNODEBYADDR
+#define _PR_HAVE_GETADDRINFO
+#define _PR_INET6_PROBE
+#endif
+
+#ifndef _PR_PTHREADS
+
+#include <setjmp.h>
+
+#if defined(_PR_BSDI_JMPBUF_IS_ARRAY)
+#define _MD_GET_SP(_t) (_t)->md.context[2]
+#elif defined(_PR_BSDI_JMPBUF_IS_STRUCT)
+#define _MD_GET_SP(_t) (_t)->md.context[0].jb_esp
+#else
+#error "Unknown BSDI jmp_buf type"
+#endif
+
+#define PR_NUM_GCREGS _JBLEN
+#define PR_CONTEXT_TYPE jmp_buf
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (setjmp(CONTEXT(_thread))) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (int) (_sp - 64); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!setjmp(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ longjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+#endif /* ! _PR_PTHREADS */
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+
+#include <sys/syscall.h>
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+
+#define _MD_INTERVAL_USE_GTOD
+
+#endif /* nspr_bsdi_defs_h___ */
diff --git a/nsprpub/pr/include/md/_darwin.cfg b/nsprpub/pr/include/md/_darwin.cfg
new file mode 100644
index 0000000000..5e11893315
--- /dev/null
+++ b/nsprpub/pr/include/md/_darwin.cfg
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#define PR_AF_INET6 30 /* same as AF_INET6 */
+
+#ifdef __LITTLE_ENDIAN__
+#undef IS_BIG_ENDIAN
+#define IS_LITTLE_ENDIAN 1
+#else
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#endif
+
+#ifdef __LP64__
+#define IS_64
+#endif
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+#undef HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS 1
+
+#ifdef IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+#define PR_BITS_PER_DWORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+#define PR_BITS_PER_DWORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+#define PR_ALIGN_OF_DWORD 8
+
+#else /* IS_64 */
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#endif /* IS_64 */
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
+
diff --git a/nsprpub/pr/include/md/_darwin.h b/nsprpub/pr/include/md/_darwin.h
new file mode 100644
index 0000000000..b3f89fbf8d
--- /dev/null
+++ b/nsprpub/pr/include/md/_darwin.h
@@ -0,0 +1,308 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_darwin_defs_h___
+#define nspr_darwin_defs_h___
+
+#include "prthread.h"
+
+#include <libkern/OSAtomic.h>
+#include <sys/syscall.h>
+
+#ifdef __APPLE__
+#include <AvailabilityMacros.h>
+#include <TargetConditionals.h>
+#endif
+
+#define PR_LINKER_ARCH "darwin"
+#define _PR_SI_SYSNAME "DARWIN"
+#ifdef __i386__
+#define _PR_SI_ARCHITECTURE "x86"
+#elif defined(__x86_64__)
+#define _PR_SI_ARCHITECTURE "x86-64"
+#elif defined(__ppc__)
+#define _PR_SI_ARCHITECTURE "ppc"
+#elif defined(__arm__)
+#define _PR_SI_ARCHITECTURE "arm"
+#elif defined(__aarch64__)
+#define _PR_SI_ARCHITECTURE "aarch64"
+#else
+#error "Unknown CPU architecture"
+#endif
+#define PR_DLL_SUFFIX ".dylib"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+#define _PR_HAVE_SOCKADDR_LEN
+#define _PR_STAT_HAS_ST_ATIMESPEC
+#define _PR_HAVE_LARGE_OFF_T
+#define _PR_HAVE_SYSV_SEMAPHORES
+#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY
+
+#define _PR_INET6
+/*
+ * I'd prefer to use getipnodebyname and getipnodebyaddr but the
+ * getipnodebyname(3) man page on Mac OS X 10.2 says they are not
+ * thread-safe. AI_V4MAPPED|AI_ADDRCONFIG doesn't work either.
+ */
+#define _PR_HAVE_GETHOSTBYNAME2
+#define _PR_HAVE_GETADDRINFO
+/*
+ * On Mac OS X 10.2, gethostbyaddr fails with h_errno=NO_RECOVERY
+ * if you pass an IPv4-mapped IPv6 address to it.
+ */
+#define _PR_GHBA_DISALLOW_V4MAPPED
+#ifdef __APPLE__
+#if !defined(MAC_OS_X_VERSION_10_3) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_3
+/*
+ * socket(AF_INET6) fails with EPROTONOSUPPORT on Mac OS X 10.1.
+ * IPv6 under OS X 10.2 and below is not complete (see bug 222031).
+ */
+#define _PR_INET6_PROBE
+#endif /* DT < 10.3 */
+#if defined(MAC_OS_X_VERSION_10_2) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_2
+/* Mac OS X 10.2 has inet_ntop and inet_pton. */
+#define _PR_HAVE_INET_NTOP
+#endif /* DT >= 10.2 */
+#endif /* __APPLE__ */
+#define _PR_IPV6_V6ONLY_PROBE
+/* The IPV6_V6ONLY socket option is not defined on Mac OS X 10.1. */
+#ifndef IPV6_V6ONLY
+#define IPV6_V6ONLY 27
+#endif
+
+#ifdef __ppc__
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+extern PRInt32 _PR_DarwinPPC_AtomicIncrement(PRInt32 *val);
+#define _MD_ATOMIC_INCREMENT(val) _PR_DarwinPPC_AtomicIncrement(val)
+extern PRInt32 _PR_DarwinPPC_AtomicDecrement(PRInt32 *val);
+#define _MD_ATOMIC_DECREMENT(val) _PR_DarwinPPC_AtomicDecrement(val)
+extern PRInt32 _PR_DarwinPPC_AtomicSet(PRInt32 *val, PRInt32 newval);
+#define _MD_ATOMIC_SET(val, newval) _PR_DarwinPPC_AtomicSet(val, newval)
+extern PRInt32 _PR_DarwinPPC_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+#define _MD_ATOMIC_ADD(ptr, val) _PR_DarwinPPC_AtomicAdd(ptr, val)
+#endif /* __ppc__ */
+
+#ifdef __i386__
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+extern PRInt32 _PR_Darwin_x86_AtomicIncrement(PRInt32 *val);
+#define _MD_ATOMIC_INCREMENT(val) _PR_Darwin_x86_AtomicIncrement(val)
+extern PRInt32 _PR_Darwin_x86_AtomicDecrement(PRInt32 *val);
+#define _MD_ATOMIC_DECREMENT(val) _PR_Darwin_x86_AtomicDecrement(val)
+extern PRInt32 _PR_Darwin_x86_AtomicSet(PRInt32 *val, PRInt32 newval);
+#define _MD_ATOMIC_SET(val, newval) _PR_Darwin_x86_AtomicSet(val, newval)
+extern PRInt32 _PR_Darwin_x86_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+#define _MD_ATOMIC_ADD(ptr, val) _PR_Darwin_x86_AtomicAdd(ptr, val)
+#endif /* __i386__ */
+
+#ifdef __x86_64__
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+extern PRInt32 _PR_Darwin_x86_64_AtomicIncrement(PRInt32 *val);
+#define _MD_ATOMIC_INCREMENT(val) _PR_Darwin_x86_64_AtomicIncrement(val)
+extern PRInt32 _PR_Darwin_x86_64_AtomicDecrement(PRInt32 *val);
+#define _MD_ATOMIC_DECREMENT(val) _PR_Darwin_x86_64_AtomicDecrement(val)
+extern PRInt32 _PR_Darwin_x86_64_AtomicSet(PRInt32 *val, PRInt32 newval);
+#define _MD_ATOMIC_SET(val, newval) _PR_Darwin_x86_64_AtomicSet(val, newval)
+extern PRInt32 _PR_Darwin_x86_64_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+#define _MD_ATOMIC_ADD(ptr, val) _PR_Darwin_x86_64_AtomicAdd(ptr, val)
+#endif /* __x86_64__ */
+
+#ifdef __aarch64__
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+#define _MD_ATOMIC_INCREMENT(val) __sync_add_and_fetch(val, 1)
+#define _MD_ATOMIC_DECREMENT(val) __sync_sub_and_fetch(val, 1)
+#define _MD_ATOMIC_SET(val, newval) __sync_lock_test_and_set(val, newval)
+#define _MD_ATOMIC_ADD(ptr, val) __sync_add_and_fetch(ptr, val)
+#endif /* __aarch64__ */
+
+#if defined(__arm__)
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+#define _MD_ATOMIC_INCREMENT(val) OSAtomicIncrement32(val)
+#define _MD_ATOMIC_DECREMENT(val) OSAtomicDecrement32(val)
+static inline PRInt32 _MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ PRInt32 oldval;
+ do {
+ oldval = *val;
+ } while (!OSAtomicCompareAndSwap32(oldval, newval, val));
+ return oldval;
+}
+#define _MD_ATOMIC_ADD(ptr, val) OSAtomicAdd32(val, ptr)
+#endif /* __arm__ */
+
+#define USE_SETJMP
+
+#if !defined(_PR_PTHREADS)
+
+#include <setjmp.h>
+
+#define PR_CONTEXT_TYPE jmp_buf
+
+#define CONTEXT(_th) ((_th)->md.context)
+#define _MD_GET_SP(_th) (((struct sigcontext *) (_th)->md.context)->sc_onstack)
+#define PR_NUM_GCREGS _JBLEN
+
+/*
+** Initialize a thread context to run "_main()" when started
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (setjmp(CONTEXT(_thread))) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 64); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!setjmp(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ longjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+extern PRStatus _MD_InitializeThread(PRThread *thread);
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread
+#define _MD_RESUME_THREAD(thread) _MD_resume_thread
+#define _MD_CLEAN_THREAD(_thread)
+
+extern PRStatus _MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+extern void _MD_SET_PRIORITY(struct _MDThread *thread, PRUintn newPri);
+extern PRStatus _MD_WAIT(PRThread *, PRIntervalTime timeout);
+extern PRStatus _MD_WAKEUP_WAITER(PRThread *);
+extern void _MD_YIELD(void);
+
+#endif /* ! _PR_PTHREADS */
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INTERVAL_INIT _PR_Mach_IntervalInit
+#define _MD_GET_INTERVAL _PR_Mach_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_Mach_TicksPerSecond
+
+extern void _MD_EarlyInit(void);
+extern void _PR_Mach_IntervalInit(void);
+extern PRIntervalTime _PR_Mach_GetInterval(void);
+extern PRIntervalTime _PR_Mach_TicksPerSecond(void);
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+
+/* For writev() */
+#include <sys/uio.h>
+
+#endif /* nspr_darwin_defs_h___ */
diff --git a/nsprpub/pr/include/md/_freebsd.cfg b/nsprpub/pr/include/md/_freebsd.cfg
new file mode 100644
index 0000000000..1d1039a5b8
--- /dev/null
+++ b/nsprpub/pr/include/md/_freebsd.cfg
@@ -0,0 +1,594 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef FREEBSD
+#define FREEBSD
+#endif
+
+#define PR_AF_INET6 28 /* same as AF_INET6 */
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+
+#if defined(__i386__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#elif defined(__alpha__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#elif defined(__sparc__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#elif defined(__ia64__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#elif defined(__amd64__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#elif defined(__powerpc64__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__powerpc__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__aarch64__)
+
+#undef IS_BIG_ENDIAN
+#define IS_LITTLE_ENDIAN 1
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__arm__)
+
+#if defined(__ARMEB__) || defined(__ARM_BIG_ENDIAN__)
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#else
+#undef IS_BIG_ENDIAN
+#define IS_LITTLE_ENDIAN 1
+#endif
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__mips64__)
+
+#if defined(__MIPSEB__) || defined(_MIPSEB)
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#else
+#undef IS_BIG_ENDIAN
+#define IS_LITTLE_ENDIAN 1
+#endif
+
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__mips__)
+
+#if defined(__MIPSEB__) || defined(_MIPSEB)
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#else
+#undef IS_BIG_ENDIAN
+#define IS_LITTLE_ENDIAN 1
+#endif
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else
+
+#error "Unknown CPU architecture"
+
+#endif
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_freebsd.h b/nsprpub/pr/include/md/_freebsd.h
new file mode 100644
index 0000000000..118cb9f933
--- /dev/null
+++ b/nsprpub/pr/include/md/_freebsd.h
@@ -0,0 +1,255 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_freebsd_defs_h___
+#define nspr_freebsd_defs_h___
+
+#include "prthread.h"
+
+#if __FreeBSD__ >= 2
+#include <osreldate.h> /* for __FreeBSD_version */
+#endif
+#include <sys/syscall.h>
+
+#define PR_LINKER_ARCH "freebsd"
+#define _PR_SI_SYSNAME "FREEBSD"
+#if defined(__i386__)
+#define _PR_SI_ARCHITECTURE "x86"
+#elif defined(__alpha__)
+#define _PR_SI_ARCHITECTURE "alpha"
+#elif defined(__sparc__)
+#define _PR_SI_ARCHITECTURE "sparc"
+#elif defined(__ia64__)
+#define _PR_SI_ARCHITECTURE "ia64"
+#elif defined(__amd64__)
+#define _PR_SI_ARCHITECTURE "amd64"
+#elif defined(__powerpc64__)
+#define _PR_SI_ARCHITECTURE "powerpc64"
+#elif defined(__powerpc__)
+#define _PR_SI_ARCHITECTURE "powerpc"
+#elif defined(__aarch64__)
+#define _PR_SI_ARCHITECTURE "aarch64"
+#elif defined(__arm__)
+#define _PR_SI_ARCHITECTURE "arm"
+#elif defined(__mips64__)
+#define _PR_SI_ARCHITECTURE "mips64"
+#elif defined(__mips__)
+#define _PR_SI_ARCHITECTURE "mips"
+#else
+#error "Unknown CPU architecture"
+#endif
+#if defined(__ELF__)
+#define PR_DLL_SUFFIX ".so"
+#else
+#define PR_DLL_SUFFIX ".so.1.0"
+#endif
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+#define _PR_HAVE_SOCKADDR_LEN
+#define _PR_STAT_HAS_ST_ATIMESPEC
+#define _PR_HAVE_LARGE_OFF_T
+
+#if defined(_PR_PTHREADS)
+#if __FreeBSD_version >= 400008
+/*
+ * libc_r before this version of FreeBSD doesn't have poll().
+ * Although libc has poll(), it is not thread-safe so we can't
+ * use it in the pthreads version.
+ */
+#define _PR_POLL_AVAILABLE
+#endif
+#else
+#if __FreeBSD_version >= 300000
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#endif
+#endif
+
+#define _PR_HAVE_SYSV_SEMAPHORES
+#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY
+
+#if __FreeBSD_version >= 400014
+#define _PR_INET6
+#define _PR_HAVE_INET_NTOP
+#define _PR_HAVE_GETHOSTBYNAME2
+#define _PR_HAVE_GETADDRINFO
+#define _PR_INET6_PROBE
+#define _PR_IPV6_V6ONLY_PROBE
+#endif
+
+#define USE_SETJMP
+
+#ifndef _PR_PTHREADS
+#include <setjmp.h>
+
+#define PR_CONTEXT_TYPE sigjmp_buf
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+#define _MD_GET_SP(_th) (_th)->md.context[0]._sjb[2]
+#define PR_NUM_GCREGS _JBLEN
+
+/*
+** Initialize a thread context to run "_main()" when started
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (sigsetjmp(CONTEXT(_thread), 1)) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 64); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!sigsetjmp(CONTEXT(_thread), 1)) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ siglongjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+extern PRStatus _MD_InitializeThread(PRThread *thread);
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread
+#define _MD_RESUME_THREAD(thread) _MD_resume_thread
+#define _MD_CLEAN_THREAD(_thread)
+
+extern PRStatus _MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+extern void _MD_SET_PRIORITY(struct _MDThread *thread, PRUintn newPri);
+extern PRStatus _MD_WAIT(PRThread *, PRIntervalTime timeout);
+extern PRStatus _MD_WAKEUP_WAITER(PRThread *);
+extern void _MD_YIELD(void);
+
+#endif /* ! _PR_PTHREADS */
+
+extern void _MD_EarlyInit(void);
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _PR_HAVE_CLOCK_MONOTONIC
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+
+#if defined(_PR_POLL_AVAILABLE)
+#include <poll.h>
+#define _MD_POLL(fds,nfds,timeout) syscall(SYS_poll,fds,nfds,timeout)
+#endif
+
+/* freebsd has INADDR_LOOPBACK defined, but in /usr/include/rpc/types.h, and I didn't
+ want to be including that.. */
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK (u_long)0x7F000001
+#endif
+
+/* For writev() */
+#include <sys/uio.h>
+
+#endif /* nspr_freebsd_defs_h___ */
diff --git a/nsprpub/pr/include/md/_hpux.h b/nsprpub/pr/include/md/_hpux.h
new file mode 100644
index 0000000000..2d8017e972
--- /dev/null
+++ b/nsprpub/pr/include/md/_hpux.h
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_xhppa_defs_h___
+#define nspr_xhppa_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "hpux"
+#define _PR_SI_SYSNAME "HPUX"
+#ifdef __ia64
+#define _PR_SI_ARCHITECTURE "ia64"
+#define PR_DLL_SUFFIX ".so"
+#else
+/*
+ * _PR_SI_ARCHITECTURE must be "hppa1.1" for backward compatibility.
+ * It was changed to "hppa" in NSPR 4.6.2, but was changed back in
+ * NSPR 4.6.4.
+ */
+#define _PR_SI_ARCHITECTURE "hppa1.1"
+#define PR_DLL_SUFFIX ".sl"
+#endif
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+/*
+ * _USE_BIG_FDS increases the size of fd_set from 256 bytes to
+ * about 7500 bytes. PR_Poll allocates three fd_sets on the
+ * stack, so it is safer to also increase the default thread
+ * stack size.
+ */
+#define _MD_DEFAULT_STACK_SIZE (2*65536L)
+#define _MD_MINIMUM_STACK_SIZE (2*65536L)
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#define NEED_TIME_R
+
+#define HAVE_STACK_GROWING_UP
+#undef HAVE_WEAK_IO_SYMBOLS
+#undef HAVE_WEAK_MALLOC_SYMBOLS
+#define HAVE_DLL
+#ifdef IS_64
+#define USE_DLFCN
+#else
+#define USE_HPSHL
+#endif
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR
+#endif
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+#define _PR_HAVE_POSIX_SEMAPHORES
+#define PR_HAVE_POSIX_NAMED_SHARED_MEMORY
+#define _PR_ACCEPT_INHERIT_NONBLOCK
+
+#if defined(__ia64)
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+extern PRInt32 _PR_ia64_AtomicIncrement(PRInt32 *val);
+#define _MD_ATOMIC_INCREMENT _PR_ia64_AtomicIncrement
+extern PRInt32 _PR_ia64_AtomicDecrement(PRInt32 *val);
+#define _MD_ATOMIC_DECREMENT _PR_ia64_AtomicDecrement
+extern PRInt32 _PR_ia64_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+#define _MD_ATOMIC_ADD _PR_ia64_AtomicAdd
+extern PRInt32 _PR_ia64_AtomicSet(PRInt32 *val, PRInt32 newval);
+#define _MD_ATOMIC_SET _PR_ia64_AtomicSet
+#endif
+
+#define _PR_HAVE_GETIPNODEBYNAME
+#define _PR_HAVE_GETIPNODEBYADDR
+#define _PR_HAVE_GETADDRINFO
+#ifdef _PR_INET6
+#define _PR_HAVE_INET_NTOP
+#else
+#define _PR_INET6_PROBE
+
+/* for HP-UX 11.11 without IPv6 */
+#ifndef AF_INET6
+#define AF_INET6 22
+#define AI_CANONNAME 2
+#define AI_NUMERICHOST 4
+#define AI_NUMERICSERV 8
+#define AI_V4MAPPED 0x00000010
+#define AI_ADDRCONFIG 0x00000040
+#define AI_ALL 0x00000020
+#define AI_DEFAULT (AI_V4MAPPED|AI_ADDRCONFIG)
+#define NI_NUMERICHOST 2
+struct addrinfo {
+ int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
+ int ai_family; /* PF_xxx */
+ int ai_socktype; /* SOCK_xxx */
+ int ai_protocol; /* IPPROTO_xxx for IPv4 and IPv6 */
+ socklen_t ai_addrlen; /* length of ai_addr */
+ char *ai_canonname; /* canonical name for host */
+ struct sockaddr *ai_addr; /* binary address */
+ struct addrinfo *ai_next; /* next structure in linked list */
+};
+#endif /* for HP-UX 11.11 without IPv6 */
+
+#define _PR_HAVE_MD_SOCKADDR_IN6
+/* isomorphic to struct in6_addr on HP-UX B.11.23 */
+struct _md_in6_addr {
+ union {
+ PRUint8 _S6_u8[16];
+ PRUint16 _S6_u16[8];
+ PRUint32 _S6_u32[4];
+ PRUint32 __S6_align;
+ } _s6_un;
+};
+/* isomorphic to struct sockaddr_in6 on HP-UX B.11.23 */
+struct _md_sockaddr_in6 {
+ PRUint16 sin6_family;
+ PRUint16 sin6_port;
+ PRUint32 sin6_flowinfo;
+ struct _md_in6_addr sin6_addr;
+ PRUint32 sin6_scope_id;
+};
+#endif
+
+#if !defined(_PR_PTHREADS)
+
+#include <syscall.h>
+#include <setjmp.h>
+
+#define USE_SETJMP
+
+#define _MD_GET_SP(_t) (*((int *)((_t)->md.jb) + 1))
+#define PR_NUM_GCREGS _JBLEN
+/* Caveat: This makes jmp_buf full of doubles. */
+#define CONTEXT(_th) ((_th)->md.jb)
+
+/* Stack needs two frames (64 bytes) at the bottom */ \
+#define _MD_SET_THR_SP(_t, _sp) ((_MD_GET_SP(_t)) = (int) (_sp + 64 *2))
+#define SAVE_CONTEXT(_th) _setjmp(CONTEXT(_th))
+#define GOTO_CONTEXT(_th) _longjmp(CONTEXT(_th), 1)
+
+#if !defined(PTHREADS_USER)
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *(status) = PR_TRUE; \
+ if (_setjmp(CONTEXT(_thread))) (*_main)(); \
+ /* Stack needs two frames (64 bytes) at the bottom */ \
+ (_MD_GET_SP(_thread)) = (int) ((_sp) + 64*2); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!_setjmp(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _longjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures. HP-UX has no native threads. */
+
+struct _MDThread {
+ jmp_buf jb;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread
+#define _MD_RESUME_THREAD(thread) _MD_resume_thread
+#define _MD_CLEAN_THREAD(_thread)
+
+#else /* PTHREADS_USER */
+
+#include "_nspr_pthread.h"
+
+#endif /* PTHREADS_USER */
+
+#endif /* !defined(_PR_PTHREADS) */
+
+#if !defined(PTHREADS_USER)
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#endif
+
+#if defined(HPUX_LW_TIMER)
+extern void _PR_HPUX_LW_IntervalInit(void);
+extern PRIntervalTime _PR_HPUX_LW_GetInterval(void);
+#define _MD_INTERVAL_INIT _PR_HPUX_LW_IntervalInit
+#define _MD_GET_INTERVAL _PR_HPUX_LW_GetInterval
+#define _MD_INTERVAL_PER_SEC() 1000
+#else
+#define _MD_INTERVAL_USE_GTOD
+#endif
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+
+#include <poll.h>
+#define _MD_POLL(fds,nfds,timeout) syscall(SYS_poll,fds,nfds,timeout)
+
+#ifdef HPUX11
+extern void _MD_hpux_map_sendfile_error(int err);
+#endif /* HPUX11 */
+
+#endif /* nspr_xhppa_defs_h___ */
diff --git a/nsprpub/pr/include/md/_hpux32.cfg b/nsprpub/pr/include/md/_hpux32.cfg
new file mode 100644
index 0000000000..08950cbf13
--- /dev/null
+++ b/nsprpub/pr/include/md/_hpux32.cfg
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef HPUX
+#define HPUX
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_AF_INET6 22 /* same as AF_INET6 */
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_hpux64.cfg b/nsprpub/pr/include/md/_hpux64.cfg
new file mode 100644
index 0000000000..028a034f68
--- /dev/null
+++ b/nsprpub/pr/include/md/_hpux64.cfg
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef HPUX
+#define HPUX
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define IS_64
+
+#define PR_AF_INET6 22 /* same as AF_INET6 */
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_linux.cfg b/nsprpub/pr/include/md/_linux.cfg
new file mode 100644
index 0000000000..2232820afe
--- /dev/null
+++ b/nsprpub/pr/include/md/_linux.cfg
@@ -0,0 +1,1365 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file is used by not only Linux but also other glibc systems
+ * such as GNU/Hurd and GNU/k*BSD.
+ */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#if !defined(LINUX) && defined(__linux__)
+#define LINUX
+#endif
+
+#ifdef __FreeBSD_kernel__
+#define PR_AF_INET6 28 /* same as AF_INET6 */
+#elif defined(__GNU__)
+#define PR_AF_INET6 26 /* same as AF_INET6 */
+#else
+#define PR_AF_INET6 10 /* same as AF_INET6 */
+#endif
+
+#ifdef __powerpc64__
+
+#ifdef __LITTLE_ENDIAN__
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#else
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#endif
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__powerpc__)
+
+#ifdef __LITTLE_ENDIAN__
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#else
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#endif
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__alpha)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__ia64__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__x86_64__)
+
+#ifdef __ILP32__
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#endif
+
+#elif defined(__mc68000__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 2
+#define PR_ALIGN_OF_LONG 2
+#define PR_ALIGN_OF_INT64 2
+#define PR_ALIGN_OF_FLOAT 2
+#define PR_ALIGN_OF_DOUBLE 2
+#define PR_ALIGN_OF_POINTER 2
+#define PR_ALIGN_OF_WORD 2
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__sparc__) && defined (__arch64__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__sparc__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__i386__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__mips__)
+
+/* For _ABI64 */
+#include <asm/sgidefs.h>
+
+#ifdef __MIPSEB__
+#define IS_BIG_ENDIAN 1
+#undef IS_LITTLE_ENDIAN
+#elif defined(__MIPSEL__)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#else
+#error "Unknown MIPS endianness."
+#endif
+
+#if _MIPS_SIM == _ABI64
+
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else /* _ABI64 */
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#endif /* _ABI64 */
+
+#elif defined(__arm__)
+
+#ifdef __ARMEB__
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#elif defined(__ARMEL__)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#else
+#error "Unknown ARM endianness."
+#endif
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__aarch64__)
+
+#ifdef __AARCH64EB__
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#elif defined(__AARCH64EL__)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#else
+#error "Unknown Aarch64 endianness."
+#endif
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__hppa__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__s390x__)
+
+#define IS_BIG_ENDIAN 1
+#undef IS_LITTLE_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__s390__)
+
+#define IS_BIG_ENDIAN 1
+#undef IS_LITTLE_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__sh__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__avr32__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__m32r__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__or1k__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__riscv) && (__riscv_xlen == 32)
+
+#undef IS_BIG_ENDIAN
+#define IS_LITTLE_ENDIAN 1
+#undef IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__riscv) && (__riscv_xlen == 64)
+
+#undef IS_BIG_ENDIAN
+#define IS_LITTLE_ENDIAN 1
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__arc__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__nios2__) || defined(__microblaze__) || defined(__nds32__) || \
+ defined(__xtensa__)
+
+#if defined(__microblaze__) && defined(__BIG_ENDIAN__)
+#define IS_BIG_ENDIAN 1
+#undef IS_LITTLE_ENDIAN
+#else
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#endif
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__e2k__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 4
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 32
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 5
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__loongarch64)
+
+#undef IS_BIG_ENDIAN
+#define IS_LITTLE_ENDIAN 1
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else
+
+#error "Unknown CPU architecture"
+
+#endif
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+#if PR_ALIGN_OF_DOUBLE == 8
+#define HAVE_ALIGNED_DOUBLES
+#endif
+#if PR_ALIGN_OF_INT64 == 8
+#define HAVE_ALIGNED_LONGLONGS
+#endif
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_linux.h b/nsprpub/pr/include/md/_linux.h
new file mode 100644
index 0000000000..ad57d08330
--- /dev/null
+++ b/nsprpub/pr/include/md/_linux.h
@@ -0,0 +1,724 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file is used by not only Linux but also other glibc systems
+ * such as GNU/Hurd and GNU/k*BSD.
+ */
+
+#ifndef nspr_linux_defs_h___
+#define nspr_linux_defs_h___
+
+#include "prthread.h"
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "linux"
+#define _PR_SI_SYSNAME "LINUX"
+#ifdef __powerpc64__
+#define _PR_SI_ARCHITECTURE "ppc64"
+#elif defined(__powerpc__)
+#define _PR_SI_ARCHITECTURE "ppc"
+#elif defined(__alpha)
+#define _PR_SI_ARCHITECTURE "alpha"
+#elif defined(__ia64__)
+#define _PR_SI_ARCHITECTURE "ia64"
+#elif defined(__x86_64__)
+#define _PR_SI_ARCHITECTURE "x86-64"
+#elif defined(__mc68000__)
+#define _PR_SI_ARCHITECTURE "m68k"
+#elif defined(__sparc__) && defined(__arch64__)
+#define _PR_SI_ARCHITECTURE "sparc64"
+#elif defined(__sparc__)
+#define _PR_SI_ARCHITECTURE "sparc"
+#elif defined(__i386__)
+#define _PR_SI_ARCHITECTURE "x86"
+#elif defined(__mips__)
+#define _PR_SI_ARCHITECTURE "mips"
+#elif defined(__arm__)
+#define _PR_SI_ARCHITECTURE "arm"
+#elif defined(__aarch64__)
+#define _PR_SI_ARCHITECTURE "aarch64"
+#elif defined(__hppa__)
+#define _PR_SI_ARCHITECTURE "hppa"
+#elif defined(__s390x__)
+#define _PR_SI_ARCHITECTURE "s390x"
+#elif defined(__s390__)
+#define _PR_SI_ARCHITECTURE "s390"
+#elif defined(__sh__)
+#define _PR_SI_ARCHITECTURE "sh"
+#elif defined(__avr32__)
+#define _PR_SI_ARCHITECTURE "avr32"
+#elif defined(__m32r__)
+#define _PR_SI_ARCHITECTURE "m32r"
+#elif defined(__or1k__)
+#define _PR_SI_ARCHITECTURE "or1k"
+#elif defined(__riscv) && (__riscv_xlen == 32)
+#define _PR_SI_ARCHITECTURE "riscv32"
+#elif defined(__riscv) && (__riscv_xlen == 64)
+#define _PR_SI_ARCHITECTURE "riscv64"
+#elif defined(__e2k__)
+#define _PR_SI_ARCHITECTURE "e2k"
+#elif defined(__arc__)
+#define _PR_SI_ARCHITECTURE "arc"
+#elif defined(__nios2__)
+#define _PR_SI_ARCHITECTURE "nios2"
+#elif defined(__microblaze__)
+#define _PR_SI_ARCHITECTURE "microblaze"
+#elif defined(__nds32__)
+#define _PR_SI_ARCHITECTURE "nds32"
+#elif defined(__xtensa__)
+#define _PR_SI_ARCHITECTURE "xtensa"
+#elif defined(__loongarch64)
+#define _PR_SI_ARCHITECTURE "loongarch64"
+#else
+#error "Unknown CPU architecture"
+#endif
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#if defined(__aarch64__) || defined(__mips__)
+#define _MD_MINIMUM_STACK_SIZE 0x20000
+#endif
+
+#undef HAVE_STACK_GROWING_UP
+
+/*
+ * Elf linux supports dl* functions
+ */
+#define HAVE_DLL
+#define USE_DLFCN
+#if defined(ANDROID)
+#define NO_DLOPEN_NULL
+#endif
+
+#if defined(__FreeBSD_kernel__) || defined(__GNU__)
+#define _PR_HAVE_SOCKADDR_LEN
+#endif
+
+#if defined(__i386__)
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+extern PRInt32 _PR_x86_AtomicIncrement(PRInt32 *val);
+#define _MD_ATOMIC_INCREMENT _PR_x86_AtomicIncrement
+extern PRInt32 _PR_x86_AtomicDecrement(PRInt32 *val);
+#define _MD_ATOMIC_DECREMENT _PR_x86_AtomicDecrement
+extern PRInt32 _PR_x86_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+#define _MD_ATOMIC_ADD _PR_x86_AtomicAdd
+extern PRInt32 _PR_x86_AtomicSet(PRInt32 *val, PRInt32 newval);
+#define _MD_ATOMIC_SET _PR_x86_AtomicSet
+#endif
+
+#if defined(__ia64__)
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+extern PRInt32 _PR_ia64_AtomicIncrement(PRInt32 *val);
+#define _MD_ATOMIC_INCREMENT _PR_ia64_AtomicIncrement
+extern PRInt32 _PR_ia64_AtomicDecrement(PRInt32 *val);
+#define _MD_ATOMIC_DECREMENT _PR_ia64_AtomicDecrement
+extern PRInt32 _PR_ia64_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+#define _MD_ATOMIC_ADD _PR_ia64_AtomicAdd
+extern PRInt32 _PR_ia64_AtomicSet(PRInt32 *val, PRInt32 newval);
+#define _MD_ATOMIC_SET _PR_ia64_AtomicSet
+#endif
+
+#if defined(__x86_64__)
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+extern PRInt32 _PR_x86_64_AtomicIncrement(PRInt32 *val);
+#define _MD_ATOMIC_INCREMENT _PR_x86_64_AtomicIncrement
+extern PRInt32 _PR_x86_64_AtomicDecrement(PRInt32 *val);
+#define _MD_ATOMIC_DECREMENT _PR_x86_64_AtomicDecrement
+extern PRInt32 _PR_x86_64_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+#define _MD_ATOMIC_ADD _PR_x86_64_AtomicAdd
+extern PRInt32 _PR_x86_64_AtomicSet(PRInt32 *val, PRInt32 newval);
+#define _MD_ATOMIC_SET _PR_x86_64_AtomicSet
+#endif
+
+#if defined(__loongarch__)
+#if defined(__GNUC__)
+/* Use GCC built-in functions */
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+#define _MD_ATOMIC_INCREMENT(ptr) __sync_add_and_fetch(ptr, 1)
+#define _MD_ATOMIC_DECREMENT(ptr) __sync_sub_and_fetch(ptr, 1)
+#define _MD_ATOMIC_ADD(ptr, i) __sync_add_and_fetch(ptr, i)
+#define _MD_ATOMIC_SET(ptr, nv) __sync_lock_test_and_set(ptr, nv)
+#endif
+#endif
+
+#if defined(__or1k__)
+#if defined(__GNUC__)
+/* Use GCC built-in functions */
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+#define _MD_ATOMIC_INCREMENT(ptr) __sync_add_and_fetch(ptr, 1)
+#define _MD_ATOMIC_DECREMENT(ptr) __sync_sub_and_fetch(ptr, 1)
+#define _MD_ATOMIC_ADD(ptr, i) __sync_add_and_fetch(ptr, i)
+#define _MD_ATOMIC_SET(ptr, nv) __sync_lock_test_and_set(ptr, nv)
+#endif
+#endif
+
+#if defined(__powerpc__) && !defined(__powerpc64__)
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+extern PRInt32 _PR_ppc_AtomicIncrement(PRInt32 *val);
+#define _MD_ATOMIC_INCREMENT _PR_ppc_AtomicIncrement
+extern PRInt32 _PR_ppc_AtomicDecrement(PRInt32 *val);
+#define _MD_ATOMIC_DECREMENT _PR_ppc_AtomicDecrement
+extern PRInt32 _PR_ppc_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+#define _MD_ATOMIC_ADD _PR_ppc_AtomicAdd
+extern PRInt32 _PR_ppc_AtomicSet(PRInt32 *val, PRInt32 newval);
+#define _MD_ATOMIC_SET _PR_ppc_AtomicSet
+#endif
+
+#if defined(__powerpc64__)
+#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
+/* Use GCC built-in functions */
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+#define _MD_ATOMIC_INCREMENT(ptr) __sync_add_and_fetch(ptr, 1)
+#define _MD_ATOMIC_DECREMENT(ptr) __sync_sub_and_fetch(ptr, 1)
+#define _MD_ATOMIC_ADD(ptr, i) __sync_add_and_fetch(ptr, i)
+#define _MD_ATOMIC_SET(ptr, nv) __sync_lock_test_and_set(ptr, nv)
+#endif
+#endif
+
+#if defined(__mips__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
+/* Use GCC built-in functions */
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+#define _MD_ATOMIC_INCREMENT(ptr) __sync_add_and_fetch(ptr, 1)
+#define _MD_ATOMIC_DECREMENT(ptr) __sync_sub_and_fetch(ptr, 1)
+#define _MD_ATOMIC_ADD(ptr, i) __sync_add_and_fetch(ptr, i)
+#define _MD_ATOMIC_SET(ptr, nv) __sync_lock_test_and_set(ptr, nv)
+#endif
+
+#if defined(__alpha)
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+#define _MD_ATOMIC_ADD(ptr, i) ({ \
+ PRInt32 __atomic_tmp, __atomic_ret; \
+ __asm__ __volatile__( \
+ "1: ldl_l %[ret], %[val] \n" \
+ " addl %[ret], %[inc], %[tmp] \n" \
+ " addl %[ret], %[inc], %[ret] \n" \
+ " stl_c %[tmp], %[val] \n" \
+ " beq %[tmp], 2f \n" \
+ ".subsection 2 \n" \
+ "2: br 1b \n" \
+ ".previous" \
+ : [ret] "=&r" (__atomic_ret), \
+ [tmp] "=&r" (__atomic_tmp), \
+ [val] "=m" (*ptr) \
+ : [inc] "Ir" (i), "m" (*ptr)); \
+ __atomic_ret; \
+})
+#define _MD_ATOMIC_INCREMENT(ptr) _MD_ATOMIC_ADD(ptr, 1)
+#define _MD_ATOMIC_DECREMENT(ptr) ({ \
+ PRInt32 __atomic_tmp, __atomic_ret; \
+ __asm__ __volatile__( \
+ "1: ldl_l %[ret], %[val] \n" \
+ " subl %[ret], 1, %[tmp] \n" \
+ " subl %[ret], 1, %[ret] \n" \
+ " stl_c %[tmp], %[val] \n" \
+ " beq %[tmp], 2f \n" \
+ ".subsection 2 \n" \
+ "2: br 1b \n" \
+ ".previous" \
+ : [ret] "=&r" (__atomic_ret), \
+ [tmp] "=&r" (__atomic_tmp), \
+ [val] "=m" (*ptr) \
+ : "m" (*ptr)); \
+ __atomic_ret; \
+})
+#define _MD_ATOMIC_SET(ptr, n) ({ \
+ PRInt32 __atomic_tmp, __atomic_ret; \
+ __asm__ __volatile__( \
+ "1: ldl_l %[ret], %[val] \n" \
+ " mov %[newval], %[tmp] \n" \
+ " stl_c %[tmp], %[val] \n" \
+ " beq %[tmp], 2f \n" \
+ ".subsection 2 \n" \
+ "2: br 1b \n" \
+ ".previous" \
+ : [ret] "=&r" (__atomic_ret), \
+ [tmp] "=&r"(__atomic_tmp), \
+ [val] "=m" (*ptr) \
+ : [newval] "Ir" (n), "m" (*ptr)); \
+ __atomic_ret; \
+})
+#endif
+
+#if defined(__arm__) || defined(__aarch64__)
+#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
+/* Use GCC built-in functions */
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+
+#define _MD_ATOMIC_INCREMENT(ptr) __sync_add_and_fetch(ptr, 1)
+#define _MD_ATOMIC_DECREMENT(ptr) __sync_sub_and_fetch(ptr, 1)
+#define _MD_ATOMIC_SET(ptr, nv) __sync_lock_test_and_set(ptr, nv)
+#define _MD_ATOMIC_ADD(ptr, i) __sync_add_and_fetch(ptr, i)
+
+#elif defined(_PR_ARM_KUSER)
+#define _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+
+/*
+ * The kernel provides this helper function at a fixed address with a fixed
+ * ABI signature, directly callable from user space.
+ *
+ * Definition:
+ * Atomically store newval in *ptr if *ptr is equal to oldval.
+ * Return zero if *ptr was changed or non-zero if no exchange happened.
+ */
+typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
+#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0)
+
+#define _MD_ATOMIC_INCREMENT(ptr) _MD_ATOMIC_ADD(ptr, 1)
+#define _MD_ATOMIC_DECREMENT(ptr) _MD_ATOMIC_ADD(ptr, -1)
+
+static inline PRInt32 _MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 n)
+{
+ PRInt32 ov, nv;
+ volatile PRInt32 *vp = ptr;
+
+ do {
+ ov = *vp;
+ nv = ov + n;
+ } while (__kernel_cmpxchg(ov, nv, vp));
+
+ return nv;
+}
+
+static inline PRInt32 _MD_ATOMIC_SET(PRInt32 *ptr, PRInt32 nv)
+{
+ PRInt32 ov;
+ volatile PRInt32 *vp = ptr;
+
+ do {
+ ov = *vp;
+ } while (__kernel_cmpxchg(ov, nv, vp));
+
+ return ov;
+}
+#endif
+#endif /* __arm__ */
+
+#define USE_SETJMP
+#if (defined(__GLIBC__) && __GLIBC__ >= 2) || defined(ANDROID)
+#define _PR_POLL_AVAILABLE
+#endif
+#undef _PR_USE_POLL
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+#if defined(__alpha) || defined(__ia64__)
+#define _PR_HAVE_LARGE_OFF_T
+#elif (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) \
+ || defined(ANDROID)
+#define _PR_HAVE_OFF64_T
+#else
+#define _PR_NO_LARGE_FILES
+#endif
+#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) \
+ || defined(ANDROID)
+#define _PR_INET6
+#define _PR_HAVE_INET_NTOP
+#define _PR_HAVE_GETHOSTBYNAME2
+#define _PR_HAVE_GETADDRINFO
+#define _PR_INET6_PROBE
+#endif
+#ifndef ANDROID
+#define _PR_HAVE_SYSV_SEMAPHORES
+#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY
+#endif
+/* Android has gethostbyname_r but not gethostbyaddr_r or gethostbyname2_r. */
+#if (__GLIBC__ >= 2) && defined(_PR_PTHREADS)
+#define _PR_HAVE_GETHOST_R
+#define _PR_HAVE_GETHOST_R_INT
+#endif
+
+#ifdef _PR_PTHREADS
+
+extern void _MD_CleanupBeforeExit(void);
+#define _MD_CLEANUP_BEFORE_EXIT _MD_CleanupBeforeExit
+
+#else /* ! _PR_PTHREADS */
+
+#include <setjmp.h>
+
+#define PR_CONTEXT_TYPE sigjmp_buf
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+#ifdef __powerpc__
+/*
+ * PowerPC based MkLinux
+ *
+ * On the PowerPC, the new style jmp_buf isn't used until glibc
+ * 2.1.
+ */
+#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[JB_GPR1]
+#else
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__misc[0]
+#endif /* glibc 2.1 or later */
+#define _MD_SET_FP(_t, val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) ((void *) 0)
+/* aix = 64, macos = 70 */
+#define PR_NUM_GCREGS 64
+
+#elif defined(__alpha)
+/* Alpha based Linux */
+
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[JB_SP]
+#define _MD_SET_FP(_t, val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) ((void *) 0)
+#define _MD_SP_TYPE long int
+#else
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp
+#define _MD_SET_FP(_t, val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) ((void *) 0)
+#define _MD_SP_TYPE __ptr_t
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+
+/* XXX not sure if this is correct, or maybe it should be 17? */
+#define PR_NUM_GCREGS 9
+
+#elif defined(__ia64__)
+
+#define _MD_GET_SP(_t) ((long *)((_t)->md.context[0].__jmpbuf)[0])
+#define _MD_SET_FP(_t, val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) ((void *) 0)
+#define _MD_SP_TYPE long int
+
+#define PR_NUM_GCREGS _JBLEN
+
+#elif defined(__mc68000__)
+/* m68k based Linux */
+
+/*
+ * On the m68k, glibc still uses the old style sigjmp_buf, even
+ * in glibc 2.0.7.
+ */
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp
+#define _MD_SET_FP(_t, val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) ((void *) 0)
+#define _MD_SP_TYPE int
+#else
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp
+#define _MD_SET_FP(_t, val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) ((void *) 0)
+#define _MD_SP_TYPE __ptr_t
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+
+/* XXX not sure if this is correct, or maybe it should be 17? */
+#define PR_NUM_GCREGS 9
+
+#elif defined(__sparc__)
+/* Sparc */
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+/*
+ * You need glibc2-2.0.7-25 or later. The libraries that came with
+ * Red Hat 5.1 are not new enough, but they are in 5.2.
+ */
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[JB_SP]
+#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[JB_FP] = val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[JB_FP])
+#define _MD_SP_TYPE int
+#else
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__fp
+#define _MD_SET_FP(_t, val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) ((void *) 0)
+#define _MD_SP_TYPE __ptr_t
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+
+#elif defined(__i386__)
+/* Intel based Linux */
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[JB_SP]
+#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[JB_BP] = val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[JB_BP])
+#define _MD_SP_TYPE int
+#else
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp
+#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[0].__bp = val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) &((_t)->md.context[0].__jmpbuf[0].__bp)
+#define _MD_SP_TYPE __ptr_t
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+#define PR_NUM_GCREGS 6
+
+#elif defined(__mips__)
+/* Linux/MIPS */
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp
+#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[0].__fp = (val))
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[0].__fp)
+#define _MD_SP_TYPE __ptr_t
+#else
+#error "Linux/MIPS pre-glibc2 not supported yet"
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+
+#elif defined(__arm__)
+/* ARM/Linux */
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+#ifdef __ARM_EABI__
+/* EABI */
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[8]
+#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[7] = (val))
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[7])
+#define _MD_SP_TYPE __ptr_t
+#else /* __ARM_EABI__ */
+/* old ABI */
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[20]
+#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[19] = (val))
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[19])
+#define _MD_SP_TYPE __ptr_t
+#endif /* __ARM_EABI__ */
+#else
+#error "ARM/Linux pre-glibc2 not supported yet"
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+
+#elif defined(__sh__)
+/* SH/Linux */
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[7]
+#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[6] = (val))
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[6])
+#define _MD_SP_TYPE __ptr_t
+#else
+#error "SH/Linux pre-glibc2 not supported yet"
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+
+#elif defined(__m32r__)
+/* Linux/M32R */
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__regs[JB_SP]
+#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[0].__regs[JB_FP] = (val))
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[0].__regs[JB_FP])
+#define _MD_SP_TYPE __ptr_t
+#else
+#error "Linux/M32R pre-glibc2 not supported yet"
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+
+#else
+
+#error "Unknown CPU architecture"
+
+#endif /*__powerpc__*/
+
+/*
+** Initialize a thread context to run "_main()" when started
+*/
+#ifdef __powerpc__
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (sigsetjmp(CONTEXT(_thread), 1)) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 128); \
+ _thread->md.sp = _MD_GET_SP_PTR(_thread); \
+ _thread->md.fp = _MD_GET_FP_PTR(_thread); \
+ _MD_SET_FP(_thread, 0); \
+}
+
+#elif defined(__mips__)
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ (void) sigsetjmp(CONTEXT(_thread), 1); \
+ _thread->md.context[0].__jmpbuf[0].__pc = (__ptr_t) _main; \
+ _MD_GET_SP(_thread) = (_MD_SP_TYPE) ((_sp) - 64); \
+ _thread->md.sp = _MD_GET_SP_PTR(_thread); \
+ _thread->md.fp = _MD_GET_FP_PTR(_thread); \
+ _MD_SET_FP(_thread, 0); \
+}
+
+#else
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (sigsetjmp(CONTEXT(_thread), 1)) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (_MD_SP_TYPE) ((_sp) - 64); \
+ _thread->md.sp = _MD_GET_SP_PTR(_thread); \
+ _thread->md.fp = _MD_GET_FP_PTR(_thread); \
+ _MD_SET_FP(_thread, 0); \
+}
+
+#endif /*__powerpc__*/
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!sigsetjmp(CONTEXT(_thread), 1)) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ siglongjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ void *sp;
+ void *fp;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#include <sys/time.h> /* for FD_SETSIZE */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+extern PRStatus _MD_InitializeThread(PRThread *thread);
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread
+#define _MD_RESUME_THREAD(thread) _MD_resume_thread
+#define _MD_CLEAN_THREAD(_thread)
+
+extern PRStatus _MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+extern void _MD_SET_PRIORITY(struct _MDThread *thread, PRUintn newPri);
+extern PRStatus _MD_WAIT(PRThread *, PRIntervalTime timeout);
+extern PRStatus _MD_WAKEUP_WAITER(PRThread *);
+extern void _MD_YIELD(void);
+
+#endif /* ! _PR_PTHREADS */
+
+extern void _MD_EarlyInit(void);
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _PR_HAVE_CLOCK_MONOTONIC
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT __select
+
+#ifdef _PR_POLL_AVAILABLE
+#include <sys/poll.h>
+extern int __syscall_poll(struct pollfd *ufds, unsigned long int nfds,
+ int timeout);
+#define _MD_POLL __syscall_poll
+#endif
+
+/* For writev() */
+#include <sys/uio.h>
+
+extern void _MD_linux_map_sendfile_error(int err);
+
+#endif /* nspr_linux_defs_h___ */
diff --git a/nsprpub/pr/include/md/_netbsd.cfg b/nsprpub/pr/include/md/_netbsd.cfg
new file mode 100644
index 0000000000..1326556e0a
--- /dev/null
+++ b/nsprpub/pr/include/md/_netbsd.cfg
@@ -0,0 +1,351 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef NETBSD
+#define NETBSD
+#endif
+
+#define PR_AF_INET6 24 /* same as AF_INET6 */
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+
+#if defined(__i386__) || defined(__arm32__) || defined(__ARMEL__) || \
+ defined(__MIPSEL__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#elif defined(__sparc_v9__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#elif defined(__sparc__) || defined(__MIPSEB__) || defined(__ARMEB__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#elif defined(__alpha__)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__amd64__)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__powerpc__) || defined(__m68k__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else
+
+#error Must define constants for type sizes here.
+
+#endif
+
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_netbsd.h b/nsprpub/pr/include/md/_netbsd.h
new file mode 100644
index 0000000000..1ec0fe6ec8
--- /dev/null
+++ b/nsprpub/pr/include/md/_netbsd.h
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_netbsd_defs_h___
+#define nspr_netbsd_defs_h___
+
+#include <sys/syscall.h>
+#include <sys/param.h> /* for __NetBSD_Version__ */
+
+#define PR_LINKER_ARCH "netbsd"
+#define _PR_SI_SYSNAME "NetBSD"
+#if defined(__i386__)
+#define _PR_SI_ARCHITECTURE "x86"
+#elif defined(__alpha__)
+#define _PR_SI_ARCHITECTURE "alpha"
+#elif defined(__amd64__)
+#define _PR_SI_ARCHITECTURE "amd64"
+#elif defined(__m68k__)
+#define _PR_SI_ARCHITECTURE "m68k"
+#elif defined(__powerpc__)
+#define _PR_SI_ARCHITECTURE "powerpc"
+#elif defined(__sparc_v9__)
+#define _PR_SI_ARCHITECTURE "sparc64"
+#elif defined(__sparc__)
+#define _PR_SI_ARCHITECTURE "sparc"
+#elif defined(__mips__)
+#define _PR_SI_ARCHITECTURE "mips"
+#elif defined(__arm32__) || defined(__arm__) || defined(__armel__) \
+ || defined(__armeb__)
+#define _PR_SI_ARCHITECTURE "arm"
+#endif
+
+#if defined(__ELF__)
+#define PR_DLL_SUFFIX ".so"
+#else
+#define PR_DLL_SUFFIX ".so.1.0"
+#endif
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+#define _PR_HAVE_SOCKADDR_LEN
+#define _PR_NO_LARGE_FILES
+#define _PR_STAT_HAS_ST_ATIMESPEC
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_HAVE_SYSV_SEMAPHORES
+#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY
+
+#if __NetBSD_Version__ >= 105000000
+#define _PR_INET6
+#define _PR_HAVE_INET_NTOP
+#define _PR_HAVE_GETHOSTBYNAME2
+#define _PR_HAVE_GETADDRINFO
+#define _PR_INET6_PROBE
+#endif
+
+#define USE_SETJMP
+
+#ifndef _PR_PTHREADS
+#include <setjmp.h>
+
+#define PR_CONTEXT_TYPE sigjmp_buf
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+#if defined(__i386__) || defined(__sparc__) || defined(__m68k__) || defined(__powerpc__)
+#define JB_SP_INDEX 2
+#elif defined(__mips__)
+#define JB_SP_INDEX 4
+#elif defined(__alpha__)
+#define JB_SP_INDEX 34
+#elif defined(__arm32__)
+/*
+ * On the arm32, the jmpbuf regs underwent a name change after NetBSD 1.3.
+ */
+#ifdef JMPBUF_REG_R13
+#define JB_SP_INDEX JMPBUF_REG_R13
+#else
+#define JB_SP_INDEX _JB_REG_R13
+#endif
+#else
+#error "Need to define SP index in jmp_buf here"
+#endif
+#define _MD_GET_SP(_th) (_th)->md.context[JB_SP_INDEX]
+
+#define PR_NUM_GCREGS _JBLEN
+
+/*
+** Initialize a thread context to run "_main()" when started
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (sigsetjmp(CONTEXT(_thread), 1)) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 64); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!sigsetjmp(CONTEXT(_thread), 1)) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ siglongjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread
+#define _MD_RESUME_THREAD(thread) _MD_resume_thread
+#define _MD_CLEAN_THREAD(_thread)
+
+#endif /* ! _PR_PTHREADS */
+
+extern void _MD_EarlyInit(void);
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _PR_HAVE_CLOCK_MONOTONIC
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+#if defined(_PR_POLL_AVAILABLE)
+#include <poll.h>
+#define _MD_POLL(fds,nfds,timeout) syscall(SYS_poll,fds,nfds,timeout)
+#endif
+
+#if NetBSD1_3 == 1L
+typedef unsigned int nfds_t;
+#endif
+
+#endif /* nspr_netbsd_defs_h___ */
diff --git a/nsprpub/pr/include/md/_nspr_pthread.h b/nsprpub/pr/include/md/_nspr_pthread.h
new file mode 100644
index 0000000000..01796e7721
--- /dev/null
+++ b/nsprpub/pr/include/md/_nspr_pthread.h
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_pthread_defs_h___
+#define nspr_pthread_defs_h___
+
+#include <pthread.h>
+#include "prthread.h"
+
+#if defined(PTHREADS_USER)
+/*
+** Thread Local Storage
+*/
+extern pthread_key_t current_thread_key;
+extern pthread_key_t current_cpu_key;
+extern pthread_key_t last_thread_key;
+extern pthread_key_t intsoff_key;
+
+#define _MD_CURRENT_THREAD() \
+ ((struct PRThread *) pthread_getspecific(current_thread_key))
+#define _MD_CURRENT_CPU() \
+ ((struct _PRCPU *) pthread_getspecific(current_cpu_key))
+#define _MD_LAST_THREAD() \
+ ((struct PRThread *) pthread_getspecific(last_thread_key))
+
+#define _MD_SET_CURRENT_THREAD(newval) \
+ pthread_setspecific(current_thread_key, (void *)newval)
+
+#define _MD_SET_CURRENT_CPU(newval) \
+ pthread_setspecific(current_cpu_key, (void *)newval)
+
+#define _MD_SET_LAST_THREAD(newval) \
+ pthread_setspecific(last_thread_key, (void *)newval)
+
+#define _MD_SET_INTSOFF(_val)
+#define _MD_GET_INTSOFF() 1
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ *status = PR_TRUE; \
+ if (SAVE_CONTEXT(_thread)) { \
+ (*_main)(); \
+ } \
+ _MD_SET_THR_SP(_thread, _sp); \
+ _thread->no_sched = 0; \
+ PR_END_MACRO
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ PR_BEGIN_MACRO \
+ PR_ASSERT(_thread->no_sched); \
+ if (!SAVE_CONTEXT(_thread)) { \
+ (_thread)->md.errcode = errno; \
+ _MD_SET_LAST_THREAD(_thread); \
+ _PR_Schedule(); \
+ } else { \
+ (_MD_LAST_THREAD())->no_sched = 0; \
+ } \
+ PR_END_MACRO
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+ PR_BEGIN_MACRO \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _thread->no_sched = 1; \
+ GOTO_CONTEXT(_thread); \
+ PR_END_MACRO
+
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ jmp_buf jb;
+ int id;
+ int errcode;
+ pthread_t pthread;
+ pthread_mutex_t pthread_mutex;
+ pthread_cond_t pthread_cond;
+ int wait;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ pthread_mutex_t mutex;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ pthread_mutex_t mutex;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ jmp_buf jb;
+ pthread_t pthread;
+ struct _MDCPU_Unix md_unix;
+};
+
+/*
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+*/
+
+extern pthread_mutex_t _pr_heapLock;
+
+#define _PR_LOCK(lock) pthread_mutex_lock(lock)
+
+#define _PR_UNLOCK(lock) pthread_mutex_unlock(lock)
+
+
+#define _PR_LOCK_HEAP() { \
+ if (_pr_primordialCPU) { \
+ _PR_LOCK(_pr_heapLock); \
+ }
+
+#define _PR_UNLOCK_HEAP() if (_pr_primordialCPU) { \
+ _PR_UNLOCK(_pr_heapLock); \
+ } \
+ }
+
+NSPR_API(PRStatus) _MD_NEW_LOCK(struct _MDLock *md);
+NSPR_API(void) _MD_FREE_LOCK(struct _MDLock *lockp);
+
+#define _MD_LOCK(_lockp) _PR_LOCK(&(_lockp)->mutex)
+#define _MD_UNLOCK(_lockp) _PR_UNLOCK(&(_lockp)->mutex)
+
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+#define _MD_CHECK_FOR_EXIT()
+
+NSPR_API(PRStatus) _MD_InitThread(struct PRThread *thread);
+#define _MD_INIT_THREAD _MD_InitThread
+#define _MD_INIT_ATTACHED_THREAD _MD_InitThread
+
+NSPR_API(void) _MD_ExitThread(struct PRThread *thread);
+#define _MD_EXIT_THREAD _MD_ExitThread
+
+NSPR_API(void) _MD_SuspendThread(struct PRThread *thread);
+#define _MD_SUSPEND_THREAD _MD_SuspendThread
+
+NSPR_API(void) _MD_ResumeThread(struct PRThread *thread);
+#define _MD_RESUME_THREAD _MD_ResumeThread
+
+NSPR_API(void) _MD_SuspendCPU(struct _PRCPU *thread);
+#define _MD_SUSPEND_CPU _MD_SuspendCPU
+
+NSPR_API(void) _MD_ResumeCPU(struct _PRCPU *thread);
+#define _MD_RESUME_CPU _MD_ResumeCPU
+
+#define _MD_BEGIN_SUSPEND_ALL()
+#define _MD_END_SUSPEND_ALL()
+#define _MD_BEGIN_RESUME_ALL()
+#define _MD_END_RESUME_ALL()
+
+NSPR_API(void) _MD_EarlyInit(void);
+#define _MD_EARLY_INIT _MD_EarlyInit
+
+#define _MD_FINAL_INIT _PR_UnixInit
+
+NSPR_API(void) _MD_InitLocks(void);
+#define _MD_INIT_LOCKS _MD_InitLocks
+
+NSPR_API(void) _MD_CleanThread(struct PRThread *thread);
+#define _MD_CLEAN_THREAD _MD_CleanThread
+
+NSPR_API(PRStatus) _MD_CreateThread(
+ struct PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+#define _MD_CREATE_THREAD _MD_CreateThread
+
+extern void _MD_CleanupBeforeExit(void);
+#define _MD_CLEANUP_BEFORE_EXIT _MD_CleanupBeforeExit
+
+NSPR_API(void) _MD_InitRunningCPU(struct _PRCPU *cpu);
+#define _MD_INIT_RUNNING_CPU _MD_InitRunningCPU
+
+/* The _PR_MD_WAIT_LOCK and _PR_MD_WAKEUP_WAITER functions put to sleep and
+ * awaken a thread which is waiting on a lock or cvar.
+ */
+NSPR_API(PRStatus) _MD_wait(struct PRThread *, PRIntervalTime timeout);
+#define _MD_WAIT _MD_wait
+
+NSPR_API(PRStatus) _MD_WakeupWaiter(struct PRThread *);
+#define _MD_WAKEUP_WAITER _MD_WakeupWaiter
+
+NSPR_API(void) _MD_SetPriority(struct _MDThread *thread,
+ PRThreadPriority newPri);
+#define _MD_SET_PRIORITY _MD_SetPriority
+
+#endif /* PTHREADS_USER */
+
+#endif /* nspr_pthread_defs_h___ */
diff --git a/nsprpub/pr/include/md/_nto.cfg b/nsprpub/pr/include/md/_nto.cfg
new file mode 100644
index 0000000000..b76a56057e
--- /dev/null
+++ b/nsprpub/pr/include/md/_nto.cfg
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef NTO
+#define NTO
+#endif
+
+#define PR_AF_INET6 24 /* same as AF_INET6 */
+
+#ifdef __i386__
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1L
+#define PR_BYTES_PER_SHORT 2L
+#define PR_BYTES_PER_INT 4L
+#define PR_BYTES_PER_INT64 8L
+#define PR_BYTES_PER_LONG 4L
+#define PR_BYTES_PER_FLOAT 4L
+#define PR_BYTES_PER_DOUBLE 8L
+#define PR_BYTES_PER_WORD 4L
+#define PR_BYTES_PER_DWORD 8L
+
+#define PR_BITS_PER_BYTE 8L
+#define PR_BITS_PER_SHORT 16L
+#define PR_BITS_PER_INT 32L
+#define PR_BITS_PER_INT64 64L
+#define PR_BITS_PER_LONG 32L
+#define PR_BITS_PER_FLOAT 32L
+#define PR_BITS_PER_DOUBLE 64L
+#define PR_BITS_PER_WORD 32L
+
+#define PR_BITS_PER_BYTE_LOG2 3L
+#define PR_BITS_PER_SHORT_LOG2 4L
+#define PR_BITS_PER_INT_LOG2 5L
+#define PR_BITS_PER_INT64_LOG2 6L
+#define PR_BITS_PER_LONG_LOG2 5L
+#define PR_BITS_PER_FLOAT_LOG2 5L
+#define PR_BITS_PER_DOUBLE_LOG2 6L
+#define PR_BITS_PER_WORD_LOG2 5L
+
+#define PR_ALIGN_OF_SHORT 2L
+#define PR_ALIGN_OF_INT 4L
+#define PR_ALIGN_OF_LONG 4L
+#define PR_ALIGN_OF_INT64 4L
+#define PR_ALIGN_OF_FLOAT 4L
+#define PR_ALIGN_OF_DOUBLE 4L
+#define PR_ALIGN_OF_POINTER 4L
+#define PR_ALIGN_OF_WORD 4L
+
+#define PR_BYTES_PER_WORD_LOG2 2L
+#define PR_BYTES_PER_DWORD_LOG2 3L
+#define PR_WORDS_PER_DWORD_LOG2 1L
+
+#else
+
+#error Undefined CPU Architecture
+
+#endif
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_nto.h b/nsprpub/pr/include/md/_nto.h
new file mode 100644
index 0000000000..1094dd183f
--- /dev/null
+++ b/nsprpub/pr/include/md/_nto.h
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_nto_defs_h___
+#define nspr_nto_defs_h___
+
+/*
+** Internal configuration macros
+*/
+#define PR_LINKER_ARCH "nto"
+#define _PR_SI_SYSNAME "NTO"
+#define _PR_SI_ARCHITECTURE "x86"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MINIMUM_STACK_SIZE 131072L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#ifndef HAVE_WEAK_IO_SYMBOLS
+#define HAVE_WEAK_IO_SYMBOLS
+#endif
+
+#undef _PR_POLL_AVAILABLE
+#undef _PR_USE_POLL
+#define _PR_HAVE_SOCKADDR_LEN
+#undef HAVE_BSD_FLOCK
+#define HAVE_FCNTL_FILE_LOCKING
+#define _PR_NO_LARGE_FILES
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+#define PR_HAVE_POSIX_NAMED_SHARED_MEMORY
+#define _PR_HAVE_POSIX_SEMAPHORES
+
+#undef FD_SETSIZE
+#define FD_SETSIZE 4096
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+#define NEED_STRFTIME_LOCK
+#define NEED_TIME_R
+#define _PR_NEED_STRCASECMP
+
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR
+#endif
+
+#define USE_SETJMP
+
+#include <setjmp.h>
+
+#define _SETJMP setjmp
+#define _LONGJMP longjmp
+#define _PR_CONTEXT_TYPE jmp_buf
+#define _PR_NUM_GCREGS _JBLEN
+#define _MD_GET_SP(_t) (_t)->md.context[7]
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if(_SETJMP(CONTEXT(_thread))) (*_main)(); \
+ _MD_GET_SP(_thread) = (int) ((_sp) - 128); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!_SETJMP(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _LONGJMP(CONTEXT(_thread), 1); \
+}
+
+/*
+** Machine-dependent (MD) data structures.
+*/
+struct _MDThread {
+ _PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+** md-specific cpu structure field
+*/
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD], fd_write_cnt[_PR_MD_MAX_OSFD], fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_INTERVAL_USE_GTOD
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+/*
+** We wrapped the select() call. _MD_SELECT refers to the built-in,
+** unwrapped version.
+*/
+#define _MD_SELECT select
+
+#define SA_RESTART 0
+
+#endif /* nspr_nto_defs_h___ */
diff --git a/nsprpub/pr/include/md/_openbsd.cfg b/nsprpub/pr/include/md/_openbsd.cfg
new file mode 100644
index 0000000000..b68d6e9cd6
--- /dev/null
+++ b/nsprpub/pr/include/md/_openbsd.cfg
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef OPENBSD
+#define OPENBSD
+#endif
+
+#define PR_AF_INET6 24 /* same as AF_INET6 */
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+
+#if defined(__i386__) || defined(__arm__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#elif defined(__amd64__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#elif defined(__sparc_v9__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__sparc__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#elif defined(__alpha__)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__powerpc__) || defined(__m68k__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else
+
+#error Must define constants for type sizes here.
+
+#endif
+
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_openbsd.h b/nsprpub/pr/include/md/_openbsd.h
new file mode 100644
index 0000000000..1949631a96
--- /dev/null
+++ b/nsprpub/pr/include/md/_openbsd.h
@@ -0,0 +1,213 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_openbsd_defs_h___
+#define nspr_openbsd_defs_h___
+
+#include <sys/syscall.h>
+
+#define PR_LINKER_ARCH "openbsd"
+#define _PR_SI_SYSNAME "OPENBSD"
+#if defined(__i386__)
+#define _PR_SI_ARCHITECTURE "x86"
+#elif defined(__alpha__)
+#define _PR_SI_ARCHITECTURE "alpha"
+#elif defined(__amd64__)
+#define _PR_SI_ARCHITECTURE "amd64"
+#elif defined(__powerpc64__)
+#define _PR_SI_ARCHITECTURE "powerpc64"
+#elif defined(__powerpc__)
+#define _PR_SI_ARCHITECTURE "powerpc"
+#elif defined(__aarch64__)
+#define _PR_SI_ARCHITECTURE "aarch64"
+#elif defined(__sparc__)
+#define _PR_SI_ARCHITECTURE "sparc"
+#elif defined(__arm__)
+#define _PR_SI_ARCHITECTURE "arm"
+#endif
+
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+#define _PR_HAVE_SOCKADDR_LEN
+#define _PR_HAVE_LARGE_OFF_T
+#define _PR_STAT_HAS_ST_ATIMESPEC
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_HAVE_SYSV_SEMAPHORES
+#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY
+
+#define _PR_INET6
+#define _PR_HAVE_INET_NTOP
+#define _PR_HAVE_GETHOSTBYNAME2
+#define _PR_HAVE_GETADDRINFO
+#define _PR_INET6_PROBE
+
+#define USE_SETJMP
+
+#ifndef _PR_PTHREADS
+#include <setjmp.h>
+
+#define PR_CONTEXT_TYPE sigjmp_buf
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+#if defined(__i386__) || defined(__sparc__) || defined(__m68k__)
+#define JB_SP_INDEX 2
+#elif defined(__powerpc__)
+#define JB_SP_INDEX 1
+#elif defined(__alpha__)
+#define JB_SP_INDEX 34
+#elif defined(__amd64__)
+#define JB_SP_INDEX 6
+#elif defined(__arm__)
+#define JB_SP_INDEX 23
+#else
+#error "Need to define SP index in jmp_buf here"
+#endif
+#define _MD_GET_SP(_th) (_th)->md.context[JB_SP_INDEX]
+
+#define PR_NUM_GCREGS _JBLEN
+
+/*
+** Initialize a thread context to run "_main()" when started
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (sigsetjmp(CONTEXT(_thread), 1)) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 64); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!sigsetjmp(CONTEXT(_thread), 1)) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ siglongjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread
+#define _MD_RESUME_THREAD(thread) _MD_resume_thread
+#define _MD_CLEAN_THREAD(_thread)
+
+#endif /* ! _PR_PTHREADS */
+
+extern void _MD_EarlyInit(void);
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _PR_HAVE_CLOCK_MONOTONIC
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+#include <poll.h>
+#define _MD_POLL(fds,nfds,timeout) syscall(SYS_poll,fds,nfds,timeout)
+
+#if OpenBSD1_3 == 1L
+typedef unsigned int nfds_t;
+#endif
+
+#endif /* nspr_openbsd_defs_h___ */
diff --git a/nsprpub/pr/include/md/_os2.cfg b/nsprpub/pr/include/md/_os2.cfg
new file mode 100644
index 0000000000..67c2dedf54
--- /dev/null
+++ b/nsprpub/pr/include/md/_os2.cfg
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_PC
+#define XP_PC
+#endif
+
+#ifndef XP_OS2
+#define XP_OS2
+#endif
+
+#ifndef OS2
+#define OS2
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#ifdef NO_LONG_LONG
+#undef HAVE_LONG_LONG
+#else
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG 1
+#endif
+#endif
+
+#define PR_AF_INET6 24 /* same as AF_INET6 */
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 5
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_WORD 4
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 2
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_os2.h b/nsprpub/pr/include/md/_os2.h
new file mode 100644
index 0000000000..2c3165a146
--- /dev/null
+++ b/nsprpub/pr/include/md/_os2.h
@@ -0,0 +1,504 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_os2_defs_h___
+#define nspr_os2_defs_h___
+
+#ifndef NO_LONG_LONG
+#define INCL_LONGLONG
+#endif
+#define INCL_DOS
+#define INCL_DOSPROCESS
+#define INCL_DOSERRORS
+#define INCL_WIN
+#define INCL_WPS
+#include <os2.h>
+#include <sys/select.h>
+
+#include "prio.h"
+
+#include <errno.h>
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "os2"
+#define _PR_SI_SYSNAME "OS2"
+#define _PR_SI_ARCHITECTURE "x86" /* XXXMB hardcode for now */
+
+#define HAVE_DLL
+#define _PR_GLOBAL_THREADS_ONLY
+#undef HAVE_THREAD_AFFINITY
+#define _PR_HAVE_THREADSAFE_GETHOST
+#define _PR_HAVE_ATOMIC_OPS
+#define HAVE_NETINET_TCP_H
+
+#define HANDLE unsigned long
+#define HINSTANCE HMODULE
+
+/* --- Common User-Thread/Native-Thread Definitions --------------------- */
+
+/* --- Globals --- */
+extern struct PRLock *_pr_schedLock;
+
+/* --- Typedefs --- */
+typedef void (*FiberFunc)(void *);
+
+#define PR_NUM_GCREGS 8
+typedef PRInt32 PR_CONTEXT_TYPE[PR_NUM_GCREGS];
+#define GC_VMBASE 0x40000000
+#define GC_VMLIMIT 0x00FFFFFF
+typedef int (*FARPROC)();
+
+#define _MD_MAGIC_THREAD 0x22222222
+#define _MD_MAGIC_THREADSTACK 0x33333333
+#define _MD_MAGIC_SEGMENT 0x44444444
+#define _MD_MAGIC_DIR 0x55555555
+#define _MD_MAGIC_CV 0x66666666
+
+struct _MDSemaphore {
+ HEV sem;
+};
+
+struct _MDCPU {
+ int unused;
+};
+
+struct _MDThread {
+ HEV blocked_sema; /* Threads block on this when waiting
+ * for IO or CondVar.
+ */
+ PRBool inCVWaitQueue; /* PR_TRUE if the thread is in the
+ * wait queue of some cond var.
+ * PR_FALSE otherwise. */
+ TID handle; /* OS/2 thread handle */
+ void *sp; /* only valid when suspended */
+ PRUint32 magic; /* for debugging */
+ PR_CONTEXT_TYPE gcContext; /* Thread context for GC */
+ struct PRThread *prev, *next; /* used by the cvar wait queue to
+ * chain the PRThread structures
+ * together */
+};
+
+struct _MDThreadStack {
+ PRUint32 magic; /* for debugging */
+};
+
+struct _MDSegment {
+ PRUint32 magic; /* for debugging */
+};
+
+#undef PROFILE_LOCKS
+
+struct _MDDir {
+ HDIR d_hdl;
+ union {
+ FILEFINDBUF3 small;
+ FILEFINDBUF3L large;
+ } d_entry;
+ PRBool firstEntry; /* Is this the entry returned
+ * by FindFirstFile()? */
+ PRUint32 magic; /* for debugging */
+};
+
+struct _MDCVar {
+ PRUint32 magic;
+ struct PRThread *waitHead, *waitTail; /* the wait queue: a doubly-
+ * linked list of threads
+ * waiting on this condition
+ * variable */
+ PRIntn nwait; /* number of threads in the
+ * wait queue */
+};
+
+#define _MD_CV_NOTIFIED_LENGTH 6
+typedef struct _MDNotified _MDNotified;
+struct _MDNotified {
+ PRIntn length; /* # of used entries in this
+ * structure */
+ struct {
+ struct _MDCVar *cv; /* the condition variable notified */
+ PRIntn times; /* and the number of times notified */
+ struct PRThread *notifyHead; /* list of threads to wake up */
+ } cv[_MD_CV_NOTIFIED_LENGTH];
+ _MDNotified *link; /* link to another of these, or NULL */
+};
+
+struct _MDLock {
+ HMTX mutex; /* this is recursive on OS/2 */
+
+ /*
+ * When notifying cvars, there is no point in actually
+ * waking up the threads waiting on the cvars until we've
+ * released the lock. So, we temporarily record the cvars.
+ * When doing an unlock, we'll then wake up the waiting threads.
+ */
+ struct _MDNotified notified; /* array of conditions notified */
+#ifdef PROFILE_LOCKS
+ PRInt32 hitcount;
+ PRInt32 misscount;
+#endif
+};
+
+struct _MDFileDesc {
+ PRInt32 osfd; /* The osfd can come from one of three spaces:
+ * - For stdin, stdout, and stderr, we are using
+ * the libc file handle (0, 1, 2), which is an int.
+ * - For files and pipes, we are using OS/2 handles,
+ * which is a void*.
+ * - For sockets, we are using int
+ */
+};
+
+struct _MDProcess {
+ PID pid;
+};
+
+/* --- Misc stuff --- */
+#define _MD_GET_SP(thread) (thread)->md.gcContext[6]
+
+/* --- IO stuff --- */
+
+#define _MD_OPEN (_PR_MD_OPEN)
+#define _MD_OPEN_FILE (_PR_MD_OPEN)
+#define _MD_READ (_PR_MD_READ)
+#define _MD_WRITE (_PR_MD_WRITE)
+#define _MD_WRITEV (_PR_MD_WRITEV)
+#define _MD_LSEEK (_PR_MD_LSEEK)
+#define _MD_LSEEK64 (_PR_MD_LSEEK64)
+extern PRInt32 _MD_CloseFile(PRInt32 osfd);
+#define _MD_CLOSE_FILE _MD_CloseFile
+#define _MD_GETFILEINFO (_PR_MD_GETFILEINFO)
+#define _MD_GETFILEINFO64 (_PR_MD_GETFILEINFO64)
+#define _MD_GETOPENFILEINFO (_PR_MD_GETOPENFILEINFO)
+#define _MD_GETOPENFILEINFO64 (_PR_MD_GETOPENFILEINFO64)
+#define _MD_STAT (_PR_MD_STAT)
+#define _MD_RENAME (_PR_MD_RENAME)
+#define _MD_ACCESS (_PR_MD_ACCESS)
+#define _MD_DELETE (_PR_MD_DELETE)
+#define _MD_MKDIR (_PR_MD_MKDIR)
+#define _MD_MAKE_DIR (_PR_MD_MKDIR)
+#define _MD_RMDIR (_PR_MD_RMDIR)
+#define _MD_LOCKFILE (_PR_MD_LOCKFILE)
+#define _MD_TLOCKFILE (_PR_MD_TLOCKFILE)
+#define _MD_UNLOCKFILE (_PR_MD_UNLOCKFILE)
+
+/* --- Socket IO stuff --- */
+
+/* The ones that don't map directly may need to be re-visited... */
+#define _MD_EACCES EACCES
+#define _MD_EADDRINUSE EADDRINUSE
+#define _MD_EADDRNOTAVAIL EADDRNOTAVAIL
+#define _MD_EAFNOSUPPORT EAFNOSUPPORT
+#define _MD_EAGAIN EWOULDBLOCK
+#define _MD_EALREADY EALREADY
+#define _MD_EBADF EBADF
+#define _MD_ECONNREFUSED ECONNREFUSED
+#define _MD_ECONNRESET ECONNRESET
+#define _MD_EFAULT SOCEFAULT
+#define _MD_EINPROGRESS EINPROGRESS
+#define _MD_EINTR EINTR
+#define _MD_EINVAL EINVAL
+#define _MD_EISCONN EISCONN
+#define _MD_ENETUNREACH ENETUNREACH
+#define _MD_ENOENT ENOENT
+#define _MD_ENOTCONN ENOTCONN
+#define _MD_ENOTSOCK ENOTSOCK
+#define _MD_EOPNOTSUPP EOPNOTSUPP
+#define _MD_EWOULDBLOCK EWOULDBLOCK
+#define _MD_GET_SOCKET_ERROR() sock_errno()
+#ifndef INADDR_LOOPBACK /* For some reason this is not defined in OS2 tcpip */
+/* #define INADDR_LOOPBACK INADDR_ANY */
+#endif
+
+#define _MD_INIT_FILEDESC(fd)
+extern void _MD_MakeNonblock(PRFileDesc *f);
+#define _MD_MAKE_NONBLOCK _MD_MakeNonblock
+#define _MD_INIT_FD_INHERITABLE (_PR_MD_INIT_FD_INHERITABLE)
+#define _MD_QUERY_FD_INHERITABLE (_PR_MD_QUERY_FD_INHERITABLE)
+#define _MD_SHUTDOWN (_PR_MD_SHUTDOWN)
+#define _MD_LISTEN _PR_MD_LISTEN
+extern PRInt32 _MD_CloseSocket(PRInt32 osfd);
+#define _MD_CLOSE_SOCKET _MD_CloseSocket
+#define _MD_SENDTO (_PR_MD_SENDTO)
+#define _MD_RECVFROM (_PR_MD_RECVFROM)
+#define _MD_SOCKETPAIR (_PR_MD_SOCKETPAIR)
+#define _MD_GETSOCKNAME (_PR_MD_GETSOCKNAME)
+#define _MD_GETPEERNAME (_PR_MD_GETPEERNAME)
+#define _MD_GETSOCKOPT (_PR_MD_GETSOCKOPT)
+#define _MD_SETSOCKOPT (_PR_MD_SETSOCKOPT)
+
+#define _MD_FSYNC _PR_MD_FSYNC
+#define _MD_SET_FD_INHERITABLE (_PR_MD_SET_FD_INHERITABLE)
+
+#ifdef _PR_HAVE_ATOMIC_OPS
+#define _MD_INIT_ATOMIC()
+#define _MD_ATOMIC_INCREMENT _PR_MD_ATOMIC_INCREMENT
+#define _MD_ATOMIC_ADD _PR_MD_ATOMIC_ADD
+#define _MD_ATOMIC_DECREMENT _PR_MD_ATOMIC_DECREMENT
+#define _MD_ATOMIC_SET _PR_MD_ATOMIC_SET
+#endif
+
+#define _MD_INIT_IO (_PR_MD_INIT_IO)
+#define _MD_PR_POLL (_PR_MD_PR_POLL)
+
+#define _MD_SOCKET (_PR_MD_SOCKET)
+extern PRInt32 _MD_SocketAvailable(PRFileDesc *fd);
+#define _MD_SOCKETAVAILABLE _MD_SocketAvailable
+#define _MD_PIPEAVAILABLE _MD_SocketAvailable
+#define _MD_CONNECT (_PR_MD_CONNECT)
+extern PRInt32 _MD_Accept(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
+ PRIntervalTime timeout);
+#define _MD_ACCEPT _MD_Accept
+#define _MD_BIND (_PR_MD_BIND)
+#define _MD_RECV (_PR_MD_RECV)
+#define _MD_SEND (_PR_MD_SEND)
+
+/* --- Scheduler stuff --- */
+/* #define _MD_PAUSE_CPU _PR_MD_PAUSE_CPU */
+#define _MD_PAUSE_CPU
+
+/* --- DIR stuff --- */
+#define PR_DIRECTORY_SEPARATOR '\\'
+#define PR_DIRECTORY_SEPARATOR_STR "\\"
+#define PR_PATH_SEPARATOR ';'
+#define PR_PATH_SEPARATOR_STR ";"
+#define _MD_ERRNO() errno
+#define _MD_OPEN_DIR (_PR_MD_OPEN_DIR)
+#define _MD_CLOSE_DIR (_PR_MD_CLOSE_DIR)
+#define _MD_READ_DIR (_PR_MD_READ_DIR)
+
+/* --- Segment stuff --- */
+#define _MD_INIT_SEGS()
+#define _MD_ALLOC_SEGMENT(seg, size, vaddr) 0
+#define _MD_FREE_SEGMENT(seg)
+
+/* --- Environment Stuff --- */
+#define _MD_GET_ENV (_PR_MD_GET_ENV)
+#define _MD_PUT_ENV (_PR_MD_PUT_ENV)
+
+/* --- Threading Stuff --- */
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_INIT_THREAD (_PR_MD_INIT_THREAD)
+#define _MD_INIT_ATTACHED_THREAD (_PR_MD_INIT_THREAD)
+#define _MD_CREATE_THREAD (_PR_MD_CREATE_THREAD)
+#define _MD_YIELD (_PR_MD_YIELD)
+#define _MD_SET_PRIORITY (_PR_MD_SET_PRIORITY)
+#define _MD_CLEAN_THREAD (_PR_MD_CLEAN_THREAD)
+#define _MD_SETTHREADAFFINITYMASK (_PR_MD_SETTHREADAFFINITYMASK)
+#define _MD_GETTHREADAFFINITYMASK (_PR_MD_GETTHREADAFFINITYMASK)
+#define _MD_EXIT_THREAD (_PR_MD_EXIT_THREAD)
+#define _MD_SUSPEND_THREAD (_PR_MD_SUSPEND_THREAD)
+#define _MD_RESUME_THREAD (_PR_MD_RESUME_THREAD)
+#define _MD_SUSPEND_CPU (_PR_MD_SUSPEND_CPU)
+#define _MD_RESUME_CPU (_PR_MD_RESUME_CPU)
+#define _MD_WAKEUP_CPUS (_PR_MD_WAKEUP_CPUS)
+#define _MD_BEGIN_SUSPEND_ALL()
+#define _MD_BEGIN_RESUME_ALL()
+#define _MD_END_SUSPEND_ALL()
+#define _MD_END_RESUME_ALL()
+
+/* --- Lock stuff --- */
+#define _PR_LOCK _MD_LOCK
+#define _PR_UNLOCK _MD_UNLOCK
+
+#define _MD_NEW_LOCK (_PR_MD_NEW_LOCK)
+#define _MD_FREE_LOCK(lock) (DosCloseMutexSem((lock)->mutex))
+#define _MD_LOCK(lock) (DosRequestMutexSem((lock)->mutex, SEM_INDEFINITE_WAIT))
+#define _MD_TEST_AND_LOCK(lock) (DosRequestMutexSem((lock)->mutex, SEM_INDEFINITE_WAIT),0)
+#define _MD_UNLOCK (_PR_MD_UNLOCK)
+
+/* --- lock and cv waiting --- */
+#define _MD_WAIT (_PR_MD_WAIT)
+#define _MD_WAKEUP_WAITER (_PR_MD_WAKEUP_WAITER)
+
+/* --- CVar ------------------- */
+#define _MD_WAIT_CV (_PR_MD_WAIT_CV)
+#define _MD_NEW_CV (_PR_MD_NEW_CV)
+#define _MD_FREE_CV (_PR_MD_FREE_CV)
+#define _MD_NOTIFY_CV (_PR_MD_NOTIFY_CV )
+#define _MD_NOTIFYALL_CV (_PR_MD_NOTIFYALL_CV)
+
+/* XXXMB- the IOQ stuff is certainly not working correctly yet. */
+/* extern struct _MDLock _pr_ioq_lock; */
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+
+/* --- Initialization stuff --- */
+#define _MD_START_INTERRUPTS()
+#define _MD_STOP_INTERRUPTS()
+#define _MD_DISABLE_CLOCK_INTERRUPTS()
+#define _MD_ENABLE_CLOCK_INTERRUPTS()
+#define _MD_BLOCK_CLOCK_INTERRUPTS()
+#define _MD_UNBLOCK_CLOCK_INTERRUPTS()
+#define _MD_EARLY_INIT (_PR_MD_EARLY_INIT)
+#define _MD_FINAL_INIT()
+#define _MD_EARLY_CLEANUP()
+#define _MD_INIT_CPUS()
+#define _MD_INIT_RUNNING_CPU(cpu)
+
+struct PRProcess;
+struct PRProcessAttr;
+
+#define _MD_CREATE_PROCESS _PR_CreateOS2Process
+extern struct PRProcess * _PR_CreateOS2Process(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const struct PRProcessAttr *attr
+);
+
+#define _MD_DETACH_PROCESS _PR_DetachOS2Process
+extern PRStatus _PR_DetachOS2Process(struct PRProcess *process);
+
+/* --- Wait for a child process to terminate --- */
+#define _MD_WAIT_PROCESS _PR_WaitOS2Process
+extern PRStatus _PR_WaitOS2Process(struct PRProcess *process,
+ PRInt32 *exitCode);
+
+#define _MD_KILL_PROCESS _PR_KillOS2Process
+extern PRStatus _PR_KillOS2Process(struct PRProcess *process);
+
+#define _MD_CLEANUP_BEFORE_EXIT()
+#define _MD_EXIT (_PR_MD_EXIT)
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ *status = PR_TRUE; \
+ PR_END_MACRO
+#define _MD_SWITCH_CONTEXT
+#define _MD_RESTORE_CONTEXT
+
+/* --- Intervals --- */
+#define _MD_INTERVAL_INIT (_PR_MD_INTERVAL_INIT)
+#define _MD_GET_INTERVAL (_PR_MD_GET_INTERVAL)
+#define _MD_INTERVAL_PER_SEC (_PR_MD_INTERVAL_PER_SEC)
+#define _MD_INTERVAL_PER_MILLISEC() (_PR_MD_INTERVAL_PER_SEC() / 1000)
+#define _MD_INTERVAL_PER_MICROSEC() (_PR_MD_INTERVAL_PER_SEC() / 1000000)
+
+/* --- Native-Thread Specific Definitions ------------------------------- */
+
+typedef struct __NSPR_TLS
+{
+ struct PRThread *_pr_thread_last_run;
+ struct PRThread *_pr_currentThread;
+ struct _PRCPU *_pr_currentCPU;
+} _NSPR_TLS;
+
+extern _NSPR_TLS* pThreadLocalStorage;
+NSPR_API(void) _PR_MD_ENSURE_TLS(void);
+
+#define _MD_GET_ATTACHED_THREAD() pThreadLocalStorage->_pr_currentThread
+extern struct PRThread * _MD_CURRENT_THREAD(void);
+#define _MD_SET_CURRENT_THREAD(_thread) _PR_MD_ENSURE_TLS(); pThreadLocalStorage->_pr_currentThread = (_thread)
+
+#define _MD_LAST_THREAD() pThreadLocalStorage->_pr_thread_last_run
+#define _MD_SET_LAST_THREAD(_thread) _PR_MD_ENSURE_TLS(); pThreadLocalStorage->_pr_thread_last_run = (_thread)
+
+#define _MD_CURRENT_CPU() pThreadLocalStorage->_pr_currentCPU
+#define _MD_SET_CURRENT_CPU(_cpu) _PR_MD_ENSURE_TLS(); pThreadLocalStorage->_pr_currentCPU = (_cpu)
+
+/* lth. #define _MD_SET_INTSOFF(_val) (_pr_ints_off = (_val)) */
+/* lth. #define _MD_GET_INTSOFF() _pr_ints_off */
+/* lth. #define _MD_INCREMENT_INTSOFF() (_pr_ints_off++) */
+/* lth. #define _MD_DECREMENT_INTSOFF() (_pr_ints_off--) */
+
+/* --- Scheduler stuff --- */
+#define LOCK_SCHEDULER() 0
+#define UNLOCK_SCHEDULER() 0
+#define _PR_LockSched() 0
+#define _PR_UnlockSched() 0
+
+/* --- Initialization stuff --- */
+#define _MD_INIT_LOCKS()
+
+/* --- Stack stuff --- */
+#define _MD_INIT_STACK(stack, redzone)
+#define _MD_CLEAR_STACK(stack)
+
+/* --- Memory-mapped files stuff --- */
+/* ReadOnly and WriteCopy modes are simulated on OS/2;
+ * ReadWrite mode is not supported.
+ */
+struct _MDFileMap {
+ PROffset64 maxExtent;
+};
+
+extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size);
+#define _MD_CREATE_FILE_MAP _MD_CreateFileMap
+
+extern PRInt32 _MD_GetMemMapAlignment(void);
+#define _MD_GET_MEM_MAP_ALIGNMENT _MD_GetMemMapAlignment
+
+extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset,
+ PRUint32 len);
+#define _MD_MEM_MAP _MD_MemMap
+
+extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size);
+#define _MD_MEM_UNMAP _MD_MemUnmap
+
+extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap);
+#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap
+
+/* Some stuff for setting up thread contexts */
+typedef ULONG DWORD, *PDWORD;
+
+/* The following definitions and two structures are new in OS/2 Warp 4.0.
+ */
+#ifndef CONTEXT_CONTROL
+#define CONTEXT_CONTROL 0x00000001
+#define CONTEXT_INTEGER 0x00000002
+#define CONTEXT_SEGMENTS 0x00000004
+#define CONTEXT_FLOATING_POINT 0x00000008
+#define CONTEXT_FULL 0x0000000F
+
+#pragma pack(2)
+typedef struct _FPREG {
+ ULONG losig; /* Low 32-bits of the significand. */
+ ULONG hisig; /* High 32-bits of the significand. */
+ USHORT signexp; /* Sign and exponent. */
+} FPREG;
+typedef struct _CONTEXTRECORD {
+ ULONG ContextFlags;
+ ULONG ctx_env[7];
+ FPREG ctx_stack[8];
+ ULONG ctx_SegGs; /* GS register. */
+ ULONG ctx_SegFs; /* FS register. */
+ ULONG ctx_SegEs; /* ES register. */
+ ULONG ctx_SegDs; /* DS register. */
+ ULONG ctx_RegEdi; /* EDI register. */
+ ULONG ctx_RegEsi; /* ESI register. */
+ ULONG ctx_RegEax; /* EAX register. */
+ ULONG ctx_RegEbx; /* EBX register. */
+ ULONG ctx_RegEcx; /* ECX register. */
+ ULONG ctx_RegEdx; /* EDX register. */
+ ULONG ctx_RegEbp; /* EBP register. */
+ ULONG ctx_RegEip; /* EIP register. */
+ ULONG ctx_SegCs; /* CS register. */
+ ULONG ctx_EFlags; /* EFLAGS register. */
+ ULONG ctx_RegEsp; /* ESP register. */
+ ULONG ctx_SegSs; /* SS register. */
+} CONTEXTRECORD, *PCONTEXTRECORD;
+#pragma pack()
+#endif
+
+extern APIRET (* APIENTRY QueryThreadContext)(TID, ULONG, PCONTEXTRECORD);
+
+/*
+#define _pr_tid (((PTIB2)_getTIBvalue(offsetof(TIB, tib_ptib2)))->tib2_ultid)
+#define _pr_current_Thread (_system_tls[_pr_tid-1].__pr_current_thread)
+*/
+
+/* Some simple mappings of Windows API's to OS/2 API's to make our lives a
+ * little bit easier. Only add one here if it is a DIRECT mapping. We are
+ * not emulating anything. Just mapping.
+ */
+#define FreeLibrary(x) DosFreeModule((HMODULE)x)
+#define OutputDebugStringA(x)
+
+extern int _MD_os2_get_nonblocking_connect_error(int osfd);
+
+#endif /* nspr_os2_defs_h___ */
diff --git a/nsprpub/pr/include/md/_os2_errors.h b/nsprpub/pr/include/md/_os2_errors.h
new file mode 100644
index 0000000000..556a9ae8d4
--- /dev/null
+++ b/nsprpub/pr/include/md/_os2_errors.h
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_os2_errors_h___
+#define nspr_os2_errors_h___
+
+#include "md/_os2.h"
+#ifndef assert
+#include <assert.h>
+#endif
+
+NSPR_API(void) _MD_os2_map_default_error(PRInt32 err);
+#define _PR_MD_MAP_DEFAULT_ERROR _MD_os2_map_default_error
+
+NSPR_API(void) _MD_os2_map_opendir_error(PRInt32 err);
+#define _PR_MD_MAP_OPENDIR_ERROR _MD_os2_map_opendir_error
+
+NSPR_API(void) _MD_os2_map_closedir_error(PRInt32 err);
+#define _PR_MD_MAP_CLOSEDIR_ERROR _MD_os2_map_closedir_error
+
+NSPR_API(void) _MD_os2_readdir_error(PRInt32 err);
+#define _PR_MD_MAP_READDIR_ERROR _MD_os2_readdir_error
+
+NSPR_API(void) _MD_os2_map_delete_error(PRInt32 err);
+#define _PR_MD_MAP_DELETE_ERROR _MD_os2_map_delete_error
+
+NSPR_API(void) _MD_os2_map_stat_error(PRInt32 err);
+#define _PR_MD_MAP_STAT_ERROR _MD_os2_map_stat_error
+
+NSPR_API(void) _MD_os2_map_fstat_error(PRInt32 err);
+#define _PR_MD_MAP_FSTAT_ERROR _MD_os2_map_fstat_error
+
+NSPR_API(void) _MD_os2_map_rename_error(PRInt32 err);
+#define _PR_MD_MAP_RENAME_ERROR _MD_os2_map_rename_error
+
+NSPR_API(void) _MD_os2_map_access_error(PRInt32 err);
+#define _PR_MD_MAP_ACCESS_ERROR _MD_os2_map_access_error
+
+NSPR_API(void) _MD_os2_map_mkdir_error(PRInt32 err);
+#define _PR_MD_MAP_MKDIR_ERROR _MD_os2_map_mkdir_error
+
+NSPR_API(void) _MD_os2_map_rmdir_error(PRInt32 err);
+#define _PR_MD_MAP_RMDIR_ERROR _MD_os2_map_rmdir_error
+
+NSPR_API(void) _MD_os2_map_read_error(PRInt32 err);
+#define _PR_MD_MAP_READ_ERROR _MD_os2_map_read_error
+
+NSPR_API(void) _MD_os2_map_transmitfile_error(PRInt32 err);
+#define _PR_MD_MAP_TRANSMITFILE_ERROR _MD_os2_map_transmitfile_error
+
+NSPR_API(void) _MD_os2_map_write_error(PRInt32 err);
+#define _PR_MD_MAP_WRITE_ERROR _MD_os2_map_write_error
+
+NSPR_API(void) _MD_os2_map_lseek_error(PRInt32 err);
+#define _PR_MD_MAP_LSEEK_ERROR _MD_os2_map_lseek_error
+
+NSPR_API(void) _MD_os2_map_fsync_error(PRInt32 err);
+#define _PR_MD_MAP_FSYNC_ERROR _MD_os2_map_fsync_error
+
+NSPR_API(void) _MD_os2_map_close_error(PRInt32 err);
+#define _PR_MD_MAP_CLOSE_ERROR _MD_os2_map_close_error
+
+NSPR_API(void) _MD_os2_map_socket_error(PRInt32 err);
+#define _PR_MD_MAP_SOCKET_ERROR _MD_os2_map_socket_error
+
+NSPR_API(void) _MD_os2_map_recv_error(PRInt32 err);
+#define _PR_MD_MAP_RECV_ERROR _MD_os2_map_recv_error
+
+NSPR_API(void) _MD_os2_map_recvfrom_error(PRInt32 err);
+#define _PR_MD_MAP_RECVFROM_ERROR _MD_os2_map_recvfrom_error
+
+NSPR_API(void) _MD_os2_map_send_error(PRInt32 err);
+#define _PR_MD_MAP_SEND_ERROR _MD_os2_map_send_error
+
+NSPR_API(void) _MD_os2_map_sendto_error(PRInt32 err);
+#define _PR_MD_MAP_SENDTO_ERROR _MD_os2_map_sendto_error
+
+NSPR_API(void) _MD_os2_map_writev_error(int err);
+#define _PR_MD_MAP_WRITEV_ERROR _MD_os2_map_writev_error
+
+NSPR_API(void) _MD_os2_map_accept_error(PRInt32 err);
+#define _PR_MD_MAP_ACCEPT_ERROR _MD_os2_map_accept_error
+
+NSPR_API(void) _MD_os2_map_acceptex_error(PRInt32 err);
+#define _PR_MD_MAP_ACCEPTEX_ERROR _MD_os2_map_acceptex_error
+
+NSPR_API(void) _MD_os2_map_connect_error(PRInt32 err);
+#define _PR_MD_MAP_CONNECT_ERROR _MD_os2_map_connect_error
+
+NSPR_API(void) _MD_os2_map_bind_error(PRInt32 err);
+#define _PR_MD_MAP_BIND_ERROR _MD_os2_map_bind_error
+
+NSPR_API(void) _MD_os2_map_listen_error(PRInt32 err);
+#define _PR_MD_MAP_LISTEN_ERROR _MD_os2_map_listen_error
+
+NSPR_API(void) _MD_os2_map_shutdown_error(PRInt32 err);
+#define _PR_MD_MAP_SHUTDOWN_ERROR _MD_os2_map_shutdown_error
+
+NSPR_API(void) _MD_os2_map_socketpair_error(int err);
+#define _PR_MD_MAP_SOCKETPAIR_ERROR _MD_os2_map_socketpair_error
+
+NSPR_API(void) _MD_os2_map_getsockname_error(PRInt32 err);
+#define _PR_MD_MAP_GETSOCKNAME_ERROR _MD_os2_map_getsockname_error
+
+NSPR_API(void) _MD_os2_map_getpeername_error(PRInt32 err);
+#define _PR_MD_MAP_GETPEERNAME_ERROR _MD_os2_map_getpeername_error
+
+NSPR_API(void) _MD_os2_map_getsockopt_error(PRInt32 err);
+#define _PR_MD_MAP_GETSOCKOPT_ERROR _MD_os2_map_getsockopt_error
+
+NSPR_API(void) _MD_os2_map_setsockopt_error(PRInt32 err);
+#define _PR_MD_MAP_SETSOCKOPT_ERROR _MD_os2_map_setsockopt_error
+
+NSPR_API(void) _MD_os2_map_open_error(PRInt32 err);
+#define _PR_MD_MAP_OPEN_ERROR _MD_os2_map_open_error
+
+NSPR_API(void) _MD_os2_map_gethostname_error(PRInt32 err);
+#define _PR_MD_MAP_GETHOSTNAME_ERROR _MD_os2_map_gethostname_error
+
+NSPR_API(void) _MD_os2_map_select_error(PRInt32 err);
+#define _PR_MD_MAP_SELECT_ERROR _MD_os2_map_select_error
+
+NSPR_API(void) _MD_os2_map_lockf_error(int err);
+#define _PR_MD_MAP_LOCKF_ERROR _MD_os2_map_lockf_error
+
+#endif /* nspr_os2_errors_h___ */
diff --git a/nsprpub/pr/include/md/_pcos.h b/nsprpub/pr/include/md/_pcos.h
new file mode 100644
index 0000000000..679fe19b10
--- /dev/null
+++ b/nsprpub/pr/include/md/_pcos.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prpcos_h___
+#define prpcos_h___
+
+#define PR_DLL_SUFFIX ".dll"
+
+#include <stdlib.h>
+
+#define DIRECTORY_SEPARATOR '\\'
+#define DIRECTORY_SEPARATOR_STR "\\"
+#define PATH_SEPARATOR ';'
+
+/*
+** Routines for processing command line arguments
+*/
+PR_BEGIN_EXTERN_C
+#ifndef XP_OS2
+extern char *optarg;
+extern int optind;
+extern int getopt(int argc, char **argv, char *spec);
+#endif
+PR_END_EXTERN_C
+
+
+/*
+** Definitions of directory structures amd functions
+** These definitions are from:
+** <dirent.h>
+*/
+#ifdef XP_OS2
+#include <sys/types.h>
+#endif
+#include <sys/stat.h>
+#include <io.h>
+#include <fcntl.h> /* O_BINARY */
+
+#ifdef OS2
+extern PRStatus _MD_OS2GetHostName(char *name, PRUint32 namelen);
+#define _MD_GETHOSTNAME _MD_OS2GetHostName
+#else
+extern PRStatus _MD_WindowsGetHostName(char *name, PRUint32 namelen);
+#define _MD_GETHOSTNAME _MD_WindowsGetHostName
+extern PRStatus _MD_WindowsGetSysInfo(PRSysInfo cmd, char *name, PRUint32 namelen);
+#define _MD_GETSYSINFO _MD_WindowsGetSysInfo
+#endif
+
+#endif /* prpcos_h___ */
diff --git a/nsprpub/pr/include/md/_pth.h b/nsprpub/pr/include/md/_pth.h
new file mode 100644
index 0000000000..37d26e7005
--- /dev/null
+++ b/nsprpub/pr/include/md/_pth.h
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_pth_defs_h_
+#define nspr_pth_defs_h_
+
+/*
+** Appropriate definitions of entry points not used in a pthreads world
+*/
+#define _PR_MD_BLOCK_CLOCK_INTERRUPTS()
+#define _PR_MD_UNBLOCK_CLOCK_INTERRUPTS()
+#define _PR_MD_DISABLE_CLOCK_INTERRUPTS()
+#define _PR_MD_ENABLE_CLOCK_INTERRUPTS()
+
+#if defined(BSDI)
+/*
+ * Mutex and condition attributes are not supported. The attr
+ * argument to pthread_mutex_init() and pthread_cond_init() must
+ * be passed as NULL.
+ *
+ * The memset calls in _PT_PTHREAD_MUTEX_INIT and _PT_PTHREAD_COND_INIT
+ * are to work around BSDI's using a single bit to indicate a mutex
+ * or condition variable is initialized. This entire BSDI section
+ * will go away when BSDI releases updated threads libraries for
+ * BSD/OS 3.1 and 4.0.
+ */
+#define _PT_PTHREAD_MUTEXATTR_INIT(x) 0
+#define _PT_PTHREAD_MUTEXATTR_DESTROY(x) /* */
+#define _PT_PTHREAD_MUTEX_INIT(m, a) (memset(&(m), 0, sizeof(m)), \
+ pthread_mutex_init(&(m), NULL))
+#define _PT_PTHREAD_MUTEX_IS_LOCKED(m) (EBUSY == pthread_mutex_trylock(&(m)))
+#define _PT_PTHREAD_CONDATTR_INIT(x) 0
+#define _PT_PTHREAD_CONDATTR_DESTROY(x) /* */
+#define _PT_PTHREAD_COND_INIT(m, a) (memset(&(m), 0, sizeof(m)), \
+ pthread_cond_init(&(m), NULL))
+#else
+#define _PT_PTHREAD_MUTEXATTR_INIT pthread_mutexattr_init
+#define _PT_PTHREAD_MUTEXATTR_DESTROY pthread_mutexattr_destroy
+#define _PT_PTHREAD_MUTEX_INIT(m, a) pthread_mutex_init(&(m), &(a))
+#if defined(FREEBSD)
+#define _PT_PTHREAD_MUTEX_IS_LOCKED(m) pt_pthread_mutex_is_locked(&(m))
+#else
+#define _PT_PTHREAD_MUTEX_IS_LOCKED(m) (EBUSY == pthread_mutex_trylock(&(m)))
+#endif
+#if defined(ANDROID)
+/* Conditional attribute init and destroy aren't implemented in bionic. */
+#define _PT_PTHREAD_CONDATTR_INIT(x) 0
+#define _PT_PTHREAD_CONDATTR_DESTROY(x) /* */
+#else
+#define _PT_PTHREAD_CONDATTR_INIT pthread_condattr_init
+#define _PT_PTHREAD_CONDATTR_DESTROY pthread_condattr_destroy
+#endif
+#define _PT_PTHREAD_COND_INIT(m, a) pthread_cond_init(&(m), &(a))
+#endif
+
+/* The pthreads standard does not specify an invalid value for the
+ * pthread_t handle. (0 is usually an invalid pthread identifier
+ * but there are exceptions, for example, DG/UX.) These macros
+ * define a way to set the handle to or compare the handle with an
+ * invalid identifier. These macros are not portable and may be
+ * more of a problem as we adapt to more pthreads implementations.
+ * They are only used in the PRMonitor functions. Do not use them
+ * in new code.
+ *
+ * Unfortunately some of our clients depend on certain properties
+ * of our PRMonitor implementation, preventing us from replacing
+ * it by a portable implementation.
+ * - High-performance servers like the fact that PR_EnterMonitor
+ * only calls PR_Lock and PR_ExitMonitor only calls PR_Unlock.
+ * (A portable implementation would use a PRLock and a PRCondVar
+ * to implement the recursive lock in a monitor and call both
+ * PR_Lock and PR_Unlock in PR_EnterMonitor and PR_ExitMonitor.)
+ * Unfortunately this forces us to read the monitor owner field
+ * without holding a lock.
+ * - One way to make it safe to read the monitor owner field
+ * without holding a lock is to make that field a PRThread*
+ * (one should be able to read a pointer with a single machine
+ * instruction). However, PR_GetCurrentThread calls calloc if
+ * it is called by a thread that was not created by NSPR. The
+ * malloc tracing tools in the Mozilla client use PRMonitor for
+ * locking in their malloc, calloc, and free functions. If
+ * PR_EnterMonitor calls any of these functions, infinite
+ * recursion ensues.
+ */
+#if defined(AIX) || defined(SOLARIS) \
+ || defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \
+ || defined(HPUX) || defined(FREEBSD) \
+ || defined(NETBSD) || defined(OPENBSD) || defined(BSDI) \
+ || defined(NTO) || defined(DARWIN) \
+ || defined(UNIXWARE) || defined(RISCOS)
+#define _PT_PTHREAD_INVALIDATE_THR_HANDLE(t) (t) = 0
+#define _PT_PTHREAD_THR_HANDLE_IS_INVALID(t) (t) == 0
+#define _PT_PTHREAD_COPY_THR_HANDLE(st, dt) (dt) = (st)
+#else
+#error "pthreads is not supported for this architecture"
+#endif
+
+#if defined(_PR_PTHREADS)
+#define _PT_PTHREAD_ATTR_INIT pthread_attr_init
+#define _PT_PTHREAD_ATTR_DESTROY pthread_attr_destroy
+#define _PT_PTHREAD_CREATE(t, a, f, r) pthread_create(t, &a, f, r)
+#define _PT_PTHREAD_KEY_CREATE pthread_key_create
+#define _PT_PTHREAD_ATTR_SETSCHEDPOLICY pthread_attr_setschedpolicy
+#define _PT_PTHREAD_ATTR_GETSTACKSIZE(a, s) pthread_attr_getstacksize(a, s)
+#define _PT_PTHREAD_GETSPECIFIC(k, r) (r) = pthread_getspecific(k)
+#else
+#error "Cannot determine pthread strategy"
+#endif
+
+/*
+ * These platforms don't have sigtimedwait()
+ */
+#if (defined(AIX) && !defined(AIX4_3_PLUS)) \
+ || defined(LINUX) || defined(__GNU__)|| defined(__GLIBC__) \
+ || defined(FREEBSD) || defined(NETBSD) || defined(OPENBSD) \
+ || defined(BSDI) || defined(UNIXWARE) \
+ || defined(DARWIN)
+#define PT_NO_SIGTIMEDWAIT
+#endif
+
+#if defined(AIX)
+#include <sys/priv.h>
+#include <sys/sched.h>
+#ifndef PTHREAD_CREATE_JOINABLE
+#define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED
+#endif
+#define PT_PRIO_MIN DEFAULT_PRIO
+#define PT_PRIO_MAX DEFAULT_PRIO
+#elif defined(HPUX)
+#include <sys/sched.h>
+#define PT_PRIO_MIN sched_get_priority_min(SCHED_OTHER)
+#define PT_PRIO_MAX sched_get_priority_max(SCHED_OTHER)
+#elif defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \
+ || defined(FREEBSD)
+#define PT_PRIO_MIN sched_get_priority_min(SCHED_OTHER)
+#define PT_PRIO_MAX sched_get_priority_max(SCHED_OTHER)
+#elif defined(NTO)
+/*
+ * Neutrino has functions that return the priority range but
+ * they return invalid numbers, so I just hard coded these here
+ * for now. Jerry.Kirk@Nexarecorp.com
+ */
+#define PT_PRIO_MIN 0
+#define PT_PRIO_MAX 30
+#elif defined(SOLARIS)
+/*
+ * Solaris doesn't seem to have macros for the min/max priorities.
+ * The range of 0-127 is mentioned in the pthread_setschedparam(3T)
+ * man pages, and pthread_setschedparam indeed allows 0-127. However,
+ * pthread_attr_setschedparam does not allow 0; it allows 1-127.
+ */
+#define PT_PRIO_MIN 1
+#define PT_PRIO_MAX 127
+#elif defined(OPENBSD)
+#define PT_PRIO_MIN 0
+#define PT_PRIO_MAX 31
+#elif defined(NETBSD) \
+ || defined(BSDI) || defined(DARWIN) || defined(UNIXWARE) \
+ || defined(RISCOS) /* XXX */
+#define PT_PRIO_MIN 0
+#define PT_PRIO_MAX 126
+#else
+#error "pthreads is not supported for this architecture"
+#endif
+
+/*
+ * The _PT_PTHREAD_YIELD function is called from a signal handler.
+ * Needed for garbage collection -- Look at PR_Suspend/PR_Resume
+ * implementation.
+ */
+#if defined(AIX)
+extern int (*_PT_aix_yield_fcn)();
+#define _PT_PTHREAD_YIELD() (*_PT_aix_yield_fcn)()
+#elif defined(HPUX) || defined(SOLARIS) \
+ || defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \
+ || defined(FREEBSD) || defined(NETBSD) || defined(OPENBSD) \
+ || defined(BSDI) || defined(NTO) || defined(DARWIN) \
+ || defined(UNIXWARE) || defined(RISCOS)
+#define _PT_PTHREAD_YIELD() sched_yield()
+#else
+#error "Need to define _PT_PTHREAD_YIELD for this platform"
+#endif
+
+#endif /* nspr_pth_defs_h_ */
diff --git a/nsprpub/pr/include/md/_qnx.cfg b/nsprpub/pr/include/md/_qnx.cfg
new file mode 100644
index 0000000000..a459b8cb7c
--- /dev/null
+++ b/nsprpub/pr/include/md/_qnx.cfg
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef QNX
+#define QNX
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#undef HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 1
+#define PR_ALIGN_OF_INT 1
+#define PR_ALIGN_OF_LONG 1
+#define PR_ALIGN_OF_INT64 1
+#define PR_ALIGN_OF_FLOAT 1
+#define PR_ALIGN_OF_DOUBLE 1
+#define PR_ALIGN_OF_POINTER 1
+#define PR_ALIGN_OF_WORD 1
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+#define PR_WORDS_PER_DWORD_LOG2 1
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_qnx.h b/nsprpub/pr/include/md/_qnx.h
new file mode 100644
index 0000000000..923e174386
--- /dev/null
+++ b/nsprpub/pr/include/md/_qnx.h
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_qnx_defs_h___
+#define nspr_qnx_defs_h___
+
+/*
+** Internal configuration macros
+*/
+#define PR_LINKER_ARCH "qnx"
+#define _PR_SI_SYSNAME "QNX"
+#define _PR_SI_ARCHITECTURE "x86"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#ifndef HAVE_WEAK_IO_SYMBOLS
+#define HAVE_WEAK_IO_SYMBOLS
+#endif
+
+#undef _PR_POLL_AVAILABLE
+#undef _PR_USE_POLL
+#define _PR_HAVE_SOCKADDR_LEN
+#define HAVE_BSD_FLOCK
+#define _PR_NO_LARGE_FILES
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+
+#include <sys/select.h>
+
+#undef HAVE_STACK_GROWING_UP
+#undef HAVE_DLL
+#undef USE_DLFCN
+#define NEED_STRFTIME_LOCK
+#define NEED_TIME_R
+#define _PR_NEED_STRCASECMP
+
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR
+#endif
+
+#define USE_SETJMP
+
+#include <setjmp.h>
+
+#define _SETJMP setjmp
+#define _LONGJMP longjmp
+#define _PR_CONTEXT_TYPE jmp_buf
+#define _PR_NUM_GCREGS _JBLEN
+#define _MD_GET_SP(_t) (_t)->md.context[7]
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if(_SETJMP(CONTEXT(_thread))) (*_main)(); \
+ _MD_GET_SP(_thread) = (int) ((_sp) - 128); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!_SETJMP(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _LONGJMP(CONTEXT(_thread), 1); \
+}
+
+/*
+** Machine-dependent (MD) data structures.
+*/
+struct _MDThread {
+ _PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+** md-specific cpu structure field
+*/
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD], fd_write_cnt[_PR_MD_MAX_OSFD], fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_INTERVAL_USE_GTOD
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+/*
+** We wrapped the select() call. _MD_SELECT refers to the built-in,
+** unwrapped version.
+*/
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#define _MD_SELECT select
+
+#define SA_RESTART 0
+
+#endif /* nspr_qnx_defs_h___ */
diff --git a/nsprpub/pr/include/md/_riscos.cfg b/nsprpub/pr/include/md/_riscos.cfg
new file mode 100644
index 0000000000..c7adf2e135
--- /dev/null
+++ b/nsprpub/pr/include/md/_riscos.cfg
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef RISCOS
+#define RISCOS
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+#define PR_WORDS_PER_DWORD_LOG2 1
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_riscos.h b/nsprpub/pr/include/md/_riscos.h
new file mode 100644
index 0000000000..ec03dcbd7e
--- /dev/null
+++ b/nsprpub/pr/include/md/_riscos.h
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_riscos_defs_h___
+#define nspr_riscos_defs_h___
+
+/*
+** Internal configuration macros
+*/
+#define PR_LINKER_ARCH "riscos"
+#define _PR_SI_SYSNAME "RISCOS"
+#define _PR_SI_ARCHITECTURE "arm"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_HAVE_SOCKADDR_LEN
+#undef HAVE_BSD_FLOCK
+#define _PR_NO_LARGE_FILES
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+#define _PR_HAVE_POSIX_SEMAPHORES
+
+#include <sys/select.h>
+#include <sys/poll.h>
+#include <kernel.h>
+
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+#define NEED_STRFTIME_LOCK
+#define NEED_TIME_R
+#define PT_NO_SIGTIMEDWAIT
+
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR
+#endif
+
+#define USE_SETJMP
+
+#include <setjmp.h>
+
+#define _SETJMP setjmp
+#define _LONGJMP longjmp
+#define _PR_CONTEXT_TYPE jmp_buf
+#define _PR_NUM_GCREGS _JBLEN
+#define _MD_GET_SP(_t) (_t)->md.context[7]
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if(_SETJMP(CONTEXT(_thread))) (*_main)(); \
+ _MD_GET_SP(_thread) = (int) ((_sp) - 128); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!_SETJMP(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _LONGJMP(CONTEXT(_thread), 1); \
+}
+
+/*
+** Machine-dependent (MD) data structures.
+*/
+struct _MDThread {
+ _PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+** md-specific cpu structure field
+*/
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD], fd_write_cnt[_PR_MD_MAX_OSFD], fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif
+};
+
+#define _PR_IOQ(_cpu) /* */ ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_INTERVAL_USE_GTOD
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+/*
+** We wrapped the select() call. _MD_SELECT refers to the built-in,
+** unwrapped version.
+*/
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#define _MD_SELECT select
+
+#endif /* nspr_riscos_defs_h___ */
diff --git a/nsprpub/pr/include/md/_scoos.cfg b/nsprpub/pr/include/md/_scoos.cfg
new file mode 100644
index 0000000000..70cc6b7109
--- /dev/null
+++ b/nsprpub/pr/include/md/_scoos.cfg
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef SCO
+#define SCO
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#undef HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define _PR_POLL_BACKCOMPAT
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_scoos.h b/nsprpub/pr/include/md/_scoos.h
new file mode 100644
index 0000000000..1f6dda1535
--- /dev/null
+++ b/nsprpub/pr/include/md/_scoos.h
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_scoos5_defs_h___
+#define nspr_scoos5_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "scoos5"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_SI_SYSNAME "SCO"
+#define _PR_SI_ARCHITECTURE "x86"
+#define _PR_STACK_VMBASE 0x50000000
+
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+
+#if !defined (HAVE_STRERROR)
+#define HAVE_STRERROR
+#endif
+
+#ifndef HAVE_WEAK_IO_SYMBOLS
+#define HAVE_WEAK_IO_SYMBOLS
+#endif
+
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_NO_LARGE_FILES
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+
+#define NEED_STRFTIME_LOCK
+#define NEED_TIME_R
+#define _PR_RECV_BROKEN /* recv doesn't work on Unix Domain Sockets */
+
+#define USE_SETJMP
+
+#ifdef _PR_LOCAL_THREADS_ONLY
+#include <setjmp.h>
+
+#define _MD_GET_SP(_t) (_t)->md.jb[4]
+#define PR_NUM_GCREGS _SIGJBLEN
+#define PR_CONTEXT_TYPE sigjmp_buf
+
+#define CONTEXT(_th) ((_th)->md.jb)
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (sigsetjmp(CONTEXT(_thread),1)) { \
+ (*_main)(); \
+ } \
+ _MD_GET_SP(_thread) = (int) ((_sp) - 64); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!sigsetjmp(CONTEXT(_thread), 1)) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->osErrorCode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ siglongjmp(CONTEXT(_thread), 1); \
+}
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+struct _MDThread {
+ jmp_buf jb;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+#define _MD_INTERVAL_USE_GTOD
+
+#define _MD_SELECT _select
+#define _MD_POLL _poll
+
+#endif /* nspr_scoos5_defs_h___ */
diff --git a/nsprpub/pr/include/md/_solaris.cfg b/nsprpub/pr/include/md/_solaris.cfg
new file mode 100644
index 0000000000..674563747a
--- /dev/null
+++ b/nsprpub/pr/include/md/_solaris.cfg
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef SOLARIS
+#define SOLARIS
+#endif
+
+#define PR_AF_INET6 26 /* same as AF_INET6 */
+
+#if defined(sparc) || defined(__sparc)
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_DOUBLE 8
+#if defined(__sparcv9)
+#define IS_64
+#endif
+#elif defined(__x86_64)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_DOUBLE 8
+#define IS_64
+#elif defined(i386) || defined(__i386)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_DOUBLE 4
+#else
+#error unknown processor
+#endif
+
+#ifdef IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_POINTER 8
+
+#else /* IS_64 */
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_POINTER 4
+
+#endif /* IS_64 */
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* ifndef nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_solaris.h b/nsprpub/pr/include/md/_solaris.h
new file mode 100644
index 0000000000..4ebf77fd96
--- /dev/null
+++ b/nsprpub/pr/include/md/_solaris.h
@@ -0,0 +1,493 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_solaris_defs_h___
+#define nspr_solaris_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "solaris"
+#define _PR_SI_SYSNAME "SOLARIS"
+#ifdef sparc
+#define _PR_SI_ARCHITECTURE "sparc"
+#elif defined(__x86_64)
+#define _PR_SI_ARCHITECTURE "x86-64"
+#elif defined(i386)
+#define _PR_SI_ARCHITECTURE "x86"
+#else
+#error unknown processor
+#endif
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE (2*65536L)
+#define _MD_MMAP_FLAGS MAP_SHARED
+
+#undef HAVE_STACK_GROWING_UP
+
+#ifndef HAVE_WEAK_IO_SYMBOLS
+#define HAVE_WEAK_IO_SYMBOLS
+#endif
+
+#undef HAVE_WEAK_MALLOC_SYMBOLS
+#define HAVE_DLL
+#define USE_DLFCN
+#define NEED_STRFTIME_LOCK
+
+/*
+ * Intel x86 has atomic instructions.
+ *
+ * Sparc v8 does not have instructions to efficiently implement
+ * atomic increment/decrement operations. We use the default
+ * atomic routine implementation in pratom.c.
+ *
+ * 64-bit Solaris requires sparc v9, which has atomic instructions.
+ */
+#if defined(i386) || defined(IS_64)
+#define _PR_HAVE_ATOMIC_OPS
+#endif
+
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_STAT_HAS_ST_ATIM
+#ifdef SOLARIS2_5
+#define _PR_HAVE_SYSV_SEMAPHORES
+#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY
+#else
+#define _PR_HAVE_POSIX_SEMAPHORES
+#define PR_HAVE_POSIX_NAMED_SHARED_MEMORY
+#endif
+#define _PR_HAVE_GETIPNODEBYNAME
+#define _PR_HAVE_GETIPNODEBYADDR
+#define _PR_HAVE_GETADDRINFO
+#define _PR_INET6_PROBE
+#define _PR_ACCEPT_INHERIT_NONBLOCK
+#ifdef _PR_INET6
+#define _PR_HAVE_INET_NTOP
+#else
+#define AF_INET6 26
+struct addrinfo {
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ char *ai_canonname;
+ struct sockaddr *ai_addr;
+ struct addrinfo *ai_next;
+};
+#define AI_CANONNAME 0x0010
+#define AI_V4MAPPED 0x0001
+#define AI_ALL 0x0002
+#define AI_ADDRCONFIG 0x0004
+#define _PR_HAVE_MD_SOCKADDR_IN6
+/* isomorphic to struct in6_addr on Solaris 8 */
+struct _md_in6_addr {
+ union {
+ PRUint8 _S6_u8[16];
+ PRUint32 _S6_u32[4];
+ PRUint32 __S6_align;
+ } _S6_un;
+};
+/* isomorphic to struct sockaddr_in6 on Solaris 8 */
+struct _md_sockaddr_in6 {
+ PRUint16 sin6_family;
+ PRUint16 sin6_port;
+ PRUint32 sin6_flowinfo;
+ struct _md_in6_addr sin6_addr;
+ PRUint32 sin6_scope_id;
+ PRUint32 __sin6_src_id;
+};
+#endif
+#if defined(_PR_PTHREADS)
+#define _PR_HAVE_GETHOST_R
+#define _PR_HAVE_GETHOST_R_POINTER
+#endif
+
+#include "prinrval.h"
+#define _MD_INTERVAL_INIT()
+NSPR_API(PRIntervalTime) _MD_Solaris_GetInterval(void);
+#define _MD_GET_INTERVAL _MD_Solaris_GetInterval
+NSPR_API(PRIntervalTime) _MD_Solaris_TicksPerSecond(void);
+#define _MD_INTERVAL_PER_SEC _MD_Solaris_TicksPerSecond
+
+#if defined(_PR_HAVE_ATOMIC_OPS)
+/*
+** Atomic Operations
+*/
+#define _MD_INIT_ATOMIC()
+
+NSPR_API(PRInt32) _MD_AtomicIncrement(PRInt32 *val);
+#define _MD_ATOMIC_INCREMENT _MD_AtomicIncrement
+
+NSPR_API(PRInt32) _MD_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+#define _MD_ATOMIC_ADD _MD_AtomicAdd
+
+NSPR_API(PRInt32) _MD_AtomicDecrement(PRInt32 *val);
+#define _MD_ATOMIC_DECREMENT _MD_AtomicDecrement
+
+NSPR_API(PRInt32) _MD_AtomicSet(PRInt32 *val, PRInt32 newval);
+#define _MD_ATOMIC_SET _MD_AtomicSet
+#endif /* _PR_HAVE_ATOMIC_OPS */
+
+#if defined(_PR_PTHREADS)
+
+NSPR_API(void) _MD_EarlyInit(void);
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+
+#else /* _PR_PTHREADS */
+
+/*
+ * _PR_LOCAL_THREADS_ONLY implementation on Solaris
+ */
+
+#include "prthread.h"
+
+#include <errno.h>
+#include <ucontext.h>
+#include <sys/stack.h>
+#include <synch.h>
+
+/*
+** Initialization Related definitions
+*/
+
+NSPR_API(void) _MD_EarlyInit(void);
+NSPR_API(void) _MD_SolarisInit();
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _MD_SolarisInit
+#define _MD_INIT_THREAD _MD_InitializeThread
+
+#ifdef USE_SETJMP
+
+#include <setjmp.h>
+
+#define _PR_CONTEXT_TYPE jmp_buf
+
+#ifdef sparc
+#define _MD_GET_SP(_t) (_t)->md.context[2]
+#else
+#define _MD_GET_SP(_t) (_t)->md.context[4]
+#endif
+
+#define PR_NUM_GCREGS _JBLEN
+#define CONTEXT(_thread) (_thread)->md.context
+
+#else /* ! USE_SETJMP */
+
+#ifdef sparc
+#define _PR_CONTEXT_TYPE ucontext_t
+#define _MD_GET_SP(_t) (_t)->md.context.uc_mcontext.gregs[REG_SP]
+/*
+** Sparc's use register windows. the _MD_GetRegisters for the sparc's
+** doesn't actually store anything into the argument buffer; instead the
+** register windows are homed to the stack. I assume that the stack
+** always has room for the registers to spill to...
+*/
+#define PR_NUM_GCREGS 0
+#else
+#define _PR_CONTEXT_TYPE unsigned int edi; sigset_t oldMask, blockMask; ucontext_t
+#define _MD_GET_SP(_t) (_t)->md.context.uc_mcontext.gregs[USP]
+#define PR_NUM_GCREGS _JBLEN
+#endif
+
+#define CONTEXT(_thread) (&(_thread)->md.context)
+
+#endif /* ! USE_SETJMP */
+
+#include <time.h>
+/*
+ * Because clock_gettime() on Solaris/x86 always generates a
+ * segmentation fault, we use an emulated version _pr_solx86_clock_gettime(),
+ * which is implemented using gettimeofday().
+ */
+#ifdef i386
+#define GETTIME(tt) _pr_solx86_clock_gettime(CLOCK_REALTIME, (tt))
+#else
+#define GETTIME(tt) clock_gettime(CLOCK_REALTIME, (tt))
+#endif /* i386 */
+
+#define _MD_SAVE_ERRNO(_thread) (_thread)->md.errcode = errno;
+#define _MD_RESTORE_ERRNO(_thread) errno = (_thread)->md.errcode;
+
+#ifdef sparc
+
+#ifdef USE_SETJMP
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ int *context = (_thread)->md.context; \
+ *status = PR_TRUE; \
+ (void) setjmp(context); \
+ (_thread)->md.context[1] = (int) ((_sp) - 64); \
+ (_thread)->md.context[2] = (int) _main; \
+ (_thread)->md.context[3] = (int) _main + 4; \
+ _thread->no_sched = 0; \
+ PR_END_MACRO
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!setjmp(CONTEXT(_thread))) { \
+ _MD_SAVE_ERRNO(_thread) \
+ _MD_SET_LAST_THREAD(_thread); \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _PR_Schedule(); \
+ }
+
+#define _MD_RESTORE_CONTEXT(_newThread) \
+{ \
+ _MD_RESTORE_ERRNO(_newThread) \
+ _MD_SET_CURRENT_THREAD(_newThread); \
+ longjmp(CONTEXT(_newThread), 1); \
+}
+
+#else
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ ucontext_t *uc = CONTEXT(_thread); \
+ *status = PR_TRUE; \
+ getcontext(uc); \
+ uc->uc_stack.ss_sp = (char *) ((unsigned long)(_sp - WINDOWSIZE - SA(MINFRAME)) & 0xfffffff8); \
+ uc->uc_stack.ss_size = _thread->stack->stackSize; \
+ uc->uc_stack.ss_flags = 0; /* ? */ \
+ uc->uc_mcontext.gregs[REG_SP] = (unsigned int) uc->uc_stack.ss_sp; \
+ uc->uc_mcontext.gregs[REG_PC] = (unsigned int) _main; \
+ uc->uc_mcontext.gregs[REG_nPC] = (unsigned int) ((char*)_main)+4; \
+ uc->uc_flags = UC_ALL; \
+ _thread->no_sched = 0; \
+ PR_END_MACRO
+
+/*
+** Switch away from the current thread context by saving its state and
+** calling the thread scheduler. Reload cpu when we come back from the
+** context switch because it might have changed.
+*/
+#define _MD_SWITCH_CONTEXT(_thread) \
+ PR_BEGIN_MACRO \
+ if (!getcontext(CONTEXT(_thread))) { \
+ _MD_SAVE_ERRNO(_thread); \
+ _MD_SET_LAST_THREAD(_thread); \
+ _PR_Schedule(); \
+ } \
+ PR_END_MACRO
+
+/*
+** Restore a thread context that was saved by _MD_SWITCH_CONTEXT or
+** initialized by _MD_INIT_CONTEXT.
+*/
+#define _MD_RESTORE_CONTEXT(_newThread) \
+ PR_BEGIN_MACRO \
+ ucontext_t *uc = CONTEXT(_newThread); \
+ uc->uc_mcontext.gregs[11] = 1; \
+ _MD_RESTORE_ERRNO(_newThread); \
+ _MD_SET_CURRENT_THREAD(_newThread); \
+ setcontext(uc); \
+ PR_END_MACRO
+#endif
+
+#else /* x86 solaris */
+
+#ifdef USE_SETJMP
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ *status = PR_TRUE; \
+ if (setjmp(CONTEXT(_thread))) _main(); \
+ _MD_GET_SP(_thread) = (int) ((_sp) - 64); \
+ PR_END_MACRO
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!setjmp(CONTEXT(_thread))) { \
+ _MD_SAVE_ERRNO(_thread) \
+ _PR_Schedule(); \
+ }
+
+#define _MD_RESTORE_CONTEXT(_newThread) \
+{ \
+ _MD_RESTORE_ERRNO(_newThread) \
+ _MD_SET_CURRENT_THREAD(_newThread); \
+ longjmp(CONTEXT(_newThread), 1); \
+}
+
+#else /* USE_SETJMP */
+
+#define WINDOWSIZE 0
+
+int getedi(void);
+void setedi(int);
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ ucontext_t *uc = CONTEXT(_thread); \
+ *status = PR_TRUE; \
+ getcontext(uc); \
+ /* Force sp to be double aligned! */ \
+ uc->uc_mcontext.gregs[USP] = (int) ((unsigned long)(_sp - WINDOWSIZE - SA(MINFRAME)) & 0xfffffff8); \
+ uc->uc_mcontext.gregs[PC] = (int) _main; \
+ (_thread)->no_sched = 0; \
+ PR_END_MACRO
+
+/* getcontext() may return 1, contrary to what the man page says */
+#define _MD_SWITCH_CONTEXT(_thread) \
+ PR_BEGIN_MACRO \
+ ucontext_t *uc = CONTEXT(_thread); \
+ PR_ASSERT(_thread->no_sched); \
+ sigfillset(&((_thread)->md.blockMask)); \
+ sigprocmask(SIG_BLOCK, &((_thread)->md.blockMask), \
+ &((_thread)->md.oldMask)); \
+ (_thread)->md.edi = getedi(); \
+ if (! getcontext(uc)) { \
+ sigprocmask(SIG_SETMASK, &((_thread)->md.oldMask), NULL); \
+ uc->uc_mcontext.gregs[EDI] = (_thread)->md.edi; \
+ _MD_SAVE_ERRNO(_thread) \
+ _MD_SET_LAST_THREAD(_thread); \
+ _PR_Schedule(); \
+ } else { \
+ sigprocmask(SIG_SETMASK, &((_thread)->md.oldMask), NULL); \
+ setedi((_thread)->md.edi); \
+ PR_ASSERT(_MD_LAST_THREAD() !=_MD_CURRENT_THREAD()); \
+ _MD_LAST_THREAD()->no_sched = 0; \
+ } \
+ PR_END_MACRO
+
+/*
+** Restore a thread context, saved by _PR_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_newthread) \
+ PR_BEGIN_MACRO \
+ ucontext_t *uc = CONTEXT(_newthread); \
+ uc->uc_mcontext.gregs[EAX] = 1; \
+ _MD_RESTORE_ERRNO(_newthread) \
+ _MD_SET_CURRENT_THREAD(_newthread); \
+ (_newthread)->no_sched = 1; \
+ setcontext(uc); \
+ PR_END_MACRO
+#endif /* USE_SETJMP */
+
+#endif /* sparc */
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDThread {
+ _PR_CONTEXT_TYPE context;
+ int errcode;
+ int id;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+extern PRStatus _MD_WAIT(struct PRThread *, PRIntervalTime timeout);
+extern PRStatus _MD_WAKEUP_WAITER(struct PRThread *);
+extern void _MD_YIELD(void);
+extern PRStatus _MD_InitializeThread(PRThread *thread);
+extern void _MD_SET_PRIORITY(struct _MDThread *thread,
+ PRThreadPriority newPri);
+extern PRStatus _MD_CREATE_THREAD(PRThread *thread, void (*start) (void *),
+ PRThreadPriority priority, PRThreadScope scope, PRThreadState state,
+ PRUint32 stackSize);
+
+/* The following defines the unwrapped versions of select() and poll(). */
+extern int _select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timeval *timeout);
+#define _MD_SELECT _select
+
+#include <stropts.h>
+#include <poll.h>
+#define _MD_POLL _poll
+extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout);
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Missing function prototypes
+*/
+extern int gethostname (char *name, int namelen);
+
+PR_END_EXTERN_C
+
+#endif /* _PR_PTHREADS */
+
+extern void _MD_solaris_map_sendfile_error(int err);
+
+#endif /* nspr_solaris_defs_h___ */
+
diff --git a/nsprpub/pr/include/md/_unix_errors.h b/nsprpub/pr/include/md/_unix_errors.h
new file mode 100644
index 0000000000..0578fd0275
--- /dev/null
+++ b/nsprpub/pr/include/md/_unix_errors.h
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prunixerrors_h___
+#define prunixerrors_h___
+
+#include <unistd.h>
+#include <stddef.h>
+
+PR_BEGIN_EXTERN_C
+
+extern void _MD_unix_map_default_error(int err);
+#define _PR_MD_MAP_DEFAULT_ERROR _MD_unix_map_default_error
+
+extern void _MD_unix_map_opendir_error(int err);
+#define _PR_MD_MAP_OPENDIR_ERROR _MD_unix_map_opendir_error
+
+extern void _MD_unix_map_closedir_error(int err);
+#define _PR_MD_MAP_CLOSEDIR_ERROR _MD_unix_map_closedir_error
+
+extern void _MD_unix_readdir_error(int err);
+#define _PR_MD_MAP_READDIR_ERROR _MD_unix_readdir_error
+
+extern void _MD_unix_map_unlink_error(int err);
+#define _PR_MD_MAP_UNLINK_ERROR _MD_unix_map_unlink_error
+
+extern void _MD_unix_map_stat_error(int err);
+#define _PR_MD_MAP_STAT_ERROR _MD_unix_map_stat_error
+
+extern void _MD_unix_map_fstat_error(int err);
+#define _PR_MD_MAP_FSTAT_ERROR _MD_unix_map_fstat_error
+
+extern void _MD_unix_map_rename_error(int err);
+#define _PR_MD_MAP_RENAME_ERROR _MD_unix_map_rename_error
+
+extern void _MD_unix_map_access_error(int err);
+#define _PR_MD_MAP_ACCESS_ERROR _MD_unix_map_access_error
+
+extern void _MD_unix_map_mkdir_error(int err);
+#define _PR_MD_MAP_MKDIR_ERROR _MD_unix_map_mkdir_error
+
+extern void _MD_unix_map_rmdir_error(int err);
+#define _PR_MD_MAP_RMDIR_ERROR _MD_unix_map_rmdir_error
+
+extern void _MD_unix_map_read_error(int err);
+#define _PR_MD_MAP_READ_ERROR _MD_unix_map_read_error
+
+extern void _MD_unix_map_write_error(int err);
+#define _PR_MD_MAP_WRITE_ERROR _MD_unix_map_write_error
+
+extern void _MD_unix_map_lseek_error(int err);
+#define _PR_MD_MAP_LSEEK_ERROR _MD_unix_map_lseek_error
+
+extern void _MD_unix_map_fsync_error(int err);
+#define _PR_MD_MAP_FSYNC_ERROR _MD_unix_map_fsync_error
+
+extern void _MD_unix_map_close_error(int err);
+#define _PR_MD_MAP_CLOSE_ERROR _MD_unix_map_close_error
+
+extern void _MD_unix_map_socket_error(int err);
+#define _PR_MD_MAP_SOCKET_ERROR _MD_unix_map_socket_error
+
+extern void _MD_unix_map_socketavailable_error(int err);
+#define _PR_MD_MAP_SOCKETAVAILABLE_ERROR _MD_unix_map_socketavailable_error
+
+extern void _MD_unix_map_recv_error(int err);
+#define _PR_MD_MAP_RECV_ERROR _MD_unix_map_recv_error
+
+extern void _MD_unix_map_recvfrom_error(int err);
+#define _PR_MD_MAP_RECVFROM_ERROR _MD_unix_map_recvfrom_error
+
+extern void _MD_unix_map_send_error(int err);
+#define _PR_MD_MAP_SEND_ERROR _MD_unix_map_send_error
+
+extern void _MD_unix_map_sendto_error(int err);
+#define _PR_MD_MAP_SENDTO_ERROR _MD_unix_map_sendto_error
+
+extern void _MD_unix_map_writev_error(int err);
+#define _PR_MD_MAP_WRITEV_ERROR _MD_unix_map_writev_error
+
+extern void _MD_unix_map_accept_error(int err);
+#define _PR_MD_MAP_ACCEPT_ERROR _MD_unix_map_accept_error
+
+extern void _MD_unix_map_connect_error(int err);
+#define _PR_MD_MAP_CONNECT_ERROR _MD_unix_map_connect_error
+
+extern void _MD_unix_map_bind_error(int err);
+#define _PR_MD_MAP_BIND_ERROR _MD_unix_map_bind_error
+
+extern void _MD_unix_map_listen_error(int err);
+#define _PR_MD_MAP_LISTEN_ERROR _MD_unix_map_listen_error
+
+extern void _MD_unix_map_shutdown_error(int err);
+#define _PR_MD_MAP_SHUTDOWN_ERROR _MD_unix_map_shutdown_error
+
+extern void _MD_unix_map_socketpair_error(int err);
+#define _PR_MD_MAP_SOCKETPAIR_ERROR _MD_unix_map_socketpair_error
+
+extern void _MD_unix_map_getsockname_error(int err);
+#define _PR_MD_MAP_GETSOCKNAME_ERROR _MD_unix_map_getsockname_error
+
+extern void _MD_unix_map_getpeername_error(int err);
+#define _PR_MD_MAP_GETPEERNAME_ERROR _MD_unix_map_getpeername_error
+
+extern void _MD_unix_map_getsockopt_error(int err);
+#define _PR_MD_MAP_GETSOCKOPT_ERROR _MD_unix_map_getsockopt_error
+
+extern void _MD_unix_map_setsockopt_error(int err);
+#define _PR_MD_MAP_SETSOCKOPT_ERROR _MD_unix_map_setsockopt_error
+
+extern void _MD_unix_map_open_error(int err);
+#define _PR_MD_MAP_OPEN_ERROR _MD_unix_map_open_error
+
+extern void _MD_unix_map_mmap_error(int err);
+#define _PR_MD_MAP_MMAP_ERROR _MD_unix_map_mmap_error
+
+extern void _MD_unix_map_gethostname_error(int err);
+#define _PR_MD_MAP_GETHOSTNAME_ERROR _MD_unix_map_gethostname_error
+
+extern void _MD_unix_map_select_error(int err);
+#define _PR_MD_MAP_SELECT_ERROR _MD_unix_map_select_error
+
+extern void _MD_unix_map_poll_error(int err);
+#define _PR_MD_MAP_POLL_ERROR _MD_unix_map_poll_error
+
+extern void _MD_unix_map_poll_revents_error(int err);
+#define _PR_MD_MAP_POLL_REVENTS_ERROR _MD_unix_map_poll_revents_error
+
+extern void _MD_unix_map_flock_error(int err);
+#define _PR_MD_MAP_FLOCK_ERROR _MD_unix_map_flock_error
+
+extern void _MD_unix_map_lockf_error(int err);
+#define _PR_MD_MAP_LOCKF_ERROR _MD_unix_map_lockf_error
+
+PR_END_EXTERN_C
+
+#endif /* prunixerrors_h___ */
diff --git a/nsprpub/pr/include/md/_unixos.h b/nsprpub/pr/include/md/_unixos.h
new file mode 100644
index 0000000000..7d444cda16
--- /dev/null
+++ b/nsprpub/pr/include/md/_unixos.h
@@ -0,0 +1,620 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prunixos_h___
+#define prunixos_h___
+
+/*
+ * If FD_SETSIZE is not defined on the command line, set the default value
+ * before include select.h
+ */
+/*
+ * Linux: FD_SETSIZE is defined in /usr/include/sys/select.h and should
+ * not be redefined.
+ */
+#if !defined(LINUX) && !defined(__GNU__) && !defined(__GLIBC__) \
+ && !defined(DARWIN)
+#ifndef FD_SETSIZE
+#define FD_SETSIZE 4096
+#endif
+#endif
+
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include "prio.h"
+#include "prmem.h"
+#include "prclist.h"
+
+/*
+ * For select(), fd_set, and struct timeval.
+ *
+ * In The Single UNIX(R) Specification, Version 2,
+ * the header file for select() is <sys/time.h>.
+ * In Version 3, the header file for select() is
+ * changed to <sys/select.h>.
+ *
+ * fd_set is defined in <sys/types.h>. Usually
+ * <sys/time.h> includes <sys/types.h>, but on some
+ * older systems <sys/time.h> does not include
+ * <sys/types.h>, so we include it explicitly.
+ */
+#include <sys/time.h>
+#include <sys/types.h>
+#if defined(AIX)
+#include <sys/select.h>
+#endif
+
+#define HAVE_NETINET_TCP_H
+
+#define _PR_HAVE_O_APPEND
+
+#define PR_DIRECTORY_SEPARATOR '/'
+#define PR_DIRECTORY_SEPARATOR_STR "/"
+#define PR_PATH_SEPARATOR ':'
+#define PR_PATH_SEPARATOR_STR ":"
+typedef int (*FARPROC)();
+
+/*
+ * intervals at which GLOBAL threads wakeup to check for pending interrupt
+ */
+#define _PR_INTERRUPT_CHECK_INTERVAL_SECS 5
+extern PRIntervalTime intr_timeout_ticks;
+
+/*
+ * The bit flags for the in_flags and out_flags fields
+ * of _PR_UnixPollDesc
+ */
+#ifdef _PR_USE_POLL
+#define _PR_UNIX_POLL_READ POLLIN
+#define _PR_UNIX_POLL_WRITE POLLOUT
+#define _PR_UNIX_POLL_EXCEPT POLLPRI
+#define _PR_UNIX_POLL_ERR POLLERR
+#define _PR_UNIX_POLL_NVAL POLLNVAL
+#define _PR_UNIX_POLL_HUP POLLHUP
+#else /* _PR_USE_POLL */
+#define _PR_UNIX_POLL_READ 0x1
+#define _PR_UNIX_POLL_WRITE 0x2
+#define _PR_UNIX_POLL_EXCEPT 0x4
+#define _PR_UNIX_POLL_ERR 0x8
+#define _PR_UNIX_POLL_NVAL 0x10
+#define _PR_UNIX_POLL_HUP 0x20
+#endif /* _PR_USE_POLL */
+
+typedef struct _PRUnixPollDesc {
+ PRInt32 osfd;
+ PRInt16 in_flags;
+ PRInt16 out_flags;
+} _PRUnixPollDesc;
+
+typedef struct PRPollQueue {
+ PRCList links; /* for linking PRPollQueue's together */
+ _PRUnixPollDesc *pds; /* array of poll descriptors */
+ PRUintn npds; /* length of the array */
+ PRPackedBool on_ioq; /* is this on the async i/o work q? */
+ PRIntervalTime timeout; /* timeout, in ticks */
+ struct PRThread *thr;
+} PRPollQueue;
+
+#define _PR_POLLQUEUE_PTR(_qp) \
+ ((PRPollQueue*) ((char*) (_qp) - offsetof(PRPollQueue,links)))
+
+
+extern PRInt32 _PR_WaitForMultipleFDs(
+ _PRUnixPollDesc *unixpds,
+ PRInt32 pdcnt,
+ PRIntervalTime timeout);
+extern void _PR_Unblock_IO_Wait(struct PRThread *thr);
+
+#if defined(_PR_LOCAL_THREADS_ONLY) || defined(_PR_GLOBAL_THREADS_ONLY)
+#define _MD_CHECK_FOR_EXIT()
+#endif
+
+extern fd_set _pr_md_read_set, _pr_md_write_set, _pr_md_exception_set;
+extern PRInt16 _pr_md_read_cnt[], _pr_md_write_cnt[], _pr_md_exception_cnt[];
+extern PRInt32 _pr_md_ioq_max_osfd;
+extern PRUint32 _pr_md_ioq_timeout;
+
+struct _MDFileDesc {
+ int osfd;
+#if defined(LINUX) && defined(_PR_PTHREADS)
+ int tcp_nodelay; /* used by pt_LinuxSendFile */
+#endif
+};
+
+struct _MDDir {
+ DIR *d;
+};
+
+struct _PRCPU;
+extern void _MD_unix_init_running_cpu(struct _PRCPU *cpu);
+
+/*
+** Make a redzone at both ends of the stack segment. Disallow access
+** to those pages of memory. It's ok if the mprotect call's don't
+** work - it just means that we don't really have a functional
+** redzone.
+*/
+#include <sys/mman.h>
+#ifndef PROT_NONE
+#define PROT_NONE 0x0
+#endif
+
+#if defined(DEBUG) && !defined(DARWIN)
+#if !defined(SOLARIS)
+#include <string.h> /* for memset() */
+#define _MD_INIT_STACK(ts,REDZONE) \
+ PR_BEGIN_MACRO \
+ (void) mprotect((void*)ts->seg->vaddr, REDZONE, PROT_NONE); \
+ (void) mprotect((void*) ((char*)ts->seg->vaddr + REDZONE + ts->stackSize),\
+ REDZONE, PROT_NONE); \
+ /* \
+ ** Fill stack memory with something that turns into an illegal \
+ ** pointer value. This will sometimes find runtime references to \
+ ** uninitialized pointers. We don't do this for solaris because we \
+ ** can use purify instead. \
+ */ \
+ if (_pr_debugStacks) { \
+ memset(ts->allocBase + REDZONE, 0xf7, ts->stackSize); \
+ } \
+ PR_END_MACRO
+#else /* !SOLARIS */
+#define _MD_INIT_STACK(ts,REDZONE) \
+ PR_BEGIN_MACRO \
+ (void) mprotect((void*)ts->seg->vaddr, REDZONE, PROT_NONE); \
+ (void) mprotect((void*) ((char*)ts->seg->vaddr + REDZONE + ts->stackSize),\
+ REDZONE, PROT_NONE); \
+ PR_END_MACRO
+#endif /* !SOLARIS */
+
+/*
+ * _MD_CLEAR_STACK
+ * Allow access to the redzone pages; the access was turned off in
+ * _MD_INIT_STACK.
+ */
+#define _MD_CLEAR_STACK(ts) \
+ PR_BEGIN_MACRO \
+ (void) mprotect((void*)ts->seg->vaddr, REDZONE, PROT_READ|PROT_WRITE);\
+ (void) mprotect((void*) ((char*)ts->seg->vaddr + REDZONE + ts->stackSize),\
+ REDZONE, PROT_READ|PROT_WRITE); \
+ PR_END_MACRO
+
+#else /* DEBUG */
+
+#define _MD_INIT_STACK(ts,REDZONE)
+#define _MD_CLEAR_STACK(ts)
+
+#endif /* DEBUG */
+
+#if !defined(SOLARIS)
+
+#define PR_SET_INTSOFF(newval)
+
+#endif
+
+/************************************************************************/
+
+extern void _PR_UnixInit(void);
+
+extern void _PR_UnixCleanup(void);
+#define _MD_EARLY_CLEANUP _PR_UnixCleanup
+
+/************************************************************************/
+
+struct _MDProcess {
+ pid_t pid;
+};
+
+struct PRProcess;
+struct PRProcessAttr;
+
+/* Create a new process (fork() + exec()) */
+#define _MD_CREATE_PROCESS _MD_CreateUnixProcess
+extern struct PRProcess * _MD_CreateUnixProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const struct PRProcessAttr *attr
+);
+
+#define _MD_DETACH_PROCESS _MD_DetachUnixProcess
+extern PRStatus _MD_DetachUnixProcess(struct PRProcess *process);
+
+/* Wait for a child process to terminate */
+#define _MD_WAIT_PROCESS _MD_WaitUnixProcess
+extern PRStatus _MD_WaitUnixProcess(struct PRProcess *process,
+ PRInt32 *exitCode);
+
+#define _MD_KILL_PROCESS _MD_KillUnixProcess
+extern PRStatus _MD_KillUnixProcess(struct PRProcess *process);
+
+/************************************************************************/
+
+extern void _MD_EnableClockInterrupts(void);
+extern void _MD_DisableClockInterrupts(void);
+
+#define _MD_START_INTERRUPTS _MD_StartInterrupts
+#define _MD_STOP_INTERRUPTS _MD_StopInterrupts
+#define _MD_DISABLE_CLOCK_INTERRUPTS _MD_DisableClockInterrupts
+#define _MD_ENABLE_CLOCK_INTERRUPTS _MD_EnableClockInterrupts
+#define _MD_BLOCK_CLOCK_INTERRUPTS _MD_BlockClockInterrupts
+#define _MD_UNBLOCK_CLOCK_INTERRUPTS _MD_UnblockClockInterrupts
+
+/************************************************************************/
+
+extern void _MD_InitCPUS(void);
+#define _MD_INIT_CPUS _MD_InitCPUS
+
+extern void _MD_Wakeup_CPUs(void);
+#define _MD_WAKEUP_CPUS _MD_Wakeup_CPUs
+
+#define _MD_PAUSE_CPU _MD_PauseCPU
+
+#if defined(_PR_LOCAL_THREADS_ONLY) || defined(_PR_GLOBAL_THREADS_ONLY)
+#define _MD_CLEANUP_BEFORE_EXIT()
+#endif
+
+#define _MD_EXIT(status) _exit(status)
+
+/************************************************************************/
+
+#define _MD_GET_ENV getenv
+#define _MD_PUT_ENV putenv
+
+/************************************************************************/
+
+#define _MD_INIT_FILEDESC(fd)
+
+extern void _MD_MakeNonblock(PRFileDesc *fd);
+#define _MD_MAKE_NONBLOCK _MD_MakeNonblock
+
+/************************************************************************/
+
+#if !defined(_PR_PTHREADS)
+
+extern void _MD_InitSegs(void);
+extern PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size,
+ void *vaddr);
+extern void _MD_FreeSegment(PRSegment *seg);
+
+#define _MD_INIT_SEGS _MD_InitSegs
+#define _MD_ALLOC_SEGMENT _MD_AllocSegment
+#define _MD_FREE_SEGMENT _MD_FreeSegment
+
+#endif /* !defined(_PR_PTHREADS) */
+
+/************************************************************************/
+
+#ifdef _MD_INTERVAL_USE_GTOD
+extern PRIntervalTime _PR_UNIX_GetInterval(void);
+extern PRIntervalTime _PR_UNIX_TicksPerSecond(void);
+#define _MD_INTERVAL_INIT()
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+#endif
+
+#ifdef _PR_HAVE_CLOCK_MONOTONIC
+extern PRIntervalTime _PR_UNIX_GetInterval2(void);
+extern PRIntervalTime _PR_UNIX_TicksPerSecond2(void);
+#define _MD_INTERVAL_INIT()
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval2
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond2
+#endif
+
+#define _MD_INTERVAL_PER_MILLISEC() (_PR_MD_INTERVAL_PER_SEC() / 1000)
+#define _MD_INTERVAL_PER_MICROSEC() (_PR_MD_INTERVAL_PER_SEC() / 1000000)
+
+/************************************************************************/
+
+#define _MD_ERRNO() (errno)
+#define _MD_GET_SOCKET_ERROR() (errno)
+
+/************************************************************************/
+
+extern PRInt32 _MD_AvailableSocket(PRInt32 osfd);
+
+extern void _MD_StartInterrupts(void);
+extern void _MD_StopInterrupts(void);
+extern void _MD_DisableClockInterrupts(void);
+extern void _MD_BlockClockInterrupts(void);
+extern void _MD_UnblockClockInterrupts(void);
+extern void _MD_PauseCPU(PRIntervalTime timeout);
+
+extern PRStatus _MD_open_dir(struct _MDDir *, const char *);
+extern PRInt32 _MD_close_dir(struct _MDDir *);
+extern char * _MD_read_dir(struct _MDDir *, PRIntn);
+extern PRInt32 _MD_open(const char *name, PRIntn osflags, PRIntn mode);
+extern PRInt32 _MD_delete(const char *name);
+extern PRInt32 _MD_getfileinfo(const char *fn, PRFileInfo *info);
+extern PRInt32 _MD_getfileinfo64(const char *fn, PRFileInfo64 *info);
+extern PRInt32 _MD_getopenfileinfo(const PRFileDesc *fd, PRFileInfo *info);
+extern PRInt32 _MD_getopenfileinfo64(const PRFileDesc *fd, PRFileInfo64 *info);
+extern PRInt32 _MD_rename(const char *from, const char *to);
+extern PRInt32 _MD_access(const char *name, PRAccessHow how);
+extern PRInt32 _MD_mkdir(const char *name, PRIntn mode);
+extern PRInt32 _MD_rmdir(const char *name);
+extern PRInt32 _MD_accept_read(PRInt32 sock, PRInt32 *newSock,
+ PRNetAddr **raddr, void *buf, PRInt32 amount);
+extern PRInt32 _PR_UnixSendFile(PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout);
+
+extern PRStatus _MD_LockFile(PRInt32 osfd);
+extern PRStatus _MD_TLockFile(PRInt32 osfd);
+extern PRStatus _MD_UnlockFile(PRInt32 osfd);
+
+#define _MD_OPEN_DIR(dir, name) _MD_open_dir(dir, name)
+#define _MD_CLOSE_DIR(dir) _MD_close_dir(dir)
+#define _MD_READ_DIR(dir, flags) _MD_read_dir(dir, flags)
+#define _MD_OPEN(name, osflags, mode) _MD_open(name, osflags, mode)
+#define _MD_OPEN_FILE(name, osflags, mode) _MD_open(name, osflags, mode)
+extern PRInt32 _MD_read(PRFileDesc *fd, void *buf, PRInt32 amount);
+#define _MD_READ(fd,buf,amount) _MD_read(fd,buf,amount)
+extern PRInt32 _MD_write(PRFileDesc *fd, const void *buf, PRInt32 amount);
+#define _MD_WRITE(fd,buf,amount) _MD_write(fd,buf,amount)
+#define _MD_DELETE(name) _MD_delete(name)
+#define _MD_GETFILEINFO(fn, info) _MD_getfileinfo(fn, info)
+#define _MD_GETFILEINFO64(fn, info) _MD_getfileinfo64(fn, info)
+#define _MD_GETOPENFILEINFO(fd, info) _MD_getopenfileinfo(fd, info)
+#define _MD_GETOPENFILEINFO64(fd, info) _MD_getopenfileinfo64(fd, info)
+#define _MD_RENAME(from, to) _MD_rename(from, to)
+#define _MD_ACCESS(name, how) _MD_access(name, how)
+#define _MD_MKDIR(name, mode) _MD_mkdir(name, mode)
+#define _MD_MAKE_DIR(name, mode) _MD_mkdir(name, mode)
+#define _MD_RMDIR(name) _MD_rmdir(name)
+#define _MD_ACCEPT_READ(sock, newSock, raddr, buf, amount) _MD_accept_read(sock, newSock, raddr, buf, amount)
+
+#define _MD_LOCKFILE _MD_LockFile
+#define _MD_TLOCKFILE _MD_TLockFile
+#define _MD_UNLOCKFILE _MD_UnlockFile
+
+
+extern PRInt32 _MD_socket(int af, int type, int flags);
+#define _MD_SOCKET _MD_socket
+extern PRInt32 _MD_connect(PRFileDesc *fd, const PRNetAddr *addr,
+ PRUint32 addrlen, PRIntervalTime timeout);
+#define _MD_CONNECT _MD_connect
+extern PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen,
+ PRIntervalTime timeout);
+#define _MD_ACCEPT _MD_accept
+extern PRInt32 _MD_bind(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen);
+#define _MD_BIND _MD_bind
+extern PRInt32 _MD_listen(PRFileDesc *fd, PRIntn backlog);
+#define _MD_LISTEN _MD_listen
+extern PRInt32 _MD_shutdown(PRFileDesc *fd, PRIntn how);
+#define _MD_SHUTDOWN _MD_shutdown
+
+extern PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+#define _MD_RECV _MD_recv
+extern PRInt32 _MD_send(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+#define _MD_SEND _MD_send
+extern PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen,
+ PRIntervalTime timeout);
+#define _MD_RECVFROM _MD_recvfrom
+extern PRInt32 _MD_sendto(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, const PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout);
+#define _MD_SENDTO _MD_sendto
+extern PRInt32 _MD_writev(PRFileDesc *fd, const struct PRIOVec *iov,
+ PRInt32 iov_size, PRIntervalTime timeout);
+#define _MD_WRITEV _MD_writev
+
+extern PRInt32 _MD_socketavailable(PRFileDesc *fd);
+#define _MD_SOCKETAVAILABLE _MD_socketavailable
+extern PRInt64 _MD_socketavailable64(PRFileDesc *fd);
+#define _MD_SOCKETAVAILABLE64 _MD_socketavailable64
+
+#define _MD_PIPEAVAILABLE _MD_socketavailable
+
+extern PRInt32 _MD_pr_poll(PRPollDesc *pds, PRIntn npds,
+ PRIntervalTime timeout);
+#define _MD_PR_POLL _MD_pr_poll
+
+extern PRInt32 _MD_close(PRInt32 osfd);
+#define _MD_CLOSE_FILE _MD_close
+extern PRInt32 _MD_lseek(PRFileDesc*, PRInt32, PRSeekWhence);
+#define _MD_LSEEK _MD_lseek
+extern PRInt64 _MD_lseek64(PRFileDesc*, PRInt64, PRSeekWhence);
+#define _MD_LSEEK64 _MD_lseek64
+extern PRInt32 _MD_fsync(PRFileDesc *fd);
+#define _MD_FSYNC _MD_fsync
+
+extern PRInt32 _MD_socketpair(int af, int type, int flags, PRInt32 *osfd);
+#define _MD_SOCKETPAIR _MD_socketpair
+
+#define _MD_CLOSE_SOCKET _MD_close
+
+#ifndef NO_NSPR_10_SUPPORT
+#define _MD_STAT stat
+#endif
+
+extern PRStatus _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen);
+#define _MD_GETPEERNAME _MD_getpeername
+extern PRStatus _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen);
+#define _MD_GETSOCKNAME _MD_getsockname
+
+extern PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level,
+ PRInt32 optname, char* optval, PRInt32* optlen);
+#define _MD_GETSOCKOPT _MD_getsockopt
+extern PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level,
+ PRInt32 optname, const char* optval, PRInt32 optlen);
+#define _MD_SETSOCKOPT _MD_setsockopt
+
+extern PRStatus _MD_set_fd_inheritable(PRFileDesc *fd, PRBool inheritable);
+#define _MD_SET_FD_INHERITABLE _MD_set_fd_inheritable
+
+extern void _MD_init_fd_inheritable(PRFileDesc *fd, PRBool imported);
+#define _MD_INIT_FD_INHERITABLE _MD_init_fd_inheritable
+
+extern void _MD_query_fd_inheritable(PRFileDesc *fd);
+#define _MD_QUERY_FD_INHERITABLE _MD_query_fd_inheritable
+
+extern PRStatus _MD_gethostname(char *name, PRUint32 namelen);
+#define _MD_GETHOSTNAME _MD_gethostname
+
+extern PRStatus _MD_getsysinfo(PRSysInfo cmd, char *name, PRUint32 namelen);
+#define _MD_GETSYSINFO _MD_getsysinfo
+
+extern int _MD_unix_get_nonblocking_connect_error(int osfd);
+
+/* Memory-mapped files */
+
+struct _MDFileMap {
+ PRIntn prot;
+ PRIntn flags;
+ PRBool isAnonFM; /* when true, PR_CloseFileMap() must close the related fd */
+};
+
+extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size);
+#define _MD_CREATE_FILE_MAP _MD_CreateFileMap
+
+#define _MD_GET_MEM_MAP_ALIGNMENT() PR_GetPageSize()
+
+extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset,
+ PRUint32 len);
+#define _MD_MEM_MAP _MD_MemMap
+
+extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size);
+#define _MD_MEM_UNMAP _MD_MemUnmap
+
+extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap);
+#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap
+
+extern PRStatus _MD_SyncMemMap(
+ PRFileDesc *fd,
+ void *addr,
+ PRUint32 len);
+#define _MD_SYNC_MEM_MAP _MD_SyncMemMap
+
+/*
+ * The standard (XPG4) gettimeofday() (from BSD) takes two arguments.
+ * On some SVR4 derivatives, gettimeofday() takes only one argument.
+ * The GETTIMEOFDAY macro is intended to hide this difference.
+ */
+#ifdef HAVE_SVID_GETTOD
+#define GETTIMEOFDAY(tp) gettimeofday(tp)
+#else
+#define GETTIMEOFDAY(tp) gettimeofday((tp), NULL)
+#endif
+
+#if defined(_PR_PTHREADS) && !defined(_PR_POLL_AVAILABLE)
+#define _PR_NEED_FAKE_POLL
+#endif
+
+#if defined(_PR_NEED_FAKE_POLL)
+
+/*
+ * Some platforms don't have poll(), but our pthreads code calls poll().
+ * As a temporary measure, I implemented a fake poll() using select().
+ * Here are the struct and macro definitions copied from sys/poll.h
+ * on Solaris 2.5.
+ */
+
+struct pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+
+/* poll events */
+
+#define POLLIN 0x0001 /* fd is readable */
+#define POLLPRI 0x0002 /* high priority info at fd */
+#define POLLOUT 0x0004 /* fd is writeable (won't block) */
+#define POLLRDNORM 0x0040 /* normal data is readable */
+#define POLLWRNORM POLLOUT
+#define POLLRDBAND 0x0080 /* out-of-band data is readable */
+#define POLLWRBAND 0x0100 /* out-of-band data is writeable */
+
+#define POLLNORM POLLRDNORM
+
+#define POLLERR 0x0008 /* fd has error condition */
+#define POLLHUP 0x0010 /* fd has been hung up on */
+#define POLLNVAL 0x0020 /* invalid pollfd entry */
+
+extern int poll(struct pollfd *, unsigned long, int);
+
+#endif /* _PR_NEED_FAKE_POLL */
+
+/*
+** A vector of the UNIX I/O calls we use. These are here to smooth over
+** the rough edges needed for large files. All of NSPR's implmentaions
+** go through this vector using syntax of the form
+** result = _md_iovector.xxx64(args);
+*/
+
+#if defined(SOLARIS2_5)
+/*
+** Special case: Solaris 2.5.1
+** Solaris starts to have 64-bit file I/O in 2.6. We build on Solaris
+** 2.5.1 so that we can use the same binaries on both Solaris 2.5.1 and
+** 2.6. At run time, we detect whether 64-bit file I/O is available by
+** looking up the 64-bit file function symbols in libc. At build time,
+** we need to define the 64-bit file I/O datatypes that are compatible
+** with their definitions on Solaris 2.6.
+*/
+typedef PRInt64 off64_t;
+typedef PRUint64 ino64_t;
+typedef PRInt64 blkcnt64_t;
+struct stat64 {
+ dev_t st_dev;
+ long st_pad1[3];
+ ino64_t st_ino;
+ mode_t st_mode;
+ nlink_t st_nlink;
+ uid_t st_uid;
+ gid_t st_gid;
+ dev_t st_rdev;
+ long t_pad2[2];
+ off64_t st_size;
+ timestruc_t st_atim;
+ timestruc_t st_mtim;
+ timestruc_t st_ctim;
+ long st_blksize;
+ blkcnt64_t st_blocks;
+ char st_fstype[_ST_FSTYPSZ];
+ long st_pad4[8];
+};
+typedef struct stat64 _MDStat64;
+typedef off64_t _MDOff64_t;
+
+#elif defined(_PR_HAVE_OFF64_T)
+typedef struct stat64 _MDStat64;
+typedef off64_t _MDOff64_t;
+#elif defined(_PR_HAVE_LARGE_OFF_T)
+typedef struct stat _MDStat64;
+typedef off_t _MDOff64_t;
+#elif defined(_PR_NO_LARGE_FILES)
+typedef struct stat _MDStat64;
+typedef PRInt64 _MDOff64_t;
+#else
+#error "I don't know yet"
+#endif
+
+typedef PRIntn (*_MD_Fstat64)(PRIntn osfd, _MDStat64 *buf);
+typedef PRIntn (*_MD_Open64)(const char *path, int oflag, ...);
+typedef PRIntn (*_MD_Stat64)(const char *path, _MDStat64 *buf);
+typedef _MDOff64_t (*_MD_Lseek64)(PRIntn osfd, _MDOff64_t, PRIntn whence);
+typedef void* (*_MD_Mmap64)(
+ void *addr, PRSize len, PRIntn prot, PRIntn flags,
+ PRIntn fildes, _MDOff64_t offset);
+struct _MD_IOVector
+{
+ _MD_Open64 _open64;
+ _MD_Mmap64 _mmap64;
+ _MD_Stat64 _stat64;
+ _MD_Fstat64 _fstat64;
+ _MD_Lseek64 _lseek64;
+};
+extern struct _MD_IOVector _md_iovector;
+
+#endif /* prunixos_h___ */
diff --git a/nsprpub/pr/include/md/_unixware.cfg b/nsprpub/pr/include/md/_unixware.cfg
new file mode 100644
index 0000000000..0fa8dfc9b5
--- /dev/null
+++ b/nsprpub/pr/include/md/_unixware.cfg
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef UNIXWARE
+#define UNIXWARE
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#undef HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define _PR_POLL_BACKCOMPAT
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_unixware.h b/nsprpub/pr/include/md/_unixware.h
new file mode 100644
index 0000000000..06667b2988
--- /dev/null
+++ b/nsprpub/pr/include/md/_unixware.h
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_unixware_defs_h___
+#define nspr_unixware_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "unixware"
+#define _PR_SI_SYSNAME "UnixWare"
+#define _PR_SI_ARCHITECTURE "x86"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#ifndef HAVE_WEAK_IO_SYMBOLS
+#define HAVE_WEAK_IO_SYMBOLS
+#endif
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_STAT_HAS_ST_ATIM_UNION
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_NETCONFIG
+#define HAVE_DLL
+#define USE_DLFCN
+#define HAVE_STRERROR
+#define NEED_STRFTIME_LOCK
+#define NEED_TIME_R
+#define _PR_NEED_STRCASECMP
+
+#define USE_SETJMP
+
+#include <setjmp.h>
+
+#define _SETJMP setjmp
+#define _LONGJMP longjmp
+#define _PR_CONTEXT_TYPE jmp_buf
+#define _MD_GET_SP(_t) (_t)->md.context[4]
+#define _PR_NUM_GCREGS _JBLEN
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if(_SETJMP(CONTEXT(_thread))) (*_main)(); \
+ _MD_GET_SP(_thread) = (int) ((_sp) - 128); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!_SETJMP(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _LONGJMP(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures.
+ * Don't use SVR4 native threads (yet).
+ */
+
+struct _MDThread {
+ _PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+/*
+ * The following are copied from _sunos.h, _aix.h. This means
+ * some of them should probably be moved into _unixos.h. But
+ * _irix.h seems to be quite different in regard to these macros.
+ */
+#define _MD_INTERVAL_USE_GTOD
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+extern int _select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *execptfds, struct timeval *timeout);
+#define _MD_SELECT _select
+
+#define _MD_POLL _poll
+extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout);
+
+#endif /* nspr_unixware_defs_h___ */
diff --git a/nsprpub/pr/include/md/_unixware7.cfg b/nsprpub/pr/include/md/_unixware7.cfg
new file mode 100644
index 0000000000..33ce83bda1
--- /dev/null
+++ b/nsprpub/pr/include/md/_unixware7.cfg
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef UNIXWARE
+#define UNIXWARE
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_AF_INET6 27 /* same as AF_INET6 */
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define _PR_POLL_BACKCOMPAT
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_win32_errors.h b/nsprpub/pr/include/md/_win32_errors.h
new file mode 100644
index 0000000000..d267d8a28c
--- /dev/null
+++ b/nsprpub/pr/include/md/_win32_errors.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_win32_errors_h___
+#define nspr_win32_errors_h___
+
+#include <windows.h>
+#include <winsock.h>
+#include <errno.h>
+
+
+extern void _MD_win32_map_default_error(PRInt32 err);
+#define _PR_MD_MAP_DEFAULT_ERROR _MD_win32_map_default_error
+
+extern void _MD_win32_map_opendir_error(PRInt32 err);
+#define _PR_MD_MAP_OPENDIR_ERROR _MD_win32_map_opendir_error
+
+extern void _MD_win32_map_closedir_error(PRInt32 err);
+#define _PR_MD_MAP_CLOSEDIR_ERROR _MD_win32_map_closedir_error
+
+extern void _MD_unix_readdir_error(PRInt32 err);
+#define _PR_MD_MAP_READDIR_ERROR _MD_unix_readdir_error
+
+extern void _MD_win32_map_delete_error(PRInt32 err);
+#define _PR_MD_MAP_DELETE_ERROR _MD_win32_map_delete_error
+
+extern void _MD_win32_map_stat_error(PRInt32 err);
+#define _PR_MD_MAP_STAT_ERROR _MD_win32_map_stat_error
+
+extern void _MD_win32_map_fstat_error(PRInt32 err);
+#define _PR_MD_MAP_FSTAT_ERROR _MD_win32_map_fstat_error
+
+extern void _MD_win32_map_rename_error(PRInt32 err);
+#define _PR_MD_MAP_RENAME_ERROR _MD_win32_map_rename_error
+
+extern void _MD_win32_map_access_error(PRInt32 err);
+#define _PR_MD_MAP_ACCESS_ERROR _MD_win32_map_access_error
+
+extern void _MD_win32_map_mkdir_error(PRInt32 err);
+#define _PR_MD_MAP_MKDIR_ERROR _MD_win32_map_mkdir_error
+
+extern void _MD_win32_map_rmdir_error(PRInt32 err);
+#define _PR_MD_MAP_RMDIR_ERROR _MD_win32_map_rmdir_error
+
+extern void _MD_win32_map_read_error(PRInt32 err);
+#define _PR_MD_MAP_READ_ERROR _MD_win32_map_read_error
+
+extern void _MD_win32_map_transmitfile_error(PRInt32 err);
+#define _PR_MD_MAP_TRANSMITFILE_ERROR _MD_win32_map_transmitfile_error
+
+extern void _MD_win32_map_write_error(PRInt32 err);
+#define _PR_MD_MAP_WRITE_ERROR _MD_win32_map_write_error
+
+extern void _MD_win32_map_lseek_error(PRInt32 err);
+#define _PR_MD_MAP_LSEEK_ERROR _MD_win32_map_lseek_error
+
+extern void _MD_win32_map_fsync_error(PRInt32 err);
+#define _PR_MD_MAP_FSYNC_ERROR _MD_win32_map_fsync_error
+
+extern void _MD_win32_map_close_error(PRInt32 err);
+#define _PR_MD_MAP_CLOSE_ERROR _MD_win32_map_close_error
+
+extern void _MD_win32_map_socket_error(PRInt32 err);
+#define _PR_MD_MAP_SOCKET_ERROR _MD_win32_map_socket_error
+
+extern void _MD_win32_map_recv_error(PRInt32 err);
+#define _PR_MD_MAP_RECV_ERROR _MD_win32_map_recv_error
+
+extern void _MD_win32_map_recvfrom_error(PRInt32 err);
+#define _PR_MD_MAP_RECVFROM_ERROR _MD_win32_map_recvfrom_error
+
+extern void _MD_win32_map_send_error(PRInt32 err);
+#define _PR_MD_MAP_SEND_ERROR _MD_win32_map_send_error
+
+extern void _MD_win32_map_sendto_error(PRInt32 err);
+#define _PR_MD_MAP_SENDTO_ERROR _MD_win32_map_sendto_error
+
+extern void _MD_win32_map_accept_error(PRInt32 err);
+#define _PR_MD_MAP_ACCEPT_ERROR _MD_win32_map_accept_error
+
+extern void _MD_win32_map_acceptex_error(PRInt32 err);
+#define _PR_MD_MAP_ACCEPTEX_ERROR _MD_win32_map_acceptex_error
+
+extern void _MD_win32_map_connect_error(PRInt32 err);
+#define _PR_MD_MAP_CONNECT_ERROR _MD_win32_map_connect_error
+
+extern void _MD_win32_map_bind_error(PRInt32 err);
+#define _PR_MD_MAP_BIND_ERROR _MD_win32_map_bind_error
+
+extern void _MD_win32_map_listen_error(PRInt32 err);
+#define _PR_MD_MAP_LISTEN_ERROR _MD_win32_map_listen_error
+
+extern void _MD_win32_map_shutdown_error(PRInt32 err);
+#define _PR_MD_MAP_SHUTDOWN_ERROR _MD_win32_map_shutdown_error
+
+extern void _MD_win32_map_getsockname_error(PRInt32 err);
+#define _PR_MD_MAP_GETSOCKNAME_ERROR _MD_win32_map_getsockname_error
+
+extern void _MD_win32_map_getpeername_error(PRInt32 err);
+#define _PR_MD_MAP_GETPEERNAME_ERROR _MD_win32_map_getpeername_error
+
+extern void _MD_win32_map_getsockopt_error(PRInt32 err);
+#define _PR_MD_MAP_GETSOCKOPT_ERROR _MD_win32_map_getsockopt_error
+
+extern void _MD_win32_map_setsockopt_error(PRInt32 err);
+#define _PR_MD_MAP_SETSOCKOPT_ERROR _MD_win32_map_setsockopt_error
+
+extern void _MD_win32_map_open_error(PRInt32 err);
+#define _PR_MD_MAP_OPEN_ERROR _MD_win32_map_open_error
+
+extern void _MD_win32_map_gethostname_error(PRInt32 err);
+#define _PR_MD_MAP_GETHOSTNAME_ERROR _MD_win32_map_gethostname_error
+
+extern void _MD_win32_map_select_error(PRInt32 err);
+#define _PR_MD_MAP_SELECT_ERROR _MD_win32_map_select_error
+
+extern void _MD_win32_map_lockf_error(int err);
+#define _PR_MD_MAP_LOCKF_ERROR _MD_win32_map_lockf_error
+
+#endif /* nspr_win32_errors_h___ */
diff --git a/nsprpub/pr/include/md/_win95.cfg b/nsprpub/pr/include/md/_win95.cfg
new file mode 100644
index 0000000000..7c379fce70
--- /dev/null
+++ b/nsprpub/pr/include/md/_win95.cfg
@@ -0,0 +1,321 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_PC
+#define XP_PC
+#endif
+
+#ifndef WIN32
+#define WIN32
+#endif
+
+#ifndef WIN95
+#define WIN95
+#endif
+
+#define PR_AF_INET6 23 /* same as AF_INET6 */
+
+#if defined(_M_IX86) || defined(_X86_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 5
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 4
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 64
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 6
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 8
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(_M_IA64) || defined(_IA64_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 64
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 6
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 8
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(_M_ARM) || defined(_ARM_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 5
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 4
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(_M_ARM64) || defined(_ARM64_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 64
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 6
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 8
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else /* defined(_M_IX86) || defined(_X86_) */
+
+#error unknown processor architecture
+
+#endif /* defined(_M_IX86) || defined(_X86_) */
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_win95.h b/nsprpub/pr/include/md/_win95.h
new file mode 100644
index 0000000000..281cd6f09d
--- /dev/null
+++ b/nsprpub/pr/include/md/_win95.h
@@ -0,0 +1,549 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_win95_defs_h___
+#define nspr_win95_defs_h___
+
+#include "prio.h"
+
+#include <windows.h>
+#include <winsock.h>
+#include <errno.h>
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "win32"
+#define _PR_SI_SYSNAME "WIN95"
+#if defined(_M_IX86) || defined(_X86_)
+#define _PR_SI_ARCHITECTURE "x86"
+#elif defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)
+#define _PR_SI_ARCHITECTURE "x86-64"
+#elif defined(_M_IA64) || defined(_IA64_)
+#define _PR_SI_ARCHITECTURE "ia64"
+#elif defined(_M_ARM) || defined(_ARM_)
+#define _PR_SI_ARCHITECTURE "arm"
+#elif defined(_M_ARM64)
+#define _PR_SI_ARCHITECTURE "aarch64"
+#else
+#error unknown processor architecture
+#endif
+
+#define HAVE_DLL
+#undef HAVE_THREAD_AFFINITY
+#define _PR_HAVE_GETADDRINFO
+#define _PR_INET6_PROBE
+#ifndef _PR_INET6
+#define AF_INET6 23
+/* newer ws2tcpip.h provides these */
+#ifndef AI_CANONNAME
+#define AI_CANONNAME 0x2
+#define AI_NUMERICHOST 0x4
+#define NI_NUMERICHOST 0x02
+struct addrinfo {
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ char *ai_canonname;
+ struct sockaddr *ai_addr;
+ struct addrinfo *ai_next;
+};
+#endif
+#define _PR_HAVE_MD_SOCKADDR_IN6
+/* isomorphic to struct in6_addr on Windows */
+struct _md_in6_addr {
+ union {
+ PRUint8 _S6_u8[16];
+ PRUint16 _S6_u16[8];
+ } _S6_un;
+};
+/* isomorphic to struct sockaddr_in6 on Windows */
+struct _md_sockaddr_in6 {
+ PRInt16 sin6_family;
+ PRUint16 sin6_port;
+ PRUint32 sin6_flowinfo;
+ struct _md_in6_addr sin6_addr;
+ PRUint32 sin6_scope_id;
+};
+#endif
+#define _PR_HAVE_THREADSAFE_GETHOST
+#define _PR_HAVE_ATOMIC_OPS
+#define PR_HAVE_WIN32_NAMED_SHARED_MEMORY
+
+/* --- Common User-Thread/Native-Thread Definitions --------------------- */
+
+/* --- Globals --- */
+extern struct PRLock *_pr_schedLock;
+
+/* --- Typedefs --- */
+typedef void (*FiberFunc)(void *);
+
+#define PR_NUM_GCREGS 8
+typedef PRInt32 PR_CONTEXT_TYPE[PR_NUM_GCREGS];
+#define GC_VMBASE 0x40000000
+#define GC_VMLIMIT 0x00FFFFFF
+
+#define _MD_MAGIC_THREAD 0x22222222
+#define _MD_MAGIC_THREADSTACK 0x33333333
+#define _MD_MAGIC_SEGMENT 0x44444444
+#define _MD_MAGIC_DIR 0x55555555
+#define _MD_MAGIC_CV 0x66666666
+
+struct _MDCPU {
+ int unused;
+};
+
+struct _MDThread {
+ HANDLE blocked_sema; /* Threads block on this when waiting
+ * for IO or CondVar.
+ */
+ PRBool inCVWaitQueue; /* PR_TRUE if the thread is in the
+ * wait queue of some cond var.
+ * PR_FALSE otherwise. */
+ HANDLE handle; /* Win32 thread handle */
+ PRUint32 id;
+ void *sp; /* only valid when suspended */
+ PRUint32 magic; /* for debugging */
+ PR_CONTEXT_TYPE gcContext; /* Thread context for GC */
+ struct PRThread *prev, *next; /* used by the cvar wait queue to
+ * chain the PRThread structures
+ * together */
+ void (*start)(void *); /* used by _PR_MD_CREATE_THREAD to
+ * pass its 'start' argument to
+ * pr_root. */
+};
+
+struct _MDThreadStack {
+ PRUint32 magic; /* for debugging */
+};
+
+struct _MDSegment {
+ PRUint32 magic; /* for debugging */
+};
+
+#undef PROFILE_LOCKS
+
+struct _MDDir {
+ HANDLE d_hdl;
+ WIN32_FIND_DATAA d_entry;
+ PRBool firstEntry; /* Is this the entry returned
+ * by FindFirstFile()? */
+ PRUint32 magic; /* for debugging */
+};
+
+#ifdef MOZ_UNICODE
+struct _MDDirUTF16 {
+ HANDLE d_hdl;
+ WIN32_FIND_DATAW d_entry;
+ PRBool firstEntry; /* Is this the entry returned
+ * by FindFirstFileW()? */
+ PRUint32 magic; /* for debugging */
+};
+#endif /* MOZ_UNICODE */
+
+struct _MDCVar {
+ PRUint32 magic;
+ struct PRThread *waitHead, *waitTail; /* the wait queue: a doubly-
+ * linked list of threads
+ * waiting on this condition
+ * variable */
+ PRIntn nwait; /* number of threads in the
+ * wait queue */
+};
+
+#define _MD_CV_NOTIFIED_LENGTH 6
+typedef struct _MDNotified _MDNotified;
+struct _MDNotified {
+ PRIntn length; /* # of used entries in this
+ * structure */
+ struct {
+ struct _MDCVar *cv; /* the condition variable notified */
+ PRIntn times; /* and the number of times notified */
+ struct PRThread *notifyHead; /* list of threads to wake up */
+ } cv[_MD_CV_NOTIFIED_LENGTH];
+ _MDNotified *link; /* link to another of these, or NULL */
+};
+
+struct _MDLock {
+ CRITICAL_SECTION mutex; /* this is recursive on NT */
+
+ /*
+ * When notifying cvars, there is no point in actually
+ * waking up the threads waiting on the cvars until we've
+ * released the lock. So, we temporarily record the cvars.
+ * When doing an unlock, we'll then wake up the waiting threads.
+ */
+ struct _MDNotified notified; /* array of conditions notified */
+#ifdef PROFILE_LOCKS
+ PRInt32 hitcount;
+ PRInt32 misscount;
+#endif
+};
+
+struct _MDSemaphore {
+ HANDLE sem;
+};
+
+struct _MDFileDesc {
+ PROsfd osfd; /* The osfd can come from one of three spaces:
+ * - For stdin, stdout, and stderr, we are using
+ * the libc file handle (0, 1, 2), which is an int.
+ * - For files and pipes, we are using Win32 HANDLE,
+ * which is a void*.
+ * - For sockets, we are using Winsock SOCKET, which
+ * is a u_int.
+ */
+};
+
+struct _MDProcess {
+ HANDLE handle;
+ DWORD id;
+};
+
+/* --- Misc stuff --- */
+#define _MD_GET_SP(thread) (thread)->md.gcContext[6]
+
+/* --- NT security stuff --- */
+
+extern void _PR_NT_InitSids(void);
+extern void _PR_NT_FreeSids(void);
+extern PRStatus _PR_NT_MakeSecurityDescriptorACL(
+ PRIntn mode,
+ DWORD accessTable[],
+ PSECURITY_DESCRIPTOR *resultSD,
+ PACL *resultACL
+);
+extern void _PR_NT_FreeSecurityDescriptorACL(
+ PSECURITY_DESCRIPTOR pSD, PACL pACL);
+
+/* --- IO stuff --- */
+
+#define _MD_OPEN _PR_MD_OPEN
+#define _MD_OPEN_FILE _PR_MD_OPEN_FILE
+#define _MD_READ _PR_MD_READ
+#define _MD_WRITE _PR_MD_WRITE
+#define _MD_WRITEV _PR_MD_WRITEV
+#define _MD_LSEEK _PR_MD_LSEEK
+#define _MD_LSEEK64 _PR_MD_LSEEK64
+extern PRInt32 _MD_CloseFile(PROsfd osfd);
+#define _MD_CLOSE_FILE _MD_CloseFile
+#define _MD_GETFILEINFO _PR_MD_GETFILEINFO
+#define _MD_GETFILEINFO64 _PR_MD_GETFILEINFO64
+#define _MD_GETOPENFILEINFO _PR_MD_GETOPENFILEINFO
+#define _MD_GETOPENFILEINFO64 _PR_MD_GETOPENFILEINFO64
+#define _MD_STAT _PR_MD_STAT
+#define _MD_RENAME _PR_MD_RENAME
+#define _MD_ACCESS _PR_MD_ACCESS
+#define _MD_DELETE _PR_MD_DELETE
+#define _MD_MKDIR _PR_MD_MKDIR
+#define _MD_MAKE_DIR _PR_MD_MAKE_DIR
+#define _MD_RMDIR _PR_MD_RMDIR
+#define _MD_LOCKFILE _PR_MD_LOCKFILE
+#define _MD_TLOCKFILE _PR_MD_TLOCKFILE
+#define _MD_UNLOCKFILE _PR_MD_UNLOCKFILE
+
+/* --- UTF16 IO stuff --- */
+#ifdef MOZ_UNICODE
+#define _MD_OPEN_FILE_UTF16 _PR_MD_OPEN_FILE_UTF16
+#define _MD_OPEN_DIR_UTF16 _PR_MD_OPEN_DIR_UTF16
+#define _MD_READ_DIR_UTF16 _PR_MD_READ_DIR_UTF16
+#define _MD_CLOSE_DIR_UTF16 _PR_MD_CLOSE_DIR_UTF16
+#define _MD_GETFILEINFO64_UTF16 _PR_MD_GETFILEINFO64_UTF16
+#endif /* MOZ_UNICODE */
+
+/* --- Socket IO stuff --- */
+extern void _PR_MD_InitSockets(void);
+extern void _PR_MD_CleanupSockets(void);
+#define _MD_EACCES WSAEACCES
+#define _MD_EADDRINUSE WSAEADDRINUSE
+#define _MD_EADDRNOTAVAIL WSAEADDRNOTAVAIL
+#define _MD_EAFNOSUPPORT WSAEAFNOSUPPORT
+#define _MD_EAGAIN WSAEWOULDBLOCK
+#define _MD_EALREADY WSAEALREADY
+#define _MD_EBADF WSAEBADF
+#define _MD_ECONNREFUSED WSAECONNREFUSED
+#define _MD_ECONNRESET WSAECONNRESET
+#define _MD_EFAULT WSAEFAULT
+#define _MD_EINPROGRESS WSAEINPROGRESS
+#define _MD_EINTR WSAEINTR
+#define _MD_EINVAL EINVAL
+#define _MD_EISCONN WSAEISCONN
+#define _MD_ENETUNREACH WSAENETUNREACH
+#define _MD_ENOENT ENOENT
+#define _MD_ENOTCONN WSAENOTCONN
+#define _MD_ENOTSOCK WSAENOTSOCK
+#define _MD_EOPNOTSUPP WSAEOPNOTSUPP
+#define _MD_EWOULDBLOCK WSAEWOULDBLOCK
+#define _MD_GET_SOCKET_ERROR() WSAGetLastError()
+#define _MD_SET_SOCKET_ERROR(_err) WSASetLastError(_err)
+
+#define _MD_INIT_FILEDESC(fd)
+extern void _MD_MakeNonblock(PRFileDesc *f);
+#define _MD_MAKE_NONBLOCK _MD_MakeNonblock
+#define _MD_INIT_FD_INHERITABLE _PR_MD_INIT_FD_INHERITABLE
+#define _MD_QUERY_FD_INHERITABLE _PR_MD_QUERY_FD_INHERITABLE
+#define _MD_SHUTDOWN _PR_MD_SHUTDOWN
+#define _MD_LISTEN _PR_MD_LISTEN
+extern PRInt32 _MD_CloseSocket(PROsfd osfd);
+#define _MD_CLOSE_SOCKET _MD_CloseSocket
+#define _MD_SENDTO _PR_MD_SENDTO
+#ifdef _WIN64
+#define _MD_TCPSENDTO _PR_MD_TCPSENDTO
+#endif
+#define _MD_RECVFROM _PR_MD_RECVFROM
+#define _MD_SOCKETPAIR(s, type, proto, sv) -1
+#define _MD_GETSOCKNAME _PR_MD_GETSOCKNAME
+#define _MD_GETPEERNAME _PR_MD_GETPEERNAME
+#define _MD_GETSOCKOPT _PR_MD_GETSOCKOPT
+#define _MD_SETSOCKOPT _PR_MD_SETSOCKOPT
+#define _MD_SET_FD_INHERITABLE _PR_MD_SET_FD_INHERITABLE
+#define _MD_SELECT select
+#define _MD_FSYNC _PR_MD_FSYNC
+#define READ_FD 1
+#define WRITE_FD 2
+
+#define _MD_INIT_ATOMIC()
+#if defined(_M_IX86) || defined(_X86_)
+#define _MD_ATOMIC_INCREMENT _PR_MD_ATOMIC_INCREMENT
+#define _MD_ATOMIC_ADD _PR_MD_ATOMIC_ADD
+#define _MD_ATOMIC_DECREMENT _PR_MD_ATOMIC_DECREMENT
+#else /* non-x86 processors */
+#define _MD_ATOMIC_INCREMENT(x) InterlockedIncrement((PLONG)x)
+#define _MD_ATOMIC_ADD(ptr,val) (InterlockedExchangeAdd((PLONG)ptr, (LONG)val) + val)
+#define _MD_ATOMIC_DECREMENT(x) InterlockedDecrement((PLONG)x)
+#endif /* x86 */
+#define _MD_ATOMIC_SET(x,y) InterlockedExchange((PLONG)x, (LONG)y)
+
+#define _MD_INIT_IO _PR_MD_INIT_IO
+
+
+/* win95 doesn't have async IO */
+#define _MD_SOCKET _PR_MD_SOCKET
+extern PRInt32 _MD_SocketAvailable(PRFileDesc *fd);
+#define _MD_SOCKETAVAILABLE _MD_SocketAvailable
+#define _MD_PIPEAVAILABLE _PR_MD_PIPEAVAILABLE
+#define _MD_CONNECT _PR_MD_CONNECT
+extern PROsfd _MD_Accept(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
+ PRIntervalTime timeout);
+#define _MD_ACCEPT _MD_Accept
+#define _MD_BIND _PR_MD_BIND
+#define _MD_RECV _PR_MD_RECV
+#define _MD_SEND _PR_MD_SEND
+#define _MD_PR_POLL _PR_MD_PR_POLL
+
+/* --- Scheduler stuff --- */
+// #define _MD_PAUSE_CPU _PR_MD_PAUSE_CPU
+#define _MD_PAUSE_CPU
+
+/* --- DIR stuff --- */
+#define PR_DIRECTORY_SEPARATOR '\\'
+#define PR_DIRECTORY_SEPARATOR_STR "\\"
+#define PR_PATH_SEPARATOR ';'
+#define PR_PATH_SEPARATOR_STR ";"
+#define _MD_ERRNO() GetLastError()
+#define _MD_OPEN_DIR _PR_MD_OPEN_DIR
+#define _MD_CLOSE_DIR _PR_MD_CLOSE_DIR
+#define _MD_READ_DIR _PR_MD_READ_DIR
+
+/* --- Segment stuff --- */
+#define _MD_INIT_SEGS()
+#define _MD_ALLOC_SEGMENT(seg, size, vaddr) 0
+#define _MD_FREE_SEGMENT(seg)
+
+/* --- Environment Stuff --- */
+#define _MD_GET_ENV _PR_MD_GET_ENV
+#define _MD_PUT_ENV _PR_MD_PUT_ENV
+
+/* --- Threading Stuff --- */
+#define _MD_DEFAULT_STACK_SIZE 0
+#define _MD_INIT_THREAD _PR_MD_INIT_THREAD
+#define _MD_INIT_ATTACHED_THREAD _PR_MD_INIT_THREAD
+#define _MD_CREATE_THREAD _PR_MD_CREATE_THREAD
+#define _MD_YIELD _PR_MD_YIELD
+#define _MD_SET_PRIORITY _PR_MD_SET_PRIORITY
+#define _MD_SET_CURRENT_THREAD_NAME _PR_MD_SET_CURRENT_THREAD_NAME
+#define _MD_CLEAN_THREAD _PR_MD_CLEAN_THREAD
+#define _MD_SETTHREADAFFINITYMASK _PR_MD_SETTHREADAFFINITYMASK
+#define _MD_GETTHREADAFFINITYMASK _PR_MD_GETTHREADAFFINITYMASK
+#define _MD_EXIT_THREAD _PR_MD_EXIT_THREAD
+#define _MD_EXIT _PR_MD_EXIT
+#define _MD_SUSPEND_THREAD _PR_MD_SUSPEND_THREAD
+#define _MD_RESUME_THREAD _PR_MD_RESUME_THREAD
+#define _MD_SUSPEND_CPU _PR_MD_SUSPEND_CPU
+#define _MD_RESUME_CPU _PR_MD_RESUME_CPU
+#define _MD_BEGIN_SUSPEND_ALL()
+#define _MD_BEGIN_RESUME_ALL()
+#define _MD_END_SUSPEND_ALL()
+#define _MD_END_RESUME_ALL()
+
+/* --- Lock stuff --- */
+#define _PR_LOCK _MD_LOCK
+#define _PR_UNLOCK _MD_UNLOCK
+
+#define _MD_NEW_LOCK _PR_MD_NEW_LOCK
+#define _MD_FREE_LOCK(lock) DeleteCriticalSection(&((lock)->mutex))
+#define _MD_LOCK(lock) EnterCriticalSection(&((lock)->mutex))
+#define _MD_TEST_AND_LOCK(lock) (EnterCriticalSection(&((lock)->mutex)),0)
+#define _MD_UNLOCK _PR_MD_UNLOCK
+
+/* --- lock and cv waiting --- */
+#define _MD_WAIT _PR_MD_WAIT
+#define _MD_WAKEUP_WAITER _PR_MD_WAKEUP_WAITER
+
+/* --- CVar ------------------- */
+#define _MD_WAIT_CV _PR_MD_WAIT_CV
+#define _MD_NEW_CV _PR_MD_NEW_CV
+#define _MD_FREE_CV _PR_MD_FREE_CV
+#define _MD_NOTIFY_CV _PR_MD_NOTIFY_CV
+#define _MD_NOTIFYALL_CV _PR_MD_NOTIFYALL_CV
+
+/* XXXMB- the IOQ stuff is certainly not working correctly yet. */
+// extern struct _MDLock _pr_ioq_lock;
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+
+/* --- Initialization stuff --- */
+#define _MD_START_INTERRUPTS()
+#define _MD_STOP_INTERRUPTS()
+#define _MD_DISABLE_CLOCK_INTERRUPTS()
+#define _MD_ENABLE_CLOCK_INTERRUPTS()
+#define _MD_BLOCK_CLOCK_INTERRUPTS()
+#define _MD_UNBLOCK_CLOCK_INTERRUPTS()
+#define _MD_EARLY_INIT _PR_MD_EARLY_INIT
+#define _MD_FINAL_INIT()
+#define _MD_EARLY_CLEANUP()
+#define _MD_INIT_CPUS()
+#define _MD_INIT_RUNNING_CPU(cpu)
+
+struct PRProcess;
+struct PRProcessAttr;
+
+#define _MD_CREATE_PROCESS _PR_CreateWindowsProcess
+extern struct PRProcess * _PR_CreateWindowsProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const struct PRProcessAttr *attr
+);
+
+#define _MD_DETACH_PROCESS _PR_DetachWindowsProcess
+extern PRStatus _PR_DetachWindowsProcess(struct PRProcess *process);
+
+/* --- Wait for a child process to terminate --- */
+#define _MD_WAIT_PROCESS _PR_WaitWindowsProcess
+extern PRStatus _PR_WaitWindowsProcess(struct PRProcess *process,
+ PRInt32 *exitCode);
+
+#define _MD_KILL_PROCESS _PR_KillWindowsProcess
+extern PRStatus _PR_KillWindowsProcess(struct PRProcess *process);
+
+#define _MD_CLEANUP_BEFORE_EXIT _PR_MD_CLEANUP_BEFORE_EXIT
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ *status = PR_TRUE; \
+ PR_END_MACRO
+#define _MD_SWITCH_CONTEXT
+#define _MD_RESTORE_CONTEXT
+
+/* --- Intervals --- */
+#define _MD_INTERVAL_INIT _PR_MD_INTERVAL_INIT
+#define _MD_GET_INTERVAL _PR_MD_GET_INTERVAL
+#define _MD_INTERVAL_PER_SEC _PR_MD_INTERVAL_PER_SEC
+#define _MD_INTERVAL_PER_MILLISEC() (_PR_MD_INTERVAL_PER_SEC() / 1000)
+#define _MD_INTERVAL_PER_MICROSEC() (_PR_MD_INTERVAL_PER_SEC() / 1000000)
+
+/* --- Time --- */
+extern void _PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm);
+
+#ifdef WINCE
+extern void _MD_InitTime(void);
+extern void _MD_CleanupTime(void);
+#endif
+
+/* --- Native-Thread Specific Definitions ------------------------------- */
+
+extern struct PRThread * _MD_CURRENT_THREAD(void);
+
+#ifdef _PR_USE_STATIC_TLS
+extern __declspec(thread) struct PRThread *_pr_currentThread;
+#define _MD_GET_ATTACHED_THREAD() _pr_currentThread
+#define _MD_SET_CURRENT_THREAD(_thread) (_pr_currentThread = (_thread))
+
+extern __declspec(thread) struct PRThread *_pr_thread_last_run;
+#define _MD_LAST_THREAD() _pr_thread_last_run
+#define _MD_SET_LAST_THREAD(_thread) (_pr_thread_last_run = 0)
+
+extern __declspec(thread) struct _PRCPU *_pr_currentCPU;
+#define _MD_CURRENT_CPU() _pr_currentCPU
+#define _MD_SET_CURRENT_CPU(_cpu) (_pr_currentCPU = 0)
+#else /* _PR_USE_STATIC_TLS */
+extern DWORD _pr_currentThreadIndex;
+#define _MD_GET_ATTACHED_THREAD() ((PRThread *) TlsGetValue(_pr_currentThreadIndex))
+#define _MD_SET_CURRENT_THREAD(_thread) TlsSetValue(_pr_currentThreadIndex, (_thread))
+
+extern DWORD _pr_lastThreadIndex;
+#define _MD_LAST_THREAD() ((PRThread *) TlsGetValue(_pr_lastThreadIndex))
+#define _MD_SET_LAST_THREAD(_thread) TlsSetValue(_pr_lastThreadIndex, 0)
+
+extern DWORD _pr_currentCPUIndex;
+#define _MD_CURRENT_CPU() ((struct _PRCPU *) TlsGetValue(_pr_currentCPUIndex))
+#define _MD_SET_CURRENT_CPU(_cpu) TlsSetValue(_pr_currentCPUIndex, 0)
+#endif /* _PR_USE_STATIC_TLS */
+
+/* --- Scheduler stuff --- */
+#define LOCK_SCHEDULER() 0
+#define UNLOCK_SCHEDULER() 0
+#define _PR_LockSched() 0
+#define _PR_UnlockSched() 0
+
+/* --- Initialization stuff --- */
+#define _MD_INIT_LOCKS _PR_MD_INIT_LOCKS
+
+/* --- Stack stuff --- */
+#define _MD_INIT_STACK(stack, redzone)
+#define _MD_CLEAR_STACK(stack)
+
+/* --- Memory-mapped files stuff --- */
+
+struct _MDFileMap {
+ HANDLE hFileMap;
+ DWORD dwAccess;
+};
+
+extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size);
+#define _MD_CREATE_FILE_MAP _MD_CreateFileMap
+
+extern PRInt32 _MD_GetMemMapAlignment(void);
+#define _MD_GET_MEM_MAP_ALIGNMENT _MD_GetMemMapAlignment
+
+extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset,
+ PRUint32 len);
+#define _MD_MEM_MAP _MD_MemMap
+
+extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size);
+#define _MD_MEM_UNMAP _MD_MemUnmap
+
+extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap);
+#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap
+
+extern PRStatus _MD_SyncMemMap(
+ PRFileDesc *fd,
+ void *addr,
+ PRUint32 len);
+#define _MD_SYNC_MEM_MAP _MD_SyncMemMap
+
+/* --- Named semaphores stuff --- */
+#define _PR_HAVE_NAMED_SEMAPHORES
+#define _MD_OPEN_SEMAPHORE _PR_MD_OPEN_SEMAPHORE
+#define _MD_WAIT_SEMAPHORE _PR_MD_WAIT_SEMAPHORE
+#define _MD_POST_SEMAPHORE _PR_MD_POST_SEMAPHORE
+#define _MD_CLOSE_SEMAPHORE _PR_MD_CLOSE_SEMAPHORE
+#define _MD_DELETE_SEMAPHORE(name) PR_SUCCESS /* no op */
+
+#endif /* nspr_win32_defs_h___ */
diff --git a/nsprpub/pr/include/md/_winnt.cfg b/nsprpub/pr/include/md/_winnt.cfg
new file mode 100644
index 0000000000..e2a277fdcd
--- /dev/null
+++ b/nsprpub/pr/include/md/_winnt.cfg
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_PC
+#define XP_PC
+#endif
+
+#ifndef WIN32
+#define WIN32
+#endif
+
+#ifndef WINNT
+#define WINNT
+#endif
+
+#define PR_AF_INET6 23 /* same as AF_INET6 */
+
+#if defined(_M_IX86) || defined(_X86_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 5
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 4
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 2
+
+#elif defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 64
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 6
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 8
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(_M_IA64) || defined(_IA64_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 64
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 6
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 8
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else /* defined(_M_IX86) || defined(_X86_) */
+
+#error unknown processor architecture
+
+#endif /* defined(_M_IX86) || defined(_X86_) */
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/nsprpub/pr/include/md/_winnt.h b/nsprpub/pr/include/md/_winnt.h
new file mode 100644
index 0000000000..faed03ec83
--- /dev/null
+++ b/nsprpub/pr/include/md/_winnt.h
@@ -0,0 +1,599 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_win32_defs_h___
+#define nspr_win32_defs_h___
+
+/* Need to force service-pack 3 extensions to be defined by
+** setting _WIN32_WINNT to NT 4.0 for winsock.h, winbase.h, winnt.h.
+*/
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0400
+#elif (_WIN32_WINNT < 0x0400)
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0400
+#endif /* _WIN32_WINNT */
+
+#include <windows.h>
+#include <winsock.h>
+#ifdef __MINGW32__
+#include <mswsock.h>
+#endif
+#include <errno.h>
+
+#include "prio.h"
+#include "prclist.h"
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "win32"
+#define _PR_SI_SYSNAME "WINNT"
+#if defined(_M_IX86) || defined(_X86_)
+#define _PR_SI_ARCHITECTURE "x86"
+#elif defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)
+#define _PR_SI_ARCHITECTURE "x86-64"
+#elif defined(_M_IA64) || defined(_IA64_)
+#define _PR_SI_ARCHITECTURE "ia64"
+#else
+#error unknown processor architecture
+#endif
+
+#define HAVE_DLL
+#define HAVE_CUSTOM_USER_THREADS
+#define HAVE_THREAD_AFFINITY
+#define _PR_HAVE_GETADDRINFO
+#define _PR_INET6_PROBE
+#ifndef _PR_INET6
+#define AF_INET6 23
+/* newer ws2tcpip.h provides these */
+#ifndef AI_CANONNAME
+#define AI_CANONNAME 0x2
+#define AI_NUMERICHOST 0x4
+#define NI_NUMERICHOST 0x02
+struct addrinfo {
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ char *ai_canonname;
+ struct sockaddr *ai_addr;
+ struct addrinfo *ai_next;
+};
+#endif
+#define _PR_HAVE_MD_SOCKADDR_IN6
+/* isomorphic to struct in6_addr on Windows */
+struct _md_in6_addr {
+ union {
+ PRUint8 _S6_u8[16];
+ PRUint16 _S6_u16[8];
+ } _S6_un;
+};
+/* isomorphic to struct sockaddr_in6 on Windows */
+struct _md_sockaddr_in6 {
+ PRInt16 sin6_family;
+ PRUint16 sin6_port;
+ PRUint32 sin6_flowinfo;
+ struct _md_in6_addr sin6_addr;
+ PRUint32 sin6_scope_id;
+};
+#endif
+#define _PR_HAVE_THREADSAFE_GETHOST
+#define _PR_HAVE_ATOMIC_OPS
+#if defined(_M_IX86) || defined(_X86_)
+#define _PR_HAVE_ATOMIC_CAS
+#endif
+#define PR_HAVE_WIN32_NAMED_SHARED_MEMORY
+#define _PR_HAVE_PEEK_BUFFER
+#define _PR_PEEK_BUFFER_MAX (32 * 1024)
+#define _PR_FD_NEED_EMULATE_MSG_PEEK(fd) \
+ (!(fd)->secret->nonblocking && (fd)->secret->inheritable != _PR_TRI_TRUE)
+#define _PR_NEED_SECRET_AF
+
+/* --- Common User-Thread/Native-Thread Definitions --------------------- */
+
+/* --- Globals --- */
+extern struct PRLock *_pr_schedLock;
+
+/* --- Typedefs --- */
+typedef void (*FiberFunc)(void *);
+
+#define PR_NUM_GCREGS 8
+typedef PRInt32 PR_CONTEXT_TYPE[PR_NUM_GCREGS];
+#define GC_VMBASE 0x40000000
+#define GC_VMLIMIT 0x00FFFFFF
+
+#define _MD_MAGIC_THREAD 0x22222222
+#define _MD_MAGIC_THREADSTACK 0x33333333
+#define _MD_MAGIC_SEGMENT 0x44444444
+#define _MD_MAGIC_DIR 0x55555555
+
+struct _MDCPU {
+ int unused;
+};
+
+enum _MDIOModel {
+ _MD_BlockingIO = 0x38,
+ _MD_MultiWaitIO = 0x49
+};
+
+typedef struct _MDOverlapped {
+ OVERLAPPED overlapped; /* Used for async I/O */
+
+ enum _MDIOModel ioModel; /* The I/O model to implement
+ * using overlapped I/O.
+ */
+ union {
+ struct _MDThread *mdThread; /* For blocking I/O, this structure
+ * is embedded in the _MDThread
+ * structure.
+ */
+ struct {
+ PRCList links; /* for group->io_ready list */
+ struct PRRecvWait *desc; /* For multiwait I/O, this structure
+ * is associated with a PRRecvWait
+ * structure.
+ */
+ struct PRWaitGroup *group;
+ struct TimerEvent *timer;
+ DWORD error;
+ } mw;
+ } data;
+} _MDOverlapped;
+
+struct _MDThread {
+ /* The overlapped structure must be first! */
+ struct _MDOverlapped overlapped; /* Used for async IO for this thread */
+ void *acceptex_buf; /* Used for AcceptEx() */
+ TRANSMIT_FILE_BUFFERS *xmit_bufs; /* Used for TransmitFile() */
+ HANDLE blocked_sema; /* Threads block on this when waiting
+ * for IO or CondVar.
+ */
+ PRInt32 blocked_io_status; /* Status of the completed IO */
+ PRInt32 blocked_io_bytes; /* Bytes transferred for completed IO */
+ PRInt32 blocked_io_error; /* Save error if status is FALSE */
+ HANDLE handle;
+ PRUint32 id;
+ void *sp; /* only valid when suspended */
+ PRUint32 magic; /* for debugging */
+ PR_CONTEXT_TYPE gcContext; /* Thread context for GC */
+ struct _PRCPU *thr_bound_cpu; /* thread bound to cpu */
+ PRBool interrupt_disabled;/* thread cannot be interrupted */
+ HANDLE thr_event; /* For native-threads-only support,
+ thread blocks on this event */
+
+ /* The following are used only if this is a fiber */
+ void *fiber_id; /* flag whether or not this is a fiber*/
+ FiberFunc fiber_fn; /* main fiber routine */
+ void *fiber_arg; /* arg to main fiber routine */
+ PRUint32 fiber_stacksize; /* stacksize for fiber */
+ PRInt32 fiber_last_error; /* last error for the fiber */
+ void (*start)(void *); /* used by _PR_MD_CREATE_THREAD to
+ * pass its 'start' argument to
+ * pr_root. */
+};
+
+struct _MDThreadStack {
+ PRUint32 magic; /* for debugging */
+};
+
+struct _MDSegment {
+ PRUint32 magic; /* for debugging */
+};
+
+#undef PROFILE_LOCKS
+
+struct _MDLock {
+ CRITICAL_SECTION mutex; /* this is recursive on NT */
+#ifdef PROFILE_LOCKS
+ PRInt32 hitcount;
+ PRInt32 misscount;
+#endif
+};
+
+struct _MDDir {
+ HANDLE d_hdl;
+ WIN32_FIND_DATA d_entry;
+ PRBool firstEntry; /* Is this the entry returned
+ * by FindFirstFile()? */
+ PRUint32 magic; /* for debugging */
+};
+
+struct _MDCVar {
+ PRUint32 unused;
+};
+
+struct _MDSemaphore {
+ HANDLE sem;
+};
+
+struct _MDFileDesc {
+ PROsfd osfd; /* The osfd can come from one of three spaces:
+ * - For stdin, stdout, and stderr, we are using
+ * the libc file handle (0, 1, 2), which is an int.
+ * - For files and pipes, we are using Win32 HANDLE,
+ * which is a void*.
+ * - For sockets, we are using Winsock SOCKET, which
+ * is a u_int.
+ */
+ PRBool io_model_committed; /* The io model (blocking or nonblocking)
+ * for this osfd has been committed and
+ * cannot be changed. The osfd has been
+ * either associated with the io
+ * completion port or made nonblocking. */
+ PRBool sync_file_io; /* Use synchronous file I/O on the osfd
+ * (a file handle) */
+ PRBool accepted_socket; /* Is this an accepted socket (on the
+ * server side)? */
+ PRNetAddr peer_addr; /* If this is an accepted socket, cache
+ * the peer's address returned by
+ * AcceptEx(). This is to work around
+ * the bug that getpeername() on an
+ * socket accepted by AcceptEx() returns
+ * an all-zero net address. */
+};
+
+struct _MDProcess {
+ HANDLE handle;
+ DWORD id;
+};
+
+
+/* --- Misc stuff --- */
+#define _MD_GET_SP(thread) (thread)->md.gcContext[6]
+
+/* --- NT security stuff --- */
+
+extern void _PR_NT_InitSids(void);
+extern void _PR_NT_FreeSids(void);
+extern PRStatus _PR_NT_MakeSecurityDescriptorACL(
+ PRIntn mode,
+ DWORD accessTable[],
+ PSECURITY_DESCRIPTOR *resultSD,
+ PACL *resultACL
+);
+extern void _PR_NT_FreeSecurityDescriptorACL(
+ PSECURITY_DESCRIPTOR pSD, PACL pACL);
+
+/* --- IO stuff --- */
+
+extern PRInt32 _md_Associate(HANDLE);
+extern PRInt32 _PR_MD_CLOSE(PROsfd osfd, PRBool socket);
+
+#define _MD_OPEN _PR_MD_OPEN
+#define _MD_OPEN_FILE _PR_MD_OPEN_FILE
+#define _MD_READ _PR_MD_READ
+#define _MD_WRITE _PR_MD_WRITE
+#define _MD_WRITEV _PR_MD_WRITEV
+#define _MD_LSEEK _PR_MD_LSEEK
+#define _MD_LSEEK64 _PR_MD_LSEEK64
+#define _MD_CLOSE_FILE(f) _PR_MD_CLOSE(f, PR_FALSE)
+#define _MD_GETFILEINFO _PR_MD_GETFILEINFO
+#define _MD_GETFILEINFO64 _PR_MD_GETFILEINFO64
+#define _MD_GETOPENFILEINFO _PR_MD_GETOPENFILEINFO
+#define _MD_GETOPENFILEINFO64 _PR_MD_GETOPENFILEINFO64
+#define _MD_STAT _PR_MD_STAT
+#define _MD_RENAME _PR_MD_RENAME
+#define _MD_ACCESS _PR_MD_ACCESS
+#define _MD_DELETE _PR_MD_DELETE
+#define _MD_MKDIR _PR_MD_MKDIR
+#define _MD_MAKE_DIR _PR_MD_MAKE_DIR
+#define _MD_RMDIR _PR_MD_RMDIR
+#define _MD_LOCKFILE _PR_MD_LOCKFILE
+#define _MD_TLOCKFILE _PR_MD_TLOCKFILE
+#define _MD_UNLOCKFILE _PR_MD_UNLOCKFILE
+
+/* --- Socket IO stuff --- */
+#define _MD_GET_SOCKET_ERROR() WSAGetLastError()
+#define _MD_SET_SOCKET_ERROR(_err) WSASetLastError(_err)
+
+#define _MD_INIT_FILEDESC(fd)
+#define _MD_MAKE_NONBLOCK _PR_MD_MAKE_NONBLOCK
+#define _MD_INIT_FD_INHERITABLE _PR_MD_INIT_FD_INHERITABLE
+#define _MD_QUERY_FD_INHERITABLE _PR_MD_QUERY_FD_INHERITABLE
+#define _MD_SHUTDOWN _PR_MD_SHUTDOWN
+#define _MD_LISTEN _PR_MD_LISTEN
+#define _MD_CLOSE_SOCKET(s) _PR_MD_CLOSE(s, PR_TRUE)
+#define _MD_SENDTO _PR_MD_SENDTO
+#define _MD_RECVFROM _PR_MD_RECVFROM
+#define _MD_SOCKETPAIR(s, type, proto, sv) -1
+#define _MD_GETSOCKNAME _PR_MD_GETSOCKNAME
+#define _MD_GETPEERNAME _PR_MD_GETPEERNAME
+#define _MD_GETSOCKOPT _PR_MD_GETSOCKOPT
+#define _MD_SETSOCKOPT _PR_MD_SETSOCKOPT
+#define _MD_SELECT select
+extern int _PR_NTFiberSafeSelect(int, fd_set *, fd_set *, fd_set *,
+ const struct timeval *);
+#define _MD_FSYNC _PR_MD_FSYNC
+#define _MD_SOCKETAVAILABLE _PR_MD_SOCKETAVAILABLE
+#define _MD_PIPEAVAILABLE _PR_MD_PIPEAVAILABLE
+#define _MD_SET_FD_INHERITABLE _PR_MD_SET_FD_INHERITABLE
+
+#define _MD_INIT_ATOMIC()
+#if defined(_M_IX86) || defined(_X86_)
+#define _MD_ATOMIC_INCREMENT _PR_MD_ATOMIC_INCREMENT
+#define _MD_ATOMIC_ADD _PR_MD_ATOMIC_ADD
+#define _MD_ATOMIC_DECREMENT _PR_MD_ATOMIC_DECREMENT
+#else /* non-x86 processors */
+#define _MD_ATOMIC_INCREMENT(x) InterlockedIncrement((PLONG)x)
+#define _MD_ATOMIC_ADD(ptr,val) (InterlockedExchangeAdd((PLONG)ptr, (LONG)val) + val)
+#define _MD_ATOMIC_DECREMENT(x) InterlockedDecrement((PLONG)x)
+#endif /* x86 */
+#define _MD_ATOMIC_SET(x,y) InterlockedExchange((PLONG)x, (LONG)y)
+
+#define _MD_INIT_IO _PR_MD_INIT_IO
+#define _MD_SOCKET _PR_MD_SOCKET
+#define _MD_CONNECT _PR_MD_CONNECT
+
+#define _MD_ACCEPT(s, a, l, to) \
+ _MD_FAST_ACCEPT(s, a, l, to, PR_FALSE, NULL, NULL)
+#define _MD_FAST_ACCEPT(s, a, l, to, fast, cb, cba) \
+ _PR_MD_FAST_ACCEPT(s, a, l, to, fast, cb, cba)
+#define _MD_ACCEPT_READ(s, ns, ra, buf, l, t) \
+ _MD_FAST_ACCEPT_READ(s, ns, ra, buf, l, t, PR_FALSE, NULL, NULL)
+#define _MD_FAST_ACCEPT_READ(s, ns, ra, buf, l, t, fast, cb, cba) \
+ _PR_MD_FAST_ACCEPT_READ(s, ns, ra, buf, l, t, fast, cb, cba)
+#define _MD_UPDATE_ACCEPT_CONTEXT _PR_MD_UPDATE_ACCEPT_CONTEXT
+
+#define _MD_BIND _PR_MD_BIND
+#define _MD_RECV _PR_MD_RECV
+#define _MD_SEND _PR_MD_SEND
+#define _MD_SENDFILE _PR_MD_SENDFILE
+#define _MD_PR_POLL _PR_MD_PR_POLL
+
+/* --- Scheduler stuff --- */
+#define _MD_PAUSE_CPU _PR_MD_PAUSE_CPU
+
+/* --- DIR stuff --- */
+#define PR_DIRECTORY_SEPARATOR '\\'
+#define PR_DIRECTORY_SEPARATOR_STR "\\"
+#define PR_PATH_SEPARATOR ';'
+#define PR_PATH_SEPARATOR_STR ";"
+#define _MD_ERRNO() GetLastError()
+#define _MD_OPEN_DIR _PR_MD_OPEN_DIR
+#define _MD_CLOSE_DIR _PR_MD_CLOSE_DIR
+#define _MD_READ_DIR _PR_MD_READ_DIR
+
+/* --- Segment stuff --- */
+#define _MD_INIT_SEGS()
+#define _MD_ALLOC_SEGMENT(seg, size, vaddr) 0
+#define _MD_FREE_SEGMENT(seg)
+
+/* --- Environment Stuff --- */
+#define _MD_GET_ENV _PR_MD_GET_ENV
+#define _MD_PUT_ENV _PR_MD_PUT_ENV
+
+/* --- Threading Stuff --- */
+#define _MD_DEFAULT_STACK_SIZE 0
+#define _MD_INIT_THREAD _PR_MD_INIT_THREAD
+#define _MD_INIT_ATTACHED_THREAD _PR_MD_INIT_THREAD
+#define _MD_CREATE_THREAD _PR_MD_CREATE_THREAD
+#define _MD_JOIN_THREAD _PR_MD_JOIN_THREAD
+#define _MD_END_THREAD _PR_MD_END_THREAD
+#define _MD_YIELD _PR_MD_YIELD
+#define _MD_SET_PRIORITY _PR_MD_SET_PRIORITY
+#define _MD_SET_CURRENT_THREAD_NAME _PR_MD_SET_CURRENT_THREAD_NAME
+#define _MD_CLEAN_THREAD _PR_MD_CLEAN_THREAD
+#define _MD_SETTHREADAFFINITYMASK _PR_MD_SETTHREADAFFINITYMASK
+#define _MD_GETTHREADAFFINITYMASK _PR_MD_GETTHREADAFFINITYMASK
+#define _MD_EXIT_THREAD _PR_MD_EXIT_THREAD
+#define _MD_SUSPEND_THREAD _PR_MD_SUSPEND_THREAD
+#define _MD_RESUME_THREAD _PR_MD_RESUME_THREAD
+#define _MD_SUSPEND_CPU _PR_MD_SUSPEND_CPU
+#define _MD_RESUME_CPU _PR_MD_RESUME_CPU
+#define _MD_BEGIN_SUSPEND_ALL()
+#define _MD_BEGIN_RESUME_ALL()
+#define _MD_END_SUSPEND_ALL()
+#define _MD_END_RESUME_ALL()
+
+extern void _PR_Unblock_IO_Wait(PRThread *thr);
+
+/* --- Lock stuff --- */
+#define _MD_NEW_LOCK(lock) (InitializeCriticalSection(&((lock)->mutex)),PR_SUCCESS)
+#define _MD_FREE_LOCK(lock) DeleteCriticalSection(&((lock)->mutex))
+#ifndef PROFILE_LOCKS
+#define _MD_LOCK(lock) EnterCriticalSection(&((lock)->mutex))
+#define _MD_TEST_AND_LOCK(lock) (TryEnterCriticalSection(&((lock)->mutex))== FALSE)
+#define _MD_UNLOCK(lock) LeaveCriticalSection(&((lock)->mutex))
+#else
+#define _MD_LOCK(lock) \
+ PR_BEGIN_MACRO \
+ BOOL rv = TryEnterCriticalSection(&((lock)->mutex)); \
+ if (rv == TRUE) { \
+ InterlockedIncrement(&((lock)->hitcount)); \
+ } else { \
+ InterlockedIncrement(&((lock)->misscount)); \
+ EnterCriticalSection(&((lock)->mutex)); \
+ } \
+ PR_END_MACRO
+#define _MD_TEST_AND_LOCK(lock) 0 /* XXXMB */
+#define _MD_UNLOCK(lock) LeaveCriticalSection(&((lock)->mutex))
+#endif
+#define _PR_LOCK _MD_LOCK
+#define _PR_UNLOCK _MD_UNLOCK
+
+/* --- lock and cv waiting --- */
+#define _MD_WAIT _PR_MD_WAIT
+#define _MD_WAKEUP_WAITER _PR_MD_WAKEUP_WAITER
+
+/* XXXMB- the IOQ stuff is certainly not working correctly yet. */
+extern struct _MDLock _pr_ioq_lock;
+#define _MD_IOQ_LOCK() _MD_LOCK(&_pr_ioq_lock)
+#define _MD_IOQ_UNLOCK() _MD_UNLOCK(&_pr_ioq_lock)
+
+
+/* --- Initialization stuff --- */
+#define _MD_START_INTERRUPTS()
+#define _MD_STOP_INTERRUPTS()
+#define _MD_DISABLE_CLOCK_INTERRUPTS()
+#define _MD_ENABLE_CLOCK_INTERRUPTS()
+#define _MD_BLOCK_CLOCK_INTERRUPTS()
+#define _MD_UNBLOCK_CLOCK_INTERRUPTS()
+#define _MD_EARLY_INIT _PR_MD_EARLY_INIT
+#define _MD_FINAL_INIT()
+#define _MD_EARLY_CLEANUP()
+#define _MD_INIT_CPUS()
+#define _MD_INIT_RUNNING_CPU(cpu)
+
+struct PRProcess;
+struct PRProcessAttr;
+
+/* --- Create a new process --- */
+#define _MD_CREATE_PROCESS _PR_CreateWindowsProcess
+extern struct PRProcess * _PR_CreateWindowsProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const struct PRProcessAttr *attr
+);
+
+#define _MD_DETACH_PROCESS _PR_DetachWindowsProcess
+extern PRStatus _PR_DetachWindowsProcess(struct PRProcess *process);
+
+/* --- Wait for a child process to terminate --- */
+#define _MD_WAIT_PROCESS _PR_WaitWindowsProcess
+extern PRStatus _PR_WaitWindowsProcess(struct PRProcess *process,
+ PRInt32 *exitCode);
+
+#define _MD_KILL_PROCESS _PR_KillWindowsProcess
+extern PRStatus _PR_KillWindowsProcess(struct PRProcess *process);
+
+/* --- User Threading stuff --- */
+#define HAVE_FIBERS
+#define _MD_CREATE_USER_THREAD _PR_MD_CREATE_USER_THREAD
+#define _MD_CREATE_PRIMORDIAL_USER_THREAD _PR_MD_CREATE_PRIMORDIAL_USER_THREAD
+#define _MD_CLEANUP_BEFORE_EXIT _PR_MD_CLEANUP_BEFORE_EXIT
+#define _MD_EXIT _PR_MD_EXIT
+#define _MD_INIT_CONTEXT _PR_MD_INIT_CONTEXT
+#define _MD_SWITCH_CONTEXT _PR_MD_SWITCH_CONTEXT
+#define _MD_RESTORE_CONTEXT _PR_MD_RESTORE_CONTEXT
+
+/* --- Intervals --- */
+#define _MD_INTERVAL_INIT _PR_MD_INTERVAL_INIT
+#define _MD_GET_INTERVAL _PR_MD_GET_INTERVAL
+#define _MD_INTERVAL_PER_SEC _PR_MD_INTERVAL_PER_SEC
+#define _MD_INTERVAL_PER_MILLISEC() (_PR_MD_INTERVAL_PER_SEC() / 1000)
+#define _MD_INTERVAL_PER_MICROSEC() (_PR_MD_INTERVAL_PER_SEC() / 1000000)
+
+/* --- Time --- */
+extern void _PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm);
+
+/* --- Native-Thread Specific Definitions ------------------------------- */
+
+extern BOOL _pr_use_static_tls;
+
+extern __declspec(thread) struct PRThread *_pr_current_fiber;
+extern DWORD _pr_currentFiberIndex;
+
+#define _MD_GET_ATTACHED_THREAD() \
+ (_pr_use_static_tls ? _pr_current_fiber \
+ : (PRThread *) TlsGetValue(_pr_currentFiberIndex))
+
+extern struct PRThread * _MD_CURRENT_THREAD(void);
+
+#define _MD_SET_CURRENT_THREAD(_thread) \
+ PR_BEGIN_MACRO \
+ if (_pr_use_static_tls) { \
+ _pr_current_fiber = (_thread); \
+ } else { \
+ TlsSetValue(_pr_currentFiberIndex, (_thread)); \
+ } \
+ PR_END_MACRO
+
+extern __declspec(thread) struct PRThread *_pr_fiber_last_run;
+extern DWORD _pr_lastFiberIndex;
+
+#define _MD_LAST_THREAD() \
+ (_pr_use_static_tls ? _pr_fiber_last_run \
+ : (PRThread *) TlsGetValue(_pr_lastFiberIndex))
+
+#define _MD_SET_LAST_THREAD(_thread) \
+ PR_BEGIN_MACRO \
+ if (_pr_use_static_tls) { \
+ _pr_fiber_last_run = (_thread); \
+ } else { \
+ TlsSetValue(_pr_lastFiberIndex, (_thread)); \
+ } \
+ PR_END_MACRO
+
+extern __declspec(thread) struct _PRCPU *_pr_current_cpu;
+extern DWORD _pr_currentCPUIndex;
+
+#define _MD_CURRENT_CPU() \
+ (_pr_use_static_tls ? _pr_current_cpu \
+ : (struct _PRCPU *) TlsGetValue(_pr_currentCPUIndex))
+
+#define _MD_SET_CURRENT_CPU(_cpu) \
+ PR_BEGIN_MACRO \
+ if (_pr_use_static_tls) { \
+ _pr_current_cpu = (_cpu); \
+ } else { \
+ TlsSetValue(_pr_currentCPUIndex, (_cpu)); \
+ } \
+ PR_END_MACRO
+
+extern __declspec(thread) PRUintn _pr_ints_off;
+extern DWORD _pr_intsOffIndex;
+
+#define _MD_GET_INTSOFF() \
+ (_pr_use_static_tls ? _pr_ints_off \
+ : (PRUintn) TlsGetValue(_pr_intsOffIndex))
+
+#define _MD_SET_INTSOFF(_val) \
+ PR_BEGIN_MACRO \
+ if (_pr_use_static_tls) { \
+ _pr_ints_off = (_val); \
+ } else { \
+ TlsSetValue(_pr_intsOffIndex, (LPVOID) (_val)); \
+ } \
+ PR_END_MACRO
+
+/* --- Initialization stuff --- */
+#define _MD_INIT_LOCKS()
+
+/* --- Stack stuff --- */
+#define _MD_INIT_STACK(stack, redzone)
+#define _MD_CLEAR_STACK(stack)
+
+/* --- Memory-mapped files stuff --- */
+
+struct _MDFileMap {
+ HANDLE hFileMap;
+ DWORD dwAccess;
+};
+
+extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size);
+#define _MD_CREATE_FILE_MAP _MD_CreateFileMap
+
+extern PRInt32 _MD_GetMemMapAlignment(void);
+#define _MD_GET_MEM_MAP_ALIGNMENT _MD_GetMemMapAlignment
+
+extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset,
+ PRUint32 len);
+#define _MD_MEM_MAP _MD_MemMap
+
+extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size);
+#define _MD_MEM_UNMAP _MD_MemUnmap
+
+extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap);
+#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap
+
+extern PRStatus _MD_SyncMemMap(
+ PRFileDesc *fd,
+ void *addr,
+ PRUint32 len);
+#define _MD_SYNC_MEM_MAP _MD_SyncMemMap
+
+/* --- Named semaphores stuff --- */
+#define _PR_HAVE_NAMED_SEMAPHORES
+#define _MD_OPEN_SEMAPHORE _PR_MD_OPEN_SEMAPHORE
+#define _MD_WAIT_SEMAPHORE _PR_MD_WAIT_SEMAPHORE
+#define _MD_POST_SEMAPHORE _PR_MD_POST_SEMAPHORE
+#define _MD_CLOSE_SEMAPHORE _PR_MD_CLOSE_SEMAPHORE
+#define _MD_DELETE_SEMAPHORE(name) PR_SUCCESS /* no op */
+
+#endif /* nspr_win32_defs_h___ */
diff --git a/nsprpub/pr/include/md/prosdep.h b/nsprpub/pr/include/md/prosdep.h
new file mode 100644
index 0000000000..42eba3da74
--- /dev/null
+++ b/nsprpub/pr/include/md/prosdep.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prosdep_h___
+#define prosdep_h___
+
+/*
+** Get OS specific header information
+*/
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+#ifdef XP_PC
+
+#include "md/_pcos.h"
+#ifdef WINNT
+#include "md/_winnt.h"
+#include "md/_win32_errors.h"
+#elif defined(WIN95) || defined(WINCE)
+#include "md/_win95.h"
+#include "md/_win32_errors.h"
+#elif defined(OS2)
+#include "md/_os2.h"
+#include "md/_os2_errors.h"
+#else
+#error unknown Windows platform
+#endif
+
+#elif defined(XP_UNIX)
+
+#if defined(AIX)
+#include "md/_aix.h"
+
+#elif defined(FREEBSD)
+#include "md/_freebsd.h"
+
+#elif defined(NETBSD)
+#include "md/_netbsd.h"
+
+#elif defined(OPENBSD)
+#include "md/_openbsd.h"
+
+#elif defined(BSDI)
+#include "md/_bsdi.h"
+
+#elif defined(HPUX)
+#include "md/_hpux.h"
+
+#elif defined(LINUX) || defined(__GNU__) || defined(__GLIBC__)
+#include "md/_linux.h"
+
+#elif defined(DARWIN)
+#include "md/_darwin.h"
+
+#elif defined(SOLARIS)
+#include "md/_solaris.h"
+
+#elif defined(SCO)
+#include "md/_scoos.h"
+
+#elif defined(UNIXWARE)
+#include "md/_unixware.h"
+
+#elif defined(QNX)
+#include "md/_qnx.h"
+
+#elif defined(NTO)
+#include "md/_nto.h"
+
+#elif defined(RISCOS)
+#include "md/_riscos.h"
+
+#else
+#error unknown Unix flavor
+
+#endif
+
+#include "md/_unixos.h"
+#include "md/_unix_errors.h"
+
+#else
+
+#error "The platform is not Unix, Windows, or Mac"
+
+#endif
+
+#ifdef _PR_PTHREADS
+#include "md/_pth.h"
+#endif
+
+PR_END_EXTERN_C
+
+#endif /* prosdep_h___ */
diff --git a/nsprpub/pr/include/nspr.h b/nsprpub/pr/include/nspr.h
new file mode 100644
index 0000000000..0cbc71c10d
--- /dev/null
+++ b/nsprpub/pr/include/nspr.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nspr_h___
+#define nspr_h___
+
+#include "pratom.h"
+#include "prbit.h"
+#include "prclist.h"
+#include "prcmon.h"
+#include "prcvar.h"
+#include "prdtoa.h"
+#include "prenv.h"
+#include "prerror.h"
+#include "prinet.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prio.h"
+#include "pripcsem.h"
+#include "prlink.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prlong.h"
+#include "prmem.h"
+#include "prmon.h"
+#include "prmwait.h"
+#include "prnetdb.h"
+#include "prprf.h"
+#include "prproces.h"
+#include "prrng.h"
+#include "prrwlock.h"
+#include "prshm.h"
+#include "prshma.h"
+#include "prsystem.h"
+#include "prthread.h"
+#include "prtime.h"
+#include "prtpool.h"
+#include "prtrace.h"
+#include "prtypes.h"
+
+#endif /* nspr_h___ */
diff --git a/nsprpub/pr/include/obsolete/Makefile.in b/nsprpub/pr/include/obsolete/Makefile.in
new file mode 100644
index 0000000000..27116a2c7a
--- /dev/null
+++ b/nsprpub/pr/include/obsolete/Makefile.in
@@ -0,0 +1,28 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+HEADERS = $(wildcard $(srcdir)/*.h)
+
+RELEASE_HEADERS = $(HEADERS)
+RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR)/obsolete
+
+include_subdir = obsolete
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(RELEASE_HEADERS)
+ $(INSTALL) -m 444 $(RELEASE_HEADERS) $(dist_includedir)/obsolete
diff --git a/nsprpub/pr/include/obsolete/pralarm.h b/nsprpub/pr/include/obsolete/pralarm.h
new file mode 100644
index 0000000000..9ae3a4bcf0
--- /dev/null
+++ b/nsprpub/pr/include/obsolete/pralarm.h
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: pralarm.h
+** Description: API to periodic alarms.
+**
+**
+** Alarms are defined to invoke some client specified function at
+** a time in the future. The notification may be a one time event
+** or repeated at a fixed interval. The interval at which the next
+** notification takes place may be modified by the client code only
+** during the respective notification.
+**
+** The notification is delivered on a thread that is part of the
+** alarm context (PRAlarm). The thread will inherit the priority
+** of the Alarm creator.
+**
+** Any number of periodic alarms (PRAlarmID) may be created within
+** the context of a single alarm (PRAlarm). The notifications will be
+** scheduled as close to the desired time as possible.
+**
+** Repeating periodic notifies are expected to run at a fixed rate.
+** That rate is expressed as some number of notifies per period where
+** the period is much larger than a PRIntervalTime (see prinrval.h).
+*/
+
+#if !defined(pralarm_h)
+#define pralarm_h
+
+#include "prtypes.h"
+#include "prinrval.h"
+
+
+PR_BEGIN_EXTERN_C
+
+/**********************************************************************/
+/************************* TYPES AND CONSTANTS ************************/
+/**********************************************************************/
+
+typedef struct PRAlarm PRAlarm;
+typedef struct PRAlarmID PRAlarmID;
+
+typedef PRBool (PR_CALLBACK *PRPeriodicAlarmFn)(
+ PRAlarmID *id, void *clientData, PRUint32 late);
+
+/**********************************************************************/
+/****************************** FUNCTIONS *****************************/
+/**********************************************************************/
+
+/***********************************************************************
+** FUNCTION: PR_CreateAlarm
+** DESCRIPTION:
+** Create an alarm context.
+** INPUTS: void
+** OUTPUTS: None
+** RETURN: PRAlarm*
+**
+** SIDE EFFECTS:
+** This creates an alarm context, which is an object used for subsequent
+** notification creations. It also creates a thread that will be used to
+** deliver the notifications that are expected to be defined. The client
+** is resposible for destroying the context when appropriate.
+** RESTRICTIONS:
+** None.
+** MEMORY: The object (PRAlarm) and a thread to support notifications.
+** ALGORITHM: N/A
+***********************************************************************/
+NSPR_API(PRAlarm*) PR_CreateAlarm(void);
+
+/***********************************************************************
+** FUNCTION: PR_DestroyAlarm
+** DESCRIPTION:
+** Destroys the context created by PR_CreateAlarm().
+** INPUTS: PRAlarm*
+** OUTPUTS: None
+** RETURN: PRStatus
+**
+** SIDE EFFECTS:
+** This destroys the context that was created by PR_CreateAlarm().
+** If there are any active alarms (PRAlarmID), they will be cancelled.
+** Once that is done, the thread that was used to deliver the alarms
+** will be joined.
+** RESTRICTIONS:
+** None.
+** MEMORY: N/A
+** ALGORITHM: N/A
+***********************************************************************/
+NSPR_API(PRStatus) PR_DestroyAlarm(PRAlarm *alarm);
+
+/***********************************************************************
+** FUNCTION: PR_SetAlarm
+** DESCRIPTION:
+** Creates a periodic notifier that is to be delivered to a specified
+** function at some fixed interval.
+** INPUTS: PRAlarm *alarm Parent alarm context
+** PRIntervalTime period Interval over which the notifies
+** are delivered.
+** PRUint32 rate The rate within the interval that
+** the notifies will be delivered.
+** PRPeriodicAlarmFn function Entry point where the notifies
+** will be delivered.
+** OUTPUTS: None
+** RETURN: PRAlarmID* Handle to the notifier just created
+** or NULL if the request failed.
+**
+** SIDE EFFECTS:
+** A periodic notifier is created. The notifications will be delivered
+** by the alarm's internal thread at a fixed interval whose rate is the
+** number of interrupts per interval specified. The first notification
+** will be delivered as soon as possible, and they will continue until
+** the notifier routine indicates that they should cease of the alarm
+** context is destroyed (PR_DestroyAlarm).
+** RESTRICTIONS:
+** None.
+** MEMORY: Memory for the notifier object.
+** ALGORITHM: The rate at which notifications are delivered are stated
+** to be "'rate' notifies per 'interval'". The exact time of
+** the notification is computed based on a epoch established
+** when the notifier was set. Each notification is delivered
+** not ealier than the epoch plus the fixed rate times the
+** notification sequence number. Such notifications have the
+** potential to be late by not more than 'interval'/'rate'.
+** The amount of lateness of one notification is taken into
+** account on the next in an attempt to avoid long term slew.
+***********************************************************************/
+NSPR_API(PRAlarmID*) PR_SetAlarm(
+ PRAlarm *alarm, PRIntervalTime period, PRUint32 rate,
+ PRPeriodicAlarmFn function, void *clientData);
+
+/***********************************************************************
+** FUNCTION: PR_ResetAlarm
+** DESCRIPTION:
+** Resets an existing alarm.
+** INPUTS: PRAlarmID *id Identify of the notifier.
+** PRIntervalTime period Interval over which the notifies
+** are delivered.
+** PRUint32 rate The rate within the interval that
+** the notifies will be delivered.
+** OUTPUTS: None
+** RETURN: PRStatus Indication of completion.
+**
+** SIDE EFFECTS:
+** An existing alarm may have its period and rate redefined. The
+** additional side effect is that the notifier's epoch is recomputed.
+** The first notification delivered by the newly refreshed alarm is
+** defined to be 'interval'/'rate' from the time of the reset.
+** RESTRICTIONS:
+** This function may only be called in the notifier for that alarm.
+** MEMORY: N/A.
+** ALGORITHM: See PR_SetAlarm().
+***********************************************************************/
+NSPR_API(PRStatus) PR_ResetAlarm(
+ PRAlarmID *id, PRIntervalTime period, PRUint32 rate);
+
+PR_END_EXTERN_C
+
+#endif /* !defined(pralarm_h) */
+
+/* prinrval.h */
diff --git a/nsprpub/pr/include/obsolete/probslet.h b/nsprpub/pr/include/obsolete/probslet.h
new file mode 100644
index 0000000000..db743c5ed1
--- /dev/null
+++ b/nsprpub/pr/include/obsolete/probslet.h
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** A collection of things thought to be obsolete
+*/
+
+#if defined(PROBSLET_H)
+#else
+#define PROBSLET_H
+
+#include "prio.h"
+#include "private/pprio.h" /* for PROsfd */
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Yield the current thread. The proper function to use in place of
+** PR_Yield() is PR_Sleep() with an argument of PR_INTERVAL_NO_WAIT.
+*/
+NSPR_API(PRStatus) PR_Yield(void);
+
+/************************************************************************/
+/************* The following definitions are for select *****************/
+/************************************************************************/
+
+/*
+** The following is obsolete and will be deleted in the next release!
+** These are provided for compatibility, but are GUARANTEED to be slow.
+**
+** Override PR_MAX_SELECT_DESC if you need more space in the select set.
+*/
+#ifndef PR_MAX_SELECT_DESC
+#define PR_MAX_SELECT_DESC 1024
+#endif
+typedef struct PR_fd_set {
+ PRUint32 hsize;
+ PRFileDesc *harray[PR_MAX_SELECT_DESC];
+ PRUint32 nsize;
+ PROsfd narray[PR_MAX_SELECT_DESC];
+} PR_fd_set;
+
+/*
+*************************************************************************
+** FUNCTION: PR_Select
+** DESCRIPTION:
+**
+** The call returns as soon as I/O is ready on one or more of the underlying
+** file/socket descriptors or an exceptional condition is pending. A count of the
+** number of ready descriptors is returned unless a timeout occurs in which case
+** zero is returned. On return, PR_Select replaces the given descriptor sets with
+** subsets consisting of those descriptors that are ready for the requested condition.
+** The total number of ready descriptors in all the sets is the return value.
+**
+** INPUTS:
+** PRInt32 num
+** This argument is unused but is provided for select(unix) interface
+** compatability. All input PR_fd_set arguments are self-describing
+** with its own maximum number of elements in the set.
+**
+** PR_fd_set *readfds
+** A set describing the io descriptors for which ready for reading
+** condition is of interest.
+**
+** PR_fd_set *writefds
+** A set describing the io descriptors for which ready for writing
+** condition is of interest.
+**
+** PR_fd_set *exceptfds
+** A set describing the io descriptors for which exception pending
+** condition is of interest.
+**
+** Any of the above readfds, writefds or exceptfds may be given as NULL
+** pointers if no descriptors are of interest for that particular condition.
+**
+** PRIntervalTime timeout
+** Amount of time the call will block waiting for I/O to become ready.
+** If this time expires without any I/O becoming ready, the result will
+** be zero.
+**
+** OUTPUTS:
+** PR_fd_set *readfds
+** A set describing the io descriptors which are ready for reading.
+**
+** PR_fd_set *writefds
+** A set describing the io descriptors which are ready for writing.
+**
+** PR_fd_set *exceptfds
+** A set describing the io descriptors which have pending exception.
+**
+** RETURN:PRInt32
+** Number of io descriptors with asked for conditions or zero if the function
+** timed out or -1 on failure. The reason for the failure is obtained by
+** calling PR_GetError().
+** XXX can we implement this on windoze and mac?
+**************************************************************************
+*/
+NSPR_API(PRInt32) PR_Select(
+ PRInt32 num, PR_fd_set *readfds, PR_fd_set *writefds,
+ PR_fd_set *exceptfds, PRIntervalTime timeout);
+
+/*
+** The following are not thread safe for two threads operating on them at the
+** same time.
+**
+** The following routines are provided for manipulating io descriptor sets.
+** PR_FD_ZERO(&fdset) initializes a descriptor set fdset to the null set.
+** PR_FD_SET(fd, &fdset) includes a particular file descriptor fd in fdset.
+** PR_FD_CLR(fd, &fdset) removes a file descriptor fd from fdset.
+** PR_FD_ISSET(fd, &fdset) is nonzero if file descriptor fd is a member of
+** fdset, zero otherwise.
+**
+** PR_FD_NSET(osfd, &fdset) includes a particular native file descriptor osfd
+** in fdset.
+** PR_FD_NCLR(osfd, &fdset) removes a native file descriptor osfd from fdset.
+** PR_FD_NISSET(osfd, &fdset) is nonzero if native file descriptor osfd is a member of
+** fdset, zero otherwise.
+*/
+
+NSPR_API(void) PR_FD_ZERO(PR_fd_set *set);
+NSPR_API(void) PR_FD_SET(PRFileDesc *fd, PR_fd_set *set);
+NSPR_API(void) PR_FD_CLR(PRFileDesc *fd, PR_fd_set *set);
+NSPR_API(PRInt32) PR_FD_ISSET(PRFileDesc *fd, PR_fd_set *set);
+NSPR_API(void) PR_FD_NSET(PROsfd osfd, PR_fd_set *set);
+NSPR_API(void) PR_FD_NCLR(PROsfd osfd, PR_fd_set *set);
+NSPR_API(PRInt32) PR_FD_NISSET(PROsfd osfd, PR_fd_set *set);
+
+/*
+** The next two entry points should not be in the API, but they are
+** declared here for historical reasons.
+*/
+
+NSPR_API(PRInt32) PR_GetSysfdTableMax(void);
+
+NSPR_API(PRInt32) PR_SetSysfdTableSize(PRIntn table_size);
+
+#ifndef NO_NSPR_10_SUPPORT
+#include <sys/stat.h>
+
+NSPR_API(PRInt32) PR_Stat(const char *path, struct stat *buf);
+#endif /* NO_NSPR_10_SUPPORT */
+
+PR_END_EXTERN_C
+
+#endif /* defined(PROBSLET_H) */
+
+/* probslet.h */
diff --git a/nsprpub/pr/include/obsolete/protypes.h b/nsprpub/pr/include/obsolete/protypes.h
new file mode 100644
index 0000000000..5400afb234
--- /dev/null
+++ b/nsprpub/pr/include/obsolete/protypes.h
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This header typedefs the old 'native' types to the new PR<type>s.
+ * These definitions are scheduled to be eliminated at the earliest
+ * possible time. The NSPR API is implemented and documented using
+ * the new definitions.
+ */
+
+#if !defined(PROTYPES_H)
+#define PROTYPES_H
+
+typedef PRUintn uintn;
+#ifndef _XP_Core_
+typedef PRIntn intn;
+#endif
+
+/*
+ * It is trickier to define uint, int8, uint8, int16, uint16,
+ * int32, uint32, int64, and uint64 because some of these int
+ * types are defined by standard header files on some platforms.
+ * Our strategy here is to include all such standard headers
+ * first, and then define these int types only if they are not
+ * defined by those standard headers.
+ */
+
+/*
+ * SVR4 typedef of uint is commonly found on UNIX machines.
+ *
+ * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h)
+ * defines the types int8, int16, int32, and int64.
+ *
+ * On OS/2, sys/types.h defines uint.
+ */
+#if defined(XP_UNIX) || defined(XP_OS2)
+#include <sys/types.h>
+#endif
+
+/* model.h on HP-UX defines int8, int16, and int32. */
+#ifdef HPUX
+#include <model.h>
+#endif
+
+/*
+ * uint
+ */
+
+#if !defined(XP_OS2) && !defined(XP_UNIX) || defined(NTO)
+typedef PRUintn uint;
+#endif
+
+/*
+ * uint64
+ */
+
+typedef PRUint64 uint64;
+
+/*
+ * uint32
+ */
+
+#if !defined(_WIN32) && !defined(XP_OS2) && !defined(NTO)
+typedef PRUint32 uint32;
+#else
+typedef unsigned long uint32;
+#endif
+
+/*
+ * uint16
+ */
+
+typedef PRUint16 uint16;
+
+/*
+ * uint8
+ */
+
+typedef PRUint8 uint8;
+
+/*
+ * int64
+ */
+
+#if !defined(_PR_AIX_HAVE_BSD_INT_TYPES)
+typedef PRInt64 int64;
+#endif
+
+/*
+ * int32
+ */
+
+#if !defined(_PR_AIX_HAVE_BSD_INT_TYPES) \
+ && !defined(HPUX)
+#if !defined(_WIN32) && !defined(XP_OS2) && !defined(NTO)
+typedef PRInt32 int32;
+#else
+typedef long int32;
+#endif
+#endif
+
+/*
+ * int16
+ */
+
+#if !defined(_PR_AIX_HAVE_BSD_INT_TYPES) \
+ && !defined(HPUX)
+typedef PRInt16 int16;
+#endif
+
+/*
+ * int8
+ */
+
+#if !defined(_PR_AIX_HAVE_BSD_INT_TYPES) \
+ && !defined(HPUX)
+typedef PRInt8 int8;
+#endif
+
+typedef PRFloat64 float64;
+typedef PRUptrdiff uptrdiff_t;
+typedef PRUword uprword_t;
+typedef PRWord prword_t;
+
+
+/* Re: prbit.h */
+#define TEST_BIT PR_TEST_BIT
+#define SET_BIT PR_SET_BIT
+#define CLEAR_BIT PR_CLEAR_BIT
+
+/* Re: prarena.h->plarena.h */
+#define PRArena PLArena
+#define PRArenaPool PLArenaPool
+#define PRArenaStats PLArenaStats
+#define PR_ARENA_ALIGN PL_ARENA_ALIGN
+#define PR_INIT_ARENA_POOL PL_INIT_ARENA_POOL
+#define PR_ARENA_ALLOCATE PL_ARENA_ALLOCATE
+#define PR_ARENA_GROW PL_ARENA_GROW
+#define PR_ARENA_MARK PL_ARENA_MARK
+#define PR_CLEAR_UNUSED PL_CLEAR_UNUSED
+#define PR_CLEAR_ARENA PL_CLEAR_ARENA
+#define PR_ARENA_RELEASE PL_ARENA_RELEASE
+#define PR_COUNT_ARENA PL_COUNT_ARENA
+#define PR_ARENA_DESTROY PL_ARENA_DESTROY
+#define PR_InitArenaPool PL_InitArenaPool
+#define PR_FreeArenaPool PL_FreeArenaPool
+#define PR_FinishArenaPool PL_FinishArenaPool
+#define PR_CompactArenaPool PL_CompactArenaPool
+#define PR_ArenaFinish PL_ArenaFinish
+#define PR_ArenaAllocate PL_ArenaAllocate
+#define PR_ArenaGrow PL_ArenaGrow
+#define PR_ArenaRelease PL_ArenaRelease
+#define PR_ArenaCountAllocation PL_ArenaCountAllocation
+#define PR_ArenaCountInplaceGrowth PL_ArenaCountInplaceGrowth
+#define PR_ArenaCountGrowth PL_ArenaCountGrowth
+#define PR_ArenaCountRelease PL_ArenaCountRelease
+#define PR_ArenaCountRetract PL_ArenaCountRetract
+
+/* Re: prhash.h->plhash.h */
+#define PRHashEntry PLHashEntry
+#define PRHashTable PLHashTable
+#define PRHashNumber PLHashNumber
+#define PRHashFunction PLHashFunction
+#define PRHashComparator PLHashComparator
+#define PRHashEnumerator PLHashEnumerator
+#define PRHashAllocOps PLHashAllocOps
+#define PR_NewHashTable PL_NewHashTable
+#define PR_HashTableDestroy PL_HashTableDestroy
+#define PR_HashTableRawLookup PL_HashTableRawLookup
+#define PR_HashTableRawAdd PL_HashTableRawAdd
+#define PR_HashTableRawRemove PL_HashTableRawRemove
+#define PR_HashTableAdd PL_HashTableAdd
+#define PR_HashTableRemove PL_HashTableRemove
+#define PR_HashTableEnumerateEntries PL_HashTableEnumerateEntries
+#define PR_HashTableLookup PL_HashTableLookup
+#define PR_HashTableDump PL_HashTableDump
+#define PR_HashString PL_HashString
+#define PR_CompareStrings PL_CompareStrings
+#define PR_CompareValues PL_CompareValues
+
+#endif /* !defined(PROTYPES_H) */
diff --git a/nsprpub/pr/include/obsolete/prsem.h b/nsprpub/pr/include/obsolete/prsem.h
new file mode 100644
index 0000000000..d1981d2b9b
--- /dev/null
+++ b/nsprpub/pr/include/obsolete/prsem.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prsem_h___
+#define prsem_h___
+
+/*
+** API for counting semaphores. Semaphores are counting synchronizing
+** variables based on a lock and a condition variable. They are lightweight
+** contention control for a given count of resources.
+*/
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PRSemaphore PRSemaphore;
+
+/*
+** Create a new semaphore object.
+*/
+NSPR_API(PRSemaphore*) PR_NewSem(PRUintn value);
+
+/*
+** Destroy the given semaphore object.
+**
+*/
+NSPR_API(void) PR_DestroySem(PRSemaphore *sem);
+
+/*
+** Wait on a Semaphore.
+**
+** This routine allows a calling thread to wait or proceed depending upon the
+** state of the semahore sem. The thread can proceed only if the counter value
+** of the semaphore sem is currently greater than 0. If the value of semaphore
+** sem is positive, it is decremented by one and the routine returns immediately
+** allowing the calling thread to continue. If the value of semaphore sem is 0,
+** the calling thread blocks awaiting the semaphore to be released by another
+** thread.
+**
+** This routine can return PR_PENDING_INTERRUPT if the waiting thread
+** has been interrupted.
+*/
+NSPR_API(PRStatus) PR_WaitSem(PRSemaphore *sem);
+
+/*
+** This routine increments the counter value of the semaphore. If other threads
+** are blocked for the semaphore, then the scheduler will determine which ONE
+** thread will be unblocked.
+*/
+NSPR_API(void) PR_PostSem(PRSemaphore *sem);
+
+/*
+** Returns the value of the semaphore referenced by sem without affecting
+** the state of the semaphore. The value represents the semaphore vaule
+F** at the time of the call, but may not be the actual value when the
+** caller inspects it.
+*/
+NSPR_API(PRUintn) PR_GetValueSem(PRSemaphore *sem);
+
+PR_END_EXTERN_C
+
+#endif /* prsem_h___ */
diff --git a/nsprpub/pr/include/pratom.h b/nsprpub/pr/include/pratom.h
new file mode 100644
index 0000000000..11a8aaedc9
--- /dev/null
+++ b/nsprpub/pr/include/pratom.h
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* GLOBAL FUNCTIONS:
+** DESCRIPTION:
+** PR Atomic operations
+*/
+
+#ifndef pratom_h___
+#define pratom_h___
+
+#include "prtypes.h"
+#include "prlock.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** FUNCTION: PR_AtomicIncrement
+** DESCRIPTION:
+** Atomically increment a 32 bit value.
+** INPUTS:
+** val: a pointer to the value to increment
+** RETURN:
+** the returned value is the result of the increment
+*/
+NSPR_API(PRInt32) PR_AtomicIncrement(PRInt32 *val);
+
+/*
+** FUNCTION: PR_AtomicDecrement
+** DESCRIPTION:
+** Atomically decrement a 32 bit value.
+** INPUTS:
+** val: a pointer to the value to decrement
+** RETURN:
+** the returned value is the result of the decrement
+*/
+NSPR_API(PRInt32) PR_AtomicDecrement(PRInt32 *val);
+
+/*
+** FUNCTION: PR_AtomicSet
+** DESCRIPTION:
+** Atomically set a 32 bit value.
+** INPUTS:
+** val: A pointer to a 32 bit value to be set
+** newval: The newvalue to assign to val
+** RETURN:
+** Returns the prior value
+*/
+NSPR_API(PRInt32) PR_AtomicSet(PRInt32 *val, PRInt32 newval);
+
+/*
+** FUNCTION: PR_AtomicAdd
+** DESCRIPTION:
+** Atomically add a 32 bit value.
+** INPUTS:
+** ptr: a pointer to the value to increment
+** val: value to be added
+** RETURN:
+** the returned value is the result of the addition
+*/
+NSPR_API(PRInt32) PR_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+
+/*
+** MACRO: PR_ATOMIC_INCREMENT
+** MACRO: PR_ATOMIC_DECREMENT
+** MACRO: PR_ATOMIC_SET
+** MACRO: PR_ATOMIC_ADD
+** DESCRIPTION:
+** Macro versions of the atomic operations. They may be implemented
+** as compiler intrinsics.
+**
+** IMPORTANT NOTE TO NSPR MAINTAINERS:
+** Implement these macros with compiler intrinsics only on platforms
+** where the PR_AtomicXXX functions are truly atomic (i.e., where the
+** configuration macro _PR_HAVE_ATOMIC_OPS is defined). Otherwise,
+** the macros and functions won't be compatible and can't be used
+** interchangeably.
+*/
+#if defined(_WIN32) && !defined(_WIN32_WCE) && \
+ (!defined(_MSC_VER) || (_MSC_VER >= 1310))
+
+#include <intrin.h>
+
+#ifdef _MSC_VER
+#pragma intrinsic(_InterlockedIncrement)
+#pragma intrinsic(_InterlockedDecrement)
+#pragma intrinsic(_InterlockedExchange)
+#pragma intrinsic(_InterlockedExchangeAdd)
+#endif
+
+#define PR_ATOMIC_INCREMENT(val) _InterlockedIncrement((long volatile *)(val))
+#define PR_ATOMIC_DECREMENT(val) _InterlockedDecrement((long volatile *)(val))
+#define PR_ATOMIC_SET(val, newval) \
+ _InterlockedExchange((long volatile *)(val), (long)(newval))
+#define PR_ATOMIC_ADD(ptr, val) \
+ (_InterlockedExchangeAdd((long volatile *)(ptr), (long)(val)) + (val))
+
+#elif ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && \
+ ((defined(__APPLE__) && \
+ (defined(__ppc__) || defined(__i386__) || defined(__x86_64__))) || \
+ (defined(__linux__) && \
+ ((defined(__i386__) && \
+ defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) || \
+ defined(__ia64__) || defined(__x86_64__) || \
+ defined(__powerpc__) || \
+ (defined(__arm__) && \
+ defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) || \
+ defined(__aarch64__) || defined(__alpha) || \
+ (defined(__mips__) && \
+ defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)))))
+
+/*
+ * Because the GCC manual warns that some processors may support
+ * reduced functionality of __sync_lock_test_and_set, we test for the
+ * processors that we believe support a full atomic exchange operation.
+ */
+
+#define PR_ATOMIC_INCREMENT(val) __sync_add_and_fetch(val, 1)
+#define PR_ATOMIC_DECREMENT(val) __sync_sub_and_fetch(val, 1)
+#define PR_ATOMIC_SET(val, newval) __sync_lock_test_and_set(val, newval)
+#define PR_ATOMIC_ADD(ptr, val) __sync_add_and_fetch(ptr, val)
+
+#else
+
+#define PR_ATOMIC_INCREMENT(val) PR_AtomicIncrement(val)
+#define PR_ATOMIC_DECREMENT(val) PR_AtomicDecrement(val)
+#define PR_ATOMIC_SET(val, newval) PR_AtomicSet(val, newval)
+#define PR_ATOMIC_ADD(ptr, val) PR_AtomicAdd(ptr, val)
+
+#endif
+
+/*
+** LIFO linked-list (stack)
+*/
+typedef struct PRStackElemStr PRStackElem;
+
+struct PRStackElemStr {
+ PRStackElem *prstk_elem_next; /* next pointer MUST be at offset 0;
+ assembly language code relies on this */
+};
+
+typedef struct PRStackStr PRStack;
+
+/*
+** FUNCTION: PR_CreateStack
+** DESCRIPTION:
+** Create a stack, a LIFO linked list
+** INPUTS:
+** stack_name: a pointer to string containing the name of the stack
+** RETURN:
+** A pointer to the created stack, if successful, else NULL.
+*/
+NSPR_API(PRStack *) PR_CreateStack(const char *stack_name);
+
+/*
+** FUNCTION: PR_StackPush
+** DESCRIPTION:
+** Push an element on the top of the stack
+** INPUTS:
+** stack: pointer to the stack
+** stack_elem: pointer to the stack element
+** RETURN:
+** None
+*/
+NSPR_API(void) PR_StackPush(PRStack *stack, PRStackElem *stack_elem);
+
+/*
+** FUNCTION: PR_StackPop
+** DESCRIPTION:
+** Remove the element on the top of the stack
+** INPUTS:
+** stack: pointer to the stack
+** RETURN:
+** A pointer to the stack element removed from the top of the stack,
+** if non-empty,
+** else NULL
+*/
+NSPR_API(PRStackElem *) PR_StackPop(PRStack *stack);
+
+/*
+** FUNCTION: PR_DestroyStack
+** DESCRIPTION:
+** Destroy the stack
+** INPUTS:
+** stack: pointer to the stack
+** RETURN:
+** PR_SUCCESS - if successfully deleted
+** PR_FAILURE - if the stack is not empty
+** PR_GetError will return
+** PR_INVALID_STATE_ERROR - stack is not empty
+*/
+NSPR_API(PRStatus) PR_DestroyStack(PRStack *stack);
+
+PR_END_EXTERN_C
+
+#endif /* pratom_h___ */
diff --git a/nsprpub/pr/include/prbit.h b/nsprpub/pr/include/prbit.h
new file mode 100644
index 0000000000..50c65923af
--- /dev/null
+++ b/nsprpub/pr/include/prbit.h
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prbit_h___
+#define prbit_h___
+
+#include "prtypes.h"
+PR_BEGIN_EXTERN_C
+
+/*
+** Replace compare/jump/add/shift sequence with compiler built-in/intrinsic
+** functions.
+*/
+#if defined(_WIN32) && (_MSC_VER >= 1300) && \
+ (defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) || \
+ defined(_M_ARM64))
+# include <intrin.h>
+# pragma intrinsic(_BitScanForward,_BitScanReverse)
+__forceinline static int __prBitScanForward32(unsigned int val)
+{
+ unsigned long idx;
+ _BitScanForward(&idx, (unsigned long)val);
+ return( (int)idx );
+}
+__forceinline static int __prBitScanReverse32(unsigned int val)
+{
+ unsigned long idx;
+ _BitScanReverse(&idx, (unsigned long)val);
+ return( (int)(31-idx) );
+}
+# define pr_bitscan_ctz32(val) __prBitScanForward32(val)
+# define pr_bitscan_clz32(val) __prBitScanReverse32(val)
+# define PR_HAVE_BUILTIN_BITSCAN32
+#elif ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && \
+ (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \
+ defined(__aarch64__))
+# define pr_bitscan_ctz32(val) __builtin_ctz(val)
+# define pr_bitscan_clz32(val) __builtin_clz(val)
+# define PR_HAVE_BUILTIN_BITSCAN32
+#endif /* MSVC || GCC */
+
+/*
+** A prbitmap_t is a long integer that can be used for bitmaps
+*/
+typedef unsigned long prbitmap_t;
+
+#define PR_TEST_BIT(_map,_bit) \
+ ((_map)[(_bit)>>PR_BITS_PER_LONG_LOG2] & (1L << ((_bit) & (PR_BITS_PER_LONG-1))))
+#define PR_SET_BIT(_map,_bit) \
+ ((_map)[(_bit)>>PR_BITS_PER_LONG_LOG2] |= (1L << ((_bit) & (PR_BITS_PER_LONG-1))))
+#define PR_CLEAR_BIT(_map,_bit) \
+ ((_map)[(_bit)>>PR_BITS_PER_LONG_LOG2] &= ~(1L << ((_bit) & (PR_BITS_PER_LONG-1))))
+
+/*
+** Compute the log of the least power of 2 greater than or equal to n
+*/
+NSPR_API(PRIntn) PR_CeilingLog2(PRUint32 i);
+
+/*
+** Compute the log of the greatest power of 2 less than or equal to n
+*/
+NSPR_API(PRIntn) PR_FloorLog2(PRUint32 i);
+
+/*
+** Macro version of PR_CeilingLog2: Compute the log of the least power of
+** 2 greater than or equal to _n. The result is returned in _log2.
+*/
+#ifdef PR_HAVE_BUILTIN_BITSCAN32
+#define PR_CEILING_LOG2(_log2,_n) \
+ PR_BEGIN_MACRO \
+ PRUint32 j_ = (PRUint32)(_n); \
+ (_log2) = (j_ <= 1 ? 0 : 32 - pr_bitscan_clz32(j_ - 1)); \
+ PR_END_MACRO
+#else
+#define PR_CEILING_LOG2(_log2,_n) \
+ PR_BEGIN_MACRO \
+ PRUint32 j_ = (PRUint32)(_n); \
+ (_log2) = 0; \
+ if ((j_) & ((j_)-1)) \
+ (_log2) += 1; \
+ if ((j_) >> 16) \
+ (_log2) += 16, (j_) >>= 16; \
+ if ((j_) >> 8) \
+ (_log2) += 8, (j_) >>= 8; \
+ if ((j_) >> 4) \
+ (_log2) += 4, (j_) >>= 4; \
+ if ((j_) >> 2) \
+ (_log2) += 2, (j_) >>= 2; \
+ if ((j_) >> 1) \
+ (_log2) += 1; \
+ PR_END_MACRO
+#endif /* PR_HAVE_BUILTIN_BITSCAN32 */
+
+/*
+** Macro version of PR_FloorLog2: Compute the log of the greatest power of
+** 2 less than or equal to _n. The result is returned in _log2.
+**
+** This is equivalent to finding the highest set bit in the word.
+*/
+#ifdef PR_HAVE_BUILTIN_BITSCAN32
+#define PR_FLOOR_LOG2(_log2,_n) \
+ PR_BEGIN_MACRO \
+ PRUint32 j_ = (PRUint32)(_n); \
+ (_log2) = 31 - pr_bitscan_clz32((j_) | 1); \
+ PR_END_MACRO
+#else
+#define PR_FLOOR_LOG2(_log2,_n) \
+ PR_BEGIN_MACRO \
+ PRUint32 j_ = (PRUint32)(_n); \
+ (_log2) = 0; \
+ if ((j_) >> 16) \
+ (_log2) += 16, (j_) >>= 16; \
+ if ((j_) >> 8) \
+ (_log2) += 8, (j_) >>= 8; \
+ if ((j_) >> 4) \
+ (_log2) += 4, (j_) >>= 4; \
+ if ((j_) >> 2) \
+ (_log2) += 2, (j_) >>= 2; \
+ if ((j_) >> 1) \
+ (_log2) += 1; \
+ PR_END_MACRO
+#endif /* PR_HAVE_BUILTIN_BITSCAN32 */
+
+/*
+** Macros for rotate left and right. The argument 'a' must be an unsigned
+** 32-bit integer type such as PRUint32.
+**
+** There is no rotate operation in the C Language, so the construct
+** (a << 4) | (a >> 28) is frequently used instead. Most compilers convert
+** this to a rotate instruction, but MSVC doesn't without a little help.
+** To get MSVC to generate a rotate instruction, we have to use the _rotl
+** or _rotr intrinsic and use a pragma to make it inline.
+**
+** Note: MSVC in VS2005 will do an inline rotate instruction on the above
+** construct.
+*/
+
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || \
+ defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64))
+#include <stdlib.h>
+#pragma intrinsic(_rotl, _rotr)
+#define PR_ROTATE_LEFT32(a, bits) _rotl(a, bits)
+#define PR_ROTATE_RIGHT32(a, bits) _rotr(a, bits)
+#else
+#define PR_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits))))
+#define PR_ROTATE_RIGHT32(a, bits) (((a) >> (bits)) | ((a) << (32 - (bits))))
+#endif
+
+PR_END_EXTERN_C
+#endif /* prbit_h___ */
diff --git a/nsprpub/pr/include/prclist.h b/nsprpub/pr/include/prclist.h
new file mode 100644
index 0000000000..a069176055
--- /dev/null
+++ b/nsprpub/pr/include/prclist.h
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prclist_h___
+#define prclist_h___
+
+#include "prtypes.h"
+
+typedef struct PRCListStr PRCList;
+
+/*
+** Circular linked list
+*/
+struct PRCListStr {
+ PRCList *next;
+ PRCList *prev;
+};
+
+/*
+** Insert element "_e" into the list, before "_l".
+*/
+#define PR_INSERT_BEFORE(_e,_l) \
+ PR_BEGIN_MACRO \
+ (_e)->next = (_l); \
+ (_e)->prev = (_l)->prev; \
+ (_l)->prev->next = (_e); \
+ (_l)->prev = (_e); \
+ PR_END_MACRO
+
+/*
+** Insert element "_e" into the list, after "_l".
+*/
+#define PR_INSERT_AFTER(_e,_l) \
+ PR_BEGIN_MACRO \
+ (_e)->next = (_l)->next; \
+ (_e)->prev = (_l); \
+ (_l)->next->prev = (_e); \
+ (_l)->next = (_e); \
+ PR_END_MACRO
+
+/*
+** Return the element following element "_e"
+*/
+#define PR_NEXT_LINK(_e) \
+ ((_e)->next)
+/*
+** Return the element preceding element "_e"
+*/
+#define PR_PREV_LINK(_e) \
+ ((_e)->prev)
+
+/*
+** Append an element "_e" to the end of the list "_l"
+*/
+#define PR_APPEND_LINK(_e,_l) PR_INSERT_BEFORE(_e,_l)
+
+/*
+** Insert an element "_e" at the head of the list "_l"
+*/
+#define PR_INSERT_LINK(_e,_l) PR_INSERT_AFTER(_e,_l)
+
+/* Return the head/tail of the list */
+#define PR_LIST_HEAD(_l) (_l)->next
+#define PR_LIST_TAIL(_l) (_l)->prev
+
+/*
+** Remove the element "_e" from it's circular list.
+*/
+#define PR_REMOVE_LINK(_e) \
+ PR_BEGIN_MACRO \
+ (_e)->prev->next = (_e)->next; \
+ (_e)->next->prev = (_e)->prev; \
+ PR_END_MACRO
+
+/*
+** Remove the element "_e" from it's circular list. Also initializes the
+** linkage.
+*/
+#define PR_REMOVE_AND_INIT_LINK(_e) \
+ PR_BEGIN_MACRO \
+ (_e)->prev->next = (_e)->next; \
+ (_e)->next->prev = (_e)->prev; \
+ (_e)->next = (_e); \
+ (_e)->prev = (_e); \
+ PR_END_MACRO
+
+/*
+** Return non-zero if the given circular list "_l" is empty, zero if the
+** circular list is not empty
+*/
+#define PR_CLIST_IS_EMPTY(_l) \
+ ((_l)->next == (_l))
+
+/*
+** Initialize a circular list
+*/
+#define PR_INIT_CLIST(_l) \
+ PR_BEGIN_MACRO \
+ (_l)->next = (_l); \
+ (_l)->prev = (_l); \
+ PR_END_MACRO
+
+#define PR_INIT_STATIC_CLIST(_l) \
+ {(_l), (_l)}
+
+#endif /* prclist_h___ */
diff --git a/nsprpub/pr/include/prcmon.h b/nsprpub/pr/include/prcmon.h
new file mode 100644
index 0000000000..69171138b2
--- /dev/null
+++ b/nsprpub/pr/include/prcmon.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prcmon_h___
+#define prcmon_h___
+
+/*
+** Interface to cached monitors. Cached monitors use an address to find a
+** given PR monitor. In this way a monitor can be associated with another
+** object without preallocating a monitor for all objects.
+**
+** A hash table is used to quickly map addresses to individual monitors
+** and the system automatically grows the hash table as needed.
+**
+** Cache monitors are about 5 times slower to use than uncached monitors.
+*/
+#include "prmon.h"
+#include "prinrval.h"
+
+PR_BEGIN_EXTERN_C
+
+/**
+** Like PR_EnterMonitor except use the "address" to find a monitor in the
+** monitor cache. If successful, returns the PRMonitor now associated
+** with "address". Note that you must PR_CExitMonitor the address to
+** release the monitor cache entry (otherwise the monitor cache will fill
+** up). This call will return NULL if the monitor cache needs to be
+** expanded and the system is out of memory.
+*/
+NSPR_API(PRMonitor*) PR_CEnterMonitor(void *address);
+
+/*
+** Like PR_ExitMonitor except use the "address" to find a monitor in the
+** monitor cache.
+*/
+NSPR_API(PRStatus) PR_CExitMonitor(void *address);
+
+/*
+** Like PR_Wait except use the "address" to find a monitor in the
+** monitor cache.
+*/
+NSPR_API(PRStatus) PR_CWait(void *address, PRIntervalTime timeout);
+
+/*
+** Like PR_Notify except use the "address" to find a monitor in the
+** monitor cache.
+*/
+NSPR_API(PRStatus) PR_CNotify(void *address);
+
+/*
+** Like PR_NotifyAll except use the "address" to find a monitor in the
+** monitor cache.
+*/
+NSPR_API(PRStatus) PR_CNotifyAll(void *address);
+
+/*
+** Set a callback to be invoked each time a monitor is recycled from the cache
+** freelist, with the monitor's cache-key passed in address.
+*/
+NSPR_API(void) PR_CSetOnMonitorRecycle(void (PR_CALLBACK *callback)(void *address));
+
+PR_END_EXTERN_C
+
+#endif /* prcmon_h___ */
diff --git a/nsprpub/pr/include/prcountr.h b/nsprpub/pr/include/prcountr.h
new file mode 100644
index 0000000000..1503d06d83
--- /dev/null
+++ b/nsprpub/pr/include/prcountr.h
@@ -0,0 +1,525 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prcountr_h___
+#define prcountr_h___
+
+/*----------------------------------------------------------------------------
+** prcountr.h -- NSPR Instrumentation counters
+**
+** The NSPR Counter Feature provides a means to "count
+** something." Counters can be dynamically defined, incremented,
+** decremented, set, and deleted under application program
+** control.
+**
+** The Counter Feature is intended to be used as instrumentation,
+** not as operational data. If you need a counter for operational
+** data, use native integral types.
+**
+** Counters are 32bit unsigned intergers. On overflow, a counter
+** will wrap. No exception is recognized or reported.
+**
+** A counter can be dynamically created using a two level naming
+** convention. A "handle" is returned when the counter is
+** created. The counter can subsequently be addressed by its
+** handle. An API is provided to get an existing counter's handle
+** given the names with which it was originally created.
+** Similarly, a counter's name can be retrieved given its handle.
+**
+** The counter naming convention is a two-level hierarchy. The
+** QName is the higher level of the hierarchy; RName is the
+** lower level. RNames can be thought of as existing within a
+** QName. The same RName can exist within multiple QNames. QNames
+** are unique. The NSPR Counter is not a near-zero overhead
+** feature. Application designers should be aware of
+** serialization issues when using the Counter API. Creating a
+** counter locks a large asset, potentially causing a stall. This
+** suggest that applications should create counters at component
+** initialization, for example, and not create and destroy them
+** willy-nilly. ... You have been warned.
+**
+** Incrementing and Adding to counters uses atomic operations.
+** The performance of these operations will vary from platform
+** to platform. On platforms where atomic operations are not
+** supported the overhead may be substantial.
+**
+** When traversing the counter database with FindNext functions,
+** the instantaneous values of any given counter is that at the
+** moment of extraction. The state of the entire counter database
+** may not be viewed as atomic.
+**
+** The counter interface may be disabled (No-Op'd) at compile
+** time. When DEBUG is defined at compile time, the Counter
+** Feature is compiled into NSPR and applications invoking it.
+** When DEBUG is not defined, the counter macros compile to
+** nothing. To force the Counter Feature to be compiled into an
+** optimized build, define FORCE_NSPR_COUNTERS at compile time
+** for both NSPR and the application intending to use it.
+**
+** Application designers should use the macro form of the Counter
+** Feature methods to minimize performance impact in optimized
+** builds. The macros normally compile to nothing on optimized
+** builds.
+**
+** Application designers should be aware of the effects of
+** debug and optimized build differences when using result of the
+** Counter Feature macros in expressions.
+**
+** The Counter Feature is thread-safe and SMP safe.
+**
+** /lth. 09-Jun-1998.
+*/
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Opaque counter handle type.
+** ... don't even think of looking in here.
+**
+*/
+typedef void * PRCounterHandle;
+
+#define PRCOUNTER_NAME_MAX 31
+#define PRCOUNTER_DESC_MAX 255
+
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_DEFINE_COUNTER() -- Define a PRCounterHandle
+**
+** DESCRIPTION: PR_DEFINE_COUNTER() is used to define a counter
+** handle.
+**
+*/
+#define PR_DEFINE_COUNTER(name) PRCounterHandle name
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_INIT_COUNTER_HANDLE() -- Set the value of a PRCounterHandle
+**
+** DESCRIPTION:
+** PR_INIT_COUNTER_HANDLE() sets the value of a PRCounterHandle
+** to value.
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#define PR_INIT_COUNTER_HANDLE(handle,value)\
+ (handle) = (PRCounterHandle)(value)
+#else
+#define PR_INIT_COUNTER_HANDLE(handle,value)
+#endif
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_CreateCounter() -- Create a counter
+**
+** DESCRIPTION: PR_CreateCounter() creates a counter object and
+** initializes it to zero.
+**
+** The macro form takes as its first argument the name of the
+** PRCounterHandle to receive the handle returned from
+** PR_CreateCounter().
+**
+** INPUTS:
+** qName: The QName for the counter object. The maximum length
+** of qName is defined by PRCOUNTER_NAME_MAX
+**
+** rName: The RName for the counter object. The maximum length
+** of qName is defined by PRCOUNTER_NAME_MAX
+**
+** descrioption: The description of the counter object. The
+** maximum length of description is defined by
+** PRCOUNTER_DESC_MAX.
+**
+** OUTPUTS:
+**
+** RETURNS:
+** PRCounterHandle.
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#define PR_CREATE_COUNTER(handle,qName,rName,description)\
+ (handle) = PR_CreateCounter((qName),(rName),(description))
+#else
+#define PR_CREATE_COUNTER(handle,qName,rName,description)
+#endif
+
+NSPR_API(PRCounterHandle)
+PR_CreateCounter(
+ const char *qName,
+ const char *rName,
+ const char *description
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_DestroyCounter() -- Destroy a counter object.
+**
+** DESCRIPTION: PR_DestroyCounter() removes a counter and
+** unregisters its handle from the counter database.
+**
+** INPUTS:
+** handle: the PRCounterHandle of the counter to be destroyed.
+**
+** OUTPUTS:
+** The counter is destroyed.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#define PR_DESTROY_COUNTER(handle) PR_DestroyCounter((handle))
+#else
+#define PR_DESTROY_COUNTER(handle)
+#endif
+
+NSPR_API(void)
+PR_DestroyCounter(
+ PRCounterHandle handle
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetCounterHandleFromName() -- Retreive a
+** counter's handle give its name.
+**
+** DESCRIPTION: PR_GetCounterHandleFromName() retreives a
+** counter's handle from the counter database, given the name
+** the counter was originally created with.
+**
+** INPUTS:
+** qName: Counter's original QName.
+** rName: Counter's original RName.
+**
+** OUTPUTS:
+**
+** RETURNS:
+** PRCounterHandle or PRCounterError.
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#define PR_GET_COUNTER_HANDLE_FROM_NAME(handle,qName,rName)\
+ (handle) = PR_GetCounterHandleFromName((qName),(rName))
+#else
+#define PR_GET_COUNTER_HANDLE_FROM_NAME(handle,qName,rName)
+#endif
+
+NSPR_API(PRCounterHandle)
+PR_GetCounterHandleFromName(
+ const char *qName,
+ const char *rName
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetCounterNameFromHandle() -- Retreive a
+** counter's name, given its handle.
+**
+** DESCRIPTION: PR_GetCounterNameFromHandle() retreives a
+** counter's name given its handle.
+**
+** INPUTS:
+** qName: Where to store a pointer to qName.
+** rName: Where to store a pointer to rName.
+** description: Where to store a pointer to description.
+**
+** OUTPUTS: Pointers to the Counter Feature's copies of the names
+** used when the counters were created.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#define PR_GET_COUNTER_NAME_FROM_HANDLE(handle,qName,rName,description)\
+ PR_GetCounterNameFromHandle((handle),(qName),(rName),(description))
+#else
+#define PR_GET_COUNTER_NAME_FROM_HANDLE(handle,qName,rName,description )
+#endif
+
+NSPR_API(void)
+PR_GetCounterNameFromHandle(
+ PRCounterHandle handle,
+ const char **qName,
+ const char **rName,
+ const char **description
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_IncrementCounter() -- Add one to the referenced
+** counter.
+**
+** DESCRIPTION: Add one to the referenced counter.
+**
+** INPUTS:
+** handle: The PRCounterHandle of the counter to be incremented
+**
+** OUTPUTS: The counter is incrementd.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#define PR_INCREMENT_COUNTER(handle) PR_IncrementCounter(handle)
+#else
+#define PR_INCREMENT_COUNTER(handle)
+#endif
+
+NSPR_API(void)
+PR_IncrementCounter(
+ PRCounterHandle handle
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_DecrementCounter() -- Subtract one from the
+** referenced counter
+**
+** DESCRIPTION: Subtract one from the referenced counter.
+**
+** INPUTS:
+** handle: The PRCounterHandle of the coutner to be
+** decremented.
+**
+** OUTPUTS: the counter is decremented.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#define PR_DECREMENT_COUNTER(handle) PR_DecrementCounter(handle)
+#else
+#define PR_DECREMENT_COUNTER(handle)
+#endif
+
+NSPR_API(void)
+PR_DecrementCounter(
+ PRCounterHandle handle
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_AddToCounter() -- Add a value to a counter.
+**
+** DESCRIPTION: Add value to the counter referenced by handle.
+**
+** INPUTS:
+** handle: the PRCounterHandle of the counter to be added to.
+**
+** value: the value to be added to the counter.
+**
+** OUTPUTS: new value for counter.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#define PR_ADD_TO_COUNTER(handle,value)\
+ PR_AddToCounter((handle),(value))
+#else
+#define PR_ADD_TO_COUNTER(handle,value)
+#endif
+
+NSPR_API(void)
+PR_AddToCounter(
+ PRCounterHandle handle,
+ PRUint32 value
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_SubtractFromCounter() -- A value is subtracted
+** from a counter.
+**
+** DESCRIPTION:
+** Subtract a value from a counter.
+**
+** INPUTS:
+** handle: the PRCounterHandle of the counter to be subtracted
+** from.
+**
+** value: the value to be subtracted from the counter.
+**
+** OUTPUTS: new value for counter
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#define PR_SUBTRACT_FROM_COUNTER(handle,value)\
+ PR_SubtractFromCounter((handle),(value))
+#else
+#define PR_SUBTRACT_FROM_COUNTER(handle,value)
+#endif
+
+NSPR_API(void)
+PR_SubtractFromCounter(
+ PRCounterHandle handle,
+ PRUint32 value
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetCounter() -- Retreive the value of a counter
+**
+** DESCRIPTION:
+** Retreive the value of a counter.
+**
+** INPUTS:
+** handle: the PR_CounterHandle of the counter to be retreived
+**
+** OUTPUTS:
+**
+** RETURNS: The value of the referenced counter
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#define PR_GET_COUNTER(counter,handle)\
+ (counter) = PR_GetCounter((handle))
+#else
+#define PR_GET_COUNTER(counter,handle) 0
+#endif
+
+NSPR_API(PRUint32)
+PR_GetCounter(
+ PRCounterHandle handle
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_SetCounter() -- Replace the content of counter
+** with value.
+**
+** DESCRIPTION: The contents of the referenced counter are
+** replaced by value.
+**
+** INPUTS:
+** handle: the PRCounterHandle of the counter whose contents
+** are to be replaced.
+**
+** value: the new value of the counter.
+**
+** OUTPUTS:
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#define PR_SET_COUNTER(handle,value) PR_SetCounter((handle),(value))
+#else
+#define PR_SET_COUNTER(handle,value)
+#endif
+
+NSPR_API(void)
+PR_SetCounter(
+ PRCounterHandle handle,
+ PRUint32 value
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_FindNextCounterQname() -- Retreive the next QName counter
+** handle iterator
+**
+** DESCRIPTION:
+** PR_FindNextCounterQname() retreives the first or next Qname
+** the counter data base, depending on the value of handle. When
+** handle is NULL, the function attempts to retreive the first
+** QName handle in the database. When handle is a handle previosly
+** retreived QName handle, then the function attempts to retreive
+** the next QName handle.
+**
+** INPUTS:
+** handle: PRCounterHandle or NULL.
+**
+** OUTPUTS: returned
+**
+** RETURNS: PRCounterHandle or NULL when no more QName counter
+** handles are present.
+**
+** RESTRICTIONS:
+** A concurrent PR_CreateCounter() or PR_DestroyCounter() may
+** cause unpredictable results.
+**
+** A PRCounterHandle returned from this function may only be used
+** in another PR_FindNextCounterQname() function call; other
+** operations may cause unpredictable results.
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#define PR_FIND_NEXT_COUNTER_QNAME(next,handle)\
+ (next) = PR_FindNextCounterQname((handle))
+#else
+#define PR_FIND_NEXT_COUNTER_QNAME(next,handle) NULL
+#endif
+
+NSPR_API(PRCounterHandle)
+PR_FindNextCounterQname(
+ PRCounterHandle handle
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_FindNextCounterRname() -- Retreive the next RName counter
+** handle iterator
+**
+** DESCRIPTION:
+** PR_FindNextCounterRname() retreives the first or next RNname
+** handle from the counter data base, depending on the
+** value of handle. When handle is NULL, the function attempts to
+** retreive the first RName handle in the database. When handle is
+** a handle previosly retreived RName handle, then the function
+** attempts to retreive the next RName handle.
+**
+** INPUTS:
+** handle: PRCounterHandle or NULL.
+** qhandle: PRCounterHandle of a previously aquired via
+** PR_FIND_NEXT_QNAME_HANDLE()
+**
+** OUTPUTS: returned
+**
+** RETURNS: PRCounterHandle or NULL when no more RName counter
+** handles are present.
+**
+** RESTRICTIONS:
+** A concurrent PR_CreateCounter() or PR_DestroyCounter() may
+** cause unpredictable results.
+**
+** A PRCounterHandle returned from this function may only be used
+** in another PR_FindNextCounterRname() function call; other
+** operations may cause unpredictable results.
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#define PR_FIND_NEXT_COUNTER_RNAME(next,rhandle,qhandle)\
+ (next) = PR_FindNextCounterRname((rhandle),(qhandle))
+#else
+#define PR_FIND_NEXT_COUNTER_RNAME(next,rhandle,qhandle)
+#endif
+
+NSPR_API(PRCounterHandle)
+PR_FindNextCounterRname(
+ PRCounterHandle rhandle,
+ PRCounterHandle qhandle
+);
+
+PR_END_EXTERN_C
+
+#endif /* prcountr_h___ */
diff --git a/nsprpub/pr/include/prcvar.h b/nsprpub/pr/include/prcvar.h
new file mode 100644
index 0000000000..413a3646ac
--- /dev/null
+++ b/nsprpub/pr/include/prcvar.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prcvar_h___
+#define prcvar_h___
+
+#include "prlock.h"
+#include "prinrval.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PRCondVar PRCondVar;
+
+/*
+** Create a new condition variable.
+**
+** "lock" is the lock used to protect the condition variable.
+**
+** Condition variables are synchronization objects that threads can use
+** to wait for some condition to occur.
+**
+** This may fail if memory is tight or if some operating system resource
+** is low. In such cases, a NULL will be returned.
+*/
+NSPR_API(PRCondVar*) PR_NewCondVar(PRLock *lock);
+
+/*
+** Destroy a condition variable. There must be no thread
+** waiting on the condvar. The caller is responsible for guaranteeing
+** that the condvar is no longer in use.
+**
+*/
+NSPR_API(void) PR_DestroyCondVar(PRCondVar *cvar);
+
+/*
+** The thread that waits on a condition is blocked in a "waiting on
+** condition" state until another thread notifies the condition or a
+** caller specified amount of time expires. The lock associated with
+** the condition variable will be released, which must have be held
+** prior to the call to wait.
+**
+** Logically a notified thread is moved from the "waiting on condition"
+** state and made "ready." When scheduled, it will attempt to reacquire
+** the lock that it held when wait was called.
+**
+** The timeout has two well known values, PR_INTERVAL_NO_TIMEOUT and
+** PR_INTERVAL_NO_WAIT. The former value requires that a condition be
+** notified (or the thread interrupted) before it will resume from the
+** wait. If the timeout has a value of PR_INTERVAL_NO_WAIT, the effect
+** is to release the lock, possibly causing a rescheduling within the
+** runtime, then immediately attempting to reacquire the lock and resume.
+**
+** Any other value for timeout will cause the thread to be rescheduled
+** either due to explicit notification or an expired interval. The latter
+** must be determined by treating time as one part of the monitored data
+** being protected by the lock and tested explicitly for an expired
+** interval.
+**
+** Returns PR_FAILURE if the caller has not locked the lock associated
+** with the condition variable or the thread was interrupted (PR_Interrupt()).
+** The particular reason can be extracted with PR_GetError().
+*/
+NSPR_API(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout);
+
+/*
+** Notify ONE thread that is currently waiting on 'cvar'. Which thread is
+** dependent on the implementation of the runtime. Common sense would dictate
+** that all threads waiting on a single condition have identical semantics,
+** therefore which one gets notified is not significant.
+**
+** The calling thead must hold the lock that protects the condition, as
+** well as the invariants that are tightly bound to the condition, when
+** notify is called.
+**
+** Returns PR_FAILURE if the caller has not locked the lock associated
+** with the condition variable.
+*/
+NSPR_API(PRStatus) PR_NotifyCondVar(PRCondVar *cvar);
+
+/*
+** Notify all of the threads waiting on the condition variable. The order
+** that the threads are notified is indeterminant. The lock that protects
+** the condition must be held.
+**
+** Returns PR_FAILURE if the caller has not locked the lock associated
+** with the condition variable.
+*/
+NSPR_API(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar);
+
+PR_END_EXTERN_C
+
+#endif /* prcvar_h___ */
diff --git a/nsprpub/pr/include/prdtoa.h b/nsprpub/pr/include/prdtoa.h
new file mode 100644
index 0000000000..312a9d60ad
--- /dev/null
+++ b/nsprpub/pr/include/prdtoa.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prdtoa_h___
+#define prdtoa_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** PR_strtod() returns as a double-precision floating-point number
+** the value represented by the character string pointed to by
+** s00. The string is scanned up to the first unrecognized
+** character.
+**a
+** If the value of se is not (char **)NULL, a pointer to
+** the character terminating the scan is returned in the location pointed
+** to by se. If no number can be formed, se is set to s00, and
+** zero is returned.
+*/
+NSPR_API(PRFloat64)
+PR_strtod(const char *s00, char **se);
+
+/*
+** PR_cnvtf()
+** conversion routines for floating point
+** prcsn - number of digits of precision to generate floating
+** point value.
+*/
+NSPR_API(void) PR_cnvtf(char *buf, PRIntn bufsz, PRIntn prcsn, PRFloat64 fval);
+
+/*
+** PR_dtoa() converts double to a string.
+**
+** ARGUMENTS:
+** If rve is not null, *rve is set to point to the end of the return value.
+** If d is +-Infinity or NaN, then *decpt is set to 9999.
+**
+** mode:
+** 0 ==> shortest string that yields d when read in
+** and rounded to nearest.
+*/
+NSPR_API(PRStatus) PR_dtoa(PRFloat64 d, PRIntn mode, PRIntn ndigits,
+ PRIntn *decpt, PRIntn *sign, char **rve, char *buf, PRSize bufsize);
+
+PR_END_EXTERN_C
+
+#endif /* prdtoa_h___ */
diff --git a/nsprpub/pr/include/prenv.h b/nsprpub/pr/include/prenv.h
new file mode 100644
index 0000000000..550f05c936
--- /dev/null
+++ b/nsprpub/pr/include/prenv.h
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prenv_h___
+#define prenv_h___
+
+#include "prtypes.h"
+
+/*******************************************************************************/
+/*******************************************************************************/
+/****************** THESE FUNCTIONS MAY NOT BE THREAD SAFE *********************/
+/*******************************************************************************/
+/*******************************************************************************/
+
+PR_BEGIN_EXTERN_C
+
+/*
+** PR_GetEnv() -- Retrieve value of environment variable
+**
+** Description:
+** PR_GetEnv() is modeled on Unix getenv().
+**
+**
+** Inputs:
+** var -- The name of the environment variable
+**
+** Returns:
+** The value of the environment variable 'var' or NULL if
+** the variable is undefined.
+**
+** Restrictions:
+** You'd think that a POSIX getenv(), putenv() would be
+** consistently implemented everywhere. Surprise! It is not. On
+** some platforms, a putenv() where the argument is of
+** the form "name" causes the named environment variable to
+** be un-set; that is: a subsequent getenv() returns NULL. On
+** other platforms, the putenv() fails, on others, it is a
+** no-op. Similarly, a putenv() where the argument is of the
+** form "name=" causes the named environment variable to be
+** un-set; a subsequent call to getenv() returns NULL. On
+** other platforms, a subsequent call to getenv() returns a
+** pointer to a null-string (a byte of zero).
+**
+** PR_GetEnv(), PR_SetEnv() provide a consistent behavior
+** across all supported platforms. There are, however, some
+** restrictions and some practices you must use to achieve
+** consistent results everywhere.
+**
+** When manipulating the environment there is no way to un-set
+** an environment variable across all platforms. We suggest
+** you interpret the return of a pointer to null-string to
+** mean the same as a return of NULL from PR_GetEnv().
+**
+** A call to PR_SetEnv() where the parameter is of the form
+** "name" will return PR_FAILURE; the environment remains
+** unchanged. A call to PR_SetEnv() where the parameter is
+** of the form "name=" may un-set the envrionment variable on
+** some platforms; on others it may set the value of the
+** environment variable to the null-string.
+**
+** For example, to test for NULL return or return of the
+** null-string from PR_GetEnv(), use the following code
+** fragment:
+**
+** char *val = PR_GetEnv("foo");
+** if ((NULL == val) || ('\0' == *val)) {
+** ... interpret this as un-set ...
+** }
+**
+** The caller must ensure that the string passed
+** to PR_SetEnv() is persistent. That is: The string should
+** not be on the stack, where it can be overwritten
+** on return from the function calling PR_SetEnv().
+** Similarly, the string passed to PR_SetEnv() must not be
+** overwritten by other actions of the process. ... Some
+** platforms use the string by reference rather than copying
+** it into the environment space. ... You have been warned!
+**
+** Use of platform-native functions that manipulate the
+** environment (getenv(), putenv(),
+** SetEnvironmentVariable(), etc.) must not be used with
+** NSPR's similar functions. The platform-native functions
+** may not be thread safe and/or may operate on different
+** conceptual environment space than that operated upon by
+** NSPR's functions or other environment manipulating
+** functions on the same platform. (!)
+**
+*/
+NSPR_API(char*) PR_GetEnv(const char *var);
+
+/*
+** PR_GetEnvSecure() -- get a security-sensitive environment variable
+**
+** Description:
+**
+** PR_GetEnvSecure() is similar to PR_GetEnv(), but it returns NULL if
+** the program was run with elevated privilege (e.g., setuid or setgid
+** on Unix). This can be used for cases like log file paths which
+** could otherwise be used for privilege escalation. Note that some
+** platforms may have platform-specific privilege elevation mechanisms
+** not recognized by this function; see the implementation for details.
+*/
+NSPR_API(char*) PR_GetEnvSecure(const char *var);
+
+/*
+** PR_SetEnv() -- set, unset or change an environment variable
+**
+** Description:
+** PR_SetEnv() is modeled on the Unix putenv() function.
+**
+** Inputs:
+** string -- pointer to a caller supplied
+** constant, persistent string of the form name=value. Where
+** name is the name of the environment variable to be set or
+** changed; value is the value assigned to the variable.
+**
+** Returns:
+** PRStatus.
+**
+** Restrictions:
+** See the Restrictions documented in the description of
+** PR_GetEnv() in this header file.
+**
+**
+*/
+NSPR_API(PRStatus) PR_SetEnv(const char *string);
+
+/*
+** PR_DuplicateEnvironment() -- Obtain a copy of the environment.
+**
+** Description:
+** PR_DuplicateEnvironment() copies the environment so that it can be
+** modified without changing the current process's environment, and
+** then passed to interfaces such as POSIX execve(). In particular,
+** this avoids needing to allocate memory or take locks in the child
+** after a fork(); neither of these is allowed by POSIX after a
+** multithreaded process calls fork(), and PR_SetEnv does both.
+**
+** Inputs:
+** none
+**
+** Returns:
+** A pointer to a null-terminated array of null-terminated strings,
+** like the traditional global variable "environ". The array and
+** the strings are allocated with PR_Malloc(), and it is the
+** caller's responsibility to free them.
+**
+** In case of memory allocation failure, or if the operating system
+** doesn't support reading the entire environment through the global
+** variable "environ" or similar, returns NULL instead.
+**
+** Restrictions:
+** Similarly to PR_GetEnv(), this function may not interoperate as
+** expected with the operating system's native environment accessors.
+*/
+NSPR_API(char **) PR_DuplicateEnvironment(void);
+
+PR_END_EXTERN_C
+
+#endif /* prenv_h___ */
diff --git a/nsprpub/pr/include/prerr.h b/nsprpub/pr/include/prerr.h
new file mode 100644
index 0000000000..8512329e19
--- /dev/null
+++ b/nsprpub/pr/include/prerr.h
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prerr_h___
+#define prerr_h___
+
+/*
+ *
+ * prerr.h
+ * This file is automatically generated; please do not edit it.
+ */
+
+/* Memory allocation attempt failed */
+#define PR_OUT_OF_MEMORY_ERROR (-6000L)
+
+/* Invalid file descriptor */
+#define PR_BAD_DESCRIPTOR_ERROR (-5999L)
+
+/* The operation would have blocked */
+#define PR_WOULD_BLOCK_ERROR (-5998L)
+
+/* Invalid memory address argument */
+#define PR_ACCESS_FAULT_ERROR (-5997L)
+
+/* Invalid function for file type */
+#define PR_INVALID_METHOD_ERROR (-5996L)
+
+/* Invalid memory address argument */
+#define PR_ILLEGAL_ACCESS_ERROR (-5995L)
+
+/* Some unknown error has occurred */
+#define PR_UNKNOWN_ERROR (-5994L)
+
+/* Operation interrupted by another thread */
+#define PR_PENDING_INTERRUPT_ERROR (-5993L)
+
+/* function not implemented */
+#define PR_NOT_IMPLEMENTED_ERROR (-5992L)
+
+/* I/O function error */
+#define PR_IO_ERROR (-5991L)
+
+/* I/O operation timed out */
+#define PR_IO_TIMEOUT_ERROR (-5990L)
+
+/* I/O operation on busy file descriptor */
+#define PR_IO_PENDING_ERROR (-5989L)
+
+/* The directory could not be opened */
+#define PR_DIRECTORY_OPEN_ERROR (-5988L)
+
+/* Invalid function argument */
+#define PR_INVALID_ARGUMENT_ERROR (-5987L)
+
+/* Network address not available (in use?) */
+#define PR_ADDRESS_NOT_AVAILABLE_ERROR (-5986L)
+
+/* Network address type not supported */
+#define PR_ADDRESS_NOT_SUPPORTED_ERROR (-5985L)
+
+/* Already connected */
+#define PR_IS_CONNECTED_ERROR (-5984L)
+
+/* Network address is invalid */
+#define PR_BAD_ADDRESS_ERROR (-5983L)
+
+/* Local Network address is in use */
+#define PR_ADDRESS_IN_USE_ERROR (-5982L)
+
+/* Connection refused by peer */
+#define PR_CONNECT_REFUSED_ERROR (-5981L)
+
+/* Network address is presently unreachable */
+#define PR_NETWORK_UNREACHABLE_ERROR (-5980L)
+
+/* Connection attempt timed out */
+#define PR_CONNECT_TIMEOUT_ERROR (-5979L)
+
+/* Network file descriptor is not connected */
+#define PR_NOT_CONNECTED_ERROR (-5978L)
+
+/* Failure to load dynamic library */
+#define PR_LOAD_LIBRARY_ERROR (-5977L)
+
+/* Failure to unload dynamic library */
+#define PR_UNLOAD_LIBRARY_ERROR (-5976L)
+
+/* Symbol not found in any of the loaded dynamic libraries */
+#define PR_FIND_SYMBOL_ERROR (-5975L)
+
+/* Insufficient system resources */
+#define PR_INSUFFICIENT_RESOURCES_ERROR (-5974L)
+
+/* A directory lookup on a network address has failed */
+#define PR_DIRECTORY_LOOKUP_ERROR (-5973L)
+
+/* Attempt to access a TPD key that is out of range */
+#define PR_TPD_RANGE_ERROR (-5972L)
+
+/* Process open FD table is full */
+#define PR_PROC_DESC_TABLE_FULL_ERROR (-5971L)
+
+/* System open FD table is full */
+#define PR_SYS_DESC_TABLE_FULL_ERROR (-5970L)
+
+/* Network operation attempted on non-network file descriptor */
+#define PR_NOT_SOCKET_ERROR (-5969L)
+
+/* TCP-specific function attempted on a non-TCP file descriptor */
+#define PR_NOT_TCP_SOCKET_ERROR (-5968L)
+
+/* TCP file descriptor is already bound */
+#define PR_SOCKET_ADDRESS_IS_BOUND_ERROR (-5967L)
+
+/* Access Denied */
+#define PR_NO_ACCESS_RIGHTS_ERROR (-5966L)
+
+/* The requested operation is not supported by the platform */
+#define PR_OPERATION_NOT_SUPPORTED_ERROR (-5965L)
+
+/* The host operating system does not support the protocol requested */
+#define PR_PROTOCOL_NOT_SUPPORTED_ERROR (-5964L)
+
+/* Access to the remote file has been severed */
+#define PR_REMOTE_FILE_ERROR (-5963L)
+
+/* The value requested is too large to be stored in the data buffer provided */
+#define PR_BUFFER_OVERFLOW_ERROR (-5962L)
+
+/* TCP connection reset by peer */
+#define PR_CONNECT_RESET_ERROR (-5961L)
+
+/* Unused */
+#define PR_RANGE_ERROR (-5960L)
+
+/* The operation would have deadlocked */
+#define PR_DEADLOCK_ERROR (-5959L)
+
+/* The file is already locked */
+#define PR_FILE_IS_LOCKED_ERROR (-5958L)
+
+/* Write would result in file larger than the system allows */
+#define PR_FILE_TOO_BIG_ERROR (-5957L)
+
+/* The device for storing the file is full */
+#define PR_NO_DEVICE_SPACE_ERROR (-5956L)
+
+/* Unused */
+#define PR_PIPE_ERROR (-5955L)
+
+/* Unused */
+#define PR_NO_SEEK_DEVICE_ERROR (-5954L)
+
+/* Cannot perform a normal file operation on a directory */
+#define PR_IS_DIRECTORY_ERROR (-5953L)
+
+/* Symbolic link loop */
+#define PR_LOOP_ERROR (-5952L)
+
+/* File name is too long */
+#define PR_NAME_TOO_LONG_ERROR (-5951L)
+
+/* File not found */
+#define PR_FILE_NOT_FOUND_ERROR (-5950L)
+
+/* Cannot perform directory operation on a normal file */
+#define PR_NOT_DIRECTORY_ERROR (-5949L)
+
+/* Cannot write to a read-only file system */
+#define PR_READ_ONLY_FILESYSTEM_ERROR (-5948L)
+
+/* Cannot delete a directory that is not empty */
+#define PR_DIRECTORY_NOT_EMPTY_ERROR (-5947L)
+
+/* Cannot delete or rename a file object while the file system is busy */
+#define PR_FILESYSTEM_MOUNTED_ERROR (-5946L)
+
+/* Cannot rename a file to a file system on another device */
+#define PR_NOT_SAME_DEVICE_ERROR (-5945L)
+
+/* The directory object in the file system is corrupted */
+#define PR_DIRECTORY_CORRUPTED_ERROR (-5944L)
+
+/* Cannot create or rename a filename that already exists */
+#define PR_FILE_EXISTS_ERROR (-5943L)
+
+/* Directory is full. No additional filenames may be added */
+#define PR_MAX_DIRECTORY_ENTRIES_ERROR (-5942L)
+
+/* The required device was in an invalid state */
+#define PR_INVALID_DEVICE_STATE_ERROR (-5941L)
+
+/* The device is locked */
+#define PR_DEVICE_IS_LOCKED_ERROR (-5940L)
+
+/* No more entries in the directory */
+#define PR_NO_MORE_FILES_ERROR (-5939L)
+
+/* Encountered end of file */
+#define PR_END_OF_FILE_ERROR (-5938L)
+
+/* Seek error */
+#define PR_FILE_SEEK_ERROR (-5937L)
+
+/* The file is busy */
+#define PR_FILE_IS_BUSY_ERROR (-5936L)
+
+/* The I/O operation was aborted */
+#define PR_OPERATION_ABORTED_ERROR (-5935L)
+
+/* Operation is still in progress (probably a non-blocking connect) */
+#define PR_IN_PROGRESS_ERROR (-5934L)
+
+/* Operation has already been initiated (probably a non-blocking connect) */
+#define PR_ALREADY_INITIATED_ERROR (-5933L)
+
+/* The wait group is empty */
+#define PR_GROUP_EMPTY_ERROR (-5932L)
+
+/* Object state improper for request */
+#define PR_INVALID_STATE_ERROR (-5931L)
+
+/* Network is down */
+#define PR_NETWORK_DOWN_ERROR (-5930L)
+
+/* Socket shutdown */
+#define PR_SOCKET_SHUTDOWN_ERROR (-5929L)
+
+/* Connection aborted */
+#define PR_CONNECT_ABORTED_ERROR (-5928L)
+
+/* Host is unreachable */
+#define PR_HOST_UNREACHABLE_ERROR (-5927L)
+
+/* The library is not loaded */
+#define PR_LIBRARY_NOT_LOADED_ERROR (-5926L)
+
+/* The one-time function was previously called and failed. Its error code is no longer available */
+#define PR_CALL_ONCE_ERROR (-5925L)
+
+/* Placeholder for the end of the list */
+#define PR_MAX_ERROR (-5924L)
+
+extern void nspr_InitializePRErrorTable(void);
+#define ERROR_TABLE_BASE_nspr (-6000L)
+
+#endif /* prerr_h___ */
diff --git a/nsprpub/pr/include/prerror.h b/nsprpub/pr/include/prerror.h
new file mode 100644
index 0000000000..f445aa90aa
--- /dev/null
+++ b/nsprpub/pr/include/prerror.h
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prerror_h___
+#define prerror_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef PRInt32 PRErrorCode;
+
+#define PR_NSPR_ERROR_BASE -6000
+
+#include "prerr.h"
+
+/*
+** Set error will preserve an error condition within a thread context.
+** The values stored are the NSPR (platform independent) translation of
+** the error. Also, if available, the platform specific oserror is stored.
+** If there is no appropriate OS error number, a zero my be supplied.
+*/
+NSPR_API(void) PR_SetError(PRErrorCode errorCode, PRInt32 oserr);
+
+/*
+** The text value specified may be NULL. If it is not NULL and the text length
+** is zero, the string is assumed to be a null terminated C string. Otherwise
+** the text is assumed to be the length specified and possibly include NULL
+** characters (e.g., a multi-national string).
+**
+** The text will be copied into to thread structure and remain there
+** until the next call to PR_SetError.
+*/
+NSPR_API(void) PR_SetErrorText(
+ PRIntn textLength, const char *text);
+
+/*
+** Return the current threads last set error code.
+*/
+NSPR_API(PRErrorCode) PR_GetError(void);
+
+/*
+** Return the current threads last set os error code. This is used for
+** machine specific code that desires the underlying os error.
+*/
+NSPR_API(PRInt32) PR_GetOSError(void);
+
+/*
+** Get the length of the error text. If a zero is returned, then there
+** is no text. Otherwise, the value returned is sufficient to contain
+** the error text currently available.
+*/
+NSPR_API(PRInt32) PR_GetErrorTextLength(void);
+
+/*
+** Copy the current threads current error text. Then actual number of bytes
+** copied is returned as the result. If the result is zero, the 'text' area
+** is unaffected.
+*/
+NSPR_API(PRInt32) PR_GetErrorText(char *text);
+
+
+/*
+Copyright (C) 1987, 1988 Student Information Processing Board of the
+Massachusetts Institute of Technology.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+used in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission. M.I.T. and the M.I.T. S.I.P.B.
+make no representations about the suitability of this software for any
+purpose. It is provided "as is" without express or implied warranty.
+*/
+
+
+/*
+ * NOTE:
+ * The interfaces for error-code-translation described in the rest of
+ * this file are preliminary in the 3.1 release of nspr and are subject
+ * to change in future releases.
+ */
+
+/*
+** Description: Localizable error code to string function.
+**
+**
+** NSPR provides a mechanism for converting an error code to a
+** descriptive string, in a caller-specified language.
+**
+** Error codes themselves are 32 bit (signed) integers. Typically,
+** the high order 24 bits are an identifier of which error table the
+** error code is from, and the low order 8 bits are a sequential error
+** number within the table. NSPR supports error tables whose first
+** error code is not a multiple of 256, such error code assignments
+** should be avoided when possible.
+**
+** Error table 0 is defined to match the UNIX system call error table
+** (sys_errlist); this allows errno values to be used directly in the
+** library. Other error table numbers are typically formed by
+** compacting together the first four characters of the error table
+** name. The mapping between characters in the name and numeric
+** values in the error code are defined in a system-independent
+** fashion, so that two systems that can pass integral values between
+** them can reliably pass error codes without loss of meaning; this
+** should work even if the character sets used are not the
+** same. (However, if this is to be done, error table 0 should be
+** avoided, since the local system call error tables may differ.)
+**
+** Libraries defining error codes need only provide a table mapping
+** error code numbers to names and default English descriptions,
+** calling a routine to install the table, making it ``known'' to NSPR
+** library. Once installed, a table may not be removed. Any error
+** code the library generates can be converted to the corresponding
+** error message. There is also a default format for error codes
+** accidentally returned before making the table known, which is of
+** the form "unknown code foo 32", where "foo" would be the name of
+** the table.
+**
+** Normally, the error code conversion routine only supports the
+** languages "i-default" and "en", returning the error-table-provided
+** English description for both languages. The application may
+** provide a localization plugin, allowing support for additional
+** languages.
+**
+**/
+
+/**********************************************************************/
+/************************* TYPES AND CONSTANTS ************************/
+/**********************************************************************/
+
+/*
+ * PRLanguageCode --
+ *
+ * NSPR represents a language code as a non-negative integer.
+ * Languages 0 is always "i-default" the language you get without
+ * explicit negotiation. Language 1 is always "en", English
+ * which has been explicitly negotiated. Additional language
+ * codes are defined by an application-provided localization plugin.
+ */
+typedef PRUint32 PRLanguageCode;
+#define PR_LANGUAGE_I_DEFAULT 0 /* i-default, the default language */
+#define PR_LANGUAGE_EN 1 /* English, explicitly negotiated */
+
+/*
+ * struct PRErrorMessage --
+ *
+ * An error message in an error table.
+ */
+struct PRErrorMessage {
+ const char * name; /* Macro name for error */
+ const char * en_text; /* Default English text */
+};
+
+/*
+ * struct PRErrorTable --
+ *
+ * An error table, provided by a library.
+ */
+struct PRErrorTable {
+ const struct PRErrorMessage * msgs; /* Array of error information */
+ const char *name; /* Name of error table source */
+ PRErrorCode base; /* Error code for first error in table */
+ int n_msgs; /* Number of codes in table */
+};
+
+/*
+ * struct PRErrorCallbackPrivate --
+ *
+ * A private structure for the localization plugin
+ */
+struct PRErrorCallbackPrivate;
+
+/*
+ * struct PRErrorCallbackTablePrivate --
+ *
+ * A data structure under which the localization plugin may store information,
+ * associated with an error table, that is private to itself.
+ */
+struct PRErrorCallbackTablePrivate;
+
+/*
+ * PRErrorCallbackLookupFn --
+ *
+ * A function of PRErrorCallbackLookupFn type is a localization
+ * plugin callback which converts an error code into a description
+ * in the requested language. The callback is provided the
+ * appropriate error table, private data for the plugin and the table.
+ * The callback returns the appropriate UTF-8 encoded description, or NULL
+ * if no description can be found.
+ */
+typedef const char *
+PRErrorCallbackLookupFn(PRErrorCode code, PRLanguageCode language,
+ const struct PRErrorTable *table,
+ struct PRErrorCallbackPrivate *cb_private,
+ struct PRErrorCallbackTablePrivate *table_private);
+
+/*
+ * PRErrorCallbackNewTableFn --
+ *
+ * A function PRErrorCallbackNewTableFn type is a localization plugin
+ * callback which is called once with each error table registered
+ * with NSPR. The callback is provided with the error table and
+ * the plugin's private structure. The callback returns any table private
+ * data it wishes to associate with the error table. Does not need to be thread
+ * safe.
+ */
+typedef struct PRErrorCallbackTablePrivate *
+PRErrorCallbackNewTableFn(const struct PRErrorTable *table,
+ struct PRErrorCallbackPrivate *cb_private);
+
+/**********************************************************************/
+/****************************** FUNCTIONS *****************************/
+/**********************************************************************/
+
+/***********************************************************************
+** FUNCTION: PR_ErrorToString
+** DESCRIPTION:
+** Returns the UTF-8 message for an error code in
+** the requested language. May return the message
+** in the default language if a translation in the requested
+** language is not available. The returned string is
+** valid for the duration of the process. Never returns NULL.
+**
+***********************************************************************/
+NSPR_API(const char *) PR_ErrorToString(PRErrorCode code,
+ PRLanguageCode language);
+
+
+/***********************************************************************
+** FUNCTION: PR_ErrorToName
+** DESCRIPTION:
+** Returns the macro name for an error code, or NULL
+** if the error code is not known. The returned string is
+** valid for the duration of the process.
+**
+** Does not work for error table 0, the system error codes.
+**
+***********************************************************************/
+NSPR_API(const char *) PR_ErrorToName(PRErrorCode code);
+
+
+/***********************************************************************
+** FUNCTION: PR_ErrorLanguages
+** DESCRIPTION:
+** Returns the RFC 1766 language tags for the language
+** codes PR_ErrorToString() supports. The returned array is valid
+** for the duration of the process. Never returns NULL. The first
+** item in the returned array is the language tag for PRLanguageCode 0,
+** the second is for PRLanguageCode 1, and so on. The array is terminated
+** with a null pointer.
+**
+***********************************************************************/
+NSPR_API(const char * const *) PR_ErrorLanguages(void);
+
+
+/***********************************************************************
+** FUNCTION: PR_ErrorInstallTable
+** DESCRIPTION:
+** Registers an error table with NSPR. Must be done exactly once per
+** table. Memory pointed to by `table' must remain valid for the life
+** of the process.
+**
+** NOT THREAD SAFE!
+**
+***********************************************************************/
+NSPR_API(PRErrorCode) PR_ErrorInstallTable(const struct PRErrorTable *table);
+
+
+/***********************************************************************
+** FUNCTION: PR_ErrorInstallCallback
+** DESCRIPTION:
+** Registers an error localization plugin with NSPR. May be called
+** at most one time. `languages' contains the language codes supported
+** by this plugin. Languages 0 and 1 must be "i-default" and "en"
+** respectively. `lookup' and `newtable' contain pointers to
+** the plugin callback functions. `cb_private' contains any information
+** private to the plugin functions.
+**
+** NOT THREAD SAFE!
+**
+***********************************************************************/
+NSPR_API(void) PR_ErrorInstallCallback(const char * const * languages,
+ PRErrorCallbackLookupFn *lookup,
+ PRErrorCallbackNewTableFn *newtable,
+ struct PRErrorCallbackPrivate *cb_private);
+
+PR_END_EXTERN_C
+
+#endif /* prerror_h___ */
diff --git a/nsprpub/pr/include/prinet.h b/nsprpub/pr/include/prinet.h
new file mode 100644
index 0000000000..b743ef63ef
--- /dev/null
+++ b/nsprpub/pr/include/prinet.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: prinet.h
+ * Description:
+ * Header file used to find the system header files for socket support[1].
+ * This file serves the following purposes:
+ * - A cross-platform, "get-everything" socket header file. On
+ * Unix, socket support is scattered in several header files,
+ * while Windows has a "get-everything" socket header file[2].
+ * - NSPR needs the following macro definitions and function
+ * prototype declarations from these header files:
+ * AF_INET
+ * INADDR_ANY, INADDR_LOOPBACK, INADDR_BROADCAST
+ * ntohl(), ntohs(), htonl(), ntons().
+ * NSPR does not define its own versions of these macros and
+ * functions. It simply uses the native versions, which have
+ * the same names on all supported platforms.
+ * This file is intended to be included by NSPR public header
+ * files, such as prio.h. One should not include this file directly.
+ *
+ * Notes:
+ * 1. This file should have been an internal header. Please do not
+ * depend on it to pull in the system header files you need.
+ * 2. WARNING: This file is no longer cross-platform as it is a no-op
+ * for WIN32! See the comment in the WIN32 section for details.
+ */
+
+#ifndef prinet_h__
+#define prinet_h__
+
+#if defined(XP_UNIX) || defined(XP_OS2)
+#include <sys/types.h>
+#include <sys/socket.h> /* AF_INET */
+#include <netinet/in.h> /* INADDR_ANY, ..., ntohl(), ... */
+#ifdef XP_OS2
+#include <sys/ioctl.h>
+#endif
+#ifdef XP_UNIX
+#ifdef AIX
+/*
+ * On AIX 4.3, the header <arpa/inet.h> refers to struct
+ * ether_addr and struct sockaddr_dl that are not declared.
+ * The following struct declarations eliminate the compiler
+ * warnings.
+ */
+struct ether_addr;
+struct sockaddr_dl;
+#endif /* AIX */
+#include <arpa/inet.h>
+#endif /* XP_UNIX */
+#include <netdb.h>
+
+#if defined(BSDI) || defined(QNX)
+#include <rpc/types.h> /* the only place that defines INADDR_LOOPBACK */
+#endif
+
+/*
+ * OS/2 hack. For some reason INADDR_LOOPBACK is not defined in the
+ * socket headers.
+ */
+#if defined(OS2) && !defined(INADDR_LOOPBACK)
+#define INADDR_LOOPBACK 0x7f000001
+#endif
+
+/*
+ * Prototypes of ntohl() etc. are declared in <machine/endian.h>
+ * on these platforms.
+ */
+#if defined(BSDI)
+#include <machine/endian.h>
+#endif
+
+/* On Android, ntohl() etc. are declared in <sys/endian.h>. */
+#ifdef __ANDROID__
+#include <sys/endian.h>
+#endif
+
+#elif defined(WIN32)
+
+/*
+ * Do not include any system header files.
+ *
+ * Originally we were including <windows.h>. It slowed down the
+ * compilation of files that included NSPR headers, so we removed
+ * the <windows.h> inclusion at customer's request, which created
+ * an unfortunate inconsistency with other platforms.
+ */
+
+#else
+
+#error Unknown platform
+
+#endif
+
+#endif /* prinet_h__ */
diff --git a/nsprpub/pr/include/prinit.h b/nsprpub/pr/include/prinit.h
new file mode 100644
index 0000000000..9f3be5c061
--- /dev/null
+++ b/nsprpub/pr/include/prinit.h
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prinit_h___
+#define prinit_h___
+
+#include "prthread.h"
+#include "prtypes.h"
+#include "prwin16.h"
+#include <stdio.h>
+
+PR_BEGIN_EXTERN_C
+
+/************************************************************************/
+/**************************IDENTITY AND VERSIONING***********************/
+/************************************************************************/
+
+/*
+** NSPR's name, this should persist until at least the turn of the
+** century.
+*/
+#define PR_NAME "NSPR"
+
+/*
+** NSPR's version is used to determine the likelihood that the version you
+** used to build your component is anywhere close to being compatible with
+** what is in the underlying library.
+**
+** The format of the version string is
+** "<major version>.<minor version>[.<patch level>] [<Beta>]"
+*/
+#define PR_VERSION "4.35"
+#define PR_VMAJOR 4
+#define PR_VMINOR 35
+#define PR_VPATCH 0
+#define PR_BETA PR_FALSE
+
+/*
+** PRVersionCheck
+**
+** The basic signature of the function that is called to provide version
+** checking. The result will be a boolean that indicates the likelihood
+** that the underling library will perform as the caller expects.
+**
+** The only argument is a string, which should be the verson identifier
+** of the library in question. That string will be compared against an
+** equivalent string that represents the actual build version of the
+** exporting library.
+**
+** The result will be the logical union of the directly called library
+** and all dependent libraries.
+*/
+
+typedef PRBool (*PRVersionCheck)(const char*);
+
+/*
+** PR_VersionCheck
+**
+** NSPR's existance proof of the version check function.
+**
+** Note that NSPR has no cooperating dependencies.
+*/
+
+NSPR_API(PRBool) PR_VersionCheck(const char *importedVersion);
+
+/*
+ * Returns a const string of the NSPR library version.
+ */
+NSPR_API(const char*) PR_GetVersion(void);
+
+
+/************************************************************************/
+/*******************************INITIALIZATION***************************/
+/************************************************************************/
+
+/*
+** Initialize the runtime. Attach a thread object to the currently
+** executing native thread of type "type".
+**
+** The specificaiton of 'maxPTDs' is ignored.
+*/
+NSPR_API(void) PR_Init(
+ PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs);
+
+/*
+** And alternate form of initialization, one that may become the default if
+** not the only mechanism, provides a method to get the NSPR runtime init-
+** ialized and place NSPR between the caller and the runtime library. This
+** allows main() to be treated as any other thread root function, signalling
+** its compeletion by returning and allowing the runtime to coordinate the
+** completion of the other threads of the runtime.
+**
+** The priority of the main (or primordial) thread will be PR_PRIORITY_NORMAL.
+** The thread may adjust its own priority by using PR_SetPriority(), though
+** at this time the support for priorities is somewhat weak.
+**
+** The specificaiton of 'maxPTDs' is ignored.
+**
+** The value returned by PR_Initialize is the value returned from the root
+** function, 'prmain'.
+*/
+
+typedef PRIntn (PR_CALLBACK *PRPrimordialFn)(PRIntn argc, char **argv);
+
+NSPR_API(PRIntn) PR_Initialize(
+ PRPrimordialFn prmain, PRIntn argc, char **argv, PRUintn maxPTDs);
+
+/*
+** Return PR_TRUE if PR_Init has already been called.
+*/
+NSPR_API(PRBool) PR_Initialized(void);
+
+/*
+ * Perform a graceful shutdown of NSPR. PR_Cleanup() may be called by
+ * the primordial thread near the end of the main() function.
+ *
+ * PR_Cleanup() attempts to synchronize the natural termination of
+ * process. It does that by blocking the caller, if and only if it is
+ * the primordial thread, until the number of user threads has dropped
+ * to zero. When the primordial thread returns from main(), the process
+ * will immediately and silently exit. That is, it will (if necessary)
+ * forcibly terminate any existing threads and exit without significant
+ * blocking and there will be no error messages or core files.
+ *
+ * PR_Cleanup() returns PR_SUCCESS if NSPR is successfully shutdown,
+ * or PR_FAILURE if the calling thread of this function is not the
+ * primordial thread.
+ */
+NSPR_API(PRStatus) PR_Cleanup(void);
+
+/*
+** Disable Interrupts
+** Disables timer signals used for pre-emptive scheduling.
+*/
+NSPR_API(void) PR_DisableClockInterrupts(void);
+
+/*
+** Enables Interrupts
+** Enables timer signals used for pre-emptive scheduling.
+*/
+NSPR_API(void) PR_EnableClockInterrupts(void);
+
+/*
+** Block Interrupts
+** Blocks the timer signal used for pre-emptive scheduling
+*/
+NSPR_API(void) PR_BlockClockInterrupts(void);
+
+/*
+** Unblock Interrupts
+** Unblocks the timer signal used for pre-emptive scheduling
+*/
+NSPR_API(void) PR_UnblockClockInterrupts(void);
+
+/*
+** Create extra virtual processor threads. Generally used with MP systems.
+*/
+NSPR_API(void) PR_SetConcurrency(PRUintn numCPUs);
+
+/*
+** Control the method and size of the file descriptor (PRFileDesc*)
+** cache used by the runtime. Setting 'high' to zero is for performance,
+** any other value probably for debugging (see memo on FD caching).
+*/
+NSPR_API(PRStatus) PR_SetFDCacheSize(PRIntn low, PRIntn high);
+
+/*
+ * Cause an immediate, nongraceful, forced termination of the process.
+ * It takes a PRIntn argument, which is the exit status code of the
+ * process.
+ */
+NSPR_API(void) PR_ProcessExit(PRIntn status);
+
+/*
+** Abort the process in a non-graceful manner. This will cause a core file,
+** call to the debugger or other moral equivalent as well as causing the
+** entire process to stop.
+*/
+NSPR_API(void) PR_Abort(void);
+
+/*
+ ****************************************************************
+ *
+ * Module initialization:
+ *
+ ****************************************************************
+ */
+
+typedef struct PRCallOnceType {
+ PRIntn initialized;
+ PRInt32 inProgress;
+ PRStatus status;
+} PRCallOnceType;
+
+typedef PRStatus (PR_CALLBACK *PRCallOnceFN)(void);
+
+typedef PRStatus (PR_CALLBACK *PRCallOnceWithArgFN)(void *arg);
+
+NSPR_API(PRStatus) PR_CallOnce(
+ PRCallOnceType *once,
+ PRCallOnceFN func
+);
+
+NSPR_API(PRStatus) PR_CallOnceWithArg(
+ PRCallOnceType *once,
+ PRCallOnceWithArgFN func,
+ void *arg
+);
+
+
+PR_END_EXTERN_C
+
+#endif /* prinit_h___ */
diff --git a/nsprpub/pr/include/prinrval.h b/nsprpub/pr/include/prinrval.h
new file mode 100644
index 0000000000..7797291d63
--- /dev/null
+++ b/nsprpub/pr/include/prinrval.h
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: prinrval.h
+** Description: API to interval timing functions of NSPR.
+**
+**
+** NSPR provides interval times that are independent of network time
+** of day values. Interval times are (in theory) accurate regardless
+** of host processing requirements and also very cheap to acquire. It
+** is expected that getting an interval time while in a synchronized
+** function (holding one's lock).
+**/
+
+#if !defined(prinrval_h)
+#define prinrval_h
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/**********************************************************************/
+/************************* TYPES AND CONSTANTS ************************/
+/**********************************************************************/
+
+typedef PRUint32 PRIntervalTime;
+
+/***********************************************************************
+** DEFINES: PR_INTERVAL_MIN
+** PR_INTERVAL_MAX
+** DESCRIPTION:
+** These two constants define the range (in ticks / second) of the
+** platform dependent type, PRIntervalTime. These constants bound both
+** the period and the resolution of a PRIntervalTime.
+***********************************************************************/
+#define PR_INTERVAL_MIN 1000UL
+#define PR_INTERVAL_MAX 100000UL
+
+/***********************************************************************
+** DEFINES: PR_INTERVAL_NO_WAIT
+** PR_INTERVAL_NO_TIMEOUT
+** DESCRIPTION:
+** Two reserved constants are defined in the PRIntervalTime namespace.
+** They are used to indicate that the process should wait no time (return
+** immediately) or wait forever (never time out), respectively.
+** Note: PR_INTERVAL_NO_TIMEOUT passed as input to PR_Connect is
+** interpreted as use the OS's connect timeout.
+**
+***********************************************************************/
+#define PR_INTERVAL_NO_WAIT 0UL
+#define PR_INTERVAL_NO_TIMEOUT 0xffffffffUL
+
+/**********************************************************************/
+/****************************** FUNCTIONS *****************************/
+/**********************************************************************/
+
+/***********************************************************************
+** FUNCTION: PR_IntervalNow
+** DESCRIPTION:
+** Return the value of NSPR's free running interval timer. That timer
+** can be used to establish epochs and determine intervals (be computing
+** the difference between two times).
+** INPUTS: void
+** OUTPUTS: void
+** RETURN: PRIntervalTime
+**
+** SIDE EFFECTS:
+** None
+** RESTRICTIONS:
+** The units of PRIntervalTime are platform dependent. They are chosen
+** such that they are appropriate for the host OS, yet provide sufficient
+** resolution and period to be useful to clients.
+** MEMORY: N/A
+** ALGORITHM: Platform dependent
+***********************************************************************/
+NSPR_API(PRIntervalTime) PR_IntervalNow(void);
+
+/***********************************************************************
+** FUNCTION: PR_TicksPerSecond
+** DESCRIPTION:
+** Return the number of ticks per second for PR_IntervalNow's clock.
+** The value will be in the range [PR_INTERVAL_MIN..PR_INTERVAL_MAX].
+** INPUTS: void
+** OUTPUTS: void
+** RETURN: PRUint32
+**
+** SIDE EFFECTS:
+** None
+** RESTRICTIONS:
+** None
+** MEMORY: N/A
+** ALGORITHM: N/A
+***********************************************************************/
+NSPR_API(PRUint32) PR_TicksPerSecond(void);
+
+/***********************************************************************
+** FUNCTION: PR_SecondsToInterval
+** PR_MillisecondsToInterval
+** PR_MicrosecondsToInterval
+** DESCRIPTION:
+** Convert standard clock units to platform dependent intervals.
+** INPUTS: PRUint32
+** OUTPUTS: void
+** RETURN: PRIntervalTime
+**
+** SIDE EFFECTS:
+** None
+** RESTRICTIONS:
+** Conversion may cause overflow, which is not reported.
+** MEMORY: N/A
+** ALGORITHM: N/A
+***********************************************************************/
+NSPR_API(PRIntervalTime) PR_SecondsToInterval(PRUint32 seconds);
+NSPR_API(PRIntervalTime) PR_MillisecondsToInterval(PRUint32 milli);
+NSPR_API(PRIntervalTime) PR_MicrosecondsToInterval(PRUint32 micro);
+
+/***********************************************************************
+** FUNCTION: PR_IntervalToSeconds
+** PR_IntervalToMilliseconds
+** PR_IntervalToMicroseconds
+** DESCRIPTION:
+** Convert platform dependent intervals to standard clock units.
+** INPUTS: PRIntervalTime
+** OUTPUTS: void
+** RETURN: PRUint32
+**
+** SIDE EFFECTS:
+** None
+** RESTRICTIONS:
+** Conversion may cause overflow, which is not reported.
+** MEMORY: N/A
+** ALGORITHM: N/A
+***********************************************************************/
+NSPR_API(PRUint32) PR_IntervalToSeconds(PRIntervalTime ticks);
+NSPR_API(PRUint32) PR_IntervalToMilliseconds(PRIntervalTime ticks);
+NSPR_API(PRUint32) PR_IntervalToMicroseconds(PRIntervalTime ticks);
+
+PR_END_EXTERN_C
+
+
+#endif /* !defined(prinrval_h) */
+
+/* prinrval.h */
diff --git a/nsprpub/pr/include/prio.h b/nsprpub/pr/include/prio.h
new file mode 100644
index 0000000000..8b331a324c
--- /dev/null
+++ b/nsprpub/pr/include/prio.h
@@ -0,0 +1,2016 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: prio.h
+ *
+ * Description: PR i/o related stuff, such as file system access, file
+ * i/o, socket i/o, etc.
+ */
+
+#ifndef prio_h___
+#define prio_h___
+
+#include "prlong.h"
+#include "prtime.h"
+#include "prinrval.h"
+#include "prinet.h"
+
+PR_BEGIN_EXTERN_C
+
+/* Typedefs */
+typedef struct PRDir PRDir;
+typedef struct PRDirEntry PRDirEntry;
+#ifdef MOZ_UNICODE
+typedef struct PRDirUTF16 PRDirUTF16;
+typedef struct PRDirEntryUTF16 PRDirEntryUTF16;
+#endif /* MOZ_UNICODE */
+typedef struct PRFileDesc PRFileDesc;
+typedef struct PRFileInfo PRFileInfo;
+typedef struct PRFileInfo64 PRFileInfo64;
+typedef union PRNetAddr PRNetAddr;
+typedef struct PRIOMethods PRIOMethods;
+typedef struct PRPollDesc PRPollDesc;
+typedef struct PRFilePrivate PRFilePrivate;
+typedef struct PRSendFileData PRSendFileData;
+
+/*
+***************************************************************************
+** The file descriptor.
+** This is the primary structure to represent any active open socket,
+** whether it be a normal file or a network connection. Such objects
+** are stackable (or layerable). Each layer may have its own set of
+** method pointers and context private to that layer. All each layer
+** knows about its neighbors is how to get to their method table.
+***************************************************************************
+*/
+
+typedef PRIntn PRDescIdentity; /* see: Layering file descriptors */
+
+struct PRFileDesc {
+ const PRIOMethods *methods; /* the I/O methods table */
+ PRFilePrivate *secret; /* layer dependent data */
+ PRFileDesc *lower, *higher; /* pointers to adjacent layers */
+ void (PR_CALLBACK *dtor)(PRFileDesc *fd);
+ /* A destructor function for layer */
+ PRDescIdentity identity; /* Identity of this particular layer */
+};
+
+/*
+***************************************************************************
+** PRTransmitFileFlags
+**
+** Flags for PR_TransmitFile. Pass PR_TRANSMITFILE_CLOSE_SOCKET to
+** PR_TransmitFile if the connection should be closed after the file
+** is transmitted.
+***************************************************************************
+*/
+typedef enum PRTransmitFileFlags {
+ PR_TRANSMITFILE_KEEP_OPEN = 0, /* socket is left open after file
+ * is transmitted. */
+ PR_TRANSMITFILE_CLOSE_SOCKET = 1 /* socket is closed after file
+ * is transmitted. */
+} PRTransmitFileFlags;
+
+/*
+**************************************************************************
+** Macros for PRNetAddr
+**
+** Address families: PR_AF_INET, PR_AF_INET6, PR_AF_LOCAL
+** IP addresses: PR_INADDR_ANY, PR_INADDR_LOOPBACK, PR_INADDR_BROADCAST
+**************************************************************************
+*/
+
+#ifdef WIN32
+
+#define PR_AF_INET 2
+#define PR_AF_LOCAL 1
+#define PR_INADDR_ANY (unsigned long)0x00000000
+#define PR_INADDR_LOOPBACK 0x7f000001
+#define PR_INADDR_BROADCAST (unsigned long)0xffffffff
+
+#else /* WIN32 */
+
+#define PR_AF_INET AF_INET
+#define PR_AF_LOCAL AF_UNIX
+#define PR_INADDR_ANY INADDR_ANY
+#define PR_INADDR_LOOPBACK INADDR_LOOPBACK
+#define PR_INADDR_BROADCAST INADDR_BROADCAST
+
+#endif /* WIN32 */
+
+/*
+** Define PR_AF_INET6 in prcpucfg.h with the same
+** value as AF_INET6 on platforms with IPv6 support.
+** Otherwise define it here.
+*/
+#ifndef PR_AF_INET6
+#define PR_AF_INET6 100
+#endif
+
+#define PR_AF_INET_SDP 101
+#define PR_AF_INET6_SDP 102
+
+#ifndef PR_AF_UNSPEC
+#define PR_AF_UNSPEC 0
+#endif
+
+/*
+**************************************************************************
+** A network address
+**
+** Only Internet Protocol (IPv4 and IPv6) addresses are supported.
+** The address family must always represent IPv4 (AF_INET, probably == 2)
+** or IPv6 (AF_INET6).
+**************************************************************************
+*************************************************************************/
+
+struct PRIPv6Addr {
+ union {
+ PRUint8 _S6_u8[16];
+ PRUint16 _S6_u16[8];
+ PRUint32 _S6_u32[4];
+ PRUint64 _S6_u64[2];
+ } _S6_un;
+};
+#define pr_s6_addr _S6_un._S6_u8
+#define pr_s6_addr16 _S6_un._S6_u16
+#define pr_s6_addr32 _S6_un._S6_u32
+#define pr_s6_addr64 _S6_un._S6_u64
+
+typedef struct PRIPv6Addr PRIPv6Addr;
+
+union PRNetAddr {
+ struct {
+ PRUint16 family; /* address family (0x00ff maskable) */
+ char data[14]; /* raw address data */
+ } raw;
+ struct {
+ PRUint16 family; /* address family (AF_INET) */
+ PRUint16 port; /* port number */
+ PRUint32 ip; /* The actual 32 bits of address */
+ char pad[8];
+ } inet;
+ struct {
+ PRUint16 family; /* address family (AF_INET6) */
+ PRUint16 port; /* port number */
+ PRUint32 flowinfo; /* routing information */
+ PRIPv6Addr ip; /* the actual 128 bits of address */
+ PRUint32 scope_id; /* set of interfaces for a scope */
+ } ipv6;
+#if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_WIN)
+ struct { /* Unix domain socket address */
+ PRUint16 family; /* address family (AF_UNIX) */
+#ifdef XP_OS2
+ char path[108]; /* null-terminated pathname */
+ /* bind fails if size is not 108. */
+#else
+ char path[104]; /* null-terminated pathname */
+#endif
+ } local;
+#endif
+};
+
+/*
+***************************************************************************
+** PRSockOption
+**
+** The file descriptors can have predefined options set after they file
+** descriptor is created to change their behavior. Only the options in
+** the following enumeration are supported.
+***************************************************************************
+*/
+typedef enum PRSockOption
+{
+ PR_SockOpt_Nonblocking, /* nonblocking io */
+ PR_SockOpt_Linger, /* linger on close if data present */
+ PR_SockOpt_Reuseaddr, /* allow local address reuse */
+ PR_SockOpt_Keepalive, /* keep connections alive */
+ PR_SockOpt_RecvBufferSize, /* receive buffer size */
+ PR_SockOpt_SendBufferSize, /* send buffer size */
+
+ PR_SockOpt_IpTimeToLive, /* time to live */
+ PR_SockOpt_IpTypeOfService, /* type of service and precedence */
+
+ PR_SockOpt_AddMember, /* add an IP group membership */
+ PR_SockOpt_DropMember, /* drop an IP group membership */
+ PR_SockOpt_McastInterface, /* multicast interface address */
+ PR_SockOpt_McastTimeToLive, /* multicast timetolive */
+ PR_SockOpt_McastLoopback, /* multicast loopback */
+
+ PR_SockOpt_NoDelay, /* don't delay send to coalesce packets */
+ PR_SockOpt_MaxSegment, /* maximum segment size */
+ PR_SockOpt_Broadcast, /* enable broadcast */
+ PR_SockOpt_Reuseport, /* allow local address & port reuse on
+ * platforms that support it */
+ PR_SockOpt_DontFrag, /* Do not fragment flag */
+ PR_SockOpt_Last
+} PRSockOption;
+
+typedef struct PRLinger {
+ PRBool polarity; /* Polarity of the option's setting */
+ PRIntervalTime linger; /* Time to linger before closing */
+} PRLinger;
+
+typedef struct PRMcastRequest {
+ PRNetAddr mcaddr; /* IP multicast address of group */
+ PRNetAddr ifaddr; /* local IP address of interface */
+} PRMcastRequest;
+
+typedef struct PRSocketOptionData
+{
+ PRSockOption option;
+ union
+ {
+ PRUintn ip_ttl; /* IP time to live */
+ PRUintn mcast_ttl; /* IP multicast time to live */
+ PRUintn tos; /* IP type of service and precedence */
+ PRBool non_blocking; /* Non-blocking (network) I/O */
+ PRBool reuse_addr; /* Allow local address reuse */
+ PRBool reuse_port; /* Allow local address & port reuse on
+ * platforms that support it */
+ PRBool dont_fragment; /* Do not fragment flag */
+ PRBool keep_alive; /* Keep connections alive */
+ PRBool mcast_loopback; /* IP multicast loopback */
+ PRBool no_delay; /* Don't delay send to coalesce packets */
+ PRBool broadcast; /* Enable broadcast */
+ PRSize max_segment; /* Maximum segment size */
+ PRSize recv_buffer_size; /* Receive buffer size */
+ PRSize send_buffer_size; /* Send buffer size */
+ PRLinger linger; /* Time to linger on close if data present */
+ PRMcastRequest add_member; /* add an IP group membership */
+ PRMcastRequest drop_member; /* Drop an IP group membership */
+ PRNetAddr mcast_if; /* multicast interface address */
+ } value;
+} PRSocketOptionData;
+
+/*
+***************************************************************************
+** PRIOVec
+**
+** The I/O vector is used by the write vector method to describe the areas
+** that are affected by the ouput operation.
+***************************************************************************
+*/
+typedef struct PRIOVec {
+ char *iov_base;
+ int iov_len;
+} PRIOVec;
+
+/*
+***************************************************************************
+** Discover what type of socket is being described by the file descriptor.
+***************************************************************************
+*/
+typedef enum PRDescType
+{
+ PR_DESC_FILE = 1,
+ PR_DESC_SOCKET_TCP = 2,
+ PR_DESC_SOCKET_UDP = 3,
+ PR_DESC_LAYERED = 4,
+ PR_DESC_PIPE = 5
+} PRDescType;
+
+typedef enum PRSeekWhence {
+ PR_SEEK_SET = 0,
+ PR_SEEK_CUR = 1,
+ PR_SEEK_END = 2
+} PRSeekWhence;
+
+NSPR_API(PRDescType) PR_GetDescType(PRFileDesc *file);
+
+/*
+***************************************************************************
+** PRIOMethods
+**
+** The I/O methods table provides procedural access to the functions of
+** the file descriptor. It is the responsibility of a layer implementor
+** to provide suitable functions at every entry point. If a layer provides
+** no functionality, it should call the next lower(higher) function of the
+** same name (e.g., return fd->lower->method->close(fd->lower));
+**
+** Not all functions are implemented for all types of files. In cases where
+** that is true, the function will return a error indication with an error
+** code of PR_INVALID_METHOD_ERROR.
+***************************************************************************
+*/
+
+typedef PRStatus (PR_CALLBACK *PRCloseFN)(PRFileDesc *fd);
+typedef PRInt32 (PR_CALLBACK *PRReadFN)(PRFileDesc *fd, void *buf, PRInt32 amount);
+typedef PRInt32 (PR_CALLBACK *PRWriteFN)(PRFileDesc *fd, const void *buf, PRInt32 amount);
+typedef PRInt32 (PR_CALLBACK *PRAvailableFN)(PRFileDesc *fd);
+typedef PRInt64 (PR_CALLBACK *PRAvailable64FN)(PRFileDesc *fd);
+typedef PRStatus (PR_CALLBACK *PRFsyncFN)(PRFileDesc *fd);
+typedef PROffset32 (PR_CALLBACK *PRSeekFN)(PRFileDesc *fd, PROffset32 offset, PRSeekWhence how);
+typedef PROffset64 (PR_CALLBACK *PRSeek64FN)(PRFileDesc *fd, PROffset64 offset, PRSeekWhence how);
+typedef PRStatus (PR_CALLBACK *PRFileInfoFN)(PRFileDesc *fd, PRFileInfo *info);
+typedef PRStatus (PR_CALLBACK *PRFileInfo64FN)(PRFileDesc *fd, PRFileInfo64 *info);
+typedef PRInt32 (PR_CALLBACK *PRWritevFN)(
+ PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size,
+ PRIntervalTime timeout);
+typedef PRStatus (PR_CALLBACK *PRConnectFN)(
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout);
+typedef PRFileDesc* (PR_CALLBACK *PRAcceptFN) (
+ PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout);
+typedef PRStatus (PR_CALLBACK *PRBindFN)(PRFileDesc *fd, const PRNetAddr *addr);
+typedef PRStatus (PR_CALLBACK *PRListenFN)(PRFileDesc *fd, PRIntn backlog);
+typedef PRStatus (PR_CALLBACK *PRShutdownFN)(PRFileDesc *fd, PRIntn how);
+typedef PRInt32 (PR_CALLBACK *PRRecvFN)(
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+typedef PRInt32 (PR_CALLBACK *PRSendFN) (
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+typedef PRInt32 (PR_CALLBACK *PRRecvfromFN)(
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout);
+typedef PRInt32 (PR_CALLBACK *PRSendtoFN)(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout);
+typedef PRInt16 (PR_CALLBACK *PRPollFN)(
+ PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags);
+typedef PRInt32 (PR_CALLBACK *PRAcceptreadFN)(
+ PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
+ void *buf, PRInt32 amount, PRIntervalTime t);
+typedef PRInt32 (PR_CALLBACK *PRTransmitfileFN)(
+ PRFileDesc *sd, PRFileDesc *fd, const void *headers,
+ PRInt32 hlen, PRTransmitFileFlags flags, PRIntervalTime t);
+typedef PRStatus (PR_CALLBACK *PRGetsocknameFN)(PRFileDesc *fd, PRNetAddr *addr);
+typedef PRStatus (PR_CALLBACK *PRGetpeernameFN)(PRFileDesc *fd, PRNetAddr *addr);
+typedef PRStatus (PR_CALLBACK *PRGetsocketoptionFN)(
+ PRFileDesc *fd, PRSocketOptionData *data);
+typedef PRStatus (PR_CALLBACK *PRSetsocketoptionFN)(
+ PRFileDesc *fd, const PRSocketOptionData *data);
+typedef PRInt32 (PR_CALLBACK *PRSendfileFN)(
+ PRFileDesc *networkSocket, PRSendFileData *sendData,
+ PRTransmitFileFlags flags, PRIntervalTime timeout);
+typedef PRStatus (PR_CALLBACK *PRConnectcontinueFN)(
+ PRFileDesc *fd, PRInt16 out_flags);
+typedef PRIntn (PR_CALLBACK *PRReservedFN)(PRFileDesc *fd);
+
+struct PRIOMethods {
+ PRDescType file_type; /* Type of file represented (tos) */
+ PRCloseFN close; /* close file and destroy descriptor */
+ PRReadFN read; /* read up to specified bytes into buffer */
+ PRWriteFN write; /* write specified bytes from buffer */
+ PRAvailableFN available; /* determine number of bytes available */
+ PRAvailable64FN available64; /* ditto, 64 bit */
+ PRFsyncFN fsync; /* flush all buffers to permanent store */
+ PRSeekFN seek; /* position the file to the desired place */
+ PRSeek64FN seek64; /* ditto, 64 bit */
+ PRFileInfoFN fileInfo; /* Get information about an open file */
+ PRFileInfo64FN fileInfo64; /* ditto, 64 bit */
+ PRWritevFN writev; /* Write segments as described by iovector */
+ PRConnectFN connect; /* Connect to the specified (net) address */
+ PRAcceptFN accept; /* Accept a connection for a (net) peer */
+ PRBindFN bind; /* Associate a (net) address with the fd */
+ PRListenFN listen; /* Prepare to listen for (net) connections */
+ PRShutdownFN shutdown; /* Shutdown a (net) connection */
+ PRRecvFN recv; /* Solicit up the the specified bytes */
+ PRSendFN send; /* Send all the bytes specified */
+ PRRecvfromFN recvfrom; /* Solicit (net) bytes and report source */
+ PRSendtoFN sendto; /* Send bytes to (net) address specified */
+ PRPollFN poll; /* Test the fd to see if it is ready */
+ PRAcceptreadFN acceptread; /* Accept and read on a new (net) fd */
+ PRTransmitfileFN transmitfile; /* Transmit at entire file */
+ PRGetsocknameFN getsockname; /* Get (net) address associated with fd */
+ PRGetpeernameFN getpeername; /* Get peer's (net) address */
+ PRReservedFN reserved_fn_6; /* reserved for future use */
+ PRReservedFN reserved_fn_5; /* reserved for future use */
+ PRGetsocketoptionFN getsocketoption;
+ /* Get current setting of specified option */
+ PRSetsocketoptionFN setsocketoption;
+ /* Set value of specified option */
+ PRSendfileFN sendfile; /* Send a (partial) file with header/trailer*/
+ PRConnectcontinueFN connectcontinue;
+ /* Continue a nonblocking connect */
+ PRReservedFN reserved_fn_3; /* reserved for future use */
+ PRReservedFN reserved_fn_2; /* reserved for future use */
+ PRReservedFN reserved_fn_1; /* reserved for future use */
+ PRReservedFN reserved_fn_0; /* reserved for future use */
+};
+
+/*
+ **************************************************************************
+ * FUNCTION: PR_GetSpecialFD
+ * DESCRIPTION: Get the file descriptor that represents the standard input,
+ * output, or error stream.
+ * INPUTS:
+ * PRSpecialFD id
+ * A value indicating the type of stream desired:
+ * PR_StandardInput: standard input
+ * PR_StandardOuput: standard output
+ * PR_StandardError: standard error
+ * OUTPUTS: none
+ * RETURNS: PRFileDesc *
+ * If the argument is valid, PR_GetSpecialFD returns a file descriptor
+ * that represents the corresponding standard I/O stream. Otherwise,
+ * PR_GetSpecialFD returns NULL and sets error PR_INVALID_ARGUMENT_ERROR.
+ **************************************************************************
+ */
+
+typedef enum PRSpecialFD
+{
+ PR_StandardInput, /* standard input */
+ PR_StandardOutput, /* standard output */
+ PR_StandardError /* standard error */
+} PRSpecialFD;
+
+NSPR_API(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD id);
+
+#define PR_STDIN PR_GetSpecialFD(PR_StandardInput)
+#define PR_STDOUT PR_GetSpecialFD(PR_StandardOutput)
+#define PR_STDERR PR_GetSpecialFD(PR_StandardError)
+
+/*
+ **************************************************************************
+ * Layering file descriptors
+ *
+ * File descriptors may be layered. Each layer has it's own identity.
+ * Identities are allocated by the runtime and are to be associated
+ * (by the layer implementor) with all layers that are of that type.
+ * It is then possible to scan the chain of layers and find a layer
+ * that one recongizes and therefore predict that it will implement
+ * a desired protocol.
+ *
+ * There are three well-known identities:
+ * PR_INVALID_IO_LAYER => an invalid layer identity, for error return
+ * PR_TOP_IO_LAYER => the identity of the top of the stack
+ * PR_NSPR_IO_LAYER => the identity used by NSPR proper
+ * PR_TOP_IO_LAYER may be used as a shorthand for identifying the topmost
+ * layer of an existing stack. Ie., the following two constructs are
+ * equivalent.
+ *
+ * rv = PR_PushIOLayer(stack, PR_TOP_IO_LAYER, my_layer);
+ * rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), my_layer)
+ *
+ * A string may be associated with the creation of the identity. It
+ * will be copied by the runtime. If queried the runtime will return
+ * a reference to that copied string (not yet another copy). There
+ * is no facility for deleting an identity.
+ **************************************************************************
+ */
+
+#define PR_IO_LAYER_HEAD (PRDescIdentity)-3
+#define PR_INVALID_IO_LAYER (PRDescIdentity)-1
+#define PR_TOP_IO_LAYER (PRDescIdentity)-2
+#define PR_NSPR_IO_LAYER (PRDescIdentity)0
+
+NSPR_API(PRDescIdentity) PR_GetUniqueIdentity(const char *layer_name);
+NSPR_API(const char*) PR_GetNameForIdentity(PRDescIdentity ident);
+NSPR_API(PRDescIdentity) PR_GetLayersIdentity(PRFileDesc* fd);
+NSPR_API(PRFileDesc*) PR_GetIdentitiesLayer(PRFileDesc* fd_stack, PRDescIdentity id);
+
+/*
+ **************************************************************************
+ * PR_GetDefaultIOMethods: Accessing the default methods table.
+ * You may get a pointer to the default methods table by calling this function.
+ * You may then select any elements from that table with which to build your
+ * layer's methods table. You may NOT modify the table directly.
+ **************************************************************************
+ */
+NSPR_API(const PRIOMethods *) PR_GetDefaultIOMethods(void);
+
+/*
+ **************************************************************************
+ * Creating a layer
+ *
+ * A new layer may be allocated by calling PR_CreateIOLayerStub(). The
+ * file descriptor returned will contain the pointer to the methods table
+ * provided. The runtime will not modify the table nor test its correctness.
+ **************************************************************************
+ */
+NSPR_API(PRFileDesc*) PR_CreateIOLayerStub(
+ PRDescIdentity ident, const PRIOMethods *methods);
+
+/*
+ **************************************************************************
+ * Creating a layer
+ *
+ * A new stack may be created by calling PR_CreateIOLayer(). The
+ * file descriptor returned will point to the top of the stack, which has
+ * the layer 'fd' as the topmost layer.
+ *
+ * NOTE: This function creates a new style stack, which has a fixed, dummy
+ * header. The old style stack, created by a call to PR_PushIOLayer,
+ * results in modifying contents of the top layer of the stack, when
+ * pushing and popping layers of the stack.
+ **************************************************************************
+ */
+NSPR_API(PRFileDesc*) PR_CreateIOLayer(PRFileDesc* fd);
+
+/*
+ **************************************************************************
+ * Pushing a layer
+ *
+ * A file descriptor (perhaps allocated using PR_CreateIOLayerStub()) may
+ * be pushed into an existing stack of file descriptors at any point the
+ * caller deems appropriate. The new layer will be inserted into the stack
+ * just above the layer with the indicated identity.
+ *
+ * Note: Even if the identity parameter indicates the top-most layer of
+ * the stack, the value of the file descriptor describing the original
+ * stack will not change.
+ **************************************************************************
+ */
+NSPR_API(PRStatus) PR_PushIOLayer(
+ PRFileDesc *fd_stack, PRDescIdentity id, PRFileDesc *layer);
+
+/*
+ **************************************************************************
+ * Popping a layer
+ *
+ * A layer may be popped from a stack by indicating the identity of the
+ * layer to be removed. If found, a pointer to the removed object will
+ * be returned to the caller. The object then becomes the responsibility
+ * of the caller.
+ *
+ * Note: Even if the identity indicates the top layer of the stack, the
+ * reference returned will not be the file descriptor for the stack and
+ * that file descriptor will remain valid.
+ **************************************************************************
+ */
+NSPR_API(PRFileDesc*) PR_PopIOLayer(PRFileDesc *fd_stack, PRDescIdentity id);
+
+/*
+ **************************************************************************
+ * FUNCTION: PR_Open
+ * DESCRIPTION: Open a file for reading, writing, or both.
+ * INPUTS:
+ * const char *name
+ * The path name of the file to be opened
+ * PRIntn flags
+ * The file status flags.
+ * It is a bitwise OR of the following bit flags (only one of
+ * the first three flags below may be used):
+ * PR_RDONLY Open for reading only.
+ * PR_WRONLY Open for writing only.
+ * PR_RDWR Open for reading and writing.
+ * PR_CREATE_FILE If the file does not exist, the file is created
+ * If the file exists, this flag has no effect.
+ * PR_SYNC If set, each write will wait for both the file data
+ * and file status to be physically updated.
+ * PR_APPEND The file pointer is set to the end of
+ * the file prior to each write.
+ * PR_TRUNCATE If the file exists, its length is truncated to 0.
+ * PR_EXCL With PR_CREATE_FILE, if the file does not exist,
+ * the file is created. If the file already
+ * exists, no action and NULL is returned
+ *
+ * PRIntn mode
+ * The access permission bits of the file mode, if the file is
+ * created when PR_CREATE_FILE is on.
+ * OUTPUTS: None
+ * RETURNS: PRFileDesc *
+ * If the file is successfully opened,
+ * returns a pointer to the PRFileDesc
+ * created for the newly opened file.
+ * Returns a NULL pointer if the open
+ * failed.
+ * SIDE EFFECTS:
+ * RESTRICTIONS:
+ * MEMORY:
+ * The return value, if not NULL, points to a dynamically allocated
+ * PRFileDesc object.
+ * ALGORITHM:
+ **************************************************************************
+ */
+
+/* Open flags */
+#define PR_RDONLY 0x01
+#define PR_WRONLY 0x02
+#define PR_RDWR 0x04
+#define PR_CREATE_FILE 0x08
+#define PR_APPEND 0x10
+#define PR_TRUNCATE 0x20
+#define PR_SYNC 0x40
+#define PR_EXCL 0x80
+
+/*
+** File modes ....
+**
+** CAVEAT: 'mode' is currently only applicable on UNIX platforms.
+** The 'mode' argument may be ignored by PR_Open on other platforms.
+**
+** 00400 Read by owner.
+** 00200 Write by owner.
+** 00100 Execute (search if a directory) by owner.
+** 00040 Read by group.
+** 00020 Write by group.
+** 00010 Execute by group.
+** 00004 Read by others.
+** 00002 Write by others
+** 00001 Execute by others.
+**
+*/
+
+NSPR_API(PRFileDesc*) PR_Open(const char *name, PRIntn flags, PRIntn mode);
+
+/*
+ **************************************************************************
+ * FUNCTION: PR_OpenFile
+ * DESCRIPTION:
+ * Open a file for reading, writing, or both.
+ * PR_OpenFile has the same prototype as PR_Open but implements
+ * the specified file mode where possible.
+ **************************************************************************
+ */
+
+/* File mode bits */
+#define PR_IRWXU 00700 /* read, write, execute/search by owner */
+#define PR_IRUSR 00400 /* read permission, owner */
+#define PR_IWUSR 00200 /* write permission, owner */
+#define PR_IXUSR 00100 /* execute/search permission, owner */
+#define PR_IRWXG 00070 /* read, write, execute/search by group */
+#define PR_IRGRP 00040 /* read permission, group */
+#define PR_IWGRP 00020 /* write permission, group */
+#define PR_IXGRP 00010 /* execute/search permission, group */
+#define PR_IRWXO 00007 /* read, write, execute/search by others */
+#define PR_IROTH 00004 /* read permission, others */
+#define PR_IWOTH 00002 /* write permission, others */
+#define PR_IXOTH 00001 /* execute/search permission, others */
+
+NSPR_API(PRFileDesc*) PR_OpenFile(
+ const char *name, PRIntn flags, PRIntn mode);
+
+#ifdef MOZ_UNICODE
+/*
+ * EXPERIMENTAL: This function may be removed in a future release.
+ */
+NSPR_API(PRFileDesc*) PR_OpenFileUTF16(
+ const PRUnichar *name, PRIntn flags, PRIntn mode);
+#endif /* MOZ_UNICODE */
+
+/*
+ **************************************************************************
+ * FUNCTION: PR_Close
+ * DESCRIPTION:
+ * Close a file or socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * a pointer to a PRFileDesc.
+ * OUTPUTS:
+ * None.
+ * RETURN:
+ * PRStatus
+ * SIDE EFFECTS:
+ * RESTRICTIONS:
+ * None.
+ * MEMORY:
+ * The dynamic memory pointed to by the argument fd is freed.
+ **************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_Close(PRFileDesc *fd);
+
+/*
+ **************************************************************************
+ * FUNCTION: PR_Read
+ * DESCRIPTION:
+ * Read bytes from a file or socket.
+ * The operation will block until either an end of stream indication is
+ * encountered, some positive number of bytes are transferred, or there
+ * is an error. No more than 'amount' bytes will be transferred.
+ * INPUTS:
+ * PRFileDesc *fd
+ * pointer to the PRFileDesc object for the file or socket
+ * void *buf
+ * pointer to a buffer to hold the data read in.
+ * PRInt32 amount
+ * the size of 'buf' (in bytes)
+ * OUTPUTS:
+ * RETURN:
+ * PRInt32
+ * a positive number indicates the number of bytes actually read in.
+ * 0 means end of file is reached or the network connection is closed.
+ * -1 indicates a failure. The reason for the failure is obtained
+ * by calling PR_GetError().
+ * SIDE EFFECTS:
+ * data is written into the buffer pointed to by 'buf'.
+ * RESTRICTIONS:
+ * None.
+ * MEMORY:
+ * N/A
+ * ALGORITHM:
+ * N/A
+ **************************************************************************
+ */
+
+NSPR_API(PRInt32) PR_Read(PRFileDesc *fd, void *buf, PRInt32 amount);
+
+/*
+ ***************************************************************************
+ * FUNCTION: PR_Write
+ * DESCRIPTION:
+ * Write a specified number of bytes to a file or socket. The thread
+ * invoking this function blocks until all the data is written.
+ * INPUTS:
+ * PRFileDesc *fd
+ * pointer to a PRFileDesc object that refers to a file or socket
+ * const void *buf
+ * pointer to the buffer holding the data
+ * PRInt32 amount
+ * amount of data in bytes to be written from the buffer
+ * OUTPUTS:
+ * None.
+ * RETURN: PRInt32
+ * A positive number indicates the number of bytes successfully written.
+ * A -1 is an indication that the operation failed. The reason
+ * for the failure is obtained by calling PR_GetError().
+ ***************************************************************************
+ */
+
+NSPR_API(PRInt32) PR_Write(PRFileDesc *fd,const void *buf,PRInt32 amount);
+
+/*
+ ***************************************************************************
+ * FUNCTION: PR_Writev
+ * DESCRIPTION:
+ * Write data to a socket. The data is organized in a PRIOVec array. The
+ * operation will block until all the data is written or the operation
+ * fails.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Pointer that points to a PRFileDesc object for a socket.
+ * const PRIOVec *iov
+ * An array of PRIOVec. PRIOVec is a struct with the following
+ * two fields:
+ * char *iov_base;
+ * int iov_len;
+ * PRInt32 iov_size
+ * Number of elements in the iov array. The value of this
+ * argument must not be greater than PR_MAX_IOVECTOR_SIZE.
+ * If it is, the method will fail (PR_BUFFER_OVERFLOW_ERROR).
+ * PRIntervalTime timeout
+ * Time limit for completion of the entire write operation.
+ * OUTPUTS:
+ * None
+ * RETURN:
+ * A positive number indicates the number of bytes successfully written.
+ * A -1 is an indication that the operation failed. The reason
+ * for the failure is obtained by calling PR_GetError().
+ ***************************************************************************
+ */
+
+#define PR_MAX_IOVECTOR_SIZE 16 /* 'iov_size' must be <= */
+
+NSPR_API(PRInt32) PR_Writev(
+ PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size,
+ PRIntervalTime timeout);
+
+/*
+ ***************************************************************************
+ * FUNCTION: PR_Delete
+ * DESCRIPTION:
+ * Delete a file from the filesystem. The operation may fail if the
+ * file is open.
+ * INPUTS:
+ * const char *name
+ * Path name of the file to be deleted.
+ * OUTPUTS:
+ * None.
+ * RETURN: PRStatus
+ * The function returns PR_SUCCESS if the file is successfully
+ * deleted, otherwise it returns PR_FAILURE.
+ ***************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_Delete(const char *name);
+
+/**************************************************************************/
+
+typedef enum PRFileType
+{
+ PR_FILE_FILE = 1,
+ PR_FILE_DIRECTORY = 2,
+ PR_FILE_OTHER = 3
+} PRFileType;
+
+struct PRFileInfo {
+ PRFileType type; /* Type of file */
+ PROffset32 size; /* Size, in bytes, of file's contents */
+ PRTime creationTime; /* Creation time per definition of PRTime */
+ PRTime modifyTime; /* Last modification time per definition of PRTime */
+};
+
+struct PRFileInfo64 {
+ PRFileType type; /* Type of file */
+ PROffset64 size; /* Size, in bytes, of file's contents */
+ PRTime creationTime; /* Creation time per definition of PRTime */
+ PRTime modifyTime; /* Last modification time per definition of PRTime */
+};
+
+/****************************************************************************
+ * FUNCTION: PR_GetFileInfo, PR_GetFileInfo64
+ * DESCRIPTION:
+ * Get the information about the file with the given path name. This is
+ * applicable only to NSFileDesc describing 'file' types (see
+ * INPUTS:
+ * const char *fn
+ * path name of the file
+ * OUTPUTS:
+ * PRFileInfo *info
+ * Information about the given file is written into the file
+ * information object pointer to by 'info'.
+ * RETURN: PRStatus
+ * PR_GetFileInfo returns PR_SUCCESS if file information is successfully
+ * obtained, otherwise it returns PR_FAILURE.
+ ***************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_GetFileInfo(const char *fn, PRFileInfo *info);
+NSPR_API(PRStatus) PR_GetFileInfo64(const char *fn, PRFileInfo64 *info);
+
+#ifdef MOZ_UNICODE
+/*
+ * EXPERIMENTAL: This function may be removed in a future release.
+ */
+NSPR_API(PRStatus) PR_GetFileInfo64UTF16(const PRUnichar *fn, PRFileInfo64 *info);
+#endif /* MOZ_UNICODE */
+
+/*
+ **************************************************************************
+ * FUNCTION: PR_GetOpenFileInfo, PR_GetOpenFileInfo64
+ * DESCRIPTION:
+ * Get information about an open file referred to by the
+ * given PRFileDesc object.
+ * INPUTS:
+ * const PRFileDesc *fd
+ * A reference to a valid, open file.
+ * OUTPUTS:
+ * Same as PR_GetFileInfo, PR_GetFileInfo64
+ * RETURN: PRStatus
+ * PR_GetFileInfo returns PR_SUCCESS if file information is successfully
+ * obtained, otherwise it returns PR_FAILURE.
+ ***************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_GetOpenFileInfo(PRFileDesc *fd, PRFileInfo *info);
+NSPR_API(PRStatus) PR_GetOpenFileInfo64(PRFileDesc *fd, PRFileInfo64 *info);
+
+/*
+ **************************************************************************
+ * FUNCTION: PR_Rename
+ * DESCRIPTION:
+ * Rename a file from the old name 'from' to the new name 'to'.
+ * INPUTS:
+ * const char *from
+ * The old name of the file to be renamed.
+ * const char *to
+ * The new name of the file.
+ * OUTPUTS:
+ * None.
+ * RETURN: PRStatus
+ **************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_Rename(const char *from, const char *to);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Access
+ * DESCRIPTION:
+ * Determine accessibility of a file.
+ * INPUTS:
+ * const char *name
+ * path name of the file
+ * PRAccessHow how
+ * specifies which access permission to check for.
+ * It can be one of the following values:
+ * PR_ACCESS_READ_OK Test for read permission
+ * PR_ACCESS_WRITE_OK Test for write permission
+ * PR_ACCESS_EXISTS Check existence of file
+ * OUTPUTS:
+ * None.
+ * RETURN: PRStatus
+ * PR_SUCCESS is returned if the requested access is permitted.
+ * Otherwise, PR_FAILURE is returned. Additional information
+ * regarding the reason for the failure may be retrieved from
+ * PR_GetError().
+ *************************************************************************
+ */
+
+typedef enum PRAccessHow {
+ PR_ACCESS_EXISTS = 1,
+ PR_ACCESS_WRITE_OK = 2,
+ PR_ACCESS_READ_OK = 3
+} PRAccessHow;
+
+NSPR_API(PRStatus) PR_Access(const char *name, PRAccessHow how);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Seek, PR_Seek64
+ * DESCRIPTION:
+ * Moves read-write file offset
+ * INPUTS:
+ * PRFileDesc *fd
+ * Pointer to a PRFileDesc object.
+ * PROffset32, PROffset64 offset
+ * Specifies a value, in bytes, that is used in conjunction
+ * with the 'whence' parameter to set the file pointer. A
+ * negative value causes seeking in the reverse direction.
+ * PRSeekWhence whence
+ * Specifies how to interpret the 'offset' parameter in setting
+ * the file pointer associated with the 'fd' parameter.
+ * Values for the 'whence' parameter are:
+ * PR_SEEK_SET Sets the file pointer to the value of the
+ * 'offset' parameter
+ * PR_SEEK_CUR Sets the file pointer to its current location
+ * plus the value of the offset parameter.
+ * PR_SEEK_END Sets the file pointer to the size of the
+ * file plus the value of the offset parameter.
+ * OUTPUTS:
+ * None.
+ * RETURN: PROffset32, PROffset64
+ * Upon successful completion, the resulting pointer location,
+ * measured in bytes from the beginning of the file, is returned.
+ * If the PR_Seek() function fails, the file offset remains
+ * unchanged, and the returned value is -1. The error code can
+ * then be retrieved via PR_GetError().
+ *************************************************************************
+ */
+
+NSPR_API(PROffset32) PR_Seek(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence);
+NSPR_API(PROffset64) PR_Seek64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence);
+
+/*
+ ************************************************************************
+ * FUNCTION: PR_Available
+ * DESCRIPTION:
+ * Determine the amount of data in bytes available for reading
+ * in the given file or socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Pointer to a PRFileDesc object that refers to a file or
+ * socket.
+ * OUTPUTS:
+ * None
+ * RETURN: PRInt32, PRInt64
+ * Upon successful completion, PR_Available returns the number of
+ * bytes beyond the current read pointer that is available for
+ * reading. Otherwise, it returns a -1 and the reason for the
+ * failure can be retrieved via PR_GetError().
+ ************************************************************************
+ */
+
+NSPR_API(PRInt32) PR_Available(PRFileDesc *fd);
+NSPR_API(PRInt64) PR_Available64(PRFileDesc *fd);
+
+/*
+ ************************************************************************
+ * FUNCTION: PR_Sync
+ * DESCRIPTION:
+ * Sync any buffered data for a fd to its backing device (disk).
+ * INPUTS:
+ * PRFileDesc *fd
+ * Pointer to a PRFileDesc object that refers to a file or
+ * socket
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * PR_SUCCESS is returned if the requested access is permitted.
+ * Otherwise, PR_FAILURE is returned.
+ ************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_Sync(PRFileDesc *fd);
+
+/************************************************************************/
+
+struct PRDirEntry {
+ const char *name; /* name of entry, relative to directory name */
+};
+
+#ifdef MOZ_UNICODE
+struct PRDirEntryUTF16 {
+ const PRUnichar *name; /* name of entry in UTF16, relative to
+ * directory name */
+};
+#endif /* MOZ_UNICODE */
+
+#if !defined(NO_NSPR_10_SUPPORT)
+#define PR_DirName(dirEntry) (dirEntry->name)
+#endif
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_OpenDir
+ * DESCRIPTION:
+ * Open the directory by the given name
+ * INPUTS:
+ * const char *name
+ * path name of the directory to be opened
+ * OUTPUTS:
+ * None
+ * RETURN: PRDir *
+ * If the directory is sucessfully opened, a PRDir object is
+ * dynamically allocated and a pointer to it is returned.
+ * If the directory cannot be opened, a NULL pointer is returned.
+ * MEMORY:
+ * Upon successful completion, the return value points to
+ * dynamically allocated memory.
+ *************************************************************************
+ */
+
+NSPR_API(PRDir*) PR_OpenDir(const char *name);
+
+#ifdef MOZ_UNICODE
+/*
+ * EXPERIMENTAL: This function may be removed in a future release.
+ */
+NSPR_API(PRDirUTF16*) PR_OpenDirUTF16(const PRUnichar *name);
+#endif /* MOZ_UNICODE */
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_ReadDir
+ * DESCRIPTION:
+ * INPUTS:
+ * PRDir *dir
+ * pointer to a PRDir object that designates an open directory
+ * PRDirFlags flags
+ * PR_SKIP_NONE Do not skip any files
+ * PR_SKIP_DOT Skip the directory entry "." that
+ * represents the current directory
+ * PR_SKIP_DOT_DOT Skip the directory entry ".." that
+ * represents the parent directory.
+ * PR_SKIP_BOTH Skip both '.' and '..'
+ * PR_SKIP_HIDDEN Skip hidden files
+ * OUTPUTS:
+ * RETURN: PRDirEntry*
+ * Returns a pointer to the next entry in the directory. Returns
+ * a NULL pointer upon reaching the end of the directory or when an
+ * error occurs. The actual reason can be retrieved via PR_GetError().
+ *************************************************************************
+ */
+
+typedef enum PRDirFlags {
+ PR_SKIP_NONE = 0x0,
+ PR_SKIP_DOT = 0x1,
+ PR_SKIP_DOT_DOT = 0x2,
+ PR_SKIP_BOTH = 0x3,
+ PR_SKIP_HIDDEN = 0x4
+} PRDirFlags;
+
+NSPR_API(PRDirEntry*) PR_ReadDir(PRDir *dir, PRDirFlags flags);
+
+#ifdef MOZ_UNICODE
+/*
+ * EXPERIMENTAL: This function may be removed in a future release.
+ */
+NSPR_API(PRDirEntryUTF16*) PR_ReadDirUTF16(PRDirUTF16 *dir, PRDirFlags flags);
+#endif /* MOZ_UNICODE */
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_CloseDir
+ * DESCRIPTION:
+ * Close the specified directory.
+ * INPUTS:
+ * PRDir *dir
+ * The directory to be closed.
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * If successful, will return a status of PR_SUCCESS. Otherwise
+ * a value of PR_FAILURE. The reason for the failure may be re-
+ * trieved using PR_GetError().
+ *************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_CloseDir(PRDir *dir);
+
+#ifdef MOZ_UNICODE
+/*
+ * EXPERIMENTAL: This function may be removed in a future release.
+ */
+NSPR_API(PRStatus) PR_CloseDirUTF16(PRDirUTF16 *dir);
+#endif /* MOZ_UNICODE */
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_MkDir
+ * DESCRIPTION:
+ * Create a new directory with the given name and access mode.
+ * INPUTS:
+ * const char *name
+ * The name of the directory to be created. All the path components
+ * up to but not including the leaf component must already exist.
+ * PRIntn mode
+ * See 'mode' definiton in PR_Open().
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * If successful, will return a status of PR_SUCCESS. Otherwise
+ * a value of PR_FAILURE. The reason for the failure may be re-
+ * trieved using PR_GetError().
+ *************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_MkDir(const char *name, PRIntn mode);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_MakeDir
+ * DESCRIPTION:
+ * Create a new directory with the given name and access mode.
+ * PR_MakeDir has the same prototype as PR_MkDir but implements
+ * the specified access mode where possible.
+ *************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_MakeDir(const char *name, PRIntn mode);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_RmDir
+ * DESCRIPTION:
+ * Remove a directory by the given name.
+ * INPUTS:
+ * const char *name
+ * The name of the directory to be removed. All the path components
+ * must already exist. Only the leaf component will be removed.
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * If successful, will return a status of PR_SUCCESS. Otherwise
+ * a value of PR_FAILURE. The reason for the failure may be re-
+ * trieved using PR_GetError().
+ **************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_RmDir(const char *name);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_NewUDPSocket
+ * DESCRIPTION:
+ * Create a new UDP socket.
+ * INPUTS:
+ * None
+ * OUTPUTS:
+ * None
+ * RETURN: PRFileDesc*
+ * Upon successful completion, PR_NewUDPSocket returns a pointer
+ * to the PRFileDesc created for the newly opened UDP socket.
+ * Returns a NULL pointer if the creation of a new UDP socket failed.
+ *
+ **************************************************************************
+ */
+
+NSPR_API(PRFileDesc*) PR_NewUDPSocket(void);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_NewTCPSocket
+ * DESCRIPTION:
+ * Create a new TCP socket.
+ * INPUTS:
+ * None
+ * OUTPUTS:
+ * None
+ * RETURN: PRFileDesc*
+ * Upon successful completion, PR_NewTCPSocket returns a pointer
+ * to the PRFileDesc created for the newly opened TCP socket.
+ * Returns a NULL pointer if the creation of a new TCP socket failed.
+ *
+ **************************************************************************
+ */
+
+NSPR_API(PRFileDesc*) PR_NewTCPSocket(void);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_OpenUDPSocket
+ * DESCRIPTION:
+ * Create a new UDP socket of the specified address family.
+ * INPUTS:
+ * PRIntn af
+ * Address family
+ * OUTPUTS:
+ * None
+ * RETURN: PRFileDesc*
+ * Upon successful completion, PR_OpenUDPSocket returns a pointer
+ * to the PRFileDesc created for the newly opened UDP socket.
+ * Returns a NULL pointer if the creation of a new UDP socket failed.
+ *
+ **************************************************************************
+ */
+
+NSPR_API(PRFileDesc*) PR_OpenUDPSocket(PRIntn af);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_OpenTCPSocket
+ * DESCRIPTION:
+ * Create a new TCP socket of the specified address family.
+ * INPUTS:
+ * PRIntn af
+ * Address family
+ * OUTPUTS:
+ * None
+ * RETURN: PRFileDesc*
+ * Upon successful completion, PR_NewTCPSocket returns a pointer
+ * to the PRFileDesc created for the newly opened TCP socket.
+ * Returns a NULL pointer if the creation of a new TCP socket failed.
+ *
+ **************************************************************************
+ */
+
+NSPR_API(PRFileDesc*) PR_OpenTCPSocket(PRIntn af);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Connect
+ * DESCRIPTION:
+ * Initiate a connection on a socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Points to a PRFileDesc object representing a socket
+ * PRNetAddr *addr
+ * Specifies the address of the socket in its own communication
+ * space.
+ * PRIntervalTime timeout
+ * The function uses the lesser of the provided timeout and
+ * the OS's connect timeout. In particular, if you specify
+ * PR_INTERVAL_NO_TIMEOUT as the timeout, the OS's connection
+ * time limit will be used.
+ *
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * Upon successful completion of connection initiation, PR_Connect
+ * returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further
+ * failure information can be obtained by calling PR_GetError().
+ **************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_Connect(
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_ConnectContinue
+ * DESCRIPTION:
+ * Continue a nonblocking connect. After a nonblocking connect
+ * is initiated with PR_Connect() (which fails with
+ * PR_IN_PROGRESS_ERROR), one should call PR_Poll() on the socket,
+ * with the in_flags PR_POLL_WRITE | PR_POLL_EXCEPT. When
+ * PR_Poll() returns, one calls PR_ConnectContinue() on the
+ * socket to determine whether the nonblocking connect has
+ * completed or is still in progress. Repeat the PR_Poll(),
+ * PR_ConnectContinue() sequence until the nonblocking connect
+ * has completed.
+ * INPUTS:
+ * PRFileDesc *fd
+ * the file descriptor representing a socket
+ * PRInt16 out_flags
+ * the out_flags field of the poll descriptor returned by
+ * PR_Poll()
+ * RETURN: PRStatus
+ * If the nonblocking connect has successfully completed,
+ * PR_ConnectContinue returns PR_SUCCESS. If PR_ConnectContinue()
+ * returns PR_FAILURE, call PR_GetError():
+ * - PR_IN_PROGRESS_ERROR: the nonblocking connect is still in
+ * progress and has not completed yet. The caller should poll
+ * on the file descriptor for the in_flags
+ * PR_POLL_WRITE|PR_POLL_EXCEPT and retry PR_ConnectContinue
+ * later when PR_Poll() returns.
+ * - Other errors: the nonblocking connect has failed with this
+ * error code.
+ */
+
+NSPR_API(PRStatus) PR_ConnectContinue(PRFileDesc *fd, PRInt16 out_flags);
+
+/*
+ *************************************************************************
+ * THIS FUNCTION IS DEPRECATED. USE PR_ConnectContinue INSTEAD.
+ *
+ * FUNCTION: PR_GetConnectStatus
+ * DESCRIPTION:
+ * Get the completion status of a nonblocking connect. After
+ * a nonblocking connect is initiated with PR_Connect() (which
+ * fails with PR_IN_PROGRESS_ERROR), one should call PR_Poll()
+ * on the socket, with the in_flags PR_POLL_WRITE | PR_POLL_EXCEPT.
+ * When PR_Poll() returns, one calls PR_GetConnectStatus on the
+ * PRPollDesc structure to determine whether the nonblocking
+ * connect has succeeded or failed.
+ * INPUTS:
+ * const PRPollDesc *pd
+ * Pointer to a PRPollDesc whose fd member is the socket,
+ * and in_flags must contain PR_POLL_WRITE and PR_POLL_EXCEPT.
+ * PR_Poll() should have been called and set the out_flags.
+ * RETURN: PRStatus
+ * If the nonblocking connect has successfully completed,
+ * PR_GetConnectStatus returns PR_SUCCESS. If PR_GetConnectStatus()
+ * returns PR_FAILURE, call PR_GetError():
+ * - PR_IN_PROGRESS_ERROR: the nonblocking connect is still in
+ * progress and has not completed yet.
+ * - Other errors: the nonblocking connect has failed with this
+ * error code.
+ */
+
+NSPR_API(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Accept
+ * DESCRIPTION:
+ * Accept a connection on a socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Points to a PRFileDesc object representing the rendezvous socket
+ * on which the caller is willing to accept new connections.
+ * PRIntervalTime timeout
+ * Time limit for completion of the accept operation.
+ * OUTPUTS:
+ * PRNetAddr *addr
+ * Returns the address of the connecting entity in its own
+ * communication space. It may be NULL.
+ * RETURN: PRFileDesc*
+ * Upon successful acceptance of a connection, PR_Accept
+ * returns a valid file descriptor. Otherwise, it returns NULL.
+ * Further failure information can be obtained by calling PR_GetError().
+ **************************************************************************
+ */
+
+NSPR_API(PRFileDesc*) PR_Accept(
+ PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Bind
+ * DESCRIPTION:
+ * Bind an address to a socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Points to a PRFileDesc object representing a socket.
+ * PRNetAddr *addr
+ * Specifies the address to which the socket will be bound.
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * Upon successful binding of an address to a socket, PR_Bind
+ * returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further
+ * failure information can be obtained by calling PR_GetError().
+ **************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_Bind(PRFileDesc *fd, const PRNetAddr *addr);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Listen
+ * DESCRIPTION:
+ * Listen for connections on a socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Points to a PRFileDesc object representing a socket that will be
+ * used to listen for new connections.
+ * PRIntn backlog
+ * Specifies the maximum length of the queue of pending connections.
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * Upon successful completion of listen request, PR_Listen
+ * returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further
+ * failure information can be obtained by calling PR_GetError().
+ **************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_Listen(PRFileDesc *fd, PRIntn backlog);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Shutdown
+ * DESCRIPTION:
+ * Shut down part of a full-duplex connection on a socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Points to a PRFileDesc object representing a connected socket.
+ * PRIntn how
+ * Specifies the kind of disallowed operations on the socket.
+ * PR_SHUTDOWN_RCV - Further receives will be disallowed
+ * PR_SHUTDOWN_SEND - Further sends will be disallowed
+ * PR_SHUTDOWN_BOTH - Further sends and receives will be disallowed
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * Upon successful completion of shutdown request, PR_Shutdown
+ * returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further
+ * failure information can be obtained by calling PR_GetError().
+ **************************************************************************
+ */
+
+typedef enum PRShutdownHow
+{
+ PR_SHUTDOWN_RCV = 0, /* disallow further receives */
+ PR_SHUTDOWN_SEND = 1, /* disallow further sends */
+ PR_SHUTDOWN_BOTH = 2 /* disallow further receives and sends */
+} PRShutdownHow;
+
+NSPR_API(PRStatus) PR_Shutdown(PRFileDesc *fd, PRShutdownHow how);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Recv
+ * DESCRIPTION:
+ * Receive a specified number of bytes from a connected socket.
+ * The operation will block until some positive number of bytes are
+ * transferred, a time out has occurred, or there is an error.
+ * No more than 'amount' bytes will be transferred.
+ * INPUTS:
+ * PRFileDesc *fd
+ * points to a PRFileDesc object representing a socket.
+ * void *buf
+ * pointer to a buffer to hold the data received.
+ * PRInt32 amount
+ * the size of 'buf' (in bytes)
+ * PRIntn flags
+ * must be zero or PR_MSG_PEEK.
+ * PRIntervalTime timeout
+ * Time limit for completion of the receive operation.
+ * OUTPUTS:
+ * None
+ * RETURN: PRInt32
+ * a positive number indicates the number of bytes actually received.
+ * 0 means the network connection is closed.
+ * -1 indicates a failure. The reason for the failure is obtained
+ * by calling PR_GetError().
+ **************************************************************************
+ */
+
+#define PR_MSG_PEEK 0x2
+
+NSPR_API(PRInt32) PR_Recv(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Send
+ * DESCRIPTION:
+ * Send a specified number of bytes from a connected socket.
+ * The operation will block until all bytes are
+ * processed, a time out has occurred, or there is an error.
+ * INPUTS:
+ * PRFileDesc *fd
+ * points to a PRFileDesc object representing a socket.
+ * void *buf
+ * pointer to a buffer from where the data is sent.
+ * PRInt32 amount
+ * the size of 'buf' (in bytes)
+ * PRIntn flags
+ * (OBSOLETE - must always be zero)
+ * PRIntervalTime timeout
+ * Time limit for completion of the send operation.
+ * OUTPUTS:
+ * None
+ * RETURN: PRInt32
+ * A positive number indicates the number of bytes successfully processed.
+ * This number must always equal 'amount'. A -1 is an indication that the
+ * operation failed. The reason for the failure is obtained by calling
+ * PR_GetError().
+ **************************************************************************
+ */
+
+NSPR_API(PRInt32) PR_Send(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_RecvFrom
+ * DESCRIPTION:
+ * Receive up to a specified number of bytes from socket which may
+ * or may not be connected.
+ * The operation will block until one or more bytes are
+ * transferred, a time out has occurred, or there is an error.
+ * No more than 'amount' bytes will be transferred.
+ * INPUTS:
+ * PRFileDesc *fd
+ * points to a PRFileDesc object representing a socket.
+ * void *buf
+ * pointer to a buffer to hold the data received.
+ * PRInt32 amount
+ * the size of 'buf' (in bytes)
+ * PRIntn flags
+ * (OBSOLETE - must always be zero)
+ * PRNetAddr *addr
+ * Specifies the address of the sending peer. It may be NULL.
+ * PRIntervalTime timeout
+ * Time limit for completion of the receive operation.
+ * OUTPUTS:
+ * None
+ * RETURN: PRInt32
+ * a positive number indicates the number of bytes actually received.
+ * 0 means the network connection is closed.
+ * -1 indicates a failure. The reason for the failure is obtained
+ * by calling PR_GetError().
+ **************************************************************************
+ */
+
+NSPR_API(PRInt32) PR_RecvFrom(
+ PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRIntervalTime timeout);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_SendTo
+ * DESCRIPTION:
+ * Send a specified number of bytes from an unconnected socket.
+ * The operation will block until all bytes are
+ * sent, a time out has occurred, or there is an error.
+ * INPUTS:
+ * PRFileDesc *fd
+ * points to a PRFileDesc object representing an unconnected socket.
+ * void *buf
+ * pointer to a buffer from where the data is sent.
+ * PRInt32 amount
+ * the size of 'buf' (in bytes)
+ * PRIntn flags
+ * (OBSOLETE - must always be zero)
+ * PRNetAddr *addr
+ * Specifies the address of the peer.
+.* PRIntervalTime timeout
+ * Time limit for completion of the send operation.
+ * OUTPUTS:
+ * None
+ * RETURN: PRInt32
+ * A positive number indicates the number of bytes successfully sent.
+ * -1 indicates a failure. The reason for the failure is obtained
+ * by calling PR_GetError().
+ **************************************************************************
+ */
+
+NSPR_API(PRInt32) PR_SendTo(
+ PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRIntervalTime timeout);
+
+/*
+*************************************************************************
+** FUNCTION: PR_TransmitFile
+** DESCRIPTION:
+** Transmitfile sends a complete file (sourceFile) across a socket
+** (networkSocket). If headers is non-NULL, the headers will be sent across
+** the socket prior to sending the file.
+**
+** Optionally, the PR_TRANSMITFILE_CLOSE_SOCKET flag may be passed to
+** transmitfile. This flag specifies that transmitfile should close the
+** socket after sending the data.
+**
+** INPUTS:
+** PRFileDesc *networkSocket
+** The socket to send data over
+** PRFileDesc *sourceFile
+** The file to send
+** const void *headers
+** A pointer to headers to be sent before sending data
+** PRInt32 hlen
+** length of header buffers in bytes.
+** PRTransmitFileFlags flags
+** If the flags indicate that the connection should be closed,
+** it will be done immediately after transferring the file, unless
+** the operation is unsuccessful.
+.* PRIntervalTime timeout
+ * Time limit for completion of the transmit operation.
+**
+** RETURNS:
+** Returns the number of bytes written or -1 if the operation failed.
+** If an error occurs while sending the file, the PR_TRANSMITFILE_CLOSE_
+** SOCKET flag is ignored. The reason for the failure is obtained
+** by calling PR_GetError().
+**************************************************************************
+*/
+
+NSPR_API(PRInt32) PR_TransmitFile(
+ PRFileDesc *networkSocket, PRFileDesc *sourceFile,
+ const void *headers, PRInt32 hlen, PRTransmitFileFlags flags,
+ PRIntervalTime timeout);
+
+/*
+*************************************************************************
+** FUNCTION: PR_SendFile
+** DESCRIPTION:
+** PR_SendFile sends data from a file (sendData->fd) across a socket
+** (networkSocket). If specified, a header and/or trailer buffer are sent
+** before and after the file, respectively. The file offset, number of bytes
+** of file data to send, the header and trailer buffers are specified in the
+** sendData argument.
+**
+** Optionally, if the PR_TRANSMITFILE_CLOSE_SOCKET flag is passed, the
+** socket is closed after successfully sending the data.
+**
+** INPUTS:
+** PRFileDesc *networkSocket
+** The socket to send data over
+** PRSendFileData *sendData
+** Contains the FD, file offset and length, header and trailer
+** buffer specifications.
+** PRTransmitFileFlags flags
+** If the flags indicate that the connection should be closed,
+** it will be done immediately after transferring the file, unless
+** the operation is unsuccessful.
+.* PRIntervalTime timeout
+ * Time limit for completion of the send operation.
+**
+** RETURNS:
+** Returns the number of bytes written or -1 if the operation failed.
+** If an error occurs while sending the file, the PR_TRANSMITFILE_CLOSE_
+** SOCKET flag is ignored. The reason for the failure is obtained
+** by calling PR_GetError().
+**************************************************************************
+*/
+
+struct PRSendFileData {
+ PRFileDesc *fd; /* file to send */
+ PRUint32 file_offset; /* file offset */
+ PRSize file_nbytes; /* number of bytes of file data to send */
+ /* if 0, send data from file_offset to */
+ /* end-of-file. */
+ const void *header; /* header buffer */
+ PRInt32 hlen; /* header len */
+ const void *trailer; /* trailer buffer */
+ PRInt32 tlen; /* trailer len */
+};
+
+
+NSPR_API(PRInt32) PR_SendFile(
+ PRFileDesc *networkSocket, PRSendFileData *sendData,
+ PRTransmitFileFlags flags, PRIntervalTime timeout);
+
+/*
+*************************************************************************
+** FUNCTION: PR_AcceptRead
+** DESCRIPTION:
+** AcceptRead accepts a new connection, returns the newly created
+** socket's descriptor and also returns the connecting peer's address.
+** AcceptRead, as its name suggests, also receives the first block of data
+** sent by the peer.
+**
+** INPUTS:
+** PRFileDesc *listenSock
+** A socket descriptor that has been called with the PR_Listen()
+** function, also known as the rendezvous socket.
+** void *buf
+** A pointer to a buffer to receive data sent by the client. This
+** buffer must be large enough to receive <amount> bytes of data
+** and two PRNetAddr structures, plus an extra 32 bytes. See:
+** PR_ACCEPT_READ_BUF_OVERHEAD.
+** PRInt32 amount
+** The number of bytes of client data to receive. Does not include
+** the size of the PRNetAddr structures. If 0, no data will be read
+** from the client.
+** PRIntervalTime timeout
+** The timeout interval only applies to the read portion of the
+** operation. PR_AcceptRead will block indefinitely until the
+** connection is accepted; the read will timeout after the timeout
+** interval elapses.
+** OUTPUTS:
+** PRFileDesc **acceptedSock
+** The file descriptor for the newly connected socket. This parameter
+** will only be valid if the function return does not indicate failure.
+** PRNetAddr **peerAddr,
+** The address of the remote socket. This parameter will only be
+** valid if the function return does not indicate failure. The
+** returned address is not guaranteed to be properly aligned.
+**
+** RETURNS:
+** The number of bytes read from the client or -1 on failure. The reason
+** for the failure is obtained by calling PR_GetError().
+**************************************************************************
+**/
+/* define buffer overhead constant. Add this value to the user's
+** data length when allocating a buffer to accept data.
+** Example:
+** #define USER_DATA_SIZE 10
+** char buf[USER_DATA_SIZE + PR_ACCEPT_READ_BUF_OVERHEAD];
+** bytesRead = PR_AcceptRead( s, fd, &a, &p, USER_DATA_SIZE, ...);
+*/
+#define PR_ACCEPT_READ_BUF_OVERHEAD (32+(2*sizeof(PRNetAddr)))
+
+NSPR_API(PRInt32) PR_AcceptRead(
+ PRFileDesc *listenSock, PRFileDesc **acceptedSock,
+ PRNetAddr **peerAddr, void *buf, PRInt32 amount, PRIntervalTime timeout);
+
+/*
+*************************************************************************
+** FUNCTION: PR_NewTCPSocketPair
+** DESCRIPTION:
+** Create a new TCP socket pair. The returned descriptors can be used
+** interchangeably; they are interconnected full-duplex descriptors: data
+** written to one can be read from the other and vice-versa.
+**
+** INPUTS:
+** None
+** OUTPUTS:
+** PRFileDesc *fds[2]
+** The file descriptor pair for the newly created TCP sockets.
+** RETURN: PRStatus
+** Upon successful completion of TCP socket pair, PR_NewTCPSocketPair
+** returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further
+** failure information can be obtained by calling PR_GetError().
+** XXX can we implement this on windoze and mac?
+**************************************************************************
+**/
+NSPR_API(PRStatus) PR_NewTCPSocketPair(PRFileDesc *fds[2]);
+
+/*
+*************************************************************************
+** FUNCTION: PR_GetSockName
+** DESCRIPTION:
+** Get socket name. Return the network address for this socket.
+**
+** INPUTS:
+** PRFileDesc *fd
+** Points to a PRFileDesc object representing the socket.
+** OUTPUTS:
+** PRNetAddr *addr
+** Returns the address of the socket in its own communication space.
+** RETURN: PRStatus
+** Upon successful completion, PR_GetSockName returns PR_SUCCESS.
+** Otherwise, it returns PR_FAILURE. Further failure information can
+** be obtained by calling PR_GetError().
+**************************************************************************
+**/
+NSPR_API(PRStatus) PR_GetSockName(PRFileDesc *fd, PRNetAddr *addr);
+
+/*
+*************************************************************************
+** FUNCTION: PR_GetPeerName
+** DESCRIPTION:
+** Get name of the connected peer. Return the network address for the
+** connected peer socket.
+**
+** INPUTS:
+** PRFileDesc *fd
+** Points to a PRFileDesc object representing the connected peer.
+** OUTPUTS:
+** PRNetAddr *addr
+** Returns the address of the connected peer in its own communication
+** space.
+** RETURN: PRStatus
+** Upon successful completion, PR_GetPeerName returns PR_SUCCESS.
+** Otherwise, it returns PR_FAILURE. Further failure information can
+** be obtained by calling PR_GetError().
+**************************************************************************
+**/
+NSPR_API(PRStatus) PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr);
+
+NSPR_API(PRStatus) PR_GetSocketOption(
+ PRFileDesc *fd, PRSocketOptionData *data);
+
+NSPR_API(PRStatus) PR_SetSocketOption(
+ PRFileDesc *fd, const PRSocketOptionData *data);
+
+/*
+ *********************************************************************
+ *
+ * File descriptor inheritance
+ *
+ *********************************************************************
+ */
+
+/*
+ ************************************************************************
+ * FUNCTION: PR_SetFDInheritable
+ * DESCRIPTION:
+ * Set the inheritance attribute of a file descriptor.
+ *
+ * INPUTS:
+ * PRFileDesc *fd
+ * Points to a PRFileDesc object.
+ * PRBool inheritable
+ * If PR_TRUE, the file descriptor fd is set to be inheritable
+ * by a child process. If PR_FALSE, the file descriptor is set
+ * to be not inheritable by a child process.
+ * RETURN: PRStatus
+ * Upon successful completion, PR_SetFDInheritable returns PR_SUCCESS.
+ * Otherwise, it returns PR_FAILURE. Further failure information can
+ * be obtained by calling PR_GetError().
+ *************************************************************************
+ */
+NSPR_API(PRStatus) PR_SetFDInheritable(
+ PRFileDesc *fd,
+ PRBool inheritable);
+
+/*
+ ************************************************************************
+ * FUNCTION: PR_GetInheritedFD
+ * DESCRIPTION:
+ * Get an inherited file descriptor with the specified name.
+ *
+ * INPUTS:
+ * const char *name
+ * The name of the inherited file descriptor.
+ * RETURN: PRFileDesc *
+ * Upon successful completion, PR_GetInheritedFD returns the
+ * inherited file descriptor with the specified name. Otherwise,
+ * it returns NULL. Further failure information can be obtained
+ * by calling PR_GetError().
+ *************************************************************************
+ */
+NSPR_API(PRFileDesc *) PR_GetInheritedFD(const char *name);
+
+/*
+ *********************************************************************
+ *
+ * Memory-mapped files
+ *
+ *********************************************************************
+ */
+
+typedef struct PRFileMap PRFileMap;
+
+/*
+ * protection options for read and write accesses of a file mapping
+ */
+typedef enum PRFileMapProtect {
+ PR_PROT_READONLY, /* read only */
+ PR_PROT_READWRITE, /* readable, and write is shared */
+ PR_PROT_WRITECOPY /* readable, and write is private (copy-on-write) */
+} PRFileMapProtect;
+
+NSPR_API(PRFileMap *) PR_CreateFileMap(
+ PRFileDesc *fd,
+ PRInt64 size,
+ PRFileMapProtect prot);
+
+/*
+ * return the alignment (in bytes) of the offset argument to PR_MemMap
+ */
+NSPR_API(PRInt32) PR_GetMemMapAlignment(void);
+
+NSPR_API(void *) PR_MemMap(
+ PRFileMap *fmap,
+ PROffset64 offset, /* must be aligned and sized according to the
+ * return value of PR_GetMemMapAlignment() */
+ PRUint32 len);
+
+NSPR_API(PRStatus) PR_MemUnmap(void *addr, PRUint32 len);
+
+NSPR_API(PRStatus) PR_CloseFileMap(PRFileMap *fmap);
+
+/*
+ * Synchronously flush the given memory-mapped address range of the given open
+ * file to disk. The function does not return until all modified data have
+ * been written to disk.
+ *
+ * On some platforms, the function will call PR_Sync(fd) internally if it is
+ * necessary for flushing modified data to disk synchronously.
+ */
+NSPR_API(PRStatus) PR_SyncMemMap(
+ PRFileDesc *fd,
+ void *addr,
+ PRUint32 len);
+
+/*
+ ******************************************************************
+ *
+ * Interprocess communication
+ *
+ ******************************************************************
+ */
+
+/*
+ * Creates an anonymous pipe and returns file descriptors for the
+ * read and write ends of the pipe.
+ */
+
+NSPR_API(PRStatus) PR_CreatePipe(
+ PRFileDesc **readPipe,
+ PRFileDesc **writePipe
+);
+
+/************************************************************************/
+/************** The following definitions are for poll ******************/
+/************************************************************************/
+
+struct PRPollDesc {
+ PRFileDesc* fd;
+ PRInt16 in_flags;
+ PRInt16 out_flags;
+};
+
+/*
+** Bit values for PRPollDesc.in_flags or PRPollDesc.out_flags. Binary-or
+** these together to produce the desired poll request.
+*/
+
+#if defined(_PR_POLL_BACKCOMPAT)
+
+#include <poll.h>
+#define PR_POLL_READ POLLIN
+#define PR_POLL_WRITE POLLOUT
+#define PR_POLL_EXCEPT POLLPRI
+#define PR_POLL_ERR POLLERR /* only in out_flags */
+#define PR_POLL_NVAL POLLNVAL /* only in out_flags when fd is bad */
+#define PR_POLL_HUP POLLHUP /* only in out_flags */
+
+#else /* _PR_POLL_BACKCOMPAT */
+
+#define PR_POLL_READ 0x1
+#define PR_POLL_WRITE 0x2
+#define PR_POLL_EXCEPT 0x4
+#define PR_POLL_ERR 0x8 /* only in out_flags */
+#define PR_POLL_NVAL 0x10 /* only in out_flags when fd is bad */
+#define PR_POLL_HUP 0x20 /* only in out_flags */
+
+#endif /* _PR_POLL_BACKCOMPAT */
+
+/*
+*************************************************************************
+** FUNCTION: PR_Poll
+** DESCRIPTION:
+**
+** The call returns as soon as I/O is ready on one or more of the underlying
+** socket objects. A count of the number of ready descriptors is
+** returned unless a timeout occurs in which case zero is returned.
+**
+** PRPollDesc.fd should be set to a pointer to a PRFileDesc object
+** representing a socket. This field can be set to NULL to indicate to
+** PR_Poll that this PRFileDesc object should be ignored.
+** PRPollDesc.in_flags should be set to the desired request
+** (read/write/except or some combination). Upon successful return from
+** this call PRPollDesc.out_flags will be set to indicate what kind of
+** i/o can be performed on the respective descriptor. PR_Poll() uses the
+** out_flags fields as scratch variables during the call. If PR_Poll()
+** returns 0 or -1, the out_flags fields do not contain meaningful values
+** and must not be used.
+**
+** INPUTS:
+** PRPollDesc *pds A pointer to an array of PRPollDesc
+**
+** PRIntn npds The number of elements in the array
+** If this argument is zero PR_Poll is
+** equivalent to a PR_Sleep(timeout).
+**
+** PRIntervalTime timeout Amount of time the call will block waiting
+** for I/O to become ready. If this time expires
+** w/o any I/O becoming ready, the result will
+** be zero.
+**
+** OUTPUTS: None
+** RETURN:
+** PRInt32 Number of PRPollDesc's with events or zero
+** if the function timed out or -1 on failure.
+** The reason for the failure is obtained by
+** calling PR_GetError().
+**************************************************************************
+*/
+NSPR_API(PRInt32) PR_Poll(
+ PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout);
+
+/*
+**************************************************************************
+**
+** Pollable events
+**
+** A pollable event is a special kind of file descriptor.
+** The only I/O operation you can perform on a pollable event
+** is to poll it with the PR_POLL_READ flag. You can't
+** read from or write to a pollable event.
+**
+** The purpose of a pollable event is to combine event waiting
+** with I/O waiting in a single PR_Poll call. Pollable events
+** are implemented using a pipe or a pair of TCP sockets
+** connected via the loopback address, therefore setting and
+** waiting for pollable events are expensive operating system
+** calls. Do not use pollable events for general thread
+** synchronization. Use condition variables instead.
+**
+** A pollable event has two states: set and unset. Events
+** are not queued, so there is no notion of an event count.
+** A pollable event is either set or unset.
+**
+** A new pollable event is created by a PR_NewPollableEvent
+** call and is initially in the unset state.
+**
+** PR_WaitForPollableEvent blocks the calling thread until
+** the pollable event is set, and then it atomically unsets
+** the pollable event before it returns.
+**
+** To set a pollable event, call PR_SetPollableEvent.
+**
+** One can call PR_Poll with the PR_POLL_READ flag on a pollable
+** event. When the pollable event is set, PR_Poll returns with
+** the PR_POLL_READ flag set in the out_flags.
+**
+** To close a pollable event, call PR_DestroyPollableEvent
+** (not PR_Close).
+**
+**************************************************************************
+*/
+
+NSPR_API(PRFileDesc *) PR_NewPollableEvent(void);
+
+NSPR_API(PRStatus) PR_DestroyPollableEvent(PRFileDesc *event);
+
+NSPR_API(PRStatus) PR_SetPollableEvent(PRFileDesc *event);
+
+NSPR_API(PRStatus) PR_WaitForPollableEvent(PRFileDesc *event);
+
+PR_END_EXTERN_C
+
+#endif /* prio_h___ */
diff --git a/nsprpub/pr/include/pripcsem.h b/nsprpub/pr/include/pripcsem.h
new file mode 100644
index 0000000000..f5a524da1a
--- /dev/null
+++ b/nsprpub/pr/include/pripcsem.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: pripcsem.h
+ *
+ * Description: named semaphores for interprocess
+ * synchronization
+ *
+ * Unrelated processes obtain access to a shared semaphore
+ * by specifying its name.
+ *
+ * Our goal is to support named semaphores on at least
+ * Unix and Win32 platforms. The implementation will use
+ * one of the three native semaphore APIs: POSIX, System V,
+ * and Win32.
+ *
+ * Because POSIX named semaphores have kernel persistence,
+ * we are forced to have a delete function in this API.
+ */
+
+#ifndef pripcsem_h___
+#define pripcsem_h___
+
+#include "prtypes.h"
+#include "prio.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+ * PRSem is an opaque structure that represents a named
+ * semaphore.
+ */
+typedef struct PRSem PRSem;
+
+/*
+ * PR_OpenSemaphore --
+ *
+ * Create or open a named semaphore with the specified name.
+ * A handle to the semaphore is returned.
+ *
+ * If the named semaphore doesn't exist and the PR_SEM_CREATE
+ * flag is specified, the named semaphore is created. The
+ * created semaphore needs to be removed from the system with
+ * a PR_DeleteSemaphore call.
+ *
+ * If PR_SEM_CREATE is specified, the third argument is the
+ * access permission bits of the new semaphore (same
+ * interpretation as the mode argument to PR_Open) and the
+ * fourth argument is the initial value of the new semaphore.
+ * If PR_SEM_CREATE is not specified, the third and fourth
+ * arguments are ignored.
+ */
+
+#define PR_SEM_CREATE 0x1 /* create if not exist */
+#define PR_SEM_EXCL 0x2 /* fail if already exists */
+
+NSPR_API(PRSem *) PR_OpenSemaphore(
+ const char *name, PRIntn flags, PRIntn mode, PRUintn value);
+
+/*
+ * PR_WaitSemaphore --
+ *
+ * If the value of the semaphore is > 0, decrement the value and return.
+ * If the value is 0, sleep until the value becomes > 0, then decrement
+ * the value and return.
+ *
+ * The "test and decrement" operation is performed atomically.
+ */
+
+NSPR_API(PRStatus) PR_WaitSemaphore(PRSem *sem);
+
+/*
+ * PR_PostSemaphore --
+ *
+ * Increment the value of the named semaphore by 1.
+ */
+
+NSPR_API(PRStatus) PR_PostSemaphore(PRSem *sem);
+
+/*
+ * PR_CloseSemaphore --
+ *
+ * Close a named semaphore handle.
+ */
+
+NSPR_API(PRStatus) PR_CloseSemaphore(PRSem *sem);
+
+/*
+ * PR_DeleteSemaphore --
+ *
+ * Remove a named semaphore from the system.
+ */
+
+NSPR_API(PRStatus) PR_DeleteSemaphore(const char *name);
+
+PR_END_EXTERN_C
+
+#endif /* pripcsem_h___ */
diff --git a/nsprpub/pr/include/private/Makefile.in b/nsprpub/pr/include/private/Makefile.in
new file mode 100644
index 0000000000..db0c2f1e24
--- /dev/null
+++ b/nsprpub/pr/include/private/Makefile.in
@@ -0,0 +1,29 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+RELEASE_HEADERS = pprio.h pprthred.h prpriv.h
+RELEASE_HEADERS := $(addprefix $(srcdir)/, $(RELEASE_HEADERS))
+RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR)/private
+
+HEADERS = $(RELEASE_HEADERS) $(srcdir)/pprmwait.h $(srcdir)/primpl.h
+
+include_subdir = private
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(RELEASE_HEADERS)
+ $(INSTALL) -m 444 $(RELEASE_HEADERS) $(dist_includedir)/private
diff --git a/nsprpub/pr/include/private/pprio.h b/nsprpub/pr/include/private/pprio.h
new file mode 100644
index 0000000000..be8252b887
--- /dev/null
+++ b/nsprpub/pr/include/private/pprio.h
@@ -0,0 +1,242 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: pprio.h
+**
+** Description: Private definitions for I/O related structures
+*/
+
+#ifndef pprio_h___
+#define pprio_h___
+
+#include "prtypes.h"
+#include "prio.h"
+
+PR_BEGIN_EXTERN_C
+
+/************************************************************************/
+/************************************************************************/
+
+#ifdef _WIN64
+typedef __int64 PROsfd;
+#else
+typedef PRInt32 PROsfd;
+#endif
+
+/* Return the method tables for files, tcp sockets and udp sockets */
+NSPR_API(const PRIOMethods*) PR_GetFileMethods(void);
+NSPR_API(const PRIOMethods*) PR_GetTCPMethods(void);
+NSPR_API(const PRIOMethods*) PR_GetUDPMethods(void);
+NSPR_API(const PRIOMethods*) PR_GetPipeMethods(void);
+
+/*
+** Convert a NSPR socket handle to a native socket handle.
+**
+** Using this function makes your code depend on the properties of the
+** current NSPR implementation, which may change (although extremely
+** unlikely because of NSPR's backward compatibility requirement). Avoid
+** using it if you can.
+**
+** If you use this function, you need to understand what NSPR does to
+** the native handle. For example, NSPR puts native socket handles in
+** non-blocking mode or associates them with an I/O completion port (the
+** WINNT build configuration only). Your use of the native handle should
+** not interfere with NSPR's use of the native handle. If your code
+** changes the configuration of the native handle, (e.g., changes it to
+** blocking or closes it), NSPR will not work correctly.
+*/
+NSPR_API(PROsfd) PR_FileDesc2NativeHandle(PRFileDesc *);
+NSPR_API(void) PR_ChangeFileDescNativeHandle(PRFileDesc *, PROsfd);
+NSPR_API(PRFileDesc*) PR_AllocFileDesc(PROsfd osfd,
+ const PRIOMethods *methods);
+NSPR_API(void) PR_FreeFileDesc(PRFileDesc *fd);
+/*
+** Import an existing OS file to NSPR.
+*/
+NSPR_API(PRFileDesc*) PR_ImportFile(PROsfd osfd);
+NSPR_API(PRFileDesc*) PR_ImportPipe(PROsfd osfd);
+NSPR_API(PRFileDesc*) PR_ImportTCPSocket(PROsfd osfd);
+NSPR_API(PRFileDesc*) PR_ImportUDPSocket(PROsfd osfd);
+
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_CreateSocketPollFd
+ * DESCRIPTION:
+ * Create a PRFileDesc wrapper for a native socket handle, for use with
+ * PR_Poll only
+ * INPUTS:
+ * None
+ * OUTPUTS:
+ * None
+ * RETURN: PRFileDesc*
+ * Upon successful completion, PR_CreateSocketPollFd returns a pointer
+ * to the PRFileDesc created for the native socket handle
+ * Returns a NULL pointer if the create of a new PRFileDesc failed
+ *
+ **************************************************************************
+ */
+
+NSPR_API(PRFileDesc*) PR_CreateSocketPollFd(PROsfd osfd);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_DestroySocketPollFd
+ * DESCRIPTION:
+ * Destroy the PRFileDesc wrapper created by PR_CreateSocketPollFd
+ * INPUTS:
+ * None
+ * OUTPUTS:
+ * None
+ * RETURN: PRFileDesc*
+ * Upon successful completion, PR_DestroySocketPollFd returns
+ * PR_SUCCESS, else PR_FAILURE
+ *
+ **************************************************************************
+ */
+
+NSPR_API(PRStatus) PR_DestroySocketPollFd(PRFileDesc *fd);
+
+
+/*
+** Macros for PR_Socket
+**
+** Socket types: PR_SOCK_STREAM, PR_SOCK_DGRAM
+*/
+
+#ifdef WIN32
+
+#define PR_SOCK_STREAM 1
+#define PR_SOCK_DGRAM 2
+
+#else /* WIN32 */
+
+#define PR_SOCK_STREAM SOCK_STREAM
+#define PR_SOCK_DGRAM SOCK_DGRAM
+
+#endif /* WIN32 */
+
+/*
+** Create a new Socket; this function is obsolete.
+*/
+NSPR_API(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto);
+
+/* FUNCTION: PR_LockFile
+** DESCRIPTION:
+** Lock a file for exclusive access.
+** RETURNS:
+** PR_SUCCESS when the lock is held
+** PR_FAILURE otherwise
+*/
+NSPR_API(PRStatus) PR_LockFile(PRFileDesc *fd);
+
+/* FUNCTION: PR_TLockFile
+** DESCRIPTION:
+** Test and Lock a file for exclusive access. Do not block if the
+** file cannot be locked immediately.
+** RETURNS:
+** PR_SUCCESS when the lock is held
+** PR_FAILURE otherwise
+*/
+NSPR_API(PRStatus) PR_TLockFile(PRFileDesc *fd);
+
+/* FUNCTION: PR_UnlockFile
+** DESCRIPTION:
+** Unlock a file which has been previously locked successfully by this
+** process.
+** RETURNS:
+** PR_SUCCESS when the lock is released
+** PR_FAILURE otherwise
+*/
+NSPR_API(PRStatus) PR_UnlockFile(PRFileDesc *fd);
+
+/*
+** Emulate acceptread by accept and recv.
+*/
+NSPR_API(PRInt32) PR_EmulateAcceptRead(PRFileDesc *sd, PRFileDesc **nd,
+ PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime timeout);
+
+/*
+** Emulate sendfile by reading from the file and writing to the socket.
+** The file is memory-mapped if memory-mapped files are supported.
+*/
+NSPR_API(PRInt32) PR_EmulateSendFile(
+ PRFileDesc *networkSocket, PRSendFileData *sendData,
+ PRTransmitFileFlags flags, PRIntervalTime timeout);
+
+#ifdef WIN32
+/* FUNCTION: PR_NTFast_AcceptRead
+** DESCRIPTION:
+** NT has the notion of an "accept context", which is only needed in
+** order to make certain calls. By default, a socket connected via
+** AcceptEx can only do a limited number of things without updating
+** the acceptcontext. The generic version of PR_AcceptRead always
+** updates the accept context. This version does not.
+**/
+NSPR_API(PRInt32) PR_NTFast_AcceptRead(PRFileDesc *sd, PRFileDesc **nd,
+ PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime t);
+
+typedef void (*_PR_AcceptTimeoutCallback)(void *);
+
+/* FUNCTION: PR_NTFast_AcceptRead_WithTimeoutCallback
+** DESCRIPTION:
+** The AcceptEx call combines the accept with the read function. However,
+** our daemon threads need to be able to wakeup and reliably flush their
+** log buffers if the Accept times out. However, with the current blocking
+** interface to AcceptRead, there is no way for us to timeout the Accept;
+** this is because when we timeout the Read, we can close the newly
+** socket and continue; but when we timeout the accept itself, there is no
+** new socket to timeout. So instead, this version of the function is
+** provided. After the initial timeout period elapses on the accept()
+** portion of the function, it will call the callback routine and then
+** continue the accept. If the timeout occurs on the read, it will
+** close the connection and return error.
+*/
+NSPR_API(PRInt32) PR_NTFast_AcceptRead_WithTimeoutCallback(
+ PRFileDesc *sd,
+ PRFileDesc **nd,
+ PRNetAddr **raddr,
+ void *buf,
+ PRInt32 amount,
+ PRIntervalTime t,
+ _PR_AcceptTimeoutCallback callback,
+ void *callback_arg);
+
+/* FUNCTION: PR_NTFast_Accept
+** DESCRIPTION:
+** NT has the notion of an "accept context", which is only needed in
+** order to make certain calls. By default, a socket connected via
+** AcceptEx can only do a limited number of things without updating
+** the acceptcontext. The generic version of PR_Accept always
+** updates the accept context. This version does not.
+**/
+NSPR_API(PRFileDesc*) PR_NTFast_Accept(PRFileDesc *fd, PRNetAddr *addr,
+ PRIntervalTime timeout);
+
+/* FUNCTION: PR_NTFast_Update
+** DESCRIPTION:
+** For sockets accepted with PR_NTFast_Accept or PR_NTFastAcceptRead,
+** this function will update the accept context for those sockets,
+** so that the socket can make general purpose socket calls.
+** Without calling this, the only operations supported on the socket
+** Are PR_Read, PR_Write, PR_Transmitfile, and PR_Close.
+*/
+NSPR_API(void) PR_NTFast_UpdateAcceptContext(PRFileDesc *acceptSock,
+ PRFileDesc *listenSock);
+
+
+/* FUNCTION: PR_NT_CancelIo
+** DESCRIPTION:
+** Cancel IO operations on fd.
+*/
+NSPR_API(PRStatus) PR_NT_CancelIo(PRFileDesc *fd);
+
+
+#endif /* WIN32 */
+
+PR_END_EXTERN_C
+
+#endif /* pprio_h___ */
diff --git a/nsprpub/pr/include/private/pprmwait.h b/nsprpub/pr/include/private/pprmwait.h
new file mode 100644
index 0000000000..3e4057c5f4
--- /dev/null
+++ b/nsprpub/pr/include/private/pprmwait.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if defined(_PPRMWAIT_H)
+#else
+#define _PPRMWAIT_H
+
+#include "prlock.h"
+#include "prcvar.h"
+#include "prclist.h"
+#include "prthread.h"
+
+#define MAX_POLLING_INTERVAL 100
+#define _PR_POLL_COUNT_FUDGE 64
+#define _PR_DEFAULT_HASH_LENGTH 59
+
+/*
+ * Our hash table resolves collisions by open addressing with
+ * double hashing. See Cormen, Leiserson, and Rivest,
+ * Introduction to Algorithms, p. 232, The MIT Press, 1990.
+ */
+
+#define _MW_HASH(a, m) ((((PRUptrdiff)(a) >> 4) ^ ((PRUptrdiff)(a) >> 10)) % (m))
+#define _MW_HASH2(a, m) (1 + ((((PRUptrdiff)(a) >> 4) ^ ((PRUptrdiff)(a) >> 10)) % (m - 2)))
+#define _MW_ABORTED(_rv) \
+ ((PR_FAILURE == (_rv)) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError()))
+
+typedef enum {_prmw_success, _prmw_rehash, _prmw_error} _PR_HashStory;
+
+typedef struct _PRWaiterHash
+{
+ PRUint16 count; /* current number in hash table */
+ PRUint16 length; /* current size of the hash table */
+ PRRecvWait *recv_wait; /* hash table of receive wait objects */
+} _PRWaiterHash;
+
+typedef enum {_prmw_running, _prmw_stopping, _prmw_stopped} PRMWGroupState;
+
+struct PRWaitGroup
+{
+ PRCList group_link; /* all groups are linked to each other */
+ PRCList io_ready; /* list of I/O requests that are ready */
+ PRMWGroupState state; /* state of this group (so we can shut down) */
+
+ PRLock *ml; /* lock for synchronizing this wait group */
+ PRCondVar *io_taken; /* calling threads notify when they take I/O */
+ PRCondVar *io_complete; /* calling threads wait here for completions */
+ PRCondVar *new_business; /* polling thread waits here more work */
+ PRCondVar *mw_manage; /* used to manage group lists */
+ PRThread* poller; /* thread that's actually doing the poll() */
+ PRUint16 waiting_threads; /* number of threads waiting for recv */
+ PRUint16 polling_count; /* number of elements in the polling list */
+ PRUint32 p_timestamp; /* pseudo-time group had element removed */
+ PRPollDesc *polling_list; /* list poller builds for polling */
+ PRIntervalTime last_poll; /* last time we polled */
+ _PRWaiterHash *waiter; /* pointer to hash table of wait receive objects */
+
+#ifdef WINNT
+ /*
+ * On NT, idle threads are responsible for getting completed i/o.
+ * They need to add completed i/o to the io_ready list. Since
+ * idle threads cannot use nspr locks, we have to use an md lock
+ * to protect the io_ready list.
+ */
+ _MDLock mdlock; /* protect io_ready, waiter, and wait_list */
+ PRCList wait_list; /* used in place of io_complete. reuse
+ * waitQLinks in the PRThread structure. */
+#endif /* WINNT */
+};
+
+/**********************************************************************
+***********************************************************************
+******************** Wait group enumerations **************************
+***********************************************************************
+**********************************************************************/
+typedef struct _PRGlobalState
+{
+ PRCList group_list; /* master of the group list */
+ PRWaitGroup *group; /* the default (NULL) group */
+} _PRGlobalState;
+
+#ifdef WINNT
+extern PRStatus NT_HashRemoveInternal(PRWaitGroup *group, PRFileDesc *fd);
+#endif
+
+typedef enum {_PR_ENUM_UNSEALED=0, _PR_ENUM_SEALED=0x0eadface} _PREnumSeal;
+
+struct PRMWaitEnumerator
+{
+ PRWaitGroup *group; /* group this enumerator is bound to */
+ PRThread *thread; /* thread in midst of an enumeration */
+ _PREnumSeal seal; /* trying to detect deleted objects */
+ PRUint32 p_timestamp; /* when enumeration was (re)started */
+ PRRecvWait **waiter; /* pointer into hash table */
+ PRUintn index; /* position in hash table */
+ void *pad[4]; /* some room to grow */
+};
+
+#endif /* defined(_PPRMWAIT_H) */
+
+/* pprmwait.h */
diff --git a/nsprpub/pr/include/private/pprthred.h b/nsprpub/pr/include/private/pprthred.h
new file mode 100644
index 0000000000..9b7c3c1995
--- /dev/null
+++ b/nsprpub/pr/include/private/pprthred.h
@@ -0,0 +1,311 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef pprthred_h___
+#define pprthred_h___
+
+/*
+** API for PR private functions. These calls are to be used by internal
+** developers only.
+*/
+#include "nspr.h"
+
+#if defined(XP_OS2)
+#define INCL_DOS
+#define INCL_DOSERRORS
+#define INCL_WIN
+#include <os2.h>
+#endif
+
+PR_BEGIN_EXTERN_C
+
+/*---------------------------------------------------------------------------
+** THREAD PRIVATE FUNCTIONS
+---------------------------------------------------------------------------*/
+
+/*
+** Associate a thread object with an existing native thread.
+** "type" is the type of thread object to attach
+** "priority" is the priority to assign to the thread
+** "stack" defines the shape of the threads stack
+**
+** This can return NULL if some kind of error occurs, or if memory is
+** tight. This call invokes "start(obj,arg)" and returns when the
+** function returns. The thread object is automatically destroyed.
+**
+** This call is not normally needed unless you create your own native
+** thread. PR_Init does this automatically for the primordial thread.
+*/
+NSPR_API(PRThread*) PR_AttachThread(PRThreadType type,
+ PRThreadPriority priority,
+ PRThreadStack *stack);
+
+/*
+** Detach the nspr thread from the currently executing native thread.
+** The thread object will be destroyed and all related data attached
+** to it. The exit procs will be invoked.
+**
+** This call is not normally needed unless you create your own native
+** thread. PR_Exit will automatially detach the nspr thread object
+** created by PR_Init for the primordial thread.
+**
+** This call returns after the nspr thread object is destroyed.
+*/
+NSPR_API(void) PR_DetachThread(void);
+
+/*
+** Get the id of the named thread. Each thread is assigned a unique id
+** when it is created or attached.
+*/
+NSPR_API(PRUint32) PR_GetThreadID(PRThread *thread);
+
+/*
+** Set the procedure that is called when a thread is dumped. The procedure
+** will be applied to the argument, arg, when called. Setting the procedure
+** to NULL effectively removes it.
+*/
+typedef void (*PRThreadDumpProc)(PRFileDesc *fd, PRThread *t, void *arg);
+NSPR_API(void) PR_SetThreadDumpProc(
+ PRThread* thread, PRThreadDumpProc dump, void *arg);
+
+/*
+** Get this thread's affinity mask. The affinity mask is a 32 bit quantity
+** marking a bit for each processor this process is allowed to run on.
+** The processor mask is returned in the mask argument.
+** The least-significant-bit represents processor 0.
+**
+** Returns 0 on success, -1 on failure.
+*/
+NSPR_API(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask);
+
+/*
+** Set this thread's affinity mask.
+**
+** Returns 0 on success, -1 on failure.
+*/
+NSPR_API(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask );
+
+/*
+** Set the default CPU Affinity mask.
+**
+*/
+NSPR_API(PRInt32) PR_SetCPUAffinityMask(PRUint32 mask);
+
+/*
+** Show status of all threads to standard error output.
+*/
+NSPR_API(void) PR_ShowStatus(void);
+
+/*
+** Set thread recycle mode to on (1) or off (0)
+*/
+NSPR_API(void) PR_SetThreadRecycleMode(PRUint32 flag);
+
+
+/*---------------------------------------------------------------------------
+** THREAD PRIVATE FUNCTIONS FOR GARBAGE COLLECTIBLE THREADS
+---------------------------------------------------------------------------*/
+
+/*
+** Only Garbage collectible threads participate in resume all, suspend all and
+** enumeration operations. They are also different during creation when
+** platform specific action may be needed (For example, all Solaris GC able
+** threads are bound threads).
+*/
+
+/*
+** Same as PR_CreateThread except that the thread is marked as garbage
+** collectible.
+*/
+NSPR_API(PRThread*) PR_CreateThreadGCAble(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+
+/*
+** Same as PR_AttachThread except that the thread being attached is marked as
+** garbage collectible.
+*/
+NSPR_API(PRThread*) PR_AttachThreadGCAble(PRThreadType type,
+ PRThreadPriority priority,
+ PRThreadStack *stack);
+
+/*
+** Mark the thread as garbage collectible.
+*/
+NSPR_API(void) PR_SetThreadGCAble(void);
+
+/*
+** Unmark the thread as garbage collectible.
+*/
+NSPR_API(void) PR_ClearThreadGCAble(void);
+
+/*
+** This routine prevents all other GC able threads from running. This call is needed by
+** the garbage collector.
+*/
+NSPR_API(void) PR_SuspendAll(void);
+
+/*
+** This routine unblocks all other GC able threads that were suspended from running by
+** PR_SuspendAll(). This call is needed by the garbage collector.
+*/
+NSPR_API(void) PR_ResumeAll(void);
+
+/*
+** Return the thread stack pointer of the given thread.
+** Needed by the garbage collector.
+*/
+NSPR_API(void *) PR_GetSP(PRThread *thread);
+
+/*
+** Save the registers that the GC would find interesting into the thread
+** "t". isCurrent will be non-zero if the thread state that is being
+** saved is the currently executing thread. Return the address of the
+** first register to be scanned as well as the number of registers to
+** scan in "np".
+**
+** If "isCurrent" is non-zero then it is allowed for the thread context
+** area to be used as scratch storage to hold just the registers
+** necessary for scanning.
+**
+** This function simply calls the internal function _MD_HomeGCRegisters().
+*/
+NSPR_API(PRWord *) PR_GetGCRegisters(PRThread *t, int isCurrent, int *np);
+
+/*
+** (Get|Set)ExecutionEnvironent
+**
+** Used by Java to associate it's execution environment so garbage collector
+** can find it. If return is NULL, then it's probably not a collectable thread.
+**
+** There's no locking required around these calls.
+*/
+NSPR_API(void*) GetExecutionEnvironment(PRThread *thread);
+NSPR_API(void) SetExecutionEnvironment(PRThread* thread, void *environment);
+
+/*
+** Enumeration function that applies "func(thread,i,arg)" to each active
+** thread in the process. The enumerator returns PR_SUCCESS if the enumeration
+** should continue, any other value is considered failure, and enumeration
+** stops, returning the failure value from PR_EnumerateThreads.
+** Needed by the garbage collector.
+*/
+typedef PRStatus (PR_CALLBACK *PREnumerator)(PRThread *t, int i, void *arg);
+NSPR_API(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg);
+
+/*
+** Signature of a thread stack scanning function. It is applied to every
+** contiguous group of potential pointers within a thread. Count denotes the
+** number of pointers.
+*/
+typedef PRStatus
+(PR_CALLBACK *PRScanStackFun)(PRThread* t,
+ void** baseAddr, PRUword count, void* closure);
+
+/*
+** Applies scanFun to all contiguous groups of potential pointers
+** within a thread. This includes the stack, registers, and thread-local
+** data. If scanFun returns a status value other than PR_SUCCESS the scan
+** is aborted, and the status value is returned.
+*/
+NSPR_API(PRStatus)
+PR_ThreadScanStackPointers(PRThread* t,
+ PRScanStackFun scanFun, void* scanClosure);
+
+/*
+** Calls PR_ThreadScanStackPointers for every thread.
+*/
+NSPR_API(PRStatus)
+PR_ScanStackPointers(PRScanStackFun scanFun, void* scanClosure);
+
+/*
+** Returns a conservative estimate on the amount of stack space left
+** on a thread in bytes, sufficient for making decisions about whether
+** to continue recursing or not.
+*/
+NSPR_API(PRUword)
+PR_GetStackSpaceLeft(PRThread* t);
+
+/*---------------------------------------------------------------------------
+** THREAD CPU PRIVATE FUNCTIONS
+---------------------------------------------------------------------------*/
+
+/*
+** Get a pointer to the primordial CPU.
+*/
+NSPR_API(struct _PRCPU *) _PR_GetPrimordialCPU(void);
+
+/*---------------------------------------------------------------------------
+** THREAD SYNCHRONIZATION PRIVATE FUNCTIONS
+---------------------------------------------------------------------------*/
+
+/*
+** Create a new named monitor (named for debugging purposes).
+** Monitors are re-entrant locks with a built-in condition variable.
+**
+** This may fail if memory is tight or if some operating system resource
+** is low.
+*/
+NSPR_API(PRMonitor*) PR_NewNamedMonitor(const char* name);
+
+/*
+** Test and then lock the lock if it's not already locked by some other
+** thread. Return PR_FALSE if some other thread owned the lock at the
+** time of the call.
+*/
+NSPR_API(PRBool) PR_TestAndLock(PRLock *lock);
+
+/*
+** Test and then enter the mutex associated with the monitor if it's not
+** already entered by some other thread. Return PR_FALSE if some other
+** thread owned the mutex at the time of the call.
+*/
+NSPR_API(PRBool) PR_TestAndEnterMonitor(PRMonitor *mon);
+
+/*
+** Return the number of times that the current thread has entered the
+** mutex. Returns zero if the current thread has not entered the mutex.
+*/
+NSPR_API(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon);
+
+/*
+** Just like PR_CEnterMonitor except that if the monitor is owned by
+** another thread NULL is returned.
+*/
+NSPR_API(PRMonitor*) PR_CTestAndEnterMonitor(void *address);
+
+/*---------------------------------------------------------------------------
+** PLATFORM-SPECIFIC INITIALIZATION FUNCTIONS
+---------------------------------------------------------------------------*/
+#if defined(XP_OS2)
+/*
+** These functions need to be called at the start and end of a thread.
+** An EXCEPTIONREGISTRATIONRECORD must be declared on the stack and its
+** address passed to the two functions.
+*/
+NSPR_API(void) PR_OS2_SetFloatExcpHandler(EXCEPTIONREGISTRATIONRECORD* e);
+NSPR_API(void) PR_OS2_UnsetFloatExcpHandler(EXCEPTIONREGISTRATIONRECORD* e);
+#endif /* XP_OS2 */
+
+/* I think PR_GetMonitorEntryCount is useless. All you really want is this... */
+#define PR_InMonitor(m) (PR_GetMonitorEntryCount(m) > 0)
+
+/*---------------------------------------------------------------------------
+** Special X-Lock hack for client
+---------------------------------------------------------------------------*/
+
+#ifdef XP_UNIX
+extern void _PR_XLock(void);
+extern void _PR_XUnlock(void);
+extern PRBool _PR_XIsLocked(void);
+#endif /* XP_UNIX */
+
+PR_END_EXTERN_C
+
+#endif /* pprthred_h___ */
diff --git a/nsprpub/pr/include/private/primpl.h b/nsprpub/pr/include/private/primpl.h
new file mode 100644
index 0000000000..3f5a150470
--- /dev/null
+++ b/nsprpub/pr/include/private/primpl.h
@@ -0,0 +1,2149 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef primpl_h___
+#define primpl_h___
+
+#if defined(_PR_PTHREADS)
+#include <pthread.h>
+#endif
+
+#ifdef WIN32
+/*
+ * Allow use of functions and symbols first defined in Win2k.
+ */
+#if !defined(WINVER) || (WINVER < 0x0500)
+#undef WINVER
+#define WINVER 0x0500
+#endif
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+#endif /* WIN32 */
+
+#include "nspr.h"
+#include "prpriv.h"
+
+typedef struct PRSegment PRSegment;
+
+#include "md/prosdep.h"
+#include "obsolete/probslet.h"
+
+#ifdef _PR_HAVE_POSIX_SEMAPHORES
+#include <semaphore.h>
+#elif defined(_PR_HAVE_SYSV_SEMAPHORES)
+#include <sys/sem.h>
+#endif
+
+#ifdef HAVE_SYSCALL
+#include <sys/syscall.h>
+#endif
+
+/*************************************************************************
+***** A Word about Model Dependent Function Naming Convention ***********
+*************************************************************************/
+
+/*
+NSPR 2.0 must implement its function across a range of platforms
+including: MAC, Windows/16, Windows/95, Windows/NT, and several
+variants of Unix. Each implementation shares common code as well
+as having platform dependent portions. This standard describes how
+the model dependent portions are to be implemented.
+
+In header file pr/include/primpl.h, each publicly declared
+platform dependent function is declared as:
+
+NSPR_API void _PR_MD_FUNCTION( long arg1, long arg2 );
+#define _PR_MD_FUNCTION _MD_FUNCTION
+
+In header file pr/include/md/<platform>/_<platform>.h,
+each #define'd macro is redefined as one of:
+
+#define _MD_FUNCTION <blanks>
+#define _MD_FUNCTION <expanded macro>
+#define _MD_FUNCTION <osFunction>
+#define _MD_FUNCTION <_MD_Function>
+
+Where:
+
+<blanks> is no definition at all. In this case, the function is not implemented
+and is never called for this platform.
+For example:
+#define _MD_INIT_CPUS()
+
+<expanded macro> is a C language macro expansion.
+For example:
+#define _MD_CLEAN_THREAD(_thread) \
+ PR_BEGIN_MACRO \
+ PR_DestroyCondVar(_thread->md.asyncIOCVar); \
+ PR_DestroyLock(_thread->md.asyncIOLock); \
+ PR_END_MACRO
+
+<osFunction> is some function implemented by the host operating system.
+For example:
+#define _MD_EXIT exit
+
+<_MD_function> is the name of a function implemented for this platform in
+pr/src/md/<platform>/<soruce>.c file.
+For example:
+#define _MD_GETFILEINFO _MD_GetFileInfo
+
+In <source>.c, the implementation is:
+PR_IMPLEMENT(PRInt32) _MD_GetFileInfo(const char *fn, PRFileInfo *info);
+*/
+
+PR_BEGIN_EXTERN_C
+
+typedef struct _MDLock _MDLock;
+typedef struct _MDCVar _MDCVar;
+typedef struct _MDSegment _MDSegment;
+typedef struct _MDThread _MDThread;
+typedef struct _MDThreadStack _MDThreadStack;
+typedef struct _MDSemaphore _MDSemaphore;
+typedef struct _MDDir _MDDir;
+#ifdef MOZ_UNICODE
+typedef struct _MDDirUTF16 _MDDirUTF16;
+#endif /* MOZ_UNICODE */
+typedef struct _MDFileDesc _MDFileDesc;
+typedef struct _MDProcess _MDProcess;
+typedef struct _MDFileMap _MDFileMap;
+
+#if defined(_PR_PTHREADS)
+
+/*
+** The following definitions are unique to implementing NSPR using pthreads.
+** Since pthreads defines most of the thread and thread synchronization
+** stuff, this is a pretty small set.
+*/
+
+#define PT_CV_NOTIFIED_LENGTH 6
+typedef struct _PT_Notified _PT_Notified;
+struct _PT_Notified
+{
+ PRIntn length; /* # of used entries in this structure */
+ struct
+ {
+ PRCondVar *cv; /* the condition variable notified */
+ PRIntn times; /* and the number of times notified */
+ } cv[PT_CV_NOTIFIED_LENGTH];
+ _PT_Notified *link; /* link to another of these | NULL */
+};
+
+/*
+ * bits defined for pthreads 'state' field
+ */
+#define PT_THREAD_DETACHED 0x01 /* thread can't be joined */
+#define PT_THREAD_GLOBAL 0x02 /* a global thread (unlikely) */
+#define PT_THREAD_SYSTEM 0x04 /* system (not user) thread */
+#define PT_THREAD_PRIMORD 0x08 /* this is the primordial thread */
+#define PT_THREAD_ABORTED 0x10 /* thread has been interrupted */
+#define PT_THREAD_GCABLE 0x20 /* thread is garbage collectible */
+#define PT_THREAD_SUSPENDED 0x40 /* thread has been suspended */
+#define PT_THREAD_FOREIGN 0x80 /* thread is not one of ours */
+#define PT_THREAD_BOUND 0x100 /* a bound-global thread */
+
+#define _PT_THREAD_INTERRUPTED(thr) \
+ (!(thr->interrupt_blocked) && (thr->state & PT_THREAD_ABORTED))
+#define _PT_THREAD_BLOCK_INTERRUPT(thr) \
+ (thr->interrupt_blocked = 1)
+#define _PT_THREAD_UNBLOCK_INTERRUPT(thr) \
+ (thr->interrupt_blocked = 0)
+
+#define _PT_IS_GCABLE_THREAD(thr) ((thr)->state & PT_THREAD_GCABLE)
+
+/*
+** Possible values for thread's suspend field
+** Note that the first two can be the same as they are really mutually exclusive,
+** i.e. both cannot be happening at the same time. We have two symbolic names
+** just as a mnemonic.
+**/
+#define PT_THREAD_RESUMED 0x80 /* thread has been resumed */
+#define PT_THREAD_SETGCABLE 0x100 /* set the GCAble flag */
+
+#if defined(DEBUG)
+
+typedef struct PTDebug
+{
+ PRTime timeStarted;
+ PRUintn locks_created, locks_destroyed;
+ PRUintn locks_acquired, locks_released;
+ PRUintn cvars_created, cvars_destroyed;
+ PRUintn cvars_notified, delayed_cv_deletes;
+} PTDebug;
+
+#endif /* defined(DEBUG) */
+
+NSPR_API(void) PT_FPrintStats(PRFileDesc *fd, const char *msg);
+
+/*
+ * On Linux and its derivatives POSIX priority scheduling works only for
+ * real-time threads. On those platforms we set thread's nice values
+ * instead which requires us to track kernel thread IDs for each POSIX
+ * thread we create.
+ */
+#if defined(LINUX) && defined(HAVE_SETPRIORITY) && \
+ ((defined(HAVE_SYSCALL) && defined(SYS_gettid)) || defined(HAVE_GETTID))
+#define _PR_NICE_PRIORITY_SCHEDULING
+#endif
+
+#else /* defined(_PR_PTHREADS) */
+
+NSPR_API(void) PT_FPrintStats(PRFileDesc *fd, const char *msg);
+
+/*
+** This section is contains those parts needed to implement NSPR on
+** platforms in general. One would assume that the pthreads implementation
+** included lots of the same types, at least conceptually.
+*/
+
+/*
+ * Local threads only. No multiple CPU support and hence all the
+ * following routines are no-op.
+ */
+#ifdef _PR_LOCAL_THREADS_ONLY
+
+#define _PR_MD_SUSPEND_THREAD(thread)
+#define _PR_MD_RESUME_THREAD(thread)
+#define _PR_MD_SUSPEND_CPU(cpu)
+#define _PR_MD_RESUME_CPU(cpu)
+#define _PR_MD_BEGIN_SUSPEND_ALL()
+#define _PR_MD_END_SUSPEND_ALL()
+#define _PR_MD_BEGIN_RESUME_ALL()
+#define _PR_MD_END_RESUME_ALL()
+#define _PR_MD_INIT_ATTACHED_THREAD(thread) PR_FAILURE
+
+#endif
+
+typedef struct _PRCPUQueue _PRCPUQueue;
+typedef struct _PRCPU _PRCPU;
+typedef struct _MDCPU _MDCPU;
+
+struct _PRCPUQueue {
+ _MDLock runQLock; /* lock for the run + wait queues */
+ _MDLock sleepQLock; /* lock for the run + wait queues */
+ _MDLock miscQLock; /* lock for the run + wait queues */
+
+ PRCList runQ[PR_PRIORITY_LAST + 1]; /* run queue for this CPU */
+ PRUint32 runQReadyMask;
+ PRCList sleepQ;
+ PRIntervalTime sleepQmax;
+ PRCList pauseQ;
+ PRCList suspendQ;
+ PRCList waitingToJoinQ;
+
+ PRUintn numCPUs; /* number of CPUs using this Q */
+};
+
+struct _PRCPU {
+ PRCList links; /* link list of CPUs */
+ PRUint32 id; /* id for this CPU */
+
+ union {
+ PRInt32 bits;
+ PRUint8 missed[4];
+ } u;
+ PRIntn where; /* index into u.missed */
+ PRPackedBool paused; /* cpu is paused */
+ PRPackedBool exit; /* cpu should exit */
+
+ PRThread *thread; /* native thread for this CPUThread */
+ PRThread *idle_thread; /* user-level idle thread for this CPUThread */
+
+ PRIntervalTime last_clock; /* the last time we went into
+ * _PR_ClockInterrupt() on this CPU
+ */
+
+ _PRCPUQueue *queue;
+
+ _MDCPU md;
+};
+
+typedef struct _PRInterruptTable {
+ const char *name;
+ PRUintn missed_bit;
+ void (*handler)(void);
+} _PRInterruptTable;
+
+#define _PR_CPU_PTR(_qp) \
+ ((_PRCPU*) ((char*) (_qp) - offsetof(_PRCPU,links)))
+
+#if !defined(WIN32) && !defined(XP_OS2) \
+ && !(defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY))
+#define _MD_GET_ATTACHED_THREAD() (_PR_MD_CURRENT_THREAD())
+#endif
+
+#ifdef _PR_LOCAL_THREADS_ONLY
+
+NSPR_API(struct _PRCPU *) _pr_currentCPU;
+NSPR_API(PRThread *) _pr_currentThread;
+NSPR_API(PRThread *) _pr_lastThread;
+NSPR_API(PRInt32) _pr_intsOff;
+
+#define _MD_CURRENT_CPU() (_pr_currentCPU)
+#define _MD_SET_CURRENT_CPU(_cpu) (_pr_currentCPU = (_cpu))
+#define _MD_CURRENT_THREAD() (_pr_currentThread)
+#define _MD_SET_CURRENT_THREAD(_thread) (_pr_currentThread = (_thread))
+#define _MD_LAST_THREAD() (_pr_lastThread)
+#define _MD_SET_LAST_THREAD(t) (_pr_lastThread = t)
+
+#define _MD_GET_INTSOFF() (_pr_intsOff)
+#define _MD_SET_INTSOFF(_val) (_pr_intsOff = _val)
+
+
+/* The unbalanced curly braces in these two macros are intentional */
+#define _PR_LOCK_HEAP() { PRIntn _is; if (_pr_currentCPU) _PR_INTSOFF(_is);
+#define _PR_UNLOCK_HEAP() if (_pr_currentCPU) _PR_INTSON(_is); }
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+extern PRInt32 _native_threads_only;
+
+#if defined(_PR_GLOBAL_THREADS_ONLY)
+
+#define _MD_GET_INTSOFF() 0
+#define _MD_SET_INTSOFF(_val)
+#define _PR_INTSOFF(_is)
+#define _PR_FAST_INTSON(_is)
+#define _PR_INTSON(_is)
+#define _PR_THREAD_LOCK(_thread)
+#define _PR_THREAD_UNLOCK(_thread)
+#define _PR_RUNQ_LOCK(cpu)
+#define _PR_RUNQ_UNLOCK(cpu)
+#define _PR_SLEEPQ_LOCK(thread)
+#define _PR_SLEEPQ_UNLOCK(thread)
+#define _PR_MISCQ_LOCK(thread)
+#define _PR_MISCQ_UNLOCK(thread)
+#define _PR_CPU_LIST_LOCK()
+#define _PR_CPU_LIST_UNLOCK()
+
+#define _PR_ADD_RUNQ(_thread, _cpu, _pri)
+#define _PR_DEL_RUNQ(_thread)
+#define _PR_ADD_SLEEPQ(_thread, _timeout)
+#define _PR_DEL_SLEEPQ(_thread, _propogate)
+#define _PR_ADD_JOINQ(_thread, _cpu)
+#define _PR_DEL_JOINQ(_thread)
+#define _PR_ADD_SUSPENDQ(_thread, _cpu)
+#define _PR_DEL_SUSPENDQ(_thread)
+
+#define _PR_THREAD_SWITCH_CPU(_thread, _newCPU)
+
+#define _PR_IS_NATIVE_THREAD(thread) 1
+#define _PR_IS_NATIVE_THREAD_SUPPORTED() 1
+
+#else
+
+#define _PR_INTSOFF(_is) \
+ PR_BEGIN_MACRO \
+ (_is) = _PR_MD_GET_INTSOFF(); \
+ _PR_MD_SET_INTSOFF(1); \
+ PR_END_MACRO
+
+#define _PR_FAST_INTSON(_is) \
+ PR_BEGIN_MACRO \
+ _PR_MD_SET_INTSOFF(_is); \
+ PR_END_MACRO
+
+#define _PR_INTSON(_is) \
+ PR_BEGIN_MACRO \
+ if ((_is == 0) && (_PR_MD_CURRENT_CPU())->u.bits) \
+ _PR_IntsOn((_PR_MD_CURRENT_CPU())); \
+ _PR_MD_SET_INTSOFF(_is); \
+ PR_END_MACRO
+
+#ifdef _PR_LOCAL_THREADS_ONLY
+
+#define _PR_IS_NATIVE_THREAD(thread) 0
+#define _PR_THREAD_LOCK(_thread)
+#define _PR_THREAD_UNLOCK(_thread)
+#define _PR_RUNQ_LOCK(cpu)
+#define _PR_RUNQ_UNLOCK(cpu)
+#define _PR_SLEEPQ_LOCK(thread)
+#define _PR_SLEEPQ_UNLOCK(thread)
+#define _PR_MISCQ_LOCK(thread)
+#define _PR_MISCQ_UNLOCK(thread)
+#define _PR_CPU_LIST_LOCK()
+#define _PR_CPU_LIST_UNLOCK()
+
+#define _PR_ADD_RUNQ(_thread, _cpu, _pri) \
+ PR_BEGIN_MACRO \
+ PR_APPEND_LINK(&(_thread)->links, &_PR_RUNQ(_cpu)[_pri]); \
+ _PR_RUNQREADYMASK(_cpu) |= (1L << _pri); \
+ PR_END_MACRO
+
+#define _PR_DEL_RUNQ(_thread) \
+ PR_BEGIN_MACRO \
+ _PRCPU *_cpu = _thread->cpu; \
+ PRInt32 _pri = _thread->priority; \
+ PR_REMOVE_LINK(&(_thread)->links); \
+ if (PR_CLIST_IS_EMPTY(&_PR_RUNQ(_cpu)[_pri])) \
+ _PR_RUNQREADYMASK(_cpu) &= ~(1L << _pri); \
+ PR_END_MACRO
+
+#define _PR_ADD_SLEEPQ(_thread, _timeout) \
+ _PR_AddSleepQ(_thread, _timeout);
+
+#define _PR_DEL_SLEEPQ(_thread, _propogate) \
+ _PR_DelSleepQ(_thread, _propogate);
+
+#define _PR_ADD_JOINQ(_thread, _cpu) \
+ PR_APPEND_LINK(&(_thread)->links, &_PR_WAITINGTOJOINQ(_cpu));
+
+#define _PR_DEL_JOINQ(_thread) \
+ PR_REMOVE_LINK(&(_thread)->links);
+
+#define _PR_ADD_SUSPENDQ(_thread, _cpu) \
+ PR_APPEND_LINK(&(_thread)->links, &_PR_SUSPENDQ(_cpu));
+
+#define _PR_DEL_SUSPENDQ(_thread) \
+ PR_REMOVE_LINK(&(_thread)->links);
+
+#define _PR_THREAD_SWITCH_CPU(_thread, _newCPU)
+
+#define _PR_IS_NATIVE_THREAD_SUPPORTED() 0
+
+#else /* _PR_LOCAL_THREADS_ONLY */
+
+/* These are for the "combined" thread model */
+
+#define _PR_THREAD_LOCK(_thread) \
+ _PR_MD_LOCK(&(_thread)->threadLock);
+
+#define _PR_THREAD_UNLOCK(_thread) \
+ _PR_MD_UNLOCK(&(_thread)->threadLock);
+
+#define _PR_RUNQ_LOCK(_cpu) \
+ PR_BEGIN_MACRO \
+ _PR_MD_LOCK(&(_cpu)->queue->runQLock );\
+ PR_END_MACRO
+
+#define _PR_RUNQ_UNLOCK(_cpu) \
+ PR_BEGIN_MACRO \
+ _PR_MD_UNLOCK(&(_cpu)->queue->runQLock );\
+ PR_END_MACRO
+
+#define _PR_SLEEPQ_LOCK(_cpu) \
+ _PR_MD_LOCK(&(_cpu)->queue->sleepQLock );
+
+#define _PR_SLEEPQ_UNLOCK(_cpu) \
+ _PR_MD_UNLOCK(&(_cpu)->queue->sleepQLock );
+
+#define _PR_MISCQ_LOCK(_cpu) \
+ _PR_MD_LOCK(&(_cpu)->queue->miscQLock );
+
+#define _PR_MISCQ_UNLOCK(_cpu) \
+ _PR_MD_UNLOCK(&(_cpu)->queue->miscQLock );
+
+#define _PR_CPU_LIST_LOCK() _PR_MD_LOCK(&_pr_cpuLock)
+#define _PR_CPU_LIST_UNLOCK() _PR_MD_UNLOCK(&_pr_cpuLock)
+
+#define QUEUE_RUN 0x1
+#define QUEUE_SLEEP 0x2
+#define QUEUE_JOIN 0x4
+#define QUEUE_SUSPEND 0x8
+#define QUEUE_LOCK 0x10
+
+#define _PR_ADD_RUNQ(_thread, _cpu, _pri) \
+ PR_BEGIN_MACRO \
+ PR_APPEND_LINK(&(_thread)->links, &_PR_RUNQ(_cpu)[_pri]); \
+ _PR_RUNQREADYMASK(_cpu) |= (1L << _pri); \
+ PR_ASSERT((_thread)->queueCount == 0); \
+ (_thread)->queueCount = QUEUE_RUN; \
+ PR_END_MACRO
+
+#define _PR_DEL_RUNQ(_thread) \
+ PR_BEGIN_MACRO \
+ _PRCPU *_cpu = _thread->cpu; \
+ PRInt32 _pri = _thread->priority; \
+ PR_REMOVE_LINK(&(_thread)->links); \
+ if (PR_CLIST_IS_EMPTY(&_PR_RUNQ(_cpu)[_pri])) \
+ _PR_RUNQREADYMASK(_cpu) &= ~(1L << _pri); \
+ PR_ASSERT((_thread)->queueCount == QUEUE_RUN);\
+ (_thread)->queueCount = 0; \
+ PR_END_MACRO
+
+#define _PR_ADD_SLEEPQ(_thread, _timeout) \
+ PR_ASSERT((_thread)->queueCount == 0); \
+ (_thread)->queueCount = QUEUE_SLEEP; \
+ _PR_AddSleepQ(_thread, _timeout);
+
+#define _PR_DEL_SLEEPQ(_thread, _propogate) \
+ PR_ASSERT((_thread)->queueCount == QUEUE_SLEEP);\
+ (_thread)->queueCount = 0; \
+ _PR_DelSleepQ(_thread, _propogate);
+
+#define _PR_ADD_JOINQ(_thread, _cpu) \
+ PR_ASSERT((_thread)->queueCount == 0); \
+ (_thread)->queueCount = QUEUE_JOIN; \
+ PR_APPEND_LINK(&(_thread)->links, &_PR_WAITINGTOJOINQ(_cpu));
+
+#define _PR_DEL_JOINQ(_thread) \
+ PR_ASSERT((_thread)->queueCount == QUEUE_JOIN);\
+ (_thread)->queueCount = 0; \
+ PR_REMOVE_LINK(&(_thread)->links);
+
+#define _PR_ADD_SUSPENDQ(_thread, _cpu) \
+ PR_ASSERT((_thread)->queueCount == 0); \
+ (_thread)->queueCount = QUEUE_SUSPEND; \
+ PR_APPEND_LINK(&(_thread)->links, &_PR_SUSPENDQ(_cpu));
+
+#define _PR_DEL_SUSPENDQ(_thread) \
+ PR_ASSERT((_thread)->queueCount == QUEUE_SUSPEND);\
+ (_thread)->queueCount = 0; \
+ PR_REMOVE_LINK(&(_thread)->links);
+
+#define _PR_THREAD_SWITCH_CPU(_thread, _newCPU) \
+ (_thread)->cpu = (_newCPU);
+
+#define _PR_IS_NATIVE_THREAD(thread) (thread->flags & _PR_GLOBAL_SCOPE)
+#define _PR_IS_NATIVE_THREAD_SUPPORTED() 1
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+
+#define _PR_SET_RESCHED_FLAG() _PR_MD_CURRENT_CPU()->u.missed[3] = 1
+#define _PR_CLEAR_RESCHED_FLAG() _PR_MD_CURRENT_CPU()->u.missed[3] = 0
+
+extern _PRInterruptTable _pr_interruptTable[];
+
+/* Bits for _pr_interruptState.u.missed[0,1] */
+#define _PR_MISSED_CLOCK 0x1
+#define _PR_MISSED_IO 0x2
+#define _PR_MISSED_CHILD 0x4
+
+extern void _PR_IntsOn(_PRCPU *cpu);
+
+NSPR_API(void) _PR_WakeupCPU(void);
+NSPR_API(void) _PR_PauseCPU(void);
+
+/************************************************************************/
+
+#define _PR_LOCK_LOCK(_lock) \
+ _PR_MD_LOCK(&(_lock)->ilock);
+#define _PR_LOCK_UNLOCK(_lock) \
+ _PR_MD_UNLOCK(&(_lock)->ilock);
+
+extern void _PR_UnblockLockWaiter(PRLock *lock);
+extern PRStatus _PR_InitLock(PRLock *lock);
+extern void _PR_FreeLock(PRLock *lock);
+
+#define _PR_LOCK_PTR(_qp) \
+ ((PRLock*) ((char*) (_qp) - offsetof(PRLock,links)))
+
+/************************************************************************/
+
+#define _PR_CVAR_LOCK(_cvar) \
+ _PR_MD_LOCK(&(_cvar)->ilock);
+#define _PR_CVAR_UNLOCK(_cvar) \
+ _PR_MD_UNLOCK(&(_cvar)->ilock);
+
+extern PRStatus _PR_InitCondVar(PRCondVar *cvar, PRLock *lock);
+extern void _PR_FreeCondVar(PRCondVar *cvar);
+extern PRStatus _PR_WaitCondVar(
+ PRThread *thread, PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout);
+extern void _PR_NotifyCondVar(PRCondVar *cvar, PRThread *me);
+extern PRUint32 _PR_CondVarToString(PRCondVar *cvar, char *buf, PRUint32 buflen);
+
+NSPR_API(void) _PR_Notify(PRMonitor *mon, PRBool all, PRBool sticky);
+
+/* PRThread.flags */
+#define _PR_SYSTEM 0x01
+#define _PR_INTERRUPT 0x02
+#define _PR_ATTACHED 0x04 /* created via PR_AttachThread */
+#define _PR_PRIMORDIAL 0x08 /* the thread that called PR_Init */
+#define _PR_ON_SLEEPQ 0x10 /* thread is on the sleepQ */
+#define _PR_ON_PAUSEQ 0x20 /* thread is on the pauseQ */
+#define _PR_SUSPENDING 0x40 /* thread wants to suspend */
+#define _PR_GLOBAL_SCOPE 0x80 /* thread is global scope */
+#define _PR_IDLE_THREAD 0x200 /* this is an idle thread */
+#define _PR_GCABLE_THREAD 0x400 /* this is a collectable thread */
+#define _PR_BOUND_THREAD 0x800 /* a bound thread */
+#define _PR_INTERRUPT_BLOCKED 0x1000 /* interrupts blocked */
+
+/* PRThread.state */
+#define _PR_UNBORN 0
+#define _PR_RUNNABLE 1
+#define _PR_RUNNING 2
+#define _PR_LOCK_WAIT 3
+#define _PR_COND_WAIT 4
+#define _PR_JOIN_WAIT 5
+#define _PR_IO_WAIT 6
+#define _PR_SUSPENDED 7
+#define _PR_DEAD_STATE 8 /* for debugging */
+
+/* PRThreadStack.flags */
+#define _PR_STACK_VM 0x1 /* using vm instead of malloc */
+#define _PR_STACK_MAPPED 0x2 /* vm is mapped */
+#define _PR_STACK_PRIMORDIAL 0x4 /* stack for primordial thread */
+
+/*
+** If the default stcksize from the client is zero, we need to pick a machine
+** dependent value. This is only for standard user threads. For custom threads,
+** 0 has a special meaning.
+** Adjust stackSize. Round up to a page boundary.
+*/
+
+#ifndef _MD_MINIMUM_STACK_SIZE
+#define _MD_MINIMUM_STACK_SIZE 0
+#endif
+
+#if (!defined(HAVE_CUSTOM_USER_THREADS))
+#define _PR_ADJUST_STACKSIZE(stackSize) \
+ PR_BEGIN_MACRO \
+ if (stackSize == 0) \
+ stackSize = _MD_DEFAULT_STACK_SIZE; \
+ if (stackSize < _MD_MINIMUM_STACK_SIZE) \
+ stackSize = _MD_MINIMUM_STACK_SIZE; \
+ stackSize = (stackSize + (1 << _pr_pageShift) - 1) >> _pr_pageShift; \
+ stackSize <<= _pr_pageShift; \
+ PR_END_MACRO
+#else
+#define _PR_ADJUST_STACKSIZE(stackSize)
+#endif
+
+#define _PR_IS_GCABLE_THREAD(thr) ((thr)->flags & _PR_GCABLE_THREAD)
+
+#define _PR_PENDING_INTERRUPT(thr) \
+ (!((thr)->flags & _PR_INTERRUPT_BLOCKED) && ((thr)->flags & _PR_INTERRUPT))
+#define _PR_THREAD_BLOCK_INTERRUPT(thr) \
+ (thr->flags |= _PR_INTERRUPT_BLOCKED)
+#define _PR_THREAD_UNBLOCK_INTERRUPT(thr) \
+ (thr->flags &= ~_PR_INTERRUPT_BLOCKED)
+
+#define _PR_THREAD_PTR(_qp) \
+ ((PRThread*) ((char*) (_qp) - offsetof(PRThread,links)))
+
+#define _PR_ACTIVE_THREAD_PTR(_qp) \
+ ((PRThread*) ((char*) (_qp) - offsetof(PRThread,active)))
+
+#define _PR_THREAD_CONDQ_PTR(_qp) \
+ ((PRThread*) ((char*) (_qp) - offsetof(PRThread,waitQLinks)))
+
+#define _PR_THREAD_MD_TO_PTR(_md) \
+ ((PRThread*) ((char*) (_md) - offsetof(PRThread,md)))
+
+#define _PR_THREAD_STACK_TO_PTR(_stack) \
+ ((PRThread*) (_stack->thr))
+
+extern PRCList _pr_active_local_threadQ;
+extern PRCList _pr_active_global_threadQ;
+extern PRCList _pr_cpuQ;
+extern _MDLock _pr_cpuLock;
+extern PRInt32 _pr_md_idle_cpus;
+
+#define _PR_ACTIVE_LOCAL_THREADQ() _pr_active_local_threadQ
+#define _PR_ACTIVE_GLOBAL_THREADQ() _pr_active_global_threadQ
+#define _PR_CPUQ() _pr_cpuQ
+#define _PR_RUNQ(_cpu) ((_cpu)->queue->runQ)
+#define _PR_RUNQREADYMASK(_cpu) ((_cpu)->queue->runQReadyMask)
+#define _PR_SLEEPQ(_cpu) ((_cpu)->queue->sleepQ)
+#define _PR_SLEEPQMAX(_cpu) ((_cpu)->queue->sleepQmax)
+#define _PR_PAUSEQ(_cpu) ((_cpu)->queue->pauseQ)
+#define _PR_SUSPENDQ(_cpu) ((_cpu)->queue->suspendQ)
+#define _PR_WAITINGTOJOINQ(_cpu) ((_cpu)->queue->waitingToJoinQ)
+
+extern PRUint32 _pr_recycleThreads; /* Flag for behavior on thread cleanup */
+extern PRLock *_pr_deadQLock;
+extern PRUint32 _pr_numNativeDead;
+extern PRUint32 _pr_numUserDead;
+extern PRCList _pr_deadNativeQ;
+extern PRCList _pr_deadUserQ;
+#define _PR_DEADNATIVEQ _pr_deadNativeQ
+#define _PR_DEADUSERQ _pr_deadUserQ
+#define _PR_DEADQ_LOCK PR_Lock(_pr_deadQLock);
+#define _PR_DEADQ_UNLOCK PR_Unlock(_pr_deadQLock);
+#define _PR_INC_DEADNATIVE (_pr_numNativeDead++)
+#define _PR_DEC_DEADNATIVE (_pr_numNativeDead--)
+#define _PR_NUM_DEADNATIVE (_pr_numNativeDead)
+#define _PR_INC_DEADUSER (_pr_numUserDead++)
+#define _PR_DEC_DEADUSER (_pr_numUserDead--)
+#define _PR_NUM_DEADUSER (_pr_numUserDead)
+
+extern PRUint32 _pr_utid;
+
+extern struct _PRCPU *_pr_primordialCPU;
+
+extern PRLock *_pr_activeLock; /* lock for userActive and systemActive */
+extern PRInt32 _pr_userActive; /* number of active user threads */
+extern PRInt32 _pr_systemActive; /* number of active system threads */
+extern PRInt32 _pr_primordialExitCount; /* number of user threads left
+ * before the primordial thread
+ * can exit. */
+extern PRCondVar *_pr_primordialExitCVar; /* the condition variable for
+ * notifying the primordial thread
+ * when all other user threads
+ * have terminated. */
+
+extern PRUintn _pr_maxPTDs;
+
+extern PRLock *_pr_terminationCVLock;
+
+/*************************************************************************
+* Internal routines either called by PR itself or from machine-dependent *
+* code. *
+*************************************************************************/
+
+extern void _PR_ClockInterrupt(void);
+
+extern void _PR_Schedule(void);
+extern void _PR_SetThreadPriority(
+ PRThread* thread, PRThreadPriority priority);
+
+/***********************************************************************
+** FUNCTION: _PR_NewSegment()
+** DESCRIPTION:
+** Allocate a memory segment. The "size" value is rounded up to the
+** native system page size and a page aligned portion of memory is
+** returned. This memory is not part of the malloc heap. If "vaddr" is
+** not NULL then PR tries to allocate the segment at the desired virtual
+** address.
+** INPUTS: size: size of the desired memory segment
+** vaddr: address at which the newly aquired segment is to be
+** mapped into memory.
+** OUTPUTS: a memory segment is allocated, a PRSegment is allocated
+** RETURN: pointer to PRSegment
+***********************************************************************/
+extern PRSegment* _PR_NewSegment(PRUint32 size, void *vaddr);
+
+/***********************************************************************
+** FUNCTION: _PR_DestroySegment()
+** DESCRIPTION:
+** The memory segment and the PRSegment are freed
+** INPUTS: seg: pointer to PRSegment to be freed
+** OUTPUTS: the the PRSegment and its associated memory segment are freed
+** RETURN: void
+***********************************************************************/
+extern void _PR_DestroySegment(PRSegment *seg);
+
+extern PRThreadStack * _PR_NewStack(PRUint32 stackSize);
+extern void _PR_FreeStack(PRThreadStack *stack);
+extern PRBool _PR_NotifyThread (PRThread *thread, PRThread *me);
+extern void _PR_NotifyLockedThread (PRThread *thread);
+
+NSPR_API(void) _PR_AddSleepQ(PRThread *thread, PRIntervalTime timeout);
+NSPR_API(void) _PR_DelSleepQ(PRThread *thread, PRBool propogate_time);
+
+extern void _PR_AddThreadToRunQ(PRThread *me, PRThread *thread);
+
+NSPR_API(PRThread*) _PR_CreateThread(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize,
+ PRUint32 flags);
+
+extern void _PR_NativeDestroyThread(PRThread *thread);
+extern void _PR_UserDestroyThread(PRThread *thread);
+
+extern PRThread* _PRI_AttachThread(
+ PRThreadType type, PRThreadPriority priority,
+ PRThreadStack *stack, PRUint32 flags);
+
+extern void _PRI_DetachThread(void);
+
+
+#define _PR_IO_PENDING(_thread) ((_thread)->io_pending)
+
+NSPR_API(void) _PR_MD_INIT_CPUS();
+#define _PR_MD_INIT_CPUS _MD_INIT_CPUS
+
+NSPR_API(void) _PR_MD_WAKEUP_CPUS();
+#define _PR_MD_WAKEUP_CPUS _MD_WAKEUP_CPUS
+
+/* Interrupts related */
+
+NSPR_API(void) _PR_MD_START_INTERRUPTS(void);
+#define _PR_MD_START_INTERRUPTS _MD_START_INTERRUPTS
+
+NSPR_API(void) _PR_MD_STOP_INTERRUPTS(void);
+#define _PR_MD_STOP_INTERRUPTS _MD_STOP_INTERRUPTS
+
+NSPR_API(void) _PR_MD_ENABLE_CLOCK_INTERRUPTS(void);
+#define _PR_MD_ENABLE_CLOCK_INTERRUPTS _MD_ENABLE_CLOCK_INTERRUPTS
+
+NSPR_API(void) _PR_MD_DISABLE_CLOCK_INTERRUPTS(void);
+#define _PR_MD_DISABLE_CLOCK_INTERRUPTS _MD_DISABLE_CLOCK_INTERRUPTS
+
+NSPR_API(void) _PR_MD_BLOCK_CLOCK_INTERRUPTS(void);
+#define _PR_MD_BLOCK_CLOCK_INTERRUPTS _MD_BLOCK_CLOCK_INTERRUPTS
+
+NSPR_API(void) _PR_MD_UNBLOCK_CLOCK_INTERRUPTS(void);
+#define _PR_MD_UNBLOCK_CLOCK_INTERRUPTS _MD_UNBLOCK_CLOCK_INTERRUPTS
+
+/* The _PR_MD_WAIT_LOCK and _PR_MD_WAKEUP_WAITER functions put to sleep and
+ * awaken a thread which is waiting on a lock or cvar.
+ */
+extern PRStatus _PR_MD_WAIT(PRThread *, PRIntervalTime timeout);
+#define _PR_MD_WAIT _MD_WAIT
+
+extern PRStatus _PR_MD_WAKEUP_WAITER(PRThread *);
+#define _PR_MD_WAKEUP_WAITER _MD_WAKEUP_WAITER
+
+#ifndef _PR_LOCAL_THREADS_ONLY /* not if only local threads supported */
+NSPR_API(void) _PR_MD_CLOCK_INTERRUPT(void);
+#define _PR_MD_CLOCK_INTERRUPT _MD_CLOCK_INTERRUPT
+#endif
+
+/* Stack debugging */
+NSPR_API(void) _PR_MD_INIT_STACK(PRThreadStack *ts, PRIntn redzone);
+#define _PR_MD_INIT_STACK _MD_INIT_STACK
+
+NSPR_API(void) _PR_MD_CLEAR_STACK(PRThreadStack* ts);
+#define _PR_MD_CLEAR_STACK _MD_CLEAR_STACK
+
+/* CPU related */
+NSPR_API(PRInt32) _PR_MD_GET_INTSOFF(void);
+#define _PR_MD_GET_INTSOFF _MD_GET_INTSOFF
+
+NSPR_API(void) _PR_MD_SET_INTSOFF(PRInt32 _val);
+#define _PR_MD_SET_INTSOFF _MD_SET_INTSOFF
+
+NSPR_API(_PRCPU*) _PR_MD_CURRENT_CPU(void);
+#define _PR_MD_CURRENT_CPU _MD_CURRENT_CPU
+
+NSPR_API(void) _PR_MD_SET_CURRENT_CPU(_PRCPU *cpu);
+#define _PR_MD_SET_CURRENT_CPU _MD_SET_CURRENT_CPU
+
+NSPR_API(void) _PR_MD_INIT_RUNNING_CPU(_PRCPU *cpu);
+#define _PR_MD_INIT_RUNNING_CPU _MD_INIT_RUNNING_CPU
+
+/*
+ * Returns the number of threads awoken or 0 if a timeout occurred;
+ */
+extern PRInt32 _PR_MD_PAUSE_CPU(PRIntervalTime timeout);
+#define _PR_MD_PAUSE_CPU _MD_PAUSE_CPU
+
+extern void _PR_MD_CLEANUP_BEFORE_EXIT(void);
+#define _PR_MD_CLEANUP_BEFORE_EXIT _MD_CLEANUP_BEFORE_EXIT
+
+extern void _PR_MD_EXIT(PRIntn status);
+#define _PR_MD_EXIT _MD_EXIT
+
+/* Locks related */
+
+NSPR_API(void) _PR_MD_INIT_LOCKS(void);
+#define _PR_MD_INIT_LOCKS _MD_INIT_LOCKS
+
+NSPR_API(PRStatus) _PR_MD_NEW_LOCK(_MDLock *md);
+#define _PR_MD_NEW_LOCK _MD_NEW_LOCK
+
+NSPR_API(void) _PR_MD_FREE_LOCK(_MDLock *md);
+#define _PR_MD_FREE_LOCK _MD_FREE_LOCK
+
+NSPR_API(void) _PR_MD_LOCK(_MDLock *md);
+#define _PR_MD_LOCK _MD_LOCK
+
+/* Return 0 on success, a nonzero value on failure. */
+NSPR_API(PRIntn) _PR_MD_TEST_AND_LOCK(_MDLock *md);
+#define _PR_MD_TEST_AND_LOCK _MD_TEST_AND_LOCK
+
+NSPR_API(void) _PR_MD_UNLOCK(_MDLock *md);
+#define _PR_MD_UNLOCK _MD_UNLOCK
+
+NSPR_API(void) _PR_MD_IOQ_LOCK(void);
+#define _PR_MD_IOQ_LOCK _MD_IOQ_LOCK
+
+NSPR_API(void) _PR_MD_IOQ_UNLOCK(void);
+#define _PR_MD_IOQ_UNLOCK _MD_IOQ_UNLOCK
+
+#ifndef _PR_LOCAL_THREADS_ONLY /* not if only local threads supported */
+/* Semaphore related -- only for native threads */
+#ifdef HAVE_CVAR_BUILT_ON_SEM
+NSPR_API(void) _PR_MD_NEW_SEM(_MDSemaphore *md, PRUintn value);
+#define _PR_MD_NEW_SEM _MD_NEW_SEM
+
+NSPR_API(void) _PR_MD_DESTROY_SEM(_MDSemaphore *md);
+#define _PR_MD_DESTROY_SEM _MD_DESTROY_SEM
+
+NSPR_API(PRStatus) _PR_MD_TIMED_WAIT_SEM(
+ _MDSemaphore *md, PRIntervalTime timeout);
+#define _PR_MD_TIMED_WAIT_SEM _MD_TIMED_WAIT_SEM
+
+NSPR_API(PRStatus) _PR_MD_WAIT_SEM(_MDSemaphore *md);
+#define _PR_MD_WAIT_SEM _MD_WAIT_SEM
+
+NSPR_API(void) _PR_MD_POST_SEM(_MDSemaphore *md);
+#define _PR_MD_POST_SEM _MD_POST_SEM
+#endif /* HAVE_CVAR_BUILT_ON_SEM */
+
+#endif
+
+/* Condition Variables related -- only for native threads */
+
+#ifndef _PR_LOCAL_THREADS_ONLY /* not if only local threads supported */
+NSPR_API(PRInt32) _PR_MD_NEW_CV(_MDCVar *md);
+#define _PR_MD_NEW_CV _MD_NEW_CV
+
+NSPR_API(void) _PR_MD_FREE_CV(_MDCVar *md);
+#define _PR_MD_FREE_CV _MD_FREE_CV
+
+NSPR_API(void) _PR_MD_WAIT_CV(
+ _MDCVar *mdCVar,_MDLock *mdLock,PRIntervalTime timeout);
+#define _PR_MD_WAIT_CV _MD_WAIT_CV
+
+NSPR_API(void) _PR_MD_NOTIFY_CV(_MDCVar *md, _MDLock *lock);
+#define _PR_MD_NOTIFY_CV _MD_NOTIFY_CV
+
+NSPR_API(void) _PR_MD_NOTIFYALL_CV(_MDCVar *md, _MDLock *lock);
+#define _PR_MD_NOTIFYALL_CV _MD_NOTIFYALL_CV
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+/* Threads related */
+NSPR_API(PRThread*) _PR_MD_CURRENT_THREAD(void);
+#define _PR_MD_CURRENT_THREAD _MD_CURRENT_THREAD
+
+NSPR_API(PRThread*) _PR_MD_GET_ATTACHED_THREAD(void);
+#define _PR_MD_GET_ATTACHED_THREAD _MD_GET_ATTACHED_THREAD
+
+NSPR_API(PRThread*) _PR_MD_LAST_THREAD(void);
+#define _PR_MD_LAST_THREAD _MD_LAST_THREAD
+
+NSPR_API(void) _PR_MD_SET_CURRENT_THREAD(PRThread *thread);
+#define _PR_MD_SET_CURRENT_THREAD _MD_SET_CURRENT_THREAD
+
+NSPR_API(void) _PR_MD_SET_LAST_THREAD(PRThread *thread);
+#define _PR_MD_SET_LAST_THREAD _MD_SET_LAST_THREAD
+
+extern PRStatus _PR_MD_INIT_THREAD(PRThread *thread);
+#define _PR_MD_INIT_THREAD _MD_INIT_THREAD
+
+extern void _PR_MD_EXIT_THREAD(PRThread *thread);
+#define _PR_MD_EXIT_THREAD _MD_EXIT_THREAD
+
+#ifndef _PR_LOCAL_THREADS_ONLY /* not if only local threads supported */
+
+NSPR_API(PRStatus) _PR_MD_INIT_ATTACHED_THREAD(PRThread *thread);
+#define _PR_MD_INIT_ATTACHED_THREAD _MD_INIT_ATTACHED_THREAD
+
+extern void _PR_MD_SUSPEND_THREAD(PRThread *thread);
+#define _PR_MD_SUSPEND_THREAD _MD_SUSPEND_THREAD
+
+extern void _PR_MD_RESUME_THREAD(PRThread *thread);
+#define _PR_MD_RESUME_THREAD _MD_RESUME_THREAD
+
+extern void _PR_MD_SUSPEND_CPU(_PRCPU *cpu);
+#define _PR_MD_SUSPEND_CPU _MD_SUSPEND_CPU
+
+extern void _PR_MD_RESUME_CPU(_PRCPU *cpu);
+#define _PR_MD_RESUME_CPU _MD_RESUME_CPU
+
+extern void _PR_MD_BEGIN_SUSPEND_ALL(void);
+#define _PR_MD_BEGIN_SUSPEND_ALL _MD_BEGIN_SUSPEND_ALL
+
+extern void _PR_MD_END_SUSPEND_ALL(void);
+#define _PR_MD_END_SUSPEND_ALL _MD_END_SUSPEND_ALL
+
+extern void _PR_MD_BEGIN_RESUME_ALL(void);
+#define _PR_MD_BEGIN_RESUME_ALL _MD_BEGIN_RESUME_ALL
+
+extern void _PR_MD_END_RESUME_ALL(void);
+#define _PR_MD_END_RESUME_ALL _MD_END_RESUME_ALL
+
+#endif /* !_PR_LOCAL_THREADS_ONLY */
+
+extern void _PR_MD_CLEAN_THREAD(PRThread *thread);
+#define _PR_MD_CLEAN_THREAD _MD_CLEAN_THREAD
+
+#ifdef HAVE_CUSTOM_USER_THREADS
+extern void _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(PRThread *);
+#define _PR_MD_CREATE_PRIMORDIAL_USER_THREAD _MD_CREATE_PRIMORDIAL_USER_THREAD
+
+extern PRThread* _PR_MD_CREATE_USER_THREAD(
+ PRUint32 stacksize,
+ void (*start)(void *),
+ void *arg);
+#define _PR_MD_CREATE_USER_THREAD _MD_CREATE_USER_THREAD
+#endif
+
+extern PRStatus _PR_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+#define _PR_MD_CREATE_THREAD _MD_CREATE_THREAD
+
+extern void _PR_MD_JOIN_THREAD(_MDThread *md);
+#define _PR_MD_JOIN_THREAD _MD_JOIN_THREAD
+
+extern void _PR_MD_END_THREAD(void);
+#define _PR_MD_END_THREAD _MD_END_THREAD
+
+extern void _PR_MD_YIELD(void);
+#define _PR_MD_YIELD _MD_YIELD
+
+extern void _PR_MD_SET_PRIORITY(_MDThread *md, PRThreadPriority newPri);
+#define _PR_MD_SET_PRIORITY _MD_SET_PRIORITY
+
+extern void _PR_MD_SET_CURRENT_THREAD_NAME(const char *name);
+#define _PR_MD_SET_CURRENT_THREAD_NAME _MD_SET_CURRENT_THREAD_NAME
+
+NSPR_API(void) _PR_MD_SUSPENDALL(void);
+#define _PR_MD_SUSPENDALL _MD_SUSPENDALL
+
+NSPR_API(void) _PR_MD_RESUMEALL(void);
+#define _PR_MD_RESUMEALL _MD_RESUMEALL
+
+extern void _PR_MD_INIT_CONTEXT(
+ PRThread *thread, char *top, void (*start) (void), PRBool *status);
+#define _PR_MD_INIT_CONTEXT _MD_INIT_CONTEXT
+
+extern void _PR_MD_SWITCH_CONTEXT(PRThread *thread);
+#define _PR_MD_SWITCH_CONTEXT _MD_SWITCH_CONTEXT
+
+extern void _PR_MD_RESTORE_CONTEXT(PRThread *thread);
+#define _PR_MD_RESTORE_CONTEXT _MD_RESTORE_CONTEXT
+
+/* Segment related */
+extern void _PR_MD_INIT_SEGS(void);
+#define _PR_MD_INIT_SEGS _MD_INIT_SEGS
+
+extern PRStatus _PR_MD_ALLOC_SEGMENT(PRSegment *seg, PRUint32 size, void *vaddr);
+#define _PR_MD_ALLOC_SEGMENT _MD_ALLOC_SEGMENT
+
+extern void _PR_MD_FREE_SEGMENT(PRSegment *seg);
+#define _PR_MD_FREE_SEGMENT _MD_FREE_SEGMENT
+
+/* Directory enumeration related */
+extern PRStatus _PR_MD_OPEN_DIR(_MDDir *md,const char *name);
+#define _PR_MD_OPEN_DIR _MD_OPEN_DIR
+
+extern char * _PR_MD_READ_DIR(_MDDir *md, PRIntn flags);
+#define _PR_MD_READ_DIR _MD_READ_DIR
+
+extern PRInt32 _PR_MD_CLOSE_DIR(_MDDir *md);
+#define _PR_MD_CLOSE_DIR _MD_CLOSE_DIR
+
+/* Named semaphores related */
+extern PRSem * _PR_MD_OPEN_SEMAPHORE(
+ const char *osname, PRIntn flags, PRIntn mode, PRUintn value);
+#define _PR_MD_OPEN_SEMAPHORE _MD_OPEN_SEMAPHORE
+
+extern PRStatus _PR_MD_WAIT_SEMAPHORE(PRSem *sem);
+#define _PR_MD_WAIT_SEMAPHORE _MD_WAIT_SEMAPHORE
+
+extern PRStatus _PR_MD_POST_SEMAPHORE(PRSem *sem);
+#define _PR_MD_POST_SEMAPHORE _MD_POST_SEMAPHORE
+
+extern PRStatus _PR_MD_CLOSE_SEMAPHORE(PRSem *sem);
+#define _PR_MD_CLOSE_SEMAPHORE _MD_CLOSE_SEMAPHORE
+
+extern PRStatus _PR_MD_DELETE_SEMAPHORE(const char *osname);
+#define _PR_MD_DELETE_SEMAPHORE _MD_DELETE_SEMAPHORE
+
+/* I/O related */
+extern void _PR_MD_INIT_FILEDESC(PRFileDesc *fd);
+#define _PR_MD_INIT_FILEDESC _MD_INIT_FILEDESC
+
+extern void _PR_MD_MAKE_NONBLOCK(PRFileDesc *fd);
+#define _PR_MD_MAKE_NONBLOCK _MD_MAKE_NONBLOCK
+
+/* File I/O related */
+extern PROsfd _PR_MD_OPEN(const char *name, PRIntn osflags, PRIntn mode);
+#define _PR_MD_OPEN _MD_OPEN
+
+extern PROsfd _PR_MD_OPEN_FILE(const char *name, PRIntn osflags, PRIntn mode);
+#define _PR_MD_OPEN_FILE _MD_OPEN_FILE
+
+extern PRInt32 _PR_MD_CLOSE_FILE(PROsfd osfd);
+#define _PR_MD_CLOSE_FILE _MD_CLOSE_FILE
+
+extern PRInt32 _PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 amount);
+#define _PR_MD_READ _MD_READ
+
+extern PRInt32 _PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 amount);
+#define _PR_MD_WRITE _MD_WRITE
+
+extern PRInt32 _PR_MD_WRITEV(
+ PRFileDesc *fd, const struct PRIOVec *iov,
+ PRInt32 iov_size, PRIntervalTime timeout);
+#define _PR_MD_WRITEV _MD_WRITEV
+
+extern PRInt32 _PR_MD_FSYNC(PRFileDesc *fd);
+#define _PR_MD_FSYNC _MD_FSYNC
+
+extern PRInt32 _PR_MD_DELETE(const char *name);
+#define _PR_MD_DELETE _MD_DELETE
+
+extern PRInt32 _PR_MD_RENAME(const char *from, const char *to);
+#define _PR_MD_RENAME _MD_RENAME
+
+extern PRInt32 _PR_MD_ACCESS(const char *name, PRAccessHow how);
+#define _PR_MD_ACCESS _MD_ACCESS
+
+extern PRInt32 _PR_MD_STAT(const char *name, struct stat *buf);
+#define _PR_MD_STAT _MD_STAT
+
+extern PRInt32 _PR_MD_MKDIR(const char *name, PRIntn mode);
+#define _PR_MD_MKDIR _MD_MKDIR
+
+extern PRInt32 _PR_MD_MAKE_DIR(const char *name, PRIntn mode);
+#define _PR_MD_MAKE_DIR _MD_MAKE_DIR
+
+extern PRInt32 _PR_MD_RMDIR(const char *name);
+#define _PR_MD_RMDIR _MD_RMDIR
+
+#ifdef MOZ_UNICODE
+/* UTF16 File I/O related */
+extern PRStatus _PR_MD_OPEN_DIR_UTF16(_MDDirUTF16 *md, const PRUnichar *name);
+#define _PR_MD_OPEN_DIR_UTF16 _MD_OPEN_DIR_UTF16
+
+extern PROsfd _PR_MD_OPEN_FILE_UTF16(const PRUnichar *name, PRIntn osflags, PRIntn mode);
+#define _PR_MD_OPEN_FILE_UTF16 _MD_OPEN_FILE_UTF16
+
+extern PRUnichar * _PR_MD_READ_DIR_UTF16(_MDDirUTF16 *md, PRIntn flags);
+#define _PR_MD_READ_DIR_UTF16 _MD_READ_DIR_UTF16
+
+extern PRInt32 _PR_MD_CLOSE_DIR_UTF16(_MDDirUTF16 *md);
+#define _PR_MD_CLOSE_DIR_UTF16 _MD_CLOSE_DIR_UTF16
+
+extern PRInt32 _PR_MD_GETFILEINFO64_UTF16(const PRUnichar *fn, PRFileInfo64 *info);
+#define _PR_MD_GETFILEINFO64_UTF16 _MD_GETFILEINFO64_UTF16
+#endif /* MOZ_UNICODE */
+
+/* Socket I/O related */
+extern void _PR_MD_INIT_IO(void);
+#define _PR_MD_INIT_IO _MD_INIT_IO
+
+extern PRInt32 _PR_MD_CLOSE_SOCKET(PROsfd osfd);
+#define _PR_MD_CLOSE_SOCKET _MD_CLOSE_SOCKET
+
+extern PRInt32 _PR_MD_CONNECT(
+ PRFileDesc *fd, const PRNetAddr *addr,
+ PRUint32 addrlen, PRIntervalTime timeout);
+#define _PR_MD_CONNECT _MD_CONNECT
+
+extern PROsfd _PR_MD_ACCEPT(
+ PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen, PRIntervalTime timeout);
+#define _PR_MD_ACCEPT _MD_ACCEPT
+
+extern PRInt32 _PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen);
+#define _PR_MD_BIND _MD_BIND
+
+extern PRInt32 _PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog);
+#define _PR_MD_LISTEN _MD_LISTEN
+
+extern PRInt32 _PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how);
+#define _PR_MD_SHUTDOWN _MD_SHUTDOWN
+
+extern PRInt32 _PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+#define _PR_MD_RECV _MD_RECV
+
+extern PRInt32 _PR_MD_SEND(
+ PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout);
+#define _PR_MD_SEND _MD_SEND
+
+extern PRInt32 _PR_MD_ACCEPT_READ(PRFileDesc *sd, PROsfd *newSock,
+ PRNetAddr **raddr, void *buf, PRInt32 amount,
+ PRIntervalTime timeout);
+#define _PR_MD_ACCEPT_READ _MD_ACCEPT_READ
+
+#ifdef WIN32
+extern PROsfd _PR_MD_FAST_ACCEPT(PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen, PRIntervalTime timeout,
+ PRBool fast,
+ _PR_AcceptTimeoutCallback callback,
+ void *callbackArg);
+
+extern PRInt32 _PR_MD_FAST_ACCEPT_READ(PRFileDesc *sd, PROsfd *newSock,
+ PRNetAddr **raddr, void *buf, PRInt32 amount,
+ PRIntervalTime timeout, PRBool fast,
+ _PR_AcceptTimeoutCallback callback,
+ void *callbackArg);
+
+extern void _PR_MD_UPDATE_ACCEPT_CONTEXT(PROsfd s, PROsfd ls);
+#define _PR_MD_UPDATE_ACCEPT_CONTEXT _MD_UPDATE_ACCEPT_CONTEXT
+/*
+ * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME.
+ * We store the value in a PRTime variable for convenience.
+ * This constant is used by _PR_FileTimeToPRTime().
+ * This is defined in ntmisc.c
+ */
+extern const PRTime _pr_filetime_offset;
+#endif /* WIN32 */
+
+extern PRInt32 _PR_MD_SENDFILE(
+ PRFileDesc *sock, PRSendFileData *sfd,
+ PRInt32 flags, PRIntervalTime timeout);
+#define _PR_MD_SENDFILE _MD_SENDFILE
+
+extern PRStatus _PR_MD_GETSOCKNAME(
+ PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen);
+#define _PR_MD_GETSOCKNAME _MD_GETSOCKNAME
+
+extern PRStatus _PR_MD_GETPEERNAME(
+ PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen);
+#define _PR_MD_GETPEERNAME _MD_GETPEERNAME
+
+extern PRStatus _PR_MD_GETSOCKOPT(
+ PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen);
+#define _PR_MD_GETSOCKOPT _MD_GETSOCKOPT
+
+extern PRStatus _PR_MD_SETSOCKOPT(
+ PRFileDesc *fd, PRInt32 level, PRInt32 optname,
+ const char* optval, PRInt32 optlen);
+#define _PR_MD_SETSOCKOPT _MD_SETSOCKOPT
+
+extern PRStatus PR_CALLBACK _PR_SocketGetSocketOption(
+ PRFileDesc *fd, PRSocketOptionData *data);
+
+extern PRStatus PR_CALLBACK _PR_SocketSetSocketOption(
+ PRFileDesc *fd, const PRSocketOptionData *data);
+
+extern PRInt32 _PR_MD_RECVFROM(
+ PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout);
+#define _PR_MD_RECVFROM _MD_RECVFROM
+
+extern PRInt32 _PR_MD_SENDTO(
+ PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout);
+#define _PR_MD_SENDTO _MD_SENDTO
+
+#if defined(_WIN64) && defined(WIN95)
+extern PRInt32 _PR_MD_TCPSENDTO(
+ PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout);
+#define _PR_MD_TCPSENDTO _MD_TCPSENDTO
+#endif
+
+extern PRInt32 _PR_MD_SOCKETPAIR(int af, int type, int flags, PROsfd *osfd);
+#define _PR_MD_SOCKETPAIR _MD_SOCKETPAIR
+
+extern PROsfd _PR_MD_SOCKET(int af, int type, int flags);
+#define _PR_MD_SOCKET _MD_SOCKET
+
+extern PRInt32 _PR_MD_SOCKETAVAILABLE(PRFileDesc *fd);
+#define _PR_MD_SOCKETAVAILABLE _MD_SOCKETAVAILABLE
+
+extern PRInt32 _PR_MD_PIPEAVAILABLE(PRFileDesc *fd);
+#define _PR_MD_PIPEAVAILABLE _MD_PIPEAVAILABLE
+
+extern PRInt32 _PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds,
+ PRIntervalTime timeout);
+#define _PR_MD_PR_POLL _MD_PR_POLL
+
+/*
+ * Initialize fd->secret->inheritable for a newly created fd.
+ * If 'imported' is false, the osfd (i.e., fd->secret->md.osfd)
+ * was created by NSPR and hence has the OS-dependent default
+ * inheritable attribute. If 'imported' is true, the osfd was
+ * not created by NSPR and hence a system call is required to
+ * query its inheritable attribute. Since we may never need to
+ * know the inheritable attribute of a fd, a platform may choose
+ * to initialize fd->secret->inheritable of an imported fd to
+ * _PR_TRI_UNKNOWN and only pay the cost of the system call
+ * (in _PR_MD_QUERY_FD_INHERITABLE) when necessary.
+ */
+extern void _PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported);
+#define _PR_MD_INIT_FD_INHERITABLE _MD_INIT_FD_INHERITABLE
+
+extern PRStatus _PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable);
+#define _PR_MD_SET_FD_INHERITABLE _MD_SET_FD_INHERITABLE
+
+
+#define _PR_PROCESS_TIMEOUT_INTERRUPT_ERRORS(me) \
+ if (_PR_PENDING_INTERRUPT(me)) { \
+ me->flags &= ~_PR_INTERRUPT; \
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); \
+ } else { \
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0); \
+ }
+
+extern void *_PR_MD_GET_SP(PRThread *thread);
+#define _PR_MD_GET_SP _MD_GET_SP
+
+#endif /* defined(_PR_PTHREADS) */
+
+/************************************************************************/
+/*************************************************************************
+** The remainder of the definitions are shared by pthreads and the classic
+** NSPR code. These too may be conditionalized.
+*************************************************************************/
+/************************************************************************/
+
+extern PROffset32 _PR_MD_LSEEK(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence);
+#define _PR_MD_LSEEK _MD_LSEEK
+
+extern PROffset64 _PR_MD_LSEEK64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence);
+#define _PR_MD_LSEEK64 _MD_LSEEK64
+
+extern PRInt32 _PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info);
+#define _PR_MD_GETFILEINFO _MD_GETFILEINFO
+
+extern PRInt32 _PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info);
+#define _PR_MD_GETFILEINFO64 _MD_GETFILEINFO64
+
+extern PRInt32 _PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info);
+#define _PR_MD_GETOPENFILEINFO _MD_GETOPENFILEINFO
+
+extern PRInt32 _PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info);
+#define _PR_MD_GETOPENFILEINFO64 _MD_GETOPENFILEINFO64
+
+
+/*****************************************************************************/
+/************************** File descriptor caching **************************/
+/*****************************************************************************/
+extern void _PR_InitFdCache(void);
+extern void _PR_CleanupFdCache(void);
+extern PRFileDesc *_PR_Getfd(void);
+extern void _PR_Putfd(PRFileDesc *fd);
+
+/*
+ * These flags are used by NSPR temporarily in the poll
+ * descriptor's out_flags field to record the mapping of
+ * NSPR's poll flags to the system poll flags.
+ *
+ * If _PR_POLL_READ_SYS_WRITE bit is set, it means the
+ * PR_POLL_READ flag specified by the topmost layer is
+ * mapped to the WRITE flag at the system layer. Similarly
+ * for the other three _PR_POLL_XXX_SYS_YYY flags. It is
+ * assumed that the PR_POLL_EXCEPT flag doesn't get mapped
+ * to other flags.
+ */
+#define _PR_POLL_READ_SYS_READ 0x1
+#define _PR_POLL_READ_SYS_WRITE 0x2
+#define _PR_POLL_WRITE_SYS_READ 0x4
+#define _PR_POLL_WRITE_SYS_WRITE 0x8
+
+/*
+** These methods are coerced into file descriptor methods table
+** when the intended service is inappropriate for the particular
+** type of file descriptor.
+*/
+extern PRIntn _PR_InvalidInt(void);
+extern PRInt16 _PR_InvalidInt16(void);
+extern PRInt64 _PR_InvalidInt64(void);
+extern PRStatus _PR_InvalidStatus(void);
+extern PRFileDesc *_PR_InvalidDesc(void);
+
+extern PRIOMethods _pr_faulty_methods;
+
+/*
+** The PR_NETADDR_SIZE macro can only be called on a PRNetAddr union
+** whose 'family' field is set. It returns the size of the union
+** member corresponding to the specified address family.
+*/
+
+extern PRUintn _PR_NetAddrSize(const PRNetAddr* addr);
+
+#if defined(_PR_INET6)
+
+#define PR_NETADDR_SIZE(_addr) _PR_NetAddrSize(_addr)
+
+#elif defined(_PR_HAVE_MD_SOCKADDR_IN6)
+
+/*
+** Under the following conditions:
+** 1. _PR_INET6 is not defined;
+** 2. _PR_INET6_PROBE is defined;
+** 3. struct sockaddr_in6 has nonstandard fields at the end
+** (e.g., on Solaris 8),
+** (_addr)->ipv6 is smaller than struct sockaddr_in6, and
+** hence we can't pass sizeof((_addr)->ipv6) to socket
+** functions such as connect because they would fail with
+** EINVAL.
+**
+** To pass the correct socket address length to socket
+** functions, define the macro _PR_HAVE_MD_SOCKADDR_IN6 and
+** define struct _md_sockaddr_in6 to be isomorphic to
+** struct sockaddr_in6.
+*/
+
+#if defined(XP_UNIX) || defined(XP_OS2)
+#define PR_NETADDR_SIZE(_addr) \
+ ((_addr)->raw.family == PR_AF_INET \
+ ? sizeof((_addr)->inet) \
+ : ((_addr)->raw.family == PR_AF_INET6 \
+ ? sizeof(struct _md_sockaddr_in6) \
+ : sizeof((_addr)->local)))
+#else
+#define PR_NETADDR_SIZE(_addr) \
+ ((_addr)->raw.family == PR_AF_INET \
+ ? sizeof((_addr)->inet) \
+ : sizeof(struct _md_sockaddr_in6))
+#endif /* defined(XP_UNIX) */
+
+#else
+
+#if defined(XP_UNIX) || defined(XP_OS2)
+#define PR_NETADDR_SIZE(_addr) \
+ ((_addr)->raw.family == PR_AF_INET \
+ ? sizeof((_addr)->inet) \
+ : ((_addr)->raw.family == PR_AF_INET6 \
+ ? sizeof((_addr)->ipv6) \
+ : sizeof((_addr)->local)))
+#else
+#define PR_NETADDR_SIZE(_addr) \
+ ((_addr)->raw.family == PR_AF_INET \
+ ? sizeof((_addr)->inet) \
+ : sizeof((_addr)->ipv6))
+#endif /* defined(XP_UNIX) */
+
+#endif /* defined(_PR_INET6) */
+
+extern PRStatus _PR_MapOptionName(
+ PRSockOption optname, PRInt32 *level, PRInt32 *name);
+extern void _PR_InitThreads(
+ PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs);
+
+struct PRLock {
+#if defined(_PR_PTHREADS)
+ pthread_mutex_t mutex; /* the underlying lock */
+ _PT_Notified notified; /* array of conditions notified */
+ PRBool locked; /* whether the mutex is locked */
+ pthread_t owner; /* if locked, current lock owner */
+#elif defined(_PR_BTHREADS)
+ sem_id semaphoreID; /* the underlying lock */
+ int32 benaphoreCount; /* number of people in lock */
+ thread_id owner; /* current lock owner */
+#else /* not pthreads or Be threads */
+ PRCList links; /* linkage for PRThread.lockList */
+ struct PRThread *owner; /* current lock owner */
+ PRCList waitQ; /* list of threads waiting for lock */
+ PRThreadPriority priority; /* priority of lock */
+ PRThreadPriority boostPriority; /* boosted priority of lock owner */
+ _MDLock ilock; /* Internal Lock to protect user-level fields */
+#endif
+};
+
+struct PRCondVar {
+ PRLock *lock; /* associated lock that protects the condition */
+#if defined(_PR_PTHREADS)
+ pthread_cond_t cv; /* underlying pthreads condition */
+ PRInt32 notify_pending; /* CV has destroy pending notification */
+#elif defined(_PR_BTHREADS)
+ sem_id sem; /* the underlying lock */
+ sem_id handshakeSem; /* the lock for 'notify'-threads waiting for confirmation */
+ sem_id signalSem; /* the lock for threads waiting for someone to notify */
+ volatile int32 nw; /* the number waiting */
+ volatile int32 ns; /* the number signalling */
+ long signalBenCount; /* the number waiting on the underlying sem */
+#else /* not pthreads or Be threads */
+ PRCList condQ; /* Condition variable wait Q */
+ _MDLock ilock; /* Internal Lock to protect condQ */
+ _MDCVar md;
+#endif
+};
+
+/************************************************************************/
+
+struct PRMonitor {
+ const char* name; /* monitor name for debugging */
+#if defined(_PR_PTHREADS)
+ pthread_mutex_t lock; /* lock is only held when accessing fields
+ * of the PRMonitor, instead of being held
+ * while the monitor is entered. The only
+ * exception is notifyTimes, which is
+ * protected by the monitor. */
+ pthread_t owner; /* the owner of the monitor or invalid */
+ pthread_cond_t entryCV; /* for threads waiting to enter the monitor */
+
+ pthread_cond_t waitCV; /* for threads waiting on the monitor */
+ PRInt32 refCount; /* reference count, an atomic variable.
+ * PR_NewMonitor adds a reference to the
+ * newly created PRMonitor, and
+ * PR_DestroyMonitor releases that reference.
+ * PR_ExitMonitor adds a reference before
+ * unlocking the internal lock if it needs to
+ * signal entryCV, and releases the reference
+ * after signaling entryCV. */
+#else /* defined(_PR_PTHREADS) */
+ PRLock lock; /* lock is only held when accessing fields
+ * of the PRMonitor, instead of being held
+ * while the monitor is entered. The only
+ * exception is notifyTimes, which is
+ * protected by the monitor. */
+ PRThread *owner; /* the owner of the monitor or invalid */
+ PRCondVar entryCV; /* for threads waiting to enter the monitor */
+
+ PRCondVar waitCV; /* for threads waiting on the monitor */
+#endif /* defined(_PR_PTHREADS) */
+ PRUint32 entryCount; /* # of times re-entered */
+ PRIntn notifyTimes; /* number of pending notifies for waitCV.
+ * The special value -1 means a broadcast
+ * (PR_NotifyAll). */
+};
+
+/************************************************************************/
+
+struct PRSemaphore {
+#if defined(_PR_BTHREADS)
+ sem_id sem;
+ int32 benaphoreCount;
+#else
+ PRCondVar *cvar; /* associated lock and condition variable queue */
+ PRUintn count; /* the value of the counting semaphore */
+ PRUint32 waiters; /* threads waiting on the semaphore */
+#if defined(_PR_PTHREADS)
+#else /* defined(_PR_PTHREADS) */
+ _MDSemaphore md;
+#endif /* defined(_PR_PTHREADS) */
+#endif /* defined(_PR_BTHREADS) */
+};
+
+/*************************************************************************/
+
+struct PRSem {
+#ifdef _PR_HAVE_POSIX_SEMAPHORES
+ sem_t *sem;
+#elif defined(_PR_HAVE_SYSV_SEMAPHORES)
+ int semid;
+#elif defined(WIN32)
+ HANDLE sem;
+#else
+ PRInt8 notused;
+#endif
+};
+
+/*************************************************************************/
+
+struct PRStackStr {
+ /* head MUST be at offset 0; assembly language code relies on this */
+#if defined(AIX)
+ volatile PRStackElem prstk_head;
+#else
+ PRStackElem prstk_head;
+#endif
+
+ PRLock *prstk_lock;
+ char *prstk_name;
+};
+
+/************************************************************************/
+
+/* XXX this needs to be exported (sigh) */
+struct PRThreadStack {
+ PRCList links;
+ PRUintn flags;
+
+ char *allocBase; /* base of stack's allocated memory */
+ PRUint32 allocSize; /* size of stack's allocated memory */
+ char *stackBottom; /* bottom of stack from C's point of view */
+ char *stackTop; /* top of stack from C's point of view */
+ PRUint32 stackSize; /* size of usable portion of the stack */
+
+ PRSegment *seg;
+ PRThread* thr; /* back pointer to thread owning this stack */
+
+#if defined(_PR_PTHREADS)
+#else /* defined(_PR_PTHREADS) */
+ _MDThreadStack md;
+#endif /* defined(_PR_PTHREADS) */
+};
+
+extern void _PR_DestroyThreadPrivate(PRThread*);
+
+typedef void (PR_CALLBACK *_PRStartFn)(void *);
+
+struct PRThread {
+ PRUint32 state; /* thread's creation state */
+ PRThreadPriority priority; /* apparent priority, loosly defined */
+
+ void *arg; /* argument to the client's entry point */
+ _PRStartFn startFunc; /* the root of the client's thread */
+
+ PRThreadStack *stack; /* info about thread's stack (for GC) */
+ void *environment; /* pointer to execution environment */
+
+ PRThreadDumpProc dump; /* dump thread info out */
+ void *dumpArg; /* argument for the dump function */
+
+ /*
+ ** Per thread private data
+ */
+ PRUint32 tpdLength; /* thread's current vector length */
+ void **privateData; /* private data vector or NULL */
+ PRErrorCode errorCode; /* current NSPR error code | zero */
+ PRInt32 osErrorCode; /* mapping of errorCode | zero */
+ PRIntn errorStringLength; /* textLength from last call to PR_SetErrorText() */
+ PRInt32 errorStringSize; /* malloc()'d size of buffer | zero */
+ char *errorString; /* current error string | NULL */
+ char *name; /* thread's name */
+
+#if defined(_PR_PTHREADS)
+ pthread_t id; /* pthread identifier for the thread */
+ PRBool idSet; /* whether 'id' has been set. Protected by
+ * pt_book.ml. */
+#ifdef _PR_NICE_PRIORITY_SCHEDULING
+ pid_t tid; /* Linux-specific kernel thread ID */
+#endif
+ PRBool okToDelete; /* ok to delete the PRThread struct? */
+ PRCondVar *waiting; /* where the thread is waiting | NULL */
+ void *sp; /* recorded sp for garbage collection */
+ PRThread *next, *prev; /* simple linked list of all threads */
+ PRUint32 suspend; /* used to store suspend and resume flags */
+#ifdef PT_NO_SIGTIMEDWAIT
+ pthread_mutex_t suspendResumeMutex;
+ pthread_cond_t suspendResumeCV;
+#endif
+ PRUint32 interrupt_blocked; /* interrupt blocked */
+ struct pollfd *syspoll_list; /* Unix polling list used by PR_Poll */
+ PRUint32 syspoll_count; /* number of elements in syspoll_list */
+#if defined(_PR_POLL_WITH_SELECT)
+ int *selectfd_list; /* Unix fd's that PR_Poll selects on */
+ PRUint32 selectfd_count; /* number of elements in selectfd_list */
+#endif
+#elif defined(_PR_BTHREADS)
+ PRUint32 flags;
+ _MDThread md;
+ PRBool io_pending;
+ PRInt32 io_fd;
+ PRBool io_suspended;
+#else /* not pthreads or Be threads */
+ _MDLock threadLock; /* Lock to protect thread state variables.
+ * Protects the following fields:
+ * state
+ * priority
+ * links
+ * wait
+ * cpu
+ */
+ PRUint32 queueCount;
+ PRUint32 waitCount;
+
+ PRCList active; /* on list of all active threads */
+ PRCList links;
+ PRCList waitQLinks; /* when thread is PR_Wait'ing */
+ PRCList lockList; /* list of locks currently holding */
+ PRIntervalTime sleep; /* sleep time when thread is sleeping */
+ struct _wait {
+ struct PRLock *lock;
+ struct PRCondVar *cvar;
+ } wait;
+
+ PRUint32 id;
+ PRUint32 flags;
+ PRUint32 no_sched; /* Don't schedule the thread to run.
+ * This flag has relevance only when
+ * multiple NSPR CPUs are created.
+ * When a thread is de-scheduled, there
+ * is a narrow window of time in which
+ * the thread is put on the run queue
+ * but the scheduler is actually using
+ * the stack of this thread. It is safe
+ * to run this thread on a different CPU
+ * only when its stack is not in use on
+ * any other CPU. The no_sched flag is
+ * set during this interval to prevent
+ * the thread from being scheduled on a
+ * different CPU.
+ */
+
+ /* thread termination condition variable for join */
+ PRCondVar *term;
+
+ _PRCPU *cpu; /* cpu to which this thread is bound */
+ PRUint32 threadAllocatedOnStack;/* boolean */
+
+ /* When an async IO is in progress and a second async IO cannot be
+ * initiated, the io_pending flag is set to true. Some platforms will
+ * not use the io_pending flag. If the io_pending flag is true, then
+ * io_fd is the OS-file descriptor on which IO is pending.
+ */
+ PRBool io_pending;
+ PRInt32 io_fd;
+
+ /* If a timeout occurs or if an outstanding IO is interrupted and the
+ * OS doesn't support a real cancellation (NT or MAC), then the
+ * io_suspended flag will be set to true. The thread will be resumed
+ * but may run into trouble issuing additional IOs until the io_pending
+ * flag can be cleared
+ */
+ PRBool io_suspended;
+
+ _MDThread md;
+#endif
+};
+
+struct PRProcessAttr {
+ PRFileDesc *stdinFd;
+ PRFileDesc *stdoutFd;
+ PRFileDesc *stderrFd;
+ char *currentDirectory;
+ char *fdInheritBuffer;
+ PRSize fdInheritBufferSize;
+ PRSize fdInheritBufferUsed;
+};
+
+struct PRProcess {
+ _MDProcess md;
+};
+
+struct PRFileMap {
+ PRFileDesc *fd;
+ PRFileMapProtect prot;
+ _MDFileMap md;
+};
+
+/************************************************************************/
+
+/*
+** File descriptors of the NSPR layer can be in one of the
+** following states (stored in the 'state' field of struct
+** PRFilePrivate):
+** - _PR_FILEDESC_OPEN: The OS fd is open.
+** - _PR_FILEDESC_CLOSED: The OS fd is closed. The PRFileDesc
+** is still open but is unusable. The only operation allowed
+** on the PRFileDesc is PR_Close().
+** - _PR_FILEDESC_FREED: The OS fd is closed and the PRFileDesc
+** structure is freed.
+*/
+
+#define _PR_FILEDESC_OPEN 0xaaaaaaaa /* 1010101... */
+#define _PR_FILEDESC_CLOSED 0x55555555 /* 0101010... */
+#define _PR_FILEDESC_FREED 0x11111111
+
+/*
+** A boolean type with an additional "unknown" state
+*/
+
+typedef enum {
+ _PR_TRI_TRUE = 1,
+ _PR_TRI_FALSE = 0,
+ _PR_TRI_UNKNOWN = -1
+} _PRTriStateBool;
+
+struct PRFilePrivate {
+ PRInt32 state;
+ PRBool nonblocking;
+ _PRTriStateBool inheritable;
+ PRFileDesc *next;
+ PRIntn lockCount; /* 0: not locked
+ * -1: a native lockfile call is in progress
+ * > 0: # times the file is locked */
+#ifdef _PR_HAVE_PEEK_BUFFER
+ char *peekBuffer;
+ PRInt32 peekBufSize;
+ PRInt32 peekBytes;
+#endif
+#if !defined(_PR_HAVE_O_APPEND)
+ PRBool appendMode; /* Some platforms don't have O_APPEND or its
+ * equivalent, so they have to seek to end of
+ * file on write if the file was opened in
+ * append mode. See Bugzilla 4090, 276330. */
+#endif
+ _MDFileDesc md;
+#ifdef _PR_NEED_SECRET_AF
+ PRUint16 af; /* If the platform's implementation of accept()
+ * requires knowing the address family of the
+ * socket, we save the address family here. */
+#endif
+
+#if defined(_WIN64)
+ /* This is necessary for TCP Fast Open. TCP Fast Open in windows must
+ * use ConnectEx function which uses OVERLAPPED. TCPSendTo will call
+ * ConnectEx to send fast open data. If ConnectEx returns
+ * ERROR_IO_PENDING we need to save OVERLAPPED structure and we will
+ * use it in ConnectContinue to get the final result of ConnectEx.
+ */
+ PRBool alreadyConnected;
+ PRBool overlappedActive;
+ OVERLAPPED ol;
+#endif
+};
+
+#ifdef _WIN64
+#define PR_PRIdOSFD "lld" /* for printing PROsfd */
+#define PR_PRIxOSFD "llx"
+#define PR_SCNdOSFD "lld" /* for scanning PROsfd */
+#define PR_SCNxOSFD "llx"
+#else
+#define PR_PRIdOSFD "ld" /* for printing PROsfd */
+#define PR_PRIxOSFD "lx"
+#define PR_SCNdOSFD "ld" /* for scanning PROsfd */
+#define PR_SCNxOSFD "lx"
+#endif
+
+struct PRDir {
+ PRDirEntry d;
+ _MDDir md;
+};
+
+#ifdef MOZ_UNICODE
+struct PRDirUTF16 {
+ PRDirEntry d;
+ _MDDirUTF16 md;
+};
+#endif /* MOZ_UNICODE */
+
+extern void _PR_InitLocks(void);
+extern void _PR_InitSegs(void);
+extern void _PR_InitStacks(void);
+extern void _PR_InitTPD(void);
+extern void _PR_InitMem(void);
+extern void _PR_InitEnv(void);
+extern void _PR_InitCMon(void);
+extern void _PR_InitIO(void);
+extern void _PR_InitLog(void);
+extern void _PR_InitNet(void);
+extern void _PR_InitClock(void);
+extern void _PR_InitLinker(void);
+extern void _PR_InitAtomic(void);
+extern void _PR_InitCPUs(void);
+extern void _PR_InitDtoa(void);
+extern void _PR_InitTime(void);
+extern void _PR_InitMW(void);
+extern void _PR_InitRWLocks(void);
+extern void _PR_CleanupThread(PRThread *thread);
+extern void _PR_CleanupCallOnce(void);
+extern void _PR_CleanupMW(void);
+extern void _PR_CleanupTime(void);
+extern void _PR_CleanupDtoa(void);
+extern void _PR_ShutdownLinker(void);
+extern void _PR_CleanupEnv(void);
+extern void _PR_CleanupIO(void);
+extern void _PR_CleanupCMon(void);
+extern void _PR_CleanupNet(void);
+extern void _PR_CleanupLayerCache(void);
+extern void _PR_CleanupStacks(void);
+#ifdef WINNT
+extern void _PR_CleanupCPUs(void);
+#endif
+extern void _PR_CleanupThreads(void);
+extern void _PR_CleanupTPD(void);
+extern void _PR_Cleanup(void);
+extern void _PR_LogCleanup(void);
+extern void _PR_InitLayerCache(void);
+
+extern PRBool _pr_initialized;
+extern void _PR_ImplicitInitialization(void);
+extern PRBool _PR_Obsolete(const char *obsolete, const char *preferred);
+
+/************************************************************************/
+
+struct PRSegment {
+ void *vaddr;
+ PRUint32 size;
+ PRUintn flags;
+#if defined(_PR_PTHREADS)
+#else /* defined(_PR_PTHREADS) */
+ _MDSegment md;
+#endif /* defined(_PR_PTHREADS) */
+};
+
+/* PRSegment.flags */
+#define _PR_SEG_VM 0x1
+
+/************************************************************************/
+
+extern PRInt32 _pr_pageSize;
+extern PRInt32 _pr_pageShift;
+
+extern PRLogModuleInfo *_pr_clock_lm;
+extern PRLogModuleInfo *_pr_cmon_lm;
+extern PRLogModuleInfo *_pr_io_lm;
+extern PRLogModuleInfo *_pr_cvar_lm;
+extern PRLogModuleInfo *_pr_mon_lm;
+extern PRLogModuleInfo *_pr_linker_lm;
+extern PRLogModuleInfo *_pr_sched_lm;
+extern PRLogModuleInfo *_pr_thread_lm;
+extern PRLogModuleInfo *_pr_gc_lm;
+
+extern PRFileDesc *_pr_stdin;
+extern PRFileDesc *_pr_stdout;
+extern PRFileDesc *_pr_stderr;
+
+/* Zone allocator */
+/*
+** The zone allocator code has hardcoded pthread types and
+** functions, so it can only be used in the pthreads version.
+** This can be fixed by replacing the hardcoded pthread types
+** and functions with macros that expand to the native thread
+** types and functions on each platform.
+*/
+#if defined(_PR_PTHREADS)
+#define _PR_ZONE_ALLOCATOR
+#endif
+
+#ifdef _PR_ZONE_ALLOCATOR
+extern void _PR_InitZones(void);
+extern void _PR_DestroyZones(void);
+#endif
+
+/* Overriding malloc, free, etc. */
+#if !defined(_PR_NO_PREEMPT) && defined(XP_UNIX) \
+ && !defined(_PR_PTHREADS) && !defined(_PR_GLOBAL_THREADS_ONLY) \
+ && !defined(PURIFY) \
+ && !defined(DARWIN) \
+ && !defined(QNX) \
+ && !(defined (UNIXWARE) && defined (USE_SVR4_THREADS))
+#define _PR_OVERRIDE_MALLOC
+#endif
+
+/*************************************************************************
+* External machine-dependent code provided by each OS. * *
+*************************************************************************/
+
+/* Initialization related */
+extern void _PR_MD_EARLY_INIT(void);
+#define _PR_MD_EARLY_INIT _MD_EARLY_INIT
+
+extern void _PR_MD_INTERVAL_INIT(void);
+#define _PR_MD_INTERVAL_INIT _MD_INTERVAL_INIT
+
+NSPR_API(void) _PR_MD_FINAL_INIT(void);
+#define _PR_MD_FINAL_INIT _MD_FINAL_INIT
+
+extern void _PR_MD_EARLY_CLEANUP(void);
+#define _PR_MD_EARLY_CLEANUP _MD_EARLY_CLEANUP
+
+/* Process control */
+
+extern PRProcess * _PR_MD_CREATE_PROCESS(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr);
+#define _PR_MD_CREATE_PROCESS _MD_CREATE_PROCESS
+
+extern PRStatus _PR_MD_DETACH_PROCESS(PRProcess *process);
+#define _PR_MD_DETACH_PROCESS _MD_DETACH_PROCESS
+
+extern PRStatus _PR_MD_WAIT_PROCESS(PRProcess *process, PRInt32 *exitCode);
+#define _PR_MD_WAIT_PROCESS _MD_WAIT_PROCESS
+
+extern PRStatus _PR_MD_KILL_PROCESS(PRProcess *process);
+#define _PR_MD_KILL_PROCESS _MD_KILL_PROCESS
+
+/* Current Time */
+NSPR_API(PRTime) _PR_MD_NOW(void);
+#define _PR_MD_NOW _MD_NOW
+
+/* Environment related */
+extern char* _PR_MD_GET_ENV(const char *name);
+#define _PR_MD_GET_ENV _MD_GET_ENV
+
+extern PRIntn _PR_MD_PUT_ENV(const char *name);
+#define _PR_MD_PUT_ENV _MD_PUT_ENV
+
+/* Atomic operations */
+
+extern void _PR_MD_INIT_ATOMIC(void);
+#define _PR_MD_INIT_ATOMIC _MD_INIT_ATOMIC
+
+extern PRInt32 _PR_MD_ATOMIC_INCREMENT(PRInt32 *);
+#define _PR_MD_ATOMIC_INCREMENT _MD_ATOMIC_INCREMENT
+
+extern PRInt32 _PR_MD_ATOMIC_ADD(PRInt32 *, PRInt32);
+#define _PR_MD_ATOMIC_ADD _MD_ATOMIC_ADD
+
+extern PRInt32 _PR_MD_ATOMIC_DECREMENT(PRInt32 *);
+#define _PR_MD_ATOMIC_DECREMENT _MD_ATOMIC_DECREMENT
+
+extern PRInt32 _PR_MD_ATOMIC_SET(PRInt32 *, PRInt32);
+#define _PR_MD_ATOMIC_SET _MD_ATOMIC_SET
+
+/* Garbage collection */
+
+/*
+** Save the registers that the GC would find interesting into the thread
+** "t". isCurrent will be non-zero if the thread state that is being
+** saved is the currently executing thread. Return the address of the
+** first register to be scanned as well as the number of registers to
+** scan in "np".
+**
+** If "isCurrent" is non-zero then it is allowed for the thread context
+** area to be used as scratch storage to hold just the registers
+** necessary for scanning.
+*/
+extern PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np);
+
+/* Time intervals */
+
+extern PRIntervalTime _PR_MD_GET_INTERVAL(void);
+#define _PR_MD_GET_INTERVAL _MD_GET_INTERVAL
+
+extern PRIntervalTime _PR_MD_INTERVAL_PER_SEC(void);
+#define _PR_MD_INTERVAL_PER_SEC _MD_INTERVAL_PER_SEC
+
+/* Affinity masks */
+
+extern PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask );
+#define _PR_MD_SETTHREADAFFINITYMASK _MD_SETTHREADAFFINITYMASK
+
+extern PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask);
+#define _PR_MD_GETTHREADAFFINITYMASK _MD_GETTHREADAFFINITYMASK
+
+/* File locking */
+
+extern PRStatus _PR_MD_LOCKFILE(PROsfd osfd);
+#define _PR_MD_LOCKFILE _MD_LOCKFILE
+
+extern PRStatus _PR_MD_TLOCKFILE(PROsfd osfd);
+#define _PR_MD_TLOCKFILE _MD_TLOCKFILE
+
+extern PRStatus _PR_MD_UNLOCKFILE(PROsfd osfd);
+#define _PR_MD_UNLOCKFILE _MD_UNLOCKFILE
+
+/* Memory-mapped files */
+
+extern PRStatus _PR_MD_CREATE_FILE_MAP(PRFileMap *fmap, PRInt64 size);
+#define _PR_MD_CREATE_FILE_MAP _MD_CREATE_FILE_MAP
+
+extern PRInt32 _PR_MD_GET_MEM_MAP_ALIGNMENT(void);
+#define _PR_MD_GET_MEM_MAP_ALIGNMENT _MD_GET_MEM_MAP_ALIGNMENT
+
+extern void * _PR_MD_MEM_MAP(
+ PRFileMap *fmap,
+ PROffset64 offset,
+ PRUint32 len);
+#define _PR_MD_MEM_MAP _MD_MEM_MAP
+
+extern PRStatus _PR_MD_MEM_UNMAP(void *addr, PRUint32 size);
+#define _PR_MD_MEM_UNMAP _MD_MEM_UNMAP
+
+extern PRStatus _PR_MD_CLOSE_FILE_MAP(PRFileMap *fmap);
+#define _PR_MD_CLOSE_FILE_MAP _MD_CLOSE_FILE_MAP
+
+extern PRStatus _PR_MD_SYNC_MEM_MAP(
+ PRFileDesc *fd,
+ void *addr,
+ PRUint32 len);
+#define _PR_MD_SYNC_MEM_MAP _MD_SYNC_MEM_MAP
+
+/* Named Shared Memory */
+
+/*
+** Declare PRSharedMemory.
+*/
+struct PRSharedMemory
+{
+ char *ipcname; /* after conversion to native */
+ PRSize size; /* from open */
+ PRIntn mode; /* from open */
+ PRIntn flags; /* from open */
+#if defined(PR_HAVE_POSIX_NAMED_SHARED_MEMORY)
+ int id;
+#elif defined(PR_HAVE_SYSV_NAMED_SHARED_MEMORY)
+ int id;
+#elif defined(PR_HAVE_WIN32_NAMED_SHARED_MEMORY)
+ HANDLE handle;
+#else
+ PRUint32 nothing; /* placeholder, nothing behind here */
+#endif
+ PRUint32 ident; /* guard word at end of struct */
+#define _PR_SHM_IDENT 0xdeadbad
+};
+
+extern PRSharedMemory * _MD_OpenSharedMemory(
+ const char *name,
+ PRSize size,
+ PRIntn flags,
+ PRIntn mode
+);
+#define _PR_MD_OPEN_SHARED_MEMORY _MD_OpenSharedMemory
+
+extern void * _MD_AttachSharedMemory( PRSharedMemory *shm, PRIntn flags );
+#define _PR_MD_ATTACH_SHARED_MEMORY _MD_AttachSharedMemory
+
+extern PRStatus _MD_DetachSharedMemory( PRSharedMemory *shm, void *addr );
+#define _PR_MD_DETACH_SHARED_MEMORY _MD_DetachSharedMemory
+
+extern PRStatus _MD_CloseSharedMemory( PRSharedMemory *shm );
+#define _PR_MD_CLOSE_SHARED_MEMORY _MD_CloseSharedMemory
+
+extern PRStatus _MD_DeleteSharedMemory( const char *name );
+#define _PR_MD_DELETE_SHARED_MEMORY _MD_DeleteSharedMemory
+
+extern PRFileMap* _md_OpenAnonFileMap(
+ const char *dirName,
+ PRSize size,
+ PRFileMapProtect prot
+);
+#define _PR_MD_OPEN_ANON_FILE_MAP _md_OpenAnonFileMap
+
+extern PRStatus _md_ExportFileMapAsString(
+ PRFileMap *fm,
+ PRSize bufSize,
+ char *buf
+);
+#define _PR_MD_EXPORT_FILE_MAP_AS_STRING _md_ExportFileMapAsString
+
+extern PRFileMap * _md_ImportFileMapFromString(
+ const char *fmstring
+);
+#define _PR_MD_IMPORT_FILE_MAP_FROM_STRING _md_ImportFileMapFromString
+
+
+
+/* Interprocess communications (IPC) */
+
+/*
+ * The maximum length of an NSPR IPC name, including the
+ * terminating null byte.
+ */
+#define PR_IPC_NAME_SIZE 1024
+
+/*
+ * Types of NSPR IPC objects
+ */
+typedef enum {
+ _PRIPCSem, /* semaphores */
+ _PRIPCShm /* shared memory segments */
+} _PRIPCType;
+
+/*
+ * Make a native IPC name from an NSPR IPC name.
+ */
+extern PRStatus _PR_MakeNativeIPCName(
+ const char *name, /* NSPR IPC name */
+ char *result, /* result buffer */
+ PRIntn size, /* size of result buffer */
+ _PRIPCType type /* type of IPC object */
+);
+
+/* Socket call error code */
+
+NSPR_API(PRInt32) _PR_MD_GET_SOCKET_ERROR(void);
+#define _PR_MD_GET_SOCKET_ERROR _MD_GET_SOCKET_ERROR
+
+/* Get name of current host */
+extern PRStatus _PR_MD_GETHOSTNAME(char *name, PRUint32 namelen);
+#define _PR_MD_GETHOSTNAME _MD_GETHOSTNAME
+
+extern PRStatus _PR_MD_GETSYSINFO(PRSysInfo cmd, char *name, PRUint32 namelen);
+#define _PR_MD_GETSYSINFO _MD_GETSYSINFO
+
+/* File descriptor inheritance */
+
+/*
+ * If fd->secret->inheritable is _PR_TRI_UNKNOWN and we need to
+ * know the inheritable attribute of the fd, call this function
+ * to find that out. This typically requires a system call.
+ */
+extern void _PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd);
+#define _PR_MD_QUERY_FD_INHERITABLE _MD_QUERY_FD_INHERITABLE
+
+/* --- PR_GetRandomNoise() related things --- */
+
+extern PRSize _PR_MD_GetRandomNoise( void *buf, PRSize size );
+#define _PR_MD_GET_RANDOM_NOISE(buf,size) _PR_MD_GetRandomNoise((buf),(size))
+extern PRSize _pr_CopyLowBits( void *dest, PRSize dstlen, void *src, PRSize srclen );
+
+/* end PR_GetRandomNoise() related */
+
+#if defined(_WIN64) && defined(WIN95)
+typedef struct _PRFileDescList {
+ PRFileDesc *fd;
+ struct _PRFileDescList *next;
+} PRFileDescList;
+
+extern PRLock *_fd_waiting_for_overlapped_done_lock;
+extern PRFileDescList *_fd_waiting_for_overlapped_done;
+extern void CheckOverlappedPendingSocketsAreDone();
+#endif
+
+
+PR_END_EXTERN_C
+
+#endif /* primpl_h___ */
diff --git a/nsprpub/pr/include/private/prpriv.h b/nsprpub/pr/include/private/prpriv.h
new file mode 100644
index 0000000000..f45eee4752
--- /dev/null
+++ b/nsprpub/pr/include/private/prpriv.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prpriv_h___
+#define prpriv_h___
+
+/*
+ * NSPR 2.0 Private API
+ */
+
+#include "private/pprio.h"
+#include "private/pprthred.h"
+
+#endif /* prpriv_h___ */
diff --git a/nsprpub/pr/include/prlink.h b/nsprpub/pr/include/prlink.h
new file mode 100644
index 0000000000..0e38effaf7
--- /dev/null
+++ b/nsprpub/pr/include/prlink.h
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prlink_h___
+#define prlink_h___
+
+/*
+** API to static and dynamic linking.
+*/
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PRLibrary PRLibrary;
+
+typedef struct PRStaticLinkTable {
+ const char *name;
+ void (*fp)(void);
+} PRStaticLinkTable;
+
+/*
+** Change the default library path to the given string. The string is
+** copied. This call will fail if it runs out of memory.
+**
+** The string provided as 'path' is copied. The caller can do whatever is
+** convenient with the argument when the function is complete.
+*/
+NSPR_API(PRStatus) PR_SetLibraryPath(const char *path);
+
+/*
+** Return a character string which contains the path used to search for
+** dynamically loadable libraries.
+**
+** The returned value is basically a copy of a PR_SetLibraryPath().
+** The storage is allocated by the runtime and becomes the responsibilty
+** of the caller.
+*/
+NSPR_API(char*) PR_GetLibraryPath(void);
+
+/*
+** Given a directory name "dir" and a library name "lib" construct a full
+** path name that will refer to the actual dynamically loaded
+** library. This does not test for existance of said file, it just
+** constructs the full filename. The name constructed is system dependent
+** and prepared for PR_LoadLibrary. The result must be free'd when the
+** caller is done with it.
+**
+** The storage for the result is allocated by the runtime and becomes the
+** responsibility of the caller.
+*/
+NSPR_API(char*) PR_GetLibraryName(const char *dir, const char *lib);
+
+/*
+**
+** Free the memory allocated, for the caller, by PR_GetLibraryName
+*/
+NSPR_API(void) PR_FreeLibraryName(char *mem);
+
+/*
+** Given a library "name" try to load the library. The argument "name"
+** is a machine-dependent name for the library, such as the full pathname
+** returned by PR_GetLibraryName. If the library is already loaded,
+** this function will avoid loading the library twice.
+**
+** If the library is loaded successfully, then a pointer to the PRLibrary
+** structure representing the library is returned. Otherwise, NULL is
+** returned.
+**
+** This increments the reference count of the library.
+*/
+NSPR_API(PRLibrary*) PR_LoadLibrary(const char *name);
+
+/*
+** Each operating system has its preferred way of specifying
+** a file in the file system. Most operating systems use
+** a pathname. Mac OS Classic, on the other hand, uses the FSSpec
+** structure to specify a file. PRLibSpec allows NSPR clients
+** to use the type of file specification that is most efficient
+** for a particular platform.
+**
+** On some operating systems such as Mac OS Classic, a shared library
+** may contain code fragments that can be individually loaded.
+** PRLibSpec also allows NSPR clients to identify a code fragment
+** in a library, if code fragments are supported by the OS.
+** A code fragment can be specified by name or by an integer index.
+**
+** Right now PRLibSpec supports four types of library specification:
+** a pathname in the native character encoding, a Mac code fragment
+** by name, a Mac code fragment by index, and a UTF-16 pathname.
+*/
+
+typedef enum PRLibSpecType {
+ PR_LibSpec_Pathname,
+ PR_LibSpec_MacNamedFragment, /* obsolete (for Mac OS Classic) */
+ PR_LibSpec_MacIndexedFragment, /* obsolete (for Mac OS Classic) */
+ PR_LibSpec_PathnameU /* supported only on Win32 */
+} PRLibSpecType;
+
+struct FSSpec; /* Mac OS Classic FSSpec */
+
+typedef struct PRLibSpec {
+ PRLibSpecType type;
+ union {
+ /* if type is PR_LibSpec_Pathname */
+ const char *pathname;
+
+ /* if type is PR_LibSpec_MacNamedFragment */
+ struct {
+ const struct FSSpec *fsspec;
+ const char *name;
+ } mac_named_fragment; /* obsolete (for Mac OS Classic) */
+
+ /* if type is PR_LibSpec_MacIndexedFragment */
+ struct {
+ const struct FSSpec *fsspec;
+ PRUint32 index;
+ } mac_indexed_fragment; /* obsolete (for Mac OS Classic) */
+
+ /* if type is PR_LibSpec_PathnameU */
+ const PRUnichar *pathname_u; /* supported only on Win32 */
+ } value;
+} PRLibSpec;
+
+/*
+** The following bit flags may be or'd together and passed
+** as the 'flags' argument to PR_LoadLibraryWithFlags.
+** Flags not supported by the underlying OS are ignored.
+*/
+
+#define PR_LD_LAZY 0x1 /* equivalent to RTLD_LAZY on Unix */
+#define PR_LD_NOW 0x2 /* equivalent to RTLD_NOW on Unix */
+#define PR_LD_GLOBAL 0x4 /* equivalent to RTLD_GLOBAL on Unix */
+#define PR_LD_LOCAL 0x8 /* equivalent to RTLD_LOCAL on Unix */
+/* The following is equivalent to LOAD_WITH_ALTERED_SEARCH_PATH on Windows */
+#define PR_LD_ALT_SEARCH_PATH 0x10
+/* 0x8000 reserved for NSPR internal use */
+
+/*
+** Load the specified library, in the manner specified by 'flags'.
+*/
+
+NSPR_API(PRLibrary *)
+PR_LoadLibraryWithFlags(
+ PRLibSpec libSpec, /* the shared library */
+ PRIntn flags /* flags that affect the loading */
+);
+
+/*
+** Unload a previously loaded library. If the library was a static
+** library then the static link table will no longer be referenced. The
+** associated PRLibrary object is freed.
+**
+** PR_FAILURE is returned if the library cannot be unloaded.
+**
+** This function decrements the reference count of the library.
+*/
+NSPR_API(PRStatus) PR_UnloadLibrary(PRLibrary *lib);
+
+/*
+** Given the name of a procedure, return the address of the function that
+** implements the procedure, or NULL if no such function can be
+** found. This does not find symbols in the main program (the ".exe");
+** use PR_LoadStaticLibrary to register symbols in the main program.
+**
+** This function does not modify the reference count of the library.
+*/
+NSPR_API(void*) PR_FindSymbol(PRLibrary *lib, const char *name);
+
+/*
+** Similar to PR_FindSymbol, except that the return value is a pointer to
+** a function, and not a pointer to void. Casting between a data pointer
+** and a function pointer is not portable according to the C standard.
+** Any function pointer can be cast to any other function pointer.
+**
+** This function does not modify the reference count of the library.
+*/
+typedef void (*PRFuncPtr)(void);
+NSPR_API(PRFuncPtr) PR_FindFunctionSymbol(PRLibrary *lib, const char *name);
+
+/*
+** Finds a symbol in one of the currently loaded libraries. Given the
+** name of a procedure, return the address of the function that
+** implements the procedure, and return the library that contains that
+** symbol, or NULL if no such function can be found. This does not find
+** symbols in the main program (the ".exe"); use PR_AddStaticLibrary to
+** register symbols in the main program.
+**
+** This increments the reference count of the library.
+*/
+NSPR_API(void*) PR_FindSymbolAndLibrary(const char *name,
+ PRLibrary* *lib);
+
+/*
+** Similar to PR_FindSymbolAndLibrary, except that the return value is
+** a pointer to a function, and not a pointer to void. Casting between a
+** data pointer and a function pointer is not portable according to the C
+** standard. Any function pointer can be cast to any other function pointer.
+**
+** This increments the reference count of the library.
+*/
+NSPR_API(PRFuncPtr) PR_FindFunctionSymbolAndLibrary(const char *name,
+ PRLibrary* *lib);
+
+/*
+** Register a static link table with the runtime under the name
+** "name". The symbols present in the static link table will be made
+** available to PR_FindSymbol. If "name" is null then the symbols will be
+** made available to the library which represents the executable. The
+** tables are not copied.
+**
+** Returns the library object if successful, null otherwise.
+**
+** This increments the reference count of the library.
+*/
+NSPR_API(PRLibrary*) PR_LoadStaticLibrary(
+ const char *name, const PRStaticLinkTable *table);
+
+/*
+** Return the pathname of the file that the library "name" was loaded
+** from. "addr" is the address of a function defined in the library.
+**
+** The caller is responsible for freeing the result with PR_Free.
+*/
+NSPR_API(char *) PR_GetLibraryFilePathname(const char *name, PRFuncPtr addr);
+
+PR_END_EXTERN_C
+
+#endif /* prlink_h___ */
diff --git a/nsprpub/pr/include/prlock.h b/nsprpub/pr/include/prlock.h
new file mode 100644
index 0000000000..9b7fdaa184
--- /dev/null
+++ b/nsprpub/pr/include/prlock.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: prlock.h
+** Description: API to basic locking functions of NSPR.
+**
+**
+** NSPR provides basic locking mechanisms for thread synchronization. Locks
+** are lightweight resource contention controls that prevent multiple threads
+** from accessing something (code/data) simultaneously.
+**/
+
+#ifndef prlock_h___
+#define prlock_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/**********************************************************************/
+/************************* TYPES AND CONSTANTS ************************/
+/**********************************************************************/
+
+/*
+ * PRLock --
+ *
+ * NSPR represents the lock as an opaque entity to the client of the
+ * API. All routines operate on a pointer to this opaque entity.
+ */
+
+typedef struct PRLock PRLock;
+
+/**********************************************************************/
+/****************************** FUNCTIONS *****************************/
+/**********************************************************************/
+
+/***********************************************************************
+** FUNCTION: PR_NewLock
+** DESCRIPTION:
+** Returns a pointer to a newly created opaque lock object.
+** INPUTS: void
+** OUTPUTS: void
+** RETURN: PRLock*
+** If the lock can not be created because of resource constraints, NULL
+** is returned.
+**
+***********************************************************************/
+NSPR_API(PRLock*) PR_NewLock(void);
+
+/***********************************************************************
+** FUNCTION: PR_DestroyLock
+** DESCRIPTION:
+** Destroys a given opaque lock object.
+** INPUTS: PRLock *lock
+** Lock to be freed.
+** OUTPUTS: void
+** RETURN: None
+***********************************************************************/
+NSPR_API(void) PR_DestroyLock(PRLock *lock);
+
+/***********************************************************************
+** FUNCTION: PR_Lock
+** DESCRIPTION:
+** Lock a lock.
+** INPUTS: PRLock *lock
+** Lock to locked.
+** OUTPUTS: void
+** RETURN: None
+***********************************************************************/
+NSPR_API(void) PR_Lock(PRLock *lock);
+
+/***********************************************************************
+** FUNCTION: PR_Unlock
+** DESCRIPTION:
+** Unlock a lock. Unlocking an unlocked lock has undefined results.
+** INPUTS: PRLock *lock
+** Lock to unlocked.
+** OUTPUTS: void
+** RETURN: PR_STATUS
+** Returns PR_FAILURE if the caller does not own the lock.
+***********************************************************************/
+NSPR_API(PRStatus) PR_Unlock(PRLock *lock);
+
+/***********************************************************************
+** MACRO: PR_ASSERT_CURRENT_THREAD_OWNS_LOCK
+** DESCRIPTION:
+** If the current thread owns |lock|, this assertion is guaranteed to
+** succeed. Otherwise, the behavior of this function is undefined.
+** INPUTS: PRLock *lock
+** Lock to assert ownership of.
+** OUTPUTS: void
+** RETURN: None
+***********************************************************************/
+#if defined(DEBUG) || defined(FORCE_PR_ASSERT)
+#define PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(/* PrLock* */ lock) \
+ PR_AssertCurrentThreadOwnsLock(lock)
+#else
+#define PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(/* PrLock* */ lock)
+#endif
+
+/* Don't call this function directly. */
+NSPR_API(void) PR_AssertCurrentThreadOwnsLock(PRLock *lock);
+
+PR_END_EXTERN_C
+
+#endif /* prlock_h___ */
diff --git a/nsprpub/pr/include/prlog.h b/nsprpub/pr/include/prlog.h
new file mode 100644
index 0000000000..1922c255d8
--- /dev/null
+++ b/nsprpub/pr/include/prlog.h
@@ -0,0 +1,226 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prlog_h___
+#define prlog_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** prlog.h -- Declare interfaces to NSPR's Logging service
+**
+** NSPR provides a logging service that is used by NSPR itself and is
+** available to client programs.
+**
+** To use the service from a client program, you should create a
+** PRLogModuleInfo structure by calling PR_NewLogModule(). After
+** creating the LogModule, you can write to the log using the PR_LOG()
+** macro.
+**
+** Initialization of the log service is handled by NSPR initialization.
+**
+** At execution time, you must enable the log service. To enable the
+** log service, set the environment variable: NSPR_LOG_MODULES
+** variable.
+**
+** NSPR_LOG_MODULES variable has the form:
+**
+** <moduleName>:<value>[, <moduleName>:<value>]*
+**
+** Where:
+** <moduleName> is the name passed to PR_NewLogModule().
+** <value> is a numeric constant, e.g. 5. This value is the maximum
+** value of a log event, enumerated by PRLogModuleLevel, that you want
+** written to the log.
+**
+** For example: to record all events of greater value than or equal to
+** PR_LOG_ERROR for a LogModule names "gizmo", say:
+**
+** set NSPR_LOG_MODULES=gizmo:2
+**
+** Note that you must specify the numeric value of PR_LOG_ERROR.
+**
+** Special LogModule names are provided for controlling NSPR's log
+** service at execution time. These controls should be set in the
+** NSPR_LOG_MODULES environment variable at execution time to affect
+** NSPR's log service for your application.
+**
+** The special LogModule "all" enables all LogModules. To enable all
+** LogModule calls to PR_LOG(), say:
+**
+** set NSPR_LOG_MODULES=all:5
+**
+** The special LogModule name "sync" tells the NSPR log service to do
+** unbuffered logging.
+**
+** The special LogModule name "bufsize:<size>" tells NSPR to set the
+** log buffer to <size>.
+**
+** The environment variable NSPR_LOG_FILE specifies the log file to use
+** unless the default of "stderr" is acceptable. For MS Windows
+** systems, NSPR_LOG_FILE can be set to a special value: "WinDebug"
+** (case sensitive). This value causes PR_LOG() output to be written
+** using the Windows API OutputDebugString(). OutputDebugString()
+** writes to the debugger window; some people find this helpful.
+**
+**
+** To put log messages in your programs, use the PR_LOG macro:
+**
+** PR_LOG(<module>, <level>, (<printfString>, <args>*));
+**
+** Where <module> is the address of a PRLogModuleInfo structure, and
+** <level> is one of the levels defined by the enumeration:
+** PRLogModuleLevel. <args> is a printf() style of argument list. That
+** is: (fmtstring, ...).
+**
+** Example:
+**
+** main() {
+** PRIntn one = 1;
+** PRLogModuleInfo * myLm = PR_NewLogModule("gizmo");
+** PR_LOG( myLm, PR_LOG_ALWAYS, ("Log this! %d\n", one));
+** return;
+** }
+**
+** Note the use of printf() style arguments as the third agrument(s) to
+** PR_LOG().
+**
+** After compiling and linking you application, set the environment:
+**
+** set NSPR_LOG_MODULES=gizmo:5
+** set NSPR_LOG_FILE=logfile.txt
+**
+** When you execute your application, the string "Log this! 1" will be
+** written to the file "logfile.txt".
+**
+** Note to NSPR engineers: a number of PRLogModuleInfo structures are
+** defined and initialized in prinit.c. See this module for ideas on
+** what to log where.
+**
+*/
+
+typedef enum PRLogModuleLevel {
+ PR_LOG_NONE = 0, /* nothing */
+ PR_LOG_ALWAYS = 1, /* always printed */
+ PR_LOG_ERROR = 2, /* error messages */
+ PR_LOG_WARNING = 3, /* warning messages */
+ PR_LOG_DEBUG = 4, /* debug messages */
+
+ PR_LOG_NOTICE = PR_LOG_DEBUG, /* notice messages */
+ PR_LOG_WARN = PR_LOG_WARNING, /* warning messages */
+ PR_LOG_MIN = PR_LOG_DEBUG, /* minimal debugging messages */
+ PR_LOG_MAX = PR_LOG_DEBUG /* maximal debugging messages */
+} PRLogModuleLevel;
+
+/*
+** One of these structures is created for each module that uses logging.
+** "name" is the name of the module
+** "level" is the debugging level selected for that module
+*/
+typedef struct PRLogModuleInfo {
+ const char *name;
+ PRLogModuleLevel level;
+ struct PRLogModuleInfo *next;
+} PRLogModuleInfo;
+
+/*
+** Create a new log module.
+*/
+NSPR_API(PRLogModuleInfo*) PR_NewLogModule(const char *name);
+
+/*
+** Set the file to use for logging. Returns PR_FALSE if the file cannot
+** be created
+*/
+NSPR_API(PRBool) PR_SetLogFile(const char *name);
+
+/*
+** Set the size of the logging buffer. If "buffer_size" is zero then the
+** logging becomes "synchronous" (or unbuffered).
+*/
+NSPR_API(void) PR_SetLogBuffering(PRIntn buffer_size);
+
+/*
+** Print a string to the log. "fmt" is a PR_snprintf format type. All
+** messages printed to the log are preceeded by the name of the thread
+** and a time stamp. Also, the routine provides a missing newline if one
+** is not provided.
+*/
+NSPR_API(void) PR_LogPrint(const char *fmt, ...);
+
+/*
+** Flush the log to its file.
+*/
+NSPR_API(void) PR_LogFlush(void);
+
+NSPR_API(void) PR_Assert(const char *s, const char *file, PRIntn ln)
+PR_PRETEND_NORETURN;
+
+#if defined(DEBUG) || defined(FORCE_PR_LOG)
+#define PR_LOGGING 1
+
+#define PR_LOG_TEST(_module,_level) \
+ ((_module)->level >= (_level))
+
+/*
+** Log something.
+** "module" is the address of a PRLogModuleInfo structure
+** "level" is the desired logging level
+** "args" is a variable length list of arguments to print, in the following
+** format: ("printf style format string", ...)
+*/
+#define PR_LOG(_module,_level,_args) \
+ PR_BEGIN_MACRO \
+ if (PR_LOG_TEST(_module,_level)) { \
+ PR_LogPrint _args; \
+ } \
+ PR_END_MACRO
+
+#else /* defined(DEBUG) || defined(FORCE_PR_LOG) */
+
+#undef PR_LOGGING
+#define PR_LOG_TEST(module,level) 0
+#define PR_LOG(module,level,args)
+
+#endif /* defined(DEBUG) || defined(FORCE_PR_LOG) */
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#ifdef PR_LOGGING
+#define PR_LOG_BEGIN PR_LOG
+#define PR_LOG_END PR_LOG
+#define PR_LOG_DEFINE PR_NewLogModule
+#else
+#define PR_LOG_BEGIN(module,level,args)
+#define PR_LOG_END(module,level,args)
+#define PR_LOG_DEFINE(_name) NULL
+#endif /* PR_LOGGING */
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#if defined(DEBUG) || defined(FORCE_PR_ASSERT)
+
+#define PR_ASSERT(_expr) \
+ ((_expr)?((void)0):PR_Assert(# _expr,__FILE__,__LINE__))
+
+#define PR_ASSERT_ARG(_expr) PR_ASSERT(_expr)
+
+#define PR_NOT_REACHED(_reasonStr) \
+ PR_Assert(_reasonStr,__FILE__,__LINE__)
+
+#else
+
+#define PR_ASSERT(expr) ((void) 0)
+/* PR_ASSERT_ARG avoids compiler warning: unused variable */
+#define PR_ASSERT_ARG(expr) ((void)(0 && (expr)))
+#define PR_NOT_REACHED(reasonStr)
+
+#endif /* defined(DEBUG) || defined(FORCE_PR_ASSERT) */
+
+PR_END_EXTERN_C
+
+#endif /* prlog_h___ */
diff --git a/nsprpub/pr/include/prlong.h b/nsprpub/pr/include/prlong.h
new file mode 100644
index 0000000000..7cc1d567d1
--- /dev/null
+++ b/nsprpub/pr/include/prlong.h
@@ -0,0 +1,403 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: prlong.h
+** Description: Portable access to 64 bit numerics
+**
+** Long-long (64-bit signed integer type) support. Some C compilers
+** don't support 64 bit integers yet, so we use these macros to
+** support both machines that do and don't.
+**/
+#ifndef prlong_h___
+#define prlong_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/***********************************************************************
+** DEFINES: LL_MaxInt
+** LL_MinInt
+** LL_Zero
+** LL_MaxUint
+** DESCRIPTION:
+** Various interesting constants and static variable
+** initializer
+***********************************************************************/
+NSPR_API(PRInt64) LL_MaxInt(void);
+NSPR_API(PRInt64) LL_MinInt(void);
+NSPR_API(PRInt64) LL_Zero(void);
+NSPR_API(PRUint64) LL_MaxUint(void);
+
+#if defined(HAVE_LONG_LONG)
+
+/* Keep this in sync with prtypes.h. */
+#if PR_BYTES_PER_LONG == 8 && !defined(PR_ALTERNATE_INT64_TYPEDEF)
+#define LL_MAXINT 9223372036854775807L
+#define LL_MININT (-LL_MAXINT - 1L)
+#define LL_ZERO 0L
+#define LL_MAXUINT 18446744073709551615UL
+#define LL_INIT(hi, lo) ((hi ## L << 32) + lo ## L)
+#elif defined(WIN32) && !defined(__GNUC__)
+#define LL_MAXINT 9223372036854775807i64
+#define LL_MININT (-LL_MAXINT - 1i64)
+#define LL_ZERO 0i64
+#define LL_MAXUINT 18446744073709551615ui64
+#define LL_INIT(hi, lo) ((hi ## i64 << 32) + lo ## i64)
+#else
+#define LL_MAXINT 9223372036854775807LL
+#define LL_MININT (-LL_MAXINT - 1LL)
+#define LL_ZERO 0LL
+#define LL_MAXUINT 18446744073709551615ULL
+#define LL_INIT(hi, lo) ((hi ## LL << 32) + lo ## LL)
+#endif
+
+/***********************************************************************
+** MACROS: LL_*
+** DESCRIPTION:
+** The following macros define portable access to the 64 bit
+** math facilities.
+**
+***********************************************************************/
+
+/***********************************************************************
+** MACROS: LL_<relational operators>
+**
+** LL_IS_ZERO Test for zero
+** LL_EQ Test for equality
+** LL_NE Test for inequality
+** LL_GE_ZERO Test for zero or positive
+** LL_CMP Compare two values
+***********************************************************************/
+#define LL_IS_ZERO(a) ((a) == 0)
+#define LL_EQ(a, b) ((a) == (b))
+#define LL_NE(a, b) ((a) != (b))
+#define LL_GE_ZERO(a) ((a) >= 0)
+#define LL_CMP(a, op, b) ((PRInt64)(a) op (PRInt64)(b))
+#define LL_UCMP(a, op, b) ((PRUint64)(a) op (PRUint64)(b))
+
+/***********************************************************************
+** MACROS: LL_<logical operators>
+**
+** LL_AND Logical and
+** LL_OR Logical or
+** LL_XOR Logical exclusion
+** LL_OR2 A disgusting deviation
+** LL_NOT Negation (one's complement)
+***********************************************************************/
+#define LL_AND(r, a, b) ((r) = (a) & (b))
+#define LL_OR(r, a, b) ((r) = (a) | (b))
+#define LL_XOR(r, a, b) ((r) = (a) ^ (b))
+#define LL_OR2(r, a) ((r) = (r) | (a))
+#define LL_NOT(r, a) ((r) = ~(a))
+
+/***********************************************************************
+** MACROS: LL_<mathematical operators>
+**
+** LL_NEG Negation (two's complement)
+** LL_ADD Summation (two's complement)
+** LL_SUB Difference (two's complement)
+***********************************************************************/
+#define LL_NEG(r, a) ((r) = -(a))
+#define LL_ADD(r, a, b) ((r) = (a) + (b))
+#define LL_SUB(r, a, b) ((r) = (a) - (b))
+
+/***********************************************************************
+** MACROS: LL_<mathematical operators>
+**
+** LL_MUL Product (two's complement)
+** LL_DIV Quotient (two's complement)
+** LL_MOD Modulus (two's complement)
+***********************************************************************/
+#define LL_MUL(r, a, b) ((r) = (a) * (b))
+#define LL_DIV(r, a, b) ((r) = (a) / (b))
+#define LL_MOD(r, a, b) ((r) = (a) % (b))
+
+/***********************************************************************
+** MACROS: LL_<shifting operators>
+**
+** LL_SHL Shift left [0..64] bits
+** LL_SHR Shift right [0..64] bits with sign extension
+** LL_USHR Unsigned shift right [0..64] bits
+** LL_ISHL Signed shift left [0..64] bits
+***********************************************************************/
+#define LL_SHL(r, a, b) ((r) = (PRInt64)(a) << (b))
+#define LL_SHR(r, a, b) ((r) = (PRInt64)(a) >> (b))
+#define LL_USHR(r, a, b) ((r) = (PRUint64)(a) >> (b))
+#define LL_ISHL(r, a, b) ((r) = (PRInt64)(a) << (b))
+
+/***********************************************************************
+** MACROS: LL_<conversion operators>
+**
+** LL_L2I Convert to signed 32 bit
+** LL_L2UI Convert to unsigned 32 bit
+** LL_L2F Convert to floating point
+** LL_L2D Convert to floating point
+** LL_I2L Convert signed to 64 bit
+** LL_UI2L Convert unsigned to 64 bit
+** LL_F2L Convert float to 64 bit
+** LL_D2L Convert float to 64 bit
+***********************************************************************/
+#define LL_L2I(i, l) ((i) = (PRInt32)(l))
+#define LL_L2UI(ui, l) ((ui) = (PRUint32)(l))
+#define LL_L2F(f, l) ((f) = (PRFloat64)(l))
+#define LL_L2D(d, l) ((d) = (PRFloat64)(l))
+
+#define LL_I2L(l, i) ((l) = (PRInt64)(i))
+#define LL_UI2L(l, ui) ((l) = (PRInt64)(ui))
+#define LL_F2L(l, f) ((l) = (PRInt64)(f))
+#define LL_D2L(l, d) ((l) = (PRInt64)(d))
+
+/***********************************************************************
+** MACROS: LL_UDIVMOD
+** DESCRIPTION:
+** Produce both a quotient and a remainder given an unsigned
+** INPUTS: PRUint64 a: The dividend of the operation
+** PRUint64 b: The quotient of the operation
+** OUTPUTS: PRUint64 *qp: pointer to quotient
+** PRUint64 *rp: pointer to remainder
+***********************************************************************/
+#define LL_UDIVMOD(qp, rp, a, b) \
+ (*(qp) = ((PRUint64)(a) / (b)), \
+ *(rp) = ((PRUint64)(a) % (b)))
+
+#else /* !HAVE_LONG_LONG */
+
+#define LL_MAXINT LL_MaxInt()
+#define LL_MININT LL_MinInt()
+#define LL_ZERO LL_Zero()
+#define LL_MAXUINT LL_MaxUint()
+
+#ifdef IS_LITTLE_ENDIAN
+#define LL_INIT(hi, lo) {PR_UINT32(lo), PR_UINT32(hi)}
+#else
+#define LL_INIT(hi, lo) {PR_UINT32(hi), PR_UINT32(lo)}
+#endif
+
+#define LL_IS_ZERO(a) (((a).hi == 0) && ((a).lo == 0))
+#define LL_EQ(a, b) (((a).hi == (b).hi) && ((a).lo == (b).lo))
+#define LL_NE(a, b) (((a).hi != (b).hi) || ((a).lo != (b).lo))
+#define LL_GE_ZERO(a) (((a).hi >> 31) == 0)
+
+#define LL_CMP(a, op, b) (((a).hi == (b).hi) ? ((a).lo op (b).lo) : \
+ ((PRInt32)(a).hi op (PRInt32)(b).hi))
+#define LL_UCMP(a, op, b) (((a).hi == (b).hi) ? ((a).lo op (b).lo) : \
+ ((a).hi op (b).hi))
+
+#define LL_AND(r, a, b) ((r).lo = (a).lo & (b).lo, \
+ (r).hi = (a).hi & (b).hi)
+#define LL_OR(r, a, b) ((r).lo = (a).lo | (b).lo, \
+ (r).hi = (a).hi | (b).hi)
+#define LL_XOR(r, a, b) ((r).lo = (a).lo ^ (b).lo, \
+ (r).hi = (a).hi ^ (b).hi)
+#define LL_OR2(r, a) ((r).lo = (r).lo | (a).lo, \
+ (r).hi = (r).hi | (a).hi)
+#define LL_NOT(r, a) ((r).lo = ~(a).lo, \
+ (r).hi = ~(a).hi)
+
+#define LL_NEG(r, a) ((r).lo = -(PRInt32)(a).lo, \
+ (r).hi = -(PRInt32)(a).hi - ((r).lo != 0))
+#define LL_ADD(r, a, b) { \
+ PRInt64 _a, _b; \
+ _a = a; _b = b; \
+ (r).lo = _a.lo + _b.lo; \
+ (r).hi = _a.hi + _b.hi + ((r).lo < _b.lo); \
+}
+
+#define LL_SUB(r, a, b) { \
+ PRInt64 _a, _b; \
+ _a = a; _b = b; \
+ (r).lo = _a.lo - _b.lo; \
+ (r).hi = _a.hi - _b.hi - (_a.lo < _b.lo); \
+}
+
+#define LL_MUL(r, a, b) { \
+ PRInt64 _a, _b; \
+ _a = a; _b = b; \
+ LL_MUL32(r, _a.lo, _b.lo); \
+ (r).hi += _a.hi * _b.lo + _a.lo * _b.hi; \
+}
+
+#define _lo16(a) ((a) & PR_BITMASK(16))
+#define _hi16(a) ((a) >> 16)
+
+#define LL_MUL32(r, a, b) { \
+ PRUint32 _a1, _a0, _b1, _b0, _y0, _y1, _y2, _y3; \
+ _a1 = _hi16(a), _a0 = _lo16(a); \
+ _b1 = _hi16(b), _b0 = _lo16(b); \
+ _y0 = _a0 * _b0; \
+ _y1 = _a0 * _b1; \
+ _y2 = _a1 * _b0; \
+ _y3 = _a1 * _b1; \
+ _y1 += _hi16(_y0); /* can't carry */ \
+ _y1 += _y2; /* might carry */ \
+ if (_y1 < _y2) \
+ _y3 += (PRUint32)(PR_BIT(16)); /* propagate */ \
+ (r).lo = (_lo16(_y1) << 16) + _lo16(_y0); \
+ (r).hi = _y3 + _hi16(_y1); \
+}
+
+#define LL_UDIVMOD(qp, rp, a, b) ll_udivmod(qp, rp, a, b)
+
+NSPR_API(void) ll_udivmod(PRUint64 *qp, PRUint64 *rp, PRUint64 a, PRUint64 b);
+
+#define LL_DIV(r, a, b) { \
+ PRInt64 _a, _b; \
+ PRUint32 _negative = (PRInt32)(a).hi < 0; \
+ if (_negative) { \
+ LL_NEG(_a, a); \
+ } else { \
+ _a = a; \
+ } \
+ if ((PRInt32)(b).hi < 0) { \
+ _negative ^= 1; \
+ LL_NEG(_b, b); \
+ } else { \
+ _b = b; \
+ } \
+ LL_UDIVMOD(&(r), 0, _a, _b); \
+ if (_negative) \
+ LL_NEG(r, r); \
+}
+
+#define LL_MOD(r, a, b) { \
+ PRInt64 _a, _b; \
+ PRUint32 _negative = (PRInt32)(a).hi < 0; \
+ if (_negative) { \
+ LL_NEG(_a, a); \
+ } else { \
+ _a = a; \
+ } \
+ if ((PRInt32)(b).hi < 0) { \
+ LL_NEG(_b, b); \
+ } else { \
+ _b = b; \
+ } \
+ LL_UDIVMOD(0, &(r), _a, _b); \
+ if (_negative) \
+ LL_NEG(r, r); \
+}
+
+#define LL_SHL(r, a, b) { \
+ if (b) { \
+ PRInt64 _a; \
+ _a = a; \
+ if ((b) < 32) { \
+ (r).lo = _a.lo << ((b) & 31); \
+ (r).hi = (_a.hi << ((b) & 31)) | (_a.lo >> (32 - (b))); \
+ } else { \
+ (r).lo = 0; \
+ (r).hi = _a.lo << ((b) & 31); \
+ } \
+ } else { \
+ (r) = (a); \
+ } \
+}
+
+/* a is an PRInt32, b is PRInt32, r is PRInt64 */
+#define LL_ISHL(r, a, b) { \
+ if (b) { \
+ PRInt64 _a; \
+ _a.lo = (a); \
+ _a.hi = 0; \
+ if ((b) < 32) { \
+ (r).lo = (a) << ((b) & 31); \
+ (r).hi = ((a) >> (32 - (b))); \
+ } else { \
+ (r).lo = 0; \
+ (r).hi = (a) << ((b) & 31); \
+ } \
+ } else { \
+ (r).lo = (a); \
+ (r).hi = 0; \
+ } \
+}
+
+#define LL_SHR(r, a, b) { \
+ if (b) { \
+ PRInt64 _a; \
+ _a = a; \
+ if ((b) < 32) { \
+ (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \
+ (r).hi = (PRInt32)_a.hi >> ((b) & 31); \
+ } else { \
+ (r).lo = (PRInt32)_a.hi >> ((b) & 31); \
+ (r).hi = (PRInt32)_a.hi >> 31; \
+ } \
+ } else { \
+ (r) = (a); \
+ } \
+}
+
+#define LL_USHR(r, a, b) { \
+ if (b) { \
+ PRInt64 _a; \
+ _a = a; \
+ if ((b) < 32) { \
+ (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \
+ (r).hi = _a.hi >> ((b) & 31); \
+ } else { \
+ (r).lo = _a.hi >> ((b) & 31); \
+ (r).hi = 0; \
+ } \
+ } else { \
+ (r) = (a); \
+ } \
+}
+
+#define LL_L2I(i, l) ((i) = (l).lo)
+#define LL_L2UI(ui, l) ((ui) = (l).lo)
+#define LL_L2F(f, l) { double _d; LL_L2D(_d, l); (f) = (PRFloat64)_d; }
+
+#define LL_L2D(d, l) { \
+ int _negative; \
+ PRInt64 _absval; \
+ \
+ _negative = (l).hi >> 31; \
+ if (_negative) { \
+ LL_NEG(_absval, l); \
+ } else { \
+ _absval = l; \
+ } \
+ (d) = (double)_absval.hi * 4.294967296e9 + _absval.lo; \
+ if (_negative) \
+ (d) = -(d); \
+}
+
+#define LL_I2L(l, i) { PRInt32 _i = ((PRInt32)(i)) >> 31; (l).lo = (i); (l).hi = _i; }
+#define LL_UI2L(l, ui) ((l).lo = (ui), (l).hi = 0)
+#define LL_F2L(l, f) { double _d = (double)f; LL_D2L(l, _d); }
+
+#define LL_D2L(l, d) { \
+ int _negative; \
+ double _absval, _d_hi; \
+ PRInt64 _lo_d; \
+ \
+ _negative = ((d) < 0); \
+ _absval = _negative ? -(d) : (d); \
+ \
+ (l).hi = _absval / 4.294967296e9; \
+ (l).lo = 0; \
+ LL_L2D(_d_hi, l); \
+ _absval -= _d_hi; \
+ _lo_d.hi = 0; \
+ if (_absval < 0) { \
+ _lo_d.lo = -_absval; \
+ LL_SUB(l, l, _lo_d); \
+ } else { \
+ _lo_d.lo = _absval; \
+ LL_ADD(l, l, _lo_d); \
+ } \
+ \
+ if (_negative) \
+ LL_NEG(l, l); \
+}
+
+#endif /* !HAVE_LONG_LONG */
+
+PR_END_EXTERN_C
+
+#endif /* prlong_h___ */
diff --git a/nsprpub/pr/include/prmem.h b/nsprpub/pr/include/prmem.h
new file mode 100644
index 0000000000..ed9a428933
--- /dev/null
+++ b/nsprpub/pr/include/prmem.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: prmem.h
+** Description: API to NSPR memory management functions
+**
+*/
+#ifndef prmem_h___
+#define prmem_h___
+
+#include "prtypes.h"
+#include <stdlib.h>
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Thread safe memory allocation.
+**
+** NOTE: pr wraps up malloc, free, calloc, realloc so they are already
+** thread safe (and are not declared here - look in stdlib.h).
+*/
+
+/*
+** PR_Malloc, PR_Calloc, PR_Realloc, and PR_Free have the same signatures
+** as their libc equivalent malloc, calloc, realloc, and free, and have
+** the same semantics. (Note that the argument type size_t is replaced
+** by PRUint32.) Memory allocated by PR_Malloc, PR_Calloc, or PR_Realloc
+** must be freed by PR_Free.
+*/
+
+NSPR_API(void *) PR_Malloc(PRUint32 size);
+
+NSPR_API(void *) PR_Calloc(PRUint32 nelem, PRUint32 elsize);
+
+NSPR_API(void *) PR_Realloc(void *ptr, PRUint32 size);
+
+NSPR_API(void) PR_Free(void *ptr);
+
+/*
+** The following are some convenience macros defined in terms of
+** PR_Malloc, PR_Calloc, PR_Realloc, and PR_Free.
+*/
+
+/***********************************************************************
+** FUNCTION: PR_MALLOC()
+** DESCRIPTION:
+** PR_NEW() allocates an untyped item of size _size from the heap.
+** INPUTS: _size: size in bytes of item to be allocated
+** OUTPUTS: untyped pointer to the node allocated
+** RETURN: pointer to node or error returned from malloc().
+***********************************************************************/
+#define PR_MALLOC(_bytes) (PR_Malloc((_bytes)))
+
+/***********************************************************************
+** FUNCTION: PR_NEW()
+** DESCRIPTION:
+** PR_NEW() allocates an item of type _struct from the heap.
+** INPUTS: _struct: a data type
+** OUTPUTS: pointer to _struct
+** RETURN: pointer to _struct or error returns from malloc().
+***********************************************************************/
+#define PR_NEW(_struct) ((_struct *) PR_MALLOC(sizeof(_struct)))
+
+/***********************************************************************
+** FUNCTION: PR_REALLOC()
+** DESCRIPTION:
+** PR_REALLOC() re-allocates _ptr bytes from the heap as a _size
+** untyped item.
+** INPUTS: _ptr: pointer to node to reallocate
+** _size: size of node to allocate
+** OUTPUTS: pointer to node allocated
+** RETURN: pointer to node allocated
+***********************************************************************/
+#define PR_REALLOC(_ptr, _size) (PR_Realloc((_ptr), (_size)))
+
+/***********************************************************************
+** FUNCTION: PR_CALLOC()
+** DESCRIPTION:
+** PR_CALLOC() allocates a _size bytes untyped item from the heap
+** and sets the allocated memory to all 0x00.
+** INPUTS: _size: size of node to allocate
+** OUTPUTS: pointer to node allocated
+** RETURN: pointer to node allocated
+***********************************************************************/
+#define PR_CALLOC(_size) (PR_Calloc(1, (_size)))
+
+/***********************************************************************
+** FUNCTION: PR_NEWZAP()
+** DESCRIPTION:
+** PR_NEWZAP() allocates an item of type _struct from the heap
+** and sets the allocated memory to all 0x00.
+** INPUTS: _struct: a data type
+** OUTPUTS: pointer to _struct
+** RETURN: pointer to _struct
+***********************************************************************/
+#define PR_NEWZAP(_struct) ((_struct*)PR_Calloc(1, sizeof(_struct)))
+
+/***********************************************************************
+** FUNCTION: PR_DELETE()
+** DESCRIPTION:
+** PR_DELETE() unallocates an object previosly allocated via PR_NEW()
+** or PR_NEWZAP() to the heap.
+** INPUTS: pointer to previously allocated object
+** OUTPUTS: the referenced object is returned to the heap
+** RETURN: void
+***********************************************************************/
+#define PR_DELETE(_ptr) { PR_Free(_ptr); (_ptr) = NULL; }
+
+/***********************************************************************
+** FUNCTION: PR_FREEIF()
+** DESCRIPTION:
+** PR_FREEIF() conditionally unallocates an object previously allocated
+** vial PR_NEW() or PR_NEWZAP(). If the pointer to the object is
+** equal to zero (0), the object is not released.
+** INPUTS: pointer to previously allocated object
+** OUTPUTS: the referenced object is conditionally returned to the heap
+** RETURN: void
+***********************************************************************/
+#define PR_FREEIF(_ptr) if (_ptr) PR_DELETE(_ptr)
+
+PR_END_EXTERN_C
+
+#endif /* prmem_h___ */
diff --git a/nsprpub/pr/include/prmon.h b/nsprpub/pr/include/prmon.h
new file mode 100644
index 0000000000..374e298265
--- /dev/null
+++ b/nsprpub/pr/include/prmon.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prmon_h___
+#define prmon_h___
+
+#include "prtypes.h"
+#include "prinrval.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PRMonitor PRMonitor;
+
+/*
+** Create a new monitor. Monitors are re-entrant locks with a single built-in
+** condition variable.
+**
+** This may fail if memory is tight or if some operating system resource
+** is low.
+*/
+NSPR_API(PRMonitor*) PR_NewMonitor(void);
+
+/*
+** Destroy a monitor. The caller is responsible for guaranteeing that the
+** monitor is no longer in use. There must be no thread waiting on the monitor's
+** condition variable and that the lock is not held.
+**
+*/
+NSPR_API(void) PR_DestroyMonitor(PRMonitor *mon);
+
+/*
+** Enter the lock associated with the monitor. If the calling thread currently
+** is in the monitor, the call to enter will silently succeed. In either case,
+** it will increment the entry count by one.
+*/
+NSPR_API(void) PR_EnterMonitor(PRMonitor *mon);
+
+/*
+** Decrement the entry count associated with the monitor. If the decremented
+** entry count is zero, the monitor is exited. Returns PR_FAILURE if the
+** calling thread has not entered the monitor.
+*/
+NSPR_API(PRStatus) PR_ExitMonitor(PRMonitor *mon);
+
+/*
+** Wait for a notify on the monitor's condition variable. Sleep for "ticks"
+** amount of time (if "ticks" is PR_INTERVAL_NO_TIMEOUT then the sleep is
+** indefinite).
+**
+** While the thread is waiting it exits the monitor (as if it called
+** PR_ExitMonitor as many times as it had called PR_EnterMonitor). When
+** the wait has finished the thread regains control of the monitors lock
+** with the same entry count as before the wait began.
+**
+** The thread waiting on the monitor will be resumed when the monitor is
+** notified (assuming the thread is the next in line to receive the
+** notify) or when the "ticks" timeout elapses.
+**
+** Returns PR_FAILURE if the caller has not entered the monitor.
+*/
+NSPR_API(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime ticks);
+
+/*
+** Notify a thread waiting on the monitor's condition variable. If a thread
+** is waiting on the condition variable (using PR_Wait) then it is awakened
+** and attempts to reenter the monitor.
+*/
+NSPR_API(PRStatus) PR_Notify(PRMonitor *mon);
+
+/*
+** Notify all of the threads waiting on the monitor's condition variable.
+** All of threads waiting on the condition are scheduled to reenter the
+** monitor.
+*/
+NSPR_API(PRStatus) PR_NotifyAll(PRMonitor *mon);
+
+/*
+** PR_ASSERT_CURRENT_THREAD_IN_MONITOR
+** If the current thread is in |mon|, this assertion is guaranteed to
+** succeed. Otherwise, the behavior of this function is undefined.
+*/
+#if defined(DEBUG) || defined(FORCE_PR_ASSERT)
+#define PR_ASSERT_CURRENT_THREAD_IN_MONITOR(/* PRMonitor* */ mon) \
+ PR_AssertCurrentThreadInMonitor(mon)
+#else
+#define PR_ASSERT_CURRENT_THREAD_IN_MONITOR(/* PRMonitor* */ mon)
+#endif
+
+/* Don't call this function directly. */
+NSPR_API(void) PR_AssertCurrentThreadInMonitor(PRMonitor *mon);
+
+PR_END_EXTERN_C
+
+#endif /* prmon_h___ */
diff --git a/nsprpub/pr/include/prmwait.h b/nsprpub/pr/include/prmwait.h
new file mode 100644
index 0000000000..16c0762199
--- /dev/null
+++ b/nsprpub/pr/include/prmwait.h
@@ -0,0 +1,380 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if defined(_PRMWAIT_H)
+#else
+#define _PRMWAIT_H
+
+#include "prio.h"
+#include "prtypes.h"
+#include "prclist.h"
+
+PR_BEGIN_EXTERN_C
+
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/****************************** WARNING ****************************/
+/********************************************************************************/
+/**************************** This is work in progress. *************************/
+/************************** Do not make any assumptions *************************/
+/************************** about the stability of this *************************/
+/************************** API or the underlying imple- ************************/
+/************************** mentation. ************************/
+/********************************************************************************/
+/********************************************************************************/
+
+/*
+** STRUCTURE: PRWaitGroup
+** DESCRIPTION:
+** The client may define several wait groups in order to semantically
+** tie a collection of file descriptors for a single purpose. This allows
+** easier dispatching of threads that returned with active file descriptors
+** from the wait function.
+*/
+typedef struct PRWaitGroup PRWaitGroup;
+
+/*
+** ENUMERATION: PRMWStatus
+** DESCRIPTION:
+** This enumeration is used to indicate the completion status of
+** a receive wait object. Generally stated, a positive value indicates
+** that the operation is not yet complete. A zero value indicates
+** success (similar to PR_SUCCESS) and any negative value is an
+** indication of failure. The reason for the failure can be retrieved
+** by calling PR_GetError().
+**
+** PR_MW_PENDING The operation is still pending. None of the other
+** fields of the object are currently valid.
+** PR_MW_SUCCESS The operation is complete and it was successful.
+** PR_MW_FAILURE The operation failed. The reason for the failure
+** can be retrieved by calling PR_GetError().
+** PR_MW_TIMEOUT The amount of time allowed for by the object's
+** 'timeout' field has expired w/o the operation
+** otherwise coming to closure.
+** PR_MW_INTERRUPT The operation was cancelled, either by the client
+** calling PR_CancelWaitFileDesc() or destroying the
+** entire wait group (PR_DestroyWaitGroup()).
+*/
+typedef enum PRMWStatus
+{
+ PR_MW_PENDING = 1,
+ PR_MW_SUCCESS = 0,
+ PR_MW_FAILURE = -1,
+ PR_MW_TIMEOUT = -2,
+ PR_MW_INTERRUPT = -3
+} PRMWStatus;
+
+/*
+** STRUCTURE: PRMemoryDescriptor
+** DESCRIPTION:
+** THis is a descriptor for an interval of memory. It contains a
+** pointer to the first byte of that memory and the length (in
+** bytes) of the interval.
+*/
+typedef struct PRMemoryDescriptor
+{
+ void *start; /* pointer to first byte of memory */
+ PRSize length; /* length (in bytes) of memory interval */
+} PRMemoryDescriptor;
+
+/*
+** STRUCTURE: PRMWaitClientData
+** DESCRIPTION:
+** An opague stucture for which a client MAY give provide a concrete
+** definition and associate with a receive descriptor. The NSPR runtime
+** does not manage this field. It is completely up to the client.
+*/
+typedef struct PRMWaitClientData PRMWaitClientData;
+
+/*
+** STRUCTURE: PRRecvWait
+** DESCRIPTION:
+** A receive wait object contains the file descriptor that is subject
+** to the wait and the amount of time (beginning epoch established
+** when the object is presented to the runtime) the the channel should
+** block before abandoning the process.
+**
+** The success of the wait operation will be noted in the object's
+** 'outcome' field. The fields are not valid when the NSPR runtime
+** is in possession of the object.
+**
+** The memory descriptor describes an interval of writable memory
+** in the caller's address space where data from an initial read
+** can be placed. The description may indicate a null interval.
+*/
+typedef struct PRRecvWait
+{
+ PRCList internal; /* internal runtime linkages */
+
+ PRFileDesc *fd; /* file descriptor associated w/ object */
+ PRMWStatus outcome; /* outcome of the current/last operation */
+ PRIntervalTime timeout; /* time allowed for entire operation */
+
+ PRInt32 bytesRecv; /* number of bytes transferred into buffer */
+ PRMemoryDescriptor buffer; /* where to store first segment of input data */
+ PRMWaitClientData *client; /* pointer to arbitrary client defined data */
+} PRRecvWait;
+
+/*
+** STRUCTURE: PRMWaitEnumerator
+** DESCRIPTION:
+** An enumeration object is used to store the state of an existing
+** enumeration over a wait group. The opaque object must be allocated
+** by the client and the reference presented on each call to the
+** pseudo-stateless enumerator. The enumeration objects are sharable
+** only in serial fashion.
+*/
+typedef struct PRMWaitEnumerator PRMWaitEnumerator;
+
+
+/*
+** FUNCTION: PR_AddWaitFileDesc
+** DESCRIPTION:
+** This function will effectively add a file descriptor to the
+** list of those waiting for network receive. The new descriptor
+** will be semantically tied to the wait group specified.
+**
+** The ownership for the storage pointed to by 'desc' is temporarily
+** passed over the the NSPR runtime. It will be handed back by the
+** function PR_WaitRecvReady().
+**
+** INPUTS
+** group A reference to a PRWaitGroup or NULL. Wait groups are
+** created by calling PR_CreateWaitGroup() and are used
+** to semantically group various file descriptors by the
+** client's application.
+** desc A reference to a valid PRRecvWait. The object of the
+** reference must be preserved and not be modified
+** until its ownership is returned to the client.
+** RETURN
+** PRStatus An indication of success. If equal to PR_FAILUE details
+** of the failure are avaiable via PR_GetError().
+**
+** ERRORS
+** PR_INVALID_ARGUMENT_ERROR
+** Invalid 'group' identifier or duplicate 'desc' object.
+** PR_OUT_OF_MEMORY_ERROR
+** Insuffient memory for internal data structures.
+** PR_INVALID_STATE_ERROR
+** The group is being destroyed.
+*/
+NSPR_API(PRStatus) PR_AddWaitFileDesc(PRWaitGroup *group, PRRecvWait *desc);
+
+/*
+** FUNCTION: PR_WaitRecvReady
+** DESCRIPTION:
+** PR_WaitRecvReady will block the calling thread until one of the
+** file descriptors that have been added via PR_AddWaitFileDesc is
+** available for input I/O.
+** INPUT
+** group A pointer to a valid PRWaitGroup or NULL (the null
+** group. The function will block the caller until a
+** channel from the wait group becomes ready for receive
+** or there is some sort of error.
+** RETURN
+** PRReciveWait
+** When the caller is resumed it is either returned a
+** valid pointer to a previously added receive wait or
+** a NULL. If the latter, the function has terminated
+** for a reason that can be determined by calling
+** PR_GetError().
+** If a valid pointer is returned, the reference is to the
+** file descriptor contained in the receive wait object.
+** The outcome of the wait operation may still fail, and
+** if it has, that fact will be noted in the object's
+** outcome field. Details can be retrieved from PR_GetError().
+**
+** ERRORS
+** PR_INVALID_ARGUMENT_ERROR
+** The 'group' is not known by the runtime.
+** PR_PENDING_INTERRUPT_ERROR
+ The thread was interrupted.
+** PR_INVALID_STATE_ERROR
+** The group is being destroyed.
+*/
+NSPR_API(PRRecvWait*) PR_WaitRecvReady(PRWaitGroup *group);
+
+/*
+** FUNCTION: PR_CancelWaitFileDesc
+** DESCRIPTION:
+** PR_CancelWaitFileDesc is provided as a means for cancelling operations
+** on objects previously submitted by use of PR_AddWaitFileDesc(). If
+** the runtime knows of the object, it will be marked as having failed
+** because it was interrupted (similar to PR_Interrupt()). The first
+** available thread waiting on the group will be made to return the
+** PRRecvWait object with the outcome noted.
+**
+** INPUTS
+** group The wait group under which the wait receive object was
+** added.
+** desc A pointer to the wait receive object that is to be
+** cancelled.
+** RETURN
+** PRStatus If the wait receive object was located and associated
+** with the specified wait group, the status returned will
+** be PR_SUCCESS. There is still a race condition that would
+** permit the offected object to complete normally, but it
+** is assured that it will complete in the near future.
+** If the receive object or wait group are invalid, the
+** function will return with a status of PR_FAILURE.
+**
+** ERRORS
+** PR_INVALID_ARGUMENT_ERROR
+** The 'group' argument is not recognized as a valid group.
+** PR_COLLECTION_EMPTY_ERROR
+** There are no more receive wait objects in the group's
+** collection.
+** PR_INVALID_STATE_ERROR
+** The group is being destroyed.
+*/
+NSPR_API(PRStatus) PR_CancelWaitFileDesc(PRWaitGroup *group, PRRecvWait *desc);
+
+/*
+** FUNCTION: PR_CancelWaitGroup
+** DESCRIPTION:
+** PR_CancelWaitGroup is provided as a means for cancelling operations
+** on objects previously submitted by use of PR_AddWaitFileDesc(). Each
+** successive call will return a pointer to a PRRecvWait object that
+** was previously registered via PR_AddWaitFileDesc(). If no wait
+** objects are associated with the wait group, a NULL will be returned.
+** This function should be called in a loop until a NULL is returned
+** to reclaim all the wait objects prior to calling PR_DestroyWaitGroup().
+**
+** INPUTS
+** group The wait group under which the wait receive object was
+** added.
+** RETURN
+** PRRecvWait* If the wait group is valid and at least one receive wait
+** object is present in the group, that object will be
+** marked as PR_MW_INTERRUPT'd and removed from the group's
+** queues. Otherwise a NULL will be returned and the reason
+** for the NULL may be retrieved by calling PR_GetError().
+**
+** ERRORS
+** PR_INVALID_ARGUMENT_ERROR
+** PR_GROUP_EMPTY_ERROR
+*/
+NSPR_API(PRRecvWait*) PR_CancelWaitGroup(PRWaitGroup *group);
+
+/*
+** FUNCTION: PR_CreateWaitGroup
+** DESCRIPTION:
+** A wait group is an opaque object that a client may create in order
+** to semantically group various wait requests. Each wait group is
+** unique, including the default wait group (NULL). A wait request
+** that was added under a wait group will only be serviced by a caller
+** that specified the same wait group.
+**
+** INPUT
+** size The size of the hash table to be used to contain the
+** receive wait objects. This is just the initial size.
+** It will grow as it needs to, but to avoid that hassle
+** one can suggest a suitable size initially. It should
+** be ~30% larger than the maximum number of receive wait
+** objects expected.
+** RETURN
+** PRWaitGroup If successful, the function will return a pointer to an
+** object that was allocated by and owned by the runtime.
+** The reference remains valid until it is explicitly destroyed
+** by calling PR_DestroyWaitGroup().
+**
+** ERRORS
+** PR_OUT_OF_MEMORY_ERROR
+*/
+NSPR_API(PRWaitGroup*) PR_CreateWaitGroup(PRInt32 size);
+
+/*
+** FUNCTION: PR_DestroyWaitGroup
+** DESCRIPTION:
+** Undo the effects of PR_CreateWaitGroup(). Any receive wait operations
+** on the group will be treated as if the each had been the target of a
+** PR_CancelWaitFileDesc().
+**
+** INPUT
+** group Reference to a wait group previously allocated using
+** PR_CreateWaitGroup().
+** RETURN
+** PRStatus Will be PR_SUCCESS if the wait group was valid and there
+** are no receive wait objects in that group. Otherwise
+** will indicate PR_FAILURE.
+**
+** ERRORS
+** PR_INVALID_ARGUMENT_ERROR
+** The 'group' argument does not reference a known object.
+** PR_INVALID_STATE_ERROR
+** The group still contains receive wait objects.
+*/
+NSPR_API(PRStatus) PR_DestroyWaitGroup(PRWaitGroup *group);
+
+/*
+** FUNCTION: PR_CreateMWaitEnumerator
+** DESCRIPTION:
+** The PR_CreateMWaitEnumerator() function returns a reference to an
+** opaque PRMWaitEnumerator object. The enumerator object is required
+** as an argument for each successive call in the stateless enumeration
+** of the indicated wait group.
+**
+** group The wait group that the enumeration is intended to
+** process. It may be be the default wait group (NULL).
+** RETURN
+** PRMWaitEnumerator* group
+** A reference to an object that will be used to store
+** intermediate state of enumerations.
+** ERRORS
+** Errors are indicated by the function returning a NULL.
+** PR_INVALID_ARGUMENT_ERROR
+** The 'group' argument does not reference a known object.
+** PR_OUT_OF_MEMORY_ERROR
+*/
+NSPR_API(PRMWaitEnumerator*) PR_CreateMWaitEnumerator(PRWaitGroup *group);
+
+/*
+** FUNCTION: PR_DestroyMWaitEnumerator
+** DESCRIPTION:
+** Destroys the object created by PR_CreateMWaitEnumerator(). The reference
+** used as an argument becomes invalid.
+**
+** INPUT
+** PRMWaitEnumerator* enumerator
+** The PRMWaitEnumerator object to destroy.
+** RETURN
+** PRStatus
+** PR_SUCCESS if successful, PR_FAILURE otherwise.
+** ERRORS
+** PR_INVALID_ARGUMENT_ERROR
+** The enumerator is invalid.
+*/
+NSPR_API(PRStatus) PR_DestroyMWaitEnumerator(PRMWaitEnumerator* enumerator);
+
+/*
+** FUNCTION: PR_EnumerateWaitGroup
+** DESCRIPTION:
+** PR_EnumerateWaitGroup is a thread safe enumerator over a wait group.
+** Each call to the enumerator must present a valid PRMWaitEnumerator
+** rererence and a pointer to the "previous" element returned from the
+** enumeration process or a NULL.
+**
+** An enumeration is started by passing a NULL as the "previous" value.
+** Subsequent calls to the enumerator must pass in the result of the
+** previous call. The enumeration end is signaled by the runtime returning
+** a NULL as the result.
+**
+** Modifications to the content of the wait group are allowed during
+** an enumeration. The effect is that the enumeration may have to be
+** "reset" and that may result in duplicates being returned from the
+** enumeration.
+**
+** An enumeration may be abandoned at any time. The runtime is not
+** keeping any state, so there are no issues in that regard.
+*/
+NSPR_API(PRRecvWait*) PR_EnumerateWaitGroup(
+ PRMWaitEnumerator *enumerator, const PRRecvWait *previous);
+
+PR_END_EXTERN_C
+
+#endif /* defined(_PRMWAIT_H) */
+
+/* prmwait.h */
diff --git a/nsprpub/pr/include/prnetdb.h b/nsprpub/pr/include/prnetdb.h
new file mode 100644
index 0000000000..023a46e5df
--- /dev/null
+++ b/nsprpub/pr/include/prnetdb.h
@@ -0,0 +1,475 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prnetdb_h___
+#define prnetdb_h___
+
+#include "prtypes.h"
+#include "prio.h"
+
+PR_BEGIN_EXTERN_C
+
+
+/*
+ *********************************************************************
+ * Translate an Internet address to/from a character string
+ *********************************************************************
+ */
+NSPR_API(PRStatus) PR_StringToNetAddr(
+ const char *string, PRNetAddr *addr);
+
+NSPR_API(PRStatus) PR_NetAddrToString(
+ const PRNetAddr *addr, char *string, PRUint32 size);
+
+/*
+** Structures returned by network data base library. All addresses are
+** supplied in host order, and returned in network order (suitable for
+** use in system calls).
+*/
+/*
+** Beware that WINSOCK.H defines h_addrtype and h_length as short.
+** Client code does direct struct copies of hostent to PRHostEnt and
+** hence the ifdef.
+*/
+typedef struct PRHostEnt {
+ char *h_name; /* official name of host */
+ char **h_aliases; /* alias list */
+#ifdef WIN32
+ PRInt16 h_addrtype; /* host address type */
+ PRInt16 h_length; /* length of address */
+#else
+ PRInt32 h_addrtype; /* host address type */
+ PRInt32 h_length; /* length of address */
+#endif
+ char **h_addr_list; /* list of addresses from name server */
+} PRHostEnt;
+
+/* A safe size to use that will mostly work... */
+#if (defined(AIX) && defined(_THREAD_SAFE))
+#define PR_NETDB_BUF_SIZE sizeof(struct protoent_data)
+#define PR_MIN_NETDB_BUF_SIZE PR_NETDB_BUF_SIZE
+#else
+/* PR_NETDB_BUF_SIZE is the recommended buffer size */
+#define PR_NETDB_BUF_SIZE 2048
+/* PR_MIN_NETDB_BUF_SIZE is the smallest buffer size that the API
+ * accepts (for backward compatibility). */
+#define PR_MIN_NETDB_BUF_SIZE 1024
+#endif
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_GetHostByName()
+** Lookup a host by name.
+**
+** INPUTS:
+** char *hostname Character string defining the host name of interest
+** char *buf A scratch buffer for the runtime to return result.
+** This buffer is allocated by the caller.
+** PRIntn bufsize Number of bytes in 'buf'. A recommnded value to
+** use is PR_NETDB_BUF_SIZE.
+** OUTPUTS:
+** PRHostEnt *hostentry
+** This structure is filled in by the runtime if
+** the function returns PR_SUCCESS. This structure
+** is allocated by the caller.
+** RETURN:
+** PRStatus PR_SUCCESS if the lookup succeeds. If it fails
+** the result will be PR_FAILURE and the reason
+** for the failure can be retrieved by PR_GetError().
+***********************************************************************/
+NSPR_API(PRStatus) PR_GetHostByName(
+ const char *hostname, char *buf, PRIntn bufsize, PRHostEnt *hostentry);
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_GetIPNodeByName()
+** Lookup a host by name. Equivalent to getipnodebyname(AI_DEFAULT)
+** of RFC 2553.
+**
+** INPUTS:
+** char *hostname Character string defining the host name of interest
+** PRUint16 af Address family (either PR_AF_INET or PR_AF_INET6)
+** PRIntn flags Specifies the types of addresses that are searched
+** for and the types of addresses that are returned.
+** The only supported flag is PR_AI_DEFAULT.
+** char *buf A scratch buffer for the runtime to return result.
+** This buffer is allocated by the caller.
+** PRIntn bufsize Number of bytes in 'buf'. A recommnded value to
+** use is PR_NETDB_BUF_SIZE.
+** OUTPUTS:
+** PRHostEnt *hostentry
+** This structure is filled in by the runtime if
+** the function returns PR_SUCCESS. This structure
+** is allocated by the caller.
+** RETURN:
+** PRStatus PR_SUCCESS if the lookup succeeds. If it fails
+** the result will be PR_FAILURE and the reason
+** for the failure can be retrieved by PR_GetError().
+***********************************************************************/
+
+
+#define PR_AI_ALL 0x08
+#define PR_AI_V4MAPPED 0x10
+#define PR_AI_ADDRCONFIG 0x20
+#define PR_AI_NOCANONNAME 0x8000
+#define PR_AI_DEFAULT (PR_AI_V4MAPPED | PR_AI_ADDRCONFIG)
+
+NSPR_API(PRStatus) PR_GetIPNodeByName(
+ const char *hostname,
+ PRUint16 af,
+ PRIntn flags,
+ char *buf,
+ PRIntn bufsize,
+ PRHostEnt *hostentry);
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_GetHostByAddr()
+** Lookup a host entry by its network address.
+**
+** INPUTS:
+** char *hostaddr IP address of host in question
+** char *buf A scratch buffer for the runtime to return result.
+** This buffer is allocated by the caller.
+** PRIntn bufsize Number of bytes in 'buf'. A recommnded value to
+** use is PR_NETDB_BUF_SIZE.
+** OUTPUTS:
+** PRHostEnt *hostentry
+** This structure is filled in by the runtime if
+** the function returns PR_SUCCESS. This structure
+** is allocated by the caller.
+** RETURN:
+** PRStatus PR_SUCCESS if the lookup succeeds. If it fails
+** the result will be PR_FAILURE and the reason
+** for the failure can be retrieved by PR_GetError().
+***********************************************************************/
+NSPR_API(PRStatus) PR_GetHostByAddr(
+ const PRNetAddr *hostaddr, char *buf, PRIntn bufsize, PRHostEnt *hostentry);
+
+/***********************************************************************
+** FUNCTION: PR_EnumerateHostEnt()
+** DESCRIPTION:
+** A stateless enumerator over a PRHostEnt structure acquired from
+** PR_GetHostByName() PR_GetHostByAddr() to evaluate the possible
+** network addresses.
+**
+** INPUTS:
+** PRIntn enumIndex Index of the enumeration. The enumeration starts
+** and ends with a value of zero.
+**
+** PRHostEnt *hostEnt A pointer to a host entry struct that was
+** previously returned by PR_GetHostByName() or
+** PR_GetHostByAddr().
+**
+** PRUint16 port The port number to be assigned as part of the
+** PRNetAddr.
+**
+** OUTPUTS:
+** PRNetAddr *address A pointer to an address structure that will be
+** filled in by the call to the enumeration if the
+** result of the call is greater than zero.
+**
+** RETURN:
+** PRIntn The value that should be used for the next call
+** of the enumerator ('enumIndex'). The enumeration
+** is ended if this value is returned zero.
+** If a value of -1 is returned, the enumeration
+** has failed. The reason for the failure can be
+** retrieved by calling PR_GetError().
+***********************************************************************/
+NSPR_API(PRIntn) PR_EnumerateHostEnt(
+ PRIntn enumIndex, const PRHostEnt *hostEnt, PRUint16 port, PRNetAddr *address);
+
+/***********************************************************************
+** FUNCTION: PR_InitializeNetAddr(),
+** DESCRIPTION:
+** Initialize the fields of a PRNetAddr, assigning well known values as
+** appropriate.
+**
+** INPUTS
+** PRNetAddrValue val The value to be assigned to the IP Address portion
+** of the network address. This can only specify the
+** special well known values that are equivalent to
+** INADDR_ANY and INADDR_LOOPBACK.
+**
+** PRUint16 port The port number to be assigned in the structure.
+**
+** OUTPUTS:
+** PRNetAddr *addr The address to be manipulated.
+**
+** RETURN:
+** PRStatus To indicate success or failure. If the latter, the
+** reason for the failure can be retrieved by calling
+** PR_GetError();
+***********************************************************************/
+typedef enum PRNetAddrValue
+{
+ PR_IpAddrNull, /* do NOT overwrite the IP address */
+ PR_IpAddrAny, /* assign logical INADDR_ANY to IP address */
+ PR_IpAddrLoopback, /* assign logical INADDR_LOOPBACK */
+ PR_IpAddrV4Mapped /* IPv4 mapped address */
+} PRNetAddrValue;
+
+NSPR_API(PRStatus) PR_InitializeNetAddr(
+ PRNetAddrValue val, PRUint16 port, PRNetAddr *addr);
+
+/***********************************************************************
+** FUNCTION: PR_SetNetAddr(),
+** DESCRIPTION:
+** Set the fields of a PRNetAddr, assigning well known values as
+** appropriate. This function is similar to PR_InitializeNetAddr
+** but differs in that the address family is specified.
+**
+** INPUTS
+** PRNetAddrValue val The value to be assigned to the IP Address portion
+** of the network address. This can only specify the
+** special well known values that are equivalent to
+** INADDR_ANY and INADDR_LOOPBACK.
+**
+** PRUint16 af The address family (either PR_AF_INET or PR_AF_INET6)
+**
+** PRUint16 port The port number to be assigned in the structure.
+**
+** OUTPUTS:
+** PRNetAddr *addr The address to be manipulated.
+**
+** RETURN:
+** PRStatus To indicate success or failure. If the latter, the
+** reason for the failure can be retrieved by calling
+** PR_GetError();
+***********************************************************************/
+NSPR_API(PRStatus) PR_SetNetAddr(
+ PRNetAddrValue val, PRUint16 af, PRUint16 port, PRNetAddr *addr);
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_IsNetAddrType()
+** Determine if the network address is of the specified type.
+**
+** INPUTS:
+** const PRNetAddr *addr A network address.
+** PRNetAddrValue The type of network address
+**
+** RETURN:
+** PRBool PR_TRUE if the network address is of the
+** specified type, else PR_FALSE.
+***********************************************************************/
+NSPR_API(PRBool) PR_IsNetAddrType(const PRNetAddr *addr, PRNetAddrValue val);
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_ConvertIPv4AddrToIPv6()
+** Convert an IPv4 addr to an (IPv4-mapped) IPv6 addr
+**
+** INPUTS:
+** PRUint32 v4addr IPv4 address
+**
+** OUTPUTS:
+** PRIPv6Addr *v6addr The converted IPv6 address
+**
+** RETURN:
+** void
+**
+***********************************************************************/
+NSPR_API(void) PR_ConvertIPv4AddrToIPv6(PRUint32 v4addr, PRIPv6Addr *v6addr);
+
+/***********************************************************************
+** MACRO:
+** DESCRIPTION: PR_NetAddrFamily()
+** Get the 'family' field of a PRNetAddr union.
+**
+** INPUTS:
+** const PRNetAddr *addr A network address.
+**
+** RETURN:
+** PRUint16 The 'family' field of 'addr'.
+***********************************************************************/
+#define PR_NetAddrFamily(addr) ((addr)->raw.family)
+
+/***********************************************************************
+** MACRO:
+** DESCRIPTION: PR_NetAddrInetPort()
+** Get the 'port' field of a PRNetAddr union.
+**
+** INPUTS:
+** const PRNetAddr *addr A network address.
+**
+** RETURN:
+** PRUint16 The 'port' field of 'addr'.
+***********************************************************************/
+#define PR_NetAddrInetPort(addr) \
+ ((addr)->raw.family == PR_AF_INET6 ? (addr)->ipv6.port : (addr)->inet.port)
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_GetProtoByName()
+** Lookup a protocol entry based on protocol's name
+**
+** INPUTS:
+** char *protocolname Character string of the protocol's name.
+** char *buf A scratch buffer for the runtime to return result.
+** This buffer is allocated by the caller.
+** PRIntn bufsize Number of bytes in 'buf'. A recommnded value to
+** use is PR_NETDB_BUF_SIZE.
+** OUTPUTS:
+** PRHostEnt *PRProtoEnt
+** This structure is filled in by the runtime if
+** the function returns PR_SUCCESS. This structure
+** is allocated by the caller.
+** RETURN:
+** PRStatus PR_SUCCESS if the lookup succeeds. If it fails
+** the result will be PR_FAILURE and the reason
+** for the failure can be retrieved by PR_GetError().
+***********************************************************************/
+
+typedef struct PRProtoEnt {
+ char *p_name; /* official protocol name */
+ char **p_aliases; /* alias list */
+#ifdef WIN32
+ PRInt16 p_num; /* protocol # */
+#else
+ PRInt32 p_num; /* protocol # */
+#endif
+} PRProtoEnt;
+
+NSPR_API(PRStatus) PR_GetProtoByName(
+ const char* protocolname, char* buffer, PRInt32 bufsize, PRProtoEnt* result);
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_GetProtoByNumber()
+** Lookup a protocol entry based on protocol's number
+**
+** INPUTS:
+** PRInt32 protocolnumber
+** Number assigned to the protocol.
+** char *buf A scratch buffer for the runtime to return result.
+** This buffer is allocated by the caller.
+** PRIntn bufsize Number of bytes in 'buf'. A recommnded value to
+** use is PR_NETDB_BUF_SIZE.
+** OUTPUTS:
+** PRHostEnt *PRProtoEnt
+** This structure is filled in by the runtime if
+** the function returns PR_SUCCESS. This structure
+** is allocated by the caller.
+** RETURN:
+** PRStatus PR_SUCCESS if the lookup succeeds. If it fails
+** the result will be PR_FAILURE and the reason
+** for the failure can be retrieved by PR_GetError().
+***********************************************************************/
+NSPR_API(PRStatus) PR_GetProtoByNumber(
+ PRInt32 protocolnumber, char* buffer, PRInt32 bufsize, PRProtoEnt* result);
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_GetAddrInfoByName()
+** Look up a host by name. Equivalent to getaddrinfo(host, NULL, ...) of
+** RFC 3493.
+**
+** INPUTS:
+** char *hostname Character string defining the host name of interest
+** PRUint16 af May be PR_AF_UNSPEC or PR_AF_INET.
+** PRIntn flags May be either PR_AI_ADDRCONFIG or
+** PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME. Include
+** PR_AI_NOCANONNAME to suppress the determination of
+** the canonical name corresponding to hostname.
+** RETURN:
+** PRAddrInfo* Handle to a data structure containing the results
+** of the host lookup. Use PR_EnumerateAddrInfo to
+** inspect the PRNetAddr values stored in this object.
+** When no longer needed, this handle must be destroyed
+** with a call to PR_FreeAddrInfo. If a lookup error
+** occurs, then NULL will be returned.
+***********************************************************************/
+typedef struct PRAddrInfo PRAddrInfo;
+
+NSPR_API(PRAddrInfo*) PR_GetAddrInfoByName(
+ const char *hostname, PRUint16 af, PRIntn flags);
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_FreeAddrInfo()
+** Destroy the PRAddrInfo handle allocated by PR_GetAddrInfoByName().
+**
+** INPUTS:
+** PRAddrInfo *addrInfo
+** The handle resulting from a successful call to
+** PR_GetAddrInfoByName().
+** RETURN:
+** void
+***********************************************************************/
+NSPR_API(void) PR_FreeAddrInfo(PRAddrInfo *addrInfo);
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_EnumerateAddrInfo()
+** A stateless enumerator over a PRAddrInfo handle acquired from
+** PR_GetAddrInfoByName() to inspect the possible network addresses.
+**
+** INPUTS:
+** void *enumPtr Index pointer of the enumeration. The enumeration
+** starts and ends with a value of NULL.
+** const PRAddrInfo *addrInfo
+** The PRAddrInfo handle returned by a successful
+** call to PR_GetAddrInfoByName().
+** PRUint16 port The port number to be assigned as part of the
+** PRNetAddr.
+** OUTPUTS:
+** PRNetAddr *result A pointer to an address structure that will be
+** filled in by the call to the enumeration if the
+** result of the call is not NULL.
+** RETURN:
+** void* The value that should be used for the next call
+** of the enumerator ('enumPtr'). The enumeration
+** is ended if this value is NULL.
+***********************************************************************/
+NSPR_API(void *) PR_EnumerateAddrInfo(
+ void *enumPtr, const PRAddrInfo *addrInfo, PRUint16 port, PRNetAddr *result);
+
+NSPR_API(PRStatus) PR_GetPrefLoopbackAddrInfo(PRNetAddr *result,
+ PRUint16 port);
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_GetCanonNameFromAddrInfo()
+** Extracts the canonical name of the hostname passed to
+** PR_GetAddrInfoByName().
+**
+** INPUTS:
+** const PRAddrInfo *addrInfo
+** The PRAddrInfo handle returned by a successful
+** call to PR_GetAddrInfoByName().
+** RETURN:
+** const char * A const pointer to the canonical hostname stored
+** in the given PRAddrInfo handle. This pointer is
+** invalidated once the PRAddrInfo handle is destroyed
+** by a call to PR_FreeAddrInfo().
+***********************************************************************/
+NSPR_API(const char *) PR_GetCanonNameFromAddrInfo(
+ const PRAddrInfo *addrInfo);
+
+/***********************************************************************
+** FUNCTIONS: PR_ntohs, PR_ntohl, PR_ntohll, PR_htons, PR_htonl, PR_htonll
+**
+** DESCRIPTION: API entries for the common byte ordering routines.
+**
+** PR_ntohs 16 bit conversion from network to host
+** PR_ntohl 32 bit conversion from network to host
+** PR_ntohll 64 bit conversion from network to host
+** PR_htons 16 bit conversion from host to network
+** PR_htonl 32 bit conversion from host to network
+** PR_ntonll 64 bit conversion from host to network
+**
+***********************************************************************/
+NSPR_API(PRUint16) PR_ntohs(PRUint16);
+NSPR_API(PRUint32) PR_ntohl(PRUint32);
+NSPR_API(PRUint64) PR_ntohll(PRUint64);
+NSPR_API(PRUint16) PR_htons(PRUint16);
+NSPR_API(PRUint32) PR_htonl(PRUint32);
+NSPR_API(PRUint64) PR_htonll(PRUint64);
+
+PR_END_EXTERN_C
+
+#endif /* prnetdb_h___ */
diff --git a/nsprpub/pr/include/prolock.h b/nsprpub/pr/include/prolock.h
new file mode 100644
index 0000000000..7b57b3c71e
--- /dev/null
+++ b/nsprpub/pr/include/prolock.h
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prolock_h___
+#define prolock_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** A locking mechanism, built on the existing PRLock definition,
+** is provided that will permit applications to define a Lock
+** Hierarchy (or Lock Ordering) schema. An application designed
+** using the Ordered Lock functions will terminate with a
+** diagnostic message when a lock inversion condition is
+** detected.
+**
+** The lock ordering detection is compile-time enabled only. In
+** optimized builds of NSPR, the Ordered Lock functions map
+** directly to PRLock functions, providing no lock order
+** detection.
+**
+** The Ordered Lock Facility is compiled in when DEBUG is defined at
+** compile-time. Ordered Lock can be forced on in optimized builds by
+** defining FORCE_NSPR_ORDERED_LOCK at compile-time. Both the
+** application using Ordered Lock and NSPR must be compiled with the
+** facility enabled to achieve the desired results.
+**
+** Application designers should use the macro interfaces to the Ordered
+** Lock facility to ensure that it is compiled out in optimized builds.
+**
+** Application designers are responsible for defining their own
+** lock hierarchy.
+**
+** Ordered Lock is thread-safe and SMP safe.
+**
+** See Also: prlock.h
+**
+** /lth. 10-Jun-1998.
+**
+*/
+
+/*
+** Opaque type for ordered lock.
+** ... Don't even think of looking in here.
+**
+*/
+
+#if defined(DEBUG) || defined(FORCE_NSPR_ORDERED_LOCKS)
+typedef void * PROrderedLock;
+#else
+/*
+** Map PROrderedLock and methods onto PRLock when ordered locking
+** is not compiled in.
+**
+*/
+#include "prlock.h"
+
+typedef PRLock PROrderedLock;
+#endif
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_CreateOrderedLock() -- Create an Ordered Lock
+**
+** DESCRIPTION: PR_CreateOrderedLock() creates an ordered lock.
+**
+** INPUTS:
+** order: user defined order of this lock.
+** name: name of the lock. For debugging purposes.
+**
+** OUTPUTS: returned
+**
+** RETURNS: PR_OrderedLock pointer
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_ORDERED_LOCKS)
+#define PR_CREATE_ORDERED_LOCK(order,name)\
+ PR_CreateOrderedLock((order),(name))
+#else
+#define PR_CREATE_ORDERED_LOCK(order) PR_NewLock()
+#endif
+
+NSPR_API(PROrderedLock *)
+PR_CreateOrderedLock(
+ PRInt32 order,
+ const char *name
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_DestroyOrderedLock() -- Destroy an Ordered Lock
+**
+** DESCRIPTION: PR_DestroyOrderedLock() destroys the ordered lock
+** referenced by lock.
+**
+** INPUTS: lock: pointer to a PROrderedLock
+**
+** OUTPUTS: the lock is destroyed
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_ORDERED_LOCKS)
+#define PR_DESTROY_ORDERED_LOCK(lock) PR_DestroyOrderedLock((lock))
+#else
+#define PR_DESTROY_ORDERED_LOCK(lock) PR_DestroyLock((lock))
+#endif
+
+NSPR_API(void)
+PR_DestroyOrderedLock(
+ PROrderedLock *lock
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_LockOrderedLock() -- Lock an ordered lock
+**
+** DESCRIPTION: PR_LockOrderedLock() locks the ordered lock
+** referenced by lock. If the order of lock is less than or equal
+** to the order of the highest lock held by the locking thread,
+** the function asserts.
+**
+** INPUTS: lock: a pointer to a PROrderedLock
+**
+** OUTPUTS: The lock is held or the function asserts.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_ORDERED_LOCKS)
+#define PR_LOCK_ORDERED_LOCK(lock) PR_LockOrderedLock((lock))
+#else
+#define PR_LOCK_ORDERED_LOCK(lock) PR_Lock((lock))
+#endif
+
+NSPR_API(void)
+PR_LockOrderedLock(
+ PROrderedLock *lock
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_UnlockOrderedLock() -- unlock and Ordered Lock
+**
+** DESCRIPTION: PR_UnlockOrderedLock() unlocks the lock referenced
+** by lock.
+**
+** INPUTS: lock: a pointer to a PROrderedLock
+**
+** OUTPUTS: the lock is unlocked
+**
+** RETURNS:
+** PR_SUCCESS
+** PR_FAILURE
+**
+** RESTRICTIONS:
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_ORDERED_LOCKS)
+#define PR_UNLOCK_ORDERED_LOCK(lock) PR_UnlockOrderedLock((lock))
+#else
+#define PR_UNLOCK_ORDERED_LOCK(lock) PR_Unlock((lock))
+#endif
+
+NSPR_API(PRStatus)
+PR_UnlockOrderedLock(
+ PROrderedLock *lock
+);
+
+PR_END_EXTERN_C
+
+#endif /* prolock_h___ */
diff --git a/nsprpub/pr/include/prpdce.h b/nsprpub/pr/include/prpdce.h
new file mode 100644
index 0000000000..bf9ea9e2b7
--- /dev/null
+++ b/nsprpub/pr/include/prpdce.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: prpdce.h
+ * Description: This file is the API defined to allow for DCE (aka POSIX)
+ * thread emulation in an NSPR environment. It is not the
+ * intent that this be a fully supported API.
+ */
+
+#if !defined(PRPDCE_H)
+#define PRPDCE_H
+
+#include "prlock.h"
+#include "prcvar.h"
+#include "prtypes.h"
+#include "prinrval.h"
+
+PR_BEGIN_EXTERN_C
+
+#define _PR_NAKED_CV_LOCK (PRLock*)0xdce1dce1
+
+/*
+** Test and acquire a lock.
+**
+** If the lock is acquired by the calling thread, the
+** return value will be PR_SUCCESS. If the lock is
+** already held, by another thread or this thread, the
+** result will be PR_FAILURE.
+*/
+NSPR_API(PRStatus) PRP_TryLock(PRLock *lock);
+
+/*
+** Create a naked condition variable
+**
+** A "naked" condition variable is one that is not created bound
+** to a lock. The CV created with this function is the only type
+** that may be used in the subsequent "naked" condition variable
+** operations (see PRP_NakedWait, PRP_NakedNotify, PRP_NakedBroadcast);
+*/
+NSPR_API(PRCondVar*) PRP_NewNakedCondVar(void);
+
+/*
+** Destroy a naked condition variable
+**
+** Destroy the condition variable created by PR_NewNakedCondVar.
+*/
+NSPR_API(void) PRP_DestroyNakedCondVar(PRCondVar *cvar);
+
+/*
+** Wait on a condition
+**
+** Wait on the condition variable 'cvar'. It is asserted that
+** the lock protecting the condition 'lock' is held by the
+** calling thread. If more time expires than that declared in
+** 'timeout' the condition will be notified. Waits can be
+** interrupted by another thread.
+**
+** NB: The CV ('cvar') must be one created using PR_NewNakedCondVar.
+*/
+NSPR_API(PRStatus) PRP_NakedWait(
+ PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout);
+
+/*
+** Notify a thread waiting on a condition
+**
+** Notify the condition specified 'cvar'.
+**
+** NB: The CV ('cvar') must be one created using PR_NewNakedCondVar.
+*/
+NSPR_API(PRStatus) PRP_NakedNotify(PRCondVar *cvar);
+
+/*
+** Notify all threads waiting on a condition
+**
+** Notify the condition specified 'cvar'.
+**
+** NB: The CV ('cvar') must be one created using PR_NewNakedCondVar.
+*/
+NSPR_API(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar);
+
+PR_END_EXTERN_C
+
+#endif /* PRPDCE_H */
diff --git a/nsprpub/pr/include/prprf.h b/nsprpub/pr/include/prprf.h
new file mode 100644
index 0000000000..759ef4053a
--- /dev/null
+++ b/nsprpub/pr/include/prprf.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prprf_h___
+#define prprf_h___
+
+/*
+** API for PR printf like routines. Supports the following formats
+** %d - decimal
+** %u - unsigned decimal
+** %x - unsigned hex
+** %X - unsigned uppercase hex
+** %o - unsigned octal
+** %hd, %hu, %hx, %hX, %ho - 16-bit versions of above
+** %ld, %lu, %lx, %lX, %lo - 32-bit versions of above
+** %lld, %llu, %llx, %llX, %llo - 64 bit versions of above
+** %s - string
+** %c - character
+** %p - pointer (deals with machine dependent pointer size)
+** %f - float
+** %g - float
+*/
+#include "prtypes.h"
+#include "prio.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+PR_BEGIN_EXTERN_C
+
+/*
+** sprintf into a fixed size buffer. Guarantees that a NUL is at the end
+** of the buffer. Returns the length of the written output, NOT including
+** the NUL, or (PRUint32)-1 if an error occurs.
+*/
+NSPR_API(PRUint32) PR_snprintf(char *out, PRUint32 outlen, const char *fmt, ...);
+
+/*
+** sprintf into a PR_MALLOC'd buffer. Return a pointer to the malloc'd
+** buffer on success, NULL on failure. Call "PR_smprintf_free" to release
+** the memory returned.
+*/
+NSPR_API(char*) PR_smprintf(const char *fmt, ...);
+
+/*
+** Free the memory allocated, for the caller, by PR_smprintf
+*/
+NSPR_API(void) PR_smprintf_free(char *mem);
+
+/*
+** "append" sprintf into a PR_MALLOC'd buffer. "last" is the last value of
+** the PR_MALLOC'd buffer. sprintf will append data to the end of last,
+** growing it as necessary using realloc. If last is NULL, PR_sprintf_append
+** will allocate the initial string. The return value is the new value of
+** last for subsequent calls, or NULL if there is a malloc failure.
+*/
+NSPR_API(char*) PR_sprintf_append(char *last, const char *fmt, ...);
+
+/*
+** sprintf into a function. The function "f" is called with a string to
+** place into the output. "arg" is an opaque pointer used by the stuff
+** function to hold any state needed to do the storage of the output
+** data. The return value is a count of the number of characters fed to
+** the stuff function, or (PRUint32)-1 if an error occurs.
+*/
+typedef PRIntn (*PRStuffFunc)(void *arg, const char *s, PRUint32 slen);
+
+NSPR_API(PRUint32) PR_sxprintf(PRStuffFunc f, void *arg, const char *fmt, ...);
+
+/*
+** fprintf to a PRFileDesc
+*/
+NSPR_API(PRUint32) PR_fprintf(struct PRFileDesc* fd, const char *fmt, ...);
+
+/*
+** va_list forms of the above.
+*/
+NSPR_API(PRUint32) PR_vsnprintf(char *out, PRUint32 outlen, const char *fmt, va_list ap);
+NSPR_API(char*) PR_vsmprintf(const char *fmt, va_list ap);
+NSPR_API(char*) PR_vsprintf_append(char *last, const char *fmt, va_list ap);
+NSPR_API(PRUint32) PR_vsxprintf(PRStuffFunc f, void *arg, const char *fmt, va_list ap);
+NSPR_API(PRUint32) PR_vfprintf(struct PRFileDesc* fd, const char *fmt, va_list ap);
+
+/*
+***************************************************************************
+** FUNCTION: PR_sscanf
+** DESCRIPTION:
+** PR_sscanf() scans the input character string, performs data
+** conversions, and stores the converted values in the data objects
+** pointed to by its arguments according to the format control
+** string.
+**
+** PR_sscanf() behaves the same way as the sscanf() function in the
+** Standard C Library (stdio.h), with the following exceptions:
+** - PR_sscanf() handles the NSPR integer and floating point types,
+** such as PRInt16, PRInt32, PRInt64, and PRFloat64, whereas
+** sscanf() handles the standard C types like short, int, long,
+** and double.
+** - PR_sscanf() has no multibyte character support, while sscanf()
+** does.
+** INPUTS:
+** const char *buf
+** a character string holding the input to scan
+** const char *fmt
+** the format control string for the conversions
+** ...
+** variable number of arguments, each of them is a pointer to
+** a data object in which the converted value will be stored
+** OUTPUTS: none
+** RETURNS: PRInt32
+** The number of values converted and stored.
+** RESTRICTIONS:
+** Multibyte characters in 'buf' or 'fmt' are not allowed.
+***************************************************************************
+*/
+
+NSPR_API(PRInt32) PR_sscanf(const char *buf, const char *fmt, ...);
+
+PR_END_EXTERN_C
+
+#endif /* prprf_h___ */
diff --git a/nsprpub/pr/include/prproces.h b/nsprpub/pr/include/prproces.h
new file mode 100644
index 0000000000..e23504ac57
--- /dev/null
+++ b/nsprpub/pr/include/prproces.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prproces_h___
+#define prproces_h___
+
+#include "prtypes.h"
+#include "prio.h"
+
+PR_BEGIN_EXTERN_C
+
+/************************************************************************/
+/*****************************PROCESS OPERATIONS*************************/
+/************************************************************************/
+
+typedef struct PRProcess PRProcess;
+typedef struct PRProcessAttr PRProcessAttr;
+
+NSPR_API(PRProcessAttr *) PR_NewProcessAttr(void);
+
+NSPR_API(void) PR_ResetProcessAttr(PRProcessAttr *attr);
+
+NSPR_API(void) PR_DestroyProcessAttr(PRProcessAttr *attr);
+
+NSPR_API(void) PR_ProcessAttrSetStdioRedirect(
+ PRProcessAttr *attr,
+ PRSpecialFD stdioFd,
+ PRFileDesc *redirectFd
+);
+
+/*
+ * OBSOLETE -- use PR_ProcessAttrSetStdioRedirect instead.
+ */
+NSPR_API(void) PR_SetStdioRedirect(
+ PRProcessAttr *attr,
+ PRSpecialFD stdioFd,
+ PRFileDesc *redirectFd
+);
+
+NSPR_API(PRStatus) PR_ProcessAttrSetCurrentDirectory(
+ PRProcessAttr *attr,
+ const char *dir
+);
+
+NSPR_API(PRStatus) PR_ProcessAttrSetInheritableFD(
+ PRProcessAttr *attr,
+ PRFileDesc *fd,
+ const char *name
+);
+
+/*
+** Create a new process
+**
+** Create a new process executing the file specified as 'path' and with
+** the supplied arguments and environment.
+**
+** This function may fail because of illegal access (permissions),
+** invalid arguments or insufficient resources.
+**
+** A process may be created such that the creator can later synchronize its
+** termination using PR_WaitProcess().
+*/
+
+NSPR_API(PRProcess*) PR_CreateProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr);
+
+NSPR_API(PRStatus) PR_CreateProcessDetached(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr);
+
+NSPR_API(PRStatus) PR_DetachProcess(PRProcess *process);
+
+NSPR_API(PRStatus) PR_WaitProcess(PRProcess *process, PRInt32 *exitCode);
+
+NSPR_API(PRStatus) PR_KillProcess(PRProcess *process);
+
+PR_END_EXTERN_C
+
+#endif /* prproces_h___ */
diff --git a/nsprpub/pr/include/prrng.h b/nsprpub/pr/include/prrng.h
new file mode 100644
index 0000000000..db7dc68c66
--- /dev/null
+++ b/nsprpub/pr/include/prrng.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+/*
+** prrng.h -- NSPR Random Number Generator
+**
+**
+** lth. 29-Oct-1999.
+*/
+
+#ifndef prrng_h___
+#define prrng_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** PR_GetRandomNoise() -- Get random noise from the host platform
+**
+** Description:
+** PR_GetRandomNoise() provides, depending on platform, a random value.
+** The length of the random value is dependent on platform and the
+** platform's ability to provide a random value at that moment.
+**
+** The intent of PR_GetRandomNoise() is to provide a "seed" value for a
+** another random number generator that may be suitable for
+** cryptographic operations. This implies that the random value
+** provided may not be, by itself, cryptographically secure. The value
+** generated by PR_GetRandomNoise() is at best, extremely difficult to
+** predict and is as non-deterministic as the underlying platfrom can
+** provide.
+**
+** Inputs:
+** buf -- pointer to a caller supplied buffer to contain the
+** generated random number. buf must be at least as large as
+** is specified in the 'size' argument.
+**
+** size -- the requested size of the generated random number
+**
+** Outputs:
+** a random number provided in 'buf'.
+**
+** Returns:
+** PRSize value equal to the size of the random number actually
+** generated, or zero. The generated size may be less than the size
+** requested. A return value of zero means that PR_GetRandomNoise() is
+** not implemented on this platform, or there is no available noise
+** available to be returned at the time of the call.
+**
+** Restrictions:
+** Calls to PR_GetRandomNoise() may use a lot of CPU on some platforms.
+** Some platforms may block for up to a few seconds while they
+** accumulate some noise. Busy machines generate lots of noise, but
+** care is advised when using PR_GetRandomNoise() frequently in your
+** application.
+**
+** History:
+** Parts of the model dependent implementation for PR_GetRandomNoise()
+** were taken in whole or part from code previously in Netscape's NSS
+** component.
+**
+*/
+NSPR_API(PRSize) PR_GetRandomNoise(
+ void *buf,
+ PRSize size
+);
+
+PR_END_EXTERN_C
+
+#endif /* prrng_h___ */
+/* end prrng.h */
diff --git a/nsprpub/pr/include/prrwlock.h b/nsprpub/pr/include/prrwlock.h
new file mode 100644
index 0000000000..ab47b53fb0
--- /dev/null
+++ b/nsprpub/pr/include/prrwlock.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: prrwlock.h
+** Description: API to basic reader-writer lock functions of NSPR.
+**
+**/
+
+#ifndef prrwlock_h___
+#define prrwlock_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+ * PRRWLock --
+ *
+ * The reader writer lock, PRRWLock, is an opaque object to the clients
+ * of NSPR. All routines operate on a pointer to this opaque entity.
+ */
+
+
+typedef struct PRRWLock PRRWLock;
+
+#define PR_RWLOCK_RANK_NONE 0
+
+
+/***********************************************************************
+** FUNCTION: PR_NewRWLock
+** DESCRIPTION:
+** Returns a pointer to a newly created reader-writer lock object.
+** INPUTS: Lock rank
+** Lock name
+** OUTPUTS: void
+** RETURN: PRRWLock*
+** If the lock cannot be created because of resource constraints, NULL
+** is returned.
+**
+***********************************************************************/
+NSPR_API(PRRWLock*) PR_NewRWLock(PRUint32 lock_rank, const char *lock_name);
+
+/***********************************************************************
+** FUNCTION: PR_DestroyRWLock
+** DESCRIPTION:
+** Destroys a given RW lock object.
+** INPUTS: PRRWLock *lock - Lock to be freed.
+** OUTPUTS: void
+** RETURN: None
+***********************************************************************/
+NSPR_API(void) PR_DestroyRWLock(PRRWLock *lock);
+
+/***********************************************************************
+** FUNCTION: PR_RWLock_Rlock
+** DESCRIPTION:
+** Apply a read lock (non-exclusive) on a RWLock
+** INPUTS: PRRWLock *lock - Lock to be read-locked.
+** OUTPUTS: void
+** RETURN: None
+***********************************************************************/
+NSPR_API(void) PR_RWLock_Rlock(PRRWLock *lock);
+
+/***********************************************************************
+** FUNCTION: PR_RWLock_Wlock
+** DESCRIPTION:
+** Apply a write lock (exclusive) on a RWLock
+** INPUTS: PRRWLock *lock - Lock to write-locked.
+** OUTPUTS: void
+** RETURN: None
+***********************************************************************/
+NSPR_API(void) PR_RWLock_Wlock(PRRWLock *lock);
+
+/***********************************************************************
+** FUNCTION: PR_RWLock_Unlock
+** DESCRIPTION:
+** Release a RW lock. Unlocking an unlocked lock has undefined results.
+** INPUTS: PRRWLock *lock - Lock to unlocked.
+** OUTPUTS: void
+** RETURN: void
+***********************************************************************/
+NSPR_API(void) PR_RWLock_Unlock(PRRWLock *lock);
+
+PR_END_EXTERN_C
+
+#endif /* prrwlock_h___ */
diff --git a/nsprpub/pr/include/prshm.h b/nsprpub/pr/include/prshm.h
new file mode 100644
index 0000000000..3588aed4e6
--- /dev/null
+++ b/nsprpub/pr/include/prshm.h
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** prshm.h -- NSPR Shared Memory
+**
+** NSPR Named Shared Memory API provides a cross-platform named
+** shared-memory interface. NSPR Named Shared Memory is modeled on
+** similar constructs in Unix and Windows operating systems. Shared
+** memory allows multiple processes to access one or more common shared
+** memory regions, using it as an inter-process communication channel.
+**
+** Notes on Platform Independence:
+** NSPR Named Shared Memory is built on the native services offered
+** by most platforms. The NSPR Named Shared Memory API tries to
+** provide a least common denominator interface so that it works
+** across all supported platforms. To ensure that it works everywhere,
+** some platform considerations must be accomodated and the protocol
+** for using NSPR Shared Memory API must be observed.
+**
+** Protocol:
+** Multiple shared memories can be created using NSPR's Shared Memory
+** feature. For each named shared memory, as defined by the name
+** given in the PR_OpenSharedMemory() call, a protocol for using the
+** shared memory API is required to ensure desired behavior. Failing
+** to follow the protocol may yield unpredictable results.
+**
+** PR_OpenSharedMemory() will create the shared memory segment, if it
+** does not already exist, or open a connection that the existing
+** shared memory segment if it already exists.
+**
+** PR_AttachSharedMemory() should be called following
+** PR_OpenSharedMemory() to map the memory segment to an address in
+** the application's address space.
+**
+** PR_AttachSharedMemory() may be called to re-map a shared memory
+** segment after detaching the same PRSharedMemory object. Be
+** sure to detach it when done.
+**
+** PR_DetachSharedMemory() should be called to un-map the shared
+** memory segment from the application's address space.
+**
+** PR_CloseSharedMemory() should be called when no further use of the
+** PRSharedMemory object is required within a process. Following a
+** call to PR_CloseSharedMemory() the PRSharedMemory object is
+** invalid and cannot be reused.
+**
+** PR_DeleteSharedMemory() should be called before process
+** termination. After calling PR_DeleteSharedMemory() any further use
+** of the shared memory associated with the name may cause
+** unpredictable results.
+**
+** Files:
+** The name passed to PR_OpenSharedMemory() should be a valid filename
+** for a unix platform. PR_OpenSharedMemory() creates file using the
+** name passed in. Some platforms may mangle the name before creating
+** the file and the shared memory.
+**
+** The unix implementation may use SysV IPC shared memory, Posix
+** shared memory, or memory mapped files; the filename may used to
+** define the namespace. On Windows, the name is significant, but
+** there is no file associated with name.
+**
+** No assumptions about the persistence of data in the named file
+** should be made. Depending on platform, the shared memory may be
+** mapped onto system paging space and be discarded at process
+** termination.
+**
+** All names provided to PR_OpenSharedMemory() should be valid
+** filename syntax or name syntax for shared memory for the target
+** platform. Referenced directories should have permissions
+** appropriate for writing.
+**
+** Limits:
+** Different platforms have limits on both the number and size of
+** shared memory resources. The default system limits on some
+** platforms may be smaller than your requirements. These limits may
+** be adjusted on some platforms either via boot-time options or by
+** setting the size of the system paging space to accomodate more
+** and/or larger shared memory segment(s).
+**
+** Security:
+** On unix platforms, depending on implementation, contents of the
+** backing store for the shared memory can be exposed via the file
+** system. Set permissions and or access controls at create and attach
+** time to ensure you get the desired security.
+**
+** On windows platforms, no special security measures are provided.
+**
+** Example:
+** The test case pr/tests/nameshm1.c provides an example of use as
+** well as testing the operation of NSPR's Named Shared Memory.
+**
+** lth. 18-Aug-1999.
+*/
+
+#ifndef prshm_h___
+#define prshm_h___
+
+#include "prtypes.h"
+#include "prio.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Declare opaque type PRSharedMemory.
+*/
+typedef struct PRSharedMemory PRSharedMemory;
+
+/*
+** FUNCTION: PR_OpenSharedMemory()
+**
+** DESCRIPTION:
+** PR_OpenSharedMemory() creates a new shared-memory segment or
+** associates a previously created memory segment with name.
+**
+** When parameter create is (PR_SHM_EXCL | PR_SHM_CREATE) and the
+** shared memory already exists, the function returns NULL with the
+** error set to PR_FILE_EXISTS_ERROR.
+**
+** When parameter create is PR_SHM_CREATE and the shared memory
+** already exists, a handle to that memory segment is returned. If
+** the segment does not exist, it is created and a pointer to the
+** related PRSharedMemory structure is returned.
+**
+** When parameter create is 0, and the shared memory exists, a
+** pointer to a PRSharedMemory is returned. If the shared memory does
+** not exist, NULL is returned with the error set to
+** PR_FILE_NOT_FOUND_ERROR.
+**
+** INPUTS:
+** name -- the name the shared-memory segment is known as.
+** size -- the size of the shared memory segment.
+** flags -- Options for creating the shared memory
+** mode -- Same as is passed to PR_Open()
+**
+** OUTPUTS:
+** The shared memory is allocated.
+**
+** RETURNS: Pointer to opaque structure PRSharedMemory or NULL.
+** NULL is returned on error. The reason for the error can be
+** retrieved via PR_GetError() and PR_GetOSError();
+**
+*/
+NSPR_API( PRSharedMemory * )
+PR_OpenSharedMemory(
+ const char *name,
+ PRSize size,
+ PRIntn flags,
+ PRIntn mode
+);
+/* Define values for PR_OpenShareMemory(...,create) */
+#define PR_SHM_CREATE 0x1 /* create if not exist */
+#define PR_SHM_EXCL 0x2 /* fail if already exists */
+
+/*
+** FUNCTION: PR_AttachSharedMemory()
+**
+** DESCRIPTION:
+** PR_AttachSharedMemory() maps the shared-memory described by
+** shm to the current process.
+**
+** INPUTS:
+** shm -- The handle returned from PR_OpenSharedMemory().
+** flags -- options for mapping the shared memory.
+** PR_SHM_READONLY causes the memory to be attached
+** read-only.
+**
+** OUTPUTS:
+** On success, the shared memory segment represented by shm is mapped
+** into the process' address space.
+**
+** RETURNS: Address where shared memory is mapped, or NULL.
+** NULL is returned on error. The reason for the error can be
+** retrieved via PR_GetError() and PR_GetOSError();
+**
+**
+*/
+NSPR_API( void * )
+PR_AttachSharedMemory(
+ PRSharedMemory *shm,
+ PRIntn flags
+);
+/* Define values for PR_AttachSharedMemory(...,flags) */
+#define PR_SHM_READONLY 0x01
+
+/*
+** FUNCTION: PR_DetachSharedMemory()
+**
+** DESCRIPTION:
+** PR_DetachSharedMemory() detaches the shared-memory described
+** by shm.
+**
+** INPUTS:
+** shm -- The handle returned from PR_OpenSharedMemory().
+** addr -- The address at which the memory was attached.
+**
+** OUTPUTS:
+** The shared memory mapped to an address via a previous call to
+** PR_AttachSharedMemory() is unmapped.
+**
+** RETURNS: PRStatus
+**
+*/
+NSPR_API( PRStatus )
+PR_DetachSharedMemory(
+ PRSharedMemory *shm,
+ void *addr
+);
+
+/*
+** FUNCTION: PR_CloseSharedMemory()
+**
+** DESCRIPTION:
+** PR_CloseSharedMemory() closes the shared-memory described by
+** shm.
+**
+** INPUTS:
+** shm -- The handle returned from PR_OpenSharedMemory().
+**
+** OUTPUTS:
+** the shared memory represented by shm is closed
+**
+** RETURNS: PRStatus
+**
+*/
+NSPR_API( PRStatus )
+PR_CloseSharedMemory(
+ PRSharedMemory *shm
+);
+
+/*
+** FUNCTION: PR_DeleteSharedMemory()
+**
+** DESCRIPTION:
+** The shared memory resource represented by name is released.
+**
+** INPUTS:
+** name -- the name the shared-memory segment
+**
+** OUTPUTS:
+** depending on platform, resources may be returned to the underlying
+** operating system.
+**
+** RETURNS: PRStatus
+**
+*/
+NSPR_API( PRStatus )
+PR_DeleteSharedMemory(
+ const char *name
+);
+
+PR_END_EXTERN_C
+
+#endif /* prshm_h___ */
diff --git a/nsprpub/pr/include/prshma.h b/nsprpub/pr/include/prshma.h
new file mode 100644
index 0000000000..2a1990ad6b
--- /dev/null
+++ b/nsprpub/pr/include/prshma.h
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** prshma.h -- NSPR Anonymous Shared Memory
+**
+** NSPR provides an anonymous shared memory based on NSPR's PRFileMap
+** type. The anonymous file-mapped shared memory provides an inheritable
+** shared memory, as in: the child process inherits the shared memory.
+** Compare the file-mapped anonymous shared memory to to a named shared
+** memory described in prshm.h. The intent is to provide a shared
+** memory that is accessable only by parent and child processes. ...
+** It's a security thing.
+**
+** Depending on the underlying platform, the file-mapped shared memory
+** may be backed by a file. ... surprise! ... On some platforms, no
+** real file backs the shared memory. On platforms where the shared
+** memory is backed by a file, the file's name in the filesystem is
+** visible to other processes for only the duration of the creation of
+** the file, hopefully a very short time. This restricts processess
+** that do not inherit the shared memory from opening the file and
+** reading or writing its contents. Further, when all processes
+** using an anonymous shared memory terminate, the backing file is
+** deleted. ... If you are not paranoid, you're not paying attention.
+**
+** The file-mapped shared memory requires a protocol for the parent
+** process and child process to share the memory. NSPR provides two
+** protocols. Use one or the other; don't mix and match.
+**
+** In the first protocol, the job of passing the inheritable shared
+** memory is done via helper-functions with PR_CreateProcess(). In the
+** second protocol, the parent process is responsible for creating the
+** child process; the parent and child are mutually responsible for
+** passing a FileMap string. NSPR provides helper functions for
+** extracting data from the PRFileMap object. ... See the examples
+** below.
+**
+** Both sides should adhere strictly to the protocol for proper
+** operation. The pseudo-code below shows the use of a file-mapped
+** shared memory by a parent and child processes. In the examples, the
+** server creates the file-mapped shared memory, the client attaches to
+** it.
+**
+** First protocol.
+** Server:
+**
+** fm = PR_OpenAnonFileMap(dirName, size, FilemapProt);
+** addr = PR_MemMap(fm);
+** attr = PR_NewProcessAttr();
+** PR_ProcessAttrSetInheritableFileMap( attr, fm, shmname );
+** PR_CreateProcess(Client);
+** PR_DestroyProcessAttr(attr);
+** ... yadda ...
+** PR_MemUnmap( addr );
+** PR_CloseFileMap(fm);
+**
+**
+** Client:
+** ... started by server via PR_CreateProcess()
+** fm = PR_GetInheritedFileMap( shmname );
+** addr = PR_MemMap(fm);
+** ... yadda ...
+** PR_MemUnmap(addr);
+** PR_CloseFileMap(fm);
+**
+**
+** Second Protocol:
+** Server:
+**
+** fm = PR_OpenAnonFileMap(dirName, size, FilemapProt);
+** fmstring = PR_ExportFileMapAsString( fm );
+** addr = PR_MemMap(fm);
+** ... application specific technique to pass fmstring to child
+** ... yadda ... Server uses his own magic to create child
+** PR_MemUnmap( addr );
+** PR_CloseFileMap(fm);
+**
+**
+** Client:
+** ... started by server via his own magic
+** ... application specific technique to find fmstring from parent
+** fm = PR_ImportFileMapFromString( fmstring )
+** addr = PR_MemMap(fm);
+** ... yadda ...
+** PR_MemUnmap(addr);
+** PR_CloseFileMap(fm);
+**
+**
+** lth. 2-Jul-1999.
+**
+** Note: The second protocol was requested by NelsonB (7/1999); this is
+** to accomodate servers which already create their own child processes
+** using platform native methods.
+**
+*/
+
+#ifndef prshma_h___
+#define prshma_h___
+
+#include "prtypes.h"
+#include "prio.h"
+#include "prproces.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** PR_OpenAnonFileMap() -- Creates an anonymous file-mapped shared memory
+**
+** Description:
+** PR_OpenAnonFileMap() creates an anonymous shared memory. If the
+** shared memory already exists, a handle is returned to that shared
+** memory object.
+**
+** On Unix platforms, PR_OpenAnonFileMap() uses 'dirName' as a
+** directory name, without the trailing '/', to contain the anonymous
+** file. A filename is generated for the name.
+**
+** On Windows platforms, dirName is ignored.
+**
+** Inputs:
+** dirName -- A directory name to contain the anonymous file.
+** size -- The size of the shared memory
+** prot -- How the shared memory is mapped. See prio.h
+**
+** Outputs:
+** PRFileMap *
+**
+** Returns:
+** Pointer to PRFileMap or NULL on error.
+**
+*/
+NSPR_API( PRFileMap *)
+PR_OpenAnonFileMap(
+ const char *dirName,
+ PRSize size,
+ PRFileMapProtect prot
+);
+
+/*
+** PR_ProcessAttrSetInheritableFileMap() -- Prepare FileMap for export
+** to my children processes via PR_CreateProcess()
+**
+** Description:
+** PR_ProcessAttrSetInheritableFileMap() connects the PRFileMap to
+** PRProcessAttr with shmname. A subsequent call to PR_CreateProcess()
+** makes the PRFileMap importable by the child process.
+**
+** Inputs:
+** attr -- PRProcessAttr, used to pass data to PR_CreateProcess()
+** fm -- PRFileMap structure to be passed to the child process
+** shmname -- The name for the PRFileMap; used by child.
+**
+** Outputs:
+** PRFileMap *
+**
+** Returns:
+** PRStatus
+**
+*/
+NSPR_API(PRStatus)
+PR_ProcessAttrSetInheritableFileMap(
+ PRProcessAttr *attr,
+ PRFileMap *fm,
+ const char *shmname
+);
+
+/*
+** PR_GetInheritedFileMap() -- Import a PRFileMap previously exported
+** by my parent process via PR_CreateProcess()
+**
+** Description:
+** PR_GetInheritedFileMap() retrieves a PRFileMap object exported from
+** its parent process via PR_CreateProcess().
+**
+** Inputs:
+** shmname -- The name provided to PR_ProcessAttrSetInheritableFileMap()
+**
+** Outputs:
+** PRFileMap *
+**
+** Returns:
+** PRFileMap pointer or NULL.
+**
+*/
+NSPR_API( PRFileMap *)
+PR_GetInheritedFileMap(
+ const char *shmname
+);
+
+/*
+** PR_ExportFileMapAsString() -- Creates a string identifying a PRFileMap
+**
+** Description:
+** Creates an identifier, as a string, from a PRFileMap object
+** previously created with PR_OpenAnonFileMap().
+**
+** Inputs:
+** fm -- PRFileMap pointer to be represented as a string.
+** bufsize -- sizeof(buf)
+** buf -- a buffer of length PR_FILEMAP_STRING_BUFSIZE
+**
+** Outputs:
+** buf contains the stringized PRFileMap identifier
+**
+** Returns:
+** PRStatus
+**
+*/
+NSPR_API( PRStatus )
+PR_ExportFileMapAsString(
+ PRFileMap *fm,
+ PRSize bufsize,
+ char *buf
+);
+#define PR_FILEMAP_STRING_BUFSIZE 128
+
+/*
+** PR_ImportFileMapFromString() -- Creates a PRFileMap from the identifying string
+**
+** Description:
+** PR_ImportFileMapFromString() creates a PRFileMap object from a
+** string previously created by PR_ExportFileMapAsString().
+**
+** Inputs:
+** fmstring -- string created by PR_ExportFileMapAsString()
+**
+** Returns:
+** PRFileMap pointer or NULL.
+**
+*/
+NSPR_API( PRFileMap * )
+PR_ImportFileMapFromString(
+ const char *fmstring
+);
+
+PR_END_EXTERN_C
+#endif /* prshma_h___ */
diff --git a/nsprpub/pr/include/prsystem.h b/nsprpub/pr/include/prsystem.h
new file mode 100644
index 0000000000..577e60e061
--- /dev/null
+++ b/nsprpub/pr/include/prsystem.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prsystem_h___
+#define prsystem_h___
+
+/*
+** API to NSPR functions returning system info.
+*/
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Get the host' directory separator.
+** Pathnames are then assumed to be of the form:
+** [<sep><root_component><sep>]*(<component><sep>)<leaf_name>
+*/
+
+NSPR_API(char) PR_GetDirectorySeparator(void);
+
+/*
+** OBSOLETE -- the function name is misspelled.
+** Use PR_GetDirectorySeparator instead.
+*/
+
+NSPR_API(char) PR_GetDirectorySepartor(void);
+
+/*
+** Get the host' path separator.
+** Paths are assumed to be of the form:
+** <directory>[<sep><directory>]*
+*/
+
+NSPR_API(char) PR_GetPathSeparator(void);
+
+/* Types of information available via PR_GetSystemInfo(...) */
+typedef enum {
+ PR_SI_HOSTNAME, /* the hostname with the domain name (if any)
+ * removed */
+ PR_SI_SYSNAME,
+ PR_SI_RELEASE,
+ PR_SI_ARCHITECTURE,
+ PR_SI_HOSTNAME_UNTRUNCATED, /* the hostname exactly as configured
+ * on the system */
+ PR_SI_RELEASE_BUILD
+} PRSysInfo;
+
+
+/*
+** If successful returns a null termintated string in 'buf' for
+** the information indicated in 'cmd'. If unseccussful the reason for
+** the failure can be retrieved from PR_GetError().
+**
+** The buffer is allocated by the caller and should be at least
+** SYS_INFO_BUFFER_LENGTH bytes in length.
+*/
+
+#define SYS_INFO_BUFFER_LENGTH 256
+
+NSPR_API(PRStatus) PR_GetSystemInfo(PRSysInfo cmd, char *buf, PRUint32 buflen);
+
+/*
+** Return the number of bytes in a page
+*/
+NSPR_API(PRInt32) PR_GetPageSize(void);
+
+/*
+** Return log2 of the size of a page
+*/
+NSPR_API(PRInt32) PR_GetPageShift(void);
+
+/*
+** PR_GetNumberOfProcessors() -- returns the number of CPUs
+**
+** Description:
+** PR_GetNumberOfProcessors() extracts the number of processors
+** (CPUs available in an SMP system) and returns the number.
+**
+** Parameters:
+** none
+**
+** Returns:
+** The number of available processors or -1 on error
+**
+*/
+NSPR_API(PRInt32) PR_GetNumberOfProcessors( void );
+
+/*
+** PR_GetPhysicalMemorySize() -- returns the amount of system RAM
+**
+** Description:
+** PR_GetPhysicalMemorySize() determines the amount of physical RAM
+** in the system and returns the size in bytes.
+**
+** Parameters:
+** none
+**
+** Returns:
+** The amount of system RAM, or 0 on failure.
+**
+*/
+NSPR_API(PRUint64) PR_GetPhysicalMemorySize(void);
+
+PR_END_EXTERN_C
+
+#endif /* prsystem_h___ */
diff --git a/nsprpub/pr/include/prthread.h b/nsprpub/pr/include/prthread.h
new file mode 100644
index 0000000000..cacd3891bf
--- /dev/null
+++ b/nsprpub/pr/include/prthread.h
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prthread_h___
+#define prthread_h___
+
+/*
+** API for NSPR threads. On some architectures (Mac OS Classic
+** notably) pre-emptibility is not guaranteed. Hard priority scheduling
+** is not guaranteed, so programming using priority based synchronization
+** is a no-no.
+**
+** NSPR threads are scheduled based loosely on their client set priority.
+** In general, a thread of a higher priority has a statistically better
+** chance of running relative to threads of lower priority. However,
+** NSPR uses multiple strategies to provide execution vehicles for thread
+** abstraction of various host platforms. As it turns out, there is little
+** NSPR can do to affect the scheduling attributes of "GLOBAL" threads.
+** However, a semblance of GLOBAL threads is used to implement "LOCAL"
+** threads. An arbitrary number of such LOCAL threads can be assigned to
+** a single GLOBAL thread.
+**
+** For scheduling, NSPR will attempt to run the highest priority LOCAL
+** thread associated with a given GLOBAL thread. It is further assumed
+** that the host OS will apply some form of "fair" scheduling on the
+** GLOBAL threads.
+**
+** Threads have a "system flag" which when set indicates the thread
+** doesn't count for determining when the process should exit (the
+** process exits when the last user thread exits).
+**
+** Threads also have a "scope flag" which controls whether the threads
+** are scheduled in the local scope or scheduled by the OS globally. This
+** indicates whether a thread is permanently bound to a native OS thread.
+** An unbound thread competes for scheduling resources in the same process.
+**
+** Another flag is "state flag" which control whether the thread is joinable.
+** It allows other threads to wait for the created thread to reach completion.
+**
+** Threads can have "per-thread-data" attached to them. Each thread has a
+** per-thread error number and error string which are updated when NSPR
+** operations fail.
+*/
+#include "prtypes.h"
+#include "prinrval.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PRThread PRThread;
+typedef struct PRThreadStack PRThreadStack;
+
+typedef enum PRThreadType {
+ PR_USER_THREAD,
+ PR_SYSTEM_THREAD
+} PRThreadType;
+
+typedef enum PRThreadScope {
+ PR_LOCAL_THREAD,
+ PR_GLOBAL_THREAD,
+ PR_GLOBAL_BOUND_THREAD
+} PRThreadScope;
+
+typedef enum PRThreadState {
+ PR_JOINABLE_THREAD,
+ PR_UNJOINABLE_THREAD
+} PRThreadState;
+
+typedef enum PRThreadPriority
+{
+ PR_PRIORITY_FIRST = 0, /* just a placeholder */
+ PR_PRIORITY_LOW = 0, /* the lowest possible priority */
+ PR_PRIORITY_NORMAL = 1, /* most common expected priority */
+ PR_PRIORITY_HIGH = 2, /* slightly more aggressive scheduling */
+ PR_PRIORITY_URGENT = 3, /* it does little good to have more than one */
+ PR_PRIORITY_LAST = 3 /* this is just a placeholder */
+} PRThreadPriority;
+
+/*
+** Create a new thread:
+** "type" is the type of thread to create
+** "start(arg)" will be invoked as the threads "main"
+** "priority" will be created thread's priority
+** "scope" will specify whether the thread is local or global
+** "state" will specify whether the thread is joinable or not
+** "stackSize" the size of the stack, in bytes. The value can be zero
+** and then a machine specific stack size will be chosen.
+**
+** This can return NULL if some kind of error occurs, such as if memory is
+** tight.
+**
+** If you want the thread to start up waiting for the creator to do
+** something, enter a lock before creating the thread and then have the
+** threads start routine enter and exit the same lock. When you are ready
+** for the thread to run, exit the lock.
+**
+** If you want to detect the completion of the created thread, the thread
+** should be created joinable. Then, use PR_JoinThread to synchrnoize the
+** termination of another thread.
+**
+** When the start function returns the thread exits. If it is the last
+** PR_USER_THREAD to exit then the process exits.
+*/
+NSPR_API(PRThread*) PR_CreateThread(PRThreadType type,
+ void (PR_CALLBACK *start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+
+/*
+** Wait for thread termination:
+** "thread" is the target thread
+**
+** This can return PR_FAILURE if no joinable thread could be found
+** corresponding to the specified target thread.
+**
+** The calling thread is blocked until the target thread completes.
+** Several threads cannot wait for the same thread to complete; one thread
+** will operate successfully and others will terminate with an error PR_FAILURE.
+** The calling thread will not be blocked if the target thread has already
+** terminated.
+*/
+NSPR_API(PRStatus) PR_JoinThread(PRThread *thread);
+
+/*
+** Return the current thread object for the currently running code.
+** Never returns NULL.
+*/
+NSPR_API(PRThread*) PR_GetCurrentThread(void);
+#ifndef NO_NSPR_10_SUPPORT
+#define PR_CurrentThread() PR_GetCurrentThread() /* for nspr1.0 compat. */
+#endif /* NO_NSPR_10_SUPPORT */
+
+/*
+** Get the priority of "thread".
+*/
+NSPR_API(PRThreadPriority) PR_GetThreadPriority(const PRThread *thread);
+
+/*
+** Change the priority of the "thread" to "priority".
+**
+** PR_SetThreadPriority works in a best-effort manner. On some platforms a
+** special privilege, such as root access, is required to change thread
+** priorities, especially to raise thread priorities. If the caller doesn't
+** have enough privileges to change thread priorites, the function has no
+** effect except causing a future PR_GetThreadPriority call to return
+** |priority|.
+*/
+NSPR_API(void) PR_SetThreadPriority(PRThread *thread, PRThreadPriority priority);
+
+/*
+** Set the name of the current thread, which will be visible in a debugger
+** and accessible via a call to PR_GetThreadName().
+*/
+NSPR_API(PRStatus) PR_SetCurrentThreadName(const char *name);
+
+/*
+** Return the name of "thread", if set. Otherwise return NULL.
+*/
+NSPR_API(const char *) PR_GetThreadName(const PRThread *thread);
+
+/*
+** This routine returns a new index for per-thread-private data table.
+** The index is visible to all threads within a process. This index can
+** be used with the PR_SetThreadPrivate() and PR_GetThreadPrivate() routines
+** to save and retrieve data associated with the index for a thread.
+**
+** Each index is associationed with a destructor function ('dtor'). The function
+** may be specified as NULL when the index is created. If it is not NULL, the
+** function will be called when:
+** - the thread exits and the private data for the associated index
+** is not NULL,
+** - new thread private data is set and the current private data is
+** not NULL.
+**
+** The index independently maintains specific values for each binding thread.
+** A thread can only get access to its own thread-specific-data.
+**
+** Upon a new index return the value associated with the index for all threads
+** is NULL, and upon thread creation the value associated with all indices for
+** that thread is NULL.
+**
+** Returns PR_FAILURE if the total number of indices will exceed the maximun
+** allowed.
+*/
+typedef void (PR_CALLBACK *PRThreadPrivateDTOR)(void *priv);
+
+NSPR_API(PRStatus) PR_NewThreadPrivateIndex(
+ PRUintn *newIndex, PRThreadPrivateDTOR destructor);
+
+/*
+** Define some per-thread-private data.
+** "tpdIndex" is an index into the per-thread private data table
+** "priv" is the per-thread-private data
+**
+** If the per-thread private data table has a previously registered
+** destructor function and a non-NULL per-thread-private data value,
+** the destructor function is invoked.
+**
+** This can return PR_FAILURE if the index is invalid.
+*/
+NSPR_API(PRStatus) PR_SetThreadPrivate(PRUintn tpdIndex, void *priv);
+
+/*
+** Recover the per-thread-private data for the current thread. "tpdIndex" is
+** the index into the per-thread private data table.
+**
+** The returned value may be NULL which is indistinguishable from an error
+** condition.
+**
+** A thread can only get access to its own thread-specific-data.
+*/
+NSPR_API(void*) PR_GetThreadPrivate(PRUintn tpdIndex);
+
+/*
+** This routine sets the interrupt request for a target thread. The interrupt
+** request remains in the thread's state until it is delivered exactly once
+** or explicitly canceled.
+**
+** A thread that has been interrupted will fail all NSPR blocking operations
+** that return a PRStatus (I/O, waiting on a condition, etc).
+**
+** PR_Interrupt may itself fail if the target thread is invalid.
+*/
+NSPR_API(PRStatus) PR_Interrupt(PRThread *thread);
+
+/*
+** Clear the interrupt request for the calling thread. If no such request
+** is pending, this operation is a noop.
+*/
+NSPR_API(void) PR_ClearInterrupt(void);
+
+/*
+** Block the interrupt for the calling thread.
+*/
+NSPR_API(void) PR_BlockInterrupt(void);
+
+/*
+** Unblock the interrupt for the calling thread.
+*/
+NSPR_API(void) PR_UnblockInterrupt(void);
+
+/*
+** Make the current thread sleep until "ticks" time amount of time
+** has expired. If "ticks" is PR_INTERVAL_NO_WAIT then the call is
+** equivalent to calling PR_Yield. Calling PR_Sleep with an argument
+** equivalent to PR_INTERVAL_NO_TIMEOUT is an error and will result
+** in a PR_FAILURE error return.
+*/
+NSPR_API(PRStatus) PR_Sleep(PRIntervalTime ticks);
+
+/*
+** Get the scoping of this thread.
+*/
+NSPR_API(PRThreadScope) PR_GetThreadScope(const PRThread *thread);
+
+/*
+** Get the type of this thread.
+*/
+NSPR_API(PRThreadType) PR_GetThreadType(const PRThread *thread);
+
+/*
+** Get the join state of this thread.
+*/
+NSPR_API(PRThreadState) PR_GetThreadState(const PRThread *thread);
+
+PR_END_EXTERN_C
+
+#endif /* prthread_h___ */
diff --git a/nsprpub/pr/include/prtime.h b/nsprpub/pr/include/prtime.h
new file mode 100644
index 0000000000..917a16159d
--- /dev/null
+++ b/nsprpub/pr/include/prtime.h
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * prtime.h --
+ *
+ * NSPR date and time functions
+ *
+ *-----------------------------------------------------------------------
+ */
+
+#ifndef prtime_h___
+#define prtime_h___
+
+#include "prlong.h"
+
+PR_BEGIN_EXTERN_C
+
+/**********************************************************************/
+/************************* TYPES AND CONSTANTS ************************/
+/**********************************************************************/
+
+#define PR_MSEC_PER_SEC 1000L
+#define PR_USEC_PER_SEC 1000000L
+#define PR_NSEC_PER_SEC 1000000000L
+#define PR_USEC_PER_MSEC 1000L
+#define PR_NSEC_PER_MSEC 1000000L
+
+/*
+ * PRTime --
+ *
+ * NSPR represents basic time as 64-bit signed integers relative
+ * to midnight (00:00:00), January 1, 1970 Greenwich Mean Time (GMT).
+ * (GMT is also known as Coordinated Universal Time, UTC.)
+ * The units of time are in microseconds. Negative times are allowed
+ * to represent times prior to the January 1970 epoch. Such values are
+ * intended to be exported to other systems or converted to human
+ * readable form.
+ *
+ * Notes on porting: PRTime corresponds to time_t in ANSI C. NSPR 1.0
+ * simply uses PRInt64.
+ */
+
+typedef PRInt64 PRTime;
+
+/*
+ * Time zone and daylight saving time corrections applied to GMT to
+ * obtain the local time of some geographic location
+ */
+
+typedef struct PRTimeParameters {
+ PRInt32 tp_gmt_offset; /* the offset from GMT in seconds */
+ PRInt32 tp_dst_offset; /* contribution of DST in seconds */
+} PRTimeParameters;
+
+/*
+ * PRExplodedTime --
+ *
+ * Time broken down into human-readable components such as year, month,
+ * day, hour, minute, second, and microsecond. Time zone and daylight
+ * saving time corrections may be applied. If they are applied, the
+ * offsets from the GMT must be saved in the 'tm_params' field so that
+ * all the information is available to reconstruct GMT.
+ *
+ * Notes on porting: PRExplodedTime corrresponds to struct tm in
+ * ANSI C, with the following differences:
+ * - an additional field tm_usec;
+ * - replacing tm_isdst by tm_params;
+ * - the month field is spelled tm_month, not tm_mon;
+ * - we use absolute year, AD, not the year since 1900.
+ * The corresponding type in NSPR 1.0 is called PRTime. Below is
+ * a table of date/time type correspondence in the three APIs:
+ * API time since epoch time in components
+ * ANSI C time_t struct tm
+ * NSPR 1.0 PRInt64 PRTime
+ * NSPR 2.0 PRTime PRExplodedTime
+ */
+
+typedef struct PRExplodedTime {
+ PRInt32 tm_usec; /* microseconds past tm_sec (0-99999) */
+ PRInt32 tm_sec; /* seconds past tm_min (0-61, accomodating
+ up to two leap seconds) */
+ PRInt32 tm_min; /* minutes past tm_hour (0-59) */
+ PRInt32 tm_hour; /* hours past tm_day (0-23) */
+ PRInt32 tm_mday; /* days past tm_mon (1-31, note that it
+ starts from 1) */
+ PRInt32 tm_month; /* months past tm_year (0-11, Jan = 0) */
+ PRInt16 tm_year; /* absolute year, AD (note that we do not
+ count from 1900) */
+
+ PRInt8 tm_wday; /* calculated day of the week
+ (0-6, Sun = 0) */
+ PRInt16 tm_yday; /* calculated day of the year
+ (0-365, Jan 1 = 0) */
+
+ PRTimeParameters tm_params; /* time parameters used by conversion */
+} PRExplodedTime;
+
+/*
+ * PRTimeParamFn --
+ *
+ * A function of PRTimeParamFn type returns the time zone and
+ * daylight saving time corrections for some geographic location,
+ * given the current time in GMT. The input argument gmt should
+ * point to a PRExplodedTime that is in GMT, i.e., whose
+ * tm_params contains all 0's.
+ *
+ * For any time zone other than GMT, the computation is intended to
+ * consist of two steps:
+ * - Figure out the time zone correction, tp_gmt_offset. This number
+ * usually depends on the geographic location only. But it may
+ * also depend on the current time. For example, all of China
+ * is one time zone right now. But this situation may change
+ * in the future.
+ * - Figure out the daylight saving time correction, tp_dst_offset.
+ * This number depends on both the geographic location and the
+ * current time. Most of the DST rules are expressed in local
+ * current time. If so, one should apply the time zone correction
+ * to GMT before applying the DST rules.
+ */
+
+typedef PRTimeParameters (PR_CALLBACK *PRTimeParamFn)(const PRExplodedTime *gmt);
+
+/**********************************************************************/
+/****************************** FUNCTIONS *****************************/
+/**********************************************************************/
+
+/*
+ * The PR_Now routine returns the current time relative to the
+ * epoch, midnight, January 1, 1970 UTC. The units of the returned
+ * value are microseconds since the epoch.
+ *
+ * The values returned are not guaranteed to advance in a linear fashion
+ * due to the application of time correction protocols which synchronize
+ * computer clocks to some external time source. Consequently it should
+ * not be depended on for interval timing.
+ *
+ * The implementation is machine dependent.
+ * Cf. time_t time(time_t *tp) in ANSI C.
+ */
+NSPR_API(PRTime)
+PR_Now(void);
+
+/*
+ * Expand time binding it to time parameters provided by PRTimeParamFn.
+ * The calculation is envisoned to proceed in the following steps:
+ * - From given PRTime, calculate PRExplodedTime in GMT
+ * - Apply the given PRTimeParamFn to the GMT that we just calculated
+ * to obtain PRTimeParameters.
+ * - Add the PRTimeParameters offsets to GMT to get the local time
+ * as PRExplodedTime.
+ */
+
+NSPR_API(void) PR_ExplodeTime(
+ PRTime usecs, PRTimeParamFn params, PRExplodedTime *exploded);
+
+/* Reverse operation of PR_ExplodeTime */
+NSPR_API(PRTime)
+PR_ImplodeTime(const PRExplodedTime *exploded);
+
+/*
+ * Adjust exploded time to normalize field overflows after manipulation.
+ * Note that the following fields of PRExplodedTime should not be
+ * manipulated:
+ * - tm_month and tm_year: because the number of days in a month and
+ * number of days in a year are not constant, it is ambiguous to
+ * manipulate the month and year fields, although one may be tempted
+ * to. For example, what does "a month from January 31st" mean?
+ * - tm_wday and tm_yday: these fields are calculated by NSPR. Users
+ * should treat them as "read-only".
+ */
+
+NSPR_API(void) PR_NormalizeTime(
+ PRExplodedTime *exploded, PRTimeParamFn params);
+
+/**********************************************************************/
+/*********************** TIME PARAMETER FUNCTIONS *********************/
+/**********************************************************************/
+
+/* Time parameters that suit current host machine */
+NSPR_API(PRTimeParameters) PR_LocalTimeParameters(const PRExplodedTime *gmt);
+
+/* Time parameters that represent Greenwich Mean Time */
+NSPR_API(PRTimeParameters) PR_GMTParameters(const PRExplodedTime *gmt);
+
+/*
+ * Time parameters that represent the US Pacific Time Zone, with the
+ * current daylight saving time rules (for testing only)
+ */
+NSPR_API(PRTimeParameters) PR_USPacificTimeParameters(const PRExplodedTime *gmt);
+
+/*
+ * This parses a time/date string into a PRExplodedTime
+ * struct. It populates all fields but it can't split
+ * the offset from UTC into tp_gmt_offset and tp_dst_offset in
+ * most cases (exceptions: PST/PDT, MST/MDT, CST/CDT, EST/EDT, GMT/BST).
+ * In those cases tp_gmt_offset will be the sum of these two and
+ * tp_dst_offset will be 0.
+ * It returns PR_SUCCESS on success, and PR_FAILURE
+ * if the time/date string can't be parsed.
+ *
+ * Many formats are handled, including:
+ *
+ * 14 Apr 89 03:20:12
+ * 14 Apr 89 03:20 GMT
+ * Fri, 17 Mar 89 4:01:33
+ * Fri, 17 Mar 89 4:01 GMT
+ * Mon Jan 16 16:12 PDT 1989
+ * Mon Jan 16 16:12 +0130 1989
+ * 6 May 1992 16:41-JST (Wednesday)
+ * 22-AUG-1993 10:59:12.82
+ * 22-AUG-1993 10:59pm
+ * 22-AUG-1993 12:59am
+ * 22-AUG-1993 12:59 PM
+ * Friday, August 04, 1995 3:54 PM
+ * 06/21/95 04:24:34 PM
+ * 20/06/95 21:07
+ * 95-06-08 19:32:48 EDT
+ *
+ * If the input string doesn't contain a description of the timezone,
+ * we consult the `default_to_gmt' to decide whether the string should
+ * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
+ * The correct value for this argument depends on what standard specified
+ * the time string which you are parsing.
+ */
+
+NSPR_API(PRStatus) PR_ParseTimeStringToExplodedTime (
+ const char *string,
+ PRBool default_to_gmt,
+ PRExplodedTime *result);
+
+/*
+ * This uses PR_ParseTimeStringToExplodedTime to parse
+ * a time/date string and PR_ImplodeTime to transform it into
+ * a PRTime (microseconds after "1-Jan-1970 00:00:00 GMT").
+ * It returns PR_SUCCESS on success, and PR_FAILURE
+ * if the time/date string can't be parsed.
+ */
+
+NSPR_API(PRStatus) PR_ParseTimeString (
+ const char *string,
+ PRBool default_to_gmt,
+ PRTime *result);
+
+/* Format a time value into a buffer. Same semantics as strftime() */
+NSPR_API(PRUint32) PR_FormatTime(char *buf, int buflen, const char *fmt,
+ const PRExplodedTime *time);
+
+/* Format a time value into a buffer. Time is always in US English format,
+ * regardless of locale setting.
+ */
+NSPR_API(PRUint32)
+PR_FormatTimeUSEnglish(char *buf, PRUint32 bufSize,
+ const char *format, const PRExplodedTime *time);
+
+PR_END_EXTERN_C
+
+#endif /* prtime_h___ */
diff --git a/nsprpub/pr/include/prtpool.h b/nsprpub/pr/include/prtpool.h
new file mode 100644
index 0000000000..b4d18d4152
--- /dev/null
+++ b/nsprpub/pr/include/prtpool.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prtpool_h___
+#define prtpool_h___
+
+#include "prtypes.h"
+#include "prthread.h"
+#include "prio.h"
+#include "prerror.h"
+
+/*
+ * NOTE:
+ * THIS API IS A PRELIMINARY VERSION IN NSPR 4.0 AND IS SUBJECT TO
+ * CHANGE
+ */
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PRJobIoDesc {
+ PRFileDesc *socket;
+ PRErrorCode error;
+ PRIntervalTime timeout;
+} PRJobIoDesc;
+
+typedef struct PRThreadPool PRThreadPool;
+typedef struct PRJob PRJob;
+typedef void (PR_CALLBACK *PRJobFn) (void *arg);
+
+/* Create thread pool */
+NSPR_API(PRThreadPool *)
+PR_CreateThreadPool(PRInt32 initial_threads, PRInt32 max_threads,
+ PRUint32 stacksize);
+
+/* queue a job */
+NSPR_API(PRJob *)
+PR_QueueJob(PRThreadPool *tpool, PRJobFn fn, void *arg, PRBool joinable);
+
+/* queue a job, when a socket is readable */
+NSPR_API(PRJob *)
+PR_QueueJob_Read(PRThreadPool *tpool, PRJobIoDesc *iod,
+ PRJobFn fn, void * arg, PRBool joinable);
+
+/* queue a job, when a socket is writeable */
+NSPR_API(PRJob *)
+PR_QueueJob_Write(PRThreadPool *tpool, PRJobIoDesc *iod,
+ PRJobFn fn, void * arg, PRBool joinable);
+
+/* queue a job, when a socket has a pending connection */
+NSPR_API(PRJob *)
+PR_QueueJob_Accept(PRThreadPool *tpool, PRJobIoDesc *iod,
+ PRJobFn fn, void * arg, PRBool joinable);
+
+/* queue a job, when the socket connection to addr succeeds or fails */
+NSPR_API(PRJob *)
+PR_QueueJob_Connect(PRThreadPool *tpool, PRJobIoDesc *iod,
+ const PRNetAddr *addr, PRJobFn fn, void * arg, PRBool joinable);
+
+/* queue a job, when a timer exipres */
+NSPR_API(PRJob *)
+PR_QueueJob_Timer(PRThreadPool *tpool, PRIntervalTime timeout,
+ PRJobFn fn, void * arg, PRBool joinable);
+/* cancel a job */
+NSPR_API(PRStatus)
+PR_CancelJob(PRJob *job);
+
+/* join a job */
+NSPR_API(PRStatus)
+PR_JoinJob(PRJob *job);
+
+/* shutdown pool */
+NSPR_API(PRStatus)
+PR_ShutdownThreadPool(PRThreadPool *tpool);
+
+/* join pool, wait for exit of all threads */
+NSPR_API(PRStatus)
+PR_JoinThreadPool(PRThreadPool *tpool);
+
+PR_END_EXTERN_C
+
+#endif /* prtpool_h___ */
diff --git a/nsprpub/pr/include/prtrace.h b/nsprpub/pr/include/prtrace.h
new file mode 100644
index 0000000000..49bd3edb74
--- /dev/null
+++ b/nsprpub/pr/include/prtrace.h
@@ -0,0 +1,646 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prtrace_h___
+#define prtrace_h___
+/*
+** prtrace.h -- NSPR's Trace Facility.
+**
+** The Trace Facility provides a means to trace application
+** program events within a process. When implementing an
+** application program an engineer may insert a "Trace" function
+** call, passing arguments to be traced. The "Trace" function
+** combines the user trace data with identifying data and
+** writes this data in time ordered sequence into a circular
+** in-memory buffer; when the buffer fills, it wraps.
+**
+** Functions are provided to set and/or re-configure the size of
+** the trace buffer, control what events are recorded in the
+** buffer, enable and disable tracing based on specific user
+** supplied data and other control functions. Methods are provided
+** to record the trace entries in the in-memory trace buffer to
+** a file.
+**
+** Tracing may cause a performance degredation to the application
+** depending on the number and placement of calls to the tracing
+** facility. When tracing is compiled in and all tracing is
+** disabled via the runtime controls, the overhead should be
+** minimal. ... Famous last words, eh?
+**
+** When DEBUG is defined at compile time, the Trace Facility is
+** compiled as part of NSPR and any application using NSPR's
+** header files will have tracing compiled in. When DEBUG is not
+** defined, the Trace Facility is not compiled into NSPR nor
+** exported in its header files. If the Trace Facility is
+** desired in a non-debug build, then FORCE_NSPR_TRACE may be
+** defined at compile time for both the optimized build of NSPR
+** and the application. NSPR and any application using NSPR's
+** Trace Facility must be compiled with the same level of trace
+** conditioning or unresolved references may be realized at link
+** time.
+**
+** For any of the Trace Facility methods that requires a trace
+** handle as an input argument, the caller must ensure that the
+** trace handle argument is valid. An invalid trace handle
+** argument may cause unpredictable results.
+**
+** Trace Facility methods are thread-safe and SMP safe.
+**
+** Users of the Trace Facility should use the defined macros to
+** invoke trace methods, not the function calls directly. e.g.
+** PR_TRACE( h1,0,1,2, ...); not PR_Trace(h1,0,1,2, ...);
+**
+** Application designers should be aware of the effects of
+** debug and optimized build differences when using result of the
+** Trace Facility macros in expressions.
+**
+** See Also: prcountr.h
+**
+** /lth. 08-Jun-1998.
+*/
+
+#include "prtypes.h"
+#include "prthread.h"
+#include "prtime.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Opaque type for the trace handle
+** ... Don't even think about looking in here.
+**
+*/
+typedef void * PRTraceHandle;
+
+/*
+** PRTraceEntry -- A trace entry in the in-memory trace buffer
+** looks like this.
+**
+*/
+typedef struct PRTraceEntry
+{
+ PRThread *thread; /* The thread creating the trace entry */
+ PRTraceHandle handle; /* PRTraceHandle creating the trace entry */
+ PRTime time; /* Value of PR_Now() at time of trace entry */
+ PRUint32 userData[8]; /* user supplied trace data */
+} PRTraceEntry;
+
+/*
+** PRTraceOption -- command operands to
+** PR_[Set|Get]TraceOption(). See descriptive meanings there.
+**
+*/
+typedef enum PRTraceOption
+{
+ PRTraceBufSize,
+ PRTraceEnable,
+ PRTraceDisable,
+ PRTraceSuspend,
+ PRTraceResume,
+ PRTraceSuspendRecording,
+ PRTraceResumeRecording,
+ PRTraceLockHandles,
+ PRTraceUnLockHandles,
+ PRTraceStopRecording
+} PRTraceOption;
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_DEFINE_TRACE() -- Define a PRTraceHandle
+**
+** DESCRIPTION: PR_DEFINE_TRACE() is used to define a trace
+** handle.
+**
+*/
+#define PR_DEFINE_TRACE(name) PRTraceHandle name
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_INIT_TRACE_HANDLE() -- Set the value of a PRTraceHandle
+**
+** DESCRIPTION:
+** PR_INIT_TRACE_HANDLE() sets the value of a PRTraceHandle
+** to value. e.g. PR_INIT_TRACE_HANDLE( myHandle, NULL );
+**
+*/
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+#define PR_INIT_TRACE_HANDLE(handle,value)\
+ (handle) = (PRCounterHandle)(value)
+#else
+#define PR_INIT_TRACE_HANDLE(handle,value)
+#endif
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_CreateTrace() -- Create a trace handle
+**
+** DESCRIPTION:
+** PR_CreateTrace() creates a new trace handle. Tracing is
+** enabled for this handle when it is created. The trace handle
+** is intended for use in other Trace Facility calls.
+**
+** PR_CreateTrace() registers the QName, RName and description
+** data so that this data can be retrieved later.
+**
+** INPUTS:
+** qName: pointer to string. QName for this trace handle.
+**
+** rName: pointer to string. RName for this trace handle.
+**
+** description: pointer to string. Descriptive data about this
+** trace handle.
+**
+** OUTPUTS:
+** Creates the trace handle.
+** Registers the QName and RName with the trace facility.
+**
+** RETURNS:
+** PRTraceHandle
+**
+** RESTRICTIONS:
+** qName is limited to 31 characters.
+** rName is limited to 31 characters.
+** description is limited to 255 characters.
+**
+*/
+#define PRTRACE_NAME_MAX 31
+#define PRTRACE_DESC_MAX 255
+
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+#define PR_CREATE_TRACE(handle,qName,rName,description)\
+ (handle) = PR_CreateTrace((qName),(rName),(description))
+#else
+#define PR_CREATE_TRACE(handle,qName,rName,description)
+#endif
+
+NSPR_API(PRTraceHandle)
+PR_CreateTrace(
+ const char *qName, /* QName for this trace handle */
+ const char *rName, /* RName for this trace handle */
+ const char *description /* description for this trace handle */
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_DestroyTrace() -- Destroy a trace handle
+**
+** DESCRIPTION:
+** PR_DestroyTrace() removes the referenced trace handle and
+** associated QName, RName and description data from the Trace
+** Facility.
+**
+** INPUTS: handle. A PRTraceHandle
+**
+** OUTPUTS:
+** The trace handle is unregistered.
+** The QName, RName and description are removed.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+#define PR_DESTROY_TRACE(handle)\
+ PR_DestroyTrace((handle))
+#else
+#define PR_DESTROY_TRACE(handle)
+#endif
+
+NSPR_API(void)
+PR_DestroyTrace(
+ PRTraceHandle handle /* Handle to be destroyed */
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_Trace() -- Make a trace entry in the in-memory trace
+**
+** DESCRIPTION:
+** PR_Trace() makes an entry in the in-memory trace buffer for
+** the referenced trace handle. The next logically available
+** PRTraceEntry is used; when the next trace entry would overflow
+** the trace table, the table wraps.
+**
+** PR_Trace() for a specific trace handle may be disabled by
+** calling PR_SetTraceOption() specifying PRTraceDisable for the
+** trace handle to be disabled.
+**
+** INPUTS:
+** handle: PRTraceHandle. The trace handle for this trace.
+**
+** userData[0..7]: unsigned 32bit integers. user supplied data
+** that is copied into the PRTraceEntry
+**
+** OUTPUTS:
+** A PRTraceEntry is (conditionally) formatted in the in-memory
+** trace buffer.
+**
+** RETURNS: void.
+**
+** RESTRICTIONS:
+**
+*/
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+#define PR_TRACE(handle,ud0,ud1,ud2,ud3,ud4,ud5,ud6,ud7)\
+ PR_Trace((handle),(ud0),(ud1),(ud2),(ud3),(ud4),(ud5),(ud6),(ud7))
+#else
+#define PR_TRACE(handle,ud0,ud1,ud2,ud3,ud4,ud5,ud6,ud7)
+#endif
+
+NSPR_API(void)
+PR_Trace(
+ PRTraceHandle handle, /* use this trace handle */
+ PRUint32 userData0, /* User supplied data word 0 */
+ PRUint32 userData1, /* User supplied data word 1 */
+ PRUint32 userData2, /* User supplied data word 2 */
+ PRUint32 userData3, /* User supplied data word 3 */
+ PRUint32 userData4, /* User supplied data word 4 */
+ PRUint32 userData5, /* User supplied data word 5 */
+ PRUint32 userData6, /* User supplied data word 6 */
+ PRUint32 userData7 /* User supplied data word 7 */
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_SetTraceOption() -- Control the Trace Facility
+**
+** DESCRIPTION:
+** PR_SetTraceOption() controls the Trace Facility. Depending on
+** command and value, attributes of the Trace Facility may be
+** changed.
+**
+** INPUTS:
+** command: An enumerated value in the set of PRTraceOption.
+** value: pointer to the data to be set. Type of the data is
+** dependent on command; for each value of command, the type
+** and meaning of dereferenced value is shown.
+**
+** PRTraceBufSize: unsigned long: the size of the trace buffer,
+** in bytes.
+**
+** PRTraceEnable: PRTraceHandle. The trace handle to be
+** enabled.
+**
+** PRTraceDisable: PRTraceHandle. The trace handle to be
+** disabled.
+**
+** PRTraceSuspend: void. value must be NULL. All tracing is
+** suspended.
+**
+** PRTraceResume: void. value must be NULL. Tracing for all
+** previously enabled, prior to a PRTraceSuspend, is resumed.
+**
+** PRTraceStopRecording: void. value must be NULL. If recording
+** (see: ** PR_RecordTraceEntries()) is being done,
+** PRTraceStopRecording causes PR_RecordTraceEntries() to return
+** to its caller. If recording is not being done, this function
+** has no effect.
+**
+** PRTraceSuspendRecording: void. Must be NULL. If recording is
+** being done, PRTraceSuspendRecording causes further writes to
+** the trace file to be suspended. Data in the in-memory
+** trace buffer that would ordinarily be written to the
+** trace file will not be written. Trace entries will continue
+** to be entered in the in-memory buffer. If the Trace Facility
+** recording is already in a suspended state, the call has no
+** effect.
+**
+** PRTraceResumeRecording: void. value must be NULL. If
+** recording for the Trace Facility has been previously been
+** suspended, this causes recording to resume. Recording resumes
+** with the next in-memory buffer segment that would be written
+** if trace recording had not been suspended. If recording is
+** not currently suspended, the call has no effect.
+**
+** PRTraceLockHandles: void. value must be NULL. Locks the
+** trace handle lock. While the trace handle lock is held,
+** calls to PR_CreateTrace() will block until the lock is
+** released.
+**
+** PRTraceUnlockHandles: void. value must be NULL. Unlocks the
+** trace handle lock.
+**
+** OUTPUTS:
+** The operation of the Trace Facility may be changed.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+#define PR_SET_TRACE_OPTION(command,value)\
+ PR_SetTraceOption((command),(value))
+#else
+#define PR_SET_TRACE_OPTION(command,value)
+#endif
+
+NSPR_API(void)
+PR_SetTraceOption(
+ PRTraceOption command, /* One of the enumerated values */
+ void *value /* command value or NULL */
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetTraceOption() -- Retrieve settings from the Trace Facility
+**
+** DESCRIPTION:
+** PR_GetTraceOption() retrieves the current setting of the
+** Trace Facility control depending on command.
+**
+**
+** PRTraceBufSize: unsigned long: the size of the trace buffer,
+** in bytes.
+**
+**
+** INPUTS:
+** command: one of the enumerated values in PRTraceOptions
+** valid for PR_GetTraceOption().
+**
+** OUTPUTS:
+** dependent on command.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+#define PR_GET_TRACE_OPTION(command,value)\
+ PR_GetTraceOption((command),(value))
+#else
+#define PR_GET_TRACE_OPTION(command,value)
+#endif
+
+NSPR_API(void)
+PR_GetTraceOption(
+ PRTraceOption command, /* One of the enumerated values */
+ void *value /* command value or NULL */
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetTraceHandleFromName() -- Retrieve an existing
+** handle by name.
+**
+** DESCRIPTION:
+** PR_GetTraceHandleFromName() retreives an existing tracehandle
+** using the name specified by qName and rName.
+**
+** INPUTS:
+** qName: pointer to string. QName for this trace handle.
+**
+** rName: pointer to string. RName for this trace handle.
+**
+**
+** OUTPUTS: returned.
+**
+** RETURNS:
+** PRTraceHandle associated with qName and rName or NULL when
+** there is no match.
+**
+** RESTRICTIONS:
+**
+*/
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+#define PR_GET_TRACE_HANDLE_FROM_NAME(handle,qName,rName)\
+ (handle) = PR_GetTraceHandleFromName((qName),(rName))
+#else
+#define PR_GET_TRACE_HANDLE_FROM_NAME(handle,qName,rName)
+#endif
+
+NSPR_API(PRTraceHandle)
+PR_GetTraceHandleFromName(
+ const char *qName, /* QName search argument */
+ const char *rName /* RName search argument */
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetTraceNameFromHandle() -- Retreive trace name
+** by bandle.
+**
+** DESCRIPTION:
+** PR_GetTraceNameFromHandle() retreives the existing qName,
+** rName, and description for the referenced trace handle.
+**
+** INPUTS: handle: PRTraceHandle.
+**
+** OUTPUTS: pointers to the Trace Facility's copy of qName,
+** rName and description. ... Don't mess with these values.
+** They're mine.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+#define PR_GET_TRACE_NAME_FROM_HANDLE(handle,qName,rName,description)\
+ PR_GetTraceNameFromHandle((handle),(qName),(rName),(description))
+#else
+#define PR_GET_TRACE_NAME_FROM_HANDLE(handle,qName,rName,description)
+#endif
+
+NSPR_API(void)
+PR_GetTraceNameFromHandle(
+ PRTraceHandle handle, /* handle as search argument */
+ const char **qName, /* pointer to associated QName */
+ const char **rName, /* pointer to associated RName */
+ const char **description /* pointer to associated description */
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_FindNextTraceQname() -- Retrieive a QName handle
+** iterator.
+**
+** DESCRIPTION:
+** PR_FindNextTraceQname() retreives the first or next trace
+** QName handle, depending on the value of handle, from the trace
+** database. The PRTraceHandle returned can be used as an
+** iterator to traverse the QName handles in the Trace database.
+**
+** INPUTS:
+** handle: When NULL, PR_FindNextQname() returns the first QName
+** handle. When a handle is a valid PRTraceHandle previously
+** retreived using PR_FindNextQname() the next QName handle is
+** retreived.
+**
+** OUTPUTS: returned.
+**
+** RETURNS:
+** PRTraceHandle or NULL when there are no trace handles.
+**
+** RESTRICTIONS:
+** Iterating thru the trace handles via FindFirst/FindNext
+** should be done under protection of the trace handle lock.
+** See: PR_SetTraceOption( PRLockTraceHandles ).
+**
+*/
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+#define PR_FIND_NEXT_TRACE_QNAME(next,handle)\
+ (next) = PR_FindNextTraceQname((handle))
+#else
+#define PR_FIND_NEXT_TRACE_QNAME(next,handle)
+#endif
+
+NSPR_API(PRTraceHandle)
+PR_FindNextTraceQname(
+ PRTraceHandle handle
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_FindNextTraceRname() -- Retrieive an RName handle
+** iterator.
+**
+** DESCRIPTION:
+** PR_FindNextTraceRname() retreives the first or next trace
+** RName handle, depending on the value of handle, from the trace
+** database. The PRTraceHandle returned can be used as an
+** iterator to traverse the RName handles in the Trace database.
+**
+** INPUTS:
+** rhandle: When NULL, PR_FindNextRname() returns the first
+** RName handle. When a handle is a valid PRTraceHandle
+** previously retreived using PR_FindNextRname() the next RName
+** handle is retreived.
+** qhandle: A valid PRTraceHandle retruned from a previous call
+** to PR_FIND_NEXT_TRACE_QNAME().
+**
+** OUTPUTS: returned.
+**
+** RETURNS:
+** PRTraceHandle or NULL when there are no trace handles.
+**
+** RESTRICTIONS:
+** Iterating thru the trace handles via FindNext should be done
+** under protection of the trace handle lock. See: (
+** PR_SetTraceOption( PRLockTraceHandles ).
+**
+*/
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+#define PR_FIND_NEXT_TRACE_RNAME(next,rhandle,qhandle)\
+ (next) = PR_FindNextTraceRname((rhandle),(qhandle))
+#else
+#define PR_FIND_NEXT_TRACE_RNAME(next,rhandle,qhandle)
+#endif
+
+NSPR_API(PRTraceHandle)
+PR_FindNextTraceRname(
+ PRTraceHandle rhandle,
+ PRTraceHandle qhandle
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_RecordTraceEntries() -- Write trace entries to external media
+**
+** DESCRIPTION:
+** PR_RecordTraceEntries() causes entries in the in-memory trace
+** buffer to be written to external media.
+**
+** When PR_RecordTraceEntries() is called from an application
+** thread, the function appears to block until another thread
+** calls PR_SetTraceOption() with the PRTraceStopRecording
+** option. This suggests that PR_RecordTraceEntries() should be
+** called from a user supplied thread whose only job is to
+** record trace entries.
+**
+** The environment variable NSPR_TRACE_LOG controls the operation
+** of this function. When NSPR_TRACE_LOG is not defined in the
+** environment, no recording of trace entries occurs. When
+** NSPR_TRACE_LOG is defined, the value of its definition must be
+** the filename of the file to receive the trace entry buffer.
+**
+** PR_RecordTraceEntries() attempts to record the in-memory
+** buffer to a file, subject to the setting of the environment
+** variable NSPR_TRACE_LOG. It is possible because of system
+** load, the thread priority of the recording thread, number of
+** active trace records being written over time, and other
+** variables that some trace records can be lost. ... In other
+** words: don't bet the farm on getting everything.
+**
+** INPUTS: none
+**
+** OUTPUTS: none
+**
+** RETURNS: PR_STATUS
+** PR_SUCCESS no errors were found.
+** PR_FAILURE errors were found.
+**
+** RESTRICTIONS:
+** Only one thread can call PR_RecordTraceEntries() within a
+** process.
+**
+** On error, PR_RecordTraceEntries() may return prematurely.
+**
+*/
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+#define PR_RECORD_TRACE_ENTRIES()\
+ PR_RecordTraceEntries()
+#else
+#define PR_RECORD_TRACE_ENTRIES()
+#endif
+
+NSPR_API(void)
+PR_RecordTraceEntries(
+ void
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetTraceEntries() -- Retreive trace entries from
+** the Trace Facility
+**
+** DESCRIPTION:
+** PR_GetTraceEntries() retreives trace entries from the Trace
+** Facility. Up to count trace entries are copied from the Trace
+** Facility into buffer. Only those trace entries that have not
+** been copied via a previous call to PR_GetTraceEntries() are
+** copied. The actual number copied is placed in the PRInt32
+** variable pointed to by found.
+**
+** If more than count trace entries have entered the Trace
+** Facility since the last call to PR_GetTraceEntries()
+** a lost data condition is returned. In this case, the most
+** recent count trace entries are copied into buffer and found is
+** set to count.
+**
+** INPUTS:
+** count. The number of trace entries to be copied into buffer.
+**
+**
+** OUTPUTS:
+** buffer. An array of PRTraceEntries. The buffer is supplied
+** by the caller.
+**
+** found: 32bit signed integer. The number of PRTraceEntries
+** actually copied. found is always less than or equal to count.
+**
+** RETURNS:
+** zero when there is no lost data.
+** non-zero when some PRTraceEntries have been lost.
+**
+** RESTRICTIONS:
+** This is a real performance pig. The copy out operation is bad
+** enough, but depending on then frequency of calls to the
+** function, serious performance impact to the operating
+** application may be realized. ... YMMV.
+**
+*/
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+#define PR_GET_TRACE_ENTRIES(buffer,count,found)\
+ PR_GetTraceEntries((buffer),(count),(found))
+#else
+#define PR_GET_TRACE_ENTRIES(buffer,count,found)
+#endif
+
+NSPR_API(PRIntn)
+PR_GetTraceEntries(
+ PRTraceEntry *buffer, /* where to write output */
+ PRInt32 count, /* number to get */
+ PRInt32 *found /* number you got */
+);
+
+PR_END_EXTERN_C
+
+#endif /* prtrace_h___ */
+
diff --git a/nsprpub/pr/include/prtypes.h b/nsprpub/pr/include/prtypes.h
new file mode 100644
index 0000000000..7400367f89
--- /dev/null
+++ b/nsprpub/pr/include/prtypes.h
@@ -0,0 +1,561 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: prtypes.h
+** Description: Definitions of NSPR's basic types
+**
+** Prototypes and macros used to make up for deficiencies that we have found
+** in ANSI environments.
+**
+** Since we do not wrap <stdlib.h> and all the other standard headers, authors
+** of portable code will not know in general that they need these definitions.
+** Instead of requiring these authors to find the dependent uses in their code
+** and take the following steps only in those C files, we take steps once here
+** for all C files.
+**/
+
+#ifndef prtypes_h___
+#define prtypes_h___
+
+#ifdef MDCPUCFG
+#include MDCPUCFG
+#else
+#include "prcpucfg.h"
+#endif
+
+#include <stddef.h>
+
+/***********************************************************************
+** MACROS: PR_EXTERN
+** PR_IMPLEMENT
+** DESCRIPTION:
+** These are only for externally visible routines and globals. For
+** internal routines, just use "extern" for type checking and that
+** will not export internal cross-file or forward-declared symbols.
+** Define a macro for declaring procedures return types. We use this to
+** deal with windoze specific type hackery for DLL definitions. Use
+** PR_EXTERN when the prototype for the method is declared. Use
+** PR_IMPLEMENT for the implementation of the method.
+**
+** Example:
+** in dowhim.h
+** PR_EXTERN( void ) DoWhatIMean( void );
+** in dowhim.c
+** PR_IMPLEMENT( void ) DoWhatIMean( void ) { return; }
+**
+**
+***********************************************************************/
+#if defined(WIN32)
+
+#define PR_EXPORT(__type) extern __declspec(dllexport) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT(__type) __declspec(dllimport) __type
+#define PR_IMPORT_DATA(__type) __declspec(dllimport) __type
+
+#define PR_EXTERN(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(XP_OS2) && defined(__declspec)
+
+#define PR_EXPORT(__type) extern __declspec(dllexport) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT(__type) extern __declspec(dllimport) __type
+#define PR_IMPORT_DATA(__type) extern __declspec(dllimport) __type
+
+#define PR_EXTERN(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#else /* Unix */
+
+/* GCC 3.3 and later support the visibility attribute. */
+#if (__GNUC__ >= 4) || \
+ (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
+#define PR_VISIBILITY_DEFAULT __attribute__((visibility("default")))
+#else
+#define PR_VISIBILITY_DEFAULT
+#endif
+
+#define PR_EXPORT(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_EXPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPORT(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
+
+#define PR_EXTERN(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPLEMENT(__type) PR_VISIBILITY_DEFAULT __type
+#define PR_EXTERN_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPLEMENT_DATA(__type) PR_VISIBILITY_DEFAULT __type
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#endif
+
+#if defined(_NSPR_BUILD_)
+#define NSPR_API(__type) PR_EXPORT(__type)
+#define NSPR_DATA_API(__type) PR_EXPORT_DATA(__type)
+#else
+#define NSPR_API(__type) PR_IMPORT(__type)
+#define NSPR_DATA_API(__type) PR_IMPORT_DATA(__type)
+#endif
+
+/***********************************************************************
+** MACROS: PR_BEGIN_MACRO
+** PR_END_MACRO
+** DESCRIPTION:
+** Macro body brackets so that macros with compound statement definitions
+** behave syntactically more like functions when called.
+***********************************************************************/
+#define PR_BEGIN_MACRO do {
+#define PR_END_MACRO } while (0)
+
+/***********************************************************************
+** MACROS: PR_BEGIN_EXTERN_C
+** PR_END_EXTERN_C
+** DESCRIPTION:
+** Macro shorthands for conditional C++ extern block delimiters.
+***********************************************************************/
+#ifdef __cplusplus
+#define PR_BEGIN_EXTERN_C extern "C" {
+#define PR_END_EXTERN_C }
+#else
+#define PR_BEGIN_EXTERN_C
+#define PR_END_EXTERN_C
+#endif
+
+/***********************************************************************
+** MACROS: PR_BIT
+** PR_BITMASK
+** DESCRIPTION:
+** Bit masking macros. XXX n must be <= 31 to be portable
+***********************************************************************/
+#define PR_BIT(n) ((PRUint32)1 << (n))
+#define PR_BITMASK(n) (PR_BIT(n) - 1)
+
+/***********************************************************************
+** MACROS: PR_ROUNDUP
+** PR_MIN
+** PR_MAX
+** PR_ABS
+** DESCRIPTION:
+** Commonly used macros for operations on compatible types.
+***********************************************************************/
+#define PR_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y))
+#define PR_MIN(x,y) ((x)<(y)?(x):(y))
+#define PR_MAX(x,y) ((x)>(y)?(x):(y))
+#define PR_ABS(x) ((x)<0?-(x):(x))
+
+/***********************************************************************
+** MACROS: PR_ARRAY_SIZE
+** DESCRIPTION:
+** The number of elements in an array.
+***********************************************************************/
+#define PR_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Starting in NSPR 4.9.5, NSPR's exact-width integer types should match
+** the exact-width integer types defined in <stdint.h>. This allows sloppy
+** code to use PRInt{N} and int{N}_t interchangeably.
+**
+** The 8-bit and 16-bit integer types can only be defined using char and
+** short. All platforms define the 32-bit integer types using int. So only
+** the 64-bit integer types could be defined differently.
+**
+** NSPR's original strategy was to use the "shortest" 64-bit integer type:
+** if long is 64-bit, then prefer it over long long. This strategy is also
+** used by Linux/glibc, FreeBSD, and NetBSD.
+**
+** Other platforms use a different strategy: simply define the 64-bit
+** integer types using long long. We define the PR_ALTERNATE_INT64_TYPEDEF
+** macro on these platforms. Note that PR_ALTERNATE_INT64_TYPEDEF is for
+** internal use by NSPR headers only. Do not define or test this macro in
+** your code.
+**
+** NOTE: NSPR can't use <stdint.h> because C99 requires C++ code to define
+** __STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS to make all the macros
+** defined in <stdint.h> available. This strange requirement is gone in
+** C11. When most platforms ignore this C99 requirement, NSPR will be able
+** to use <stdint.h>. A patch to do that is in NSPR bug 634793.
+*/
+
+#if defined(__APPLE__) || defined(__OpenBSD__)
+#define PR_ALTERNATE_INT64_TYPEDEF
+#endif
+
+/************************************************************************
+** TYPES: PRUint8
+** PRInt8
+** DESCRIPTION:
+** The int8 types are known to be 8 bits each. There is no type that
+** is equivalent to a plain "char".
+************************************************************************/
+#if PR_BYTES_PER_BYTE == 1
+typedef unsigned char PRUint8;
+/*
+** There are two scenarios that require us to define PRInt8 as type 'char'.
+** (1)
+** Some cfront-based C++ compilers do not like 'signed char' and
+** issue the warning message:
+** warning: "signed" not implemented (ignored)
+** For these compilers, we have to define PRInt8 as plain 'char'.
+** Make sure that plain 'char' is indeed signed under these compilers.
+** (2)
+** Mozilla C++ code expects the PRInt{N} and int{N}_t types to match (see bug
+** 634793). If a platform defines int8_t as 'char', but NSPR defines it as
+** 'signed char', it results in a type mismatch.
+** On such platforms we define PRInt8 as 'char' to avoid the mismatch.
+*/
+#if (defined(HPUX) && defined(__cplusplus) /* reason 1*/ \
+ && !defined(__GNUC__) && __cplusplus < 199707L) \
+ || (defined(SCO) && defined(__cplusplus) /* reason 1 */ \
+ && !defined(__GNUC__) && __cplusplus == 1L) \
+ || (defined(__sun) && defined(__cplusplus)) /* reason 2 */
+typedef char PRInt8;
+#else
+typedef signed char PRInt8;
+#endif
+#else
+#error No suitable type for PRInt8/PRUint8
+#endif
+
+/************************************************************************
+ * MACROS: PR_INT8_MAX
+ * PR_INT8_MIN
+ * PR_UINT8_MAX
+ * DESCRIPTION:
+ * The maximum and minimum values of a PRInt8 or PRUint8.
+************************************************************************/
+
+#define PR_INT8_MAX 127
+#define PR_INT8_MIN (-128)
+#define PR_UINT8_MAX 255U
+
+/************************************************************************
+** TYPES: PRUint16
+** PRInt16
+** DESCRIPTION:
+** The int16 types are known to be 16 bits each.
+************************************************************************/
+#if PR_BYTES_PER_SHORT == 2
+typedef unsigned short PRUint16;
+typedef short PRInt16;
+#else
+#error No suitable type for PRInt16/PRUint16
+#endif
+
+/************************************************************************
+ * MACROS: PR_INT16_MAX
+ * PR_INT16_MIN
+ * PR_UINT16_MAX
+ * DESCRIPTION:
+ * The maximum and minimum values of a PRInt16 or PRUint16.
+************************************************************************/
+
+#define PR_INT16_MAX 32767
+#define PR_INT16_MIN (-32768)
+#define PR_UINT16_MAX 65535U
+
+/************************************************************************
+** TYPES: PRUint32
+** PRInt32
+** DESCRIPTION:
+** The int32 types are known to be 32 bits each.
+************************************************************************/
+#if PR_BYTES_PER_INT == 4
+typedef unsigned int PRUint32;
+typedef int PRInt32;
+#define PR_INT32(x) x
+#define PR_UINT32(x) x ## U
+#elif PR_BYTES_PER_LONG == 4
+typedef unsigned long PRUint32;
+typedef long PRInt32;
+#define PR_INT32(x) x ## L
+#define PR_UINT32(x) x ## UL
+#else
+#error No suitable type for PRInt32/PRUint32
+#endif
+
+/************************************************************************
+ * MACROS: PR_INT32_MAX
+ * PR_INT32_MIN
+ * PR_UINT32_MAX
+ * DESCRIPTION:
+ * The maximum and minimum values of a PRInt32 or PRUint32.
+************************************************************************/
+
+#define PR_INT32_MAX PR_INT32(2147483647)
+#define PR_INT32_MIN (-PR_INT32_MAX - 1)
+#define PR_UINT32_MAX PR_UINT32(4294967295)
+
+/************************************************************************
+** TYPES: PRUint64
+** PRInt64
+** DESCRIPTION:
+** The int64 types are known to be 64 bits each. Care must be used when
+** declaring variables of type PRUint64 or PRInt64. Different hardware
+** architectures and even different compilers have varying support for
+** 64 bit values. The only guaranteed portability requires the use of
+** the LL_ macros (see prlong.h).
+**
+** MACROS: PR_INT64
+** PR_UINT64
+** DESCRIPTION:
+** The PR_INT64 and PR_UINT64 macros provide a portable way for
+** specifying 64-bit integer constants. They can only be used if
+** PRInt64 and PRUint64 are defined as compiler-supported 64-bit
+** integer types (i.e., if HAVE_LONG_LONG is defined, which is true
+** for all the supported compilers topday). If PRInt64 and PRUint64
+** are defined as structs, the LL_INIT macro defined in prlong.h has
+** to be used.
+**
+** MACROS: PR_INT64_MAX
+** PR_INT64_MIN
+** PR_UINT64_MAX
+** DESCRIPTION:
+** The maximum and minimum values of a PRInt64 or PRUint64.
+************************************************************************/
+#ifdef HAVE_LONG_LONG
+/* Keep this in sync with prlong.h. */
+#if PR_BYTES_PER_LONG == 8 && !defined(PR_ALTERNATE_INT64_TYPEDEF)
+typedef long PRInt64;
+typedef unsigned long PRUint64;
+#define PR_INT64(x) x ## L
+#define PR_UINT64(x) x ## UL
+#elif defined(WIN32) && !defined(__GNUC__)
+typedef __int64 PRInt64;
+typedef unsigned __int64 PRUint64;
+#define PR_INT64(x) x ## i64
+#define PR_UINT64(x) x ## ui64
+#else
+typedef long long PRInt64;
+typedef unsigned long long PRUint64;
+#define PR_INT64(x) x ## LL
+#define PR_UINT64(x) x ## ULL
+#endif /* PR_BYTES_PER_LONG == 8 */
+
+#define PR_INT64_MAX PR_INT64(0x7fffffffffffffff)
+#define PR_INT64_MIN (-PR_INT64_MAX - 1)
+#define PR_UINT64_MAX PR_UINT64(-1)
+#else /* !HAVE_LONG_LONG */
+typedef struct {
+#ifdef IS_LITTLE_ENDIAN
+ PRUint32 lo, hi;
+#else
+ PRUint32 hi, lo;
+#endif
+} PRInt64;
+typedef PRInt64 PRUint64;
+
+#define PR_INT64_MAX (PRInt64){0x7fffffff, 0xffffffff}
+#define PR_INT64_MIN (PRInt64){0xffffffff, 0xffffffff}
+#define PR_UINT64_MAX (PRUint64){0xffffffff, 0xffffffff}
+
+#endif /* !HAVE_LONG_LONG */
+
+/************************************************************************
+** TYPES: PRUintn
+** PRIntn
+** DESCRIPTION:
+** The PRIntn types are most appropriate for automatic variables. They are
+** guaranteed to be at least 16 bits, though various architectures may
+** define them to be wider (e.g., 32 or even 64 bits). These types are
+** never valid for fields of a structure.
+************************************************************************/
+#if PR_BYTES_PER_INT >= 2
+typedef int PRIntn;
+typedef unsigned int PRUintn;
+#else
+#error 'sizeof(int)' not sufficient for platform use
+#endif
+
+/************************************************************************
+** TYPES: PRFloat64
+** DESCRIPTION:
+** NSPR's floating point type is always 64 bits.
+************************************************************************/
+typedef double PRFloat64;
+
+/************************************************************************
+** TYPES: PRSize
+** DESCRIPTION:
+** A type for representing the size of objects.
+************************************************************************/
+typedef size_t PRSize;
+
+
+/************************************************************************
+** TYPES: PROffset32, PROffset64
+** DESCRIPTION:
+** A type for representing byte offsets from some location.
+************************************************************************/
+typedef PRInt32 PROffset32;
+typedef PRInt64 PROffset64;
+
+/************************************************************************
+** TYPES: PRPtrDiff
+** DESCRIPTION:
+** A type for pointer difference. Variables of this type are suitable
+** for storing a pointer or pointer subtraction.
+************************************************************************/
+typedef ptrdiff_t PRPtrdiff;
+
+/************************************************************************
+** TYPES: PRUptrdiff
+** DESCRIPTION:
+** A type for pointer difference. Variables of this type are suitable
+** for storing a pointer or pointer sutraction.
+************************************************************************/
+#ifdef _WIN64
+typedef PRUint64 PRUptrdiff;
+#else
+typedef unsigned long PRUptrdiff;
+#endif
+
+/************************************************************************
+** TYPES: PRBool
+** DESCRIPTION:
+** Use PRBool for variables and parameter types. Use PR_FALSE and PR_TRUE
+** for clarity of target type in assignments and actual arguments. Use
+** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans
+** just as you would C int-valued conditions.
+************************************************************************/
+typedef PRIntn PRBool;
+#define PR_TRUE 1
+#define PR_FALSE 0
+
+/************************************************************************
+** TYPES: PRPackedBool
+** DESCRIPTION:
+** Use PRPackedBool within structs where bitfields are not desirable
+** but minimum and consistant overhead matters.
+************************************************************************/
+typedef PRUint8 PRPackedBool;
+
+/*
+** Status code used by some routines that have a single point of failure or
+** special status return.
+*/
+typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus;
+
+#ifndef __PRUNICHAR__
+#define __PRUNICHAR__
+#ifdef WIN32
+typedef wchar_t PRUnichar;
+#else
+typedef PRUint16 PRUnichar;
+#endif
+#endif
+
+/*
+** WARNING: The undocumented data types PRWord and PRUword are
+** only used in the garbage collection and arena code. Do not
+** use PRWord and PRUword in new code.
+**
+** A PRWord is an integer that is the same size as a void*.
+** It implements the notion of a "word" in the Java Virtual
+** Machine. (See Sec. 3.4 "Words", The Java Virtual Machine
+** Specification, Addison-Wesley, September 1996.
+** http://java.sun.com/docs/books/vmspec/index.html.)
+*/
+#ifdef _WIN64
+typedef PRInt64 PRWord;
+typedef PRUint64 PRUword;
+#else
+typedef long PRWord;
+typedef unsigned long PRUword;
+#endif
+
+/*
+ * PR_PRETEND_NORETURN, specified at the end of a function declaration,
+ * indicates that for the purposes of static analysis, this function does not
+ * return. (The function definition does not need to be annotated.)
+ *
+ * void PR_Assert(const char *s, const char *file, PRIntn ln)
+ * PR_PRETEND_NORETURN;
+ *
+ * Some static analyzers, like scan-build from clang, can use this information
+ * to eliminate false positives. From the upstream documentation of
+ * scan-build:
+ * This attribute is useful for annotating assertion handlers that actually
+ * can return, but for the purpose of using the analyzer we want to pretend
+ * that such functions do not return.
+ */
+#ifdef __clang_analyzer__
+#if __has_extension(attribute_analyzer_noreturn)
+#define PR_PRETEND_NORETURN __attribute__((analyzer_noreturn))
+#endif
+#endif
+
+#ifndef PR_PRETEND_NORETURN
+#define PR_PRETEND_NORETURN /* no support */
+#endif
+
+#if defined(NO_NSPR_10_SUPPORT)
+#else
+/********* ???????????????? FIX ME ??????????????????????????? *****/
+/********************** Some old definitions until pr=>ds transition is done ***/
+/********************** Also, we are still using NSPR 1.0. GC ******************/
+/*
+** Fundamental NSPR macros, used nearly everywhere.
+*/
+
+#define PR_PUBLIC_API PR_IMPLEMENT
+
+/*
+** Macro body brackets so that macros with compound statement definitions
+** behave syntactically more like functions when called.
+*/
+#define NSPR_BEGIN_MACRO do {
+#define NSPR_END_MACRO } while (0)
+
+/*
+** Macro shorthands for conditional C++ extern block delimiters.
+*/
+#ifdef NSPR_BEGIN_EXTERN_C
+#undef NSPR_BEGIN_EXTERN_C
+#endif
+#ifdef NSPR_END_EXTERN_C
+#undef NSPR_END_EXTERN_C
+#endif
+
+#ifdef __cplusplus
+#define NSPR_BEGIN_EXTERN_C extern "C" {
+#define NSPR_END_EXTERN_C }
+#else
+#define NSPR_BEGIN_EXTERN_C
+#define NSPR_END_EXTERN_C
+#endif
+
+#include "obsolete/protypes.h"
+
+/********* ????????????? End Fix me ?????????????????????????????? *****/
+#endif /* NO_NSPR_10_SUPPORT */
+
+/*
+** Compile-time assert. "condition" must be a constant expression.
+** The macro can be used only in places where an "extern" declaration is
+** allowed.
+*/
+#define PR_STATIC_ASSERT(condition) \
+ extern void pr_static_assert(int arg[(condition) ? 1 : -1])
+
+PR_END_EXTERN_C
+
+#endif /* prtypes_h___ */
+
diff --git a/nsprpub/pr/include/prvrsion.h b/nsprpub/pr/include/prvrsion.h
new file mode 100755
index 0000000000..99ed1db6c2
--- /dev/null
+++ b/nsprpub/pr/include/prvrsion.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+/* author: jstewart */
+
+#if defined(_PRVERSION_H)
+#else
+#define _PRVERSION_H
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/* All components participating in the PR version protocol must expose
+ * a structure and a function. The structure is defined below and named
+ * according to the naming conventions outlined further below. The function
+ * is called libVersionPoint and returns a pointer to this structure.
+ */
+
+/* on NT, always pack the structure the same. */
+#ifdef _WIN32
+#pragma pack(push, 8)
+#endif
+
+typedef struct {
+ /*
+ * The first field defines which version of this structure is in use.
+ * At this time, only version 2 is specified. If this value is not
+ * 2, you must read no further into the structure.
+ */
+ PRInt32 version;
+
+ /* for Version 2, this is the body format. */
+ PRInt64 buildTime; /* 64 bits - usecs since midnight, 1/1/1970 */
+ char * buildTimeString;/* a human readable version of the time */
+
+ PRUint8 vMajor; /* Major version of this component */
+ PRUint8 vMinor; /* Minor version of this component */
+ PRUint8 vPatch; /* Patch level of this component */
+
+ PRBool beta; /* true if this is a beta component */
+ PRBool debug; /* true if this is a debug component */
+ PRBool special; /* true if this component is a special build */
+
+ char * filename; /* The original filename */
+ char * description; /* description of this component */
+ char * security; /* level of security in this component */
+ char * copyright; /* The copyright for this file */
+ char * comment; /* free form field for misc usage */
+ char * specialString; /* the special variant for this build */
+} PRVersionDescription;
+
+/* on NT, restore the previous packing */
+#ifdef _WIN32
+#pragma pack(pop)
+#endif
+
+/*
+ * All components must define an entrypoint named libVersionPoint which
+ * is of type versionEntryPointType.
+ *
+ * For example, for a library named libfoo, we would have:
+ *
+ * PRVersionDescription prVersionDescription_libfoo =
+ * {
+ * ...
+ * };
+ *
+ * PR_IMPLEMENT(const PRVersionDescription*) libVersionPoint(void)
+ * {
+ * return &prVersionDescription_libfoo;
+ * }
+ */
+typedef const PRVersionDescription *(*versionEntryPointType)(void);
+
+/*
+ * Where you declare your libVersionPoint, do it like this:
+ * PR_IMPLEMENT(const PRVersionDescription *) libVersionPoint(void) {
+ * fill it in...
+ * }
+ */
+
+/*
+ * NAMING CONVENTION FOR struct
+ *
+ * all components should also expose a static PRVersionDescription
+ * The name of the struct should be calculated as follows:
+ * Take the value of filename. (If filename is not specified, calculate
+ * a short, unique string.) Convert all non-alphanumeric characters
+ * to '_'. To this, prepend "PRVersionDescription_". Thus for libfoo.so,
+ * the symbol name is "PRVersionDescription_libfoo_so".
+ * so the file should have
+ * PRVersionDescription PRVersionDescription_libfoo_so { fill it in };
+ * on NT, this file should be declspec export.
+ */
+
+PR_END_EXTERN_C
+
+#endif /* defined(_PRVERSION_H) */
+
+/* prvrsion.h */
+
diff --git a/nsprpub/pr/include/prwin16.h b/nsprpub/pr/include/prwin16.h
new file mode 100644
index 0000000000..b545f04af0
--- /dev/null
+++ b/nsprpub/pr/include/prwin16.h
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef prwin16_h___
+#define prwin16_h___
+
+/*
+** Condition use of this header on platform.
+*/
+#if (defined(XP_PC) && !defined(_WIN32) && !defined(XP_OS2) && defined(MOZILLA_CLIENT)) || defined(WIN16)
+#include <stdio.h>
+
+PR_BEGIN_EXTERN_C
+/*
+** Win16 stdio special case.
+** To get stdio to work for Win16, all calls to printf() and related
+** things must be called from the environment of the .EXE; calls to
+** printf() from the .DLL send output to the bit-bucket.
+**
+** To make sure that PR_fprintf(), and related functions, work correctly,
+** the actual stream I/O to stdout, stderr, stdin must be done in the
+** .EXE. To do this, a hack is placed in _MD_Write() such that the
+** fd for stdio handles results in a call to the .EXE.
+**
+** file w16stdio.c contains the functions that get called from NSPR
+** to do the actual I/O. w16stdio.o must be statically linked with
+** any application needing stdio for Win16.
+**
+** The address of these functions must be made available to the .DLL
+** so he can call back to the .EXE. To do this, function
+** PR_MD_RegisterW16StdioCallbacks() is called from the .EXE.
+** The arguments are the functions defined in w16stdio.c
+** At runtime, MD_Write() calls the registered functions, if any
+** were registered.
+**
+** prinit.h contains a macro PR_STDIO_INIT() that calls the registration
+** function for Win16; For other platforms, the macro is a No-Op.
+**
+** Note that stdio is not operational at all on Win16 GUI applications.
+** This special case exists to provide stdio capability from the NSPR
+** .DLL for command line applications only. NSPR's test cases are
+** almost exclusively command line applications.
+**
+** See also: w16io.c, w16stdio.c
+*/
+typedef PRInt32 (PR_CALLBACK *PRStdinRead)( void *buf, PRInt32 amount);
+typedef PRInt32 (PR_CALLBACK *PRStdoutWrite)( void *buf, PRInt32 amount);
+typedef PRInt32 (PR_CALLBACK *PRStderrWrite)( void *buf, PRInt32 amount);
+
+NSPR_API(PRStatus)
+PR_MD_RegisterW16StdioCallbacks(
+ PRStdinRead inReadf, /* i: function pointer for stdin read */
+ PRStdoutWrite outWritef, /* i: function pointer for stdout write */
+ PRStderrWrite errWritef /* i: function pointer for stderr write */
+);
+
+NSPR_API(PRInt32)
+_PL_W16StdioWrite( void *buf, PRInt32 amount );
+
+NSPR_API(PRInt32)
+_PL_W16StdioRead( void *buf, PRInt32 amount );
+
+#define PR_STDIO_INIT() PR_MD_RegisterW16StdioCallbacks( \
+ _PL_W16StdioRead, _PL_W16StdioWrite, _PL_W16StdioWrite ); \
+ PR_INIT_CALLBACKS();
+
+/*
+** Win16 hackery.
+**
+*/
+struct PRMethodCallbackStr {
+ int (PR_CALLBACK *auxOutput)(const char *outputString);
+ size_t (PR_CALLBACK *strftime)(char *s, size_t len, const char *fmt, const struct tm *p);
+ void * (PR_CALLBACK *malloc)( size_t size );
+ void * (PR_CALLBACK *calloc)(size_t n, size_t size );
+ void * (PR_CALLBACK *realloc)( void* old_blk, size_t size );
+ void (PR_CALLBACK *free)( void *ptr );
+ void * (PR_CALLBACK *getenv)( const char *name);
+ int (PR_CALLBACK *putenv)( const char *assoc);
+ /* void * (PR_CALLBACK *perror)( const char *prefix ); */
+};
+
+NSPR_API(void) PR_MDRegisterCallbacks(struct PRMethodCallbackStr *);
+
+int PR_CALLBACK _PL_W16CallBackPuts( const char *outputString );
+size_t PR_CALLBACK _PL_W16CallBackStrftime(
+ char *s,
+ size_t len,
+ const char *fmt,
+ const struct tm *p );
+void * PR_CALLBACK _PL_W16CallBackMalloc( size_t size );
+void * PR_CALLBACK _PL_W16CallBackCalloc( size_t n, size_t size );
+void * PR_CALLBACK _PL_W16CallBackRealloc(
+ void *old_blk,
+ size_t size );
+void PR_CALLBACK _PL_W16CallBackFree( void *ptr );
+void * PR_CALLBACK _PL_W16CallBackGetenv( const char *name );
+int PR_CALLBACK _PL_W16CallBackPutenv( const char *assoc );
+
+/*
+** Hackery!
+**
+** These functions are provided as static link points.
+** This is to satisfy the quick port of Gromit to NSPR 2.0
+** ... Don't do this! ... alas, It may never go away.
+**
+*/
+NSPR_API(int) PR_MD_printf(const char *, ...);
+NSPR_API(void) PR_MD_exit(int);
+NSPR_API(size_t) PR_MD_strftime(char *, size_t, const char *, const struct tm *);
+NSPR_API(int) PR_MD_sscanf(const char *, const char *, ...);
+NSPR_API(void*) PR_MD_malloc( size_t size );
+NSPR_API(void*) PR_MD_calloc( size_t n, size_t size );
+NSPR_API(void*) PR_MD_realloc( void* old_blk, size_t size );
+NSPR_API(void) PR_MD_free( void *ptr );
+NSPR_API(char*) PR_MD_getenv( const char *name );
+NSPR_API(int) PR_MD_putenv( const char *assoc );
+NSPR_API(int) PR_MD_fprintf(FILE *fPtr, const char *fmt, ...);
+
+#define PR_INIT_CALLBACKS() \
+ { \
+ static struct PRMethodCallbackStr cbf = { \
+ _PL_W16CallBackPuts, \
+ _PL_W16CallBackStrftime, \
+ _PL_W16CallBackMalloc, \
+ _PL_W16CallBackCalloc, \
+ _PL_W16CallBackRealloc, \
+ _PL_W16CallBackFree, \
+ _PL_W16CallBackGetenv, \
+ _PL_W16CallBackPutenv, \
+ }; \
+ PR_MDRegisterCallbacks( &cbf ); \
+ }
+
+
+/*
+** Get the exception context for Win16 MFC applications threads
+*/
+NSPR_API(void *) PR_W16GetExceptionContext(void);
+/*
+** Set the exception context for Win16 MFC applications threads
+*/
+NSPR_API(void) PR_W16SetExceptionContext(void *context);
+
+PR_END_EXTERN_C
+#else
+/*
+** For platforms other than Win16, define
+** PR_STDIO_INIT() as a No-Op.
+*/
+#define PR_STDIO_INIT()
+#endif /* WIN16 || MOZILLA_CLIENT */
+
+#endif /* prwin16_h___ */
+
+
+
+
+
+
+
+
diff --git a/nsprpub/pr/src/Makefile.in b/nsprpub/pr/src/Makefile.in
new file mode 100644
index 0000000000..d7c633daf5
--- /dev/null
+++ b/nsprpub/pr/src/Makefile.in
@@ -0,0 +1,355 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+DIRS = io linking malloc md memory misc threads
+
+ifeq ($(USE_PTHREADS), 1)
+ DIRS += pthreads
+endif
+
+ifeq ($(USE_BTHREADS), 1)
+ DIRS += bthreads
+endif
+
+ifeq ($(USE_CPLUS), 1)
+ DIRS += cplus
+endif
+
+#
+# Define platform-dependent OS_LIBS
+#
+
+ifeq ($(OS_ARCH),SunOS)
+MAPFILE = $(OBJDIR)/nsprmap.sun
+GARBAGE += $(MAPFILE)
+ifdef NS_USE_GCC
+ifdef GCC_USE_GNU_LD
+MKSHLIB += -Wl,--version-script,$(MAPFILE)
+else
+MKSHLIB += -Wl,-M,$(MAPFILE)
+endif
+else
+MKSHLIB += -M $(MAPFILE)
+endif
+#
+# In Solaris 2.6 or earlier, -lrt is called -lposix4.
+#
+LIBRT_TEST=$(firstword $(sort 5.7 $(OS_RELEASE)))
+ifeq (5.7, $(LIBRT_TEST))
+LIBRT=-lrt
+else
+LIBRT=-lposix4
+endif
+
+ifdef USE_PTHREADS
+OS_LIBS = -lpthread ${LIBRT} -lsocket -lnsl -ldl -lc
+else
+OS_LIBS = -lsocket -lnsl -ldl -lc
+endif # USE_PTHREADS
+ifeq ($(CPU_ARCH),sparc)
+ifndef USE_64
+DSO_LDOPTS += -Wl,-f,\$$ORIGIN/cpu/\$$ISALIST/lib$(ULTRASPARC_LIBRARY)$(LIBRARY_VERSION).so
+endif
+endif # sparc
+endif # SunOS
+
+ifeq ($(OS_ARCH),AIX)
+DSO_LDOPTS += -binitfini::_PR_Fini
+OS_LIBS = -lodm -lcfg
+ifeq ($(CLASSIC_NSPR),1)
+ifeq ($(OS_RELEASE),4.1)
+OS_LIBS += -lsvld -lc
+else
+OS_LIBS += -ldl -lc
+endif
+else
+ifeq ($(OS_RELEASE),4.1)
+OS_LIBS += -lpthreads -lsvld -lC_r -lC -lc_r -lm /usr/lib/libc.a
+else
+OS_LIBS += -lpthreads -ldl -lC_r -lC -lc_r -lm /usr/lib/libc.a
+endif
+endif
+endif
+
+# On AIX, we override malloc in non-pthread versions. On AIX 4.2 or
+# above, this requires that we use the rtl-enabled version of libc.a.
+ifeq ($(OS_ARCH),AIX)
+ifneq (,$(filter-out 3.2 4.1,$(OS_RELEASE)))
+ifneq ($(USE_PTHREADS),1)
+BUILD_AIX_RTL_LIBC = 1
+AIX_RTL_LIBC = $(OBJDIR)/libc.a
+endif
+endif
+endif
+
+ifeq ($(OS_ARCH),OS2)
+MAPFILE = $(OBJDIR)/$(LIBRARY_NAME)$(LIBRARY_VERSION).def
+ADD_TO_DEF_FILE = cat $(srcdir)/os2extra.def >> $(MAPFILE)
+GARBAGE += $(MAPFILE)
+MKSHLIB += $(MAPFILE)
+endif
+
+# Linux, GNU/Hurd, and GNU/kFreeBSD systems
+ifneq (,$(filter Linux GNU%,$(OS_ARCH)))
+ifeq ($(USE_PTHREADS), 1)
+ifeq ($(OS_TARGET),Android)
+# Android has no libpthread.so in NDK
+OS_LIBS = -ldl
+else
+OS_LIBS = -lpthread -ldl
+endif
+else
+OS_LIBS = -ldl
+endif
+ifneq ($(OS_TARGET),Android)
+# Android has no librt - realtime functions are in libc
+OS_LIBS += -lrt
+endif
+endif
+
+ifeq ($(OS_ARCH),HP-UX)
+ifeq ($(USE_PTHREADS), 1)
+OS_LIBS = -lpthread -lrt
+endif
+ifeq ($(PTHREADS_USER), 1)
+OS_LIBS = -lpthread
+endif
+ifeq ($(basename $(OS_RELEASE)),A.09)
+OS_LIBS += -ldld -L/lib/pa1.1 -lm
+else
+OS_LIBS += -ldld -lm -lc
+endif
+ifneq ($(OS_TEST),ia64)
+ifndef USE_64
+DSO_LDOPTS += +I PR_HPUX10xInit
+endif
+endif
+endif
+
+ifeq ($(OS_ARCH),UNIXWARE)
+OS_LIBS = -lsocket -lc
+endif
+
+ifeq ($(OS_ARCH),WINNT)
+ifdef NS_USE_GCC
+OS_LIBS = -ladvapi32 -lws2_32 -lmswsock -lwinmm
+else
+OS_LIBS = advapi32.lib ws2_32.lib mswsock.lib winmm.lib
+endif
+endif
+
+ifeq ($(OS_ARCH),WINCE)
+OS_LIBS = ws2.lib
+endif
+
+ifeq ($(OS_TARGET),Android)
+OS_LIBS += -llog
+endif
+
+EXTRA_LIBS += $(OS_LIBS)
+
+#
+# Define platform-dependent OBJS
+#
+
+OBJS = \
+ $(OBJDIR)/prvrsion.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prfdcach.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prmwait.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prmapopt.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/priometh.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/pripv6.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prlayer.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prlog.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prmmap.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prpolevt.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prprf.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prscanf.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prstdio.$(OBJ_SUFFIX) \
+ threads/$(OBJDIR)/prcmon.$(OBJ_SUFFIX) \
+ threads/$(OBJDIR)/prrwlock.$(OBJ_SUFFIX) \
+ threads/$(OBJDIR)/prtpd.$(OBJ_SUFFIX) \
+ linking/$(OBJDIR)/prlink.$(OBJ_SUFFIX) \
+ malloc/$(OBJDIR)/prmalloc.$(OBJ_SUFFIX) \
+ malloc/$(OBJDIR)/prmem.$(OBJ_SUFFIX) \
+ md/$(OBJDIR)/prosdep.$(OBJ_SUFFIX) \
+ memory/$(OBJDIR)/prshm.$(OBJ_SUFFIX) \
+ memory/$(OBJDIR)/prshma.$(OBJ_SUFFIX) \
+ memory/$(OBJDIR)/prseg.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/pralarm.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/pratom.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prcountr.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prdtoa.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prenv.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prerr.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prerror.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prerrortable.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prinit.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prinrval.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/pripc.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prlog2.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prlong.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prnetdb.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/praton.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prolock.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prrng.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prsystem.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prthinfo.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prtpool.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prtrace.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prtime.$(OBJ_SUFFIX)
+
+ifdef USE_PTHREADS
+OBJS += \
+ pthreads/$(OBJDIR)/ptsynch.$(OBJ_SUFFIX) \
+ pthreads/$(OBJDIR)/ptio.$(OBJ_SUFFIX) \
+ pthreads/$(OBJDIR)/ptthread.$(OBJ_SUFFIX) \
+ pthreads/$(OBJDIR)/ptmisc.$(OBJ_SUFFIX)
+else
+OBJS += \
+ io/$(OBJDIR)/prdir.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prfile.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prio.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prsocket.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/pripcsem.$(OBJ_SUFFIX)
+
+ifndef USE_BTHREADS
+OBJS += \
+ threads/$(OBJDIR)/prcthr.$(OBJ_SUFFIX) \
+ threads/$(OBJDIR)/prdump.$(OBJ_SUFFIX) \
+ threads/$(OBJDIR)/prmon.$(OBJ_SUFFIX) \
+ threads/$(OBJDIR)/prsem.$(OBJ_SUFFIX) \
+ threads/combined/$(OBJDIR)/prucpu.$(OBJ_SUFFIX) \
+ threads/combined/$(OBJDIR)/prucv.$(OBJ_SUFFIX) \
+ threads/combined/$(OBJDIR)/prulock.$(OBJ_SUFFIX) \
+ threads/combined/$(OBJDIR)/prustack.$(OBJ_SUFFIX) \
+ threads/combined/$(OBJDIR)/pruthr.$(OBJ_SUFFIX)
+endif
+
+endif
+
+ifeq ($(USE_CPLUS), 1)
+OBJS += \
+ cplus/$(OBJDIR)/rcbase.$(OBJ_SUFFIX) \
+ cplus/$(OBJDIR)/rccv.$(OBJ_SUFFIX) \
+ cplus/$(OBJDIR)/rcfileio.$(OBJ_SUFFIX) \
+ cplus/$(OBJDIR)/rcinrval.$(OBJ_SUFFIX) \
+ cplus/$(OBJDIR)/rcio.$(OBJ_SUFFIX) \
+ cplus/$(OBJDIR)/rclock.$(OBJ_SUFFIX) \
+ cplus/$(OBJDIR)/rcnetdb.$(OBJ_SUFFIX) \
+ cplus/$(OBJDIR)/rcnetio.$(OBJ_SUFFIX) \
+ cplus/$(OBJDIR)/rcthread.$(OBJ_SUFFIX) \
+ cplus/$(OBJDIR)/rctime.$(OBJ_SUFFIX)
+endif
+
+ifeq ($(OS_ARCH), WINNT)
+RES=$(OBJDIR)/nspr.res
+RESNAME=nspr.rc
+endif # WINNT
+
+include $(srcdir)/md/$(PR_MD_ARCH_DIR)/objs.mk
+ifdef USE_BTHREADS
+include $(srcdir)/bthreads/objs.mk
+endif
+
+LIBRARY_NAME = nspr
+LIBRARY_VERSION = $(MOD_MAJOR_VERSION)
+
+RELEASE_LIBS = $(TARGETS)
+
+include $(topsrcdir)/config/rules.mk
+
+ifeq ($(BUILD_AIX_RTL_LIBC),1)
+TARGETS += $(AIX_RTL_LIBC)
+# XXX is this a shared library?
+endif
+
+#
+# Version information generation (begin)
+#
+ECHO = echo
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+TINC = $(OBJDIR)/_pr_bld.h
+
+ifeq ($(OS_TARGET),OS2)
+PROD = nspr$(MOD_MAJOR_VERSION).$(DLL_SUFFIX)
+else
+PROD = $(notdir $(SHARED_LIBRARY))
+endif
+
+NOW = $(MOD_DEPTH)/config/$(OBJDIR)/now
+SH_DATE = $(shell date "+%Y-%m-%d %T")
+SH_NOW = $(shell $(NOW))
+
+ifeq ($(NS_USE_GCC)_$(OS_ARCH),_WINNT)
+ SUF = i64
+else
+ SUF = LL
+endif
+
+DEFINES += -D_NSPR_BUILD_
+
+GARBAGE += $(TINC)
+
+$(TINC):
+ @$(MAKE_OBJDIR)
+ @$(ECHO) '#define _BUILD_STRING "$(SH_DATE)"' > $(TINC)
+ @if test ! -z "$(SH_NOW)"; then \
+ $(ECHO) '#define _BUILD_TIME $(SH_NOW)$(SUF)' >> $(TINC); \
+ else \
+ true; \
+ fi
+ @$(ECHO) '#define _PRODUCTION "$(PROD)"' >> $(TINC)
+
+
+$(OBJDIR)/prvrsion.$(OBJ_SUFFIX): prvrsion.c $(TINC)
+ifeq ($(NS_USE_GCC)_$(OS_ARCH),_WINNT)
+ $(CC) -Fo$@ -c $(CFLAGS) -I$(OBJDIR) $<
+else
+ $(CC) -o $@ -c $(CFLAGS) -I$(OBJDIR) $<
+endif
+#
+# Version information generation (end)
+#
+
+
+# We use a 'build' target here to ensure that we build $(TARGETS) after
+# looping over $(DIRS) to create the object files in a parallel build.
+# Recipe commands are executed sequentially in a parallel build while
+# target dependencies are executed in parallel.
+export::
+ $(MAKE) build
+
+#
+# The Client build wants the shared libraries in $(dist_bindir)
+# so we also install them there.
+#
+
+build:: $(TARGETS)
+ $(INSTALL) -m 444 $(TARGETS) $(dist_libdir)
+ifdef SHARED_LIBRARY
+ifeq ($(OS_ARCH),HP-UX)
+ $(INSTALL) -m 755 $(SHARED_LIBRARY) $(dist_libdir)
+ $(INSTALL) -m 755 $(SHARED_LIBRARY) $(dist_bindir)
+else
+ $(INSTALL) -m 444 $(SHARED_LIBRARY) $(dist_bindir)
+endif
+endif
+
+ifeq ($(BUILD_AIX_RTL_LIBC),1)
+$(AIX_RTL_LIBC): /usr/ccs/lib/libc.a
+ rtl_enable -o $@ $<
+endif
diff --git a/nsprpub/pr/src/cplus/Makefile.in b/nsprpub/pr/src/cplus/Makefile.in
new file mode 100644
index 0000000000..ec08eab1fb
--- /dev/null
+++ b/nsprpub/pr/src/cplus/Makefile.in
@@ -0,0 +1,43 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+CXXSRCS = \
+ rcbase.cpp \
+ rccv.cpp \
+ rcfileio.cpp \
+ rcinrval.cpp \
+ rcio.cpp \
+ rclock.cpp \
+ rcnetdb.cpp \
+ rcnetio.cpp \
+ rcthread.cpp \
+ rctime.cpp \
+ $(NULL)
+
+OBJS = $(addprefix $(OBJDIR)/,$(CXXSRCS:.cpp=.$(OBJ_SUFFIX)))
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir)
+
+DEFINES += -D_NSPR_BUILD_
+
+include $(topsrcdir)/config/rules.mk
+
+HEADERS = $(wildcard $(srcdir)/*.h)
+
+export:: $(TARGETS)
+
diff --git a/nsprpub/pr/src/cplus/rcascii.h b/nsprpub/pr/src/cplus/rcascii.h
new file mode 100644
index 0000000000..f8b59ce0f9
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcascii.h
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Class definitions to format ASCII data.
+*/
+
+#if defined(_RCASCII_H)
+#else
+#define _RCASCII_H
+
+/*
+** RCFormatStuff
+** This class maintains no state - that is the responsibility of
+** the class' client. For each call to Sx_printf(), the StuffFunction
+** will be called for each embedded "%" in the 'fmt' string and once
+** for each interveaning literal.
+*/
+class PR_IMPLEMENT(RCFormatStuff)
+{
+public:
+ RCFormatStuff();
+ virtual ~RCFormatStuff();
+
+ /*
+ ** Process the arbitrary argument list using as indicated by
+ ** the 'fmt' string. Each segment of the processing the stuff
+ ** function is called with the relavent translation.
+ */
+ virtual PRInt32 Sx_printf(void *state, const char *fmt, ...);
+
+ /*
+ ** The 'state' argument is not processed by the runtime. It
+ ** is merely passed from the Sx_printf() call. It is intended
+ ** to be used by the client to figure out what to do with the
+ ** new string.
+ **
+ ** The new string ('stuff') is ASCII translation driven by the
+ ** Sx_printf()'s 'fmt' string. It is not guaranteed to be a null
+ ** terminated string.
+ **
+ ** The return value is the number of bytes copied from the 'stuff'
+ ** string. It is accumulated for each of the calls to the stuff
+ ** function and returned from the original caller of Sx_printf().
+ */
+ virtual PRSize StuffFunction(
+ void *state, const char *stuff, PRSize stufflen) = 0;
+}; /* RCFormatStuff */
+
+
+/*
+** RCFormatBuffer
+** The caller is supplying the buffer, the runtime is doing all
+** the conversion. The object contains no state, so is reusable
+** and reentrant.
+*/
+class PR_IMPLEMENT(RCFormatBuffer): public RCFormatStuff
+{
+public:
+ RCFormatBuffer();
+ virtual ~RCFormatBuffer();
+
+ /*
+ ** Format the trailing arguments as indicated by the 'fmt'
+ ** string. Put the result in 'buffer'. Return the number
+ ** of bytes moved into 'buffer'. 'buffer' will always be
+ ** a properly terminated string even if the convresion fails.
+ */
+ virtual PRSize Sn_printf(
+ char *buffer, PRSize length, const char *fmt, ...);
+
+ virtual char *Sm_append(char *buffer, const char *fmt, ...);
+
+private:
+ /*
+ ** This class overrides the stuff function, does not preserve
+ ** its virtual-ness and no longer allows the clients to call
+ ** it in the clear. In other words, it is now the implementation
+ ** for this class.
+ */
+ PRSize StuffFunction(void*, const char*, PRSize);
+
+}; /* RCFormatBuffer */
+
+/*
+** RCFormat
+** The runtime is supplying the buffer. The object has state - the
+** buffer. Each operation must run to completion before the object
+** can be reused. When it is, the buffer is reset (whatever that
+** means). The result of a conversion is available via the extractor.
+** After extracted, the memory still belongs to the class - if the
+** caller wants to retain or modify, it must first be copied.
+*/
+class PR_IMPLEMENT(RCFormat): pubic RCFormatBuffer
+{
+public:
+ RCFormat();
+ virtual ~RCFormat();
+
+ /*
+ ** Translate the trailing arguments according to the 'fmt'
+ ** string and store the results in the object.
+ */
+ virtual PRSize Sm_printf(const char *fmt, ...);
+
+ /*
+ ** Extract the latest translation.
+ ** The object does not surrender the memory occupied by
+ ** the string. If the caller wishes to modify the data,
+ ** it must first be copied.
+ */
+ const char*();
+
+private:
+ char *buffer;
+
+ RCFormat(const RCFormat&);
+ RCFormat& operator=(const RCFormat&);
+}; /* RCFormat */
+
+/*
+** RCPrint
+** The output is formatted and then written to an associated file
+** descriptor. The client can provide a suitable file descriptor
+** or can indicate that the output should be directed to one of
+** the well-known "console" devices.
+*/
+class PR_IMPLEMENT(RCPrint): public RCFormat
+{
+ virtual ~RCPrint();
+ RCPrint(RCIO* output);
+ RCPrint(RCFileIO::SpecialFile output);
+
+ virtual PRSize Printf(const char *fmt, ...);
+private:
+ RCPrint();
+}; /* RCPrint */
+
+#endif /* defined(_RCASCII_H) */
+
+/* RCAscii.h */
diff --git a/nsprpub/pr/src/cplus/rcbase.cpp b/nsprpub/pr/src/cplus/rcbase.cpp
new file mode 100644
index 0000000000..7c12e6d3d4
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcbase.cpp
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** RCBase.cpp - Mixin class for NSPR C++ wrappers
+*/
+
+#include "rcbase.h"
+
+RCBase::~RCBase() { }
+
+PRSize RCBase::GetErrorTextLength() {
+ return PR_GetErrorTextLength();
+}
+PRSize RCBase::CopyErrorText(char *text) {
+ return PR_GetErrorText(text);
+}
+
+void RCBase::SetError(PRErrorCode error, PRInt32 oserror)
+{
+ PR_SetError(error, oserror);
+}
+
+void RCBase::SetErrorText(PRSize text_length, const char *text)
+{
+ PR_SetErrorText(text_length, text);
+}
+
+/* rcbase.cpp */
diff --git a/nsprpub/pr/src/cplus/rcbase.h b/nsprpub/pr/src/cplus/rcbase.h
new file mode 100644
index 0000000000..3cc91f8041
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcbase.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** RCBase.h - Mixin class for NSPR C++ wrappers
+*/
+
+#if defined(_RCRUNTIME_H)
+#else
+#define _RCRUNTIME_H
+
+#include <prerror.h>
+
+/*
+** Class: RCBase (mixin)
+**
+** Generally mixed into every base class. The functions in this class are all
+** static. Therefore this entire class is just syntatic sugar. It gives the
+** illusion that errors (in particular) are retrieved via the same object
+** that just reported a failure. It also (unfortunately) might lead one to
+** believe that the errors are persistent in that object. They're not.
+*/
+
+class PR_IMPLEMENT(RCBase)
+{
+public:
+ virtual ~RCBase();
+
+ static void AbortSelf();
+
+ static PRErrorCode GetError();
+ static PRInt32 GetOSError();
+
+ static PRSize GetErrorTextLength();
+ static PRSize CopyErrorText(char *text);
+
+ static void SetError(PRErrorCode error, PRInt32 oserror);
+ static void SetErrorText(PRSize textLength, const char *text);
+
+protected:
+ RCBase() { }
+}; /* RCObject */
+
+inline PRErrorCode RCBase::GetError() {
+ return PR_GetError();
+}
+inline PRInt32 RCBase::GetOSError() {
+ return PR_GetOSError();
+}
+
+#endif /* defined(_RCRUNTIME_H) */
+
+/* rcbase.h */
diff --git a/nsprpub/pr/src/cplus/rccv.cpp b/nsprpub/pr/src/cplus/rccv.cpp
new file mode 100644
index 0000000000..f124385350
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rccv.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** RCCondition - C++ wrapper around NSPR's PRCondVar
+*/
+
+#include "rccv.h"
+
+#include <prlog.h>
+#include <prerror.h>
+#include <prcvar.h>
+
+RCCondition::RCCondition(class RCLock *lock): RCBase()
+{
+ cv = PR_NewCondVar(lock->lock);
+ PR_ASSERT(NULL != cv);
+ timeout = PR_INTERVAL_NO_TIMEOUT;
+} /* RCCondition::RCCondition */
+
+RCCondition::~RCCondition()
+{
+ if (NULL != cv) {
+ PR_DestroyCondVar(cv);
+ }
+} /* RCCondition::~RCCondition */
+
+PRStatus RCCondition::Wait()
+{
+ PRStatus rv;
+ PR_ASSERT(NULL != cv);
+ if (NULL == cv)
+ {
+ SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ else {
+ rv = PR_WaitCondVar(cv, timeout.interval);
+ }
+ return rv;
+} /* RCCondition::Wait */
+
+PRStatus RCCondition::Notify()
+{
+ return PR_NotifyCondVar(cv);
+} /* RCCondition::Notify */
+
+PRStatus RCCondition::Broadcast()
+{
+ return PR_NotifyAllCondVar(cv);
+} /* RCCondition::Broadcast */
+
+PRStatus RCCondition::SetTimeout(const RCInterval& tmo)
+{
+ if (NULL == cv)
+ {
+ SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ timeout = tmo;
+ return PR_SUCCESS;
+} /* RCCondition::SetTimeout */
+
+RCInterval RCCondition::GetTimeout() const {
+ return timeout;
+}
+
+/* rccv.cpp */
diff --git a/nsprpub/pr/src/cplus/rccv.h b/nsprpub/pr/src/cplus/rccv.h
new file mode 100644
index 0000000000..afeec61614
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rccv.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** RCCondition - C++ wrapper around NSPR's PRCondVar
+**
+** Conditions have a notion of timeouts. A thread that waits on a condition
+** will resume execution when the condition is notified OR when a specified
+** interval of time has expired.
+**
+** Most applications don't adjust the timeouts on conditions. The literature
+** would argue that all threads waiting on a single condition must have the
+** same semantics. But if an application wishes to modify the timeout with
+** (perhaps) each wait call, that modification should be done consistantly
+** and under protection of the condition's associated lock.
+**
+** The default timeout is infinity.
+*/
+
+#if defined(_RCCOND_H)
+#else
+#define _RCCOND_H
+
+#include "rclock.h"
+#include "rcbase.h"
+#include "rcinrval.h"
+
+struct PRCondVar;
+
+class PR_IMPLEMENT(RCCondition): public RCBase
+{
+public:
+ RCCondition(RCLock*); /* associates CV with a lock and infinite tmo */
+ virtual ~RCCondition();
+
+ virtual PRStatus Wait(); /* applies object's current timeout */
+
+ virtual PRStatus Notify(); /* perhaps ready one thread */
+ virtual PRStatus Broadcast(); /* perhaps ready many threads */
+
+ virtual PRStatus SetTimeout(const RCInterval&);
+ /* set object's current timeout value */
+
+private:
+ PRCondVar *cv;
+ RCInterval timeout;
+
+ RCCondition();
+ RCCondition(const RCCondition&);
+ void operator=(const RCCondition&);
+
+public:
+ RCInterval GetTimeout() const;
+}; /* RCCondition */
+
+inline RCCondition::RCCondition(): RCBase() { }
+inline RCCondition::RCCondition(const RCCondition&): RCBase() { }
+inline void RCCondition::operator=(const RCCondition&) { }
+
+#endif /* defined(_RCCOND_H) */
+
+/* RCCond.h */
diff --git a/nsprpub/pr/src/cplus/rcfileio.cpp b/nsprpub/pr/src/cplus/rcfileio.cpp
new file mode 100644
index 0000000000..91c5fde6ff
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcfileio.cpp
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Class implementation for normal and special file I/O (ref: prio.h)
+*/
+
+#include "rcfileio.h"
+
+#include <string.h>
+
+RCFileIO::RCFileIO(): RCIO(RCIO::file) { }
+
+RCFileIO::~RCFileIO() {
+ if (NULL != fd) {
+ (void)Close();
+ }
+}
+
+PRInt64 RCFileIO::Available()
+{
+ return fd->methods->available(fd);
+}
+
+PRStatus RCFileIO::Close()
+{
+ PRStatus rv = fd->methods->close(fd);
+ fd = NULL;
+ return rv;
+}
+
+PRStatus RCFileIO::Delete(const char* filename) {
+ return PR_Delete(filename);
+}
+
+PRStatus RCFileIO::FileInfo(RCFileInfo* info) const
+{
+ return fd->methods->fileInfo64(fd, &info->info);
+}
+
+PRStatus RCFileIO::FileInfo(const char *name, RCFileInfo* info)
+{
+ return PR_GetFileInfo64(name, &info->info);
+}
+
+PRStatus RCFileIO::Fsync()
+{
+ return fd->methods->fsync(fd);
+}
+
+PRStatus RCFileIO::Open(const char *filename, PRIntn flags, PRIntn mode)
+{
+ fd = PR_Open(filename, flags, mode);
+ return (NULL == fd) ? PR_FAILURE : PR_SUCCESS;
+} /* RCFileIO::Open */
+
+PRInt32 RCFileIO::Read(void *buf, PRSize amount)
+{
+ return fd->methods->read(fd, buf, amount);
+}
+
+PRInt64 RCFileIO::Seek(PRInt64 offset, RCIO::Whence how)
+{
+ PRSeekWhence whence;
+ switch (how)
+ {
+ case RCFileIO::set: whence = PR_SEEK_SET; break;
+ case RCFileIO::current: whence = PR_SEEK_CUR; break;
+ case RCFileIO::end: whence = PR_SEEK_END; break;
+ default: whence = (PRSeekWhence)-1;
+ }
+ return fd->methods->seek64(fd, offset, whence);
+} /* RCFileIO::Seek */
+
+PRInt32 RCFileIO::Write(const void *buf, PRSize amount)
+{
+ return fd->methods->write(fd, buf, amount);
+}
+
+PRInt32 RCFileIO::Writev(
+ const PRIOVec *iov, PRSize size, const RCInterval& timeout)
+{
+ return fd->methods->writev(fd, iov, size, timeout);
+}
+
+RCIO *RCFileIO::GetSpecialFile(RCFileIO::SpecialFile special)
+{
+ PRFileDesc* fd;
+ PRSpecialFD which;
+ RCFileIO* spec = NULL;
+
+ switch (special)
+ {
+ case RCFileIO::input: which = PR_StandardInput; break;
+ case RCFileIO::output: which = PR_StandardOutput; break;
+ case RCFileIO::error: which = PR_StandardError; break;
+ default: which = (PRSpecialFD)-1;
+ }
+ fd = PR_GetSpecialFD(which);
+ if (NULL != fd)
+ {
+ spec = new RCFileIO();
+ if (NULL != spec) {
+ spec->fd = fd;
+ }
+ }
+ return spec;
+} /* RCFileIO::GetSpecialFile */
+
+
+/*
+** The following methods have been made non-virtual and private. These
+** default implementations are intended to NEVER be called. They
+** are not valid for this type of I/O class (normal and special file).
+*/
+PRStatus RCFileIO::Connect(const RCNetAddr&, const RCInterval&)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus RCFileIO::GetLocalName(RCNetAddr*) const
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus RCFileIO::GetPeerName(RCNetAddr*) const
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus RCFileIO::GetSocketOption(PRSocketOptionData*) const
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus RCFileIO::Listen(PRIntn)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRInt16 RCFileIO::Poll(PRInt16, PRInt16*)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return 0;
+}
+
+PRInt32 RCFileIO::Recv(void*, PRSize, PRIntn, const RCInterval&)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return -1;
+}
+
+PRInt32 RCFileIO::Recvfrom(void*, PRSize, PRIntn, RCNetAddr*, const RCInterval&)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return -1;
+}
+
+PRInt32 RCFileIO::Send(
+ const void*, PRSize, PRIntn, const RCInterval&)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return -1;
+}
+
+PRInt32 RCFileIO::Sendto(
+ const void*, PRSize, PRIntn, const RCNetAddr&, const RCInterval&)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return -1;
+}
+
+RCIO* RCFileIO::Accept(RCNetAddr*, const RCInterval&)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return NULL;
+}
+
+PRStatus RCFileIO::Bind(const RCNetAddr&)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRInt32 RCFileIO::AcceptRead(
+ RCIO**, RCNetAddr**, void*, PRSize, const RCInterval&)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return -1;
+}
+
+PRStatus RCFileIO::SetSocketOption(const PRSocketOptionData*)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus RCFileIO::Shutdown(RCIO::ShutdownHow)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRInt32 RCFileIO::TransmitFile(
+ RCIO*, const void*, PRSize, RCIO::FileDisposition, const RCInterval&)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return -1;
+}
+
+/*
+** Class implementation for file information object (ref: prio.h)
+*/
+
+RCFileInfo::~RCFileInfo() { }
+
+RCFileInfo::RCFileInfo(const RCFileInfo& her): RCBase()
+{
+ info = her.info; /* RCFileInfo::RCFileInfo */
+}
+
+RCTime RCFileInfo::CreationTime() const {
+ return RCTime(info.creationTime);
+}
+
+RCTime RCFileInfo::ModifyTime() const {
+ return RCTime(info.modifyTime);
+}
+
+RCFileInfo::FileType RCFileInfo::Type() const
+{
+ RCFileInfo::FileType type;
+ switch (info.type)
+ {
+ case PR_FILE_FILE: type = RCFileInfo::file; break;
+ case PR_FILE_DIRECTORY: type = RCFileInfo::directory; break;
+ default: type = RCFileInfo::other;
+ }
+ return type;
+} /* RCFileInfo::Type */
diff --git a/nsprpub/pr/src/cplus/rcfileio.h b/nsprpub/pr/src/cplus/rcfileio.h
new file mode 100644
index 0000000000..9e28b36ace
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcfileio.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Class definitions for normal and special file I/O (ref: prio.h)
+*/
+
+#if defined(_RCFILEIO_H)
+#else
+#define _RCFILEIO_H
+
+#include "rcio.h"
+#include "rctime.h"
+
+/*
+** One would normally create a concrete class, such as RCFileIO, but then
+** pass around more generic references, ie., RCIO.
+**
+** This subclass of RCIO hides (makes private) the methods that are not
+** applicable to normal files.
+*/
+
+class RCFileInfo;
+
+class PR_IMPLEMENT(RCFileIO): public RCIO
+{
+public:
+ RCFileIO();
+ virtual ~RCFileIO();
+
+ virtual PRInt64 Available();
+ virtual PRStatus Close();
+ static PRStatus Delete(const char *name);
+ virtual PRStatus FileInfo(RCFileInfo* info) const;
+ static PRStatus FileInfo(const char *name, RCFileInfo* info);
+ virtual PRStatus Fsync();
+ virtual PRStatus Open(const char *name, PRIntn flags, PRIntn mode);
+ virtual PRInt32 Read(void *buf, PRSize amount);
+ virtual PRInt64 Seek(PRInt64 offset, RCIO::Whence how);
+ virtual PRInt32 Write(const void *buf, PRSize amount);
+ virtual PRInt32 Writev(
+ const PRIOVec *iov, PRSize size,
+ const RCInterval& timeout);
+
+private:
+
+ /* These methods made private are unavailable for this object */
+ RCFileIO(const RCFileIO&);
+ void operator=(const RCFileIO&);
+
+ RCIO* Accept(RCNetAddr* addr, const RCInterval& timeout);
+ PRInt32 AcceptRead(
+ RCIO **newfd, RCNetAddr **address, void *buffer,
+ PRSize amount, const RCInterval& timeout);
+ PRStatus Bind(const RCNetAddr& addr);
+ PRStatus Connect(const RCNetAddr& addr, const RCInterval& timeout);
+ PRStatus GetLocalName(RCNetAddr *addr) const;
+ PRStatus GetPeerName(RCNetAddr *addr) const;
+ PRStatus GetSocketOption(PRSocketOptionData *data) const;
+ PRStatus Listen(PRIntn backlog);
+ PRInt16 Poll(PRInt16 in_flags, PRInt16 *out_flags);
+ PRInt32 Recv(
+ void *buf, PRSize amount, PRIntn flags,
+ const RCInterval& timeout);
+ PRInt32 Recvfrom(
+ void *buf, PRSize amount, PRIntn flags,
+ RCNetAddr* addr, const RCInterval& timeout);
+ PRInt32 Send(
+ const void *buf, PRSize amount, PRIntn flags,
+ const RCInterval& timeout);
+ PRInt32 Sendto(
+ const void *buf, PRSize amount, PRIntn flags,
+ const RCNetAddr& addr,
+ const RCInterval& timeout);
+ PRStatus SetSocketOption(const PRSocketOptionData *data);
+ PRStatus Shutdown(RCIO::ShutdownHow how);
+ PRInt32 TransmitFile(
+ RCIO *source, const void *headers,
+ PRSize hlen, RCIO::FileDisposition flags,
+ const RCInterval& timeout);
+public:
+
+ /*
+ ** The following function return a valid normal file object,
+ ** Such objects can be used for scanned input and console output.
+ */
+ typedef enum {
+ input = PR_StandardInput,
+ output = PR_StandardOutput,
+ error = PR_StandardError
+ } SpecialFile;
+
+ static RCIO *GetSpecialFile(RCFileIO::SpecialFile special);
+
+}; /* RCFileIO */
+
+class PR_IMPLEMENT(RCFileInfo): public RCBase
+{
+public:
+ typedef enum {
+ file = PR_FILE_FILE,
+ directory = PR_FILE_DIRECTORY,
+ other = PR_FILE_OTHER
+ } FileType;
+
+public:
+ RCFileInfo();
+ RCFileInfo(const RCFileInfo&);
+
+ virtual ~RCFileInfo();
+
+ PRInt64 Size() const;
+ RCTime CreationTime() const;
+ RCTime ModifyTime() const;
+ RCFileInfo::FileType Type() const;
+
+ friend PRStatus RCFileIO::FileInfo(RCFileInfo*) const;
+ friend PRStatus RCFileIO::FileInfo(const char *name, RCFileInfo*);
+
+private:
+ PRFileInfo64 info;
+}; /* RCFileInfo */
+
+inline RCFileInfo::RCFileInfo(): RCBase() { }
+inline PRInt64 RCFileInfo::Size() const {
+ return info.size;
+}
+
+#endif /* defined(_RCFILEIO_H) */
diff --git a/nsprpub/pr/src/cplus/rcinrval.cpp b/nsprpub/pr/src/cplus/rcinrval.cpp
new file mode 100644
index 0000000000..3805a57663
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcinrval.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** C++ interval times (ref: prinrval.h)
+**
+** An interval is a period of time. The start of the interval (epoch)
+** must be defined by the application. The unit of time of an interval
+** is platform dependent, therefore so is the maximum interval that is
+** representable. However, that interval is never less that ~12 hours.
+*/
+
+#include "rcinrval.h"
+
+RCInterval::~RCInterval() { }
+
+RCInterval::RCInterval(RCInterval::RCReservedInterval special): RCBase()
+{
+ switch (special)
+ {
+ case RCInterval::now:
+ interval = PR_IntervalNow();
+ break;
+ case RCInterval::no_timeout:
+ interval = PR_INTERVAL_NO_TIMEOUT;
+ break;
+ case RCInterval::no_wait:
+ interval = PR_INTERVAL_NO_WAIT;
+ break;
+ default:
+ break;
+ }
+} /* RCInterval::RCInterval */
+
+/* rcinrval.cpp */
diff --git a/nsprpub/pr/src/cplus/rcinrval.h b/nsprpub/pr/src/cplus/rcinrval.h
new file mode 100644
index 0000000000..333209e618
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcinrval.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** C++ interval times (ref: prinrval.h)
+**
+** An interval is a period of time. The start of the interval (epoch)
+** must be defined by the application. The unit of time of an interval
+** is platform dependent, therefore so is the maximum interval that is
+** representable. However, that interval is never less than ~6 hours.
+*/
+#if defined(_RCINTERVAL_H)
+#else
+#define _RCINTERVAL_H
+
+#include "rcbase.h"
+#include <prinrval.h>
+
+class PR_IMPLEMENT(RCInterval): public RCBase
+{
+public:
+ typedef enum {now, no_timeout, no_wait} RCReservedInterval;
+
+ virtual ~RCInterval();
+
+ RCInterval();
+
+ RCInterval(PRIntervalTime interval);
+ RCInterval(const RCInterval& copy);
+ RCInterval(RCReservedInterval special);
+
+ void SetToNow();
+
+ void operator=(const RCInterval&);
+ void operator=(PRIntervalTime interval);
+
+ PRBool operator<(const RCInterval&);
+ PRBool operator>(const RCInterval&);
+ PRBool operator==(const RCInterval&);
+ PRBool operator>=(const RCInterval&);
+ PRBool operator<=(const RCInterval&);
+
+ RCInterval operator+(const RCInterval&);
+ RCInterval operator-(const RCInterval&);
+ RCInterval& operator+=(const RCInterval&);
+ RCInterval& operator-=(const RCInterval&);
+
+ RCInterval operator/(PRUint32);
+ RCInterval operator*(PRUint32);
+ RCInterval& operator/=(PRUint32);
+ RCInterval& operator*=(PRUint32);
+
+
+ PRUint32 ToSeconds() const;
+ PRUint32 ToMilliseconds() const;
+ PRUint32 ToMicroseconds() const;
+ operator PRIntervalTime() const;
+
+ static PRIntervalTime FromSeconds(PRUint32 seconds);
+ static PRIntervalTime FromMilliseconds(PRUint32 milli);
+ static PRIntervalTime FromMicroseconds(PRUint32 micro);
+
+ friend class RCCondition;
+
+private:
+ PRIntervalTime interval;
+
+}; /* RCInterval */
+
+
+inline RCInterval::RCInterval(): RCBase() { }
+
+inline RCInterval::RCInterval(const RCInterval& his): RCBase()
+{
+ interval = his.interval;
+}
+
+inline RCInterval::RCInterval(PRIntervalTime ticks): RCBase()
+{
+ interval = ticks;
+}
+
+inline void RCInterval::SetToNow() {
+ interval = PR_IntervalNow();
+}
+
+inline void RCInterval::operator=(const RCInterval& his)
+{
+ interval = his.interval;
+}
+
+inline void RCInterval::operator=(PRIntervalTime his)
+{
+ interval = his;
+}
+
+inline PRBool RCInterval::operator==(const RCInterval& his)
+{
+ return (interval == his.interval) ? PR_TRUE : PR_FALSE;
+}
+inline PRBool RCInterval::operator<(const RCInterval& his)
+{
+ return (interval < his.interval)? PR_TRUE : PR_FALSE;
+}
+inline PRBool RCInterval::operator>(const RCInterval& his)
+{
+ return (interval > his.interval) ? PR_TRUE : PR_FALSE;
+}
+inline PRBool RCInterval::operator<=(const RCInterval& his)
+{
+ return (interval <= his.interval) ? PR_TRUE : PR_FALSE;
+}
+inline PRBool RCInterval::operator>=(const RCInterval& his)
+{
+ return (interval <= his.interval) ? PR_TRUE : PR_FALSE;
+}
+
+inline RCInterval RCInterval::operator+(const RCInterval& his)
+{
+ return RCInterval((PRIntervalTime)(interval + his.interval));
+}
+inline RCInterval RCInterval::operator-(const RCInterval& his)
+{
+ return RCInterval((PRIntervalTime)(interval - his.interval));
+}
+inline RCInterval& RCInterval::operator+=(const RCInterval& his)
+{
+ interval += his.interval;
+ return *this;
+}
+inline RCInterval& RCInterval::operator-=(const RCInterval& his)
+{
+ interval -= his.interval;
+ return *this;
+}
+
+inline RCInterval RCInterval::operator/(PRUint32 him)
+{
+ return RCInterval((PRIntervalTime)(interval / him));
+}
+inline RCInterval RCInterval::operator*(PRUint32 him)
+{
+ return RCInterval((PRIntervalTime)(interval * him));
+}
+
+inline RCInterval& RCInterval::operator/=(PRUint32 him)
+{
+ interval /= him;
+ return *this;
+}
+
+inline RCInterval& RCInterval::operator*=(PRUint32 him)
+{
+ interval *= him;
+ return *this;
+}
+
+inline PRUint32 RCInterval::ToSeconds() const
+{
+ return PR_IntervalToSeconds(interval);
+}
+inline PRUint32 RCInterval::ToMilliseconds() const
+{
+ return PR_IntervalToMilliseconds(interval);
+}
+inline PRUint32 RCInterval::ToMicroseconds() const
+{
+ return PR_IntervalToMicroseconds(interval);
+}
+inline RCInterval::operator PRIntervalTime() const {
+ return interval;
+}
+
+inline PRIntervalTime RCInterval::FromSeconds(PRUint32 seconds)
+{
+ return PR_SecondsToInterval(seconds);
+}
+inline PRIntervalTime RCInterval::FromMilliseconds(PRUint32 milli)
+{
+ return PR_MillisecondsToInterval(milli);
+}
+inline PRIntervalTime RCInterval::FromMicroseconds(PRUint32 micro)
+{
+ return PR_MicrosecondsToInterval(micro);
+}
+
+#endif /* defined(_RCINTERVAL_H) */
+
+/* RCInterval.h */
diff --git a/nsprpub/pr/src/cplus/rcio.cpp b/nsprpub/pr/src/cplus/rcio.cpp
new file mode 100644
index 0000000000..081a9f694a
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcio.cpp
@@ -0,0 +1,14 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Base class implmenation for I/O (ref: prio.h)
+*/
+
+#include "rcio.h"
+
+RCIO::~RCIO() { }
+
+RCIO::RCIO(RCIO::RCIOType): RCBase() { }
diff --git a/nsprpub/pr/src/cplus/rcio.h b/nsprpub/pr/src/cplus/rcio.h
new file mode 100644
index 0000000000..1bd3ac4140
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcio.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Base class definitions for I/O (ref: prio.h)
+**
+** This class is a virtual base class. Construction must be done by a
+** subclass, but the I/O operations can be done on a RCIO object reference.
+*/
+
+#if defined(_RCIO_H)
+#else
+#define _RCIO_H
+
+#include "rcbase.h"
+#include "rcnetdb.h"
+#include "rcinrval.h"
+
+#include "prio.h"
+
+class RCFileInfo;
+
+class PR_IMPLEMENT(RCIO): public RCBase
+{
+public:
+ typedef enum {
+ open = PR_TRANSMITFILE_KEEP_OPEN, /* socket is left open after file
+ * is transmitted. */
+ close = PR_TRANSMITFILE_CLOSE_SOCKET/* socket is closed after file
+ * is transmitted. */
+ } FileDisposition;
+
+ typedef enum {
+ set = PR_SEEK_SET, /* Set to value specified */
+ current = PR_SEEK_CUR, /* Seek relative to current position */
+ end = PR_SEEK_END /* seek past end of current eof */
+ } Whence;
+
+ typedef enum {
+ recv = PR_SHUTDOWN_RCV, /* receives will be disallowed */
+ send = PR_SHUTDOWN_SEND, /* sends will be disallowed */
+ both = PR_SHUTDOWN_BOTH /* sends & receives will be disallowed */
+ } ShutdownHow;
+
+public:
+ virtual ~RCIO();
+
+ virtual RCIO* Accept(RCNetAddr* addr, const RCInterval& timeout) = 0;
+ virtual PRInt32 AcceptRead(
+ RCIO **nd, RCNetAddr **raddr, void *buf,
+ PRSize amount, const RCInterval& timeout) = 0;
+ virtual PRInt64 Available() = 0;
+ virtual PRStatus Bind(const RCNetAddr& addr) = 0;
+ virtual PRStatus Close() = 0;
+ virtual PRStatus Connect(
+ const RCNetAddr& addr,
+ const RCInterval& timeout) = 0;
+ virtual PRStatus FileInfo(RCFileInfo *info) const = 0;
+ virtual PRStatus Fsync() = 0;
+ virtual PRStatus GetLocalName(RCNetAddr *addr) const = 0;
+ virtual PRStatus GetPeerName(RCNetAddr *addr) const = 0;
+ virtual PRStatus GetSocketOption(PRSocketOptionData *data) const = 0;
+ virtual PRStatus Listen(PRIntn backlog) = 0;
+ virtual PRStatus Open(const char *name, PRIntn flags, PRIntn mode) = 0;
+ virtual PRInt16 Poll(PRInt16 in_flags, PRInt16 *out_flags) = 0;
+ virtual PRInt32 Read(void *buf, PRSize amount) = 0;
+ virtual PRInt32 Recv(
+ void *buf, PRSize amount, PRIntn flags,
+ const RCInterval& timeout) = 0;
+ virtual PRInt32 Recvfrom(
+ void *buf, PRSize amount, PRIntn flags,
+ RCNetAddr* addr, const RCInterval& timeout) = 0;
+ virtual PRInt64 Seek(PRInt64 offset, Whence how) = 0;
+ virtual PRInt32 Send(
+ const void *buf, PRSize amount, PRIntn flags,
+ const RCInterval& timeout) = 0;
+ virtual PRInt32 Sendto(
+ const void *buf, PRSize amount, PRIntn flags,
+ const RCNetAddr& addr,
+ const RCInterval& timeout) = 0;
+ virtual PRStatus SetSocketOption(const PRSocketOptionData *data) = 0;
+ virtual PRStatus Shutdown(ShutdownHow how) = 0;
+ virtual PRInt32 TransmitFile(
+ RCIO *source, const void *headers,
+ PRSize hlen, RCIO::FileDisposition flags,
+ const RCInterval& timeout) = 0;
+ virtual PRInt32 Write(const void *buf, PRSize amount) = 0;
+ virtual PRInt32 Writev(
+ const PRIOVec *iov, PRSize size,
+ const RCInterval& timeout) = 0;
+
+protected:
+ typedef enum {
+ file = PR_DESC_FILE,
+ tcp = PR_DESC_SOCKET_TCP,
+ udp = PR_DESC_SOCKET_UDP,
+ layered = PR_DESC_LAYERED
+ } RCIOType;
+
+ RCIO(RCIOType);
+
+ PRFileDesc *fd; /* where the real code hides */
+
+private:
+ /* no default construction and no copies allowed */
+ RCIO();
+ RCIO(const RCIO&);
+
+}; /* RCIO */
+
+#endif /* defined(_RCIO_H) */
+
+/* RCIO.h */
+
+
diff --git a/nsprpub/pr/src/cplus/rclock.cpp b/nsprpub/pr/src/cplus/rclock.cpp
new file mode 100644
index 0000000000..8c106de088
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rclock.cpp
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+** C++ access to NSPR locks (PRLock)
+*/
+
+#include "rclock.h"
+#include <prlog.h>
+
+RCLock::RCLock()
+{
+ lock = PR_NewLock(); /* it might be NULL */
+ PR_ASSERT(NULL != lock);
+} /* RCLock::RCLock */
+
+RCLock::~RCLock()
+{
+ if (NULL != lock) {
+ PR_DestroyLock(lock);
+ }
+ lock = NULL;
+} /* RCLock::~RCLock */
+
+void RCLock::Acquire()
+{
+ PR_ASSERT(NULL != lock);
+ PR_Lock(lock);
+} /* RCLock::Acquire */
+
+void RCLock::Release()
+{
+ PRStatus rv;
+ PR_ASSERT(NULL != lock);
+ rv = PR_Unlock(lock);
+ PR_ASSERT(PR_SUCCESS == rv);
+} /* RCLock::Release */
+
+/* RCLock.cpp */
+
diff --git a/nsprpub/pr/src/cplus/rclock.h b/nsprpub/pr/src/cplus/rclock.h
new file mode 100644
index 0000000000..c86c2f17b6
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rclock.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** C++ access to NSPR locks (PRLock)
+*/
+
+#if defined(_RCLOCK_H)
+#else
+#define _RCLOCK_H
+
+#include "rcbase.h"
+
+#include <prlock.h>
+
+class PR_IMPLEMENT(RCLock): public RCBase
+{
+public:
+ RCLock();
+ virtual ~RCLock();
+
+ void Acquire(); /* non-reentrant */
+ void Release(); /* should be by owning thread */
+
+ friend class RCCondition;
+
+private:
+ RCLock(const RCLock&); /* can't do that */
+ void operator=(const RCLock&); /* nor that */
+
+ PRLock *lock;
+}; /* RCLock */
+
+/*
+** Class: RCEnter
+**
+** In scope locks. You can only allocate them on the stack. The language
+** will insure that they get released (by calling the destructor) when
+** the thread leaves scope, even if via an exception.
+*/
+class PR_IMPLEMENT(RCEnter)
+{
+public:
+ ~RCEnter(); /* releases the lock */
+ RCEnter(RCLock*); /* acquires the lock */
+
+private:
+ RCLock *lock;
+
+ RCEnter();
+ RCEnter(const RCEnter&);
+ void operator=(const RCEnter&);
+
+ void *operator new(PRSize) {
+ return NULL;
+ }
+ void operator delete(void*) { }
+}; /* RCEnter */
+
+
+inline RCEnter::RCEnter(RCLock* ml) {
+ lock = ml;
+ lock->Acquire();
+}
+inline RCEnter::~RCEnter() {
+ lock->Release();
+ lock = NULL;
+}
+
+#endif /* defined(_RCLOCK_H) */
+
+/* RCLock.h */
diff --git a/nsprpub/pr/src/cplus/rcmon.h b/nsprpub/pr/src/cplus/rcmon.h
new file mode 100644
index 0000000000..5d084efdea
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcmon.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Class: RCMonitor (ref prmonitor.h)
+**
+** RCMonitor.h - C++ wrapper around NSPR's monitors
+*/
+#if defined(_RCMONITOR_H)
+#else
+#define _RCMONITOR_H
+
+#include "rcbase.h"
+#include "rcinrval.h"
+
+struct PRMonitor;
+
+class PR_IMPLEMENT(RCMonitor): public RCBase
+{
+public:
+ RCMonitor(); /* timeout is infinity */
+ virtual ~RCMonitor();
+
+ virtual void Enter(); /* reentrant entry */
+ virtual void Exit();
+
+ virtual void Notify(); /* possibly enable one thread */
+ virtual void NotifyAll(); /* enable all waiters */
+
+ virtual void Wait(); /* applies object's timeout */
+
+ virtual void SetTimeout(const RCInterval& timeout);
+
+private:
+ PRMonitor *monitor;
+ RCInterval timeout;
+
+public:
+ RCInterval GetTimeout() const; /* get the current value */
+
+}; /* RCMonitor */
+
+#endif /* defined(_RCMONITOR_H) */
+
+/* RCMonitor.h */
diff --git a/nsprpub/pr/src/cplus/rcnetdb.cpp b/nsprpub/pr/src/cplus/rcnetdb.cpp
new file mode 100644
index 0000000000..19eb66540f
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcnetdb.cpp
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Base class implementation for network access functions (ref: prnetdb.h)
+*/
+
+#include "rclock.h"
+#include "rcnetdb.h"
+
+#include <prmem.h>
+#include <prlog.h>
+#include <string.h>
+
+RCNetAddr::RCNetAddr(const RCNetAddr& his): RCBase()
+{
+ address = his.address;
+}
+
+RCNetAddr::RCNetAddr(const RCNetAddr& his, PRUint16 port): RCBase()
+{
+ address = his.address;
+ switch (address.raw.family)
+ {
+ case PR_AF_INET: address.inet.port = port; break;
+ case PR_AF_INET6: address.ipv6.port = port; break;
+ default: break;
+ }
+} /* RCNetAddr::RCNetAddr */
+
+RCNetAddr::RCNetAddr(RCNetAddr::HostValue host, PRUint16 port): RCBase()
+{
+ PRNetAddrValue how;
+ switch (host)
+ {
+ case RCNetAddr::any: how = PR_IpAddrAny; break;
+ case RCNetAddr::loopback: how = PR_IpAddrLoopback; break;
+ default: PR_NOT_REACHED("This can't happen -- and did!");
+ }
+ (void)PR_InitializeNetAddr(how, port, &address);
+} /* RCNetAddr::RCNetAddr */
+
+RCNetAddr::~RCNetAddr() { }
+
+void RCNetAddr::operator=(const RCNetAddr& his) {
+ address = his.address;
+}
+
+PRStatus RCNetAddr::FromString(const char* string)
+{
+ return PR_StringToNetAddr(string, &address);
+}
+
+void RCNetAddr::operator=(const PRNetAddr* addr) {
+ address = *addr;
+}
+
+PRBool RCNetAddr::operator==(const RCNetAddr& his) const
+{
+ PRBool rv = EqualHost(his);
+ if (rv)
+ {
+ switch (address.raw.family)
+ {
+ case PR_AF_INET:
+ rv = (address.inet.port == his.address.inet.port); break;
+ case PR_AF_INET6:
+ rv = (address.ipv6.port == his.address.ipv6.port); break;
+ case PR_AF_LOCAL:
+ default: break;
+ }
+ }
+ return rv;
+} /* RCNetAddr::operator== */
+
+PRBool RCNetAddr::EqualHost(const RCNetAddr& his) const
+{
+ PRBool rv;
+ switch (address.raw.family)
+ {
+ case PR_AF_INET:
+ rv = (address.inet.ip == his.address.inet.ip); break;
+ case PR_AF_INET6:
+ rv = (0 == memcmp(
+ &address.ipv6.ip, &his.address.ipv6.ip,
+ sizeof(address.ipv6.ip)));
+ break;
+#if defined(XP_UNIX)
+ case PR_AF_LOCAL:
+ rv = (0 == strncmp(
+ address.local.path, his.address.local.path,
+ sizeof(address.local.path)));
+ break;
+#endif
+ default: break;
+ }
+ return rv;
+} /* RCNetAddr::operator== */
+
+PRStatus RCNetAddr::ToString(char *string, PRSize size) const
+{
+ return PR_NetAddrToString(&address, string, size);
+}
+
+/*
+** RCHostLookup
+*/
+
+RCHostLookup::~RCHostLookup()
+{
+ if (NULL != address) {
+ delete [] address;
+ }
+} /* RCHostLookup::~RCHostLookup */
+
+RCHostLookup::RCHostLookup(): RCBase()
+{
+ address = NULL;
+ max_index = 0;
+} /* RCHostLookup::RCHostLookup */
+
+PRStatus RCHostLookup::ByName(const char* name)
+{
+ PRStatus rv;
+ PRNetAddr addr;
+ PRHostEnt hostentry;
+ PRIntn index = 0, max;
+ RCNetAddr* vector = NULL;
+ RCNetAddr* old_vector = NULL;
+ void* buffer = PR_Malloc(PR_NETDB_BUF_SIZE);
+ if (NULL == buffer) {
+ return PR_FAILURE;
+ }
+ rv = PR_GetHostByName(name, (char*)buffer, PR_NETDB_BUF_SIZE, &hostentry);
+ if (PR_SUCCESS == rv)
+ {
+ for (max = 0, index = 0;; ++max)
+ {
+ index = PR_EnumerateHostEnt(index, &hostentry, 0, &addr);
+ if (0 == index) {
+ break;
+ }
+ }
+ if (max > 0)
+ {
+ vector = new RCNetAddr[max];
+ while (--max > 0)
+ {
+ index = PR_EnumerateHostEnt(index, &hostentry, 0, &addr);
+ if (0 == index) {
+ break;
+ }
+ vector[index] = &addr;
+ }
+ {
+ RCEnter entry(&ml);
+ old_vector = address;
+ address = vector;
+ max_index = max;
+ }
+ if (NULL != old_vector) {
+ delete [] old_vector;
+ }
+ }
+ }
+ if (NULL != buffer) {
+ PR_DELETE(buffer);
+ }
+ return PR_SUCCESS;
+} /* RCHostLookup::ByName */
+
+PRStatus RCHostLookup::ByAddress(const RCNetAddr& host_addr)
+{
+ PRStatus rv;
+ PRNetAddr addr;
+ PRHostEnt hostentry;
+ PRIntn index = 0, max;
+ RCNetAddr* vector = NULL;
+ RCNetAddr* old_vector = NULL;
+ char *buffer = (char*)PR_Malloc(PR_NETDB_BUF_SIZE);
+ if (NULL == buffer) {
+ return PR_FAILURE;
+ }
+ rv = PR_GetHostByAddr(host_addr, buffer, PR_NETDB_BUF_SIZE, &hostentry);
+ if (PR_SUCCESS == rv)
+ {
+ for (max = 0, index = 0;; ++max)
+ {
+ index = PR_EnumerateHostEnt(index, &hostentry, 0, &addr);
+ if (0 == index) {
+ break;
+ }
+ }
+ if (max > 0)
+ {
+ vector = new RCNetAddr[max];
+ while (--max > 0)
+ {
+ index = PR_EnumerateHostEnt(index, &hostentry, 0, &addr);
+ if (0 == index) {
+ break;
+ }
+ vector[index] = &addr;
+ }
+ {
+ RCEnter entry(&ml);
+ old_vector = address;
+ address = vector;
+ max_index = max;
+ }
+ if (NULL != old_vector) {
+ delete [] old_vector;
+ }
+ }
+ }
+ if (NULL != buffer) {
+ PR_DELETE(buffer);
+ }
+ return PR_SUCCESS;
+} /* RCHostLookup::ByAddress */
+
+const RCNetAddr* RCHostLookup::operator[](PRUintn which)
+{
+ RCNetAddr* addr = NULL;
+ if (which < max_index) {
+ addr = &address[which];
+ }
+ return addr;
+} /* RCHostLookup::operator[] */
+
+/* RCNetdb.cpp */
diff --git a/nsprpub/pr/src/cplus/rcnetdb.h b/nsprpub/pr/src/cplus/rcnetdb.h
new file mode 100644
index 0000000000..14e96ecd85
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcnetdb.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Base class definitions for network access functions (ref: prnetdb.h)
+*/
+
+#if defined(_RCNETDB_H)
+#else
+#define _RCNETDB_H
+
+#include "rclock.h"
+#include "rcbase.h"
+
+#include <prnetdb.h>
+
+class PR_IMPLEMENT(RCNetAddr): public RCBase
+{
+public:
+ typedef enum {
+ any = PR_IpAddrAny, /* assign logical INADDR_ANY */
+ loopback = PR_IpAddrLoopback /* assign logical INADDR_LOOPBACK */
+ } HostValue;
+
+ RCNetAddr(); /* default constructor is unit'd object */
+ RCNetAddr(const RCNetAddr&); /* copy constructor */
+ RCNetAddr(HostValue, PRUint16 port);/* init'd w/ 'special' assignments */
+ RCNetAddr(const RCNetAddr&, PRUint16 port);
+ /* copy w/ port reassigment */
+
+ virtual ~RCNetAddr();
+
+ void operator=(const RCNetAddr&);
+
+ virtual PRBool operator==(const RCNetAddr&) const;
+ /* compare of all relavent fields */
+ virtual PRBool EqualHost(const RCNetAddr&) const;
+ /* compare of just host field */
+
+
+public:
+
+ void operator=(const PRNetAddr*); /* construction from more primitive data */
+ operator const PRNetAddr*() const; /* extraction of underlying representation */
+ virtual PRStatus FromString(const char* string);
+ /* initialization from an ASCII string */
+ virtual PRStatus ToString(char *string, PRSize size) const;
+ /* convert internal fromat to a string */
+
+private:
+
+ PRNetAddr address;
+
+}; /* RCNetAddr */
+
+/*
+** Class: RCHostLookup
+**
+** Abstractions to look up host names and addresses.
+**
+** This is a stateful class. One looks up the host by name or by
+** address, then enumerates over a possibly empty array of network
+** addresses. The storage for the addresses is owned by the class.
+*/
+
+class RCHostLookup: public RCBase
+{
+public:
+ virtual ~RCHostLookup();
+
+ RCHostLookup();
+
+ virtual PRStatus ByName(const char* name);
+ virtual PRStatus ByAddress(const RCNetAddr&);
+
+ virtual const RCNetAddr* operator[](PRUintn);
+
+private:
+ RCLock ml;
+ PRIntn max_index;
+ RCNetAddr* address;
+
+ RCHostLookup(const RCHostLookup&);
+ RCHostLookup& operator=(const RCHostLookup&);
+};
+
+inline RCNetAddr::RCNetAddr(): RCBase() { }
+inline RCNetAddr::operator const PRNetAddr*() const {
+ return &address;
+}
+
+
+#endif /* defined(_RCNETDB_H) */
+
+/* RCNetdb.h */
+
+
diff --git a/nsprpub/pr/src/cplus/rcnetio.cpp b/nsprpub/pr/src/cplus/rcnetio.cpp
new file mode 100644
index 0000000000..e351345e43
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcnetio.cpp
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Subclass implementation for streamed network I/O (ref: prio.h)
+*/
+
+#include "rcnetio.h"
+
+#include <private/pprio.h>
+
+RCNetStreamIO::~RCNetStreamIO()
+{
+ PRStatus rv = (fd->methods->close)(fd);
+ fd = NULL;
+}
+
+RCNetStreamIO::RCNetStreamIO(): RCIO(RCIO::tcp)
+{
+ fd = PR_NewTCPSocket();
+}
+
+RCNetStreamIO::RCNetStreamIO(PRIntn protocol): RCIO(RCIO::tcp)
+{
+ fd = PR_Socket(PR_AF_INET, PR_SOCK_STREAM, protocol);
+}
+
+RCIO* RCNetStreamIO::Accept(RCNetAddr* addr, const RCInterval& timeout)
+{
+ PRNetAddr peer;
+ RCNetStreamIO* rcio = NULL;
+ PRFileDesc* newfd = fd->methods->accept(fd, &peer, timeout);
+ if (NULL != newfd)
+ {
+ rcio = new RCNetStreamIO();
+ if (NULL != rcio)
+ {
+ *addr = &peer;
+ rcio->fd = newfd;
+ }
+ else {
+ (void)(newfd->methods->close)(newfd);
+ }
+ }
+ return rcio;
+} /* RCNetStreamIO::Accept */
+
+PRInt32 RCNetStreamIO::AcceptRead(
+ RCIO **nd, RCNetAddr **raddr, void *buf,
+ PRSize amount, const RCInterval& timeout)
+{
+ PRNetAddr *from;
+ PRFileDesc *accepted;
+ PRInt32 rv = (fd->methods->acceptread)(
+ fd, &accepted, &from, buf, amount, timeout);
+ if (rv >= 0)
+ {
+ RCNetStreamIO *ns = new RCNetStreamIO();
+ if (NULL != *nd) {
+ ns->fd = accepted;
+ }
+ else {
+ PR_Close(accepted);
+ rv = -1;
+ }
+ *nd = ns;
+ }
+ return rv;
+} /* RCNetStreamIO::AcceptRead */
+
+PRInt64 RCNetStreamIO::Available()
+{
+ return (fd->methods->available64)(fd);
+}
+
+PRStatus RCNetStreamIO::Bind(const RCNetAddr& addr)
+{
+ return (fd->methods->bind)(fd, addr);
+}
+
+PRStatus RCNetStreamIO::Connect(const RCNetAddr& addr, const RCInterval& timeout)
+{
+ return (fd->methods->connect)(fd, addr, timeout);
+}
+
+PRStatus RCNetStreamIO::GetLocalName(RCNetAddr *addr) const
+{
+ PRNetAddr local;
+ PRStatus rv = (fd->methods->getsockname)(fd, &local);
+ if (PR_SUCCESS == rv) {
+ *addr = &local;
+ }
+ return rv;
+} /* RCNetStreamIO::GetLocalName */
+
+PRStatus RCNetStreamIO::GetPeerName(RCNetAddr *addr) const
+{
+ PRNetAddr peer;
+ PRStatus rv = (fd->methods->getpeername)(fd, &peer);
+ if (PR_SUCCESS == rv) {
+ *addr = &peer;
+ }
+ return rv;
+} /* RCNetStreamIO::GetPeerName */
+
+PRStatus RCNetStreamIO::GetSocketOption(PRSocketOptionData *data) const
+{
+ return (fd->methods->getsocketoption)(fd, data);
+}
+
+PRStatus RCNetStreamIO::Listen(PRIntn backlog)
+{
+ return (fd->methods->listen)(fd, backlog);
+}
+
+PRInt16 RCNetStreamIO::Poll(PRInt16 in_flags, PRInt16 *out_flags)
+{
+ return (fd->methods->poll)(fd, in_flags, out_flags);
+}
+
+PRInt32 RCNetStreamIO::Read(void *buf, PRSize amount)
+{
+ return (fd->methods->read)(fd, buf, amount);
+}
+
+PRInt32 RCNetStreamIO::Recv(
+ void *buf, PRSize amount, PRIntn flags, const RCInterval& timeout)
+{
+ return (fd->methods->recv)(fd, buf, amount, flags, timeout);
+}
+
+PRInt32 RCNetStreamIO::Recvfrom(
+ void *buf, PRSize amount, PRIntn flags,
+ RCNetAddr* addr, const RCInterval& timeout)
+{
+ PRNetAddr peer;
+ PRInt32 rv = (fd->methods->recvfrom)(
+ fd, buf, amount, flags, &peer, timeout);
+ if (-1 != rv) {
+ *addr = &peer;
+ }
+ return rv;
+} /* RCNetStreamIO::Recvfrom */
+
+PRInt32 RCNetStreamIO::Send(
+ const void *buf, PRSize amount, PRIntn flags, const RCInterval& timeout)
+{
+ return (fd->methods->send)(fd, buf, amount, flags, timeout);
+}
+
+PRInt32 RCNetStreamIO::Sendto(
+ const void *buf, PRSize amount, PRIntn flags,
+ const RCNetAddr& addr, const RCInterval& timeout)
+{
+ return (fd->methods->sendto)(fd, buf, amount, flags, addr, timeout);
+}
+
+PRStatus RCNetStreamIO::SetSocketOption(const PRSocketOptionData *data)
+{
+ return (fd->methods->setsocketoption)(fd, data);
+}
+
+PRStatus RCNetStreamIO::Shutdown(RCIO::ShutdownHow how)
+{
+ return (fd->methods->shutdown)(fd, (PRIntn)how);
+}
+
+PRInt32 RCNetStreamIO::TransmitFile(
+ RCIO *source, const void *headers, PRSize hlen,
+ RCIO::FileDisposition flags, const RCInterval& timeout)
+{
+ RCNetStreamIO *src = (RCNetStreamIO*)source;
+ return (fd->methods->transmitfile)(
+ fd, src->fd, headers, hlen, (PRTransmitFileFlags)flags, timeout);
+}
+
+PRInt32 RCNetStreamIO::Write(const void *buf, PRSize amount)
+{
+ return (fd->methods->write)(fd, buf, amount);
+}
+
+PRInt32 RCNetStreamIO::Writev(
+ const PRIOVec *iov, PRSize size, const RCInterval& timeout)
+{
+ return (fd->methods->writev)(fd, iov, size, timeout);
+}
+
+/*
+** Invalid functions
+*/
+
+PRStatus RCNetStreamIO::Close()
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus RCNetStreamIO::FileInfo(RCFileInfo*) const
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus RCNetStreamIO::Fsync()
+{
+ return (fd->methods->fsync)(fd);
+}
+
+PRStatus RCNetStreamIO::Open(const char*, PRIntn, PRIntn)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRInt64 RCNetStreamIO::Seek(PRInt64, RCIO::Whence)
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+}
+
+/* RCNetStreamIO.cpp */
+
+
diff --git a/nsprpub/pr/src/cplus/rcnetio.h b/nsprpub/pr/src/cplus/rcnetio.h
new file mode 100644
index 0000000000..7f6b669d55
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcnetio.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Subclass definitions for network I/O (ref: prio.h)
+*/
+
+#if defined(_RCNETIO_H)
+#else
+#define _RCNETIO_H
+
+#include "rcbase.h"
+#include "rcinrval.h"
+#include "rcio.h"
+#include "rcnetdb.h"
+
+#include "prio.h"
+
+class RCFileInfo;
+
+/*
+** Class: RCNetStreamIO (ref prio.h)
+**
+** Streamed (reliable) network I/O (e.g., TCP).
+** This class hides (makes private) the functions that are not applicable
+** to network I/O (i.e., those for file I/O).
+*/
+
+class PR_IMPLEMENT(RCNetStreamIO): public RCIO
+{
+
+public:
+ RCNetStreamIO();
+ virtual ~RCNetStreamIO();
+
+ virtual RCIO* Accept(RCNetAddr* addr, const RCInterval& timeout);
+ virtual PRInt32 AcceptRead(
+ RCIO **nd, RCNetAddr **raddr, void *buf,
+ PRSize amount, const RCInterval& timeout);
+ virtual PRInt64 Available();
+ virtual PRStatus Bind(const RCNetAddr& addr);
+ virtual PRStatus Connect(
+ const RCNetAddr& addr, const RCInterval& timeout);
+ virtual PRStatus GetLocalName(RCNetAddr *addr) const;
+ virtual PRStatus GetPeerName(RCNetAddr *addr) const;
+ virtual PRStatus GetSocketOption(PRSocketOptionData *data) const;
+ virtual PRStatus Listen(PRIntn backlog);
+ virtual PRInt16 Poll(PRInt16 in_flags, PRInt16 *out_flags);
+ virtual PRInt32 Read(void *buf, PRSize amount);
+ virtual PRInt32 Recv(
+ void *buf, PRSize amount, PRIntn flags,
+ const RCInterval& timeout);
+ virtual PRInt32 Recvfrom(
+ void *buf, PRSize amount, PRIntn flags,
+ RCNetAddr* addr, const RCInterval& timeout);
+ virtual PRInt32 Send(
+ const void *buf, PRSize amount, PRIntn flags,
+ const RCInterval& timeout);
+ virtual PRInt32 Sendto(
+ const void *buf, PRSize amount, PRIntn flags,
+ const RCNetAddr& addr,
+ const RCInterval& timeout);
+ virtual PRStatus SetSocketOption(const PRSocketOptionData *data);
+ virtual PRStatus Shutdown(ShutdownHow how);
+ virtual PRInt32 TransmitFile(
+ RCIO *source, const void *headers,
+ PRSize hlen, RCIO::FileDisposition flags,
+ const RCInterval& timeout);
+ virtual PRInt32 Write(const void *buf, PRSize amount);
+ virtual PRInt32 Writev(
+ const PRIOVec *iov, PRSize size,
+ const RCInterval& timeout);
+
+private:
+ /* functions unavailable to this clients of this class */
+ RCNetStreamIO(const RCNetStreamIO&);
+
+ PRStatus Close();
+ PRStatus Open(const char *name, PRIntn flags, PRIntn mode);
+ PRStatus FileInfo(RCFileInfo *info) const;
+ PRStatus Fsync();
+ PRInt64 Seek(PRInt64 offset, RCIO::Whence how);
+
+public:
+ RCNetStreamIO(PRIntn protocol);
+}; /* RCNetIO */
+
+#endif /* defined(_RCNETIO_H) */
+
+/* RCNetStreamIO.h */
+
+
diff --git a/nsprpub/pr/src/cplus/rcthread.cpp b/nsprpub/pr/src/cplus/rcthread.cpp
new file mode 100644
index 0000000000..471e580a2c
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcthread.cpp
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* RCThread.cpp - C++ wrapper on NSPR */
+
+#include "rcthread.h"
+#include "rcinrval.h"
+
+#include <prmem.h>
+#include <prlog.h>
+#include <stdio.h>
+#include <prinit.h>
+
+static RCPrimordialThread *primordial = NULL;
+
+void nas_Root(void *arg)
+{
+ RCThread *him = (RCThread*)arg;
+ while (RCThread::ex_unstarted == him->execution) {
+ (void)PR_Sleep(PR_INTERVAL_NO_TIMEOUT); /* wait for Start() */
+ }
+ him->RootFunction(); /* he gets a self reference */
+ if (PR_UNJOINABLE_THREAD == PR_GetThreadState(him->identity)) {
+ delete him;
+ }
+} /* nas_Root */
+
+RCThread::~RCThread() { }
+
+RCThread::RCThread(): RCBase() { }
+
+RCThread::RCThread(const RCThread&): RCBase()
+{
+ PR_NOT_REACHED("Cannot call thread copy constructor");
+} /* RCThread::RCThread */
+
+RCThread::RCThread(
+ RCThread::Scope scope, RCThread::State join, PRUint32 stackSize):
+ RCBase()
+{
+ execution = ex_unstarted;
+ identity = PR_CreateThread(
+ PR_USER_THREAD, nas_Root, this,
+ PR_GetThreadPriority(PR_GetCurrentThread()),
+ (PRThreadScope)scope, (PRThreadState)join, stackSize);
+} /* RCThread::RCThread */
+
+void RCThread::operator=(const RCThread&)
+{
+ PR_NOT_REACHED("Cannot call thread assignment operator");
+} /* RCThread::operator= */
+
+
+PRStatus RCThread::Start()
+{
+ PRStatus rv;
+ /* This is an unsafe check, but not too critical */
+ if (RCThread::ex_unstarted == execution)
+ {
+ execution = RCThread::ex_started;
+ rv = PR_Interrupt(identity);
+ PR_ASSERT(PR_SUCCESS == rv);
+ }
+ else
+ {
+ rv = PR_FAILURE;
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ }
+ return rv;
+} /* RCThread::Start */
+
+PRStatus RCThread::Join()
+{
+ PRStatus rv;
+ if (RCThread::ex_unstarted == execution)
+ {
+ rv = PR_FAILURE;
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ }
+ else {
+ rv = PR_JoinThread(identity);
+ }
+ if (PR_SUCCESS == rv) {
+ delete this;
+ }
+ return rv;
+} /* RCThread::Join */
+
+PRStatus RCThread::Interrupt()
+{
+ PRStatus rv;
+ if (RCThread::ex_unstarted == execution)
+ {
+ rv = PR_FAILURE;
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ }
+ else {
+ rv = PR_Interrupt(identity);
+ }
+ return rv;
+} /* RCThread::Interrupt */
+
+void RCThread::ClearInterrupt() {
+ PR_ClearInterrupt();
+}
+
+void RCThread::SetPriority(RCThread::Priority new_priority)
+{
+ PR_SetThreadPriority(identity, (PRThreadPriority)new_priority);
+}
+
+PRThread *RCThread::Self()
+{
+ return PR_GetCurrentThread();
+}
+
+RCThread::Scope RCThread::GetScope() const
+{
+ return (RCThread::Scope)PR_GetThreadScope(identity);
+}
+
+RCThread::State RCThread::GetState() const
+{
+ return (RCThread::State)PR_GetThreadState(identity);
+}
+
+RCThread::Priority RCThread::GetPriority() const
+{
+ return (RCThread::Priority)PR_GetThreadPriority(identity);
+}
+
+static void _rc_PDDestructor(RCThreadPrivateData* privateData)
+{
+ PR_ASSERT(NULL != privateData);
+ privateData->Release();
+}
+
+static PRThreadPrivateDTOR _tpd_dtor = (PRThreadPrivateDTOR)_rc_PDDestructor;
+
+PRStatus RCThread::NewPrivateIndex(PRUintn* index)
+{
+ return PR_NewThreadPrivateIndex(index, _tpd_dtor);
+}
+
+PRStatus RCThread::SetPrivateData(PRUintn index)
+{
+ return PR_SetThreadPrivate(index, NULL);
+}
+
+PRStatus RCThread::SetPrivateData(PRUintn index, RCThreadPrivateData* data)
+{
+ return PR_SetThreadPrivate(index, data);
+}
+
+RCThreadPrivateData* RCThread::GetPrivateData(PRUintn index)
+{
+ return (RCThreadPrivateData*)PR_GetThreadPrivate(index);
+}
+
+PRStatus RCThread::Sleep(const RCInterval& ticks)
+{
+ PRIntervalTime tmo = ticks;
+ return PR_Sleep(tmo);
+}
+
+RCPrimordialThread *RCThread::WrapPrimordialThread()
+{
+ /*
+ ** This needs to take more care in insuring that the thread
+ ** being wrapped is really the primordial thread. This code
+ ** is assuming that the caller is the primordial thread, and
+ ** there's nothing to insure that.
+ */
+ if (NULL == primordial)
+ {
+ /* it doesn't have to be perfect */
+ RCPrimordialThread *me = new RCPrimordialThread();
+ PR_ASSERT(NULL != me);
+ if (NULL == primordial)
+ {
+ primordial = me;
+ me->execution = RCThread::ex_started;
+ me->identity = PR_GetCurrentThread();
+ }
+ else {
+ delete me; /* somebody beat us to it */
+ }
+ }
+ return primordial;
+} /* RCThread::WrapPrimordialThread */
+
+RCPrimordialThread::RCPrimordialThread(): RCThread() { }
+
+RCPrimordialThread::~RCPrimordialThread() { }
+
+void RCPrimordialThread::RootFunction()
+{
+ PR_NOT_REACHED("Primordial thread calling root function");
+} /* RCPrimordialThread::RootFunction */
+
+PRStatus RCPrimordialThread::Cleanup() {
+ return PR_Cleanup();
+}
+
+PRStatus RCPrimordialThread::SetVirtualProcessors(PRIntn count)
+{
+ PR_SetConcurrency(count);
+ return PR_SUCCESS;
+} /* SetVirutalProcessors */
+
+RCThreadPrivateData::RCThreadPrivateData() { }
+
+RCThreadPrivateData::RCThreadPrivateData(
+ const RCThreadPrivateData& him) { }
+
+RCThreadPrivateData::~RCThreadPrivateData() { }
+
+/* RCThread.c */
+
diff --git a/nsprpub/pr/src/cplus/rcthread.h b/nsprpub/pr/src/cplus/rcthread.h
new file mode 100644
index 0000000000..b7716d4b99
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rcthread.h
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* RCThread.h */
+
+#if defined(_RCTHREAD_H)
+#else
+#define _RCTHREAD_H
+
+#include "rcbase.h"
+
+#include <prthread.h>
+
+class RCInterval;
+
+class PR_IMPLEMENT(RCThreadPrivateData)
+{
+public:
+ RCThreadPrivateData();
+ RCThreadPrivateData(const RCThreadPrivateData&);
+
+ virtual ~RCThreadPrivateData();
+
+ virtual void Release() = 0;
+
+}; /* RCThreadPrivateData */
+
+class PR_IMPLEMENT(RCThread): public RCBase
+{
+public:
+
+ typedef enum
+ {
+ local = PR_LOCAL_THREAD, global = PR_GLOBAL_THREAD
+ } Scope;
+
+ typedef enum
+ {
+ joinable = PR_JOINABLE_THREAD, unjoinable = PR_UNJOINABLE_THREAD
+ } State;
+
+ typedef enum
+ {
+ first = PR_PRIORITY_FIRST,
+ low = PR_PRIORITY_LOW,
+ normal = PR_PRIORITY_NORMAL,
+ high = PR_PRIORITY_HIGH,
+ urgent = PR_PRIORITY_URGENT,
+ last = PR_PRIORITY_LAST
+ } Priority;
+
+ /*
+ * Create a new thread, providing scope and joinability state.
+ */
+ RCThread(Scope scope, State state, PRUint32 stackSize=0);
+
+ /*
+ * New threads are created in a suspended state. It must be 'started"
+ * before it begins execution in the class' defined 'RootFunction()'.
+ */
+ virtual PRStatus Start();
+
+ /*
+ * If a thread is created joinable, then the thread's object exists
+ * until join is called. The thread that calls join will block until
+ * the target thread returns from it's root function.
+ */
+ virtual PRStatus Join();
+
+ /*
+ * The priority of a newly created thread is the same as the creator.
+ * The priority may be changed either by the new thread itself, by
+ * the creator or any other arbitrary thread.
+ */
+ virtual void SetPriority(Priority newPriority);
+
+
+ /*
+ * Interrupt another thread, causing it to stop what it
+ * is doing and return with a well known error code.
+ */
+ virtual PRStatus Interrupt();
+
+ /*
+ * And in case a thread was interrupted and didn't get a chance
+ * to have the notification delivered, a way to cancel the pending
+ * status.
+ */
+ static void ClearInterrupt();
+
+ /*
+ * Methods to discover the attributes of an existing thread.
+ */
+ static PRThread *Self();
+ Scope GetScope() const;
+ State GetState() const;
+ Priority GetPriority() const;
+
+ /*
+ * Thread private data
+ */
+ static PRStatus NewPrivateIndex(PRUintn* index);
+
+ /*
+ * Getting it - if you want to modify, make a copy
+ */
+ static RCThreadPrivateData* GetPrivateData(PRUintn index);
+
+ /*
+ * Setting it to <empty> - deletes existing data
+ */
+ static PRStatus SetPrivateData(PRUintn index);
+
+ /*
+ * Setting it - runtime will make a copy, freeing old iff necessary
+ */
+ static PRStatus SetPrivateData(PRUintn index, RCThreadPrivateData* data);
+
+ /*
+ * Scheduling control
+ */
+ static PRStatus Sleep(const RCInterval& ticks);
+
+ friend void nas_Root(void*);
+ friend class RCPrimordialThread;
+protected:
+
+ /*
+ * The instantiator of a class must not call the destructor. The base
+ * implementation of Join will, and if the thread is created unjoinable,
+ * then the code that called the RootFunction will call the desctructor.
+ */
+ virtual ~RCThread();
+
+private:
+
+ /*
+ * This is where a newly created thread begins execution. Returning
+ * from this function is equivalent to terminating the thread.
+ */
+ virtual void RootFunction() = 0;
+
+ PRThread *identity;
+
+ /* Threads are unstarted until started - pretty startling */
+ enum {ex_unstarted, ex_started} execution;
+
+ /* There is no public default constructor or copy constructor */
+ RCThread();
+ RCThread(const RCThread&);
+
+ /* And there is no assignment operator */
+ void operator=(const RCThread&);
+
+public:
+ static RCPrimordialThread *WrapPrimordialThread();
+
+};
+
+/*
+** class RCPrimordialThread
+*/
+class PR_IMPLEMENT(RCPrimordialThread): public RCThread
+{
+public:
+ /*
+ ** The primordial thread can (optionally) wait for all created
+ ** threads to terminate before allowing the process to exit.
+ ** Not calling Cleanup() before returning from main() will cause
+ ** the immediate termination of the entire process, including
+ ** any running threads.
+ */
+ static PRStatus Cleanup();
+
+ /*
+ ** Only the primordial thread is allowed to adjust the number of
+ ** virtual processors of the runtime. It's a lame security thing.
+ */
+ static PRStatus SetVirtualProcessors(PRIntn count=10);
+
+ friend class RCThread;
+private:
+ /*
+ ** None other than the runtime can create of destruct
+ ** a primordial thread. It is fabricated by the runtime
+ ** to wrap the thread that initiated the application.
+ */
+ RCPrimordialThread();
+ ~RCPrimordialThread();
+ void RootFunction();
+}; /* RCPrimordialThread */
+
+#endif /* defined(_RCTHREAD_H) */
diff --git a/nsprpub/pr/src/cplus/rctime.cpp b/nsprpub/pr/src/cplus/rctime.cpp
new file mode 100644
index 0000000000..2c9823e15b
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rctime.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Class implementation for calendar time routines (ref: prtime.h)
+*/
+
+#include "rctime.h"
+
+RCTime::~RCTime() { }
+
+RCTime::RCTime(PRTime time): RCBase() {
+ gmt = time;
+}
+RCTime::RCTime(const RCTime& his): RCBase() {
+ gmt = his.gmt;
+}
+RCTime::RCTime(RCTime::Current): RCBase() {
+ gmt = PR_Now();
+}
+RCTime::RCTime(const PRExplodedTime& time): RCBase()
+{
+ gmt = PR_ImplodeTime(&time);
+}
+
+void RCTime::operator=(const PRExplodedTime& time)
+{
+ gmt = PR_ImplodeTime(&time);
+}
+
+RCTime RCTime::operator+(const RCTime& his)
+{
+ RCTime sum(gmt + his.gmt);
+ return sum;
+}
+
+RCTime RCTime::operator-(const RCTime& his)
+{
+ RCTime difference(gmt - his.gmt);
+ return difference;
+}
+
+RCTime RCTime::operator/(PRUint64 his)
+{
+ RCTime quotient(gmt / gmt);
+ return quotient;
+}
+
+RCTime RCTime::operator*(PRUint64 his)
+{
+ RCTime product(gmt * his);
+ return product;
+}
+
diff --git a/nsprpub/pr/src/cplus/rctime.h b/nsprpub/pr/src/cplus/rctime.h
new file mode 100644
index 0000000000..3752a3d53d
--- /dev/null
+++ b/nsprpub/pr/src/cplus/rctime.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Class definitions for calendar time routines (ref: prtime.h)
+*/
+
+#if defined(_RCTIME_H)
+#else
+#define _RCTIME_H
+
+#include "rcbase.h"
+
+#include <prtime.h>
+
+/*
+** Class: RCTime (ref: prtime.h)
+**
+** RCTimes are objects that are intended to be used to represent calendar
+** times. They maintain units internally as microseconds since the defined
+** epoch (midnight, January 1, 1970, GMT). Conversions to and from external
+** formats (PRExplodedTime) are available.
+**
+** In general, NCTimes possess normal algebretic capabilities.
+*/
+
+class PR_IMPLEMENT(RCTime): public RCBase
+{
+public:
+ typedef enum {now} Current;
+
+ RCTime(); /* leaves the object unitialized */
+ RCTime(Current); /* initializes to current system time */
+ RCTime(const RCTime&); /* copy constructor */
+ RCTime(const PRExplodedTime&); /* construction from exploded representation */
+
+ virtual ~RCTime();
+
+ /* assignment operators */
+ void operator=(const RCTime&);
+ void operator=(const PRExplodedTime&);
+
+ /* comparitive operators */
+ PRBool operator<(const RCTime&);
+ PRBool operator>(const RCTime&);
+ PRBool operator<=(const RCTime&);
+ PRBool operator>=(const RCTime&);
+ PRBool operator==(const RCTime&);
+
+ /* associative operators */
+ RCTime operator+(const RCTime&);
+ RCTime operator-(const RCTime&);
+ RCTime& operator+=(const RCTime&);
+ RCTime& operator-=(const RCTime&);
+
+ /* multiply and divide operators */
+ RCTime operator/(PRUint64);
+ RCTime operator*(PRUint64);
+ RCTime& operator/=(PRUint64);
+ RCTime& operator*=(PRUint64);
+
+ void Now(); /* assign current time to object */
+
+private:
+ PRTime gmt;
+
+public:
+
+ RCTime(PRTime); /* construct from raw PRTime */
+ void operator=(PRTime); /* assign from raw PRTime */
+ operator PRTime() const; /* extract internal representation */
+}; /* RCTime */
+
+inline RCTime::RCTime(): RCBase() { }
+
+inline void RCTime::Now() {
+ gmt = PR_Now();
+}
+inline RCTime::operator PRTime() const {
+ return gmt;
+}
+
+inline void RCTime::operator=(PRTime his) {
+ gmt = his;
+}
+inline void RCTime::operator=(const RCTime& his) {
+ gmt = his.gmt;
+}
+
+inline PRBool RCTime::operator<(const RCTime& his)
+{
+ return (gmt < his.gmt) ? PR_TRUE : PR_FALSE;
+}
+inline PRBool RCTime::operator>(const RCTime& his)
+{
+ return (gmt > his.gmt) ? PR_TRUE : PR_FALSE;
+}
+inline PRBool RCTime::operator<=(const RCTime& his)
+{
+ return (gmt <= his.gmt) ? PR_TRUE : PR_FALSE;
+}
+inline PRBool RCTime::operator>=(const RCTime& his)
+{
+ return (gmt >= his.gmt) ? PR_TRUE : PR_FALSE;
+}
+inline PRBool RCTime::operator==(const RCTime& his)
+{
+ return (gmt == his.gmt) ? PR_TRUE : PR_FALSE;
+}
+
+inline RCTime& RCTime::operator+=(const RCTime& his)
+{
+ gmt += his.gmt;
+ return *this;
+}
+inline RCTime& RCTime::operator-=(const RCTime& his)
+{
+ gmt -= his.gmt;
+ return *this;
+}
+inline RCTime& RCTime::operator/=(PRUint64 his)
+{
+ gmt /= his;
+ return *this;
+}
+inline RCTime& RCTime::operator*=(PRUint64 his)
+{
+ gmt *= his;
+ return *this;
+}
+
+#endif /* defined(_RCTIME_H) */
+
+/* RCTime.h */
diff --git a/nsprpub/pr/src/cplus/tests/Makefile.in b/nsprpub/pr/src/cplus/tests/Makefile.in
new file mode 100644
index 0000000000..df48276569
--- /dev/null
+++ b/nsprpub/pr/src/cplus/tests/Makefile.in
@@ -0,0 +1,199 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+#! gmake
+
+MOD_DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+ifeq ($(OS_TARGET), WIN16)
+OS_CFLAGS = $(OS_EXE_CFLAGS)
+endif
+
+CXXSRCS = \
+ ranfile.cpp \
+ thread.cpp \
+ interval.cpp \
+ time.cpp \
+ fileio.cpp \
+ switch.cpp \
+ tpd.cpp \
+ $(NULL)
+
+OBJS = $(addprefix $(OBJDIR)/,$(CXXSRCS:.cpp=.$(OBJ_SUFFIX)))
+
+ifeq (,$(filter-out WINNT OS2,$(OS_ARCH)))
+PROG_SUFFIX = .exe
+else
+PROG_SUFFIX =
+endif
+
+PROGS = $(addprefix $(OBJDIR)/, $(CXXSRCS:.cpp=$(PROG_SUFFIX)))
+
+TARGETS = $(PROGS) $(OBJS)
+
+INCLUDES = -I.. -I$(dist_includedir)
+
+# Setting the variables LDOPTS and LIBPR. We first initialize
+# them to the default values, then adjust them for some platforms.
+LDOPTS = -L$(dist_libdir)
+LIBPR = -lnspr$(MOD_MAJOR_VERSION)
+LIBPL = -lplc$(MOD_MAJOR_VERSION)
+
+# Solaris
+ifeq ($(OS_ARCH), SunOS)
+ ifdef NS_USE_GCC
+ LDOPTS += -Xlinker -R -Xlinker $(PWD)/$(dist_libdir)
+ else
+ LDOPTS += -R $(PWD)/$(dist_libdir)
+ endif
+
+# SunOS 5.5 needs to link with -lpthread, even though we already
+# linked with this system library when we built libnspr.so.
+ ifeq ($(OS_RELEASE), 5.5)
+ ifdef USE_PTHREADS
+ EXTRA_LIBS = -lpthread
+ endif
+ endif
+endif # SunOS
+
+ifeq ($(OS_ARCH), WINNT)
+ifeq ($(OS_TARGET), WIN16)
+ LIBPR = $(dist_libdir)/nspr$(MOD_MAJOR_VERSION).lib
+ LIBPL = $(dist_libdir)/plc$(MOD_MAJOR_VERSION).lib
+else
+ LDOPTS = -NOLOGO -DEBUG -INCREMENTAL:NO
+ LIBPR = $(dist_libdir)/libnspr$(MOD_MAJOR_VERSION).$(LIB_SUFFIX)
+ LIBPL = $(dist_libdir)/libplc$(MOD_MAJOR_VERSION).$(LIB_SUFFIX)
+endif
+endif
+
+ifeq ($(OS_ARCH),OS2)
+LDOPTS += -Zomf -Zlinker /PM:VIO -lstdcpp
+endif
+
+ifneq ($(OS_ARCH), WINNT)
+PWD = $(shell pwd)
+endif
+
+ifeq ($(OS_ARCH), HP-UX)
+ LDOPTS += -Wl,+s,+b,$(PWD)/$(dist_libdir)
+endif
+
+# AIX
+ifeq ($(OS_ARCH),AIX)
+ LDOPTS += -blibpath:$(PWD)/$(dist_libdir):/usr/lib:/lib
+ ifeq ($(OS_ARCH)$(OS_RELEASE),AIX4.1)
+ LIBPR = -lnspr$(MOD_MAJOR_VERSION)_shr
+ LIBPLC = -lplc$(MOD_MAJOR_VERSION)_shr
+ else
+ LDOPTS += -brtl
+ EXTRA_LIBS = -ldl
+ endif
+endif
+
+ifeq ($(OS_ARCH), Linux)
+ ifeq ($(OS_RELEASE), 1.2)
+ EXTRA_LIBS = -ldl
+ else
+ LDOPTS += -Xlinker -rpath $(PWD)/$(dist_libdir)
+ ifeq ($(USE_PTHREADS),1)
+ EXTRA_LIBS = -lpthread
+ endif
+ endif
+endif
+
+ifeq ($(OS_ARCH), SCO_SV)
+# SCO Unix needs to link against -lsocket again even though we
+# already linked with these system libraries when we built libnspr.so.
+EXTRA_LIBS = -lsocket
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+export LD_RUN_PATH = $(PWD)/$(dist_libdir)
+endif
+
+ifeq ($(OS_ARCH), UNIXWARE)
+export LD_RUN_PATH = $(PWD)/$(dist_libdir)
+endif
+
+#####################################################
+#
+# The rules
+#
+#####################################################
+
+include $(topsrcdir)/config/rules.mk
+
+AIX_PRE_4_2 = 0
+ifeq ($(OS_ARCH),AIX)
+ifneq ($(OS_RELEASE),4.2)
+ifneq ($(USE_PTHREADS), 1)
+#AIX_PRE_4_2 = 1
+endif
+endif
+endif
+
+ifeq ($(AIX_PRE_4_2),1)
+
+# AIX releases prior to 4.2 need a special two-step linking hack
+# in order to both override the system select() and be able to
+# get at the original system select().
+#
+# We use a pattern rule in ns/nspr20/config/rules.mk to generate
+# the .$(OBJ_SUFFIX) file from the .c source file, then do the
+# two-step linking hack below.
+
+$(OBJDIR)/%: $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ rm -f $@ $(AIX_TMP)
+ $(CC) $(AIX_LINK_OPTS) -o $(AIX_TMP) $< $(dist_libdir)/libnspr$(MOD_MAJOR_VERSION).a
+ $(CC) -o $@ $(AIX_TMP) $(AIX_WRAP)
+ rm -f $(AIX_TMP)
+
+else
+
+# All platforms that are not AIX pre-4.2.
+
+$(OBJDIR)/%$(PROG_SUFFIX): $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ifeq ($(OS_ARCH), WINNT)
+ifeq ($(OS_TARGET),WIN16)
+ echo system windows >w16link
+ echo option map >>w16link
+ echo option stack=10K >>w16link
+ echo option heapsize=32K >>w16link
+ echo debug $(DEBUGTYPE) all >>w16link
+ echo name $@ >>w16link
+ echo file >>w16link
+ echo $< >>w16link
+ echo library >>w16link
+ echo $(LIBPR), >>w16link
+ echo $(LIBPL), >>w16link
+ echo winsock.lib >>w16link
+ wlink @w16link.
+else
+ link $(LDOPTS) $< $(LIBPR) $(LIBPL) ws2_32.lib -out:$@
+endif
+else
+ifeq ($(OS_ARCH),OS2)
+ $(LINK) $(LDOPTS) $< $(LIBGC) $(LIBPLC) $(LIBPR) $(OS_LIBS) $(EXTRA_LIBS) -o $@
+else
+ $(CCC) $(XCFLAGS) $< $(LDOPTS) $(LIBPR) $(LIBPL) $(EXTRA_LIBS) -o $@
+endif
+endif
+endif
+
+export:: $(TARGETS)
+clean::
+ rm -f $(TARGETS)
+
diff --git a/nsprpub/pr/src/cplus/tests/fileio.cpp b/nsprpub/pr/src/cplus/tests/fileio.cpp
new file mode 100644
index 0000000000..fdffe99972
--- /dev/null
+++ b/nsprpub/pr/src/cplus/tests/fileio.cpp
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* fileio.cpp - a test program */
+
+#include "rcfileio.h"
+
+#include <prlog.h>
+#include <prprf.h>
+
+#define DEFAULT_ITERATIONS 100
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRStatus rv;
+ RCFileIO fd;
+ RCFileInfo info;
+ rv = fd.Open("filio.dat", PR_CREATE_FILE, 0666);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = fd.FileInfo(&info);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = fd.Delete("filio.dat");
+ PR_ASSERT(PR_SUCCESS == rv);
+ fd.Close();
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ return 0;
+} /* main */
+
+/* interval.cpp */
+
diff --git a/nsprpub/pr/src/cplus/tests/interval.cpp b/nsprpub/pr/src/cplus/tests/interval.cpp
new file mode 100644
index 0000000000..8acf506256
--- /dev/null
+++ b/nsprpub/pr/src/cplus/tests/interval.cpp
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* interval.cpp - a test program */
+
+#include "rclock.h"
+#include "rcthread.h"
+#include "rcinrval.h"
+#include "rccv.h"
+
+#include <prio.h>
+#include <prlog.h>
+#include <prprf.h>
+
+#define DEFAULT_ITERATIONS 100
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ RCLock ml;
+ PRStatus rv;
+ RCCondition cv(&ml);
+
+ RCInterval now, timeout, epoch, elapsed;
+ PRFileDesc *output = PR_GetSpecialFD(PR_StandardOutput);
+ PRIntn msecs, seconds, loops, iterations = DEFAULT_ITERATIONS;
+
+ /* slow, agonizing waits */
+ for (seconds = 0; seconds < 10; ++seconds)
+ {
+ timeout = RCInterval::FromSeconds(seconds);
+ cv.SetTimeout(timeout);
+ {
+ RCEnter lock(&ml);
+
+ epoch.SetToNow();
+
+ rv = cv.Wait();
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ now = RCInterval(RCInterval::now);
+ elapsed = now - epoch;
+ }
+
+ PR_fprintf(
+ output, "Waiting %u seconds took %s%u milliseconds\n",
+ seconds, ((elapsed < timeout)? "**" : ""),
+ elapsed.ToMilliseconds());
+ }
+
+ /* more slow, agonizing sleeps */
+ for (seconds = 0; seconds < 10; ++seconds)
+ {
+ timeout = RCInterval::FromSeconds(seconds);
+ {
+ epoch.SetToNow();
+
+ rv = RCThread::Sleep(timeout);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ now = RCInterval(RCInterval::now);
+ elapsed = now - epoch;
+ }
+
+ PR_fprintf(
+ output, "Sleeping %u seconds took %s%u milliseconds\n",
+ seconds, ((elapsed < timeout)? "**" : ""),
+ elapsed.ToMilliseconds());
+ }
+
+ /* fast, spritely little devils */
+ for (msecs = 10; msecs < 100; msecs += 10)
+ {
+ timeout = RCInterval::FromMilliseconds(msecs);
+ cv.SetTimeout(timeout);
+ {
+ RCEnter lock(&ml);
+
+ epoch.SetToNow();
+
+ for (loops = 0; loops < iterations; ++loops)
+ {
+ rv = cv.Wait();
+ PR_ASSERT(PR_SUCCESS == rv);
+ }
+
+ now = RCInterval(RCInterval::now);
+ elapsed = now - epoch;
+ }
+ elapsed /= iterations;
+
+ PR_fprintf(
+ output, "Waiting %u msecs took %s%u milliseconds average\n",
+ msecs, ((elapsed < timeout)? "**" : ""), elapsed.ToMilliseconds());
+ }
+ return 0;
+} /* main */
+
+/* interval.cpp */
+
diff --git a/nsprpub/pr/src/cplus/tests/ranfile.cpp b/nsprpub/pr/src/cplus/tests/ranfile.cpp
new file mode 100644
index 0000000000..0fb4c5d23a
--- /dev/null
+++ b/nsprpub/pr/src/cplus/tests/ranfile.cpp
@@ -0,0 +1,449 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Contact: AOF<mailto:freier@netscape.com>
+**
+** Name: ranfile.c
+**
+** Description: Test to hammer on various components of NSPR
+** Modification History:
+** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include <plgetopt.h>
+#include <prprf.h>
+#include <prio.h>
+
+#include "rccv.h"
+#include "rcthread.h"
+#include "rcfileio.h"
+#include "rclock.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static PRFileDesc *output;
+static PRIntn debug_mode = 0;
+static PRIntn failed_already = 0;
+
+class HammerData
+{
+public:
+ typedef enum {
+ sg_go, sg_stop, sg_done
+ } Action;
+ typedef enum {
+ sg_okay, sg_open, sg_close, sg_delete, sg_write, sg_seek
+ } Problem;
+
+ virtual ~HammerData();
+ HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip);
+ virtual PRUint32 Random();
+
+ Action action;
+ Problem problem;
+ PRUint32 writes;
+ RCInterval timein;
+ friend class Hammer;
+private:
+ RCLock *ml;
+ RCCondition *cv;
+ PRUint32 limit;
+
+ PRFloat64 seed;
+}; /* HammerData */
+
+class Hammer: public HammerData, public RCThread
+{
+public:
+ virtual ~Hammer();
+ Hammer(RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip);
+
+private:
+ void RootFunction();
+
+};
+
+static PRInt32 pageSize = 1024;
+static const char* baseName = "./";
+static const char *programName = "Random File";
+
+/***********************************************************************
+** PRIVATE FUNCTION: Random
+** DESCRIPTION:
+** Generate a pseudo-random number
+** INPUTS: None
+** OUTPUTS: None
+** RETURN: A pseudo-random unsigned number, 32-bits wide
+** SIDE EFFECTS:
+** Updates random seed (a static)
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM:
+** Uses the current interval timer value, promoted to a 64 bit
+** float as a multiplier for a static residue (which begins
+** as an uninitialized variable). The result is bits [16..48)
+** of the product. Seed is then updated with the return value
+** promoted to a float-64.
+***********************************************************************/
+PRUint32 HammerData::Random()
+{
+ PRUint32 rv;
+ PRUint64 shift;
+ RCInterval now = RCInterval(RCInterval::now);
+ PRFloat64 random = seed * (PRFloat64)((PRIntervalTime)now);
+ LL_USHR(shift, *((PRUint64*)&random), 16);
+ LL_L2UI(rv, shift);
+ seed = (PRFloat64)rv;
+ return rv;
+} /* HammerData::Random */
+
+Hammer::~Hammer() { }
+
+Hammer::Hammer(
+ RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip):
+ HammerData(lock, cond, clip), RCThread(scope, RCThread::joinable, 0) { }
+
+HammerData::~HammerData() { }
+
+HammerData::HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip)
+{
+ ml = lock;
+ cv = cond;
+ writes = 0;
+ limit = clip;
+ seed = 0x58a9382;
+ action = HammerData::sg_go;
+ problem = HammerData::sg_okay;
+ timein = RCInterval(RCInterval::now);
+} /* HammerData::HammerData */
+
+
+/***********************************************************************
+** PRIVATE FUNCTION: Hammer::RootFunction
+** DESCRIPTION:
+** Hammer on the file I/O system
+** INPUTS: A pointer to the thread's private data
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+** Creates, accesses and deletes a file
+** RESTRICTIONS:
+** (Currently) must have file create permission in "/usr/tmp".
+** MEMORY: NA
+** ALGORITHM:
+** This function is a root of a thread
+** 1) Creates a (hopefully) unique file in /usr/tmp/
+** 2) Writes a zero to a random number of sequential pages
+** 3) Closes the file
+** 4) Reopens the file
+** 5) Seeks to a random page within the file
+** 6) Writes a one byte on that page
+** 7) Repeat steps [5..6] for each page in the file
+** 8) Close and delete the file
+** 9) Repeat steps [1..8] until told to stop
+** 10) Notify complete and return
+***********************************************************************/
+void Hammer::RootFunction()
+{
+ PRUint32 index;
+ RCFileIO file;
+ char filename[30];
+ const char zero = 0;
+ PRStatus rv = PR_SUCCESS;
+
+ limit = (Random() % limit) + 1;
+
+ (void)sprintf(filename, "%ssg%04p.dat", baseName, this);
+
+ if (debug_mode) {
+ PR_fprintf(output, "Starting work on %s\n", filename);
+ }
+
+ while (PR_TRUE)
+ {
+ PRUint64 bytes;
+ PRUint32 minor = (Random() % limit) + 1;
+ PRUint32 random = (Random() % limit) + 1;
+ PRUint32 pages = (Random() % limit) + 10;
+ while (minor-- > 0)
+ {
+ problem = sg_okay;
+ if (action != sg_go) {
+ goto finished;
+ }
+ problem = sg_open;
+ rv = file.Open(filename, PR_RDWR|PR_CREATE_FILE, 0666);
+ if (PR_FAILURE == rv) {
+ goto finished;
+ }
+ for (index = 0; index < pages; index++)
+ {
+ problem = sg_okay;
+ if (action != sg_go) {
+ goto close;
+ }
+ problem = sg_seek;
+ bytes = file.Seek(pageSize * index, RCFileIO::set);
+ if (bytes != pageSize * index) {
+ goto close;
+ }
+ problem = sg_write;
+ bytes = file.Write(&zero, sizeof(zero));
+ if (bytes <= 0) {
+ goto close;
+ }
+ writes += 1;
+ }
+ problem = sg_close;
+ rv = file.Close();
+ if (rv != PR_SUCCESS) {
+ goto purge;
+ }
+
+ problem = sg_okay;
+ if (action != sg_go) {
+ goto purge;
+ }
+
+ problem = sg_open;
+ rv = file.Open(filename, PR_RDWR, 0666);
+ if (PR_FAILURE == rv) {
+ goto finished;
+ }
+ for (index = 0; index < pages; index++)
+ {
+ problem = sg_okay;
+ if (action != sg_go) {
+ goto close;
+ }
+ problem = sg_seek;
+ bytes = file.Seek(pageSize * index, RCFileIO::set);
+ if (bytes != pageSize * index) {
+ goto close;
+ }
+ problem = sg_write;
+ bytes = file.Write(&zero, sizeof(zero));
+ if (bytes <= 0) {
+ goto close;
+ }
+ writes += 1;
+ random = (random + 511) % pages;
+ }
+ problem = sg_close;
+ rv = file.Close();
+ if (rv != PR_SUCCESS) {
+ goto purge;
+ }
+ problem = sg_delete;
+ rv = file.Delete(filename);
+ if (rv != PR_SUCCESS) {
+ goto finished;
+ }
+ }
+ }
+
+close:
+ (void)file.Close();
+purge:
+ (void)file.Delete(filename);
+finished:
+ RCEnter scope(ml);
+ action = HammerData::sg_done;
+ cv->Notify();
+
+ if (debug_mode) {
+ PR_fprintf(output, "Ending work on %s\n", filename);
+ }
+
+ return;
+} /* Hammer::RootFunction */
+
+static Hammer* hammer[100];
+/***********************************************************************
+** PRIVATE FUNCTION: main
+** DESCRIPTION:
+** Hammer on the file I/O system
+** INPUTS: The usual argc and argv
+** argv[0] - program name (not used)
+** argv[1] - the number of virtual_procs to execute the major loop
+** argv[2] - the number of threads to toss into the batch
+** argv[3] - the clipping number applied to randoms
+** default values: max_virtual_procs = 2, threads = 10, limit = 57
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+** Creates, accesses and deletes lots of files
+** RESTRICTIONS:
+** (Currently) must have file create permission in "/usr/tmp".
+** MEMORY: NA
+** ALGORITHM:
+** 1) Fork a "Thread()"
+** 2) Wait for 'interleave' seconds
+** 3) For [0..'threads') repeat [1..2]
+** 4) Mark all objects to stop
+** 5) Collect the threads, accumulating the results
+** 6) For [0..'max_virtual_procs') repeat [1..5]
+** 7) Print accumulated results and exit
+**
+** Characteristic output (from IRIX)
+** Random File: Using max_virtual_procs = 2, threads = 10, limit = 57
+** Random File: [min [avg] max] writes/sec average
+***********************************************************************/
+PRIntn main (PRIntn argc, char *argv[])
+{
+ RCLock ml;
+ PLOptStatus os;
+ RCCondition cv(&ml);
+ PRUint32 writesMax = 0, durationTot = 0;
+ RCThread::Scope thread_scope = RCThread::local;
+ PRUint32 writes, writesMin = 0x7fffffff, writesTot = 0;
+ PRIntn active, poll, limit = 0, max_virtual_procs = 0, threads = 0, virtual_procs;
+ RCInterval interleave(RCInterval::FromMilliseconds(10000)), duration(0);
+
+ const char *where[] = {"okay", "open", "close", "delete", "write", "seek"};
+
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:t:i:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 0:
+ baseName = opt->value;
+ break;
+ case 'G': /* global threads */
+ thread_scope = RCThread::global;
+ break;
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'l': /* limiting number */
+ limit = atoi(opt->value);
+ break;
+ case 't': /* number of threads */
+ threads = atoi(opt->value);
+ break;
+ case 'i': /* iteration counter */
+ max_virtual_procs = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ output = PR_GetSpecialFD(PR_StandardOutput);
+
+ /* main test */
+
+ cv.SetTimeout(interleave);
+
+ if (max_virtual_procs == 0) {
+ max_virtual_procs = 2;
+ }
+ if (limit == 0) {
+ limit = 57;
+ }
+ if (threads == 0) {
+ threads = 10;
+ }
+
+ if (debug_mode) PR_fprintf(output,
+ "%s: Using %d virtual processors, %d threads, limit = %d and %s threads\n",
+ programName, max_virtual_procs, threads, limit,
+ (thread_scope == RCThread::local) ? "LOCAL" : "GLOBAL");
+
+ for (virtual_procs = 0; virtual_procs < max_virtual_procs; ++virtual_procs)
+ {
+ if (debug_mode)
+ PR_fprintf(output,
+ "%s: Setting number of virtual processors to %d\n",
+ programName, virtual_procs + 1);
+ RCPrimordialThread::SetVirtualProcessors(virtual_procs + 1);
+ for (active = 0; active < threads; active++)
+ {
+ hammer[active] = new Hammer(thread_scope, &ml, &cv, limit);
+ hammer[active]->Start(); /* then make it roll */
+ RCThread::Sleep(interleave); /* start them slowly */
+ }
+
+ /*
+ * The last thread started has had the opportunity to run for
+ * 'interleave' seconds. Now gather them all back in.
+ */
+ {
+ RCEnter scope(&ml);
+ for (poll = 0; poll < threads; poll++)
+ {
+ if (hammer[poll]->action == HammerData::sg_go) { /* don't overwrite done */
+ hammer[poll]->action = HammerData::sg_stop; /* ask him to stop */
+ }
+ }
+ }
+
+ while (active > 0)
+ {
+ for (poll = 0; poll < threads; poll++)
+ {
+ ml.Acquire();
+ while (hammer[poll]->action < HammerData::sg_done) {
+ cv.Wait();
+ }
+ ml.Release();
+
+ if (hammer[poll]->problem == HammerData::sg_okay)
+ {
+ duration = RCInterval(RCInterval::now) - hammer[poll]->timein;
+ writes = hammer[poll]->writes * 1000 / duration;
+ if (writes < writesMin) {
+ writesMin = writes;
+ }
+ if (writes > writesMax) {
+ writesMax = writes;
+ }
+ writesTot += hammer[poll]->writes;
+ durationTot += duration;
+ }
+ else
+ {
+ if (debug_mode) PR_fprintf(output,
+ "%s: test failed %s after %ld seconds\n",
+ programName, where[hammer[poll]->problem], duration);
+ else {
+ failed_already=1;
+ }
+ }
+ active -= 1; /* this is another one down */
+ (void)hammer[poll]->Join();
+ hammer[poll] = NULL;
+ }
+ }
+ if (debug_mode) PR_fprintf(output,
+ "%s: [%ld [%ld] %ld] writes/sec average\n",
+ programName, writesMin,
+ writesTot * 1000 / durationTot, writesMax);
+ }
+
+ failed_already |= (PR_FAILURE == RCPrimordialThread::Cleanup());
+ PR_fprintf(output, "%s\n", (failed_already) ? "FAIL\n" : "PASS\n");
+ return failed_already;
+} /* main */
diff --git a/nsprpub/pr/src/cplus/tests/switch.cpp b/nsprpub/pr/src/cplus/tests/switch.cpp
new file mode 100644
index 0000000000..00de8e3064
--- /dev/null
+++ b/nsprpub/pr/src/cplus/tests/switch.cpp
@@ -0,0 +1,248 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: switch.cpp
+** Description: trying to time context switches
+*/
+
+#include "rccv.h"
+#include "rcinrval.h"
+#include "rclock.h"
+#include "rcthread.h"
+
+#include <prio.h>
+#include <prlog.h>
+#include <prprf.h>
+#include <plerror.h>
+#include <plgetopt.h>
+
+#include <stdlib.h>
+
+#define INNER_LOOPS 100
+#define DEFAULT_LOOPS 100
+#define DEFAULT_THREADS 10
+
+static PRFileDesc *debug_out = NULL;
+static PRBool debug_mode = PR_FALSE, verbosity = PR_FALSE, failed = PR_FALSE;
+
+class Home: public RCCondition
+{
+public:
+ virtual ~Home();
+ Home(Home *link, RCLock* ml);
+
+public:
+ Home *next;
+ RCLock* ml;
+ PRBool twiddle;
+}; /* Home */
+
+Home::~Home() { }
+
+Home::Home(Home *link, RCLock* lock): RCCondition(lock)
+{
+ ml = lock;
+ next = link;
+ twiddle = PR_FALSE;
+} /* Home::Home */
+
+class Shared: public Home, public RCThread
+{
+public:
+ Shared(RCThread::Scope scope, Home* link, RCLock* ml);
+
+private:
+ ~Shared();
+ void RootFunction();
+}; /* Shared */
+
+Shared::Shared(RCThread::Scope scope, Home* link, RCLock* lock):
+ Home(link, lock), RCThread(scope, RCThread::joinable) { }
+
+Shared::~Shared() { }
+
+void Shared::RootFunction()
+{
+ PRStatus status = PR_SUCCESS;
+ while (PR_SUCCESS == status)
+ {
+ RCEnter entry(ml);
+ while (twiddle && (PR_SUCCESS == status)) {
+ status = Wait();
+ }
+ if (verbosity) {
+ PR_fprintf(debug_out, "+");
+ }
+ twiddle = PR_TRUE;
+ next->twiddle = PR_FALSE;
+ next->Notify();
+ }
+} /* Shared::RootFunction */
+
+static void Help(void)
+{
+ debug_out = PR_STDOUT;
+
+ PR_fprintf(
+ debug_out, "Usage: >./switch [-d] [-c n] [-t n] [-T n] [-G]\n");
+ PR_fprintf(
+ debug_out, "-c n\tloops at thread level (default: %d)\n", DEFAULT_LOOPS);
+ PR_fprintf(
+ debug_out, "-t n\tnumber of threads (default: %d)\n", DEFAULT_THREADS);
+ PR_fprintf(debug_out, "-d\tturn on debugging output (default: FALSE)\n");
+ PR_fprintf(debug_out, "-v\tturn on verbose output (default: FALSE)\n");
+ PR_fprintf(debug_out, "-G n\tglobal threads only (default: FALSE)\n");
+ PR_fprintf(debug_out, "-C n\tconcurrency setting (default: 1)\n");
+} /* Help */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PLOptStatus os;
+ PRStatus status;
+ PRBool help = PR_FALSE;
+ PRUintn concurrency = 1;
+ RCThread::Scope thread_scope = RCThread::local;
+ PRUintn thread_count, inner_count, loop_count, average;
+ PRUintn thread_limit = DEFAULT_THREADS, loop_limit = DEFAULT_LOOPS;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hdvc:t:C:G");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'v': /* verbose mode */
+ verbosity = PR_TRUE;
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'c': /* loop counter */
+ loop_limit = atoi(opt->value);
+ break;
+ case 't': /* thread limit */
+ thread_limit = atoi(opt->value);
+ break;
+ case 'C': /* Concurrency limit */
+ concurrency = atoi(opt->value);
+ break;
+ case 'G': /* global threads only */
+ thread_scope = RCThread::global;
+ break;
+ case 'h': /* help message */
+ Help();
+ help = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (help) {
+ return -1;
+ }
+
+ if (PR_TRUE == debug_mode)
+ {
+ debug_out = PR_STDOUT;
+ PR_fprintf(debug_out, "Test parameters\n");
+ PR_fprintf(debug_out, "\tThreads involved: %d\n", thread_limit);
+ PR_fprintf(debug_out, "\tIteration limit: %d\n", loop_limit);
+ PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency);
+ PR_fprintf(
+ debug_out, "\tThread type: %s\n",
+ (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL");
+ }
+
+ /*
+ ** The interesting part starts here
+ */
+ RCLock lock;
+ Shared* shared;
+ Home home(NULL, &lock);
+ Home* link = &home;
+ RCInterval timein, timeout = 0;
+
+ /* Build up the string of objects */
+ for (thread_count = 1; thread_count <= thread_limit; ++thread_count)
+ {
+ shared = new Shared(thread_scope, link, &lock);
+ shared->Start(); /* make it run */
+ link = (Home*)shared;
+ }
+
+ /* Pass the message around the horn a few times */
+ for (loop_count = 1; loop_count <= loop_limit; ++loop_count)
+ {
+ timein.SetToNow();
+ for (inner_count = 0; inner_count < INNER_LOOPS; ++inner_count)
+ {
+ RCEnter entry(&lock);
+ home.twiddle = PR_TRUE;
+ shared->twiddle = PR_FALSE;
+ shared->Notify();
+ while (home.twiddle)
+ {
+ failed = (PR_FAILURE == home.Wait()) ? PR_TRUE : PR_FALSE;
+ }
+ }
+ timeout += (RCInterval(RCInterval::now) - timein);
+ }
+
+ /* Figure out how well we did */
+ if (debug_mode)
+ {
+ average = timeout.ToMicroseconds()
+ / (INNER_LOOPS * loop_limit * thread_count);
+ PR_fprintf(
+ debug_out, "Average switch times %d usecs for %d threads\n",
+ average, thread_limit);
+ }
+
+ /* Start reclamation process */
+ link = shared;
+ for (thread_count = 1; thread_count <= thread_limit; ++thread_count)
+ {
+ if (&home == link) {
+ break;
+ }
+ status = ((Shared*)link)->Interrupt();
+ if (PR_SUCCESS != status)
+ {
+ failed = PR_TRUE;
+ if (debug_mode) {
+ PL_FPrintError(debug_out, "Failed to interrupt");
+ }
+ }
+ link = link->next;
+ }
+
+ for (thread_count = 1; thread_count <= thread_limit; ++thread_count)
+ {
+ link = shared->next;
+ status = shared->Join();
+ if (PR_SUCCESS != status)
+ {
+ failed = PR_TRUE;
+ if (debug_mode) {
+ PL_FPrintError(debug_out, "Failed to join");
+ }
+ }
+ if (&home == link) {
+ break;
+ }
+ shared = (Shared*)link;
+ }
+
+ PR_fprintf(PR_STDOUT, ((failed) ? "FAILED\n" : "PASSED\n"));
+
+ failed |= (PR_SUCCESS == RCPrimordialThread::Cleanup());
+
+ return ((failed) ? 1 : 0);
+} /* main */
+
+/* switch.c */
diff --git a/nsprpub/pr/src/cplus/tests/thread.cpp b/nsprpub/pr/src/cplus/tests/thread.cpp
new file mode 100644
index 0000000000..ff01402d8d
--- /dev/null
+++ b/nsprpub/pr/src/cplus/tests/thread.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* thread.cpp - a test program */
+
+#include "rcthread.h"
+
+#include <prlog.h>
+
+#include <stdio.h>
+
+class TestThread: public RCThread
+{
+public:
+ TestThread(RCThread::State state, PRIntn count);
+
+ virtual void RootFunction();
+
+protected:
+ virtual ~TestThread();
+
+private:
+ PRUint32 mydata;
+};
+
+TestThread::~TestThread() { }
+
+TestThread::TestThread(RCThread::State state, PRIntn count):
+ RCThread(RCThread::global, state, 0) {
+ mydata = count;
+}
+
+void TestThread::RootFunction()
+{
+ SetPriority(RCThread::high);
+ printf("TestThread::RootFunction %d did it\n", mydata);
+} /* TestThread::RootFunction */
+
+class Foo1
+{
+public:
+ Foo1();
+ virtual ~Foo1();
+
+ TestThread *thread;
+ PRIntn data;
+};
+
+Foo1::Foo1()
+{
+ data = 0xafaf;
+ thread = new TestThread(RCThread::joinable, 0xafaf);
+ thread->Start();
+}
+
+Foo1::~Foo1()
+{
+ PRStatus rv = thread->Join();
+ PR_ASSERT(PR_SUCCESS == rv);
+} /* Foo1::~Foo1 */
+
+PRIntn main(PRIntn argc, char **agrv)
+{
+ PRStatus status;
+ PRIntn count = 100;
+ RCThread *thread[10];
+ while (--count > 0)
+ {
+ TestThread *thread = new TestThread(RCThread::joinable, count);
+ status = thread->Start(); /* have to remember to start it */
+ PR_ASSERT(PR_SUCCESS == status);
+ status = thread->Join(); /* this should work */
+ PR_ASSERT(PR_SUCCESS == status);
+ }
+ while (++count < 100)
+ {
+ TestThread *thread = new TestThread(RCThread::unjoinable, count);
+ status = thread->Start(); /* have to remember to start it */
+ PR_ASSERT(PR_SUCCESS == status);
+ }
+
+ {
+ Foo1 *foo1 = new Foo1();
+ PR_ASSERT(NULL != foo1);
+ delete foo1;
+ }
+
+ {
+ for (count = 0; count < 10; ++count)
+ {
+ thread[count] = new TestThread( RCThread::joinable, count);
+ status = thread[count]->Start(); /* have to remember to start it */
+ PR_ASSERT(PR_SUCCESS == status);
+ }
+ for (count = 0; count < 10; ++count)
+ {
+ PRStatus rv = thread[count]->Join();
+ PR_ASSERT(PR_SUCCESS == rv);
+ }
+ }
+
+ (void)RCPrimordialThread::Cleanup();
+
+ return 0;
+} /* main */
+
+/* thread.cpp */
+
diff --git a/nsprpub/pr/src/cplus/tests/time.cpp b/nsprpub/pr/src/cplus/tests/time.cpp
new file mode 100644
index 0000000000..ad27caaacc
--- /dev/null
+++ b/nsprpub/pr/src/cplus/tests/time.cpp
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* time.cpp - a test program */
+
+#include "rctime.h"
+
+#include <prlog.h>
+#include <prprf.h>
+
+#define DEFAULT_ITERATIONS 100
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ RCTime unitialized;
+ RCTime now(PR_Now());
+ RCTime current(RCTime::now);
+ PRTime time = current;
+
+ unitialized = now;
+ now.Now();
+
+ return 0;
+} /* main */
+
+/* time.cpp */
+
diff --git a/nsprpub/pr/src/cplus/tests/tpd.cpp b/nsprpub/pr/src/cplus/tests/tpd.cpp
new file mode 100644
index 0000000000..2a18c9cb11
--- /dev/null
+++ b/nsprpub/pr/src/cplus/tests/tpd.cpp
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: tpd.cpp
+** Description: Exercising the thread private data bailywick.
+*/
+
+#include "prlog.h"
+#include "prprf.h"
+#include "rcthread.h"
+
+#include <string.h>
+
+#include "plgetopt.h"
+
+/*
+** class MyThread
+*/
+class MyThread: public RCThread
+{
+public:
+ MyThread();
+
+private:
+ ~MyThread();
+ void RootFunction();
+}; /* MyThread */
+
+/*
+** class MyPrivateData
+*/
+class MyPrivateData: public RCThreadPrivateData
+{
+public:
+ virtual ~MyPrivateData();
+
+ MyPrivateData();
+ MyPrivateData(char*);
+ MyPrivateData(const MyPrivateData&);
+
+ void Release();
+
+private:
+ char *string;
+}; /* MyPrivateData */
+
+static PRUintn key[128];
+static PRIntn debug = 0;
+static PRBool failed = PR_FALSE;
+static PRBool should = PR_TRUE;
+static PRBool did = PR_TRUE;
+static PRFileDesc *fout = NULL;
+
+static void PrintProgress(PRIntn line)
+{
+ failed = failed || (should && !did);
+ failed = failed || (!should && did);
+ if (debug > 0)
+ {
+ PR_fprintf(
+ fout, "@ line %d destructor should %shave been called and was%s\n",
+ line, ((should) ? "" : "NOT "), ((did) ? "" : " NOT"));
+ }
+} /* PrintProgress */
+
+static void MyAssert(const char *expr, const char *file, PRIntn line)
+{
+ if (debug > 0) {
+ (void)PR_fprintf(fout, "'%s' in file: %s: %d\n", expr, file, line);
+ }
+} /* MyAssert */
+
+#define MY_ASSERT(_expr) \
+ ((_expr)?((void)0):MyAssert(# _expr,__FILE__,__LINE__))
+
+int main(PRIntn argc, char *argv[])
+{
+ PRStatus rv;
+ PRUintn keys;
+ MyThread *thread;
+ const RCThreadPrivateData *pd;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d");
+ RCThread *primordial = RCThread::WrapPrimordialThread();
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ fout = PR_STDOUT;
+
+ MyPrivateData extension = MyPrivateData("EXTENSION");
+ MyPrivateData key_string[] = {
+ "Key #0", "Key #1", "Key #2", "Key #3",
+ "Bogus #5", "Bogus #6", "Bogus #7", "Bogus #8"
+ };
+
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = RCThread::NewPrivateIndex(&key[keys]);
+ key[keys + 4] = key[keys] + 4;
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ /* the first four should be bu null, the last four undefined and null */
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 8; ++keys)
+ {
+ pd = RCThread::GetPrivateData(key[keys]);
+ MY_ASSERT(NULL == pd);
+ }
+ PrintProgress(__LINE__);
+
+ /* initially set private data for new keys */
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = RCThread::SetPrivateData(key[keys], &key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ /* re-assign the private data, albeit the same content */
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ pd = RCThread::GetPrivateData(key[keys]);
+ PR_ASSERT(NULL != pd);
+ rv = RCThread::SetPrivateData(key[keys], &key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ /* set private to <empty> */
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = RCThread::SetPrivateData(key[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ /* should all be null now */
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ pd = RCThread::GetPrivateData(key[keys]);
+ PR_ASSERT(NULL == pd);
+ }
+ PrintProgress(__LINE__);
+
+ /* allocate another batch of keys and assign data to them */
+ did = should = PR_FALSE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = RCThread::NewPrivateIndex(&key[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ rv = RCThread::SetPrivateData(key[keys], &extension);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ /* set all the extended slots to <empty> */
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = RCThread::SetPrivateData(key[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ /* set all the extended slots to <empty> again (noop) */
+ did = should = PR_FALSE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = RCThread::SetPrivateData(key[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+
+ if (debug) {
+ PR_fprintf(fout, "Creating thread\n");
+ }
+ thread = new MyThread();
+ if (debug) {
+ PR_fprintf(fout, "Starting thread\n");
+ }
+ thread->Start();
+ if (debug) {
+ PR_fprintf(fout, "Joining thread\n");
+ }
+ (void)thread->Join();
+ if (debug) {
+ PR_fprintf(fout, "Joined thread\n");
+ }
+
+ failed |= (PR_FAILURE == RCPrimordialThread::Cleanup());
+
+ (void)PR_fprintf(
+ fout, "%s\n",((PR_TRUE == failed) ? "FAILED" : "PASSED"));
+
+ return (failed) ? 1 : 0;
+
+} /* main */
+
+/*
+** class MyPrivateData
+*/
+MyPrivateData::~MyPrivateData()
+{
+ PR_fprintf(
+ fout, "MyPrivateData::~MyPrivateData[%s]\n",
+ (NULL != string) ? string : "NULL");
+} /* MyPrivateData::~MyPrivateData */
+
+MyPrivateData::MyPrivateData(): RCThreadPrivateData()
+{
+ PR_fprintf(fout, "MyPrivateData::MyPrivateData()\n");
+ string = NULL;
+} /* MyPrivateData::MyPrivateData */
+
+MyPrivateData::MyPrivateData(char* data): RCThreadPrivateData()
+{
+ PR_fprintf(fout, "MyPrivateData::MyPrivateData(char* data)\n");
+ string = data;
+} /* MyPrivateData:: MyPrivateData */
+
+MyPrivateData::MyPrivateData(const MyPrivateData& him): RCThreadPrivateData(him)
+{
+ PR_fprintf(fout, "MyPrivateData::MyPrivateData(const MyPrivateData& him)\n");
+ string = him.string;
+} /* MyPrivateData:: MyPrivateData */
+
+void MyPrivateData::Release()
+{
+ if (should) {
+ did = PR_TRUE;
+ }
+ else {
+ failed = PR_TRUE;
+ }
+} /* MyPrivateData::operator= */
+
+/*
+** class MyThread
+*/
+MyThread::~MyThread() { }
+MyThread::MyThread(): RCThread(RCThread::global, RCThread::joinable) { }
+
+
+void MyThread::RootFunction()
+{
+ PRStatus rv;
+ PRUintn keys;
+ const RCThreadPrivateData *pd;
+
+ MyPrivateData extension = MyPrivateData("EXTENSION");
+ MyPrivateData key_string[] = {
+ "Key #0", "Key #1", "Key #2", "Key #3",
+ "Bogus #5", "Bogus #6", "Bogus #7", "Bogus #8"
+ };
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 8; ++keys)
+ {
+ pd = GetPrivateData(key[keys]);
+ MY_ASSERT(NULL == pd);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = SetPrivateData(keys, &key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+#if !defined(DEBUG)
+ did = should = PR_FALSE;
+ for (keys = 4; keys < 8; ++keys)
+ {
+ rv = SetPrivateData(keys, &key_string[keys]);
+ MY_ASSERT(PR_FAILURE == rv);
+ }
+ PrintProgress(__LINE__);
+#endif
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = SetPrivateData(key[keys], &key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = SetPrivateData(key[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = SetPrivateData(key[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = SetPrivateData(key[keys], &extension);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = SetPrivateData(key[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = SetPrivateData(key[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+} /* MyThread::RootFunction */
+
+/* tpd.c */
diff --git a/nsprpub/pr/src/io/Makefile.in b/nsprpub/pr/src/io/Makefile.in
new file mode 100644
index 0000000000..f6b5bcd6dd
--- /dev/null
+++ b/nsprpub/pr/src/io/Makefile.in
@@ -0,0 +1,50 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+CSRCS = \
+ prfdcach.c \
+ prmwait.c \
+ priometh.c \
+ pripv6.c \
+ prmapopt.c \
+ prlayer.c \
+ prlog.c \
+ prmmap.c \
+ prpolevt.c \
+ prprf.c \
+ prscanf.c \
+ prstdio.c \
+ $(NULL)
+
+ifndef USE_PTHREADS
+ CSRCS += \
+ prdir.c \
+ prfile.c \
+ prio.c \
+ prsocket.c \
+ $(NULL)
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
diff --git a/nsprpub/pr/src/io/prdir.c b/nsprpub/pr/src/io/prdir.c
new file mode 100644
index 0000000000..365afefd2f
--- /dev/null
+++ b/nsprpub/pr/src/io/prdir.c
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+PR_IMPLEMENT(PRDir*) PR_OpenDir(const char *name)
+{
+ PRDir *dir;
+ PRStatus sts;
+
+ dir = PR_NEW(PRDir);
+ if (dir) {
+ sts = _PR_MD_OPEN_DIR(&dir->md,name);
+ if (sts != PR_SUCCESS) {
+ PR_DELETE(dir);
+ return NULL;
+ }
+ } else {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ return dir;
+}
+
+PR_IMPLEMENT(PRDirEntry*) PR_ReadDir(PRDir *dir, PRDirFlags flags)
+{
+ /* _MD_READ_DIR return a char* to the name; allocation in machine-dependent code */
+ char* name = _PR_MD_READ_DIR(&dir->md, flags);
+ dir->d.name = name;
+ return name ? &dir->d : NULL;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CloseDir(PRDir *dir)
+{
+ PRInt32 rv;
+
+ if (dir) {
+ rv = _PR_MD_CLOSE_DIR(&dir->md);
+ PR_DELETE(dir);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+ }
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_MkDir(const char *name, PRIntn mode)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_MKDIR(name, mode);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+}
+
+PR_IMPLEMENT(PRStatus) PR_MakeDir(const char *name, PRIntn mode)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ rv = _PR_MD_MAKE_DIR(name, mode);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+}
+
+PR_IMPLEMENT(PRStatus) PR_RmDir(const char *name)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_RMDIR(name);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+}
+
+#ifdef MOZ_UNICODE
+/*
+ * UTF16 Interface
+ */
+PR_IMPLEMENT(PRDirUTF16*) PR_OpenDirUTF16(const PRUnichar *name)
+{
+ PRDirUTF16 *dir;
+ PRStatus sts;
+
+ dir = PR_NEW(PRDirUTF16);
+ if (dir) {
+ sts = _PR_MD_OPEN_DIR_UTF16(&dir->md,name);
+ if (sts != PR_SUCCESS) {
+ PR_DELETE(dir);
+ return NULL;
+ }
+ } else {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ return dir;
+}
+
+PR_IMPLEMENT(PRDirEntryUTF16*) PR_ReadDirUTF16(PRDirUTF16 *dir, PRDirFlags flags)
+{
+ /*
+ * _MD_READ_DIR_UTF16 return a PRUnichar* to the name; allocation in
+ * machine-dependent code
+ */
+ PRUnichar* name = _PR_MD_READ_DIR_UTF16(&dir->md, flags);
+ dir->d.name = name;
+ return name ? &dir->d : NULL;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CloseDirUTF16(PRDirUTF16 *dir)
+{
+ PRInt32 rv;
+
+ if (dir) {
+ rv = _PR_MD_CLOSE_DIR_UTF16(&dir->md);
+ PR_DELETE(dir);
+ if (rv < 0) {
+ return PR_FAILURE;
+ }
+ else {
+ return PR_SUCCESS;
+ }
+ }
+ return PR_SUCCESS;
+}
+
+#endif /* MOZ_UNICODE */
diff --git a/nsprpub/pr/src/io/prfdcach.c b/nsprpub/pr/src/io/prfdcach.c
new file mode 100644
index 0000000000..ecfe3d39cc
--- /dev/null
+++ b/nsprpub/pr/src/io/prfdcach.c
@@ -0,0 +1,252 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <string.h>
+
+/*****************************************************************************/
+/*****************************************************************************/
+/************************** File descriptor caching **************************/
+/*****************************************************************************/
+/*****************************************************************************/
+
+/*
+** This code is built into debuggable versions of NSPR to assist in
+** finding misused file descriptors. Since file descritors (PRFileDesc)
+** are identified by a pointer to their structure, they can be the
+** target of dangling references. Furthermore, NSPR caches and tries
+** to aggressively reuse file descriptors, leading to more ambiguity.
+** The following code will allow a debugging client to set environment
+** variables and control the number of file descriptors that will be
+** preserved before they are recycled. The environment variables are
+** NSPR_FD_CACHE_SIZE_LOW and NSPR_FD_CACHE_SIZE_HIGH. The former sets
+** the number of descriptors NSPR will allocate before beginning to
+** recycle. The latter is the maximum number permitted in the cache
+** (exclusive of those in use) at a time.
+*/
+typedef struct _PR_Fd_Cache
+{
+ PRLock *ml;
+ PRIntn count;
+ PRFileDesc *head, *tail;
+ PRIntn limit_low, limit_high;
+} _PR_Fd_Cache;
+
+static _PR_Fd_Cache _pr_fd_cache;
+
+
+/*
+** Get a FileDescriptor from the cache if one exists. If not allocate
+** a new one from the heap.
+*/
+PRFileDesc *_PR_Getfd(void)
+{
+ PRFileDesc *fd;
+ /*
+ ** $$$
+ ** This may look a little wasteful. We'll see. Right now I want to
+ ** be able to toggle between caching and not at runtime to measure
+ ** the differences. If it isn't too annoying, I'll leave it in.
+ ** $$$$
+ **
+ ** The test is against _pr_fd_cache.limit_high. If that's zero,
+ ** we're not doing the extended cache but going for performance.
+ */
+ if (0 == _pr_fd_cache.limit_high)
+ {
+ goto allocate;
+ }
+ else
+ {
+ do
+ {
+ if (NULL == _pr_fd_cache.head) {
+ goto allocate; /* nothing there */
+ }
+ if (_pr_fd_cache.count < _pr_fd_cache.limit_low) {
+ goto allocate;
+ }
+
+ /* we "should" be able to extract an fd from the cache */
+ PR_Lock(_pr_fd_cache.ml); /* need the lock to do this safely */
+ fd = _pr_fd_cache.head; /* protected extraction */
+ if (NULL == fd) /* unexpected, but not fatal */
+ {
+ PR_ASSERT(0 == _pr_fd_cache.count);
+ PR_ASSERT(NULL == _pr_fd_cache.tail);
+ }
+ else
+ {
+ _pr_fd_cache.count -= 1;
+ _pr_fd_cache.head = fd->higher;
+ if (NULL == _pr_fd_cache.head)
+ {
+ PR_ASSERT(0 == _pr_fd_cache.count);
+ _pr_fd_cache.tail = NULL;
+ }
+ PR_ASSERT(&_pr_faulty_methods == fd->methods);
+ PR_ASSERT(PR_INVALID_IO_LAYER == fd->identity);
+ PR_ASSERT(_PR_FILEDESC_FREED == fd->secret->state);
+ }
+ PR_Unlock(_pr_fd_cache.ml);
+
+ } while (NULL == fd); /* then go around and allocate a new one */
+ }
+
+finished:
+ fd->dtor = NULL;
+ fd->lower = fd->higher = NULL;
+ fd->identity = PR_NSPR_IO_LAYER;
+ memset(fd->secret, 0, sizeof(PRFilePrivate));
+ return fd;
+
+allocate:
+ fd = PR_NEW(PRFileDesc);
+ if (NULL != fd)
+ {
+ fd->secret = PR_NEW(PRFilePrivate);
+ if (NULL == fd->secret) {
+ PR_DELETE(fd);
+ }
+ }
+ if (NULL != fd) {
+ goto finished;
+ }
+ else {
+ return NULL;
+ }
+
+} /* _PR_Getfd */
+
+/*
+** Return a file descriptor to the cache unless there are too many in
+** there already. If put in cache, clear the fields first.
+*/
+void _PR_Putfd(PRFileDesc *fd)
+{
+ PR_ASSERT(PR_NSPR_IO_LAYER == fd->identity);
+ fd->methods = &_pr_faulty_methods;
+ fd->identity = PR_INVALID_IO_LAYER;
+ fd->secret->state = _PR_FILEDESC_FREED;
+
+ if (0 != _pr_fd_cache.limit_high)
+ {
+ if (_pr_fd_cache.count < _pr_fd_cache.limit_high)
+ {
+ PR_Lock(_pr_fd_cache.ml);
+ if (NULL == _pr_fd_cache.tail)
+ {
+ PR_ASSERT(0 == _pr_fd_cache.count);
+ PR_ASSERT(NULL == _pr_fd_cache.head);
+ _pr_fd_cache.head = _pr_fd_cache.tail = fd;
+ }
+ else
+ {
+ PR_ASSERT(NULL == _pr_fd_cache.tail->higher);
+ _pr_fd_cache.tail->higher = fd;
+ _pr_fd_cache.tail = fd; /* new value */
+ }
+ fd->higher = NULL; /* always so */
+ _pr_fd_cache.count += 1; /* count the new entry */
+ PR_Unlock(_pr_fd_cache.ml);
+ return;
+ }
+ }
+
+ PR_Free(fd->secret);
+ PR_Free(fd);
+} /* _PR_Putfd */
+
+PR_IMPLEMENT(PRStatus) PR_SetFDCacheSize(PRIntn low, PRIntn high)
+{
+ /*
+ ** This can be called at any time, may adjust the cache sizes,
+ ** turn the caches off, or turn them on. It is not dependent
+ ** on the compilation setting of DEBUG.
+ */
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (low > high) {
+ low = high; /* sanity check the params */
+ }
+
+ PR_Lock(_pr_fd_cache.ml);
+ _pr_fd_cache.limit_high = high;
+ _pr_fd_cache.limit_low = low;
+ PR_Unlock(_pr_fd_cache.ml);
+ return PR_SUCCESS;
+} /* PR_SetFDCacheSize */
+
+void _PR_InitFdCache(void)
+{
+ /*
+ ** The fd caching is enabled by default for DEBUG builds,
+ ** disabled by default for OPT builds. That default can
+ ** be overridden at runtime using environment variables
+ ** or a super-wiz-bang API.
+ */
+ const char *low = PR_GetEnv("NSPR_FD_CACHE_SIZE_LOW");
+ const char *high = PR_GetEnv("NSPR_FD_CACHE_SIZE_HIGH");
+
+ /*
+ ** _low is allowed to be zero, _high is not.
+ ** If _high is zero, we're not doing the caching.
+ */
+
+ _pr_fd_cache.limit_low = 0;
+#if defined(DEBUG)
+ _pr_fd_cache.limit_high = FD_SETSIZE;
+#else
+ _pr_fd_cache.limit_high = 0;
+#endif /* defined(DEBUG) */
+
+ if (NULL != low) {
+ _pr_fd_cache.limit_low = atoi(low);
+ }
+ if (NULL != high) {
+ _pr_fd_cache.limit_high = atoi(high);
+ }
+
+ if (_pr_fd_cache.limit_low < 0) {
+ _pr_fd_cache.limit_low = 0;
+ }
+ if (_pr_fd_cache.limit_low > FD_SETSIZE) {
+ _pr_fd_cache.limit_low = FD_SETSIZE;
+ }
+
+ if (_pr_fd_cache.limit_high > FD_SETSIZE) {
+ _pr_fd_cache.limit_high = FD_SETSIZE;
+ }
+
+ if (_pr_fd_cache.limit_high < _pr_fd_cache.limit_low) {
+ _pr_fd_cache.limit_high = _pr_fd_cache.limit_low;
+ }
+
+ _pr_fd_cache.ml = PR_NewLock();
+ PR_ASSERT(NULL != _pr_fd_cache.ml);
+
+} /* _PR_InitFdCache */
+
+void _PR_CleanupFdCache(void)
+{
+ PRFileDesc *fd, *next;
+
+ for (fd = _pr_fd_cache.head; fd != NULL; fd = next)
+ {
+ next = fd->higher;
+ PR_DELETE(fd->secret);
+ PR_DELETE(fd);
+ }
+ _pr_fd_cache.head = NULL;
+ _pr_fd_cache.tail = NULL;
+ _pr_fd_cache.count = 0;
+ PR_DestroyLock(_pr_fd_cache.ml);
+ _pr_fd_cache.ml = NULL;
+} /* _PR_CleanupFdCache */
+
+/* prfdcach.c */
diff --git a/nsprpub/pr/src/io/prfile.c b/nsprpub/pr/src/io/prfile.c
new file mode 100644
index 0000000000..4b07baf41b
--- /dev/null
+++ b/nsprpub/pr/src/io/prfile.c
@@ -0,0 +1,818 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <string.h>
+#include <fcntl.h>
+
+#ifdef XP_UNIX
+#if defined(AIX) || defined(QNX)
+/* To pick up sysconf */
+#include <unistd.h>
+#else
+/* To pick up getrlimit, setrlimit */
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+#endif /* XP_UNIX */
+
+extern PRLock *_pr_flock_lock;
+extern PRCondVar *_pr_flock_cv;
+
+static PRInt32 PR_CALLBACK FileRead(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ PRInt32 rv = 0;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ rv = -1;
+ }
+ if (rv == -1) {
+ return rv;
+ }
+
+ rv = _PR_MD_READ(fd, buf, amount);
+ if (rv < 0) {
+ PR_ASSERT(rv == -1);
+ }
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("read -> %d", rv));
+ return rv;
+}
+
+static PRInt32 PR_CALLBACK FileWrite(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ PRInt32 rv = 0;
+ PRInt32 temp, count;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ rv = -1;
+ }
+ if (rv != 0) {
+ return rv;
+ }
+
+ count = 0;
+#if !defined(_PR_HAVE_O_APPEND) /* Bugzilla: 4090, 276330 */
+ if (fd->secret->appendMode) {
+ if (PR_Seek64(fd, 0, PR_SEEK_END) == -1) {
+ return -1;
+ }
+ } /* if (fd->secret->appendMode...) */
+#endif /* _PR_HAVE_O_APPEND */
+ while (amount > 0) {
+ temp = _PR_MD_WRITE(fd, buf, amount);
+ if (temp < 0) {
+ count = -1;
+ break;
+ }
+ count += temp;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ buf = (const void*) ((const char*)buf + temp);
+ amount -= temp;
+ }
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("write -> %d", count));
+ return count;
+}
+
+static PROffset32 PR_CALLBACK FileSeek(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence)
+{
+ PROffset32 result;
+
+ result = _PR_MD_LSEEK(fd, offset, whence);
+ return result;
+}
+
+static PROffset64 PR_CALLBACK FileSeek64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence)
+{
+ PROffset64 result;
+
+ result = _PR_MD_LSEEK64(fd, offset, whence);
+ return result;
+}
+
+static PRInt32 PR_CALLBACK FileAvailable(PRFileDesc *fd)
+{
+ PRInt32 result, cur, end;
+
+ cur = _PR_MD_LSEEK(fd, 0, PR_SEEK_CUR);
+
+ if (cur >= 0) {
+ end = _PR_MD_LSEEK(fd, 0, PR_SEEK_END);
+ }
+
+ if ((cur < 0) || (end < 0)) {
+ return -1;
+ }
+
+ result = end - cur;
+ _PR_MD_LSEEK(fd, cur, PR_SEEK_SET);
+
+ return result;
+}
+
+static PRInt64 PR_CALLBACK FileAvailable64(PRFileDesc *fd)
+{
+ PRInt64 result, cur, end;
+ PRInt64 minus_one;
+
+ LL_I2L(minus_one, -1);
+ cur = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_CUR);
+
+ if (LL_GE_ZERO(cur)) {
+ end = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_END);
+ }
+
+ if (!LL_GE_ZERO(cur) || !LL_GE_ZERO(end)) {
+ return minus_one;
+ }
+
+ LL_SUB(result, end, cur);
+ (void)_PR_MD_LSEEK64(fd, cur, PR_SEEK_SET);
+
+ return result;
+}
+
+static PRInt32 PR_CALLBACK PipeAvailable(PRFileDesc *fd)
+{
+ PRInt32 rv;
+ rv = _PR_MD_PIPEAVAILABLE(fd);
+ return rv;
+}
+
+static PRInt64 PR_CALLBACK PipeAvailable64(PRFileDesc *fd)
+{
+ PRInt64 rv;
+ LL_I2L(rv, _PR_MD_PIPEAVAILABLE(fd));
+ return rv;
+}
+
+static PRStatus PR_CALLBACK PipeSync(PRFileDesc *fd)
+{
+ return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK FileGetInfo(PRFileDesc *fd, PRFileInfo *info)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_GETOPENFILEINFO(fd, info);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+}
+
+static PRStatus PR_CALLBACK FileGetInfo64(PRFileDesc *fd, PRFileInfo64 *info)
+{
+ /* $$$$ NOT YET IMPLEMENTED */
+ PRInt32 rv;
+
+ rv = _PR_MD_GETOPENFILEINFO64(fd, info);
+ if (rv < 0) {
+ return PR_FAILURE;
+ }
+ else {
+ return PR_SUCCESS;
+ }
+}
+
+static PRStatus PR_CALLBACK FileSync(PRFileDesc *fd)
+{
+ PRInt32 result;
+ result = _PR_MD_FSYNC(fd);
+ if (result < 0) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK FileClose(PRFileDesc *fd)
+{
+ if (!fd || !fd->secret
+ || (fd->secret->state != _PR_FILEDESC_OPEN
+ && fd->secret->state != _PR_FILEDESC_CLOSED)) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (fd->secret->state == _PR_FILEDESC_OPEN) {
+ if (_PR_MD_CLOSE_FILE(fd->secret->md.osfd) < 0) {
+ return PR_FAILURE;
+ }
+ fd->secret->state = _PR_FILEDESC_CLOSED;
+ }
+ PR_FreeFileDesc(fd);
+ return PR_SUCCESS;
+}
+
+static PRInt16 PR_CALLBACK FilePoll(
+ PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
+{
+ *out_flags = 0;
+ return in_flags;
+} /* FilePoll */
+
+static PRIOMethods _pr_fileMethods = {
+ PR_DESC_FILE,
+ FileClose,
+ FileRead,
+ FileWrite,
+ FileAvailable,
+ FileAvailable64,
+ FileSync,
+ FileSeek,
+ FileSeek64,
+ FileGetInfo,
+ FileGetInfo64,
+ (PRWritevFN)_PR_InvalidInt,
+ (PRConnectFN)_PR_InvalidStatus,
+ (PRAcceptFN)_PR_InvalidDesc,
+ (PRBindFN)_PR_InvalidStatus,
+ (PRListenFN)_PR_InvalidStatus,
+ (PRShutdownFN)_PR_InvalidStatus,
+ (PRRecvFN)_PR_InvalidInt,
+ (PRSendFN)_PR_InvalidInt,
+ (PRRecvfromFN)_PR_InvalidInt,
+ (PRSendtoFN)_PR_InvalidInt,
+ FilePoll,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ (PRGetsocknameFN)_PR_InvalidStatus,
+ (PRGetpeernameFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRGetsocketoptionFN)_PR_InvalidStatus,
+ (PRSetsocketoptionFN)_PR_InvalidStatus,
+ (PRSendfileFN)_PR_InvalidInt,
+ (PRConnectcontinueFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+PR_IMPLEMENT(const PRIOMethods*) PR_GetFileMethods(void)
+{
+ return &_pr_fileMethods;
+}
+
+static PRIOMethods _pr_pipeMethods = {
+ PR_DESC_PIPE,
+ FileClose,
+ FileRead,
+ FileWrite,
+ PipeAvailable,
+ PipeAvailable64,
+ PipeSync,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ (PRWritevFN)_PR_InvalidInt,
+ (PRConnectFN)_PR_InvalidStatus,
+ (PRAcceptFN)_PR_InvalidDesc,
+ (PRBindFN)_PR_InvalidStatus,
+ (PRListenFN)_PR_InvalidStatus,
+ (PRShutdownFN)_PR_InvalidStatus,
+ (PRRecvFN)_PR_InvalidInt,
+ (PRSendFN)_PR_InvalidInt,
+ (PRRecvfromFN)_PR_InvalidInt,
+ (PRSendtoFN)_PR_InvalidInt,
+ FilePoll,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ (PRGetsocknameFN)_PR_InvalidStatus,
+ (PRGetpeernameFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRGetsocketoptionFN)_PR_InvalidStatus,
+ (PRSetsocketoptionFN)_PR_InvalidStatus,
+ (PRSendfileFN)_PR_InvalidInt,
+ (PRConnectcontinueFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+PR_IMPLEMENT(const PRIOMethods*) PR_GetPipeMethods(void)
+{
+ return &_pr_pipeMethods;
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_Open(const char *name, PRIntn flags, PRIntn mode)
+{
+ PROsfd osfd;
+ PRFileDesc *fd = 0;
+#if !defined(_PR_HAVE_O_APPEND)
+ PRBool appendMode = ( PR_APPEND & flags )? PR_TRUE : PR_FALSE;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ /* Map pr open flags and mode to os specific flags */
+
+ osfd = _PR_MD_OPEN(name, flags, mode);
+ if (osfd != -1) {
+ fd = PR_AllocFileDesc(osfd, &_pr_fileMethods);
+ if (!fd) {
+ (void) _PR_MD_CLOSE_FILE(osfd);
+ } else {
+#if !defined(_PR_HAVE_O_APPEND)
+ fd->secret->appendMode = appendMode;
+#endif
+ _PR_MD_INIT_FD_INHERITABLE(fd, PR_FALSE);
+ }
+ }
+ return fd;
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_OpenFile(
+ const char *name, PRIntn flags, PRIntn mode)
+{
+ PROsfd osfd;
+ PRFileDesc *fd = 0;
+#if !defined(_PR_HAVE_O_APPEND)
+ PRBool appendMode = ( PR_APPEND & flags )? PR_TRUE : PR_FALSE;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ /* Map pr open flags and mode to os specific flags */
+
+ osfd = _PR_MD_OPEN_FILE(name, flags, mode);
+ if (osfd != -1) {
+ fd = PR_AllocFileDesc(osfd, &_pr_fileMethods);
+ if (!fd) {
+ (void) _PR_MD_CLOSE_FILE(osfd);
+ } else {
+#if !defined(_PR_HAVE_O_APPEND)
+ fd->secret->appendMode = appendMode;
+#endif
+ _PR_MD_INIT_FD_INHERITABLE(fd, PR_FALSE);
+ }
+ }
+ return fd;
+}
+
+PR_IMPLEMENT(PRInt32) PR_GetSysfdTableMax(void)
+{
+#if defined(XP_UNIX) && !defined(AIX) && !defined(QNX)
+ struct rlimit rlim;
+
+ if ( getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+ /* XXX need to call PR_SetError() */
+ return -1;
+ }
+
+ return rlim.rlim_max;
+#elif defined(AIX) || defined(QNX)
+ return sysconf(_SC_OPEN_MAX);
+#elif defined(WIN32)
+ /*
+ * There is a systemwide limit of 65536 user handles.
+ */
+ return 16384;
+#elif defined (WIN16)
+ return FOPEN_MAX;
+#elif defined(XP_OS2)
+ ULONG ulReqCount = 0;
+ ULONG ulCurMaxFH = 0;
+ DosSetRelMaxFH(&ulReqCount, &ulCurMaxFH);
+ return ulCurMaxFH;
+#else
+ write me;
+#endif
+}
+
+PR_IMPLEMENT(PRInt32) PR_SetSysfdTableSize(int table_size)
+{
+#if defined(XP_UNIX) && !defined(AIX) && !defined(QNX)
+ struct rlimit rlim;
+ PRInt32 tableMax = PR_GetSysfdTableMax();
+
+ if (tableMax < 0) {
+ return -1;
+ }
+
+ if (tableMax > FD_SETSIZE) {
+ tableMax = FD_SETSIZE;
+ }
+
+ rlim.rlim_max = tableMax;
+
+ /* Grow as much as we can; even if too big */
+ if ( rlim.rlim_max < table_size ) {
+ rlim.rlim_cur = rlim.rlim_max;
+ }
+ else {
+ rlim.rlim_cur = table_size;
+ }
+
+ if ( setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+ /* XXX need to call PR_SetError() */
+ return -1;
+ }
+
+ return rlim.rlim_cur;
+#elif defined(XP_OS2)
+ PRInt32 tableMax = PR_GetSysfdTableMax();
+ if (table_size > tableMax) {
+ APIRET rc = NO_ERROR;
+ rc = DosSetMaxFH(table_size);
+ if (rc == NO_ERROR) {
+ return table_size;
+ }
+ else {
+ return -1;
+ }
+ }
+ return tableMax;
+#elif defined(AIX) || defined(QNX) \
+ || defined(WIN32) || defined(WIN16)
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return -1;
+#else
+ write me;
+#endif
+}
+
+PR_IMPLEMENT(PRStatus) PR_Delete(const char *name)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_DELETE(name);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetFileInfo(const char *fn, PRFileInfo *info)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_GETFILEINFO(fn, info);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetFileInfo64(const char *fn, PRFileInfo64 *info)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ rv = _PR_MD_GETFILEINFO64(fn, info);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+}
+
+PR_IMPLEMENT(PRStatus) PR_Rename(const char *from, const char *to)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_RENAME(from, to);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+}
+
+PR_IMPLEMENT(PRStatus) PR_Access(const char *name, PRAccessHow how)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_ACCESS(name, how);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+}
+
+/*
+** Import an existing OS file to NSPR
+*/
+PR_IMPLEMENT(PRFileDesc*) PR_ImportFile(PROsfd osfd)
+{
+ PRFileDesc *fd = NULL;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ fd = PR_AllocFileDesc(osfd, &_pr_fileMethods);
+ if( !fd ) {
+ (void) _PR_MD_CLOSE_FILE(osfd);
+ } else {
+ _PR_MD_INIT_FD_INHERITABLE(fd, PR_TRUE);
+ }
+
+ return fd;
+}
+
+/*
+** Import an existing OS pipe to NSPR
+*/
+PR_IMPLEMENT(PRFileDesc*) PR_ImportPipe(PROsfd osfd)
+{
+ PRFileDesc *fd = NULL;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ fd = PR_AllocFileDesc(osfd, &_pr_pipeMethods);
+ if( !fd ) {
+ (void) _PR_MD_CLOSE_FILE(osfd);
+ } else {
+ _PR_MD_INIT_FD_INHERITABLE(fd, PR_TRUE);
+#ifdef WINNT
+ fd->secret->md.sync_file_io = PR_TRUE;
+#endif
+ }
+
+ return fd;
+}
+
+#ifndef NO_NSPR_10_SUPPORT
+/*
+** PR_Stat() for Win16 is defined in w16io.c
+** it is a hack to circumvent problems in Gromit and Java
+** See also: BugSplat: 98516.
+*/
+#if !defined(WIN16)
+/*
+ * This function is supposed to be for backward compatibility with
+ * nspr 1.0. Therefore, it still uses the nspr 1.0 error-reporting
+ * mechanism -- returns a PRInt32, which is the error code when the call
+ * fails.
+ *
+ * If we need this function in nspr 2.0, it should be changed to
+ * return PRStatus, as follows:
+ *
+ * PR_IMPLEMENT(PRStatus) PR_Stat(const char *name, struct stat *buf)
+ * {
+ * PRInt32 rv;
+ *
+ * rv = _PR_MD_STAT(name, buf);
+ * if (rv < 0)
+ * return PR_FAILURE;
+ * else
+ * return PR_SUCCESS;
+ * }
+ *
+ * -- wtc, 2/14/97.
+ */
+PR_IMPLEMENT(PRInt32) PR_Stat(const char *name, struct stat *buf)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_STAT(name, buf);
+ return rv;
+}
+
+#endif /* !defined(WIN16) */
+#endif /* ! NO_NSPR_10_SUPPORT */
+
+PR_IMPLEMENT(PRStatus) PR_LockFile(PRFileDesc *fd)
+{
+ PRStatus status = PR_SUCCESS;
+
+#ifdef WINNT
+ if (!fd->secret->md.io_model_committed) {
+ PRInt32 rv;
+ rv = _md_Associate((HANDLE)fd->secret->md.osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+#endif
+
+ PR_Lock(_pr_flock_lock);
+ while (fd->secret->lockCount == -1) {
+ PR_WaitCondVar(_pr_flock_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ if (fd->secret->lockCount == 0) {
+ fd->secret->lockCount = -1;
+ PR_Unlock(_pr_flock_lock);
+ status = _PR_MD_LOCKFILE(fd->secret->md.osfd);
+ PR_Lock(_pr_flock_lock);
+ fd->secret->lockCount = (status == PR_SUCCESS) ? 1 : 0;
+ PR_NotifyAllCondVar(_pr_flock_cv);
+ } else {
+ fd->secret->lockCount++;
+ }
+ PR_Unlock(_pr_flock_lock);
+
+ return status;
+}
+
+PR_IMPLEMENT(PRStatus) PR_TLockFile(PRFileDesc *fd)
+{
+ PRStatus status = PR_SUCCESS;
+
+#ifdef WINNT
+ if (!fd->secret->md.io_model_committed) {
+ PRInt32 rv;
+ rv = _md_Associate((HANDLE)fd->secret->md.osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+#endif
+
+ PR_Lock(_pr_flock_lock);
+ if (fd->secret->lockCount == 0) {
+ status = _PR_MD_TLOCKFILE(fd->secret->md.osfd);
+ PR_ASSERT(status == PR_SUCCESS || fd->secret->lockCount == 0);
+ if (status == PR_SUCCESS) {
+ fd->secret->lockCount = 1;
+ }
+ } else {
+ fd->secret->lockCount++;
+ }
+ PR_Unlock(_pr_flock_lock);
+
+ return status;
+}
+
+PR_IMPLEMENT(PRStatus) PR_UnlockFile(PRFileDesc *fd)
+{
+ PRStatus rv = PR_SUCCESS;
+
+ PR_Lock(_pr_flock_lock);
+ if (fd->secret->lockCount == 1) {
+ rv = _PR_MD_UNLOCKFILE(fd->secret->md.osfd);
+ if (rv == PR_SUCCESS) {
+ fd->secret->lockCount = 0;
+ }
+ } else {
+ fd->secret->lockCount--;
+ }
+ PR_Unlock(_pr_flock_lock);
+
+ return rv;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CreatePipe(
+ PRFileDesc **readPipe,
+ PRFileDesc **writePipe
+)
+{
+#if defined(WIN32) && !defined(WINCE)
+ HANDLE readEnd, writeEnd;
+ SECURITY_ATTRIBUTES pipeAttributes;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ ZeroMemory(&pipeAttributes, sizeof(pipeAttributes));
+ pipeAttributes.nLength = sizeof(pipeAttributes);
+ pipeAttributes.bInheritHandle = TRUE;
+ if (CreatePipe(&readEnd, &writeEnd, &pipeAttributes, 0) == 0) {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ *readPipe = PR_AllocFileDesc((PROsfd)readEnd, &_pr_pipeMethods);
+ if (NULL == *readPipe) {
+ CloseHandle(readEnd);
+ CloseHandle(writeEnd);
+ return PR_FAILURE;
+ }
+ *writePipe = PR_AllocFileDesc((PROsfd)writeEnd, &_pr_pipeMethods);
+ if (NULL == *writePipe) {
+ PR_Close(*readPipe);
+ CloseHandle(writeEnd);
+ return PR_FAILURE;
+ }
+#ifdef WINNT
+ (*readPipe)->secret->md.sync_file_io = PR_TRUE;
+ (*writePipe)->secret->md.sync_file_io = PR_TRUE;
+#endif
+ (*readPipe)->secret->inheritable = _PR_TRI_TRUE;
+ (*writePipe)->secret->inheritable = _PR_TRI_TRUE;
+ return PR_SUCCESS;
+#elif defined(XP_UNIX) || defined(XP_OS2)
+#ifdef XP_OS2
+ HFILE pipefd[2];
+#else
+ int pipefd[2];
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+#ifdef XP_OS2
+ if (DosCreatePipe(&pipefd[0], &pipefd[1], 4096) != 0) {
+#else
+ if (pipe(pipefd) == -1) {
+#endif
+ /* XXX map pipe error */
+ PR_SetError(PR_UNKNOWN_ERROR, errno);
+ return PR_FAILURE;
+ }
+ *readPipe = PR_AllocFileDesc(pipefd[0], &_pr_pipeMethods);
+ if (NULL == *readPipe) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return PR_FAILURE;
+ }
+ *writePipe = PR_AllocFileDesc(pipefd[1], &_pr_pipeMethods);
+ if (NULL == *writePipe) {
+ PR_Close(*readPipe);
+ close(pipefd[1]);
+ return PR_FAILURE;
+ }
+ _PR_MD_MAKE_NONBLOCK(*readPipe);
+ _PR_MD_INIT_FD_INHERITABLE(*readPipe, PR_FALSE);
+ _PR_MD_MAKE_NONBLOCK(*writePipe);
+ _PR_MD_INIT_FD_INHERITABLE(*writePipe, PR_FALSE);
+ return PR_SUCCESS;
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+#endif
+}
+
+#ifdef MOZ_UNICODE
+/* ================ UTF16 Interfaces ================================ */
+PR_IMPLEMENT(PRFileDesc*) PR_OpenFileUTF16(
+ const PRUnichar *name, PRIntn flags, PRIntn mode)
+{
+ PROsfd osfd;
+ PRFileDesc *fd = 0;
+#if !defined(_PR_HAVE_O_APPEND)
+ PRBool appendMode = ( PR_APPEND & flags )? PR_TRUE : PR_FALSE;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ /* Map pr open flags and mode to os specific flags */
+ osfd = _PR_MD_OPEN_FILE_UTF16(name, flags, mode);
+ if (osfd != -1) {
+ fd = PR_AllocFileDesc(osfd, &_pr_fileMethods);
+ if (!fd) {
+ (void) _PR_MD_CLOSE_FILE(osfd);
+ } else {
+#if !defined(_PR_HAVE_O_APPEND)
+ fd->secret->appendMode = appendMode;
+#endif
+ _PR_MD_INIT_FD_INHERITABLE(fd, PR_FALSE);
+ }
+ }
+ return fd;
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetFileInfo64UTF16(const PRUnichar *fn, PRFileInfo64 *info)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ rv = _PR_MD_GETFILEINFO64_UTF16(fn, info);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+}
+
+/* ================ UTF16 Interfaces ================================ */
+#endif /* MOZ_UNICODE */
diff --git a/nsprpub/pr/src/io/prio.c b/nsprpub/pr/src/io/prio.c
new file mode 100644
index 0000000000..745d7721b9
--- /dev/null
+++ b/nsprpub/pr/src/io/prio.c
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <string.h> /* for memset() */
+
+
+/************************************************************************/
+
+PRLock *_pr_flock_lock;
+PRCondVar *_pr_flock_cv;
+
+#ifdef WINCE
+/*
+ * There are no stdin, stdout, stderr in Windows CE. INVALID_HANDLE_VALUE
+ * should cause all I/O functions on the handle to fail.
+ */
+#define STD_INPUT_HANDLE ((DWORD)-10)
+#define STD_OUTPUT_HANDLE ((DWORD)-11)
+#define STD_ERROR_HANDLE ((DWORD)-12)
+
+static HANDLE GetStdHandle(DWORD nStdHandle)
+{
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return INVALID_HANDLE_VALUE;
+}
+#endif
+
+void _PR_InitIO(void)
+{
+ const PRIOMethods *methods = PR_GetFileMethods();
+
+ _PR_InitFdCache();
+
+ _pr_flock_lock = PR_NewLock();
+ _pr_flock_cv = PR_NewCondVar(_pr_flock_lock);
+
+#ifdef WIN32
+ _pr_stdin = PR_AllocFileDesc((PROsfd)GetStdHandle(STD_INPUT_HANDLE),
+ methods);
+ _pr_stdout = PR_AllocFileDesc((PROsfd)GetStdHandle(STD_OUTPUT_HANDLE),
+ methods);
+ _pr_stderr = PR_AllocFileDesc((PROsfd)GetStdHandle(STD_ERROR_HANDLE),
+ methods);
+#ifdef WINNT
+ _pr_stdin->secret->md.sync_file_io = PR_TRUE;
+ _pr_stdout->secret->md.sync_file_io = PR_TRUE;
+ _pr_stderr->secret->md.sync_file_io = PR_TRUE;
+#endif
+#else
+ _pr_stdin = PR_AllocFileDesc(0, methods);
+ _pr_stdout = PR_AllocFileDesc(1, methods);
+ _pr_stderr = PR_AllocFileDesc(2, methods);
+#endif
+ _PR_MD_INIT_FD_INHERITABLE(_pr_stdin, PR_TRUE);
+ _PR_MD_INIT_FD_INHERITABLE(_pr_stdout, PR_TRUE);
+ _PR_MD_INIT_FD_INHERITABLE(_pr_stderr, PR_TRUE);
+
+ _PR_MD_INIT_IO();
+}
+
+void _PR_CleanupIO(void)
+{
+ PR_FreeFileDesc(_pr_stdin);
+ _pr_stdin = NULL;
+ PR_FreeFileDesc(_pr_stdout);
+ _pr_stdout = NULL;
+ PR_FreeFileDesc(_pr_stderr);
+ _pr_stderr = NULL;
+
+ if (_pr_flock_cv) {
+ PR_DestroyCondVar(_pr_flock_cv);
+ _pr_flock_cv = NULL;
+ }
+ if (_pr_flock_lock) {
+ PR_DestroyLock(_pr_flock_lock);
+ _pr_flock_lock = NULL;
+ }
+
+ _PR_CleanupFdCache();
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD osfd)
+{
+ PRFileDesc *result = NULL;
+ PR_ASSERT((int) osfd >= PR_StandardInput && osfd <= PR_StandardError);
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ switch (osfd)
+ {
+ case PR_StandardInput: result = _pr_stdin; break;
+ case PR_StandardOutput: result = _pr_stdout; break;
+ case PR_StandardError: result = _pr_stderr; break;
+ default:
+ (void)PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ }
+ return result;
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_AllocFileDesc(
+ PROsfd osfd, const PRIOMethods *methods)
+{
+ PRFileDesc *fd;
+
+#ifdef XP_UNIX
+ /*
+ * Assert that the file descriptor is small enough to fit in the
+ * fd_set passed to select
+ */
+ PR_ASSERT(osfd < FD_SETSIZE);
+#endif
+ fd = _PR_Getfd();
+ if (fd) {
+ /* Initialize the members of PRFileDesc and PRFilePrivate */
+ fd->methods = methods;
+ fd->secret->state = _PR_FILEDESC_OPEN;
+ fd->secret->md.osfd = osfd;
+#if defined(_WIN64)
+ fd->secret->alreadyConnected = PR_FALSE;
+ fd->secret->overlappedActive = PR_FALSE;
+#endif
+ _PR_MD_INIT_FILEDESC(fd);
+ } else {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+
+ return fd;
+}
+
+PR_IMPLEMENT(void) PR_FreeFileDesc(PRFileDesc *fd)
+{
+ PR_ASSERT(fd);
+ _PR_Putfd(fd);
+}
+
+#if defined(_WIN64) && defined(WIN95)
+
+PRFileDescList *_fd_waiting_for_overlapped_done = NULL;
+PRLock *_fd_waiting_for_overlapped_done_lock = NULL;
+
+void CheckOverlappedPendingSocketsAreDone()
+{
+ if (!_fd_waiting_for_overlapped_done_lock ||
+ !_fd_waiting_for_overlapped_done) {
+ return;
+ }
+
+ PR_Lock(_fd_waiting_for_overlapped_done_lock);
+
+ PRFileDescList *cur = _fd_waiting_for_overlapped_done;
+ PRFileDescList *previous = NULL;
+ while (cur) {
+ PR_ASSERT(cur->fd->secret->overlappedActive);
+ PRFileDesc *fd = cur->fd;
+ DWORD rvSent;
+ if (GetOverlappedResult((HANDLE)fd->secret->md.osfd, &fd->secret->ol, &rvSent, FALSE) == TRUE) {
+ fd->secret->overlappedActive = PR_FALSE;
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("CheckOverlappedPendingSocketsAreDone GetOverlappedResult succeeded\n"));
+ } else {
+ DWORD err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("CheckOverlappedPendingSocketsAreDone GetOverlappedResult failed %d\n", err));
+ if (err != ERROR_IO_INCOMPLETE) {
+ fd->secret->overlappedActive = PR_FALSE;
+ }
+ }
+
+ if (!fd->secret->overlappedActive) {
+
+ _PR_MD_CLOSE_SOCKET(fd->secret->md.osfd);
+ fd->secret->state = _PR_FILEDESC_CLOSED;
+#ifdef _PR_HAVE_PEEK_BUFFER
+ if (fd->secret->peekBuffer) {
+ PR_ASSERT(fd->secret->peekBufSize > 0);
+ PR_DELETE(fd->secret->peekBuffer);
+ fd->secret->peekBufSize = 0;
+ fd->secret->peekBytes = 0;
+ }
+#endif
+
+ PR_FreeFileDesc(fd);
+
+ if (previous) {
+ previous->next = cur->next;
+ } else {
+ _fd_waiting_for_overlapped_done = cur->next;
+ }
+ PRFileDescList *del = cur;
+ cur = cur->next;
+ PR_Free(del);
+ } else {
+ previous = cur;
+ cur = cur->next;
+ }
+ }
+
+ PR_Unlock(_fd_waiting_for_overlapped_done_lock);
+}
+#endif
+
+/*
+** Wait for some i/o to finish on one or more more poll descriptors.
+*/
+PR_IMPLEMENT(PRInt32) PR_Poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+#if defined(_WIN64) && defined(WIN95)
+ // For each iteration check if TFO overlapped IOs are down.
+ CheckOverlappedPendingSocketsAreDone();
+#endif
+
+ return(_PR_MD_PR_POLL(pds, npds, timeout));
+}
+
+/*
+** Set the inheritance attribute of a file descriptor.
+*/
+PR_IMPLEMENT(PRStatus) PR_SetFDInheritable(
+ PRFileDesc *fd,
+ PRBool inheritable)
+{
+#if defined(XP_UNIX) || defined(WIN32) || defined(XP_OS2)
+ /*
+ * Only a non-layered, NSPR file descriptor can be inherited
+ * by a child process.
+ */
+ if (fd->identity != PR_NSPR_IO_LAYER) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (fd->secret->inheritable != inheritable) {
+ if (_PR_MD_SET_FD_INHERITABLE(fd, inheritable) == PR_FAILURE) {
+ return PR_FAILURE;
+ }
+ fd->secret->inheritable = inheritable;
+ }
+ return PR_SUCCESS;
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+#endif
+}
+
+/*
+** This function only has a useful implementation in the debug build of
+** the pthreads version.
+*/
+PR_IMPLEMENT(void) PT_FPrintStats(PRFileDesc *debug_out, const char *msg)
+{
+ /* do nothing */
+} /* PT_FPrintStats */
diff --git a/nsprpub/pr/src/io/priometh.c b/nsprpub/pr/src/io/priometh.c
new file mode 100644
index 0000000000..508719a809
--- /dev/null
+++ b/nsprpub/pr/src/io/priometh.c
@@ -0,0 +1,607 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "primpl.h"
+
+#include <string.h>
+
+/*****************************************************************************/
+/************************** Invalid I/O method object ************************/
+/*****************************************************************************/
+PRIOMethods _pr_faulty_methods = {
+ (PRDescType)0,
+ (PRCloseFN)_PR_InvalidStatus,
+ (PRReadFN)_PR_InvalidInt,
+ (PRWriteFN)_PR_InvalidInt,
+ (PRAvailableFN)_PR_InvalidInt,
+ (PRAvailable64FN)_PR_InvalidInt64,
+ (PRFsyncFN)_PR_InvalidStatus,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ (PRWritevFN)_PR_InvalidInt,
+ (PRConnectFN)_PR_InvalidStatus,
+ (PRAcceptFN)_PR_InvalidDesc,
+ (PRBindFN)_PR_InvalidStatus,
+ (PRListenFN)_PR_InvalidStatus,
+ (PRShutdownFN)_PR_InvalidStatus,
+ (PRRecvFN)_PR_InvalidInt,
+ (PRSendFN)_PR_InvalidInt,
+ (PRRecvfromFN)_PR_InvalidInt,
+ (PRSendtoFN)_PR_InvalidInt,
+ (PRPollFN)_PR_InvalidInt16,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ (PRGetsocknameFN)_PR_InvalidStatus,
+ (PRGetpeernameFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRGetsocketoptionFN)_PR_InvalidStatus,
+ (PRSetsocketoptionFN)_PR_InvalidStatus,
+ (PRSendfileFN)_PR_InvalidInt,
+ (PRConnectcontinueFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+PRIntn _PR_InvalidInt(void)
+{
+ PR_NOT_REACHED("I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return -1;
+} /* _PR_InvalidInt */
+
+PRInt16 _PR_InvalidInt16(void)
+{
+ PR_NOT_REACHED("I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return -1;
+} /* _PR_InvalidInt */
+
+PRInt64 _PR_InvalidInt64(void)
+{
+ PRInt64 rv;
+ LL_I2L(rv, -1);
+ PR_NOT_REACHED("I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return rv;
+} /* _PR_InvalidInt */
+
+/*
+ * An invalid method that returns PRStatus
+ */
+
+PRStatus _PR_InvalidStatus(void)
+{
+ PR_NOT_REACHED("I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+} /* _PR_InvalidDesc */
+
+/*
+ * An invalid method that returns a pointer
+ */
+
+PRFileDesc *_PR_InvalidDesc(void)
+{
+ PR_NOT_REACHED("I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return NULL;
+} /* _PR_InvalidDesc */
+
+PR_IMPLEMENT(PRDescType) PR_GetDescType(PRFileDesc *file)
+{
+ return file->methods->file_type;
+}
+
+PR_IMPLEMENT(PRStatus) PR_Close(PRFileDesc *fd)
+{
+ return (fd->methods->close)(fd);
+}
+
+PR_IMPLEMENT(PRInt32) PR_Read(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ return((fd->methods->read)(fd,buf,amount));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Write(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ return((fd->methods->write)(fd,buf,amount));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence)
+{
+ return((fd->methods->seek)(fd, offset, whence));
+}
+
+PR_IMPLEMENT(PRInt64) PR_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence)
+{
+ return((fd->methods->seek64)(fd, offset, whence));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Available(PRFileDesc *fd)
+{
+ return((fd->methods->available)(fd));
+}
+
+PR_IMPLEMENT(PRInt64) PR_Available64(PRFileDesc *fd)
+{
+ return((fd->methods->available64)(fd));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo(PRFileDesc *fd, PRFileInfo *info)
+{
+ return((fd->methods->fileInfo)(fd, info));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo64(PRFileDesc *fd, PRFileInfo64 *info)
+{
+ return((fd->methods->fileInfo64)(fd, info));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Sync(PRFileDesc *fd)
+{
+ return((fd->methods->fsync)(fd));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Connect(
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ return((fd->methods->connect)(fd,addr,timeout));
+}
+
+PR_IMPLEMENT(PRStatus) PR_ConnectContinue(
+ PRFileDesc *fd, PRInt16 out_flags)
+{
+ return((fd->methods->connectcontinue)(fd,out_flags));
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_Accept(PRFileDesc *fd, PRNetAddr *addr,
+ PRIntervalTime timeout)
+{
+ return((fd->methods->accept)(fd,addr,timeout));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Bind(PRFileDesc *fd, const PRNetAddr *addr)
+{
+ return((fd->methods->bind)(fd,addr));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Shutdown(PRFileDesc *fd, PRShutdownHow how)
+{
+ return((fd->methods->shutdown)(fd,how));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Listen(PRFileDesc *fd, PRIntn backlog)
+{
+ return((fd->methods->listen)(fd,backlog));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Recv(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ return((fd->methods->recv)(fd,buf,amount,flags,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Send(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ return((fd->methods->send)(fd,buf,amount,flags,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Writev(PRFileDesc *fd, const PRIOVec *iov,
+ PRInt32 iov_size, PRIntervalTime timeout)
+{
+ if (iov_size > PR_MAX_IOVECTOR_SIZE)
+ {
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ return -1;
+ }
+ return((fd->methods->writev)(fd,iov,iov_size,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ return((fd->methods->recvfrom)(fd,buf,amount,flags,addr,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_SendTo(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ return((fd->methods->sendto)(fd,buf,amount,flags,addr,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_TransmitFile(
+ PRFileDesc *sd, PRFileDesc *fd, const void *hdr, PRInt32 hlen,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ return((sd->methods->transmitfile)(sd,fd,hdr,hlen,flags,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_AcceptRead(
+ PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
+ void *buf, PRInt32 amount, PRIntervalTime timeout)
+{
+ return((sd->methods->acceptread)(sd, nd, raddr, buf, amount,timeout));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetSockName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ return((fd->methods->getsockname)(fd,addr));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ return((fd->methods->getpeername)(fd,addr));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetSocketOption(
+ PRFileDesc *fd, PRSocketOptionData *data)
+{
+ return((fd->methods->getsocketoption)(fd, data));
+}
+
+PR_IMPLEMENT(PRStatus) PR_SetSocketOption(
+ PRFileDesc *fd, const PRSocketOptionData *data)
+{
+ return((fd->methods->setsocketoption)(fd, data));
+}
+
+PR_IMPLEMENT(PRInt32) PR_SendFile(
+ PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ return((sd->methods->sendfile)(sd,sfd,flags,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_EmulateAcceptRead(
+ PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
+ void *buf, PRInt32 amount, PRIntervalTime timeout)
+{
+ PRInt32 rv = -1;
+ PRNetAddr remote;
+ PRFileDesc *accepted = NULL;
+
+ /*
+ ** The timeout does not apply to the accept portion of the
+ ** operation - it waits indefinitely.
+ */
+ accepted = PR_Accept(sd, &remote, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == accepted) {
+ return rv;
+ }
+
+ rv = PR_Recv(accepted, buf, amount, 0, timeout);
+ if (rv >= 0)
+ {
+ /* copy the new info out where caller can see it */
+#define AMASK ((PRPtrdiff)7) /* mask for alignment of PRNetAddr */
+ PRPtrdiff aligned = (PRPtrdiff)buf + amount + AMASK;
+ *raddr = (PRNetAddr*)(aligned & ~AMASK);
+ memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote));
+ *nd = accepted;
+ return rv;
+ }
+
+ PR_Close(accepted);
+ return rv;
+}
+
+/*
+ * PR_EmulateSendFile
+ *
+ * Send file sfd->fd across socket sd. If header/trailer are specified
+ * they are sent before and after the file, respectively.
+ *
+ * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
+ *
+ * return number of bytes sent or -1 on error
+ *
+ */
+
+#if defined(XP_UNIX) || defined(WIN32)
+
+/*
+ * An implementation based on memory-mapped files
+ */
+
+#define SENDFILE_MMAP_CHUNK (256 * 1024)
+
+PR_IMPLEMENT(PRInt32) PR_EmulateSendFile(
+ PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ PRInt32 rv, count = 0;
+ PRInt32 len, file_bytes, index = 0;
+ PRFileInfo info;
+ PRIOVec iov[3];
+ PRFileMap *mapHandle = NULL;
+ void *addr = (void*)0; /* initialized to some arbitrary value. Keeps compiler warnings down. */
+ PRUint32 file_mmap_offset, alignment;
+ PRInt64 zero64;
+ PROffset64 file_mmap_offset64;
+ PRUint32 addr_offset, mmap_len;
+
+ /* Get file size */
+ if (PR_SUCCESS != PR_GetOpenFileInfo(sfd->fd, &info)) {
+ count = -1;
+ goto done;
+ }
+ if (sfd->file_nbytes &&
+ (info.size < (sfd->file_offset + sfd->file_nbytes))) {
+ /*
+ * there are fewer bytes in file to send than specified
+ */
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ count = -1;
+ goto done;
+ }
+ if (sfd->file_nbytes) {
+ file_bytes = sfd->file_nbytes;
+ }
+ else {
+ file_bytes = info.size - sfd->file_offset;
+ }
+
+ alignment = PR_GetMemMapAlignment();
+
+ /* number of initial bytes to skip in mmap'd segment */
+ addr_offset = sfd->file_offset % alignment;
+
+ /* find previous mmap alignment boundary */
+ file_mmap_offset = sfd->file_offset - addr_offset;
+
+ /*
+ * If the file is large, mmap and send the file in chunks so as
+ * to not consume too much virtual address space
+ */
+ mmap_len = PR_MIN(file_bytes + addr_offset, SENDFILE_MMAP_CHUNK);
+ len = mmap_len - addr_offset;
+
+ /*
+ * Map in (part of) file. Take care of zero-length files.
+ */
+ if (len) {
+ LL_I2L(zero64, 0);
+ mapHandle = PR_CreateFileMap(sfd->fd, zero64, PR_PROT_READONLY);
+ if (!mapHandle) {
+ count = -1;
+ goto done;
+ }
+ LL_I2L(file_mmap_offset64, file_mmap_offset);
+ addr = PR_MemMap(mapHandle, file_mmap_offset64, mmap_len);
+ if (!addr) {
+ count = -1;
+ goto done;
+ }
+ }
+ /*
+ * send headers first, followed by the file
+ */
+ if (sfd->hlen) {
+ iov[index].iov_base = (char *) sfd->header;
+ iov[index].iov_len = sfd->hlen;
+ index++;
+ }
+ if (len) {
+ iov[index].iov_base = (char*)addr + addr_offset;
+ iov[index].iov_len = len;
+ index++;
+ }
+ if ((file_bytes == len) && (sfd->tlen)) {
+ /*
+ * all file data is mapped in; send the trailer too
+ */
+ iov[index].iov_base = (char *) sfd->trailer;
+ iov[index].iov_len = sfd->tlen;
+ index++;
+ }
+ rv = PR_Writev(sd, iov, index, timeout);
+ if (len) {
+ PR_MemUnmap(addr, mmap_len);
+ }
+ if (rv < 0) {
+ count = -1;
+ goto done;
+ }
+
+ PR_ASSERT(rv == sfd->hlen + len + ((len == file_bytes) ? sfd->tlen : 0));
+
+ file_bytes -= len;
+ count += rv;
+ if (!file_bytes) { /* header, file and trailer are sent */
+ goto done;
+ }
+
+ /*
+ * send remaining bytes of the file, if any
+ */
+ len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK);
+ while (len > 0) {
+ /*
+ * Map in (part of) file
+ */
+ file_mmap_offset = sfd->file_offset + count - sfd->hlen;
+ PR_ASSERT((file_mmap_offset % alignment) == 0);
+
+ LL_I2L(file_mmap_offset64, file_mmap_offset);
+ addr = PR_MemMap(mapHandle, file_mmap_offset64, len);
+ if (!addr) {
+ count = -1;
+ goto done;
+ }
+ rv = PR_Send(sd, addr, len, 0, timeout);
+ PR_MemUnmap(addr, len);
+ if (rv < 0) {
+ count = -1;
+ goto done;
+ }
+
+ PR_ASSERT(rv == len);
+ file_bytes -= rv;
+ count += rv;
+ len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK);
+ }
+ PR_ASSERT(0 == file_bytes);
+ if (sfd->tlen) {
+ rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout);
+ if (rv >= 0) {
+ PR_ASSERT(rv == sfd->tlen);
+ count += rv;
+ } else {
+ count = -1;
+ }
+ }
+done:
+ if (mapHandle) {
+ PR_CloseFileMap(mapHandle);
+ }
+ if ((count >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) {
+ PR_Close(sd);
+ }
+ return count;
+}
+
+#else
+
+PR_IMPLEMENT(PRInt32) PR_EmulateSendFile(
+ PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ PRInt32 rv, count = 0;
+ PRInt32 rlen;
+ const void * buffer;
+ PRInt32 buflen;
+ PRInt32 sendbytes, readbytes;
+ char *buf;
+
+#define _SENDFILE_BUFSIZE (16 * 1024)
+
+ buf = (char*)PR_MALLOC(_SENDFILE_BUFSIZE);
+ if (buf == NULL) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+
+ /*
+ * send header first
+ */
+ buflen = sfd->hlen;
+ buffer = sfd->header;
+ while (buflen) {
+ rv = PR_Send(sd, buffer, buflen, 0, timeout);
+ if (rv < 0) {
+ /* PR_Send() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ } else {
+ count += rv;
+ buffer = (const void*) ((const char*)buffer + rv);
+ buflen -= rv;
+ }
+ }
+
+ /*
+ * send file next
+ */
+ if (PR_Seek(sfd->fd, sfd->file_offset, PR_SEEK_SET) < 0) {
+ rv = -1;
+ goto done;
+ }
+ sendbytes = sfd->file_nbytes;
+ if (sendbytes == 0) {
+ /* send entire file */
+ while ((rlen = PR_Read(sfd->fd, buf, _SENDFILE_BUFSIZE)) > 0) {
+ while (rlen) {
+ char *bufptr = buf;
+
+ rv = PR_Send(sd, bufptr, rlen, 0, timeout);
+ if (rv < 0) {
+ /* PR_Send() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ } else {
+ count += rv;
+ bufptr = ((char*)bufptr + rv);
+ rlen -= rv;
+ }
+ }
+ }
+ if (rlen < 0) {
+ /* PR_Read() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ }
+ } else {
+ readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE);
+ while (readbytes && ((rlen = PR_Read(sfd->fd, buf, readbytes)) > 0)) {
+ while (rlen) {
+ char *bufptr = buf;
+
+ rv = PR_Send(sd, bufptr, rlen, 0, timeout);
+ if (rv < 0) {
+ /* PR_Send() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ } else {
+ count += rv;
+ sendbytes -= rv;
+ bufptr = ((char*)bufptr + rv);
+ rlen -= rv;
+ }
+ }
+ readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE);
+ }
+ if (rlen < 0) {
+ /* PR_Read() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ } else if (sendbytes != 0) {
+ /*
+ * there are fewer bytes in file to send than specified
+ */
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ }
+
+ /*
+ * send trailer last
+ */
+ buflen = sfd->tlen;
+ buffer = sfd->trailer;
+ while (buflen) {
+ rv = PR_Send(sd, buffer, buflen, 0, timeout);
+ if (rv < 0) {
+ /* PR_Send() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ } else {
+ count += rv;
+ buffer = (const void*) ((const char*)buffer + rv);
+ buflen -= rv;
+ }
+ }
+ rv = count;
+
+done:
+ if (buf) {
+ PR_DELETE(buf);
+ }
+ if ((rv >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) {
+ PR_Close(sd);
+ }
+ return rv;
+}
+
+#endif
+
+/* priometh.c */
diff --git a/nsprpub/pr/src/io/pripv6.c b/nsprpub/pr/src/io/pripv6.c
new file mode 100644
index 0000000000..1c299652e6
--- /dev/null
+++ b/nsprpub/pr/src/io/pripv6.c
@@ -0,0 +1,372 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: pripv6.c
+** Description: Support for various functions unique to IPv6
+*/
+#include "primpl.h"
+#include <string.h>
+
+#if !defined(_PR_INET6) || defined(_PR_INET6_PROBE)
+
+static PRIOMethods ipv6_to_v4_tcpMethods;
+static PRIOMethods ipv6_to_v4_udpMethods;
+static PRDescIdentity _pr_ipv6_to_ipv4_id;
+extern PRBool IsValidNetAddr(const PRNetAddr *addr);
+extern const PRIPv6Addr _pr_in6addr_any;
+extern const PRIPv6Addr _pr_in6addr_loopback;
+
+/*
+ * convert an IPv4-mapped IPv6 addr to an IPv4 addr
+ */
+static void _PR_ConvertToIpv4NetAddr(const PRNetAddr *src_v6addr,
+ PRNetAddr *dst_v4addr)
+{
+ const PRUint8 *srcp;
+
+ PR_ASSERT(PR_AF_INET6 == src_v6addr->ipv6.family);
+
+ if (PR_IsNetAddrType(src_v6addr, PR_IpAddrV4Mapped)) {
+ srcp = src_v6addr->ipv6.ip.pr_s6_addr;
+ memcpy((char *) &dst_v4addr->inet.ip, srcp + 12, 4);
+ } else if (PR_IsNetAddrType(src_v6addr, PR_IpAddrAny)) {
+ dst_v4addr->inet.ip = htonl(INADDR_ANY);
+ } else if (PR_IsNetAddrType(src_v6addr, PR_IpAddrLoopback)) {
+ dst_v4addr->inet.ip = htonl(INADDR_LOOPBACK);
+ }
+ dst_v4addr->inet.family = PR_AF_INET;
+ dst_v4addr->inet.port = src_v6addr->ipv6.port;
+}
+
+/*
+ * convert an IPv4 addr to an IPv4-mapped IPv6 addr
+ */
+static void _PR_ConvertToIpv6NetAddr(const PRNetAddr *src_v4addr,
+ PRNetAddr *dst_v6addr)
+{
+ PRUint8 *dstp;
+
+ PR_ASSERT(PR_AF_INET == src_v4addr->inet.family);
+ dst_v6addr->ipv6.family = PR_AF_INET6;
+ dst_v6addr->ipv6.port = src_v4addr->inet.port;
+
+ if (htonl(INADDR_ANY) == src_v4addr->inet.ip) {
+ dst_v6addr->ipv6.ip = _pr_in6addr_any;
+ } else {
+ dstp = dst_v6addr->ipv6.ip.pr_s6_addr;
+ memset(dstp, 0, 10);
+ memset(dstp + 10, 0xff, 2);
+ memcpy(dstp + 12,(char *) &src_v4addr->inet.ip, 4);
+ }
+}
+
+static PRStatus PR_CALLBACK Ipv6ToIpv4SocketBind(PRFileDesc *fd,
+ const PRNetAddr *addr)
+{
+ PRNetAddr tmp_ipv4addr;
+ const PRNetAddr *tmp_addrp;
+ PRFileDesc *lo = fd->lower;
+
+ if (PR_AF_INET6 != addr->raw.family) {
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped) ||
+ PR_IsNetAddrType(addr, PR_IpAddrAny)) {
+ _PR_ConvertToIpv4NetAddr(addr, &tmp_ipv4addr);
+ tmp_addrp = &tmp_ipv4addr;
+ } else {
+ PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, 0);
+ return PR_FAILURE;
+ }
+ return((lo->methods->bind)(lo,tmp_addrp));
+}
+
+static PRStatus PR_CALLBACK Ipv6ToIpv4SocketConnect(
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRNetAddr tmp_ipv4addr;
+ const PRNetAddr *tmp_addrp;
+
+ if (PR_AF_INET6 != addr->raw.family) {
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped) ||
+ PR_IsNetAddrType(addr, PR_IpAddrLoopback)) {
+ _PR_ConvertToIpv4NetAddr(addr, &tmp_ipv4addr);
+ tmp_addrp = &tmp_ipv4addr;
+ } else {
+ PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, 0);
+ return PR_FAILURE;
+ }
+ return (fd->lower->methods->connect)(fd->lower, tmp_addrp, timeout);
+}
+
+static PRInt32 PR_CALLBACK Ipv6ToIpv4SocketSendTo(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRNetAddr tmp_ipv4addr;
+ const PRNetAddr *tmp_addrp;
+
+ if (PR_AF_INET6 != addr->raw.family) {
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped) ||
+ PR_IsNetAddrType(addr, PR_IpAddrLoopback)) {
+ _PR_ConvertToIpv4NetAddr(addr, &tmp_ipv4addr);
+ tmp_addrp = &tmp_ipv4addr;
+ } else {
+ PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, 0);
+ return PR_FAILURE;
+ }
+ return (fd->lower->methods->sendto)(
+ fd->lower, buf, amount, flags, tmp_addrp, timeout);
+}
+
+static PRFileDesc* PR_CALLBACK Ipv6ToIpv4SocketAccept (
+ PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRStatus rv;
+ PRFileDesc *newfd;
+ PRFileDesc *newstack;
+ PRNetAddr tmp_ipv4addr;
+ PRNetAddr *addrlower = NULL;
+
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ newstack = PR_NEW(PRFileDesc);
+ if (NULL == newstack)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ *newstack = *fd; /* make a copy of the accepting layer */
+
+ if (addr) {
+ addrlower = &tmp_ipv4addr;
+ }
+ newfd = (fd->lower->methods->accept)(fd->lower, addrlower, timeout);
+ if (NULL == newfd)
+ {
+ PR_DELETE(newstack);
+ return NULL;
+ }
+ if (addr) {
+ _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, addr);
+ }
+
+ rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack);
+ PR_ASSERT(PR_SUCCESS == rv);
+ return newfd; /* that's it */
+}
+
+static PRInt32 PR_CALLBACK Ipv6ToIpv4SocketAcceptRead(PRFileDesc *sd,
+ PRFileDesc **nd, PRNetAddr **ipv6_raddr, void *buf, PRInt32 amount,
+ PRIntervalTime timeout)
+{
+ PRInt32 nbytes;
+ PRStatus rv;
+ PRNetAddr tmp_ipv4addr;
+ PRFileDesc *newstack;
+
+ PR_ASSERT(sd != NULL);
+ PR_ASSERT(sd->lower != NULL);
+
+ newstack = PR_NEW(PRFileDesc);
+ if (NULL == newstack)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ *newstack = *sd; /* make a copy of the accepting layer */
+
+ nbytes = sd->lower->methods->acceptread(
+ sd->lower, nd, ipv6_raddr, buf, amount, timeout);
+ if (-1 == nbytes)
+ {
+ PR_DELETE(newstack);
+ return nbytes;
+ }
+ tmp_ipv4addr = **ipv6_raddr; /* copy */
+ _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, *ipv6_raddr);
+
+ /* this PR_PushIOLayer call cannot fail */
+ rv = PR_PushIOLayer(*nd, PR_TOP_IO_LAYER, newstack);
+ PR_ASSERT(PR_SUCCESS == rv);
+ return nbytes;
+}
+
+static PRStatus PR_CALLBACK Ipv6ToIpv4SocketGetName(PRFileDesc *fd,
+ PRNetAddr *ipv6addr)
+{
+ PRStatus result;
+ PRNetAddr tmp_ipv4addr;
+
+ result = (fd->lower->methods->getsockname)(fd->lower, &tmp_ipv4addr);
+ if (PR_SUCCESS == result) {
+ _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, ipv6addr);
+ PR_ASSERT(IsValidNetAddr(ipv6addr) == PR_TRUE);
+ }
+ return result;
+}
+
+static PRStatus PR_CALLBACK Ipv6ToIpv4SocketGetPeerName(PRFileDesc *fd,
+ PRNetAddr *ipv6addr)
+{
+ PRStatus result;
+ PRNetAddr tmp_ipv4addr;
+
+ result = (fd->lower->methods->getpeername)(fd->lower, &tmp_ipv4addr);
+ if (PR_SUCCESS == result) {
+ _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, ipv6addr);
+ PR_ASSERT(IsValidNetAddr(ipv6addr) == PR_TRUE);
+ }
+ return result;
+}
+
+static PRInt32 PR_CALLBACK Ipv6ToIpv4SocketRecvFrom(PRFileDesc *fd, void *buf,
+ PRInt32 amount, PRIntn flags, PRNetAddr *ipv6addr,
+ PRIntervalTime timeout)
+{
+ PRNetAddr tmp_ipv4addr;
+ PRInt32 result;
+
+ result = (fd->lower->methods->recvfrom)(
+ fd->lower, buf, amount, flags, &tmp_ipv4addr, timeout);
+ if (-1 != result) {
+ _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, ipv6addr);
+ PR_ASSERT(IsValidNetAddr(ipv6addr) == PR_TRUE);
+ }
+ return result;
+}
+
+#if defined(_PR_INET6_PROBE)
+static PRBool ipv6_is_present;
+PR_EXTERN(PRBool) _pr_test_ipv6_socket(void);
+
+#if !defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME)
+extern PRStatus _pr_find_getipnodebyname(void);
+#endif
+
+#if !defined(_PR_INET6) && defined(_PR_HAVE_GETADDRINFO)
+extern PRStatus _pr_find_getaddrinfo(void);
+#endif
+
+static PRBool
+_pr_probe_ipv6_presence(void)
+{
+#if !defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME)
+ if (_pr_find_getipnodebyname() != PR_SUCCESS) {
+ return PR_FALSE;
+ }
+#endif
+
+#if !defined(_PR_INET6) && defined(_PR_HAVE_GETADDRINFO)
+ if (_pr_find_getaddrinfo() != PR_SUCCESS) {
+ return PR_FALSE;
+ }
+#endif
+
+ return _pr_test_ipv6_socket();
+}
+#endif /* _PR_INET6_PROBE */
+
+static PRCallOnceType _pr_init_ipv6_once;
+
+static PRStatus PR_CALLBACK _pr_init_ipv6(void)
+{
+ const PRIOMethods *stubMethods;
+
+#if defined(_PR_INET6_PROBE)
+ ipv6_is_present = _pr_probe_ipv6_presence();
+ if (ipv6_is_present) {
+ return PR_SUCCESS;
+ }
+#endif
+
+ _pr_ipv6_to_ipv4_id = PR_GetUniqueIdentity("Ipv6_to_Ipv4 layer");
+ PR_ASSERT(PR_INVALID_IO_LAYER != _pr_ipv6_to_ipv4_id);
+
+ stubMethods = PR_GetDefaultIOMethods();
+
+ ipv6_to_v4_tcpMethods = *stubMethods; /* first get the entire batch */
+ /* then override the ones we care about */
+ ipv6_to_v4_tcpMethods.connect = Ipv6ToIpv4SocketConnect;
+ ipv6_to_v4_tcpMethods.bind = Ipv6ToIpv4SocketBind;
+ ipv6_to_v4_tcpMethods.accept = Ipv6ToIpv4SocketAccept;
+ ipv6_to_v4_tcpMethods.acceptread = Ipv6ToIpv4SocketAcceptRead;
+ ipv6_to_v4_tcpMethods.getsockname = Ipv6ToIpv4SocketGetName;
+ ipv6_to_v4_tcpMethods.getpeername = Ipv6ToIpv4SocketGetPeerName;
+ /*
+ ipv6_to_v4_tcpMethods.getsocketoption = Ipv6ToIpv4GetSocketOption;
+ ipv6_to_v4_tcpMethods.setsocketoption = Ipv6ToIpv4SetSocketOption;
+ */
+ ipv6_to_v4_udpMethods = *stubMethods; /* first get the entire batch */
+ /* then override the ones we care about */
+ ipv6_to_v4_udpMethods.connect = Ipv6ToIpv4SocketConnect;
+ ipv6_to_v4_udpMethods.bind = Ipv6ToIpv4SocketBind;
+ ipv6_to_v4_udpMethods.sendto = Ipv6ToIpv4SocketSendTo;
+ ipv6_to_v4_udpMethods.recvfrom = Ipv6ToIpv4SocketRecvFrom;
+ ipv6_to_v4_udpMethods.getsockname = Ipv6ToIpv4SocketGetName;
+ ipv6_to_v4_udpMethods.getpeername = Ipv6ToIpv4SocketGetPeerName;
+ /*
+ ipv6_to_v4_udpMethods.getsocketoption = Ipv6ToIpv4GetSocketOption;
+ ipv6_to_v4_udpMethods.setsocketoption = Ipv6ToIpv4SetSocketOption;
+ */
+ return PR_SUCCESS;
+}
+
+#if defined(_PR_INET6_PROBE)
+PRBool _pr_ipv6_is_present(void)
+{
+ if (PR_CallOnce(&_pr_init_ipv6_once, _pr_init_ipv6) != PR_SUCCESS) {
+ return PR_FALSE;
+ }
+ return ipv6_is_present;
+}
+#endif
+
+PR_IMPLEMENT(PRStatus) _pr_push_ipv6toipv4_layer(PRFileDesc *fd)
+{
+ PRFileDesc *ipv6_fd = NULL;
+
+ if (PR_CallOnce(&_pr_init_ipv6_once, _pr_init_ipv6) != PR_SUCCESS) {
+ return PR_FAILURE;
+ }
+
+ /*
+ * For platforms with no support for IPv6
+ * create layered socket for IPv4-mapped IPv6 addresses
+ */
+ if (fd->methods->file_type == PR_DESC_SOCKET_TCP)
+ ipv6_fd = PR_CreateIOLayerStub(_pr_ipv6_to_ipv4_id,
+ &ipv6_to_v4_tcpMethods);
+ else
+ ipv6_fd = PR_CreateIOLayerStub(_pr_ipv6_to_ipv4_id,
+ &ipv6_to_v4_udpMethods);
+ if (NULL == ipv6_fd) {
+ goto errorExit;
+ }
+ ipv6_fd->secret = NULL;
+
+ if (PR_PushIOLayer(fd, PR_TOP_IO_LAYER, ipv6_fd) == PR_FAILURE) {
+ goto errorExit;
+ }
+
+ return PR_SUCCESS;
+errorExit:
+
+ if (ipv6_fd) {
+ ipv6_fd->dtor(ipv6_fd);
+ }
+ return PR_FAILURE;
+}
+
+#endif /* !defined(_PR_INET6) || defined(_PR_INET6_PROBE) */
diff --git a/nsprpub/pr/src/io/prlayer.c b/nsprpub/pr/src/io/prlayer.c
new file mode 100644
index 0000000000..1e63b7b723
--- /dev/null
+++ b/nsprpub/pr/src/io/prlayer.c
@@ -0,0 +1,785 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: prlayer.c
+** Description: Routines for handling pushable protocol modules on sockets.
+*/
+
+#include "primpl.h"
+#include "prerror.h"
+#include "prmem.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prio.h"
+
+#include <string.h> /* for memset() */
+static PRStatus _PR_DestroyIOLayer(PRFileDesc *stack);
+
+void PR_CALLBACK pl_FDDestructor(PRFileDesc *fd)
+{
+ PR_ASSERT(fd != NULL);
+ if (NULL != fd->lower) {
+ fd->lower->higher = fd->higher;
+ }
+ if (NULL != fd->higher) {
+ fd->higher->lower = fd->lower;
+ }
+ PR_DELETE(fd);
+}
+
+/*
+** Default methods that just call down to the next fd.
+*/
+static PRStatus PR_CALLBACK pl_TopClose (PRFileDesc *fd)
+{
+ PRFileDesc *top, *lower;
+ PRStatus rv;
+
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+ PR_ASSERT(fd->secret == NULL);
+ PR_ASSERT(fd->methods->file_type == PR_DESC_LAYERED);
+
+ if (PR_IO_LAYER_HEAD == fd->identity) {
+ /*
+ * new style stack; close all the layers, before deleting the
+ * stack head
+ */
+ rv = fd->lower->methods->close(fd->lower);
+ _PR_DestroyIOLayer(fd);
+ return rv;
+ }
+ if ((fd->higher) && (PR_IO_LAYER_HEAD == fd->higher->identity)) {
+ /*
+ * lower layers of new style stack
+ */
+ lower = fd->lower;
+ /*
+ * pop and cleanup current layer
+ */
+ top = PR_PopIOLayer(fd->higher, PR_TOP_IO_LAYER);
+ top->dtor(top);
+ /*
+ * then call lower layer
+ */
+ return (lower->methods->close(lower));
+ } else {
+ /* old style stack */
+ top = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
+ top->dtor(top);
+ return (fd->methods->close)(fd);
+ }
+}
+
+static PRInt32 PR_CALLBACK pl_DefRead (PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->read)(fd->lower, buf, amount);
+}
+
+static PRInt32 PR_CALLBACK pl_DefWrite (
+ PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->write)(fd->lower, buf, amount);
+}
+
+static PRInt32 PR_CALLBACK pl_DefAvailable (PRFileDesc *fd)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->available)(fd->lower);
+}
+
+static PRInt64 PR_CALLBACK pl_DefAvailable64 (PRFileDesc *fd)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->available64)(fd->lower);
+}
+
+static PRStatus PR_CALLBACK pl_DefFsync (PRFileDesc *fd)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->fsync)(fd->lower);
+}
+
+static PRInt32 PR_CALLBACK pl_DefSeek (
+ PRFileDesc *fd, PRInt32 offset, PRSeekWhence how)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->seek)(fd->lower, offset, how);
+}
+
+static PRInt64 PR_CALLBACK pl_DefSeek64 (
+ PRFileDesc *fd, PRInt64 offset, PRSeekWhence how)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->seek64)(fd->lower, offset, how);
+}
+
+static PRStatus PR_CALLBACK pl_DefFileInfo (PRFileDesc *fd, PRFileInfo *info)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->fileInfo)(fd->lower, info);
+}
+
+static PRStatus PR_CALLBACK pl_DefFileInfo64 (PRFileDesc *fd, PRFileInfo64 *info)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->fileInfo64)(fd->lower, info);
+}
+
+static PRInt32 PR_CALLBACK pl_DefWritev (PRFileDesc *fd, const PRIOVec *iov,
+ PRInt32 size, PRIntervalTime timeout)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->writev)(fd->lower, iov, size, timeout);
+}
+
+static PRStatus PR_CALLBACK pl_DefConnect (
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->connect)(fd->lower, addr, timeout);
+}
+
+static PRStatus PR_CALLBACK pl_DefConnectcontinue (
+ PRFileDesc *fd, PRInt16 out_flags)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->connectcontinue)(fd->lower, out_flags);
+}
+
+static PRFileDesc* PR_CALLBACK pl_TopAccept (
+ PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRStatus rv;
+ PRFileDesc *newfd, *layer = fd;
+ PRFileDesc *newstack;
+ PRBool newstyle_stack = PR_FALSE;
+
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ /* test for new style stack */
+ while (NULL != layer->higher) {
+ layer = layer->higher;
+ }
+ newstyle_stack = (PR_IO_LAYER_HEAD == layer->identity) ? PR_TRUE : PR_FALSE;
+ newstack = PR_NEW(PRFileDesc);
+ if (NULL == newstack)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ *newstack = *fd; /* make a copy of the accepting layer */
+
+ newfd = (fd->lower->methods->accept)(fd->lower, addr, timeout);
+ if (NULL == newfd)
+ {
+ PR_DELETE(newstack);
+ return NULL;
+ }
+
+ if (newstyle_stack)
+ {
+ newstack->lower = newfd;
+ newfd->higher = newstack;
+ return newstack;
+ }
+ /* this PR_PushIOLayer call cannot fail */
+ rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack);
+ PR_ASSERT(PR_SUCCESS == rv);
+ return newfd; /* that's it */
+}
+
+static PRStatus PR_CALLBACK pl_DefBind (PRFileDesc *fd, const PRNetAddr *addr)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->bind)(fd->lower, addr);
+}
+
+static PRStatus PR_CALLBACK pl_DefListen (PRFileDesc *fd, PRIntn backlog)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->listen)(fd->lower, backlog);
+}
+
+static PRStatus PR_CALLBACK pl_DefShutdown (PRFileDesc *fd, PRIntn how)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->shutdown)(fd->lower, how);
+}
+
+static PRInt32 PR_CALLBACK pl_DefRecv (
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->recv)(
+ fd->lower, buf, amount, flags, timeout);
+}
+
+static PRInt32 PR_CALLBACK pl_DefSend (
+ PRFileDesc *fd, const void *buf,
+ PRInt32 amount, PRIntn flags, PRIntervalTime timeout)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->send)(fd->lower, buf, amount, flags, timeout);
+}
+
+static PRInt32 PR_CALLBACK pl_DefRecvfrom (
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->recvfrom)(
+ fd->lower, buf, amount, flags, addr, timeout);
+}
+
+static PRInt32 PR_CALLBACK pl_DefSendto (
+ PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->sendto)(
+ fd->lower, buf, amount, flags, addr, timeout);
+}
+
+static PRInt16 PR_CALLBACK pl_DefPoll (
+ PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->poll)(fd->lower, in_flags, out_flags);
+}
+
+static PRInt32 PR_CALLBACK pl_DefAcceptread (
+ PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf,
+ PRInt32 amount, PRIntervalTime t)
+{
+ PRInt32 nbytes;
+ PRStatus rv;
+ PRFileDesc *newstack;
+ PRFileDesc *layer = sd;
+ PRBool newstyle_stack = PR_FALSE;
+
+ PR_ASSERT(sd != NULL);
+ PR_ASSERT(sd->lower != NULL);
+
+ /* test for new style stack */
+ while (NULL != layer->higher) {
+ layer = layer->higher;
+ }
+ newstyle_stack = (PR_IO_LAYER_HEAD == layer->identity) ? PR_TRUE : PR_FALSE;
+ newstack = PR_NEW(PRFileDesc);
+ if (NULL == newstack)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ *newstack = *sd; /* make a copy of the accepting layer */
+
+ nbytes = sd->lower->methods->acceptread(
+ sd->lower, nd, raddr, buf, amount, t);
+ if (-1 == nbytes)
+ {
+ PR_DELETE(newstack);
+ return nbytes;
+ }
+ if (newstyle_stack) {
+ newstack->lower = *nd;
+ (*nd)->higher = newstack;
+ *nd = newstack;
+ return nbytes;
+ }
+ /* this PR_PushIOLayer call cannot fail */
+ rv = PR_PushIOLayer(*nd, PR_TOP_IO_LAYER, newstack);
+ PR_ASSERT(PR_SUCCESS == rv);
+ return nbytes;
+}
+
+static PRInt32 PR_CALLBACK pl_DefTransmitfile (
+ PRFileDesc *sd, PRFileDesc *fd, const void *headers, PRInt32 hlen,
+ PRTransmitFileFlags flags, PRIntervalTime t)
+{
+ PR_ASSERT(sd != NULL);
+ PR_ASSERT(sd->lower != NULL);
+
+ return sd->lower->methods->transmitfile(
+ sd->lower, fd, headers, hlen, flags, t);
+}
+
+static PRStatus PR_CALLBACK pl_DefGetsockname (PRFileDesc *fd, PRNetAddr *addr)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->getsockname)(fd->lower, addr);
+}
+
+static PRStatus PR_CALLBACK pl_DefGetpeername (PRFileDesc *fd, PRNetAddr *addr)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->getpeername)(fd->lower, addr);
+}
+
+static PRStatus PR_CALLBACK pl_DefGetsocketoption (
+ PRFileDesc *fd, PRSocketOptionData *data)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->getsocketoption)(fd->lower, data);
+}
+
+static PRStatus PR_CALLBACK pl_DefSetsocketoption (
+ PRFileDesc *fd, const PRSocketOptionData *data)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->setsocketoption)(fd->lower, data);
+}
+
+static PRInt32 PR_CALLBACK pl_DefSendfile (
+ PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ PR_ASSERT(sd != NULL);
+ PR_ASSERT(sd->lower != NULL);
+
+ return sd->lower->methods->sendfile(
+ sd->lower, sfd, flags, timeout);
+}
+
+/* Methods for the top of the stack. Just call down to the next fd. */
+static PRIOMethods pl_methods = {
+ PR_DESC_LAYERED,
+ pl_TopClose,
+ pl_DefRead,
+ pl_DefWrite,
+ pl_DefAvailable,
+ pl_DefAvailable64,
+ pl_DefFsync,
+ pl_DefSeek,
+ pl_DefSeek64,
+ pl_DefFileInfo,
+ pl_DefFileInfo64,
+ pl_DefWritev,
+ pl_DefConnect,
+ pl_TopAccept,
+ pl_DefBind,
+ pl_DefListen,
+ pl_DefShutdown,
+ pl_DefRecv,
+ pl_DefSend,
+ pl_DefRecvfrom,
+ pl_DefSendto,
+ pl_DefPoll,
+ pl_DefAcceptread,
+ pl_DefTransmitfile,
+ pl_DefGetsockname,
+ pl_DefGetpeername,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ pl_DefGetsocketoption,
+ pl_DefSetsocketoption,
+ pl_DefSendfile,
+ pl_DefConnectcontinue,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+PR_IMPLEMENT(const PRIOMethods*) PR_GetDefaultIOMethods(void)
+{
+ return &pl_methods;
+} /* PR_GetDefaultIOMethods */
+
+PR_IMPLEMENT(PRFileDesc*) PR_CreateIOLayerStub(
+ PRDescIdentity ident, const PRIOMethods *methods)
+{
+ PRFileDesc *fd = NULL;
+ PR_ASSERT((PR_NSPR_IO_LAYER != ident) && (PR_TOP_IO_LAYER != ident));
+ if ((PR_NSPR_IO_LAYER == ident) || (PR_TOP_IO_LAYER == ident)) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ }
+ else
+ {
+ fd = PR_NEWZAP(PRFileDesc);
+ if (NULL == fd) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ else
+ {
+ fd->methods = methods;
+ fd->dtor = pl_FDDestructor;
+ fd->identity = ident;
+ }
+ }
+ return fd;
+} /* PR_CreateIOLayerStub */
+
+/*
+ * PR_CreateIOLayer
+ * Create a new style stack, where the stack top is a dummy header.
+ * Unlike the old style stacks, the contents of the stack head
+ * are not modified when a layer is pushed onto or popped from a new
+ * style stack.
+ */
+
+PR_IMPLEMENT(PRFileDesc*) PR_CreateIOLayer(PRFileDesc *top)
+{
+ PRFileDesc *fd = NULL;
+
+ fd = PR_NEWZAP(PRFileDesc);
+ if (NULL == fd) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ else
+ {
+ fd->methods = &pl_methods;
+ fd->dtor = pl_FDDestructor;
+ fd->identity = PR_IO_LAYER_HEAD;
+ fd->higher = NULL;
+ fd->lower = top;
+ top->higher = fd;
+ top->lower = NULL;
+ }
+ return fd;
+} /* PR_CreateIOLayer */
+
+/*
+ * _PR_DestroyIOLayer
+ * Delete the stack head of a new style stack.
+ */
+
+static PRStatus _PR_DestroyIOLayer(PRFileDesc *stack)
+{
+ if (NULL == stack) {
+ return PR_FAILURE;
+ }
+
+ PR_DELETE(stack);
+ return PR_SUCCESS;
+} /* _PR_DestroyIOLayer */
+
+PR_IMPLEMENT(PRStatus) PR_PushIOLayer(
+ PRFileDesc *stack, PRDescIdentity id, PRFileDesc *fd)
+{
+ PRFileDesc *insert = PR_GetIdentitiesLayer(stack, id);
+
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(stack != NULL);
+ PR_ASSERT(insert != NULL);
+ PR_ASSERT(PR_IO_LAYER_HEAD != id);
+ if ((NULL == stack) || (NULL == fd) || (NULL == insert))
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (stack == insert)
+ {
+ /* going on top of the stack */
+ /* old-style stack */
+ PRFileDesc copy = *stack;
+ *stack = *fd;
+ *fd = copy;
+ fd->higher = stack;
+ if (fd->lower)
+ {
+ PR_ASSERT(fd->lower->higher == stack);
+ fd->lower->higher = fd;
+ }
+ stack->lower = fd;
+ stack->higher = NULL;
+ } else {
+ /*
+ * going somewhere in the middle of the stack for both old and new
+ * style stacks, or going on top of stack for new style stack
+ */
+ fd->lower = insert;
+ fd->higher = insert->higher;
+
+ insert->higher->lower = fd;
+ insert->higher = fd;
+ }
+
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_PopIOLayer(PRFileDesc *stack, PRDescIdentity id)
+{
+ PRFileDesc *extract = PR_GetIdentitiesLayer(stack, id);
+
+ PR_ASSERT(0 != id);
+ PR_ASSERT(NULL != stack);
+ PR_ASSERT(NULL != extract);
+ if ((NULL == stack) || (0 == id) || (NULL == extract))
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+
+ if (extract == stack) {
+ /* popping top layer of the stack */
+ /* old style stack */
+ PRFileDesc copy = *stack;
+ extract = stack->lower;
+ *stack = *extract;
+ *extract = copy;
+ stack->higher = NULL;
+ if (stack->lower) {
+ PR_ASSERT(stack->lower->higher == extract);
+ stack->lower->higher = stack;
+ }
+ } else if ((PR_IO_LAYER_HEAD == stack->identity) &&
+ (extract == stack->lower) && (extract->lower == NULL)) {
+ /*
+ * new style stack
+ * popping the only layer in the stack; delete the stack too
+ */
+ stack->lower = NULL;
+ _PR_DestroyIOLayer(stack);
+ } else {
+ /* for both kinds of stacks */
+ extract->lower->higher = extract->higher;
+ extract->higher->lower = extract->lower;
+ }
+ extract->higher = extract->lower = NULL;
+ return extract;
+} /* PR_PopIOLayer */
+
+#define ID_CACHE_INCREMENT 16
+typedef struct _PRIdentity_cache
+{
+ PRLock *ml;
+ char **name;
+ PRIntn length;
+ PRDescIdentity ident;
+} _PRIdentity_cache;
+
+static _PRIdentity_cache identity_cache;
+
+PR_IMPLEMENT(PRDescIdentity) PR_GetUniqueIdentity(const char *layer_name)
+{
+ PRDescIdentity identity, length;
+ char **names = NULL, *name = NULL, **old = NULL;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ PR_ASSERT((PRDescIdentity)0x7fff > identity_cache.ident);
+
+ if (NULL != layer_name)
+ {
+ name = (char*)PR_Malloc(strlen(layer_name) + 1);
+ if (NULL == name)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return PR_INVALID_IO_LAYER;
+ }
+ strcpy(name, layer_name);
+ }
+
+ /* this initial code runs unsafe */
+retry:
+ PR_ASSERT(NULL == names);
+ /*
+ * In the initial round, both identity_cache.ident and
+ * identity_cache.length are 0, so (identity_cache.ident + 1) is greater
+ * than length. In later rounds, identity_cache.ident is always less
+ * than length, so (identity_cache.ident + 1) can be equal to but cannot
+ * be greater than length.
+ */
+ length = identity_cache.length;
+ if ((identity_cache.ident + 1) >= length)
+ {
+ length += ID_CACHE_INCREMENT;
+ names = (char**)PR_CALLOC(length * sizeof(char*));
+ if (NULL == names)
+ {
+ if (NULL != name) {
+ PR_DELETE(name);
+ }
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return PR_INVALID_IO_LAYER;
+ }
+ }
+
+ /* now we get serious about thread safety */
+ PR_Lock(identity_cache.ml);
+ PR_ASSERT(identity_cache.length == 0 ||
+ identity_cache.ident < identity_cache.length);
+ identity = identity_cache.ident + 1;
+ if (identity >= identity_cache.length) /* there's no room */
+ {
+ /* we have to do something - hopefully it's already done */
+ if ((NULL != names) && (identity < length))
+ {
+ /* what we did is still okay */
+ if (identity_cache.length != 0) {
+ memcpy(
+ names, identity_cache.name,
+ identity_cache.length * sizeof(char*));
+ }
+ old = identity_cache.name;
+ identity_cache.name = names;
+ identity_cache.length = length;
+ names = NULL;
+ }
+ else
+ {
+ PR_Unlock(identity_cache.ml);
+ if (NULL != names) {
+ PR_DELETE(names);
+ }
+ goto retry;
+ }
+ }
+ if (NULL != name) /* there's a name to be stored */
+ {
+ identity_cache.name[identity] = name;
+ }
+ identity_cache.ident = identity;
+ PR_ASSERT(identity_cache.ident < identity_cache.length);
+ PR_Unlock(identity_cache.ml);
+
+ if (NULL != old) {
+ PR_DELETE(old);
+ }
+ if (NULL != names) {
+ PR_DELETE(names);
+ }
+
+ return identity;
+} /* PR_GetUniqueIdentity */
+
+PR_IMPLEMENT(const char*) PR_GetNameForIdentity(PRDescIdentity ident)
+{
+ const char *rv = NULL;
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if ((PR_TOP_IO_LAYER != ident) && (ident >= 0)) {
+ PR_Lock(identity_cache.ml);
+ PR_ASSERT(ident <= identity_cache.ident);
+ rv = (ident > identity_cache.ident) ? NULL : identity_cache.name[ident];
+ PR_Unlock(identity_cache.ml);
+ }
+
+ return rv;
+} /* PR_GetNameForIdentity */
+
+PR_IMPLEMENT(PRDescIdentity) PR_GetLayersIdentity(PRFileDesc* fd)
+{
+ PR_ASSERT(NULL != fd);
+ if (PR_IO_LAYER_HEAD == fd->identity) {
+ PR_ASSERT(NULL != fd->lower);
+ return fd->lower->identity;
+ }
+ return fd->identity;
+} /* PR_GetLayersIdentity */
+
+PR_IMPLEMENT(PRFileDesc*) PR_GetIdentitiesLayer(PRFileDesc* fd, PRDescIdentity id)
+{
+ PRFileDesc *layer = fd;
+
+ if (PR_TOP_IO_LAYER == id) {
+ if (PR_IO_LAYER_HEAD == fd->identity) {
+ return fd->lower;
+ }
+ return fd;
+ }
+
+ for (layer = fd; layer != NULL; layer = layer->lower)
+ {
+ if (id == layer->identity) {
+ return layer;
+ }
+ }
+ for (layer = fd; layer != NULL; layer = layer->higher)
+ {
+ if (id == layer->identity) {
+ return layer;
+ }
+ }
+ return NULL;
+} /* PR_GetIdentitiesLayer */
+
+void _PR_InitLayerCache(void)
+{
+ memset(&identity_cache, 0, sizeof(identity_cache));
+ identity_cache.ml = PR_NewLock();
+ PR_ASSERT(NULL != identity_cache.ml);
+} /* _PR_InitLayerCache */
+
+void _PR_CleanupLayerCache(void)
+{
+ if (identity_cache.ml)
+ {
+ PR_DestroyLock(identity_cache.ml);
+ identity_cache.ml = NULL;
+ }
+
+ if (identity_cache.name)
+ {
+ PRDescIdentity ident;
+
+ for (ident = 0; ident <= identity_cache.ident; ident++) {
+ PR_DELETE(identity_cache.name[ident]);
+ }
+
+ PR_DELETE(identity_cache.name);
+ }
+} /* _PR_CleanupLayerCache */
+
+/* prlayer.c */
diff --git a/nsprpub/pr/src/io/prlog.c b/nsprpub/pr/src/io/prlog.c
new file mode 100644
index 0000000000..52bd6abc5d
--- /dev/null
+++ b/nsprpub/pr/src/io/prlog.c
@@ -0,0 +1,572 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#include "prenv.h"
+#include "prprf.h"
+#include <string.h>
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+
+/*
+ * Lock used to lock the log.
+ *
+ * We can't define _PR_LOCK_LOG simply as PR_Lock because PR_Lock may
+ * contain assertions. We have to avoid assertions in _PR_LOCK_LOG
+ * because PR_ASSERT calls PR_LogPrint, which in turn calls _PR_LOCK_LOG.
+ * This can lead to infinite recursion.
+ */
+static PRLock *_pr_logLock;
+#if defined(_PR_PTHREADS) || defined(_PR_BTHREADS)
+#define _PR_LOCK_LOG() PR_Lock(_pr_logLock);
+#define _PR_UNLOCK_LOG() PR_Unlock(_pr_logLock);
+#elif defined(_PR_GLOBAL_THREADS_ONLY)
+#define _PR_LOCK_LOG() { _PR_LOCK_LOCK(_pr_logLock)
+#define _PR_UNLOCK_LOG() _PR_LOCK_UNLOCK(_pr_logLock); }
+#else
+
+#define _PR_LOCK_LOG() \
+{ \
+ PRIntn _is; \
+ PRThread *_me = _PR_MD_CURRENT_THREAD(); \
+ if (!_PR_IS_NATIVE_THREAD(_me)) \
+ _PR_INTSOFF(_is); \
+ _PR_LOCK_LOCK(_pr_logLock)
+
+#define _PR_UNLOCK_LOG() \
+ _PR_LOCK_UNLOCK(_pr_logLock); \
+ PR_ASSERT(_me == _PR_MD_CURRENT_THREAD()); \
+ if (!_PR_IS_NATIVE_THREAD(_me)) \
+ _PR_INTSON(_is); \
+}
+
+#endif
+
+#if defined(XP_PC)
+#define strcasecmp stricmp
+#endif
+
+/*
+ * On NT, we can't define _PUT_LOG as PR_Write or _PR_MD_WRITE,
+ * because every asynchronous file io operation leads to a fiber context
+ * switch. So we define _PUT_LOG as fputs (from stdio.h). A side
+ * benefit is that fputs handles the LF->CRLF translation. This
+ * code can also be used on other platforms with file stream io.
+ */
+#if defined(WIN32) || defined(XP_OS2)
+#define _PR_USE_STDIO_FOR_LOGGING
+#endif
+
+/*
+** Coerce Win32 log output to use OutputDebugString() when
+** NSPR_LOG_FILE is set to "WinDebug".
+*/
+#if defined(XP_PC)
+#define WIN32_DEBUG_FILE (FILE*)-2
+#endif
+
+#ifdef WINCE
+static void OutputDebugStringA(const char* msg) {
+ int len = MultiByteToWideChar(CP_ACP, 0, msg, -1, 0, 0);
+ WCHAR *wMsg = (WCHAR *)PR_Malloc(len * sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, 0, msg, -1, wMsg, len);
+ OutputDebugStringW(wMsg);
+ PR_Free(wMsg);
+}
+#endif
+
+/* Macros used to reduce #ifdef pollution */
+
+#if defined(_PR_USE_STDIO_FOR_LOGGING) && defined(XP_PC)
+#define _PUT_LOG(fd, buf, nb) \
+ PR_BEGIN_MACRO \
+ if (logFile == WIN32_DEBUG_FILE) { \
+ char savebyte = buf[nb]; \
+ buf[nb] = '\0'; \
+ OutputDebugStringA(buf); \
+ buf[nb] = savebyte; \
+ } else { \
+ fwrite(buf, 1, nb, fd); \
+ fflush(fd); \
+ } \
+ PR_END_MACRO
+#elif defined(_PR_USE_STDIO_FOR_LOGGING)
+#define _PUT_LOG(fd, buf, nb) {fwrite(buf, 1, nb, fd); fflush(fd);}
+#elif defined(ANDROID)
+#define _PUT_LOG(fd, buf, nb) \
+ PR_BEGIN_MACRO \
+ if (fd == _pr_stderr) { \
+ char savebyte = buf[nb]; \
+ buf[nb] = '\0'; \
+ __android_log_write(ANDROID_LOG_INFO, "PRLog", buf); \
+ buf[nb] = savebyte; \
+ } else { \
+ PR_Write(fd, buf, nb); \
+ } \
+ PR_END_MACRO
+#elif defined(_PR_PTHREADS)
+#define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb)
+#else
+#define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb)
+#endif
+
+/************************************************************************/
+
+static PRLogModuleInfo *logModules;
+
+static char *logBuf = NULL;
+static char *logp;
+static char *logEndp;
+#ifdef _PR_USE_STDIO_FOR_LOGGING
+static FILE *logFile = NULL;
+#else
+static PRFileDesc *logFile = 0;
+#endif
+static PRBool outputTimeStamp = PR_FALSE;
+static PRBool appendToLog = PR_FALSE;
+
+#define LINE_BUF_SIZE 512
+#define DEFAULT_BUF_SIZE 16384
+
+#ifdef _PR_NEED_STRCASECMP
+
+/*
+ * strcasecmp is defined in /usr/ucblib/libucb.a on some platforms
+ * such as NCR and Unixware. Linking with both libc and libucb
+ * may cause some problem, so I just provide our own implementation
+ * of strcasecmp here.
+ */
+
+static const unsigned char uc[] =
+{
+ '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+ '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+ '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+ '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+ ' ', '!', '"', '#', '$', '%', '&', '\'',
+ '(', ')', '*', '+', ',', '-', '.', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', ':', ';', '<', '=', '>', '?',
+ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
+ '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+ 'X', 'Y', 'Z', '{', '|', '}', '~', '\177'
+};
+
+PRIntn strcasecmp(const char *a, const char *b)
+{
+ const unsigned char *ua = (const unsigned char *)a;
+ const unsigned char *ub = (const unsigned char *)b;
+
+ if( ((const char *)0 == a) || (const char *)0 == b ) {
+ return (PRIntn)(a-b);
+ }
+
+ while( (uc[*ua] == uc[*ub]) && ('\0' != *a) )
+ {
+ a++;
+ ua++;
+ ub++;
+ }
+
+ return (PRIntn)(uc[*ua] - uc[*ub]);
+}
+
+#endif /* _PR_NEED_STRCASECMP */
+
+void _PR_InitLog(void)
+{
+ char *ev;
+
+ _pr_logLock = PR_NewLock();
+
+ ev = PR_GetEnv("NSPR_LOG_MODULES");
+ if (ev && ev[0]) {
+ char module[64]; /* Security-Critical: If you change this
+ * size, you must also change the sscanf
+ * format string to be size-1.
+ */
+ PRBool isSync = PR_FALSE;
+ PRIntn evlen = strlen(ev), pos = 0;
+ PRInt32 bufSize = DEFAULT_BUF_SIZE;
+ while (pos < evlen) {
+ PRIntn level = 1, count = 0, delta = 0;
+ count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n",
+ module, &delta, &level, &delta);
+ pos += delta;
+ if (count == 0) {
+ break;
+ }
+
+ /*
+ ** If count == 2, then we got module and level. If count
+ ** == 1, then level defaults to 1 (module enabled).
+ */
+ if (strcasecmp(module, "sync") == 0) {
+ isSync = PR_TRUE;
+ } else if (strcasecmp(module, "bufsize") == 0) {
+ if (level >= LINE_BUF_SIZE) {
+ bufSize = level;
+ }
+ } else if (strcasecmp(module, "timestamp") == 0) {
+ outputTimeStamp = PR_TRUE;
+ } else if (strcasecmp(module, "append") == 0) {
+ appendToLog = PR_TRUE;
+ } else {
+ PRLogModuleInfo *lm = logModules;
+ PRBool skip_modcheck =
+ (0 == strcasecmp (module, "all")) ? PR_TRUE : PR_FALSE;
+
+ while (lm != NULL) {
+ if (skip_modcheck) {
+ lm -> level = (PRLogModuleLevel)level;
+ }
+ else if (strcasecmp(module, lm->name) == 0) {
+ lm->level = (PRLogModuleLevel)level;
+ break;
+ }
+ lm = lm->next;
+ }
+ }
+ /*found:*/
+ count = sscanf(&ev[pos], " , %n", &delta);
+ pos += delta;
+ if (count == EOF) {
+ break;
+ }
+ }
+ PR_SetLogBuffering(isSync ? 0 : bufSize);
+
+ ev = PR_GetEnvSecure("NSPR_LOG_FILE");
+ if (ev && ev[0]) {
+ if (!PR_SetLogFile(ev)) {
+#ifdef XP_PC
+ char* str = PR_smprintf("Unable to create nspr log file '%s'\n", ev);
+ if (str) {
+ OutputDebugStringA(str);
+ PR_smprintf_free(str);
+ }
+#else
+ fprintf(stderr, "Unable to create nspr log file '%s'\n", ev);
+#endif
+ }
+ } else {
+#ifdef _PR_USE_STDIO_FOR_LOGGING
+ logFile = stderr;
+#else
+ logFile = _pr_stderr;
+#endif
+ }
+ }
+}
+
+void _PR_LogCleanup(void)
+{
+ PRLogModuleInfo *lm = logModules;
+
+ PR_LogFlush();
+
+#ifdef _PR_USE_STDIO_FOR_LOGGING
+ if (logFile
+ && logFile != stdout
+ && logFile != stderr
+#ifdef XP_PC
+ && logFile != WIN32_DEBUG_FILE
+#endif
+ ) {
+ fclose(logFile);
+ }
+#else
+ if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
+ PR_Close(logFile);
+ }
+#endif
+ logFile = NULL;
+
+ if (logBuf) {
+ PR_DELETE(logBuf);
+ }
+
+ while (lm != NULL) {
+ PRLogModuleInfo *next = lm->next;
+ free((/*const*/ char *)lm->name);
+ PR_Free(lm);
+ lm = next;
+ }
+ logModules = NULL;
+
+ if (_pr_logLock) {
+ PR_DestroyLock(_pr_logLock);
+ _pr_logLock = NULL;
+ }
+}
+
+static void _PR_SetLogModuleLevel( PRLogModuleInfo *lm )
+{
+ char *ev;
+
+ ev = PR_GetEnv("NSPR_LOG_MODULES");
+ if (ev && ev[0]) {
+ char module[64]; /* Security-Critical: If you change this
+ * size, you must also change the sscanf
+ * format string to be size-1.
+ */
+ PRIntn evlen = strlen(ev), pos = 0;
+ while (pos < evlen) {
+ PRIntn level = 1, count = 0, delta = 0;
+
+ count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n",
+ module, &delta, &level, &delta);
+ pos += delta;
+ if (count == 0) {
+ break;
+ }
+
+ /*
+ ** If count == 2, then we got module and level. If count
+ ** == 1, then level defaults to 1 (module enabled).
+ */
+ if (lm != NULL)
+ {
+ if ((strcasecmp(module, "all") == 0)
+ || (strcasecmp(module, lm->name) == 0))
+ {
+ lm->level = (PRLogModuleLevel)level;
+ }
+ }
+ count = sscanf(&ev[pos], " , %n", &delta);
+ pos += delta;
+ if (count == EOF) {
+ break;
+ }
+ }
+ }
+} /* end _PR_SetLogModuleLevel() */
+
+PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char *name)
+{
+ PRLogModuleInfo *lm;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ lm = PR_NEWZAP(PRLogModuleInfo);
+ if (lm) {
+ lm->name = strdup(name);
+ lm->level = PR_LOG_NONE;
+ lm->next = logModules;
+ logModules = lm;
+ _PR_SetLogModuleLevel(lm);
+ }
+ return lm;
+}
+
+PR_IMPLEMENT(PRBool) PR_SetLogFile(const char *file)
+{
+#ifdef _PR_USE_STDIO_FOR_LOGGING
+ FILE *newLogFile;
+
+#ifdef XP_PC
+ if ( strcmp( file, "WinDebug") == 0)
+ {
+ newLogFile = WIN32_DEBUG_FILE;
+ }
+ else
+#endif
+ {
+ const char *mode = appendToLog ? "a" : "w";
+ newLogFile = fopen(file, mode);
+ if (!newLogFile) {
+ return PR_FALSE;
+ }
+
+#ifndef WINCE /* _IONBF does not exist in the Windows Mobile 6 SDK. */
+ /* We do buffering ourselves. */
+ setvbuf(newLogFile, NULL, _IONBF, 0);
+#endif
+ }
+ if (logFile
+ && logFile != stdout
+ && logFile != stderr
+#ifdef XP_PC
+ && logFile != WIN32_DEBUG_FILE
+#endif
+ ) {
+ fclose(logFile);
+ }
+ logFile = newLogFile;
+ return PR_TRUE;
+#else
+ PRFileDesc *newLogFile;
+ PRIntn flags = PR_WRONLY|PR_CREATE_FILE;
+ if (appendToLog) {
+ flags |= PR_APPEND;
+ } else {
+ flags |= PR_TRUNCATE;
+ }
+
+ newLogFile = PR_Open(file, flags, 0666);
+ if (newLogFile) {
+ if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
+ PR_Close(logFile);
+ }
+ logFile = newLogFile;
+ }
+ return (PRBool) (newLogFile != 0);
+#endif /* _PR_USE_STDIO_FOR_LOGGING */
+}
+
+PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size)
+{
+ PR_LogFlush();
+
+ if (logBuf) {
+ PR_DELETE(logBuf);
+ }
+
+ if (buffer_size >= LINE_BUF_SIZE) {
+ logp = logBuf = (char*) PR_MALLOC(buffer_size);
+ logEndp = logp + buffer_size;
+ }
+}
+
+PR_IMPLEMENT(void) PR_LogPrint(const char *fmt, ...)
+{
+ va_list ap;
+ char line[LINE_BUF_SIZE];
+ char *line_long = NULL;
+ PRUint32 nb_tid = 0, nb;
+ PRThread *me;
+ PRExplodedTime now;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (!logFile) {
+ return;
+ }
+
+ if (outputTimeStamp) {
+ PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now);
+ nb_tid = PR_snprintf(line, sizeof(line)-1,
+ "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - ",
+ now.tm_year, now.tm_month + 1, now.tm_mday,
+ now.tm_hour, now.tm_min, now.tm_sec,
+ now.tm_usec);
+ }
+
+ me = PR_GetCurrentThread();
+ nb_tid += PR_snprintf(line+nb_tid, sizeof(line)-nb_tid-1, "%ld[%p]: ",
+#if defined(_PR_BTHREADS)
+ me, me);
+#else
+ me ? me->id : 0L, me);
+#endif
+
+ va_start(ap, fmt);
+ nb = nb_tid + PR_vsnprintf(line+nb_tid, sizeof(line)-nb_tid-1, fmt, ap);
+ va_end(ap);
+
+ /*
+ * Check if we might have run out of buffer space (in case we have a
+ * long line), and malloc a buffer just this once.
+ */
+ if (nb == sizeof(line)-2) {
+ va_start(ap, fmt);
+ line_long = PR_vsmprintf(fmt, ap);
+ va_end(ap);
+ /* If this failed, we'll fall back to writing the truncated line. */
+ }
+
+ if (line_long) {
+ nb = strlen(line_long);
+ _PR_LOCK_LOG();
+ if (logBuf != 0) {
+ _PUT_LOG(logFile, logBuf, logp - logBuf);
+ logp = logBuf;
+ }
+ /*
+ * Write out the thread id (with an optional timestamp) and the
+ * malloc'ed buffer.
+ */
+ _PUT_LOG(logFile, line, nb_tid);
+ _PUT_LOG(logFile, line_long, nb);
+ /* Ensure there is a trailing newline. */
+ if (!nb || (line_long[nb-1] != '\n')) {
+ char eol[2];
+ eol[0] = '\n';
+ eol[1] = '\0';
+ _PUT_LOG(logFile, eol, 1);
+ }
+ _PR_UNLOCK_LOG();
+ PR_smprintf_free(line_long);
+ } else {
+ /* Ensure there is a trailing newline. */
+ if (nb && (line[nb-1] != '\n')) {
+ line[nb++] = '\n';
+ line[nb] = '\0';
+ }
+ _PR_LOCK_LOG();
+ if (logBuf == 0) {
+ _PUT_LOG(logFile, line, nb);
+ } else {
+ /* If nb can't fit into logBuf, write out logBuf first. */
+ if (logp + nb > logEndp) {
+ _PUT_LOG(logFile, logBuf, logp - logBuf);
+ logp = logBuf;
+ }
+ /* nb is guaranteed to fit into logBuf. */
+ memcpy(logp, line, nb);
+ logp += nb;
+ }
+ _PR_UNLOCK_LOG();
+ }
+ PR_LogFlush();
+}
+
+PR_IMPLEMENT(void) PR_LogFlush(void)
+{
+ if (logBuf && logFile) {
+ _PR_LOCK_LOG();
+ if (logp > logBuf) {
+ _PUT_LOG(logFile, logBuf, logp - logBuf);
+ logp = logBuf;
+ }
+ _PR_UNLOCK_LOG();
+ }
+}
+
+PR_IMPLEMENT(void) PR_Abort(void)
+{
+ PR_LogPrint("Aborting");
+#ifdef ANDROID
+ __android_log_write(ANDROID_LOG_ERROR, "PRLog", "Aborting");
+#endif
+ abort();
+}
+
+PR_IMPLEMENT(void) PR_Assert(const char *s, const char *file, PRIntn ln)
+{
+ PR_LogPrint("Assertion failure: %s, at %s:%d\n", s, file, ln);
+ fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln);
+ fflush(stderr);
+#ifdef WIN32
+ DebugBreak();
+#elif defined(XP_OS2)
+ asm("int $3");
+#elif defined(ANDROID)
+ __android_log_assert(NULL, "PRLog", "Assertion failure: %s, at %s:%d\n",
+ s, file, ln);
+#endif
+ abort();
+}
diff --git a/nsprpub/pr/src/io/prmapopt.c b/nsprpub/pr/src/io/prmapopt.c
new file mode 100644
index 0000000000..375a73d2a9
--- /dev/null
+++ b/nsprpub/pr/src/io/prmapopt.c
@@ -0,0 +1,530 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file defines _PR_MapOptionName(). The purpose of putting
+ * _PR_MapOptionName() in a separate file is to work around a Winsock
+ * header file problem on Windows NT.
+ *
+ * On Windows NT, if we define _WIN32_WINNT to be 0x0400 (in order
+ * to use Service Pack 3 extensions), windows.h includes winsock2.h
+ * (instead of winsock.h), which doesn't define many socket options
+ * defined in winsock.h.
+ *
+ * We need the socket options defined in winsock.h. So this file
+ * includes winsock.h, with _WIN32_WINNT undefined.
+ */
+
+#if defined(WINNT) || defined(__MINGW32__)
+#include <winsock.h>
+#endif
+
+/* MinGW doesn't define these in its winsock.h. */
+#ifdef __MINGW32__
+#ifndef IP_TTL
+#define IP_TTL 7
+#endif
+#ifndef IP_TOS
+#define IP_TOS 8
+#endif
+#endif
+
+#include "primpl.h"
+
+#if defined(LINUX) || defined(ANDROID)
+#include <netinet/in.h>
+#endif
+
+#ifdef DARWIN
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#endif
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h> /* TCP_NODELAY, TCP_MAXSEG */
+#endif
+
+#ifndef _PR_PTHREADS
+
+PRStatus PR_CALLBACK _PR_SocketGetSocketOption(PRFileDesc *fd, PRSocketOptionData *data)
+{
+ PRStatus rv;
+ PRInt32 length;
+ PRInt32 level, name;
+
+ /*
+ * PR_SockOpt_Nonblocking is a special case that does not
+ * translate to a getsockopt() call
+ */
+ if (PR_SockOpt_Nonblocking == data->option)
+ {
+ data->value.non_blocking = fd->secret->nonblocking;
+ return PR_SUCCESS;
+ }
+
+ rv = _PR_MapOptionName(data->option, &level, &name);
+ if (PR_SUCCESS == rv)
+ {
+ switch (data->option)
+ {
+ case PR_SockOpt_Linger:
+ {
+ struct linger linger;
+ length = sizeof(linger);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char *) &linger, &length);
+ if (PR_SUCCESS == rv)
+ {
+ PR_ASSERT(sizeof(linger) == length);
+ data->value.linger.polarity =
+ (linger.l_onoff) ? PR_TRUE : PR_FALSE;
+ data->value.linger.linger =
+ PR_SecondsToInterval(linger.l_linger);
+ }
+ break;
+ }
+ case PR_SockOpt_Reuseaddr:
+ case PR_SockOpt_Keepalive:
+ case PR_SockOpt_NoDelay:
+ case PR_SockOpt_Broadcast:
+ case PR_SockOpt_Reuseport:
+ {
+#ifdef WIN32 /* Winsock */
+ BOOL value;
+#else
+ PRIntn value;
+#endif
+ length = sizeof(value);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char*)&value, &length);
+ if (PR_SUCCESS == rv) {
+ data->value.reuse_addr = (0 == value) ? PR_FALSE : PR_TRUE;
+ }
+ break;
+ }
+ case PR_SockOpt_McastLoopback:
+ {
+#ifdef WIN32 /* Winsock */
+ BOOL bool;
+#else
+ PRUint8 bool;
+#endif
+ length = sizeof(bool);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char*)&bool, &length);
+ if (PR_SUCCESS == rv) {
+ data->value.mcast_loopback = (0 == bool) ? PR_FALSE : PR_TRUE;
+ }
+ break;
+ }
+ case PR_SockOpt_RecvBufferSize:
+ case PR_SockOpt_SendBufferSize:
+ case PR_SockOpt_MaxSegment:
+ {
+ PRIntn value;
+ length = sizeof(value);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char*)&value, &length);
+ if (PR_SUCCESS == rv) {
+ data->value.recv_buffer_size = value;
+ }
+ break;
+ }
+ case PR_SockOpt_IpTimeToLive:
+ case PR_SockOpt_IpTypeOfService:
+ {
+ /* These options should really be an int (or PRIntn). */
+ length = sizeof(PRUintn);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char*)&data->value.ip_ttl, &length);
+ break;
+ }
+ case PR_SockOpt_McastTimeToLive:
+ {
+#ifdef WIN32 /* Winsock */
+ int ttl;
+#else
+ PRUint8 ttl;
+#endif
+ length = sizeof(ttl);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char*)&ttl, &length);
+ if (PR_SUCCESS == rv) {
+ data->value.mcast_ttl = ttl;
+ }
+ break;
+ }
+#ifdef IP_ADD_MEMBERSHIP
+ case PR_SockOpt_AddMember:
+ case PR_SockOpt_DropMember:
+ {
+ struct ip_mreq mreq;
+ length = sizeof(mreq);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char*)&mreq, &length);
+ if (PR_SUCCESS == rv)
+ {
+ data->value.add_member.mcaddr.inet.ip =
+ mreq.imr_multiaddr.s_addr;
+ data->value.add_member.ifaddr.inet.ip =
+ mreq.imr_interface.s_addr;
+ }
+ break;
+ }
+#endif /* IP_ADD_MEMBERSHIP */
+ case PR_SockOpt_McastInterface:
+ {
+ /* This option is a struct in_addr. */
+ length = sizeof(data->value.mcast_if.inet.ip);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name,
+ (char*)&data->value.mcast_if.inet.ip, &length);
+ break;
+ }
+ case PR_SockOpt_DontFrag:
+ {
+#if !defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)
+ PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
+ rv = PR_FAILURE;
+#else
+#ifdef WIN32 /* Winsock */
+ DWORD value;
+#else
+ PRIntn value;
+#endif
+ length = sizeof(value);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char*)&value, &length);
+#if defined(WIN32) || defined(DARWIN)
+ data->value.dont_fragment = value;
+#else
+ data->value.dont_fragment = (value == IP_PMTUDISC_DO) ? 1 : 0;
+#endif
+#endif /* !(!defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)) */
+ break;
+ }
+ default:
+ PR_NOT_REACHED("Unknown socket option");
+ break;
+ }
+ }
+ return rv;
+} /* _PR_SocketGetSocketOption */
+
+PRStatus PR_CALLBACK _PR_SocketSetSocketOption(PRFileDesc *fd, const PRSocketOptionData *data)
+{
+ PRStatus rv;
+ PRInt32 level, name;
+
+ /*
+ * PR_SockOpt_Nonblocking is a special case that does not
+ * translate to a setsockopt call.
+ */
+ if (PR_SockOpt_Nonblocking == data->option)
+ {
+#ifdef WINNT
+ PR_ASSERT((fd->secret->md.io_model_committed == PR_FALSE)
+ || (fd->secret->nonblocking == data->value.non_blocking));
+ if (fd->secret->md.io_model_committed
+ && (fd->secret->nonblocking != data->value.non_blocking))
+ {
+ /*
+ * On NT, once we have associated a socket with the io
+ * completion port, we can't disassociate it. So we
+ * can't change the nonblocking option of the socket
+ * afterwards.
+ */
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+#endif
+ fd->secret->nonblocking = data->value.non_blocking;
+ return PR_SUCCESS;
+ }
+
+ rv = _PR_MapOptionName(data->option, &level, &name);
+ if (PR_SUCCESS == rv)
+ {
+ switch (data->option)
+ {
+ case PR_SockOpt_Linger:
+ {
+ struct linger linger;
+ linger.l_onoff = data->value.linger.polarity;
+ linger.l_linger = PR_IntervalToSeconds(data->value.linger.linger);
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&linger, sizeof(linger));
+ break;
+ }
+ case PR_SockOpt_Reuseaddr:
+ case PR_SockOpt_Keepalive:
+ case PR_SockOpt_NoDelay:
+ case PR_SockOpt_Broadcast:
+ case PR_SockOpt_Reuseport:
+ {
+#ifdef WIN32 /* Winsock */
+ BOOL value;
+#else
+ PRIntn value;
+#endif
+ value = (data->value.reuse_addr) ? 1 : 0;
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&value, sizeof(value));
+ break;
+ }
+ case PR_SockOpt_McastLoopback:
+ {
+#ifdef WIN32 /* Winsock */
+ BOOL bool;
+#else
+ PRUint8 bool;
+#endif
+ bool = data->value.mcast_loopback ? 1 : 0;
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&bool, sizeof(bool));
+ break;
+ }
+ case PR_SockOpt_RecvBufferSize:
+ case PR_SockOpt_SendBufferSize:
+ case PR_SockOpt_MaxSegment:
+ {
+ PRIntn value = data->value.recv_buffer_size;
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&value, sizeof(value));
+ break;
+ }
+ case PR_SockOpt_IpTimeToLive:
+ case PR_SockOpt_IpTypeOfService:
+ {
+ /* These options should really be an int (or PRIntn). */
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&data->value.ip_ttl, sizeof(PRUintn));
+ break;
+ }
+ case PR_SockOpt_McastTimeToLive:
+ {
+#ifdef WIN32 /* Winsock */
+ int ttl;
+#else
+ PRUint8 ttl;
+#endif
+ ttl = data->value.mcast_ttl;
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&ttl, sizeof(ttl));
+ break;
+ }
+#ifdef IP_ADD_MEMBERSHIP
+ case PR_SockOpt_AddMember:
+ case PR_SockOpt_DropMember:
+ {
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr.s_addr =
+ data->value.add_member.mcaddr.inet.ip;
+ mreq.imr_interface.s_addr =
+ data->value.add_member.ifaddr.inet.ip;
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&mreq, sizeof(mreq));
+ break;
+ }
+#endif /* IP_ADD_MEMBERSHIP */
+ case PR_SockOpt_McastInterface:
+ {
+ /* This option is a struct in_addr. */
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&data->value.mcast_if.inet.ip,
+ sizeof(data->value.mcast_if.inet.ip));
+ break;
+ }
+ case PR_SockOpt_DontFrag:
+ {
+#if !defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)
+ PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
+ rv = PR_FAILURE;
+#else
+#if defined(WIN32) /* Winsock */
+ DWORD value;
+ value = (data->value.dont_fragment) ? 1 : 0;
+#elif defined(LINUX) || defined(ANDROID)
+ PRIntn value;
+ value = (data->value.dont_fragment) ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
+#elif defined(DARWIN)
+ PRIntn value;
+ value = data->value.dont_fragment;
+#endif
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&value, sizeof(value));
+#endif /* !(!defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)) */
+ break;
+ }
+ default:
+ PR_NOT_REACHED("Unknown socket option");
+ break;
+ }
+ }
+ return rv;
+} /* _PR_SocketSetSocketOption */
+
+#endif /* ! _PR_PTHREADS */
+
+/*
+ *********************************************************************
+ *********************************************************************
+ **
+ ** Make sure that the following is at the end of this file,
+ ** because we will be playing with macro redefines.
+ **
+ *********************************************************************
+ *********************************************************************
+ */
+
+/*
+ * Not every platform has all the socket options we want to
+ * support. Some older operating systems such as SunOS 4.1.3
+ * don't have the IP multicast socket options. Win32 doesn't
+ * have TCP_MAXSEG.
+ *
+ * To deal with this problem, we define the missing socket
+ * options as _PR_NO_SUCH_SOCKOPT. _PR_MapOptionName() fails with
+ * PR_OPERATION_NOT_SUPPORTED_ERROR if a socket option not
+ * available on the platform is requested.
+ */
+
+/*
+ * Sanity check. SO_LINGER and TCP_NODELAY should be available
+ * on all platforms. Just to make sure we have included the
+ * appropriate header files. Then any undefined socket options
+ * are really missing.
+ */
+
+#if !defined(SO_LINGER)
+#error "SO_LINGER is not defined"
+#endif
+
+#if !defined(TCP_NODELAY)
+#error "TCP_NODELAY is not defined"
+#endif
+
+/*
+ * Make sure the value of _PR_NO_SUCH_SOCKOPT is not
+ * a valid socket option.
+ */
+#define _PR_NO_SUCH_SOCKOPT -1
+
+#ifndef SO_KEEPALIVE
+#define SO_KEEPALIVE _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef SO_SNDBUF
+#define SO_SNDBUF _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef SO_RCVBUF
+#define SO_RCVBUF _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef IP_MULTICAST_IF /* set/get IP multicast interface */
+#define IP_MULTICAST_IF _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef IP_MULTICAST_TTL /* set/get IP multicast timetolive */
+#define IP_MULTICAST_TTL _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef IP_MULTICAST_LOOP /* set/get IP multicast loopback */
+#define IP_MULTICAST_LOOP _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef IP_ADD_MEMBERSHIP /* add an IP group membership */
+#define IP_ADD_MEMBERSHIP _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef IP_DROP_MEMBERSHIP /* drop an IP group membership */
+#define IP_DROP_MEMBERSHIP _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef IP_TTL /* set/get IP Time To Live */
+#define IP_TTL _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef IP_TOS /* set/get IP Type Of Service */
+#define IP_TOS _PR_NO_SUCH_SOCKOPT
+#endif
+
+/* set/get IP do not fragment */
+#if defined(WIN32)
+#ifndef IP_DONTFRAGMENT
+#define IP_DONTFRAGMENT _PR_NO_SUCH_SOCKOPT
+#endif
+
+#elif defined(LINUX) || defined(ANDROID)
+#ifndef IP_MTU_DISCOVER
+#define IP_MTU_DISCOVER _PR_NO_SUCH_SOCKOPT
+#endif
+
+#elif defined(DARWIN)
+#ifndef IP_DONTFRAG
+#define IP_DONTFRAG _PR_NO_SUCH_SOCKOPT
+#endif
+#endif
+
+#ifndef TCP_NODELAY /* don't delay to coalesce data */
+#define TCP_NODELAY _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef TCP_MAXSEG /* maxumum segment size for tcp */
+#define TCP_MAXSEG _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef SO_BROADCAST /* enable broadcast on UDP sockets */
+#define SO_BROADCAST _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef SO_REUSEPORT /* allow local address & port reuse */
+#define SO_REUSEPORT _PR_NO_SUCH_SOCKOPT
+#endif
+
+PRStatus _PR_MapOptionName(
+ PRSockOption optname, PRInt32 *level, PRInt32 *name)
+{
+ static PRInt32 socketOptions[PR_SockOpt_Last] =
+ {
+ 0, SO_LINGER, SO_REUSEADDR, SO_KEEPALIVE, SO_RCVBUF, SO_SNDBUF,
+ IP_TTL, IP_TOS, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP,
+ IP_MULTICAST_IF, IP_MULTICAST_TTL, IP_MULTICAST_LOOP,
+ TCP_NODELAY, TCP_MAXSEG, SO_BROADCAST, SO_REUSEPORT,
+#if defined(WIN32)
+ IP_DONTFRAGMENT,
+#elif defined(LINUX) || defined(ANDROID)
+ IP_MTU_DISCOVER,
+#elif defined(DARWIN)
+ IP_DONTFRAG,
+#else
+ _PR_NO_SUCH_SOCKOPT,
+#endif
+ };
+ static PRInt32 socketLevels[PR_SockOpt_Last] =
+ {
+ 0, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET,
+ IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, IPPROTO_IP,
+ IPPROTO_IP, IPPROTO_IP, IPPROTO_IP,
+ IPPROTO_TCP, IPPROTO_TCP, SOL_SOCKET, SOL_SOCKET, IPPROTO_IP
+ };
+
+ if ((optname < PR_SockOpt_Linger)
+ || (optname >= PR_SockOpt_Last))
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (socketOptions[optname] == _PR_NO_SUCH_SOCKOPT)
+ {
+ PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+ *name = socketOptions[optname];
+ *level = socketLevels[optname];
+ return PR_SUCCESS;
+} /* _PR_MapOptionName */
diff --git a/nsprpub/pr/src/io/prmmap.c b/nsprpub/pr/src/io/prmmap.c
new file mode 100644
index 0000000000..b0adf88676
--- /dev/null
+++ b/nsprpub/pr/src/io/prmmap.c
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *********************************************************************
+ *
+ * Memory-mapped files
+ *
+ *********************************************************************
+ */
+
+#include "primpl.h"
+
+PR_IMPLEMENT(PRFileMap *) PR_CreateFileMap(
+ PRFileDesc *fd,
+ PRInt64 size,
+ PRFileMapProtect prot)
+{
+ PRFileMap *fmap;
+
+ PR_ASSERT(prot == PR_PROT_READONLY || prot == PR_PROT_READWRITE
+ || prot == PR_PROT_WRITECOPY);
+ fmap = PR_NEWZAP(PRFileMap);
+ if (NULL == fmap) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ fmap->fd = fd;
+ fmap->prot = prot;
+ if (_PR_MD_CREATE_FILE_MAP(fmap, size) == PR_SUCCESS) {
+ return fmap;
+ }
+ PR_DELETE(fmap);
+ return NULL;
+}
+
+PR_IMPLEMENT(PRInt32) PR_GetMemMapAlignment(void)
+{
+ return _PR_MD_GET_MEM_MAP_ALIGNMENT();
+}
+
+PR_IMPLEMENT(void *) PR_MemMap(
+ PRFileMap *fmap,
+ PROffset64 offset,
+ PRUint32 len)
+{
+ return _PR_MD_MEM_MAP(fmap, offset, len);
+}
+
+PR_IMPLEMENT(PRStatus) PR_MemUnmap(void *addr, PRUint32 len)
+{
+ return _PR_MD_MEM_UNMAP(addr, len);
+}
+
+PR_IMPLEMENT(PRStatus) PR_CloseFileMap(PRFileMap *fmap)
+{
+ return _PR_MD_CLOSE_FILE_MAP(fmap);
+}
+
+PR_IMPLEMENT(PRStatus) PR_SyncMemMap(
+ PRFileDesc *fd,
+ void *addr,
+ PRUint32 len)
+{
+ return _PR_MD_SYNC_MEM_MAP(fd, addr, len);
+}
diff --git a/nsprpub/pr/src/io/prmwait.c b/nsprpub/pr/src/io/prmwait.c
new file mode 100644
index 0000000000..62e35ab6fb
--- /dev/null
+++ b/nsprpub/pr/src/io/prmwait.c
@@ -0,0 +1,1534 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#include "pprmwait.h"
+
+#define _MW_REHASH_MAX 11
+
+static PRLock *mw_lock = NULL;
+static _PRGlobalState *mw_state = NULL;
+
+static PRIntervalTime max_polling_interval;
+
+#ifdef WINNT
+
+typedef struct TimerEvent {
+ PRIntervalTime absolute;
+ void (*func)(void *);
+ void *arg;
+ LONG ref_count;
+ PRCList links;
+} TimerEvent;
+
+#define TIMER_EVENT_PTR(_qp) \
+ ((TimerEvent *) ((char *) (_qp) - offsetof(TimerEvent, links)))
+
+struct {
+ PRLock *ml;
+ PRCondVar *new_timer;
+ PRCondVar *cancel_timer;
+ PRThread *manager_thread;
+ PRCList timer_queue;
+} tm_vars;
+
+static PRStatus TimerInit(void);
+static void TimerManager(void *arg);
+static TimerEvent *CreateTimer(PRIntervalTime timeout,
+ void (*func)(void *), void *arg);
+static PRBool CancelTimer(TimerEvent *timer);
+
+static void TimerManager(void *arg)
+{
+ PRIntervalTime now;
+ PRIntervalTime timeout;
+ PRCList *head;
+ TimerEvent *timer;
+
+ PR_Lock(tm_vars.ml);
+ while (1)
+ {
+ if (PR_CLIST_IS_EMPTY(&tm_vars.timer_queue))
+ {
+ PR_WaitCondVar(tm_vars.new_timer, PR_INTERVAL_NO_TIMEOUT);
+ }
+ else
+ {
+ now = PR_IntervalNow();
+ head = PR_LIST_HEAD(&tm_vars.timer_queue);
+ timer = TIMER_EVENT_PTR(head);
+ if ((PRInt32) (now - timer->absolute) >= 0)
+ {
+ PR_REMOVE_LINK(head);
+ /*
+ * make its prev and next point to itself so that
+ * it's obvious that it's not on the timer_queue.
+ */
+ PR_INIT_CLIST(head);
+ PR_ASSERT(2 == timer->ref_count);
+ PR_Unlock(tm_vars.ml);
+ timer->func(timer->arg);
+ PR_Lock(tm_vars.ml);
+ timer->ref_count -= 1;
+ if (0 == timer->ref_count)
+ {
+ PR_NotifyAllCondVar(tm_vars.cancel_timer);
+ }
+ }
+ else
+ {
+ timeout = (PRIntervalTime)(timer->absolute - now);
+ PR_WaitCondVar(tm_vars.new_timer, timeout);
+ }
+ }
+ }
+ PR_Unlock(tm_vars.ml);
+}
+
+static TimerEvent *CreateTimer(
+ PRIntervalTime timeout,
+ void (*func)(void *),
+ void *arg)
+{
+ TimerEvent *timer;
+ PRCList *links, *tail;
+ TimerEvent *elem;
+
+ timer = PR_NEW(TimerEvent);
+ if (NULL == timer)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return timer;
+ }
+ timer->absolute = PR_IntervalNow() + timeout;
+ timer->func = func;
+ timer->arg = arg;
+ timer->ref_count = 2;
+ PR_Lock(tm_vars.ml);
+ tail = links = PR_LIST_TAIL(&tm_vars.timer_queue);
+ while (links->prev != tail)
+ {
+ elem = TIMER_EVENT_PTR(links);
+ if ((PRInt32)(timer->absolute - elem->absolute) >= 0)
+ {
+ break;
+ }
+ links = links->prev;
+ }
+ PR_INSERT_AFTER(&timer->links, links);
+ PR_NotifyCondVar(tm_vars.new_timer);
+ PR_Unlock(tm_vars.ml);
+ return timer;
+}
+
+static PRBool CancelTimer(TimerEvent *timer)
+{
+ PRBool canceled = PR_FALSE;
+
+ PR_Lock(tm_vars.ml);
+ timer->ref_count -= 1;
+ if (timer->links.prev == &timer->links)
+ {
+ while (timer->ref_count == 1)
+ {
+ PR_WaitCondVar(tm_vars.cancel_timer, PR_INTERVAL_NO_TIMEOUT);
+ }
+ }
+ else
+ {
+ PR_REMOVE_LINK(&timer->links);
+ canceled = PR_TRUE;
+ }
+ PR_Unlock(tm_vars.ml);
+ PR_DELETE(timer);
+ return canceled;
+}
+
+static PRStatus TimerInit(void)
+{
+ tm_vars.ml = PR_NewLock();
+ if (NULL == tm_vars.ml)
+ {
+ goto failed;
+ }
+ tm_vars.new_timer = PR_NewCondVar(tm_vars.ml);
+ if (NULL == tm_vars.new_timer)
+ {
+ goto failed;
+ }
+ tm_vars.cancel_timer = PR_NewCondVar(tm_vars.ml);
+ if (NULL == tm_vars.cancel_timer)
+ {
+ goto failed;
+ }
+ PR_INIT_CLIST(&tm_vars.timer_queue);
+ tm_vars.manager_thread = PR_CreateThread(
+ PR_SYSTEM_THREAD, TimerManager, NULL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+ if (NULL == tm_vars.manager_thread)
+ {
+ goto failed;
+ }
+ return PR_SUCCESS;
+
+failed:
+ if (NULL != tm_vars.cancel_timer)
+ {
+ PR_DestroyCondVar(tm_vars.cancel_timer);
+ }
+ if (NULL != tm_vars.new_timer)
+ {
+ PR_DestroyCondVar(tm_vars.new_timer);
+ }
+ if (NULL != tm_vars.ml)
+ {
+ PR_DestroyLock(tm_vars.ml);
+ }
+ return PR_FAILURE;
+}
+
+#endif /* WINNT */
+
+/******************************************************************/
+/******************************************************************/
+/************************ The private portion *********************/
+/******************************************************************/
+/******************************************************************/
+void _PR_InitMW(void)
+{
+#ifdef WINNT
+ /*
+ * We use NT 4's InterlockedCompareExchange() to operate
+ * on PRMWStatus variables.
+ */
+ PR_ASSERT(sizeof(LONG) == sizeof(PRMWStatus));
+ TimerInit();
+#endif
+ mw_lock = PR_NewLock();
+ PR_ASSERT(NULL != mw_lock);
+ mw_state = PR_NEWZAP(_PRGlobalState);
+ PR_ASSERT(NULL != mw_state);
+ PR_INIT_CLIST(&mw_state->group_list);
+ max_polling_interval = PR_MillisecondsToInterval(MAX_POLLING_INTERVAL);
+} /* _PR_InitMW */
+
+void _PR_CleanupMW(void)
+{
+ PR_DestroyLock(mw_lock);
+ mw_lock = NULL;
+ if (mw_state->group) {
+ PR_DestroyWaitGroup(mw_state->group);
+ /* mw_state->group is set to NULL as a side effect. */
+ }
+ PR_DELETE(mw_state);
+} /* _PR_CleanupMW */
+
+static PRWaitGroup *MW_Init2(void)
+{
+ PRWaitGroup *group = mw_state->group; /* it's the null group */
+ if (NULL == group) /* there is this special case */
+ {
+ group = PR_CreateWaitGroup(_PR_DEFAULT_HASH_LENGTH);
+ if (NULL == group) {
+ goto failed_alloc;
+ }
+ PR_Lock(mw_lock);
+ if (NULL == mw_state->group)
+ {
+ mw_state->group = group;
+ group = NULL;
+ }
+ PR_Unlock(mw_lock);
+ if (group != NULL) {
+ (void)PR_DestroyWaitGroup(group);
+ }
+ group = mw_state->group; /* somebody beat us to it */
+ }
+failed_alloc:
+ return group; /* whatever */
+} /* MW_Init2 */
+
+static _PR_HashStory MW_AddHashInternal(PRRecvWait *desc, _PRWaiterHash *hash)
+{
+ /*
+ ** The entries are put in the table using the fd (PRFileDesc*) of
+ ** the receive descriptor as the key. This allows us to locate
+ ** the appropriate entry aqain when the poll operation finishes.
+ **
+ ** The pointer to the file descriptor object is first divided by
+ ** the natural alignment of a pointer in the belief that object
+ ** will have at least that many zeros in the low order bits.
+ ** This may not be a good assuption.
+ **
+ ** We try to put the entry in by rehashing _MW_REHASH_MAX times. After
+ ** that we declare defeat and force the table to be reconstructed.
+ ** Since some fds might be added more than once, won't that cause
+ ** collisions even in an empty table?
+ */
+ PRIntn rehash = _MW_REHASH_MAX;
+ PRRecvWait **waiter;
+ PRUintn hidx = _MW_HASH(desc->fd, hash->length);
+ PRUintn hoffset = 0;
+
+ while (rehash-- > 0)
+ {
+ waiter = &hash->recv_wait;
+ if (NULL == waiter[hidx])
+ {
+ waiter[hidx] = desc;
+ hash->count += 1;
+#if 0
+ printf("Adding 0x%x->0x%x ", desc, desc->fd);
+ printf(
+ "table[%u:%u:*%u]: 0x%x->0x%x\n",
+ hidx, hash->count, hash->length, waiter[hidx], waiter[hidx]->fd);
+#endif
+ return _prmw_success;
+ }
+ if (desc == waiter[hidx])
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); /* desc already in table */
+ return _prmw_error;
+ }
+#if 0
+ printf("Failing 0x%x->0x%x ", desc, desc->fd);
+ printf(
+ "table[*%u:%u:%u]: 0x%x->0x%x\n",
+ hidx, hash->count, hash->length, waiter[hidx], waiter[hidx]->fd);
+#endif
+ if (0 == hoffset)
+ {
+ hoffset = _MW_HASH2(desc->fd, hash->length);
+ PR_ASSERT(0 != hoffset);
+ }
+ hidx = (hidx + hoffset) % (hash->length);
+ }
+ return _prmw_rehash;
+} /* MW_AddHashInternal */
+
+static _PR_HashStory MW_ExpandHashInternal(PRWaitGroup *group)
+{
+ PRRecvWait **desc;
+ PRUint32 pidx, length;
+ _PRWaiterHash *newHash, *oldHash = group->waiter;
+ PRBool retry;
+ _PR_HashStory hrv;
+
+ static const PRInt32 prime_number[] = {
+ _PR_DEFAULT_HASH_LENGTH, 179, 521, 907, 1427,
+ 2711, 3917, 5021, 8219, 11549, 18911, 26711, 33749, 44771
+ };
+ PRUintn primes = (sizeof(prime_number) / sizeof(PRInt32));
+
+ /* look up the next size we'd like to use for the hash table */
+ for (pidx = 0; pidx < primes; ++pidx)
+ {
+ if (prime_number[pidx] == oldHash->length)
+ {
+ break;
+ }
+ }
+ /* table size must be one of the prime numbers */
+ PR_ASSERT(pidx < primes);
+
+ /* if pidx == primes - 1, we can't expand the table any more */
+ while (pidx < primes - 1)
+ {
+ /* next size */
+ ++pidx;
+ length = prime_number[pidx];
+
+ /* allocate the new hash table and fill it in with the old */
+ newHash = (_PRWaiterHash*)PR_CALLOC(
+ sizeof(_PRWaiterHash) + (length * sizeof(PRRecvWait*)));
+ if (NULL == newHash)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return _prmw_error;
+ }
+
+ newHash->length = length;
+ retry = PR_FALSE;
+ for (desc = &oldHash->recv_wait;
+ newHash->count < oldHash->count; ++desc)
+ {
+ PR_ASSERT(desc < &oldHash->recv_wait + oldHash->length);
+ if (NULL != *desc)
+ {
+ hrv = MW_AddHashInternal(*desc, newHash);
+ PR_ASSERT(_prmw_error != hrv);
+ if (_prmw_success != hrv)
+ {
+ PR_DELETE(newHash);
+ retry = PR_TRUE;
+ break;
+ }
+ }
+ }
+ if (retry) {
+ continue;
+ }
+
+ PR_DELETE(group->waiter);
+ group->waiter = newHash;
+ group->p_timestamp += 1;
+ return _prmw_success;
+ }
+
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return _prmw_error; /* we're hosed */
+} /* MW_ExpandHashInternal */
+
+#ifndef WINNT
+static void _MW_DoneInternal(
+ PRWaitGroup *group, PRRecvWait **waiter, PRMWStatus outcome)
+{
+ /*
+ ** Add this receive wait object to the list of finished I/O
+ ** operations for this particular group. If there are other
+ ** threads waiting on the group, notify one. If not, arrange
+ ** for this thread to return.
+ */
+
+#if 0
+ printf("Removing 0x%x->0x%x\n", *waiter, (*waiter)->fd);
+#endif
+ (*waiter)->outcome = outcome;
+ PR_APPEND_LINK(&((*waiter)->internal), &group->io_ready);
+ PR_NotifyCondVar(group->io_complete);
+ PR_ASSERT(0 != group->waiter->count);
+ group->waiter->count -= 1;
+ *waiter = NULL;
+} /* _MW_DoneInternal */
+#endif /* WINNT */
+
+static PRRecvWait **_MW_LookupInternal(PRWaitGroup *group, PRFileDesc *fd)
+{
+ /*
+ ** Find the receive wait object corresponding to the file descriptor.
+ ** Only search the wait group specified.
+ */
+ PRRecvWait **desc;
+ PRIntn rehash = _MW_REHASH_MAX;
+ _PRWaiterHash *hash = group->waiter;
+ PRUintn hidx = _MW_HASH(fd, hash->length);
+ PRUintn hoffset = 0;
+
+ while (rehash-- > 0)
+ {
+ desc = (&hash->recv_wait) + hidx;
+ if ((*desc != NULL) && ((*desc)->fd == fd)) {
+ return desc;
+ }
+ if (0 == hoffset)
+ {
+ hoffset = _MW_HASH2(fd, hash->length);
+ PR_ASSERT(0 != hoffset);
+ }
+ hidx = (hidx + hoffset) % (hash->length);
+ }
+ return NULL;
+} /* _MW_LookupInternal */
+
+#ifndef WINNT
+static PRStatus _MW_PollInternal(PRWaitGroup *group)
+{
+ PRRecvWait **waiter;
+ PRStatus rv = PR_FAILURE;
+ PRInt32 count, count_ready;
+ PRIntervalTime polling_interval;
+
+ group->poller = PR_GetCurrentThread();
+
+ while (PR_TRUE)
+ {
+ PRIntervalTime now, since_last_poll;
+ PRPollDesc *poll_list;
+
+ while (0 == group->waiter->count)
+ {
+ PRStatus st;
+ st = PR_WaitCondVar(group->new_business, PR_INTERVAL_NO_TIMEOUT);
+ if (_prmw_running != group->state)
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ goto aborted;
+ }
+ if (_MW_ABORTED(st)) {
+ goto aborted;
+ }
+ }
+
+ /*
+ ** There's something to do. See if our existing polling list
+ ** is large enough for what we have to do?
+ */
+
+ while (group->polling_count < group->waiter->count)
+ {
+ PRUint32 old_count = group->waiter->count;
+ PRUint32 new_count = PR_ROUNDUP(old_count, _PR_POLL_COUNT_FUDGE);
+ PRSize new_size = sizeof(PRPollDesc) * new_count;
+ PRPollDesc *old_polling_list = group->polling_list;
+
+ PR_Unlock(group->ml);
+ poll_list = (PRPollDesc*)PR_CALLOC(new_size);
+ if (NULL == poll_list)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ PR_Lock(group->ml);
+ goto failed_alloc;
+ }
+ if (NULL != old_polling_list) {
+ PR_DELETE(old_polling_list);
+ }
+ PR_Lock(group->ml);
+ if (_prmw_running != group->state)
+ {
+ PR_DELETE(poll_list);
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ goto aborted;
+ }
+ group->polling_list = poll_list;
+ group->polling_count = new_count;
+ }
+
+ now = PR_IntervalNow();
+ polling_interval = max_polling_interval;
+ since_last_poll = now - group->last_poll;
+
+ waiter = &group->waiter->recv_wait;
+ poll_list = group->polling_list;
+ for (count = 0; count < group->waiter->count; ++waiter)
+ {
+ PR_ASSERT(waiter < &group->waiter->recv_wait
+ + group->waiter->length);
+ if (NULL != *waiter) /* a live one! */
+ {
+ if ((PR_INTERVAL_NO_TIMEOUT != (*waiter)->timeout)
+ && (since_last_poll >= (*waiter)->timeout)) {
+ _MW_DoneInternal(group, waiter, PR_MW_TIMEOUT);
+ }
+ else
+ {
+ if (PR_INTERVAL_NO_TIMEOUT != (*waiter)->timeout)
+ {
+ (*waiter)->timeout -= since_last_poll;
+ if ((*waiter)->timeout < polling_interval) {
+ polling_interval = (*waiter)->timeout;
+ }
+ }
+ PR_ASSERT(poll_list < group->polling_list
+ + group->polling_count);
+ poll_list->fd = (*waiter)->fd;
+ poll_list->in_flags = PR_POLL_READ;
+ poll_list->out_flags = 0;
+#if 0
+ printf(
+ "Polling 0x%x[%d]: [fd: 0x%x, tmo: %u]\n",
+ poll_list, count, poll_list->fd, (*waiter)->timeout);
+#endif
+ poll_list += 1;
+ count += 1;
+ }
+ }
+ }
+
+ PR_ASSERT(count == group->waiter->count);
+
+ /*
+ ** If there are no more threads waiting for completion,
+ ** we need to return.
+ */
+ if ((!PR_CLIST_IS_EMPTY(&group->io_ready))
+ && (1 == group->waiting_threads)) {
+ break;
+ }
+
+ if (0 == count) {
+ continue; /* wait for new business */
+ }
+
+ group->last_poll = now;
+
+ PR_Unlock(group->ml);
+
+ count_ready = PR_Poll(group->polling_list, count, polling_interval);
+
+ PR_Lock(group->ml);
+
+ if (_prmw_running != group->state)
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ goto aborted;
+ }
+ if (-1 == count_ready)
+ {
+ goto failed_poll; /* that's a shame */
+ }
+ else if (0 < count_ready)
+ {
+ for (poll_list = group->polling_list; count > 0;
+ poll_list++, count--)
+ {
+ PR_ASSERT(
+ poll_list < group->polling_list + group->polling_count);
+ if (poll_list->out_flags != 0)
+ {
+ waiter = _MW_LookupInternal(group, poll_list->fd);
+ /*
+ ** If 'waiter' is NULL, that means the wait receive
+ ** descriptor has been canceled.
+ */
+ if (NULL != waiter) {
+ _MW_DoneInternal(group, waiter, PR_MW_SUCCESS);
+ }
+ }
+ }
+ }
+ /*
+ ** If there are no more threads waiting for completion,
+ ** we need to return.
+ ** This thread was "borrowed" to do the polling, but it really
+ ** belongs to the client.
+ */
+ if ((!PR_CLIST_IS_EMPTY(&group->io_ready))
+ && (1 == group->waiting_threads)) {
+ break;
+ }
+ }
+
+ rv = PR_SUCCESS;
+
+aborted:
+failed_poll:
+failed_alloc:
+ group->poller = NULL; /* we were that, not we ain't */
+ if ((_prmw_running == group->state) && (group->waiting_threads > 1))
+ {
+ /* Wake up one thread to become the new poller. */
+ PR_NotifyCondVar(group->io_complete);
+ }
+ return rv; /* we return with the lock held */
+} /* _MW_PollInternal */
+#endif /* !WINNT */
+
+static PRMWGroupState MW_TestForShutdownInternal(PRWaitGroup *group)
+{
+ PRMWGroupState rv = group->state;
+ /*
+ ** Looking at the group's fields is safe because
+ ** once the group's state is no longer running, it
+ ** cannot revert and there is a safe check on entry
+ ** to make sure no more threads are made to wait.
+ */
+ if ((_prmw_stopping == rv)
+ && (0 == group->waiting_threads))
+ {
+ rv = group->state = _prmw_stopped;
+ PR_NotifyCondVar(group->mw_manage);
+ }
+ return rv;
+} /* MW_TestForShutdownInternal */
+
+#ifndef WINNT
+static void _MW_InitialRecv(PRCList *io_ready)
+{
+ PRRecvWait *desc = (PRRecvWait*)io_ready;
+ if ((NULL == desc->buffer.start)
+ || (0 == desc->buffer.length)) {
+ desc->bytesRecv = 0;
+ }
+ else
+ {
+ desc->bytesRecv = (desc->fd->methods->recv)(
+ desc->fd, desc->buffer.start,
+ desc->buffer.length, 0, desc->timeout);
+ if (desc->bytesRecv < 0) { /* SetError should already be there */
+ desc->outcome = PR_MW_FAILURE;
+ }
+ }
+} /* _MW_InitialRecv */
+#endif
+
+#ifdef WINNT
+static void NT_TimeProc(void *arg)
+{
+ _MDOverlapped *overlapped = (_MDOverlapped *)arg;
+ PRRecvWait *desc = overlapped->data.mw.desc;
+ PRFileDesc *bottom;
+
+ if (InterlockedCompareExchange((LONG *)&desc->outcome,
+ (LONG)PR_MW_TIMEOUT, (LONG)PR_MW_PENDING) != (LONG)PR_MW_PENDING)
+ {
+ /* This wait recv descriptor has already completed. */
+ return;
+ }
+
+ /* close the osfd to abort the outstanding async io request */
+ /* $$$$
+ ** Little late to be checking if NSPR's on the bottom of stack,
+ ** but if we don't check, we can't assert that the private data
+ ** is what we think it is.
+ ** $$$$
+ */
+ bottom = PR_GetIdentitiesLayer(desc->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+ if (NULL != bottom) /* now what!?!?! */
+ {
+ bottom->secret->state = _PR_FILEDESC_CLOSED;
+ if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR)
+ {
+ fprintf(stderr, "closesocket failed: %d\n", WSAGetLastError());
+ PR_NOT_REACHED("What shall I do?");
+ }
+ }
+ return;
+} /* NT_TimeProc */
+
+static PRStatus NT_HashRemove(PRWaitGroup *group, PRFileDesc *fd)
+{
+ PRRecvWait **waiter;
+
+ _PR_MD_LOCK(&group->mdlock);
+ waiter = _MW_LookupInternal(group, fd);
+ if (NULL != waiter)
+ {
+ group->waiter->count -= 1;
+ *waiter = NULL;
+ }
+ _PR_MD_UNLOCK(&group->mdlock);
+ return (NULL != waiter) ? PR_SUCCESS : PR_FAILURE;
+}
+
+PRStatus NT_HashRemoveInternal(PRWaitGroup *group, PRFileDesc *fd)
+{
+ PRRecvWait **waiter;
+
+ waiter = _MW_LookupInternal(group, fd);
+ if (NULL != waiter)
+ {
+ group->waiter->count -= 1;
+ *waiter = NULL;
+ }
+ return (NULL != waiter) ? PR_SUCCESS : PR_FAILURE;
+}
+#endif /* WINNT */
+
+/******************************************************************/
+/******************************************************************/
+/********************** The public API portion ********************/
+/******************************************************************/
+/******************************************************************/
+PR_IMPLEMENT(PRStatus) PR_AddWaitFileDesc(
+ PRWaitGroup *group, PRRecvWait *desc)
+{
+ _PR_HashStory hrv;
+ PRStatus rv = PR_FAILURE;
+#ifdef WINNT
+ _MDOverlapped *overlapped;
+ HANDLE hFile;
+ BOOL bResult;
+ DWORD dwError;
+ PRFileDesc *bottom;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ if ((NULL == group) && (NULL == (group = MW_Init2())))
+ {
+ return rv;
+ }
+
+ PR_ASSERT(NULL != desc->fd);
+
+ desc->outcome = PR_MW_PENDING; /* nice, well known value */
+ desc->bytesRecv = 0; /* likewise, though this value is ambiguious */
+
+ PR_Lock(group->ml);
+
+ if (_prmw_running != group->state)
+ {
+ /* Not allowed to add after cancelling the group */
+ desc->outcome = PR_MW_INTERRUPT;
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ PR_Unlock(group->ml);
+ return rv;
+ }
+
+#ifdef WINNT
+ _PR_MD_LOCK(&group->mdlock);
+#endif
+
+ /*
+ ** If the waiter count is zero at this point, there's no telling
+ ** how long we've been idle. Therefore, initialize the beginning
+ ** of the timing interval. As long as the list doesn't go empty,
+ ** it will maintain itself.
+ */
+ if (0 == group->waiter->count) {
+ group->last_poll = PR_IntervalNow();
+ }
+
+ do
+ {
+ hrv = MW_AddHashInternal(desc, group->waiter);
+ if (_prmw_rehash != hrv) {
+ break;
+ }
+ hrv = MW_ExpandHashInternal(group); /* gruesome */
+ if (_prmw_success != hrv) {
+ break;
+ }
+ } while (PR_TRUE);
+
+#ifdef WINNT
+ _PR_MD_UNLOCK(&group->mdlock);
+#endif
+
+ PR_NotifyCondVar(group->new_business); /* tell the world */
+ rv = (_prmw_success == hrv) ? PR_SUCCESS : PR_FAILURE;
+ PR_Unlock(group->ml);
+
+#ifdef WINNT
+ overlapped = PR_NEWZAP(_MDOverlapped);
+ if (NULL == overlapped)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ NT_HashRemove(group, desc->fd);
+ return rv;
+ }
+ overlapped->ioModel = _MD_MultiWaitIO;
+ overlapped->data.mw.desc = desc;
+ overlapped->data.mw.group = group;
+ if (desc->timeout != PR_INTERVAL_NO_TIMEOUT)
+ {
+ overlapped->data.mw.timer = CreateTimer(
+ desc->timeout,
+ NT_TimeProc,
+ overlapped);
+ if (0 == overlapped->data.mw.timer)
+ {
+ NT_HashRemove(group, desc->fd);
+ PR_DELETE(overlapped);
+ /*
+ * XXX It appears that a maximum of 16 timer events can
+ * be outstanding. GetLastError() returns 0 when I try it.
+ */
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ }
+
+ /* Reach to the bottom layer to get the OS fd */
+ bottom = PR_GetIdentitiesLayer(desc->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+ if (NULL == bottom)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ hFile = (HANDLE)bottom->secret->md.osfd;
+ if (!bottom->secret->md.io_model_committed)
+ {
+ PRInt32 st;
+ st = _md_Associate(hFile);
+ PR_ASSERT(0 != st);
+ bottom->secret->md.io_model_committed = PR_TRUE;
+ }
+ bResult = ReadFile(hFile,
+ desc->buffer.start,
+ (DWORD)desc->buffer.length,
+ NULL,
+ &overlapped->overlapped);
+ if (FALSE == bResult && (dwError = GetLastError()) != ERROR_IO_PENDING)
+ {
+ if (desc->timeout != PR_INTERVAL_NO_TIMEOUT)
+ {
+ if (InterlockedCompareExchange((LONG *)&desc->outcome,
+ (LONG)PR_MW_FAILURE, (LONG)PR_MW_PENDING)
+ == (LONG)PR_MW_PENDING)
+ {
+ CancelTimer(overlapped->data.mw.timer);
+ }
+ NT_HashRemove(group, desc->fd);
+ PR_DELETE(overlapped);
+ }
+ _PR_MD_MAP_READ_ERROR(dwError);
+ rv = PR_FAILURE;
+ }
+#endif
+
+ return rv;
+} /* PR_AddWaitFileDesc */
+
+PR_IMPLEMENT(PRRecvWait*) PR_WaitRecvReady(PRWaitGroup *group)
+{
+ PRCList *io_ready = NULL;
+#ifdef WINNT
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ _MDOverlapped *overlapped;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ if ((NULL == group) && (NULL == (group = MW_Init2()))) {
+ goto failed_init;
+ }
+
+ PR_Lock(group->ml);
+
+ if (_prmw_running != group->state)
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ goto invalid_state;
+ }
+
+ group->waiting_threads += 1; /* the polling thread is counted */
+
+#ifdef WINNT
+ _PR_MD_LOCK(&group->mdlock);
+ while (PR_CLIST_IS_EMPTY(&group->io_ready))
+ {
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_IO_WAIT;
+ PR_APPEND_LINK(&me->waitQLinks, &group->wait_list);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ {
+ _PR_SLEEPQ_LOCK(me->cpu);
+ _PR_ADD_SLEEPQ(me, PR_INTERVAL_NO_TIMEOUT);
+ _PR_SLEEPQ_UNLOCK(me->cpu);
+ }
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_UNLOCK(&group->mdlock);
+ PR_Unlock(group->ml);
+ _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
+ me->state = _PR_RUNNING;
+ PR_Lock(group->ml);
+ _PR_MD_LOCK(&group->mdlock);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ PR_REMOVE_LINK(&me->waitQLinks);
+ _PR_MD_UNLOCK(&group->mdlock);
+ me->flags &= ~_PR_INTERRUPT;
+ me->io_suspended = PR_FALSE;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ goto aborted;
+ }
+ }
+ io_ready = PR_LIST_HEAD(&group->io_ready);
+ PR_ASSERT(io_ready != NULL);
+ PR_REMOVE_LINK(io_ready);
+ _PR_MD_UNLOCK(&group->mdlock);
+ overlapped = (_MDOverlapped *)
+ ((char *)io_ready - offsetof(_MDOverlapped, data));
+ io_ready = &overlapped->data.mw.desc->internal;
+#else
+ do
+ {
+ /*
+ ** If the I/O ready list isn't empty, have this thread
+ ** return with the first receive wait object that's available.
+ */
+ if (PR_CLIST_IS_EMPTY(&group->io_ready))
+ {
+ /*
+ ** Is there a polling thread yet? If not, grab this thread
+ ** and use it.
+ */
+ if (NULL == group->poller)
+ {
+ /*
+ ** This thread will stay do polling until it becomes the only one
+ ** left to service a completion. Then it will return and there will
+ ** be none left to actually poll or to run completions.
+ **
+ ** The polling function should only return w/ failure or
+ ** with some I/O ready.
+ */
+ if (PR_FAILURE == _MW_PollInternal(group)) {
+ goto failed_poll;
+ }
+ }
+ else
+ {
+ /*
+ ** There are four reasons a thread can be awakened from
+ ** a wait on the io_complete condition variable.
+ ** 1. Some I/O has completed, i.e., the io_ready list
+ ** is nonempty.
+ ** 2. The wait group is canceled.
+ ** 3. The thread is interrupted.
+ ** 4. The current polling thread has to leave and needs
+ ** a replacement.
+ ** The logic to find a new polling thread is made more
+ ** complicated by all the other possible events.
+ ** I tried my best to write the logic clearly, but
+ ** it is still full of if's with continue and goto.
+ */
+ PRStatus st;
+ do
+ {
+ st = PR_WaitCondVar(group->io_complete, PR_INTERVAL_NO_TIMEOUT);
+ if (_prmw_running != group->state)
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ goto aborted;
+ }
+ if (_MW_ABORTED(st) || (NULL == group->poller)) {
+ break;
+ }
+ } while (PR_CLIST_IS_EMPTY(&group->io_ready));
+
+ /*
+ ** The thread is interrupted and has to leave. It might
+ ** have also been awakened to process ready i/o or be the
+ ** new poller. To be safe, if either condition is true,
+ ** we awaken another thread to take its place.
+ */
+ if (_MW_ABORTED(st))
+ {
+ if ((NULL == group->poller
+ || !PR_CLIST_IS_EMPTY(&group->io_ready))
+ && group->waiting_threads > 1) {
+ PR_NotifyCondVar(group->io_complete);
+ }
+ goto aborted;
+ }
+
+ /*
+ ** A new poller is needed, but can I be the new poller?
+ ** If there is no i/o ready, sure. But if there is any
+ ** i/o ready, it has a higher priority. I want to
+ ** process the ready i/o first and wake up another
+ ** thread to be the new poller.
+ */
+ if (NULL == group->poller)
+ {
+ if (PR_CLIST_IS_EMPTY(&group->io_ready)) {
+ continue;
+ }
+ if (group->waiting_threads > 1) {
+ PR_NotifyCondVar(group->io_complete);
+ }
+ }
+ }
+ PR_ASSERT(!PR_CLIST_IS_EMPTY(&group->io_ready));
+ }
+ io_ready = PR_LIST_HEAD(&group->io_ready);
+ PR_NotifyCondVar(group->io_taken);
+ PR_ASSERT(io_ready != NULL);
+ PR_REMOVE_LINK(io_ready);
+ } while (NULL == io_ready);
+
+failed_poll:
+
+#endif
+
+aborted:
+
+ group->waiting_threads -= 1;
+invalid_state:
+ (void)MW_TestForShutdownInternal(group);
+ PR_Unlock(group->ml);
+
+failed_init:
+ if (NULL != io_ready)
+ {
+ /* If the operation failed, record the reason why */
+ switch (((PRRecvWait*)io_ready)->outcome)
+ {
+ case PR_MW_PENDING:
+ PR_ASSERT(0);
+ break;
+ case PR_MW_SUCCESS:
+#ifndef WINNT
+ _MW_InitialRecv(io_ready);
+#endif
+ break;
+#ifdef WINNT
+ case PR_MW_FAILURE:
+ _PR_MD_MAP_READ_ERROR(overlapped->data.mw.error);
+ break;
+#endif
+ case PR_MW_TIMEOUT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ break;
+ case PR_MW_INTERRUPT:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ break;
+ default: break;
+ }
+#ifdef WINNT
+ if (NULL != overlapped->data.mw.timer)
+ {
+ PR_ASSERT(PR_INTERVAL_NO_TIMEOUT
+ != overlapped->data.mw.desc->timeout);
+ CancelTimer(overlapped->data.mw.timer);
+ }
+ else
+ {
+ PR_ASSERT(PR_INTERVAL_NO_TIMEOUT
+ == overlapped->data.mw.desc->timeout);
+ }
+ PR_DELETE(overlapped);
+#endif
+ }
+ return (PRRecvWait*)io_ready;
+} /* PR_WaitRecvReady */
+
+PR_IMPLEMENT(PRStatus) PR_CancelWaitFileDesc(PRWaitGroup *group, PRRecvWait *desc)
+{
+#if !defined(WINNT)
+ PRRecvWait **recv_wait;
+#endif
+ PRStatus rv = PR_SUCCESS;
+ if (NULL == group) {
+ group = mw_state->group;
+ }
+ PR_ASSERT(NULL != group);
+ if (NULL == group)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ PR_Lock(group->ml);
+
+ if (_prmw_running != group->state)
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ rv = PR_FAILURE;
+ goto unlock;
+ }
+
+#ifdef WINNT
+ if (InterlockedCompareExchange((LONG *)&desc->outcome,
+ (LONG)PR_MW_INTERRUPT, (LONG)PR_MW_PENDING) == (LONG)PR_MW_PENDING)
+ {
+ PRFileDesc *bottom = PR_GetIdentitiesLayer(desc->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+ if (NULL == bottom)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ goto unlock;
+ }
+ bottom->secret->state = _PR_FILEDESC_CLOSED;
+#if 0
+ fprintf(stderr, "cancel wait recv: closing socket\n");
+#endif
+ if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR)
+ {
+ fprintf(stderr, "closesocket failed: %d\n", WSAGetLastError());
+ exit(1);
+ }
+ }
+#else
+ if (NULL != (recv_wait = _MW_LookupInternal(group, desc->fd)))
+ {
+ /* it was in the wait table */
+ _MW_DoneInternal(group, recv_wait, PR_MW_INTERRUPT);
+ goto unlock;
+ }
+ if (!PR_CLIST_IS_EMPTY(&group->io_ready))
+ {
+ /* is it already complete? */
+ PRCList *head = PR_LIST_HEAD(&group->io_ready);
+ do
+ {
+ PRRecvWait *done = (PRRecvWait*)head;
+ if (done == desc) {
+ goto unlock;
+ }
+ head = PR_NEXT_LINK(head);
+ } while (head != &group->io_ready);
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = PR_FAILURE;
+
+#endif
+unlock:
+ PR_Unlock(group->ml);
+ return rv;
+} /* PR_CancelWaitFileDesc */
+
+PR_IMPLEMENT(PRRecvWait*) PR_CancelWaitGroup(PRWaitGroup *group)
+{
+ PRRecvWait **desc;
+ PRRecvWait *recv_wait = NULL;
+#ifdef WINNT
+ _MDOverlapped *overlapped;
+ PRRecvWait **end;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+#endif
+
+ if (NULL == group) {
+ group = mw_state->group;
+ }
+ PR_ASSERT(NULL != group);
+ if (NULL == group)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+
+ PR_Lock(group->ml);
+ if (_prmw_stopped != group->state)
+ {
+ if (_prmw_running == group->state) {
+ group->state = _prmw_stopping; /* so nothing new comes in */
+ }
+ if (0 == group->waiting_threads) { /* is there anybody else? */
+ group->state = _prmw_stopped; /* we can stop right now */
+ }
+ else
+ {
+ PR_NotifyAllCondVar(group->new_business);
+ PR_NotifyAllCondVar(group->io_complete);
+ }
+ while (_prmw_stopped != group->state) {
+ (void)PR_WaitCondVar(group->mw_manage, PR_INTERVAL_NO_TIMEOUT);
+ }
+ }
+
+#ifdef WINNT
+ _PR_MD_LOCK(&group->mdlock);
+#endif
+ /* make all the existing descriptors look done/interrupted */
+#ifdef WINNT
+ end = &group->waiter->recv_wait + group->waiter->length;
+ for (desc = &group->waiter->recv_wait; desc < end; ++desc)
+ {
+ if (NULL != *desc)
+ {
+ if (InterlockedCompareExchange((LONG *)&(*desc)->outcome,
+ (LONG)PR_MW_INTERRUPT, (LONG)PR_MW_PENDING)
+ == (LONG)PR_MW_PENDING)
+ {
+ PRFileDesc *bottom = PR_GetIdentitiesLayer(
+ (*desc)->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+ if (NULL == bottom)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ goto invalid_arg;
+ }
+ bottom->secret->state = _PR_FILEDESC_CLOSED;
+#if 0
+ fprintf(stderr, "cancel wait group: closing socket\n");
+#endif
+ if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR)
+ {
+ fprintf(stderr, "closesocket failed: %d\n",
+ WSAGetLastError());
+ exit(1);
+ }
+ }
+ }
+ }
+ while (group->waiter->count > 0)
+ {
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_IO_WAIT;
+ PR_APPEND_LINK(&me->waitQLinks, &group->wait_list);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ {
+ _PR_SLEEPQ_LOCK(me->cpu);
+ _PR_ADD_SLEEPQ(me, PR_INTERVAL_NO_TIMEOUT);
+ _PR_SLEEPQ_UNLOCK(me->cpu);
+ }
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_UNLOCK(&group->mdlock);
+ PR_Unlock(group->ml);
+ _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
+ me->state = _PR_RUNNING;
+ PR_Lock(group->ml);
+ _PR_MD_LOCK(&group->mdlock);
+ }
+#else
+ for (desc = &group->waiter->recv_wait; group->waiter->count > 0; ++desc)
+ {
+ PR_ASSERT(desc < &group->waiter->recv_wait + group->waiter->length);
+ if (NULL != *desc) {
+ _MW_DoneInternal(group, desc, PR_MW_INTERRUPT);
+ }
+ }
+#endif
+
+ /* take first element of finished list and return it or NULL */
+ if (PR_CLIST_IS_EMPTY(&group->io_ready)) {
+ PR_SetError(PR_GROUP_EMPTY_ERROR, 0);
+ }
+ else
+ {
+ PRCList *head = PR_LIST_HEAD(&group->io_ready);
+ PR_REMOVE_AND_INIT_LINK(head);
+#ifdef WINNT
+ overlapped = (_MDOverlapped *)
+ ((char *)head - offsetof(_MDOverlapped, data));
+ head = &overlapped->data.mw.desc->internal;
+ if (NULL != overlapped->data.mw.timer)
+ {
+ PR_ASSERT(PR_INTERVAL_NO_TIMEOUT
+ != overlapped->data.mw.desc->timeout);
+ CancelTimer(overlapped->data.mw.timer);
+ }
+ else
+ {
+ PR_ASSERT(PR_INTERVAL_NO_TIMEOUT
+ == overlapped->data.mw.desc->timeout);
+ }
+ PR_DELETE(overlapped);
+#endif
+ recv_wait = (PRRecvWait*)head;
+ }
+#ifdef WINNT
+invalid_arg:
+ _PR_MD_UNLOCK(&group->mdlock);
+#endif
+ PR_Unlock(group->ml);
+
+ return recv_wait;
+} /* PR_CancelWaitGroup */
+
+PR_IMPLEMENT(PRWaitGroup*) PR_CreateWaitGroup(PRInt32 size /* ignored */)
+{
+ PRWaitGroup *wg;
+
+ if (NULL == (wg = PR_NEWZAP(PRWaitGroup)))
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto failed;
+ }
+ /* the wait group itself */
+ wg->ml = PR_NewLock();
+ if (NULL == wg->ml) {
+ goto failed_lock;
+ }
+ wg->io_taken = PR_NewCondVar(wg->ml);
+ if (NULL == wg->io_taken) {
+ goto failed_cvar0;
+ }
+ wg->io_complete = PR_NewCondVar(wg->ml);
+ if (NULL == wg->io_complete) {
+ goto failed_cvar1;
+ }
+ wg->new_business = PR_NewCondVar(wg->ml);
+ if (NULL == wg->new_business) {
+ goto failed_cvar2;
+ }
+ wg->mw_manage = PR_NewCondVar(wg->ml);
+ if (NULL == wg->mw_manage) {
+ goto failed_cvar3;
+ }
+
+ PR_INIT_CLIST(&wg->group_link);
+ PR_INIT_CLIST(&wg->io_ready);
+
+ /* the waiters sequence */
+ wg->waiter = (_PRWaiterHash*)PR_CALLOC(
+ sizeof(_PRWaiterHash) +
+ (_PR_DEFAULT_HASH_LENGTH * sizeof(PRRecvWait*)));
+ if (NULL == wg->waiter)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto failed_waiter;
+ }
+ wg->waiter->count = 0;
+ wg->waiter->length = _PR_DEFAULT_HASH_LENGTH;
+
+#ifdef WINNT
+ _PR_MD_NEW_LOCK(&wg->mdlock);
+ PR_INIT_CLIST(&wg->wait_list);
+#endif /* WINNT */
+
+ PR_Lock(mw_lock);
+ PR_APPEND_LINK(&wg->group_link, &mw_state->group_list);
+ PR_Unlock(mw_lock);
+ return wg;
+
+failed_waiter:
+ PR_DestroyCondVar(wg->mw_manage);
+failed_cvar3:
+ PR_DestroyCondVar(wg->new_business);
+failed_cvar2:
+ PR_DestroyCondVar(wg->io_complete);
+failed_cvar1:
+ PR_DestroyCondVar(wg->io_taken);
+failed_cvar0:
+ PR_DestroyLock(wg->ml);
+failed_lock:
+ PR_DELETE(wg);
+ wg = NULL;
+
+failed:
+ return wg;
+} /* MW_CreateWaitGroup */
+
+PR_IMPLEMENT(PRStatus) PR_DestroyWaitGroup(PRWaitGroup *group)
+{
+ PRStatus rv = PR_SUCCESS;
+ if (NULL == group) {
+ group = mw_state->group;
+ }
+ PR_ASSERT(NULL != group);
+ if (NULL != group)
+ {
+ PR_Lock(group->ml);
+ if ((group->waiting_threads == 0)
+ && (group->waiter->count == 0)
+ && PR_CLIST_IS_EMPTY(&group->io_ready))
+ {
+ group->state = _prmw_stopped;
+ }
+ else
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ PR_Unlock(group->ml);
+ if (PR_FAILURE == rv) {
+ return rv;
+ }
+
+ PR_Lock(mw_lock);
+ PR_REMOVE_LINK(&group->group_link);
+ PR_Unlock(mw_lock);
+
+#ifdef WINNT
+ /*
+ * XXX make sure wait_list is empty and waiter is empty.
+ * These must be checked while holding mdlock.
+ */
+ _PR_MD_FREE_LOCK(&group->mdlock);
+#endif
+
+ PR_DELETE(group->waiter);
+ PR_DELETE(group->polling_list);
+ PR_DestroyCondVar(group->mw_manage);
+ PR_DestroyCondVar(group->new_business);
+ PR_DestroyCondVar(group->io_complete);
+ PR_DestroyCondVar(group->io_taken);
+ PR_DestroyLock(group->ml);
+ if (group == mw_state->group) {
+ mw_state->group = NULL;
+ }
+ PR_DELETE(group);
+ }
+ else
+ {
+ /* The default wait group is not created yet. */
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ return rv;
+} /* PR_DestroyWaitGroup */
+
+/**********************************************************************
+***********************************************************************
+******************** Wait group enumerations **************************
+***********************************************************************
+**********************************************************************/
+
+PR_IMPLEMENT(PRMWaitEnumerator*) PR_CreateMWaitEnumerator(PRWaitGroup *group)
+{
+ PRMWaitEnumerator *enumerator = PR_NEWZAP(PRMWaitEnumerator);
+ if (NULL == enumerator) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ else
+ {
+ enumerator->group = group;
+ enumerator->seal = _PR_ENUM_SEALED;
+ }
+ return enumerator;
+} /* PR_CreateMWaitEnumerator */
+
+PR_IMPLEMENT(PRStatus) PR_DestroyMWaitEnumerator(PRMWaitEnumerator* enumerator)
+{
+ PR_ASSERT(NULL != enumerator);
+ PR_ASSERT(_PR_ENUM_SEALED == enumerator->seal);
+ if ((NULL == enumerator) || (_PR_ENUM_SEALED != enumerator->seal))
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ enumerator->seal = _PR_ENUM_UNSEALED;
+ PR_Free(enumerator);
+ return PR_SUCCESS;
+} /* PR_DestroyMWaitEnumerator */
+
+PR_IMPLEMENT(PRRecvWait*) PR_EnumerateWaitGroup(
+ PRMWaitEnumerator *enumerator, const PRRecvWait *previous)
+{
+ PRRecvWait *result = NULL;
+
+ /* entry point sanity checking */
+ PR_ASSERT(NULL != enumerator);
+ PR_ASSERT(_PR_ENUM_SEALED == enumerator->seal);
+ if ((NULL == enumerator)
+ || (_PR_ENUM_SEALED != enumerator->seal)) {
+ goto bad_argument;
+ }
+
+ /* beginning of enumeration */
+ if (NULL == previous)
+ {
+ if (NULL == enumerator->group)
+ {
+ enumerator->group = mw_state->group;
+ if (NULL == enumerator->group)
+ {
+ PR_SetError(PR_GROUP_EMPTY_ERROR, 0);
+ return NULL;
+ }
+ }
+ enumerator->waiter = &enumerator->group->waiter->recv_wait;
+ enumerator->p_timestamp = enumerator->group->p_timestamp;
+ enumerator->thread = PR_GetCurrentThread();
+ enumerator->index = 0;
+ }
+ /* continuing an enumeration */
+ else
+ {
+ PRThread *me = PR_GetCurrentThread();
+ PR_ASSERT(me == enumerator->thread);
+ if (me != enumerator->thread) {
+ goto bad_argument;
+ }
+
+ /* need to restart the enumeration */
+ if (enumerator->p_timestamp != enumerator->group->p_timestamp) {
+ return PR_EnumerateWaitGroup(enumerator, NULL);
+ }
+ }
+
+ /* actually progress the enumeration */
+#if defined(WINNT)
+ _PR_MD_LOCK(&enumerator->group->mdlock);
+#else
+ PR_Lock(enumerator->group->ml);
+#endif
+ while (enumerator->index++ < enumerator->group->waiter->length)
+ {
+ if (NULL != (result = *(enumerator->waiter)++)) {
+ break;
+ }
+ }
+#if defined(WINNT)
+ _PR_MD_UNLOCK(&enumerator->group->mdlock);
+#else
+ PR_Unlock(enumerator->group->ml);
+#endif
+
+ return result; /* what we live for */
+
+bad_argument:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL; /* probably ambiguous */
+} /* PR_EnumerateWaitGroup */
+
+/* prmwait.c */
diff --git a/nsprpub/pr/src/io/prpolevt.c b/nsprpub/pr/src/io/prpolevt.c
new file mode 100644
index 0000000000..1b664ba24e
--- /dev/null
+++ b/nsprpub/pr/src/io/prpolevt.c
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *********************************************************************
+ *
+ * Pollable events
+ *
+ * Pollable events are implemented using layered I/O. The only
+ * I/O methods that are implemented for pollable events are poll
+ * and close. No other methods can be invoked on a pollable
+ * event.
+ *
+ * A pipe or socket pair is created and the pollable event layer
+ * is pushed onto the read end. A pointer to the write end is
+ * saved in the PRFilePrivate structure of the pollable event.
+ *
+ *********************************************************************
+ */
+
+#include "prinit.h"
+#include "prio.h"
+#include "prmem.h"
+#include "prerror.h"
+#include "prlog.h"
+
+/*
+ * These internal functions are declared in primpl.h,
+ * but we can't include primpl.h because the definition
+ * of struct PRFilePrivate in this file (for the pollable
+ * event layer) will conflict with the definition of
+ * struct PRFilePrivate in primpl.h (for the NSPR layer).
+ */
+extern PRIntn _PR_InvalidInt(void);
+extern PRInt64 _PR_InvalidInt64(void);
+extern PRStatus _PR_InvalidStatus(void);
+extern PRFileDesc *_PR_InvalidDesc(void);
+
+/*
+ * PRFilePrivate structure for the NSPR pollable events layer
+ */
+struct PRFilePrivate {
+ PRFileDesc *writeEnd; /* the write end of the pipe/socketpair */
+};
+
+static PRStatus PR_CALLBACK _pr_PolEvtClose(PRFileDesc *fd);
+
+static PRInt16 PR_CALLBACK _pr_PolEvtPoll(
+ PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags);
+
+static PRIOMethods _pr_polevt_methods = {
+ PR_DESC_LAYERED,
+ _pr_PolEvtClose,
+ (PRReadFN)_PR_InvalidInt,
+ (PRWriteFN)_PR_InvalidInt,
+ (PRAvailableFN)_PR_InvalidInt,
+ (PRAvailable64FN)_PR_InvalidInt64,
+ (PRFsyncFN)_PR_InvalidStatus,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ (PRWritevFN)_PR_InvalidInt,
+ (PRConnectFN)_PR_InvalidStatus,
+ (PRAcceptFN)_PR_InvalidDesc,
+ (PRBindFN)_PR_InvalidStatus,
+ (PRListenFN)_PR_InvalidStatus,
+ (PRShutdownFN)_PR_InvalidStatus,
+ (PRRecvFN)_PR_InvalidInt,
+ (PRSendFN)_PR_InvalidInt,
+ (PRRecvfromFN)_PR_InvalidInt,
+ (PRSendtoFN)_PR_InvalidInt,
+ _pr_PolEvtPoll,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ (PRGetsocknameFN)_PR_InvalidStatus,
+ (PRGetpeernameFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRGetsocketoptionFN)_PR_InvalidStatus,
+ (PRSetsocketoptionFN)_PR_InvalidStatus,
+ (PRSendfileFN)_PR_InvalidInt,
+ (PRConnectcontinueFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+static PRDescIdentity _pr_polevt_id;
+static PRCallOnceType _pr_polevt_once_control;
+static PRStatus PR_CALLBACK _pr_PolEvtInit(void);
+
+static PRInt16 PR_CALLBACK _pr_PolEvtPoll(
+ PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
+{
+ return (fd->lower->methods->poll)(fd->lower, in_flags, out_flags);
+}
+
+static PRStatus PR_CALLBACK _pr_PolEvtInit(void)
+{
+ _pr_polevt_id = PR_GetUniqueIdentity("NSPR pollable events");
+ if (PR_INVALID_IO_LAYER == _pr_polevt_id) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+#if !defined(XP_UNIX)
+#define USE_TCP_SOCKETPAIR
+#endif
+
+PR_IMPLEMENT(PRFileDesc *) PR_NewPollableEvent(void)
+{
+ PRFileDesc *event;
+ PRFileDesc *fd[2]; /* fd[0] is the read end; fd[1] is the write end */
+#ifdef USE_TCP_SOCKETPAIR
+ PRSocketOptionData socket_opt;
+ PRStatus rv;
+#endif
+
+ fd[0] = fd[1] = NULL;
+
+ if (PR_CallOnce(&_pr_polevt_once_control, _pr_PolEvtInit) == PR_FAILURE) {
+ return NULL;
+ }
+
+ event = PR_CreateIOLayerStub(_pr_polevt_id, &_pr_polevt_methods);
+ if (NULL == event) {
+ goto errorExit;
+ }
+ event->secret = PR_NEW(PRFilePrivate);
+ if (event->secret == NULL) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+#ifndef USE_TCP_SOCKETPAIR
+ if (PR_CreatePipe(&fd[0], &fd[1]) == PR_FAILURE) {
+ fd[0] = fd[1] = NULL;
+ goto errorExit;
+ }
+#else
+ if (PR_NewTCPSocketPair(fd) == PR_FAILURE) {
+ fd[0] = fd[1] = NULL;
+ goto errorExit;
+ }
+ /*
+ * set the TCP_NODELAY option to reduce notification latency
+ */
+ socket_opt.option = PR_SockOpt_NoDelay;
+ socket_opt.value.no_delay = PR_TRUE;
+ rv = PR_SetSocketOption(fd[1], &socket_opt);
+ PR_ASSERT(PR_SUCCESS == rv);
+#endif
+
+ event->secret->writeEnd = fd[1];
+ if (PR_PushIOLayer(fd[0], PR_TOP_IO_LAYER, event) == PR_FAILURE) {
+ goto errorExit;
+ }
+
+ return fd[0];
+
+errorExit:
+ if (fd[0]) {
+ PR_Close(fd[0]);
+ PR_Close(fd[1]);
+ }
+ if (event) {
+ PR_DELETE(event->secret);
+ event->dtor(event);
+ }
+ return NULL;
+}
+
+static PRStatus PR_CALLBACK _pr_PolEvtClose(PRFileDesc *fd)
+{
+ PRFileDesc *event;
+
+ event = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
+ PR_ASSERT(NULL == event->higher && NULL == event->lower);
+ PR_Close(fd);
+ PR_Close(event->secret->writeEnd);
+ PR_DELETE(event->secret);
+ event->dtor(event);
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_DestroyPollableEvent(PRFileDesc *event)
+{
+ return PR_Close(event);
+}
+
+static const char magicChar = '\x38';
+
+PR_IMPLEMENT(PRStatus) PR_SetPollableEvent(PRFileDesc *event)
+{
+ if (PR_Write(event->secret->writeEnd, &magicChar, 1) != 1) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_WaitForPollableEvent(PRFileDesc *event)
+{
+ char buf[1024];
+ PRInt32 nBytes;
+#ifdef DEBUG
+ PRIntn i;
+#endif
+
+ nBytes = PR_Read(event->lower, buf, sizeof(buf));
+ if (nBytes == -1) {
+ return PR_FAILURE;
+ }
+
+#ifdef DEBUG
+ /*
+ * Make sure people do not write to the pollable event fd
+ * directly.
+ */
+ for (i = 0; i < nBytes; i++) {
+ PR_ASSERT(buf[i] == magicChar);
+ }
+#endif
+
+ return PR_SUCCESS;
+}
diff --git a/nsprpub/pr/src/io/prprf.c b/nsprpub/pr/src/io/prprf.c
new file mode 100644
index 0000000000..9b6072e268
--- /dev/null
+++ b/nsprpub/pr/src/io/prprf.c
@@ -0,0 +1,1316 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Portable safe sprintf code.
+**
+** Author: Kipp E.B. Hickman
+*/
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include "primpl.h"
+#include "prprf.h"
+#include "prlong.h"
+#include "prlog.h"
+#include "prmem.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+/*
+** WARNING: This code may *NOT* call PR_LOG (because PR_LOG calls it)
+*/
+
+/*
+** XXX This needs to be internationalized!
+*/
+
+typedef struct SprintfStateStr SprintfState;
+
+struct SprintfStateStr {
+ int (*stuff)(SprintfState *ss, const char *sp, PRUint32 len);
+
+ char *base;
+ char *cur;
+ PRUint32 maxlen; /* Must not exceed PR_INT32_MAX. */
+
+ int (*func)(void *arg, const char *sp, PRUint32 len);
+ void *arg;
+};
+
+/*
+** Numbered Argument
+*/
+struct NumArg {
+ int type; /* type of the numbered argument */
+ union { /* the numbered argument */
+ int i;
+ unsigned int ui;
+ PRInt32 i32;
+ PRUint32 ui32;
+ PRInt64 ll;
+ PRUint64 ull;
+ double d;
+ const char *s;
+ int *ip;
+#ifdef WIN32
+ const WCHAR *ws;
+#endif
+ } u;
+};
+
+#define NAS_DEFAULT_NUM 20 /* default number of NumberedArgument array */
+
+/*
+** For numeric types, the signed versions must have even values,
+** and their corresponding unsigned versions must have the subsequent
+** odd value.
+*/
+#define TYPE_INT16 0
+#define TYPE_UINT16 1
+#define TYPE_INTN 2
+#define TYPE_UINTN 3
+#define TYPE_INT32 4
+#define TYPE_UINT32 5
+#define TYPE_INT64 6
+#define TYPE_UINT64 7
+#define TYPE_STRING 8
+#define TYPE_DOUBLE 9
+#define TYPE_INTSTR 10
+#ifdef WIN32
+#define TYPE_WSTRING 11
+#endif
+#define TYPE_UNKNOWN 20
+
+#define FLAG_LEFT 0x1
+#define FLAG_SIGNED 0x2
+#define FLAG_SPACED 0x4
+#define FLAG_ZEROS 0x8
+#define FLAG_NEG 0x10
+
+/*
+** Fill into the buffer using the data in src
+*/
+static int fill2(SprintfState *ss, const char *src, int srclen, int width,
+ int flags)
+{
+ char space = ' ';
+ int rv;
+
+ width -= srclen;
+ if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */
+ if (flags & FLAG_ZEROS) {
+ space = '0';
+ }
+ while (--width >= 0) {
+ rv = (*ss->stuff)(ss, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+
+ /* Copy out the source data */
+ rv = (*ss->stuff)(ss, src, srclen);
+ if (rv < 0) {
+ return rv;
+ }
+
+ if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */
+ while (--width >= 0) {
+ rv = (*ss->stuff)(ss, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Fill a number. The order is: optional-sign zero-filling conversion-digits
+*/
+static int fill_n(SprintfState *ss, const char *src, int srclen, int width,
+ int prec, int type, int flags)
+{
+ int zerowidth = 0;
+ int precwidth = 0;
+ int signwidth = 0;
+ int leftspaces = 0;
+ int rightspaces = 0;
+ int cvtwidth;
+ int rv;
+ char sign;
+
+ if ((type & 1) == 0) {
+ if (flags & FLAG_NEG) {
+ sign = '-';
+ signwidth = 1;
+ } else if (flags & FLAG_SIGNED) {
+ sign = '+';
+ signwidth = 1;
+ } else if (flags & FLAG_SPACED) {
+ sign = ' ';
+ signwidth = 1;
+ }
+ }
+ cvtwidth = signwidth + srclen;
+
+ if (prec > 0) {
+ if (prec > srclen) {
+ precwidth = prec - srclen; /* Need zero filling */
+ cvtwidth += precwidth;
+ }
+ }
+
+ if ((flags & FLAG_ZEROS) && (prec < 0)) {
+ if (width > cvtwidth) {
+ zerowidth = width - cvtwidth; /* Zero filling */
+ cvtwidth += zerowidth;
+ }
+ }
+
+ if (flags & FLAG_LEFT) {
+ if (width > cvtwidth) {
+ /* Space filling on the right (i.e. left adjusting) */
+ rightspaces = width - cvtwidth;
+ }
+ } else {
+ if (width > cvtwidth) {
+ /* Space filling on the left (i.e. right adjusting) */
+ leftspaces = width - cvtwidth;
+ }
+ }
+ while (--leftspaces >= 0) {
+ rv = (*ss->stuff)(ss, " ", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ if (signwidth) {
+ rv = (*ss->stuff)(ss, &sign, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ while (--precwidth >= 0) {
+ rv = (*ss->stuff)(ss, "0", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ while (--zerowidth >= 0) {
+ rv = (*ss->stuff)(ss, "0", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ rv = (*ss->stuff)(ss, src, srclen);
+ if (rv < 0) {
+ return rv;
+ }
+ while (--rightspaces >= 0) {
+ rv = (*ss->stuff)(ss, " ", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ return 0;
+}
+
+/*
+** Convert a long into its printable form
+*/
+static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,
+ int type, int flags, const char *hexp)
+{
+ char cvtbuf[100];
+ char *cvt;
+ int digits;
+
+ /* according to the man page this needs to happen */
+ if ((prec == 0) && (num == 0)) {
+ return 0;
+ }
+
+ /*
+ ** Converting decimal is a little tricky. In the unsigned case we
+ ** need to stop when we hit 10 digits. In the signed case, we can
+ ** stop when the number is zero.
+ */
+ cvt = cvtbuf + sizeof(cvtbuf);
+ digits = 0;
+ while (num) {
+ int digit = (((unsigned long)num) % radix) & 0xF;
+ *--cvt = hexp[digit];
+ digits++;
+ num = (long)(((unsigned long)num) / radix);
+ }
+ if (digits == 0) {
+ *--cvt = '0';
+ digits++;
+ }
+
+ /*
+ ** Now that we have the number converted without its sign, deal with
+ ** the sign and zero padding.
+ */
+ return fill_n(ss, cvt, digits, width, prec, type, flags);
+}
+
+/*
+** Convert a 64-bit integer into its printable form
+*/
+static int cvt_ll(SprintfState *ss, PRInt64 num, int width, int prec, int radix,
+ int type, int flags, const char *hexp)
+{
+ char cvtbuf[100];
+ char *cvt;
+ int digits;
+ PRInt64 rad;
+
+ /* according to the man page this needs to happen */
+ if ((prec == 0) && (LL_IS_ZERO(num))) {
+ return 0;
+ }
+
+ /*
+ ** Converting decimal is a little tricky. In the unsigned case we
+ ** need to stop when we hit 10 digits. In the signed case, we can
+ ** stop when the number is zero.
+ */
+ LL_I2L(rad, radix);
+ cvt = cvtbuf + sizeof(cvtbuf);
+ digits = 0;
+ while (!LL_IS_ZERO(num)) {
+ PRInt32 digit;
+ PRInt64 quot, rem;
+ LL_UDIVMOD(&quot, &rem, num, rad);
+ LL_L2I(digit, rem);
+ *--cvt = hexp[digit & 0xf];
+ digits++;
+ num = quot;
+ }
+ if (digits == 0) {
+ *--cvt = '0';
+ digits++;
+ }
+
+ /*
+ ** Now that we have the number converted without its sign, deal with
+ ** the sign and zero padding.
+ */
+ return fill_n(ss, cvt, digits, width, prec, type, flags);
+}
+
+/*
+** Convert a double precision floating point number into its printable
+** form.
+**
+** XXX stop using snprintf to convert floating point
+*/
+static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1)
+{
+ char fin[20];
+ char fout[300];
+ int amount = fmt1 - fmt0;
+
+ if (amount <= 0 || amount >= sizeof(fin)) {
+ /* Totally bogus % command to snprintf. Just ignore it */
+ return 0;
+ }
+ memcpy(fin, fmt0, amount);
+ fin[amount] = 0;
+
+ /* Convert floating point using the native snprintf code */
+#ifdef DEBUG
+ {
+ const char *p = fin;
+ while (*p) {
+ PR_ASSERT(*p != 'L');
+ p++;
+ }
+ }
+#endif
+ memset(fout, 0, sizeof(fout));
+ snprintf(fout, sizeof(fout), fin, d);
+ /* Explicitly null-terminate fout because on Windows snprintf doesn't
+ * append a null-terminator if the buffer is too small. */
+ fout[sizeof(fout) - 1] = '\0';
+
+ return (*ss->stuff)(ss, fout, strlen(fout));
+}
+
+/*
+** Convert a string into its printable form. "width" is the output
+** width. "prec" is the maximum number of characters of "s" to output,
+** where -1 means until NUL.
+*/
+static int cvt_s(SprintfState *ss, const char *str, int width, int prec,
+ int flags)
+{
+ int slen;
+
+ if (prec == 0) {
+ return 0;
+ }
+
+ /* Limit string length by precision value */
+ if (!str) {
+ str = "(null)";
+ }
+ if (prec > 0) {
+ /* this is: slen = strnlen(str, prec); */
+ register const char *s;
+
+ for(s = str; prec && *s; s++, prec-- )
+ ;
+ slen = s - str;
+ } else {
+ slen = strlen(str);
+ }
+
+ /* and away we go */
+ return fill2(ss, str, slen, width, flags);
+}
+
+/*
+** BuildArgArray stands for Numbered Argument list Sprintf
+** for example,
+** fmt = "%4$i, %2$d, %3s, %1d";
+** the number must start from 1, and no gap among them
+*/
+
+static struct NumArg* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArg* nasArray )
+{
+ int number = 0, cn = 0, i;
+ const char* p;
+ char c;
+ struct NumArg* nas;
+
+
+ /*
+ ** first pass:
+ ** determine how many legal % I have got, then allocate space
+ */
+
+ p = fmt;
+ *rv = 0;
+ i = 0;
+ while( ( c = *p++ ) != 0 ) {
+ if( c != '%' ) {
+ continue;
+ }
+ if( ( c = *p++ ) == '%' ) { /* skip %% case */
+ continue;
+ }
+
+ while( c != 0 ) {
+ if( c > '9' || c < '0' ) {
+ if( c == '$' ) { /* numbered argument case */
+ if( i > 0 ) {
+ *rv = -1;
+ return NULL;
+ }
+ number++;
+ } else { /* non-numbered argument case */
+ if( number > 0 ) {
+ *rv = -1;
+ return NULL;
+ }
+ i = 1;
+ }
+ break;
+ }
+
+ c = *p++;
+ }
+ }
+
+ if( number == 0 ) {
+ return NULL;
+ }
+
+
+ if( number > NAS_DEFAULT_NUM ) {
+ nas = (struct NumArg*)PR_MALLOC( number * sizeof( struct NumArg ) );
+ if( !nas ) {
+ *rv = -1;
+ return NULL;
+ }
+ } else {
+ nas = nasArray;
+ }
+
+ for( i = 0; i < number; i++ ) {
+ nas[i].type = TYPE_UNKNOWN;
+ }
+
+
+ /*
+ ** second pass:
+ ** set nas[].type
+ */
+
+ p = fmt;
+ while( ( c = *p++ ) != 0 ) {
+ if( c != '%' ) {
+ continue;
+ }
+ c = *p++;
+ if( c == '%' ) {
+ continue;
+ }
+
+ cn = 0;
+ while( c && c != '$' ) { /* should improve error check later */
+ cn = cn*10 + c - '0';
+ c = *p++;
+ }
+
+ if( !c || cn < 1 || cn > number ) {
+ *rv = -1;
+ break;
+ }
+
+ /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
+ cn--;
+ if( nas[cn].type != TYPE_UNKNOWN ) {
+ continue;
+ }
+
+ c = *p++;
+
+ /* width */
+ if (c == '*') {
+ /* not supported feature, for the argument is not numbered */
+ *rv = -1;
+ break;
+ }
+
+ while ((c >= '0') && (c <= '9')) {
+ c = *p++;
+ }
+
+ /* precision */
+ if (c == '.') {
+ c = *p++;
+ if (c == '*') {
+ /* not supported feature, for the argument is not numbered */
+ *rv = -1;
+ break;
+ }
+
+ while ((c >= '0') && (c <= '9')) {
+ c = *p++;
+ }
+ }
+
+ /* size */
+ nas[cn].type = TYPE_INTN;
+ if (c == 'h') {
+ nas[cn].type = TYPE_INT16;
+ c = *p++;
+ } else if (c == 'L') {
+ /* XXX not quite sure here */
+ nas[cn].type = TYPE_INT64;
+ c = *p++;
+ } else if (c == 'l') {
+ nas[cn].type = TYPE_INT32;
+ c = *p++;
+ if (c == 'l') {
+ nas[cn].type = TYPE_INT64;
+ c = *p++;
+ }
+ } else if (c == 'z') {
+ if (sizeof(size_t) == sizeof(PRInt32)) {
+ nas[ cn ].type = TYPE_INT32;
+ } else if (sizeof(size_t) == sizeof(PRInt64)) {
+ nas[ cn ].type = TYPE_INT64;
+ } else {
+ nas[ cn ].type = TYPE_UNKNOWN;
+ }
+ c = *p++;
+ }
+
+ /* format */
+ switch (c) {
+ case 'd':
+ case 'c':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ break;
+
+ case 'e':
+ case 'f':
+ case 'g':
+ nas[ cn ].type = TYPE_DOUBLE;
+ break;
+
+ case 'p':
+ /* XXX should use cpp */
+ if (sizeof(void *) == sizeof(PRInt32)) {
+ nas[ cn ].type = TYPE_UINT32;
+ } else if (sizeof(void *) == sizeof(PRInt64)) {
+ nas[ cn ].type = TYPE_UINT64;
+ } else if (sizeof(void *) == sizeof(PRIntn)) {
+ nas[ cn ].type = TYPE_UINTN;
+ } else {
+ nas[ cn ].type = TYPE_UNKNOWN;
+ }
+ break;
+
+ case 'S':
+#ifdef WIN32
+ nas[ cn ].type = TYPE_WSTRING;
+ break;
+#endif
+ case 'C':
+ case 'E':
+ case 'G':
+ /* XXX not supported I suppose */
+ PR_ASSERT(0);
+ nas[ cn ].type = TYPE_UNKNOWN;
+ break;
+
+ case 's':
+ nas[ cn ].type = TYPE_STRING;
+ break;
+
+ case 'n':
+ nas[ cn ].type = TYPE_INTSTR;
+ break;
+
+ default:
+ PR_ASSERT(0);
+ nas[ cn ].type = TYPE_UNKNOWN;
+ break;
+ }
+
+ /* get a legal para. */
+ if( nas[ cn ].type == TYPE_UNKNOWN ) {
+ *rv = -1;
+ break;
+ }
+ }
+
+
+ /*
+ ** third pass
+ ** fill the nas[cn].ap
+ */
+
+ if( *rv < 0 ) {
+ if( nas != nasArray ) {
+ PR_DELETE( nas );
+ }
+ return NULL;
+ }
+
+ cn = 0;
+ while( cn < number ) {
+ if( nas[cn].type == TYPE_UNKNOWN ) {
+ cn++;
+ continue;
+ }
+
+ switch( nas[cn].type ) {
+ case TYPE_INT16:
+ case TYPE_UINT16:
+ case TYPE_INTN:
+ nas[cn].u.i = va_arg( ap, int );
+ break;
+
+ case TYPE_UINTN:
+ nas[cn].u.ui = va_arg( ap, unsigned int );
+ break;
+
+ case TYPE_INT32:
+ nas[cn].u.i32 = va_arg( ap, PRInt32 );
+ break;
+
+ case TYPE_UINT32:
+ nas[cn].u.ui32 = va_arg( ap, PRUint32 );
+ break;
+
+ case TYPE_INT64:
+ nas[cn].u.ll = va_arg( ap, PRInt64 );
+ break;
+
+ case TYPE_UINT64:
+ nas[cn].u.ull = va_arg( ap, PRUint64 );
+ break;
+
+ case TYPE_STRING:
+ nas[cn].u.s = va_arg( ap, char* );
+ break;
+
+#ifdef WIN32
+ case TYPE_WSTRING:
+ nas[cn].u.ws = va_arg( ap, WCHAR* );
+ break;
+#endif
+
+ case TYPE_INTSTR:
+ nas[cn].u.ip = va_arg( ap, int* );
+ break;
+
+ case TYPE_DOUBLE:
+ nas[cn].u.d = va_arg( ap, double );
+ break;
+
+ default:
+ if( nas != nasArray ) {
+ PR_DELETE( nas );
+ }
+ *rv = -1;
+ return NULL;
+ }
+
+ cn++;
+ }
+
+
+ return nas;
+}
+
+/*
+** The workhorse sprintf code.
+*/
+static int dosprintf(SprintfState *ss, const char *fmt, va_list ap)
+{
+ char c;
+ int flags, width, prec, radix, type;
+ union {
+ char ch;
+ int i;
+ long l;
+ PRInt64 ll;
+ double d;
+ const char *s;
+ int *ip;
+#ifdef WIN32
+ const WCHAR *ws;
+#endif
+ } u;
+ const char *fmt0;
+ static char *hex = "0123456789abcdef";
+ static char *HEX = "0123456789ABCDEF";
+ char *hexp;
+ int rv, i;
+ struct NumArg* nas = NULL;
+ struct NumArg* nap = NULL;
+ struct NumArg nasArray[ NAS_DEFAULT_NUM ];
+ char pattern[20];
+ const char* dolPt = NULL; /* in "%4$.2f", dolPt will point to . */
+#ifdef WIN32
+ char *pBuf = NULL;
+#endif
+
+ /*
+ ** build an argument array, IF the fmt is numbered argument
+ ** list style, to contain the Numbered Argument list pointers
+ */
+
+ nas = BuildArgArray( fmt, ap, &rv, nasArray );
+ if( rv < 0 ) {
+ /* the fmt contains error Numbered Argument format, jliu@netscape.com */
+ PR_ASSERT(0);
+ return rv;
+ }
+
+ while ((c = *fmt++) != 0) {
+ if (c != '%') {
+ rv = (*ss->stuff)(ss, fmt - 1, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ continue;
+ }
+ fmt0 = fmt - 1;
+
+ /*
+ ** Gobble up the % format string. Hopefully we have handled all
+ ** of the strange cases!
+ */
+ flags = 0;
+ c = *fmt++;
+ if (c == '%') {
+ /* quoting a % with %% */
+ rv = (*ss->stuff)(ss, fmt - 1, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ continue;
+ }
+
+ if( nas != NULL ) {
+ /* the fmt contains the Numbered Arguments feature */
+ i = 0;
+ while( c && c != '$' ) { /* should improve error check later */
+ i = ( i * 10 ) + ( c - '0' );
+ c = *fmt++;
+ }
+
+ if( nas[i-1].type == TYPE_UNKNOWN ) {
+ if( nas && ( nas != nasArray ) ) {
+ PR_DELETE( nas );
+ }
+ return -1;
+ }
+
+ nap = &nas[i-1];
+ dolPt = fmt;
+ c = *fmt++;
+ }
+
+ /*
+ * Examine optional flags. Note that we do not implement the
+ * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
+ * somewhat ambiguous and not ideal, which is perhaps why
+ * the various sprintf() implementations are inconsistent
+ * on this feature.
+ */
+ while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
+ if (c == '-') {
+ flags |= FLAG_LEFT;
+ }
+ if (c == '+') {
+ flags |= FLAG_SIGNED;
+ }
+ if (c == ' ') {
+ flags |= FLAG_SPACED;
+ }
+ if (c == '0') {
+ flags |= FLAG_ZEROS;
+ }
+ c = *fmt++;
+ }
+ if (flags & FLAG_SIGNED) {
+ flags &= ~FLAG_SPACED;
+ }
+ if (flags & FLAG_LEFT) {
+ flags &= ~FLAG_ZEROS;
+ }
+
+ /* width */
+ if (c == '*') {
+ c = *fmt++;
+ width = va_arg(ap, int);
+ } else {
+ width = 0;
+ while ((c >= '0') && (c <= '9')) {
+ width = (width * 10) + (c - '0');
+ c = *fmt++;
+ }
+ }
+
+ /* precision */
+ prec = -1;
+ if (c == '.') {
+ c = *fmt++;
+ if (c == '*') {
+ c = *fmt++;
+ prec = va_arg(ap, int);
+ } else {
+ prec = 0;
+ while ((c >= '0') && (c <= '9')) {
+ prec = (prec * 10) + (c - '0');
+ c = *fmt++;
+ }
+ }
+ }
+
+ /* size */
+ type = TYPE_INTN;
+ if (c == 'h') {
+ type = TYPE_INT16;
+ c = *fmt++;
+ } else if (c == 'L') {
+ /* XXX not quite sure here */
+ type = TYPE_INT64;
+ c = *fmt++;
+ } else if (c == 'l') {
+ type = TYPE_INT32;
+ c = *fmt++;
+ if (c == 'l') {
+ type = TYPE_INT64;
+ c = *fmt++;
+ }
+ } else if (c == 'z') {
+ if (sizeof(size_t) == sizeof(PRInt32)) {
+ type = TYPE_INT32;
+ } else if (sizeof(size_t) == sizeof(PRInt64)) {
+ type = TYPE_INT64;
+ }
+ c = *fmt++;
+ }
+
+ /* format */
+ hexp = hex;
+ switch (c) {
+ case 'd': case 'i': /* decimal/integer */
+ radix = 10;
+ goto fetch_and_convert;
+
+ case 'o': /* octal */
+ radix = 8;
+ type |= 1;
+ goto fetch_and_convert;
+
+ case 'u': /* unsigned decimal */
+ radix = 10;
+ type |= 1;
+ goto fetch_and_convert;
+
+ case 'x': /* unsigned hex */
+ radix = 16;
+ type |= 1;
+ goto fetch_and_convert;
+
+ case 'X': /* unsigned HEX */
+ radix = 16;
+ hexp = HEX;
+ type |= 1;
+ goto fetch_and_convert;
+
+fetch_and_convert:
+ switch (type) {
+ case TYPE_INT16:
+ u.l = nas ? nap->u.i : va_arg(ap, int);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= FLAG_NEG;
+ }
+ goto do_long;
+ case TYPE_UINT16:
+ u.l = (nas ? nap->u.i : va_arg(ap, int)) & 0xffff;
+ goto do_long;
+ case TYPE_INTN:
+ u.l = nas ? nap->u.i : va_arg(ap, int);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= FLAG_NEG;
+ }
+ goto do_long;
+ case TYPE_UINTN:
+ u.l = (long)(nas ? nap->u.ui : va_arg(ap, unsigned int));
+ goto do_long;
+
+ case TYPE_INT32:
+ u.l = nas ? nap->u.i32 : va_arg(ap, PRInt32);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= FLAG_NEG;
+ }
+ goto do_long;
+ case TYPE_UINT32:
+ u.l = (long)(nas ? nap->u.ui32 : va_arg(ap, PRUint32));
+do_long:
+ rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+
+ case TYPE_INT64:
+ u.ll = nas ? nap->u.ll : va_arg(ap, PRInt64);
+ if (!LL_GE_ZERO(u.ll)) {
+ LL_NEG(u.ll, u.ll);
+ flags |= FLAG_NEG;
+ }
+ goto do_longlong;
+ case TYPE_UINT64:
+ u.ll = nas ? nap->u.ull : va_arg(ap, PRUint64);
+do_longlong:
+ rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+ }
+ break;
+
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ u.d = nas ? nap->u.d : va_arg(ap, double);
+ if( nas != NULL ) {
+ i = fmt - dolPt;
+ if( i < sizeof( pattern ) ) {
+ pattern[0] = '%';
+ memcpy( &pattern[1], dolPt, i );
+ rv = cvt_f(ss, u.d, pattern, &pattern[i+1] );
+ }
+ } else {
+ rv = cvt_f(ss, u.d, fmt0, fmt);
+ }
+
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+
+ case 'c':
+ u.ch = nas ? nap->u.i : va_arg(ap, int);
+ if ((flags & FLAG_LEFT) == 0) {
+ while (width-- > 1) {
+ rv = (*ss->stuff)(ss, " ", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+ rv = (*ss->stuff)(ss, &u.ch, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ if (flags & FLAG_LEFT) {
+ while (width-- > 1) {
+ rv = (*ss->stuff)(ss, " ", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+ break;
+
+ case 'p':
+ if (sizeof(void *) == sizeof(PRInt32)) {
+ type = TYPE_UINT32;
+ } else if (sizeof(void *) == sizeof(PRInt64)) {
+ type = TYPE_UINT64;
+ } else if (sizeof(void *) == sizeof(int)) {
+ type = TYPE_UINTN;
+ } else {
+ PR_ASSERT(0);
+ break;
+ }
+ radix = 16;
+ goto fetch_and_convert;
+
+#ifndef WIN32
+ case 'S':
+ /* XXX not supported I suppose */
+ PR_ASSERT(0);
+ break;
+#endif
+
+#if 0
+ case 'C':
+ case 'E':
+ case 'G':
+ /* XXX not supported I suppose */
+ PR_ASSERT(0);
+ break;
+#endif
+
+#ifdef WIN32
+ case 'S':
+ u.ws = nas ? nap->u.ws : va_arg(ap, const WCHAR*);
+
+ /* Get the required size in rv */
+ rv = WideCharToMultiByte(CP_ACP, 0, u.ws, -1, NULL, 0, NULL, NULL);
+ if (rv == 0) {
+ rv = 1;
+ }
+ pBuf = PR_MALLOC(rv);
+ WideCharToMultiByte(CP_ACP, 0, u.ws, -1, pBuf, (int)rv, NULL, NULL);
+ pBuf[rv-1] = '\0';
+
+ rv = cvt_s(ss, pBuf, width, prec, flags);
+
+ /* We don't need the allocated buffer anymore */
+ PR_Free(pBuf);
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+
+#endif
+
+ case 's':
+ u.s = nas ? nap->u.s : va_arg(ap, const char*);
+ rv = cvt_s(ss, u.s, width, prec, flags);
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+
+ case 'n':
+ u.ip = nas ? nap->u.ip : va_arg(ap, int*);
+ if (u.ip) {
+ *u.ip = ss->cur - ss->base;
+ }
+ break;
+
+ default:
+ /* Not a % token after all... skip it */
+#if 0
+ PR_ASSERT(0);
+#endif
+ rv = (*ss->stuff)(ss, "%", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ rv = (*ss->stuff)(ss, fmt - 1, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+
+ /* Stuff trailing NUL */
+ rv = (*ss->stuff)(ss, "\0", 1);
+
+ if( nas && ( nas != nasArray ) ) {
+ PR_DELETE( nas );
+ }
+
+ return rv;
+}
+
+/************************************************************************/
+
+static int FuncStuff(SprintfState *ss, const char *sp, PRUint32 len)
+{
+ int rv;
+
+ /*
+ ** We will add len to ss->maxlen at the end of the function. First check
+ ** if ss->maxlen + len would overflow or be greater than PR_INT32_MAX.
+ */
+ if (PR_UINT32_MAX - ss->maxlen < len || ss->maxlen + len > PR_INT32_MAX) {
+ return -1;
+ }
+ rv = (*ss->func)(ss->arg, sp, len);
+ if (rv < 0) {
+ return rv;
+ }
+ ss->maxlen += len;
+ return 0;
+}
+
+PR_IMPLEMENT(PRUint32) PR_sxprintf(PRStuffFunc func, void *arg,
+ const char *fmt, ...)
+{
+ va_list ap;
+ PRUint32 rv;
+
+ va_start(ap, fmt);
+ rv = PR_vsxprintf(func, arg, fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+PR_IMPLEMENT(PRUint32) PR_vsxprintf(PRStuffFunc func, void *arg,
+ const char *fmt, va_list ap)
+{
+ SprintfState ss;
+ int rv;
+
+ ss.stuff = FuncStuff;
+ ss.func = func;
+ ss.arg = arg;
+ ss.maxlen = 0;
+ rv = dosprintf(&ss, fmt, ap);
+ return (rv < 0) ? (PRUint32)-1 : ss.maxlen;
+}
+
+/*
+** Stuff routine that automatically grows the malloc'd output buffer
+** before it overflows.
+*/
+static int GrowStuff(SprintfState *ss, const char *sp, PRUint32 len)
+{
+ ptrdiff_t off;
+ char *newbase;
+ PRUint32 newlen;
+
+ off = ss->cur - ss->base;
+ if (PR_UINT32_MAX - len < off) {
+ /* off + len would be too big. */
+ return -1;
+ }
+ if (off + len >= ss->maxlen) {
+ /* Grow the buffer */
+ PRUint32 increment = (len > 32) ? len : 32;
+ if (PR_UINT32_MAX - ss->maxlen < increment) {
+ /* ss->maxlen + increment would overflow. */
+ return -1;
+ }
+ newlen = ss->maxlen + increment;
+ if (newlen > PR_INT32_MAX) {
+ return -1;
+ }
+ if (ss->base) {
+ newbase = (char*) PR_REALLOC(ss->base, newlen);
+ } else {
+ newbase = (char*) PR_MALLOC(newlen);
+ }
+ if (!newbase) {
+ /* Ran out of memory */
+ return -1;
+ }
+ ss->base = newbase;
+ ss->maxlen = newlen;
+ ss->cur = ss->base + off;
+ }
+
+ /* Copy data */
+ while (len) {
+ --len;
+ *ss->cur++ = *sp++;
+ }
+ PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen);
+ return 0;
+}
+
+/*
+** sprintf into a malloc'd buffer
+*/
+PR_IMPLEMENT(char *) PR_smprintf(const char *fmt, ...)
+{
+ va_list ap;
+ char *rv;
+
+ va_start(ap, fmt);
+ rv = PR_vsmprintf(fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+/*
+** Free memory allocated, for the caller, by PR_smprintf
+*/
+PR_IMPLEMENT(void) PR_smprintf_free(char *mem)
+{
+ PR_DELETE(mem);
+}
+
+PR_IMPLEMENT(char *) PR_vsmprintf(const char *fmt, va_list ap)
+{
+ SprintfState ss;
+ int rv;
+
+ ss.stuff = GrowStuff;
+ ss.base = 0;
+ ss.cur = 0;
+ ss.maxlen = 0;
+ rv = dosprintf(&ss, fmt, ap);
+ if (rv < 0) {
+ if (ss.base) {
+ PR_DELETE(ss.base);
+ }
+ return 0;
+ }
+ return ss.base;
+}
+
+/*
+** Stuff routine that discards overflow data
+*/
+static int LimitStuff(SprintfState *ss, const char *sp, PRUint32 len)
+{
+ PRUint32 limit = ss->maxlen - (ss->cur - ss->base);
+
+ if (len > limit) {
+ len = limit;
+ }
+ while (len) {
+ --len;
+ *ss->cur++ = *sp++;
+ }
+ return 0;
+}
+
+/*
+** sprintf into a fixed size buffer. Make sure there is a NUL at the end
+** when finished.
+*/
+PR_IMPLEMENT(PRUint32) PR_snprintf(char *out, PRUint32 outlen, const char *fmt, ...)
+{
+ va_list ap;
+ PRUint32 rv;
+
+ va_start(ap, fmt);
+ rv = PR_vsnprintf(out, outlen, fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+PR_IMPLEMENT(PRUint32) PR_vsnprintf(char *out, PRUint32 outlen,const char *fmt,
+ va_list ap)
+{
+ SprintfState ss;
+ PRUint32 n;
+
+ PR_ASSERT(outlen != 0 && outlen <= PR_INT32_MAX);
+ if (outlen == 0 || outlen > PR_INT32_MAX) {
+ return 0;
+ }
+
+ ss.stuff = LimitStuff;
+ ss.base = out;
+ ss.cur = out;
+ ss.maxlen = outlen;
+ (void) dosprintf(&ss, fmt, ap);
+
+ /* If we added chars, and we didn't append a null, do it now. */
+ if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') ) {
+ *(ss.cur - 1) = '\0';
+ }
+
+ n = ss.cur - ss.base;
+ return n ? n - 1 : n;
+}
+
+PR_IMPLEMENT(char *) PR_sprintf_append(char *last, const char *fmt, ...)
+{
+ va_list ap;
+ char *rv;
+
+ va_start(ap, fmt);
+ rv = PR_vsprintf_append(last, fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+PR_IMPLEMENT(char *) PR_vsprintf_append(char *last, const char *fmt, va_list ap)
+{
+ SprintfState ss;
+ int rv;
+
+ ss.stuff = GrowStuff;
+ if (last) {
+ size_t lastlen = strlen(last);
+ if (lastlen > PR_INT32_MAX) {
+ return 0;
+ }
+ ss.base = last;
+ ss.cur = last + lastlen;
+ ss.maxlen = lastlen;
+ } else {
+ ss.base = 0;
+ ss.cur = 0;
+ ss.maxlen = 0;
+ }
+ rv = dosprintf(&ss, fmt, ap);
+ if (rv < 0) {
+ if (ss.base) {
+ PR_DELETE(ss.base);
+ }
+ return 0;
+ }
+ return ss.base;
+}
+
diff --git a/nsprpub/pr/src/io/prscanf.c b/nsprpub/pr/src/io/prscanf.c
new file mode 100644
index 0000000000..9923ea27c7
--- /dev/null
+++ b/nsprpub/pr/src/io/prscanf.c
@@ -0,0 +1,630 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Scan functions for NSPR types
+ *
+ * Author: Wan-Teh Chang
+ *
+ * Acknowledgment: The implementation is inspired by the source code
+ * in P.J. Plauger's "The Standard C Library," Prentice-Hall, 1992.
+ */
+
+#include <limits.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include "prprf.h"
+#include "prdtoa.h"
+#include "prlog.h"
+#include "prerror.h"
+
+/*
+ * A function that reads a character from 'stream'.
+ * Returns the character read, or EOF if end of stream is reached.
+ */
+typedef int (*_PRGetCharFN)(void *stream);
+
+/*
+ * A function that pushes the character 'ch' back to 'stream'.
+ */
+typedef void (*_PRUngetCharFN)(void *stream, int ch);
+
+/*
+ * The size specifier for the integer and floating point number
+ * conversions in format control strings.
+ */
+typedef enum {
+ _PR_size_none, /* No size specifier is given */
+ _PR_size_h, /* The 'h' specifier, suggesting "short" */
+ _PR_size_l, /* The 'l' specifier, suggesting "long" */
+ _PR_size_L, /* The 'L' specifier, meaning a 'long double' */
+ _PR_size_ll /* The 'll' specifier, suggesting "long long" */
+} _PRSizeSpec;
+
+/*
+ * The collection of data that is passed between the scan function
+ * and its subordinate functions. The fields of this structure
+ * serve as the input or output arguments for these functions.
+ */
+typedef struct {
+ _PRGetCharFN get; /* get a character from input stream */
+ _PRUngetCharFN unget; /* unget (push back) a character */
+ void *stream; /* argument for get and unget */
+ va_list ap; /* the variable argument list */
+ int nChar; /* number of characters read from 'stream' */
+
+ PRBool assign; /* assign, or suppress assignment? */
+ int width; /* field width */
+ _PRSizeSpec sizeSpec; /* 'h', 'l', 'L', or 'll' */
+
+ PRBool converted; /* is the value actually converted? */
+} ScanfState;
+
+#define GET(state) ((state)->nChar++, (state)->get((state)->stream))
+#define UNGET(state, ch) \
+ ((state)->nChar--, (state)->unget((state)->stream, ch))
+
+/*
+ * The following two macros, GET_IF_WITHIN_WIDTH and WITHIN_WIDTH,
+ * are always used together.
+ *
+ * GET_IF_WITHIN_WIDTH calls the GET macro and assigns its return
+ * value to 'ch' only if we have not exceeded the field width of
+ * 'state'. Therefore, after GET_IF_WITHIN_WIDTH, the value of
+ * 'ch' is valid only if the macro WITHIN_WIDTH evaluates to true.
+ */
+
+#define GET_IF_WITHIN_WIDTH(state, ch) \
+ if (--(state)->width >= 0) { \
+ (ch) = GET(state); \
+ }
+#define WITHIN_WIDTH(state) ((state)->width >= 0)
+
+/*
+ * _pr_strtoull:
+ * Convert a string to an unsigned 64-bit integer. The string
+ * 'str' is assumed to be a representation of the integer in
+ * base 'base'.
+ *
+ * Warning:
+ * - Only handle base 8, 10, and 16.
+ * - No overflow checking.
+ */
+
+static PRUint64
+_pr_strtoull(const char *str, char **endptr, int base)
+{
+ static const int BASE_MAX = 16;
+ static const char digits[] = "0123456789abcdef";
+ char *digitPtr;
+ PRUint64 x; /* return value */
+ PRInt64 base64;
+ const char *cPtr;
+ PRBool negative;
+ const char *digitStart;
+
+ PR_ASSERT(base == 0 || base == 8 || base == 10 || base == 16);
+ if (base < 0 || base == 1 || base > BASE_MAX) {
+ if (endptr) {
+ *endptr = (char *) str;
+ return LL_ZERO;
+ }
+ }
+
+ cPtr = str;
+ while (isspace(*cPtr)) {
+ ++cPtr;
+ }
+
+ negative = PR_FALSE;
+ if (*cPtr == '-') {
+ negative = PR_TRUE;
+ cPtr++;
+ } else if (*cPtr == '+') {
+ cPtr++;
+ }
+
+ if (base == 16) {
+ if (*cPtr == '0' && (cPtr[1] == 'x' || cPtr[1] == 'X')) {
+ cPtr += 2;
+ }
+ } else if (base == 0) {
+ if (*cPtr != '0') {
+ base = 10;
+ } else if (cPtr[1] == 'x' || cPtr[1] == 'X') {
+ base = 16;
+ cPtr += 2;
+ } else {
+ base = 8;
+ }
+ }
+ PR_ASSERT(base != 0);
+ LL_I2L(base64, base);
+ digitStart = cPtr;
+
+ /* Skip leading zeros */
+ while (*cPtr == '0') {
+ cPtr++;
+ }
+
+ LL_I2L(x, 0);
+ while ((digitPtr = (char*)memchr(digits, tolower(*cPtr), base)) != NULL) {
+ PRUint64 d;
+
+ LL_I2L(d, (digitPtr - digits));
+ LL_MUL(x, x, base64);
+ LL_ADD(x, x, d);
+ cPtr++;
+ }
+
+ if (cPtr == digitStart) {
+ if (endptr) {
+ *endptr = (char *) str;
+ }
+ return LL_ZERO;
+ }
+
+ if (negative) {
+#ifdef HAVE_LONG_LONG
+ /* The cast to a signed type is to avoid a compiler warning */
+ x = -(PRInt64)x;
+#else
+ LL_NEG(x, x);
+#endif
+ }
+
+ if (endptr) {
+ *endptr = (char *) cPtr;
+ }
+ return x;
+}
+
+/*
+ * The maximum field width (in number of characters) that is enough
+ * (may be more than necessary) to represent a 64-bit integer or
+ * floating point number.
+ */
+#define FMAX 31
+#define DECIMAL_POINT '.'
+
+static PRStatus
+GetInt(ScanfState *state, int code)
+{
+ char buf[FMAX + 1], *p;
+ int ch = 0;
+ static const char digits[] = "0123456789abcdefABCDEF";
+ PRBool seenDigit = PR_FALSE;
+ int base;
+ int dlen;
+
+ switch (code) {
+ case 'd': case 'u':
+ base = 10;
+ break;
+ case 'i':
+ base = 0;
+ break;
+ case 'x': case 'X': case 'p':
+ base = 16;
+ break;
+ case 'o':
+ base = 8;
+ break;
+ default:
+ return PR_FAILURE;
+ }
+ if (state->width == 0 || state->width > FMAX) {
+ state->width = FMAX;
+ }
+ p = buf;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ }
+ if (WITHIN_WIDTH(state) && ch == '0') {
+ seenDigit = PR_TRUE;
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ if (WITHIN_WIDTH(state)
+ && (ch == 'x' || ch == 'X')
+ && (base == 0 || base == 16)) {
+ base = 16;
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ } else if (base == 0) {
+ base = 8;
+ }
+ }
+ if (base == 0 || base == 10) {
+ dlen = 10;
+ } else if (base == 8) {
+ dlen = 8;
+ } else {
+ PR_ASSERT(base == 16);
+ dlen = 16 + 6; /* 16 digits, plus 6 in uppercase */
+ }
+ while (WITHIN_WIDTH(state) && memchr(digits, ch, dlen)) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ seenDigit = PR_TRUE;
+ }
+ if (WITHIN_WIDTH(state)) {
+ UNGET(state, ch);
+ }
+ if (!seenDigit) {
+ return PR_FAILURE;
+ }
+ *p = '\0';
+ if (state->assign) {
+ if (code == 'd' || code == 'i') {
+ if (state->sizeSpec == _PR_size_ll) {
+ PRInt64 llval = _pr_strtoull(buf, NULL, base);
+ *va_arg(state->ap, PRInt64 *) = llval;
+ } else {
+ long lval = strtol(buf, NULL, base);
+
+ if (state->sizeSpec == _PR_size_none) {
+ *va_arg(state->ap, PRIntn *) = lval;
+ } else if (state->sizeSpec == _PR_size_h) {
+ *va_arg(state->ap, PRInt16 *) = (PRInt16)lval;
+ } else if (state->sizeSpec == _PR_size_l) {
+ *va_arg(state->ap, PRInt32 *) = lval;
+ } else {
+ return PR_FAILURE;
+ }
+ }
+ } else {
+ if (state->sizeSpec == _PR_size_ll) {
+ PRUint64 llval = _pr_strtoull(buf, NULL, base);
+ *va_arg(state->ap, PRUint64 *) = llval;
+ } else {
+ unsigned long lval = strtoul(buf, NULL, base);
+
+ if (state->sizeSpec == _PR_size_none) {
+ *va_arg(state->ap, PRUintn *) = lval;
+ } else if (state->sizeSpec == _PR_size_h) {
+ *va_arg(state->ap, PRUint16 *) = (PRUint16)lval;
+ } else if (state->sizeSpec == _PR_size_l) {
+ *va_arg(state->ap, PRUint32 *) = lval;
+ } else {
+ return PR_FAILURE;
+ }
+ }
+ }
+ state->converted = PR_TRUE;
+ }
+ return PR_SUCCESS;
+}
+
+static PRStatus
+GetFloat(ScanfState *state)
+{
+ char buf[FMAX + 1], *p;
+ int ch = 0;
+ PRBool seenDigit = PR_FALSE;
+
+ if (state->width == 0 || state->width > FMAX) {
+ state->width = FMAX;
+ }
+ p = buf;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ }
+ while (WITHIN_WIDTH(state) && isdigit(ch)) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ seenDigit = PR_TRUE;
+ }
+ if (WITHIN_WIDTH(state) && ch == DECIMAL_POINT) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ while (WITHIN_WIDTH(state) && isdigit(ch)) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ seenDigit = PR_TRUE;
+ }
+ }
+
+ /*
+ * This is not robust. For example, "1.2e+" would confuse
+ * the code below to read 'e' and '+', only to realize that
+ * it should have stopped at "1.2". But we can't push back
+ * more than one character, so there is nothing I can do.
+ */
+
+ /* Parse exponent */
+ if (WITHIN_WIDTH(state) && (ch == 'e' || ch == 'E') && seenDigit) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ }
+ while (WITHIN_WIDTH(state) && isdigit(ch)) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ }
+ }
+ if (WITHIN_WIDTH(state)) {
+ UNGET(state, ch);
+ }
+ if (!seenDigit) {
+ return PR_FAILURE;
+ }
+ *p = '\0';
+ if (state->assign) {
+ PRFloat64 dval = PR_strtod(buf, NULL);
+
+ state->converted = PR_TRUE;
+ if (state->sizeSpec == _PR_size_l) {
+ *va_arg(state->ap, PRFloat64 *) = dval;
+ } else if (state->sizeSpec == _PR_size_L) {
+ *va_arg(state->ap, long double *) = dval;
+ } else {
+ *va_arg(state->ap, float *) = (float) dval;
+ }
+ }
+ return PR_SUCCESS;
+}
+
+/*
+ * Convert, and return the end of the conversion spec.
+ * Return NULL on error.
+ */
+
+static const char *
+Convert(ScanfState *state, const char *fmt)
+{
+ const char *cPtr;
+ int ch;
+ char *cArg = NULL;
+
+ state->converted = PR_FALSE;
+ cPtr = fmt;
+ if (*cPtr != 'c' && *cPtr != 'n' && *cPtr != '[') {
+ do {
+ ch = GET(state);
+ } while (isspace(ch));
+ UNGET(state, ch);
+ }
+ switch (*cPtr) {
+ case 'c':
+ if (state->assign) {
+ cArg = va_arg(state->ap, char *);
+ }
+ if (state->width == 0) {
+ state->width = 1;
+ }
+ for (; state->width > 0; state->width--) {
+ ch = GET(state);
+ if (ch == EOF) {
+ return NULL;
+ }
+ if (state->assign) {
+ *cArg++ = ch;
+ }
+ }
+ if (state->assign) {
+ state->converted = PR_TRUE;
+ }
+ break;
+ case 'p':
+ case 'd': case 'i': case 'o':
+ case 'u': case 'x': case 'X':
+ if (GetInt(state, *cPtr) == PR_FAILURE) {
+ return NULL;
+ }
+ break;
+ case 'e': case 'E': case 'f':
+ case 'g': case 'G':
+ if (GetFloat(state) == PR_FAILURE) {
+ return NULL;
+ }
+ break;
+ case 'n':
+ /* do not consume any input */
+ if (state->assign) {
+ switch (state->sizeSpec) {
+ case _PR_size_none:
+ *va_arg(state->ap, PRIntn *) = state->nChar;
+ break;
+ case _PR_size_h:
+ *va_arg(state->ap, PRInt16 *) = state->nChar;
+ break;
+ case _PR_size_l:
+ *va_arg(state->ap, PRInt32 *) = state->nChar;
+ break;
+ case _PR_size_ll:
+ LL_I2L(*va_arg(state->ap, PRInt64 *), state->nChar);
+ break;
+ default:
+ PR_ASSERT(0);
+ }
+ }
+ break;
+ case 's':
+ if (state->width == 0) {
+ state->width = INT_MAX;
+ }
+ if (state->assign) {
+ cArg = va_arg(state->ap, char *);
+ }
+ for (; state->width > 0; state->width--) {
+ ch = GET(state);
+ if ((ch == EOF) || isspace(ch)) {
+ UNGET(state, ch);
+ break;
+ }
+ if (state->assign) {
+ *cArg++ = ch;
+ }
+ }
+ if (state->assign) {
+ *cArg = '\0';
+ state->converted = PR_TRUE;
+ }
+ break;
+ case '%':
+ ch = GET(state);
+ if (ch != '%') {
+ UNGET(state, ch);
+ return NULL;
+ }
+ break;
+ case '[':
+ {
+ PRBool complement = PR_FALSE;
+ const char *closeBracket;
+ size_t n;
+
+ if (*++cPtr == '^') {
+ complement = PR_TRUE;
+ cPtr++;
+ }
+ closeBracket = strchr(*cPtr == ']' ? cPtr + 1 : cPtr, ']');
+ if (closeBracket == NULL) {
+ return NULL;
+ }
+ n = closeBracket - cPtr;
+ if (state->width == 0) {
+ state->width = INT_MAX;
+ }
+ if (state->assign) {
+ cArg = va_arg(state->ap, char *);
+ }
+ for (; state->width > 0; state->width--) {
+ ch = GET(state);
+ if ((ch == EOF)
+ || (!complement && !memchr(cPtr, ch, n))
+ || (complement && memchr(cPtr, ch, n))) {
+ UNGET(state, ch);
+ break;
+ }
+ if (state->assign) {
+ *cArg++ = ch;
+ }
+ }
+ if (state->assign) {
+ *cArg = '\0';
+ state->converted = PR_TRUE;
+ }
+ cPtr = closeBracket;
+ }
+ break;
+ default:
+ return NULL;
+ }
+ return cPtr;
+}
+
+static PRInt32
+DoScanf(ScanfState *state, const char *fmt)
+{
+ PRInt32 nConverted = 0;
+ const char *cPtr;
+ int ch;
+
+ state->nChar = 0;
+ cPtr = fmt;
+ while (1) {
+ if (isspace(*cPtr)) {
+ /* white space: skip */
+ do {
+ cPtr++;
+ } while (isspace(*cPtr));
+ do {
+ ch = GET(state);
+ } while (isspace(ch));
+ UNGET(state, ch);
+ } else if (*cPtr == '%') {
+ /* format spec: convert */
+ cPtr++;
+ state->assign = PR_TRUE;
+ if (*cPtr == '*') {
+ cPtr++;
+ state->assign = PR_FALSE;
+ }
+ for (state->width = 0; isdigit(*cPtr); cPtr++) {
+ state->width = state->width * 10 + *cPtr - '0';
+ }
+ state->sizeSpec = _PR_size_none;
+ if (*cPtr == 'h') {
+ cPtr++;
+ state->sizeSpec = _PR_size_h;
+ } else if (*cPtr == 'l') {
+ cPtr++;
+ if (*cPtr == 'l') {
+ cPtr++;
+ state->sizeSpec = _PR_size_ll;
+ } else {
+ state->sizeSpec = _PR_size_l;
+ }
+ } else if (*cPtr == 'L') {
+ cPtr++;
+ state->sizeSpec = _PR_size_L;
+ }
+ cPtr = Convert(state, cPtr);
+ if (cPtr == NULL) {
+ return (nConverted > 0 ? nConverted : EOF);
+ }
+ if (state->converted) {
+ nConverted++;
+ }
+ cPtr++;
+ } else {
+ /* others: must match */
+ if (*cPtr == '\0') {
+ return nConverted;
+ }
+ ch = GET(state);
+ if (ch != *cPtr) {
+ UNGET(state, ch);
+ return nConverted;
+ }
+ cPtr++;
+ }
+ }
+}
+
+static int
+StringGetChar(void *stream)
+{
+ char *cPtr = *((char **) stream);
+
+ if (*cPtr == '\0') {
+ return EOF;
+ }
+ *((char **) stream) = cPtr + 1;
+ return (unsigned char) *cPtr;
+}
+
+static void
+StringUngetChar(void *stream, int ch)
+{
+ char *cPtr = *((char **) stream);
+
+ if (ch != EOF) {
+ *((char **) stream) = cPtr - 1;
+ }
+}
+
+PR_IMPLEMENT(PRInt32)
+PR_sscanf(const char *buf, const char *fmt, ...)
+{
+ PRInt32 rv;
+ ScanfState state;
+
+ state.get = &StringGetChar;
+ state.unget = &StringUngetChar;
+ state.stream = (void *) &buf;
+ va_start(state.ap, fmt);
+ rv = DoScanf(&state, fmt);
+ va_end(state.ap);
+ return rv;
+}
diff --git a/nsprpub/pr/src/io/prsocket.c b/nsprpub/pr/src/io/prsocket.c
new file mode 100644
index 0000000000..4ca2bad8f2
--- /dev/null
+++ b/nsprpub/pr/src/io/prsocket.c
@@ -0,0 +1,2024 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <string.h>
+
+#if defined(_WIN64)
+#ifndef SO_UPDATE_CONNECT_CONTEXT
+#define SO_UPDATE_CONNECT_CONTEXT 0x7010
+#endif
+#endif
+
+/************************************************************************/
+
+/* These two functions are only used in assertions. */
+#if defined(DEBUG)
+
+PRBool IsValidNetAddr(const PRNetAddr *addr)
+{
+ if ((addr != NULL)
+#if defined(XP_UNIX) || defined(XP_OS2)
+ && (addr->raw.family != PR_AF_LOCAL)
+#endif
+ && (addr->raw.family != PR_AF_INET6)
+ && (addr->raw.family != PR_AF_INET)) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+static PRBool IsValidNetAddrLen(const PRNetAddr *addr, PRInt32 addr_len)
+{
+ /*
+ * The definition of the length of a Unix domain socket address
+ * is not uniform, so we don't check it.
+ */
+ if ((addr != NULL)
+#if defined(XP_UNIX) || defined(XP_OS2)
+ && (addr->raw.family != AF_UNIX)
+#endif
+ && (PR_NETADDR_SIZE(addr) != addr_len)) {
+#if defined(LINUX) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 1
+ /*
+ * In glibc 2.1, struct sockaddr_in6 is 24 bytes. In glibc 2.2
+ * and in the 2.4 kernel, struct sockaddr_in6 has the scope_id
+ * field and is 28 bytes. It is possible for socket functions
+ * to return an addr_len greater than sizeof(struct sockaddr_in6).
+ * We need to allow that. (Bugzilla bug #77264)
+ */
+ if ((PR_AF_INET6 == addr->raw.family)
+ && (sizeof(addr->ipv6) == addr_len)) {
+ return PR_TRUE;
+ }
+#endif
+ /*
+ * The accept(), getsockname(), etc. calls on some platforms
+ * do not set the actual socket address length on return.
+ * In this case, we verifiy addr_len is still the value we
+ * passed in (i.e., sizeof(PRNetAddr)).
+ */
+#if defined(QNX)
+ if (sizeof(PRNetAddr) == addr_len) {
+ return PR_TRUE;
+ }
+#endif
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+#endif /* DEBUG */
+
+static PRInt32 PR_CALLBACK SocketWritev(PRFileDesc *fd, const PRIOVec *iov,
+ PRInt32 iov_size, PRIntervalTime timeout)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ int w = 0;
+ const PRIOVec *tmp_iov;
+#define LOCAL_MAXIOV 8
+ PRIOVec local_iov[LOCAL_MAXIOV];
+ PRIOVec *iov_copy = NULL;
+ int tmp_out;
+ int index, iov_cnt;
+ int count=0, sz = 0; /* 'count' is the return value. */
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+
+ /*
+ * Assume the first writev will succeed. Copy iov's only on
+ * failure.
+ */
+ tmp_iov = iov;
+ for (index = 0; index < iov_size; index++) {
+ sz += iov[index].iov_len;
+ }
+
+ iov_cnt = iov_size;
+
+ while (sz > 0) {
+
+ w = _PR_MD_WRITEV(fd, tmp_iov, iov_cnt, timeout);
+ if (w < 0) {
+ count = -1;
+ break;
+ }
+ count += w;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ sz -= w;
+
+ if (sz > 0) {
+ /* find the next unwritten vector */
+ for ( index = 0, tmp_out = count;
+ tmp_out >= iov[index].iov_len;
+ tmp_out -= iov[index].iov_len, index++) {;} /* nothing to execute */
+
+ if (tmp_iov == iov) {
+ /*
+ * The first writev failed so we
+ * must copy iov's around.
+ * Avoid calloc/free if there
+ * are few enough iov's.
+ */
+ if (iov_size - index <= LOCAL_MAXIOV) {
+ iov_copy = local_iov;
+ }
+ else if ((iov_copy = (PRIOVec *) PR_CALLOC((iov_size - index) *
+ sizeof *iov_copy)) == NULL) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ tmp_iov = iov_copy;
+ }
+
+ PR_ASSERT(tmp_iov == iov_copy);
+
+ /* fill in the first partial read */
+ iov_copy[0].iov_base = &(((char *)iov[index].iov_base)[tmp_out]);
+ iov_copy[0].iov_len = iov[index].iov_len - tmp_out;
+ index++;
+
+ /* copy the remaining vectors */
+ for (iov_cnt=1; index<iov_size; iov_cnt++, index++) {
+ iov_copy[iov_cnt].iov_base = iov[index].iov_base;
+ iov_copy[iov_cnt].iov_len = iov[index].iov_len;
+ }
+ }
+ }
+
+ if (iov_copy != local_iov) {
+ PR_DELETE(iov_copy);
+ }
+ return count;
+}
+
+/************************************************************************/
+
+PR_IMPLEMENT(PRFileDesc *) PR_ImportTCPSocket(PROsfd osfd)
+{
+ PRFileDesc *fd;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ fd = PR_AllocFileDesc(osfd, PR_GetTCPMethods());
+ if (fd != NULL) {
+ _PR_MD_MAKE_NONBLOCK(fd);
+ _PR_MD_INIT_FD_INHERITABLE(fd, PR_TRUE);
+#ifdef _PR_NEED_SECRET_AF
+ /* this means we can only import IPv4 sockets here.
+ * but this is what the function in ptio.c does.
+ * We need a way to import IPv6 sockets, too.
+ */
+ fd->secret->af = AF_INET;
+#endif
+ } else {
+ _PR_MD_CLOSE_SOCKET(osfd);
+ }
+ return(fd);
+}
+
+PR_IMPLEMENT(PRFileDesc *) PR_ImportUDPSocket(PROsfd osfd)
+{
+ PRFileDesc *fd;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods());
+ if (fd != NULL) {
+ _PR_MD_MAKE_NONBLOCK(fd);
+ _PR_MD_INIT_FD_INHERITABLE(fd, PR_TRUE);
+ } else {
+ _PR_MD_CLOSE_SOCKET(osfd);
+ }
+ return(fd);
+}
+
+
+static const PRIOMethods* PR_GetSocketPollFdMethods(void);
+
+PR_IMPLEMENT(PRFileDesc*) PR_CreateSocketPollFd(PROsfd osfd)
+{
+ PRFileDesc *fd;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ fd = _PR_Getfd();
+
+ if (fd == NULL) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ else
+ {
+ fd->secret->md.osfd = osfd;
+ fd->secret->inheritable = _PR_TRI_FALSE;
+ fd->secret->state = _PR_FILEDESC_OPEN;
+ fd->methods = PR_GetSocketPollFdMethods();
+ }
+
+ return fd;
+} /* PR_CreateSocketPollFD */
+
+PR_IMPLEMENT(PRStatus) PR_DestroySocketPollFd(PRFileDesc *fd)
+{
+ if (NULL == fd)
+ {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+ fd->secret->state = _PR_FILEDESC_CLOSED;
+ _PR_Putfd(fd);
+ return PR_SUCCESS;
+} /* PR_DestroySocketPollFd */
+
+static PRStatus PR_CALLBACK SocketConnect(
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRInt32 rv; /* Return value of _PR_MD_CONNECT */
+ const PRNetAddr *addrp = addr;
+#if defined(_PR_INET6)
+ PRNetAddr addrCopy;
+#endif
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return PR_FAILURE;
+ }
+#if defined(_PR_INET6)
+ if (addr->raw.family == PR_AF_INET6) {
+ addrCopy = *addr;
+ addrCopy.raw.family = AF_INET6;
+ addrp = &addrCopy;
+ }
+#endif
+
+ rv = _PR_MD_CONNECT(fd, addrp, PR_NETADDR_SIZE(addr), timeout);
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("connect -> %d", rv));
+ if (rv == 0) {
+ return PR_SUCCESS;
+ }
+ else {
+ return PR_FAILURE;
+ }
+}
+
+static PRStatus PR_CALLBACK SocketConnectContinue(
+ PRFileDesc *fd, PRInt16 out_flags)
+{
+ PROsfd osfd;
+ int err;
+
+ if (out_flags & PR_POLL_NVAL) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if ((out_flags & (PR_POLL_WRITE | PR_POLL_EXCEPT | PR_POLL_ERR)) == 0) {
+ PR_ASSERT(out_flags == 0);
+ PR_SetError(PR_IN_PROGRESS_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ osfd = fd->secret->md.osfd;
+
+#if defined(XP_UNIX)
+
+ err = _MD_unix_get_nonblocking_connect_error(osfd);
+ if (err != 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+
+#elif defined(WIN32) || defined(WIN16)
+
+ if (out_flags & PR_POLL_EXCEPT) {
+ int len = sizeof(err);
+ if (getsockopt(osfd, (int)SOL_SOCKET, SO_ERROR, (char *) &err, &len)
+ == SOCKET_ERROR) {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+ if (err != 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ } else {
+#if defined(_WIN64)
+ if (fd->secret->overlappedActive) {
+ PRInt32 rvSent;
+ if (GetOverlappedResult((HANDLE)osfd, &fd->secret->ol, &rvSent, FALSE) == FALSE) {
+ err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("SocketConnectContinue GetOverlappedResult failed %d\n", err));
+ if (err != ERROR_IO_INCOMPLETE) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ fd->secret->overlappedActive = PR_FALSE;
+ }
+ }
+ }
+ if (err == 0) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ }
+#else
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+#endif
+ }
+ return PR_FAILURE;
+ }
+
+ PR_ASSERT(out_flags & PR_POLL_WRITE);
+
+#if defined(_WIN64)
+ if (fd->secret->alreadyConnected) {
+ fd->secret->alreadyConnected = PR_FALSE;
+ }
+ /* TCP Fast Open on Windows must use ConnectEx, which uses overlapped
+ * input/output.
+ * To get result we need to use GetOverlappedResult. */
+ if (fd->secret->overlappedActive) {
+ PR_ASSERT(fd->secret->nonblocking);
+ PRInt32 rvSent;
+ if (GetOverlappedResult((HANDLE)osfd, &fd->secret->ol, &rvSent, FALSE) == TRUE) {
+ fd->secret->overlappedActive = PR_FALSE;
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("SocketConnectContinue GetOverlappedResult succeeded\n"));
+ /* When ConnectEx is used, all previously set socket options and
+ * property are not enabled and to enable them
+ * SO_UPDATE_CONNECT_CONTEXT option need to be set. */
+ if (setsockopt((SOCKET)osfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0) != 0) {
+ err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("SocketConnectContinue setting SO_UPDATE_CONNECT_CONTEXT failed %d\n", err));
+ _PR_MD_MAP_SETSOCKOPT_ERROR(err);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+ } else {
+ err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("SocketConnectContinue GetOverlappedResult failed %d\n", err));
+ if (err != ERROR_IO_INCOMPLETE) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ fd->secret->overlappedActive = PR_FALSE;
+ return PR_FAILURE;
+ } else {
+ PR_SetError(PR_IN_PROGRESS_ERROR, 0);
+ return PR_FAILURE;
+ }
+ }
+ }
+#endif
+
+ return PR_SUCCESS;
+
+#elif defined(XP_OS2)
+
+ err = _MD_os2_get_nonblocking_connect_error(osfd);
+ if (err != 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+
+
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+#endif
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd)
+{
+ /* Find the NSPR layer and invoke its connectcontinue method */
+ PRFileDesc *bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+
+ if (NULL == bottom) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ return SocketConnectContinue(bottom, pd->out_flags);
+}
+
+static PRFileDesc* PR_CALLBACK SocketAccept(PRFileDesc *fd, PRNetAddr *addr,
+ PRIntervalTime timeout)
+{
+ PROsfd osfd;
+ PRFileDesc *fd2;
+ PRUint32 al;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+#ifdef WINNT
+ PRNetAddr addrCopy;
+#endif
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return 0;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return 0;
+ }
+
+#ifdef WINNT
+ if (addr == NULL) {
+ addr = &addrCopy;
+ }
+#endif
+ al = sizeof(PRNetAddr);
+ osfd = _PR_MD_ACCEPT(fd, addr, &al, timeout);
+ if (osfd == -1) {
+ return 0;
+ }
+
+ fd2 = PR_AllocFileDesc(osfd, PR_GetTCPMethods());
+ if (!fd2) {
+ _PR_MD_CLOSE_SOCKET(osfd);
+ return NULL;
+ }
+
+ fd2->secret->nonblocking = fd->secret->nonblocking;
+ fd2->secret->inheritable = fd->secret->inheritable;
+#ifdef WINNT
+ if (!fd2->secret->nonblocking && fd2->secret->inheritable != _PR_TRI_TRUE) {
+ /*
+ * The new socket has been associated with an I/O
+ * completion port. There is no going back.
+ */
+ fd2->secret->md.io_model_committed = PR_TRUE;
+ }
+ PR_ASSERT(al == PR_NETADDR_SIZE(addr));
+ fd2->secret->md.accepted_socket = PR_TRUE;
+ memcpy(&fd2->secret->md.peer_addr, addr, al);
+#endif
+
+ /*
+ * On some platforms, the new socket created by accept()
+ * inherits the nonblocking (or overlapped io) attribute
+ * of the listening socket. As an optimization, these
+ * platforms can skip the following _PR_MD_MAKE_NONBLOCK
+ * call.
+ */
+#if !defined(SOLARIS) && !defined(WINNT)
+ _PR_MD_MAKE_NONBLOCK(fd2);
+#endif
+
+#ifdef _PR_INET6
+ if (addr && (AF_INET6 == addr->raw.family)) {
+ addr->raw.family = PR_AF_INET6;
+ }
+#endif
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+ PR_ASSERT(IsValidNetAddrLen(addr, al) == PR_TRUE);
+
+ return fd2;
+}
+
+#ifdef WINNT
+PR_IMPLEMENT(PRFileDesc*) PR_NTFast_Accept(PRFileDesc *fd, PRNetAddr *addr,
+ PRIntervalTime timeout)
+{
+ PROsfd osfd;
+ PRFileDesc *fd2;
+ PRIntn al;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRNetAddr addrCopy;
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return 0;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return 0;
+ }
+
+ if (addr == NULL) {
+ addr = &addrCopy;
+ }
+ al = PR_NETADDR_SIZE(addr);
+ osfd = _PR_MD_FAST_ACCEPT(fd, addr, &al, timeout, PR_TRUE, NULL, NULL);
+ if (osfd == -1) {
+ return 0;
+ }
+
+ fd2 = PR_AllocFileDesc(osfd, PR_GetTCPMethods());
+ if (!fd2) {
+ _PR_MD_CLOSE_SOCKET(osfd);
+ } else {
+ fd2->secret->nonblocking = fd->secret->nonblocking;
+ fd2->secret->md.io_model_committed = PR_TRUE;
+ PR_ASSERT(al == PR_NETADDR_SIZE(addr));
+ fd2->secret->md.accepted_socket = PR_TRUE;
+ memcpy(&fd2->secret->md.peer_addr, addr, al);
+#ifdef _PR_INET6
+ if (AF_INET6 == addr->raw.family) {
+ addr->raw.family = PR_AF_INET6;
+ }
+#endif
+#ifdef _PR_NEED_SECRET_AF
+ fd2->secret->af = fd->secret->af;
+#endif
+ }
+ return fd2;
+}
+#endif /* WINNT */
+
+
+static PRStatus PR_CALLBACK SocketBind(PRFileDesc *fd, const PRNetAddr *addr)
+{
+ PRInt32 result;
+ const PRNetAddr *addrp = addr;
+#if defined(_PR_INET6)
+ PRNetAddr addrCopy;
+#endif
+
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+
+#ifdef XP_UNIX
+ if (addr->raw.family == AF_UNIX) {
+ /* Disallow relative pathnames */
+ if (addr->local.path[0] != '/') {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ }
+#endif /* XP_UNIX */
+
+#if defined(_PR_INET6)
+ if (addr->raw.family == PR_AF_INET6) {
+ addrCopy = *addr;
+ addrCopy.raw.family = AF_INET6;
+ addrp = &addrCopy;
+ }
+#endif
+ result = _PR_MD_BIND(fd, addrp, PR_NETADDR_SIZE(addr));
+ if (result < 0) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK SocketListen(PRFileDesc *fd, PRIntn backlog)
+{
+ PRInt32 result;
+
+ result = _PR_MD_LISTEN(fd, backlog);
+ if (result < 0) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK SocketShutdown(PRFileDesc *fd, PRIntn how)
+{
+ PRInt32 result;
+
+ result = _PR_MD_SHUTDOWN(fd, how);
+ if (result < 0) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static PRInt32 PR_CALLBACK SocketRecv(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if ((flags != 0) && (flags != PR_MSG_PEEK)) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+
+ PR_LOG(_pr_io_lm, PR_LOG_MAX,
+ ("recv: fd=%p osfd=%" PR_PRIdOSFD " buf=%p amount=%d flags=%d",
+ fd, fd->secret->md.osfd, buf, amount, flags));
+
+#ifdef _PR_HAVE_PEEK_BUFFER
+ if (fd->secret->peekBytes != 0) {
+ rv = (amount < fd->secret->peekBytes) ?
+ amount : fd->secret->peekBytes;
+ memcpy(buf, fd->secret->peekBuffer, rv);
+ if (flags == 0) {
+ /* consume the bytes in the peek buffer */
+ fd->secret->peekBytes -= rv;
+ if (fd->secret->peekBytes != 0) {
+ memmove(fd->secret->peekBuffer,
+ fd->secret->peekBuffer + rv,
+ fd->secret->peekBytes);
+ }
+ }
+ return rv;
+ }
+
+ /* allocate peek buffer, if necessary */
+ if ((PR_MSG_PEEK == flags) && _PR_FD_NEED_EMULATE_MSG_PEEK(fd)) {
+ PR_ASSERT(0 == fd->secret->peekBytes);
+ /* impose a max size on the peek buffer */
+ if (amount > _PR_PEEK_BUFFER_MAX) {
+ amount = _PR_PEEK_BUFFER_MAX;
+ }
+ if (fd->secret->peekBufSize < amount) {
+ if (fd->secret->peekBuffer) {
+ PR_Free(fd->secret->peekBuffer);
+ }
+ fd->secret->peekBufSize = amount;
+ fd->secret->peekBuffer = PR_Malloc(amount);
+ if (NULL == fd->secret->peekBuffer) {
+ fd->secret->peekBufSize = 0;
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ }
+ }
+#endif
+
+ rv = _PR_MD_RECV(fd, buf, amount, flags, timeout);
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("recv -> %d, error = %d, os error = %d",
+ rv, PR_GetError(), PR_GetOSError()));
+
+#ifdef _PR_HAVE_PEEK_BUFFER
+ if ((PR_MSG_PEEK == flags) && _PR_FD_NEED_EMULATE_MSG_PEEK(fd)) {
+ if (rv > 0) {
+ memcpy(fd->secret->peekBuffer, buf, rv);
+ fd->secret->peekBytes = rv;
+ }
+ }
+#endif
+
+ return rv;
+}
+
+static PRInt32 PR_CALLBACK SocketRead(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ return SocketRecv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
+}
+
+static PRInt32 PR_CALLBACK SocketSend(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ PRInt32 temp, count;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+
+ count = 0;
+ while (amount > 0) {
+ PR_LOG(_pr_io_lm, PR_LOG_MAX,
+ ("send: fd=%p osfd=%" PR_PRIdOSFD " buf=%p amount=%d",
+ fd, fd->secret->md.osfd, buf, amount));
+ temp = _PR_MD_SEND(fd, buf, amount, flags, timeout);
+ if (temp < 0) {
+ count = -1;
+ break;
+ }
+
+ count += temp;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ buf = (const void*) ((const char*)buf + temp);
+
+ amount -= temp;
+ }
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("send -> %d", count));
+ return count;
+}
+
+static PRInt32 PR_CALLBACK SocketWrite(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ return SocketSend(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
+}
+
+static PRStatus PR_CALLBACK SocketClose(PRFileDesc *fd)
+{
+ if (!fd || !fd->secret
+ || (fd->secret->state != _PR_FILEDESC_OPEN
+ && fd->secret->state != _PR_FILEDESC_CLOSED)) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (fd->secret->state == _PR_FILEDESC_OPEN) {
+#if defined(_WIN64) && defined(WIN95)
+ /* TCP Fast Open on Windows must use ConnectEx, which uses overlapped
+ * input/output. Before closing such a socket we must cancelIO.
+ */
+ if (fd->secret->overlappedActive) {
+ PR_ASSERT(fd->secret->nonblocking);
+ if (CancelIo((HANDLE) fd->secret->md.osfd) == TRUE) {
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("SocketClose - CancelIo succeeded\n"));
+ } else {
+ DWORD err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("SocketClose - CancelIo failed err=%x\n", err));
+ }
+
+ DWORD rvSent;
+ if (GetOverlappedResult((HANDLE)fd->secret->md.osfd, &fd->secret->ol, &rvSent, FALSE) == TRUE) {
+ fd->secret->overlappedActive = PR_FALSE;
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("SocketClose GetOverlappedResult succeeded\n"));
+ } else {
+ DWORD err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("SocketClose GetOverlappedResult failed %d\n", err));
+ if (err != ERROR_IO_INCOMPLETE) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ fd->secret->overlappedActive = PR_FALSE;
+ }
+ }
+ }
+
+ if (fd->secret->overlappedActive &&
+ _fd_waiting_for_overlapped_done_lock) {
+ // Put osfd into the list to be checked later.
+ PRFileDescList *forWaiting = PR_NEW(PRFileDescList);
+ if (!forWaiting) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return PR_FAILURE;
+ }
+ forWaiting->fd = fd;
+
+ PR_Lock(_fd_waiting_for_overlapped_done_lock);
+ forWaiting->next = _fd_waiting_for_overlapped_done;
+ _fd_waiting_for_overlapped_done = forWaiting;
+ PR_Unlock(_fd_waiting_for_overlapped_done_lock);
+
+ return PR_SUCCESS;
+ }
+#endif
+
+ if (_PR_MD_CLOSE_SOCKET(fd->secret->md.osfd) < 0) {
+ return PR_FAILURE;
+ }
+ fd->secret->state = _PR_FILEDESC_CLOSED;
+ }
+
+#ifdef _PR_HAVE_PEEK_BUFFER
+ if (fd->secret->peekBuffer) {
+ PR_ASSERT(fd->secret->peekBufSize > 0);
+ PR_DELETE(fd->secret->peekBuffer);
+ fd->secret->peekBufSize = 0;
+ fd->secret->peekBytes = 0;
+ }
+#endif
+
+ PR_FreeFileDesc(fd);
+ return PR_SUCCESS;
+}
+
+static PRInt32 PR_CALLBACK SocketAvailable(PRFileDesc *fd)
+{
+ PRInt32 rv;
+#ifdef _PR_HAVE_PEEK_BUFFER
+ if (fd->secret->peekBytes != 0) {
+ return fd->secret->peekBytes;
+ }
+#endif
+ rv = _PR_MD_SOCKETAVAILABLE(fd);
+ return rv;
+}
+
+static PRInt64 PR_CALLBACK SocketAvailable64(PRFileDesc *fd)
+{
+ PRInt64 rv;
+#ifdef _PR_HAVE_PEEK_BUFFER
+ if (fd->secret->peekBytes != 0) {
+ LL_I2L(rv, fd->secret->peekBytes);
+ return rv;
+ }
+#endif
+ LL_I2L(rv, _PR_MD_SOCKETAVAILABLE(fd));
+ return rv;
+}
+
+static PRStatus PR_CALLBACK SocketSync(PRFileDesc *fd)
+{
+ return PR_SUCCESS;
+}
+
+static PRInt32 PR_CALLBACK SocketSendTo(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRInt32 temp, count;
+ const PRNetAddr *addrp = addr;
+#if defined(_PR_INET6)
+ PRNetAddr addrCopy;
+#endif
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+#if defined(_PR_INET6)
+ if (addr->raw.family == PR_AF_INET6) {
+ addrCopy = *addr;
+ addrCopy.raw.family = AF_INET6;
+ addrp = &addrCopy;
+ }
+#endif
+
+ count = 0;
+ do {
+ temp = _PR_MD_SENDTO(fd, buf, amount, flags,
+ addrp, PR_NETADDR_SIZE(addr), timeout);
+ if (temp < 0) {
+ count = -1;
+ break;
+ }
+ count += temp;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ buf = (const void*) ((const char*)buf + temp);
+ amount -= temp;
+ } while (amount > 0);
+ return count;
+}
+
+#if defined(_WIN64) && defined(WIN95)
+static PRInt32 PR_CALLBACK SocketTCPSendTo(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRInt32 temp, count;
+ const PRNetAddr *addrp = addr;
+#if defined(_PR_INET6)
+ PRNetAddr addrCopy;
+#endif
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+#if defined(_PR_INET6)
+ if (addr->raw.family == PR_AF_INET6) {
+ addrCopy = *addr;
+ addrCopy.raw.family = AF_INET6;
+ addrp = &addrCopy;
+ }
+#endif
+
+ count = 0;
+ while (amount > 0) {
+ temp = _PR_MD_TCPSENDTO(fd, buf, amount, flags,
+ addrp, PR_NETADDR_SIZE(addr), timeout);
+ if (temp < 0) {
+ count = -1;
+ break;
+ }
+ count += temp;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ buf = (const void*) ((const char*)buf + temp);
+ amount -= temp;
+ }
+ return count;
+}
+#endif
+
+static PRInt32 PR_CALLBACK SocketRecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRInt32 rv;
+ PRUint32 al;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+
+ al = sizeof(PRNetAddr);
+ rv = _PR_MD_RECVFROM(fd, buf, amount, flags, addr, &al, timeout);
+#ifdef _PR_INET6
+ if (addr && (AF_INET6 == addr->raw.family)) {
+ addr->raw.family = PR_AF_INET6;
+ }
+#endif
+ return rv;
+}
+
+static PRInt32 PR_CALLBACK SocketAcceptRead(PRFileDesc *sd, PRFileDesc **nd,
+ PRNetAddr **raddr, void *buf, PRInt32 amount,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+ /* The socket must be in blocking mode. */
+ if (sd->secret->nonblocking) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+ *nd = NULL;
+
+#if defined(WINNT)
+ {
+ PROsfd newSock;
+ PRNetAddr *raddrCopy;
+
+ if (raddr == NULL) {
+ raddr = &raddrCopy;
+ }
+ rv = _PR_MD_ACCEPT_READ(sd, &newSock, raddr, buf, amount, timeout);
+ if (rv < 0) {
+ rv = -1;
+ } else {
+ /* Successfully accepted and read; create the new PRFileDesc */
+ *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods());
+ if (*nd == 0) {
+ _PR_MD_CLOSE_SOCKET(newSock);
+ /* PR_AllocFileDesc() has invoked PR_SetError(). */
+ rv = -1;
+ } else {
+ (*nd)->secret->md.io_model_committed = PR_TRUE;
+ (*nd)->secret->md.accepted_socket = PR_TRUE;
+ memcpy(&(*nd)->secret->md.peer_addr, *raddr,
+ PR_NETADDR_SIZE(*raddr));
+#ifdef _PR_INET6
+ if (AF_INET6 == *raddr->raw.family) {
+ *raddr->raw.family = PR_AF_INET6;
+ }
+#endif
+ }
+ }
+ }
+#else
+ rv = PR_EmulateAcceptRead(sd, nd, raddr, buf, amount, timeout);
+#endif
+ return rv;
+}
+
+#ifdef WINNT
+PR_IMPLEMENT(PRInt32) PR_NTFast_AcceptRead(PRFileDesc *sd, PRFileDesc **nd,
+ PRNetAddr **raddr, void *buf, PRInt32 amount,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+ PROsfd newSock;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRNetAddr *raddrCopy;
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+ *nd = NULL;
+
+ if (raddr == NULL) {
+ raddr = &raddrCopy;
+ }
+ rv = _PR_MD_FAST_ACCEPT_READ(sd, &newSock, raddr, buf, amount,
+ timeout, PR_TRUE, NULL, NULL);
+ if (rv < 0) {
+ rv = -1;
+ } else {
+ /* Successfully accepted and read; create the new PRFileDesc */
+ *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods());
+ if (*nd == 0) {
+ _PR_MD_CLOSE_SOCKET(newSock);
+ /* PR_AllocFileDesc() has invoked PR_SetError(). */
+ rv = -1;
+ } else {
+ (*nd)->secret->md.io_model_committed = PR_TRUE;
+ (*nd)->secret->md.accepted_socket = PR_TRUE;
+ memcpy(&(*nd)->secret->md.peer_addr, *raddr,
+ PR_NETADDR_SIZE(*raddr));
+#ifdef _PR_INET6
+ if (AF_INET6 == *raddr->raw.family) {
+ *raddr->raw.family = PR_AF_INET6;
+ }
+#endif
+#ifdef _PR_NEED_SECRET_AF
+ (*nd)->secret->af = sd->secret->af;
+#endif
+ }
+ }
+ return rv;
+}
+
+PR_IMPLEMENT(PRInt32) PR_NTFast_AcceptRead_WithTimeoutCallback(
+ PRFileDesc *sd, PRFileDesc **nd,
+ PRNetAddr **raddr, void *buf, PRInt32 amount,
+ PRIntervalTime timeout,
+ _PR_AcceptTimeoutCallback callback,
+ void *callbackArg)
+{
+ PRInt32 rv;
+ PROsfd newSock;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRNetAddr *raddrCopy;
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+ *nd = NULL;
+
+ if (raddr == NULL) {
+ raddr = &raddrCopy;
+ }
+ rv = _PR_MD_FAST_ACCEPT_READ(sd, &newSock, raddr, buf, amount,
+ timeout, PR_TRUE, callback, callbackArg);
+ if (rv < 0) {
+ rv = -1;
+ } else {
+ /* Successfully accepted and read; create the new PRFileDesc */
+ *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods());
+ if (*nd == 0) {
+ _PR_MD_CLOSE_SOCKET(newSock);
+ /* PR_AllocFileDesc() has invoked PR_SetError(). */
+ rv = -1;
+ } else {
+ (*nd)->secret->md.io_model_committed = PR_TRUE;
+ (*nd)->secret->md.accepted_socket = PR_TRUE;
+ memcpy(&(*nd)->secret->md.peer_addr, *raddr,
+ PR_NETADDR_SIZE(*raddr));
+#ifdef _PR_INET6
+ if (AF_INET6 == *raddr->raw.family) {
+ *raddr->raw.family = PR_AF_INET6;
+ }
+#endif
+#ifdef _PR_NEED_SECRET_AF
+ (*nd)->secret->af = sd->secret->af;
+#endif
+ }
+ }
+ return rv;
+}
+#endif /* WINNT */
+
+#ifdef WINNT
+PR_IMPLEMENT(void)
+PR_NTFast_UpdateAcceptContext(PRFileDesc *socket, PRFileDesc *acceptSocket)
+{
+ _PR_MD_UPDATE_ACCEPT_CONTEXT(
+ socket->secret->md.osfd, acceptSocket->secret->md.osfd);
+}
+#endif /* WINNT */
+
+static PRInt32 PR_CALLBACK SocketSendFile(
+ PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ PRInt32 rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+ /* The socket must be in blocking mode. */
+ if (sd->secret->nonblocking) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+#if defined(WINNT)
+ rv = _PR_MD_SENDFILE(sd, sfd, flags, timeout);
+ if ((rv >= 0) && (flags == PR_TRANSMITFILE_CLOSE_SOCKET)) {
+ /*
+ * This should be kept the same as SocketClose, except
+ * that _PR_MD_CLOSE_SOCKET(sd->secret->md.osfd) should
+ * not be called because the socket will be recycled.
+ */
+ PR_FreeFileDesc(sd);
+ }
+#else
+ rv = PR_EmulateSendFile(sd, sfd, flags, timeout);
+#endif /* WINNT */
+
+ return rv;
+}
+
+static PRInt32 PR_CALLBACK SocketTransmitFile(PRFileDesc *sd, PRFileDesc *fd,
+ const void *headers, PRInt32 hlen, PRTransmitFileFlags flags,
+ PRIntervalTime timeout)
+{
+ PRSendFileData sfd;
+
+ sfd.fd = fd;
+ sfd.file_offset = 0;
+ sfd.file_nbytes = 0;
+ sfd.header = headers;
+ sfd.hlen = hlen;
+ sfd.trailer = NULL;
+ sfd.tlen = 0;
+
+ return(SocketSendFile(sd, &sfd, flags, timeout));
+}
+
+static PRStatus PR_CALLBACK SocketGetName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ PRInt32 result;
+ PRUint32 addrlen;
+
+ addrlen = sizeof(PRNetAddr);
+ result = _PR_MD_GETSOCKNAME(fd, addr, &addrlen);
+ if (result < 0) {
+ return PR_FAILURE;
+ }
+#ifdef _PR_INET6
+ if (AF_INET6 == addr->raw.family) {
+ addr->raw.family = PR_AF_INET6;
+ }
+#endif
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+ PR_ASSERT(IsValidNetAddrLen(addr, addrlen) == PR_TRUE);
+ return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK SocketGetPeerName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ PRInt32 result;
+ PRUint32 addrlen;
+
+ addrlen = sizeof(PRNetAddr);
+ result = _PR_MD_GETPEERNAME(fd, addr, &addrlen);
+ if (result < 0) {
+ return PR_FAILURE;
+ }
+#ifdef _PR_INET6
+ if (AF_INET6 == addr->raw.family) {
+ addr->raw.family = PR_AF_INET6;
+ }
+#endif
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+ PR_ASSERT(IsValidNetAddrLen(addr, addrlen) == PR_TRUE);
+ return PR_SUCCESS;
+}
+
+static PRInt16 PR_CALLBACK SocketPoll(
+ PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
+{
+ *out_flags = 0;
+
+#if defined(_WIN64)
+ if (in_flags & PR_POLL_WRITE) {
+ if (fd->secret->alreadyConnected) {
+ *out_flags = PR_POLL_WRITE;
+ return PR_POLL_WRITE;
+ }
+ }
+#endif
+ return in_flags;
+} /* SocketPoll */
+
+static PRIOMethods tcpMethods = {
+ PR_DESC_SOCKET_TCP,
+ SocketClose,
+ SocketRead,
+ SocketWrite,
+ SocketAvailable,
+ SocketAvailable64,
+ SocketSync,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ SocketWritev,
+ SocketConnect,
+ SocketAccept,
+ SocketBind,
+ SocketListen,
+ SocketShutdown,
+ SocketRecv,
+ SocketSend,
+ (PRRecvfromFN)_PR_InvalidInt,
+#if defined(_WIN64) && defined(WIN95)
+ SocketTCPSendTo, /* This is for fast open. We imitate Linux interface. */
+#else
+ (PRSendtoFN)_PR_InvalidInt,
+#endif
+ SocketPoll,
+ SocketAcceptRead,
+ SocketTransmitFile,
+ SocketGetName,
+ SocketGetPeerName,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ _PR_SocketGetSocketOption,
+ _PR_SocketSetSocketOption,
+ SocketSendFile,
+ SocketConnectContinue,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+static PRIOMethods udpMethods = {
+ PR_DESC_SOCKET_UDP,
+ SocketClose,
+ SocketRead,
+ SocketWrite,
+ SocketAvailable,
+ SocketAvailable64,
+ SocketSync,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ SocketWritev,
+ SocketConnect,
+ (PRAcceptFN)_PR_InvalidDesc,
+ SocketBind,
+ SocketListen,
+ SocketShutdown,
+ SocketRecv,
+ SocketSend,
+ SocketRecvFrom,
+ SocketSendTo,
+ SocketPoll,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ SocketGetName,
+ SocketGetPeerName,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ _PR_SocketGetSocketOption,
+ _PR_SocketSetSocketOption,
+ (PRSendfileFN)_PR_InvalidInt,
+ (PRConnectcontinueFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+
+static PRIOMethods socketpollfdMethods = {
+ (PRDescType) 0,
+ (PRCloseFN)_PR_InvalidStatus,
+ (PRReadFN)_PR_InvalidInt,
+ (PRWriteFN)_PR_InvalidInt,
+ (PRAvailableFN)_PR_InvalidInt,
+ (PRAvailable64FN)_PR_InvalidInt64,
+ (PRFsyncFN)_PR_InvalidStatus,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ (PRWritevFN)_PR_InvalidInt,
+ (PRConnectFN)_PR_InvalidStatus,
+ (PRAcceptFN)_PR_InvalidDesc,
+ (PRBindFN)_PR_InvalidStatus,
+ (PRListenFN)_PR_InvalidStatus,
+ (PRShutdownFN)_PR_InvalidStatus,
+ (PRRecvFN)_PR_InvalidInt,
+ (PRSendFN)_PR_InvalidInt,
+ (PRRecvfromFN)_PR_InvalidInt,
+ (PRSendtoFN)_PR_InvalidInt,
+ SocketPoll,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ (PRGetsocknameFN)_PR_InvalidStatus,
+ (PRGetpeernameFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRGetsocketoptionFN)_PR_InvalidStatus,
+ (PRSetsocketoptionFN)_PR_InvalidStatus,
+ (PRSendfileFN)_PR_InvalidInt,
+ (PRConnectcontinueFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+PR_IMPLEMENT(const PRIOMethods*) PR_GetTCPMethods()
+{
+ return &tcpMethods;
+}
+
+PR_IMPLEMENT(const PRIOMethods*) PR_GetUDPMethods()
+{
+ return &udpMethods;
+}
+
+static const PRIOMethods* PR_GetSocketPollFdMethods()
+{
+ return &socketpollfdMethods;
+} /* PR_GetSocketPollFdMethods */
+
+#if !defined(_PR_INET6) || defined(_PR_INET6_PROBE)
+PR_EXTERN(PRStatus) _pr_push_ipv6toipv4_layer(PRFileDesc *fd);
+
+#if defined(_PR_INET6_PROBE)
+
+extern PRBool _pr_ipv6_is_present(void);
+
+PR_IMPLEMENT(PRBool) _pr_test_ipv6_socket()
+{
+ PROsfd osfd;
+
+ osfd = _PR_MD_SOCKET(AF_INET6, SOCK_STREAM, 0);
+ if (osfd != -1) {
+ _PR_MD_CLOSE_SOCKET(osfd);
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+#endif /* _PR_INET6_PROBE */
+
+#endif
+
+PR_IMPLEMENT(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto)
+{
+ PROsfd osfd;
+ PRFileDesc *fd;
+ PRInt32 tmp_domain = domain;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ if (PR_AF_INET != domain
+ && PR_AF_INET6 != domain
+#if defined(XP_UNIX) || defined(XP_OS2)
+ && PR_AF_LOCAL != domain
+#endif
+ ) {
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0);
+ return NULL;
+ }
+
+#if defined(_PR_INET6_PROBE)
+ if (PR_AF_INET6 == domain) {
+ domain = _pr_ipv6_is_present() ? AF_INET6 : AF_INET;
+ }
+#elif defined(_PR_INET6)
+ if (PR_AF_INET6 == domain) {
+ domain = AF_INET6;
+ }
+#else
+ if (PR_AF_INET6 == domain) {
+ domain = AF_INET;
+ }
+#endif /* _PR_INET6 */
+ osfd = _PR_MD_SOCKET(domain, type, proto);
+ if (osfd == -1) {
+ return 0;
+ }
+ if (type == SOCK_STREAM) {
+ fd = PR_AllocFileDesc(osfd, PR_GetTCPMethods());
+ }
+ else {
+ fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods());
+ }
+ /*
+ * Make the sockets non-blocking
+ */
+ if (fd != NULL) {
+ _PR_MD_MAKE_NONBLOCK(fd);
+ _PR_MD_INIT_FD_INHERITABLE(fd, PR_FALSE);
+#ifdef _PR_NEED_SECRET_AF
+ fd->secret->af = domain;
+#endif
+#if defined(_PR_INET6_PROBE) || !defined(_PR_INET6)
+ /*
+ * For platforms with no support for IPv6
+ * create layered socket for IPv4-mapped IPv6 addresses
+ */
+ if (PR_AF_INET6 == tmp_domain && PR_AF_INET == domain) {
+ if (PR_FAILURE == _pr_push_ipv6toipv4_layer(fd)) {
+ PR_Close(fd);
+ fd = NULL;
+ }
+ }
+#endif
+ } else {
+ _PR_MD_CLOSE_SOCKET(osfd);
+ }
+
+ return fd;
+}
+
+PR_IMPLEMENT(PRFileDesc *) PR_NewTCPSocket(void)
+{
+ PRInt32 domain = AF_INET;
+
+ return PR_Socket(domain, SOCK_STREAM, 0);
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_NewUDPSocket(void)
+{
+ PRInt32 domain = AF_INET;
+
+ return PR_Socket(domain, SOCK_DGRAM, 0);
+}
+
+PR_IMPLEMENT(PRFileDesc *) PR_OpenTCPSocket(PRIntn af)
+{
+ return PR_Socket(af, SOCK_STREAM, 0);
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_OpenUDPSocket(PRIntn af)
+{
+ return PR_Socket(af, SOCK_DGRAM, 0);
+}
+
+PR_IMPLEMENT(PRStatus) PR_NewTCPSocketPair(PRFileDesc *f[])
+{
+#ifdef XP_UNIX
+ PRInt32 rv, osfd[2];
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ rv = _PR_MD_SOCKETPAIR(AF_UNIX, SOCK_STREAM, 0, osfd);
+ if (rv == -1) {
+ return PR_FAILURE;
+ }
+
+ f[0] = PR_AllocFileDesc(osfd[0], PR_GetTCPMethods());
+ if (!f[0]) {
+ _PR_MD_CLOSE_SOCKET(osfd[0]);
+ _PR_MD_CLOSE_SOCKET(osfd[1]);
+ /* PR_AllocFileDesc() has invoked PR_SetError(). */
+ return PR_FAILURE;
+ }
+ f[1] = PR_AllocFileDesc(osfd[1], PR_GetTCPMethods());
+ if (!f[1]) {
+ PR_Close(f[0]);
+ _PR_MD_CLOSE_SOCKET(osfd[1]);
+ /* PR_AllocFileDesc() has invoked PR_SetError(). */
+ return PR_FAILURE;
+ }
+ _PR_MD_MAKE_NONBLOCK(f[0]);
+ _PR_MD_INIT_FD_INHERITABLE(f[0], PR_FALSE);
+ _PR_MD_MAKE_NONBLOCK(f[1]);
+ _PR_MD_INIT_FD_INHERITABLE(f[1], PR_FALSE);
+ return PR_SUCCESS;
+#elif defined(WINNT)
+ /*
+ * A socket pair is often used for interprocess communication,
+ * so we need to make sure neither socket is associated with
+ * the I/O completion port; otherwise it can't be used by a
+ * child process.
+ *
+ * The default implementation below cannot be used for NT
+ * because PR_Accept would have associated the I/O completion
+ * port with the listening and accepted sockets.
+ */
+ SOCKET listenSock;
+ SOCKET osfd[2];
+ struct sockaddr_in selfAddr, peerAddr;
+ int addrLen;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ osfd[0] = osfd[1] = INVALID_SOCKET;
+ listenSock = socket(AF_INET, SOCK_STREAM, 0);
+ if (listenSock == INVALID_SOCKET) {
+ goto failed;
+ }
+ selfAddr.sin_family = AF_INET;
+ selfAddr.sin_port = 0;
+ selfAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* BugZilla: 35408 */
+ addrLen = sizeof(selfAddr);
+ if (bind(listenSock, (struct sockaddr *) &selfAddr,
+ addrLen) == SOCKET_ERROR) {
+ goto failed;
+ }
+ if (getsockname(listenSock, (struct sockaddr *) &selfAddr,
+ &addrLen) == SOCKET_ERROR) {
+ goto failed;
+ }
+ if (listen(listenSock, 5) == SOCKET_ERROR) {
+ goto failed;
+ }
+ osfd[0] = socket(AF_INET, SOCK_STREAM, 0);
+ if (osfd[0] == INVALID_SOCKET) {
+ goto failed;
+ }
+ selfAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ /*
+ * Only a thread is used to do the connect and accept.
+ * I am relying on the fact that connect returns
+ * successfully as soon as the connect request is put
+ * into the listen queue (but before accept is called).
+ * This is the behavior of the BSD socket code. If
+ * connect does not return until accept is called, we
+ * will need to create another thread to call connect.
+ */
+ if (connect(osfd[0], (struct sockaddr *) &selfAddr,
+ addrLen) == SOCKET_ERROR) {
+ goto failed;
+ }
+ /*
+ * A malicious local process may connect to the listening
+ * socket, so we need to verify that the accepted connection
+ * is made from our own socket osfd[0].
+ */
+ if (getsockname(osfd[0], (struct sockaddr *) &selfAddr,
+ &addrLen) == SOCKET_ERROR) {
+ goto failed;
+ }
+ osfd[1] = accept(listenSock, (struct sockaddr *) &peerAddr, &addrLen);
+ if (osfd[1] == INVALID_SOCKET) {
+ goto failed;
+ }
+ if (peerAddr.sin_port != selfAddr.sin_port) {
+ /* the connection we accepted is not from osfd[0] */
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ goto failed;
+ }
+ closesocket(listenSock);
+
+ f[0] = PR_AllocFileDesc(osfd[0], PR_GetTCPMethods());
+ if (!f[0]) {
+ closesocket(osfd[0]);
+ closesocket(osfd[1]);
+ /* PR_AllocFileDesc() has invoked PR_SetError(). */
+ return PR_FAILURE;
+ }
+ f[1] = PR_AllocFileDesc(osfd[1], PR_GetTCPMethods());
+ if (!f[1]) {
+ PR_Close(f[0]);
+ closesocket(osfd[1]);
+ /* PR_AllocFileDesc() has invoked PR_SetError(). */
+ return PR_FAILURE;
+ }
+ _PR_MD_INIT_FD_INHERITABLE(f[0], PR_FALSE);
+ _PR_MD_INIT_FD_INHERITABLE(f[1], PR_FALSE);
+ return PR_SUCCESS;
+
+failed:
+ if (listenSock != INVALID_SOCKET) {
+ closesocket(listenSock);
+ }
+ if (osfd[0] != INVALID_SOCKET) {
+ closesocket(osfd[0]);
+ }
+ if (osfd[1] != INVALID_SOCKET) {
+ closesocket(osfd[1]);
+ }
+ return PR_FAILURE;
+#else /* not Unix or NT */
+ /*
+ * default implementation
+ */
+ PRFileDesc *listenSock;
+ PRNetAddr selfAddr, peerAddr;
+ PRUint16 port;
+
+ f[0] = f[1] = NULL;
+ listenSock = PR_NewTCPSocket();
+ if (listenSock == NULL) {
+ goto failed;
+ }
+ PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &selfAddr); /* BugZilla: 35408 */
+ if (PR_Bind(listenSock, &selfAddr) == PR_FAILURE) {
+ goto failed;
+ }
+ if (PR_GetSockName(listenSock, &selfAddr) == PR_FAILURE) {
+ goto failed;
+ }
+ port = ntohs(selfAddr.inet.port);
+ if (PR_Listen(listenSock, 5) == PR_FAILURE) {
+ goto failed;
+ }
+ f[0] = PR_NewTCPSocket();
+ if (f[0] == NULL) {
+ goto failed;
+ }
+#ifdef _PR_CONNECT_DOES_NOT_BIND
+ /*
+ * If connect does not implicitly bind the socket (e.g., on
+ * BeOS), we have to bind the socket so that we can get its
+ * port with getsockname later.
+ */
+ PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &selfAddr);
+ if (PR_Bind(f[0], &selfAddr) == PR_FAILURE) {
+ goto failed;
+ }
+#endif
+ PR_InitializeNetAddr(PR_IpAddrLoopback, port, &selfAddr);
+
+ /*
+ * Only a thread is used to do the connect and accept.
+ * I am relying on the fact that PR_Connect returns
+ * successfully as soon as the connect request is put
+ * into the listen queue (but before PR_Accept is called).
+ * This is the behavior of the BSD socket code. If
+ * connect does not return until accept is called, we
+ * will need to create another thread to call connect.
+ */
+ if (PR_Connect(f[0], &selfAddr, PR_INTERVAL_NO_TIMEOUT)
+ == PR_FAILURE) {
+ goto failed;
+ }
+ /*
+ * A malicious local process may connect to the listening
+ * socket, so we need to verify that the accepted connection
+ * is made from our own socket f[0].
+ */
+ if (PR_GetSockName(f[0], &selfAddr) == PR_FAILURE) {
+ goto failed;
+ }
+ f[1] = PR_Accept(listenSock, &peerAddr, PR_INTERVAL_NO_TIMEOUT);
+ if (f[1] == NULL) {
+ goto failed;
+ }
+ if (peerAddr.inet.port != selfAddr.inet.port) {
+ /* the connection we accepted is not from f[0] */
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ goto failed;
+ }
+ PR_Close(listenSock);
+ return PR_SUCCESS;
+
+failed:
+ if (listenSock) {
+ PR_Close(listenSock);
+ }
+ if (f[0]) {
+ PR_Close(f[0]);
+ }
+ if (f[1]) {
+ PR_Close(f[1]);
+ }
+ return PR_FAILURE;
+#endif
+}
+
+PR_IMPLEMENT(PROsfd)
+PR_FileDesc2NativeHandle(PRFileDesc *fd)
+{
+ if (fd) {
+ fd = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
+ }
+ if (!fd) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+ return fd->secret->md.osfd;
+}
+
+PR_IMPLEMENT(void)
+PR_ChangeFileDescNativeHandle(PRFileDesc *fd, PROsfd handle)
+{
+ if (fd) {
+ fd->secret->md.osfd = handle;
+ }
+}
+
+/*
+** Select compatibility
+**
+*/
+
+PR_IMPLEMENT(void) PR_FD_ZERO(PR_fd_set *set)
+{
+ memset(set, 0, sizeof(PR_fd_set));
+}
+
+PR_IMPLEMENT(void) PR_FD_SET(PRFileDesc *fh, PR_fd_set *set)
+{
+ PR_ASSERT( set->hsize < PR_MAX_SELECT_DESC );
+
+ set->harray[set->hsize++] = fh;
+}
+
+PR_IMPLEMENT(void) PR_FD_CLR(PRFileDesc *fh, PR_fd_set *set)
+{
+ PRUint32 index, index2;
+
+ for (index = 0; index<set->hsize; index++)
+ if (set->harray[index] == fh) {
+ for (index2=index; index2 < (set->hsize-1); index2++) {
+ set->harray[index2] = set->harray[index2+1];
+ }
+ set->hsize--;
+ break;
+ }
+}
+
+PR_IMPLEMENT(PRInt32) PR_FD_ISSET(PRFileDesc *fh, PR_fd_set *set)
+{
+ PRUint32 index;
+ for (index = 0; index<set->hsize; index++)
+ if (set->harray[index] == fh) {
+ return 1;
+ }
+ return 0;
+}
+
+PR_IMPLEMENT(void) PR_FD_NSET(PROsfd fd, PR_fd_set *set)
+{
+ PR_ASSERT( set->nsize < PR_MAX_SELECT_DESC );
+
+ set->narray[set->nsize++] = fd;
+}
+
+PR_IMPLEMENT(void) PR_FD_NCLR(PROsfd fd, PR_fd_set *set)
+{
+ PRUint32 index, index2;
+
+ for (index = 0; index<set->nsize; index++)
+ if (set->narray[index] == fd) {
+ for (index2=index; index2 < (set->nsize-1); index2++) {
+ set->narray[index2] = set->narray[index2+1];
+ }
+ set->nsize--;
+ break;
+ }
+}
+
+PR_IMPLEMENT(PRInt32) PR_FD_NISSET(PROsfd fd, PR_fd_set *set)
+{
+ PRUint32 index;
+ for (index = 0; index<set->nsize; index++)
+ if (set->narray[index] == fd) {
+ return 1;
+ }
+ return 0;
+}
+
+
+#if !defined(NEED_SELECT)
+#include "obsolete/probslet.h"
+
+#define PD_INCR 20
+
+static PRPollDesc *_pr_setfd(
+ PR_fd_set *set, PRInt16 flags, PRPollDesc *polldesc)
+{
+ PRUintn fsidx, pdidx;
+ PRPollDesc *poll = polldesc;
+
+ if (NULL == set) {
+ return poll;
+ }
+
+ /* First set the pr file handle osfds */
+ for (fsidx = 0; fsidx < set->hsize; fsidx++)
+ {
+ for (pdidx = 0; 1; pdidx++)
+ {
+ if ((PRFileDesc*)-1 == poll[pdidx].fd)
+ {
+ /* our vector is full - extend and condition it */
+ poll = (PRPollDesc*)PR_Realloc(
+ poll, (pdidx + 1 + PD_INCR) * sizeof(PRPollDesc));
+ if (NULL == poll) {
+ goto out_of_memory;
+ }
+ memset(
+ poll + pdidx * sizeof(PRPollDesc),
+ 0, PD_INCR * sizeof(PRPollDesc));
+ poll[pdidx + PD_INCR].fd = (PRFileDesc*)-1;
+ }
+ if ((NULL == poll[pdidx].fd)
+ || (poll[pdidx].fd == set->harray[fsidx]))
+ {
+ /* PR_ASSERT(0 == (poll[pdidx].in_flags & flags)); */
+ /* either empty or prevously defined */
+ poll[pdidx].fd = set->harray[fsidx]; /* possibly redundant */
+ poll[pdidx].in_flags |= flags; /* possibly redundant */
+ break;
+ }
+ }
+ }
+
+#if 0
+ /* Second set the native osfds */
+ for (fsidx = 0; fsidx < set->nsize; fsidx++)
+ {
+ for (pdidx = 0; ((PRFileDesc*)-1 != poll[pdidx].fd); pdidx++)
+ {
+ if ((PRFileDesc*)-1 == poll[pdidx].fd)
+ {
+ /* our vector is full - extend and condition it */
+ poll = PR_Realloc(
+ poll, (pdidx + PD_INCR) * sizeof(PRPollDesc));
+ if (NULL == poll) {
+ goto out_of_memory;
+ }
+ memset(
+ poll + pdidx * sizeof(PRPollDesc),
+ 0, PD_INCR * sizeof(PRPollDesc));
+ poll[(pdidx + PD_INCR)].fd = (PRFileDesc*)-1;
+ }
+ if ((NULL == poll[pdidx].fd)
+ || (poll[pdidx].fd == set->narray[fsidx]))
+ {
+ /* either empty or prevously defined */
+ poll[pdidx].fd = set->narray[fsidx];
+ PR_ASSERT(0 == (poll[pdidx].in_flags & flags));
+ poll[pdidx].in_flags |= flags;
+ break;
+ }
+ }
+ }
+#endif /* 0 */
+
+ return poll;
+
+out_of_memory:
+ if (NULL != polldesc) {
+ PR_DELETE(polldesc);
+ }
+ return NULL;
+} /* _pr_setfd */
+
+#endif /* !defined(NEED_SELECT) */
+
+PR_IMPLEMENT(PRInt32) PR_Select(
+ PRInt32 unused, PR_fd_set *pr_rd, PR_fd_set *pr_wr,
+ PR_fd_set *pr_ex, PRIntervalTime timeout)
+{
+
+#if !defined(NEED_SELECT)
+ PRInt32 npds = 0;
+ /*
+ ** Find out how many fds are represented in the three lists.
+ ** Then allocate a polling descriptor for the logical union
+ ** (there can't be any overlapping) and call PR_Poll().
+ */
+
+ PRPollDesc *copy, *poll;
+
+ static PRBool warning = PR_TRUE;
+ if (warning) {
+ warning = _PR_Obsolete( "PR_Select()", "PR_Poll()");
+ }
+
+ /* try to get an initial guesss at how much space we need */
+ npds = 0;
+ if ((NULL != pr_rd) && ((pr_rd->hsize + pr_rd->nsize - npds) > 0)) {
+ npds = pr_rd->hsize + pr_rd->nsize;
+ }
+ if ((NULL != pr_wr) && ((pr_wr->hsize + pr_wr->nsize - npds) > 0)) {
+ npds = pr_wr->hsize + pr_wr->nsize;
+ }
+ if ((NULL != pr_ex) && ((pr_ex->hsize + pr_ex->nsize - npds) > 0)) {
+ npds = pr_ex->hsize + pr_ex->nsize;
+ }
+
+ if (0 == npds)
+ {
+ PR_Sleep(timeout);
+ return 0;
+ }
+
+ copy = poll = (PRPollDesc*)PR_Calloc(npds + PD_INCR, sizeof(PRPollDesc));
+ if (NULL == poll) {
+ goto out_of_memory;
+ }
+ poll[npds + PD_INCR - 1].fd = (PRFileDesc*)-1;
+
+ poll = _pr_setfd(pr_rd, PR_POLL_READ, poll);
+ if (NULL == poll) {
+ goto out_of_memory;
+ }
+ poll = _pr_setfd(pr_wr, PR_POLL_WRITE, poll);
+ if (NULL == poll) {
+ goto out_of_memory;
+ }
+ poll = _pr_setfd(pr_ex, PR_POLL_EXCEPT, poll);
+ if (NULL == poll) {
+ goto out_of_memory;
+ }
+ unused = 0;
+ while (NULL != poll[unused].fd && (PRFileDesc*)-1 != poll[unused].fd)
+ {
+ ++unused;
+ }
+
+ PR_ASSERT(unused > 0);
+ npds = PR_Poll(poll, unused, timeout);
+
+ if (npds > 0)
+ {
+ /* Copy the results back into the fd sets */
+ if (NULL != pr_rd) {
+ pr_rd->nsize = pr_rd->hsize = 0;
+ }
+ if (NULL != pr_wr) {
+ pr_wr->nsize = pr_wr->hsize = 0;
+ }
+ if (NULL != pr_ex) {
+ pr_ex->nsize = pr_ex->hsize = 0;
+ }
+ for (copy = &poll[unused - 1]; copy >= poll; --copy)
+ {
+ if (copy->out_flags & PR_POLL_NVAL)
+ {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ npds = -1;
+ break;
+ }
+ if (copy->out_flags & PR_POLL_READ)
+ if (NULL != pr_rd) {
+ pr_rd->harray[pr_rd->hsize++] = copy->fd;
+ }
+ if (copy->out_flags & PR_POLL_WRITE)
+ if (NULL != pr_wr) {
+ pr_wr->harray[pr_wr->hsize++] = copy->fd;
+ }
+ if (copy->out_flags & PR_POLL_EXCEPT)
+ if (NULL != pr_ex) {
+ pr_ex->harray[pr_ex->hsize++] = copy->fd;
+ }
+ }
+ }
+ PR_DELETE(poll);
+
+ return npds;
+out_of_memory:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+
+#endif /* !defined(NEED_SELECT) */
+
+}
diff --git a/nsprpub/pr/src/io/prstdio.c b/nsprpub/pr/src/io/prstdio.c
new file mode 100644
index 0000000000..74b85d9cf1
--- /dev/null
+++ b/nsprpub/pr/src/io/prstdio.c
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <string.h>
+
+/*
+** fprintf to a PRFileDesc
+*/
+PR_IMPLEMENT(PRUint32) PR_fprintf(PRFileDesc* fd, const char *fmt, ...)
+{
+ va_list ap;
+ PRUint32 rv;
+
+ va_start(ap, fmt);
+ rv = PR_vfprintf(fd, fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+PR_IMPLEMENT(PRUint32) PR_vfprintf(PRFileDesc* fd, const char *fmt, va_list ap)
+{
+ /* XXX this could be better */
+ PRUint32 rv, len;
+ char* msg = PR_vsmprintf(fmt, ap);
+ if (NULL == msg) {
+ return -1;
+ }
+ len = strlen(msg);
+#ifdef XP_OS2
+ /*
+ * OS/2 really needs a \r for every \n.
+ * In the future we should try to use scatter-gather instead of a
+ * succession of PR_Write.
+ */
+ if (isatty(PR_FileDesc2NativeHandle(fd))) {
+ PRUint32 last = 0, idx;
+ PRInt32 tmp;
+ rv = 0;
+ for (idx = 0; idx < len+1; idx++) {
+ if ((idx - last > 0) && (('\n' == msg[idx]) || (idx == len))) {
+ tmp = PR_Write(fd, msg + last, idx - last);
+ if (tmp >= 0) {
+ rv += tmp;
+ }
+ last = idx;
+ }
+ /*
+ * if current character is \n, and
+ * previous character isn't \r, and
+ * next character isn't \r
+ */
+ if (('\n' == msg[idx]) &&
+ ((0 == idx) || ('\r' != msg[idx-1])) &&
+ ('\r' != msg[idx+1])) {
+ /* add extra \r */
+ tmp = PR_Write(fd, "\r", 1);
+ if (tmp >= 0) {
+ rv += tmp;
+ }
+ }
+ }
+ } else {
+ rv = PR_Write(fd, msg, len);
+ }
+#else
+ rv = PR_Write(fd, msg, len);
+#endif
+ PR_DELETE(msg);
+ return rv;
+}
diff --git a/nsprpub/pr/src/linking/Makefile.in b/nsprpub/pr/src/linking/Makefile.in
new file mode 100644
index 0000000000..9292e9e91f
--- /dev/null
+++ b/nsprpub/pr/src/linking/Makefile.in
@@ -0,0 +1,31 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+CSRCS = \
+ prlink.c \
+ $(NULL)
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
diff --git a/nsprpub/pr/src/linking/prlink.c b/nsprpub/pr/src/linking/prlink.c
new file mode 100644
index 0000000000..011ff17e6b
--- /dev/null
+++ b/nsprpub/pr/src/linking/prlink.c
@@ -0,0 +1,1142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <string.h>
+
+#ifdef XP_UNIX
+#ifdef USE_DLFCN
+#include <dlfcn.h>
+/* Define these on systems that don't have them. */
+#ifndef RTLD_NOW
+#define RTLD_NOW 0
+#endif
+#ifndef RTLD_LAZY
+#define RTLD_LAZY RTLD_NOW
+#endif
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 0
+#endif
+#ifndef RTLD_LOCAL
+#define RTLD_LOCAL 0
+#endif
+#ifdef AIX
+#include <sys/ldr.h>
+#ifndef L_IGNOREUNLOAD /* AIX 4.3.3 does not have L_IGNOREUNLOAD. */
+#define L_IGNOREUNLOAD 0x10000000
+#endif
+#endif
+#elif defined(USE_HPSHL)
+#include <dl.h>
+#endif
+#endif /* XP_UNIX */
+
+#define _PR_DEFAULT_LD_FLAGS PR_LD_LAZY
+
+/*
+ * On these platforms, symbols have a leading '_'.
+ */
+#if defined(XP_OS2) \
+ || ((defined(OPENBSD) || defined(NETBSD)) && !defined(__ELF__))
+#define NEED_LEADING_UNDERSCORE
+#endif
+
+#define PR_LD_PATHW 0x8000 /* for PR_LibSpec_PathnameU */
+
+/************************************************************************/
+
+struct PRLibrary {
+ char* name; /* Our own copy of the name string */
+ PRLibrary* next;
+ int refCount;
+ const PRStaticLinkTable* staticTable;
+
+#ifdef XP_PC
+#ifdef XP_OS2
+ HMODULE dlh;
+#else
+ HINSTANCE dlh;
+#endif
+#endif
+
+#ifdef XP_UNIX
+#if defined(USE_HPSHL)
+ shl_t dlh;
+#else
+ void* dlh;
+#endif
+#endif
+
+};
+
+static PRLibrary *pr_loadmap;
+static PRLibrary *pr_exe_loadmap;
+static PRMonitor *pr_linker_lock;
+static char* _pr_currentLibPath = NULL;
+
+static PRLibrary *pr_LoadLibraryByPathname(const char *name, PRIntn flags);
+
+/************************************************************************/
+
+#if !defined(USE_DLFCN) && !defined(HAVE_STRERROR)
+#define ERR_STR_BUF_LENGTH 20
+#endif
+
+static void DLLErrorInternal(PRIntn oserr)
+/*
+** This whole function, and most of the code in this file, are run
+** with a big hairy lock wrapped around it. Not the best of situations,
+** but will eventually come up with the right answer.
+*/
+{
+ const char *error = NULL;
+#ifdef USE_DLFCN
+ error = dlerror(); /* $$$ That'll be wrong some of the time - AOF */
+#elif defined(HAVE_STRERROR)
+ error = strerror(oserr); /* this should be okay */
+#else
+ char errStrBuf[ERR_STR_BUF_LENGTH];
+ PR_snprintf(errStrBuf, sizeof(errStrBuf), "error %d", oserr);
+ error = errStrBuf;
+#endif
+ if (NULL != error) {
+ PR_SetErrorText(strlen(error), error);
+ }
+} /* DLLErrorInternal */
+
+void _PR_InitLinker(void)
+{
+ PRLibrary *lm = NULL;
+#if defined(XP_UNIX)
+ void *h;
+#endif
+
+ if (!pr_linker_lock) {
+ pr_linker_lock = PR_NewNamedMonitor("linker-lock");
+ }
+ PR_EnterMonitor(pr_linker_lock);
+
+#if defined(XP_PC)
+ lm = PR_NEWZAP(PRLibrary);
+ lm->name = strdup("Executable");
+#if defined(XP_OS2)
+ lm->dlh = NULLHANDLE;
+#else
+ /* A module handle for the executable. */
+ lm->dlh = GetModuleHandle(NULL);
+#endif /* ! XP_OS2 */
+
+ lm->refCount = 1;
+ lm->staticTable = NULL;
+ pr_exe_loadmap = lm;
+ pr_loadmap = lm;
+
+#elif defined(XP_UNIX)
+#ifdef HAVE_DLL
+#if defined(USE_DLFCN) && !defined(NO_DLOPEN_NULL)
+ h = dlopen(0, RTLD_LAZY);
+ if (!h) {
+ char *error;
+
+ DLLErrorInternal(_MD_ERRNO());
+ error = (char*)PR_MALLOC(PR_GetErrorTextLength());
+ (void) PR_GetErrorText(error);
+ fprintf(stderr, "failed to initialize shared libraries [%s]\n",
+ error);
+ PR_DELETE(error);
+ abort();/* XXX */
+ }
+#elif defined(USE_HPSHL)
+ h = NULL;
+ /* don't abort with this NULL */
+#elif defined(NO_DLOPEN_NULL)
+ h = NULL; /* XXXX toshok */ /* XXXX vlad */
+#else
+#error no dll strategy
+#endif /* USE_DLFCN */
+
+ lm = PR_NEWZAP(PRLibrary);
+ if (lm) {
+ lm->name = strdup("a.out");
+ lm->refCount = 1;
+ lm->dlh = h;
+ lm->staticTable = NULL;
+ }
+ pr_exe_loadmap = lm;
+ pr_loadmap = lm;
+#endif /* HAVE_DLL */
+#endif /* XP_UNIX */
+
+ if (lm) {
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN,
+ ("Loaded library %s (init)", lm->name));
+ }
+
+ PR_ExitMonitor(pr_linker_lock);
+}
+
+/*
+ * _PR_ShutdownLinker does not unload the dlls loaded by the application
+ * via calls to PR_LoadLibrary. Any dlls that still remain on the
+ * pr_loadmap list when NSPR shuts down are application programming errors.
+ * The only exception is pr_exe_loadmap, which was added to the list by
+ * NSPR and hence should be cleaned up by NSPR.
+ */
+void _PR_ShutdownLinker(void)
+{
+ /* FIXME: pr_exe_loadmap should be destroyed. */
+
+ PR_DestroyMonitor(pr_linker_lock);
+ pr_linker_lock = NULL;
+
+ if (_pr_currentLibPath) {
+ free(_pr_currentLibPath);
+ _pr_currentLibPath = NULL;
+ }
+}
+
+/******************************************************************************/
+
+PR_IMPLEMENT(PRStatus) PR_SetLibraryPath(const char *path)
+{
+ PRStatus rv = PR_SUCCESS;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_EnterMonitor(pr_linker_lock);
+ if (_pr_currentLibPath) {
+ free(_pr_currentLibPath);
+ }
+ if (path) {
+ _pr_currentLibPath = strdup(path);
+ if (!_pr_currentLibPath) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ } else {
+ _pr_currentLibPath = 0;
+ }
+ PR_ExitMonitor(pr_linker_lock);
+ return rv;
+}
+
+/*
+** Return the library path for finding shared libraries.
+*/
+PR_IMPLEMENT(char *)
+PR_GetLibraryPath(void)
+{
+ char *ev;
+ char *copy = NULL; /* a copy of _pr_currentLibPath */
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_EnterMonitor(pr_linker_lock);
+ if (_pr_currentLibPath != NULL) {
+ goto exit;
+ }
+
+ /* initialize pr_currentLibPath */
+
+#ifdef XP_PC
+ ev = getenv("LD_LIBRARY_PATH");
+ if (!ev) {
+ ev = ".;\\lib";
+ }
+ ev = strdup(ev);
+#endif
+
+#if defined(XP_UNIX)
+#if defined(USE_DLFCN)
+ {
+ char *p=NULL;
+ int len;
+
+ ev = getenv("LD_LIBRARY_PATH");
+ if (!ev) {
+ ev = "/usr/lib:/lib";
+ }
+ len = strlen(ev) + 1; /* +1 for the null */
+
+ p = (char*) malloc(len);
+ if (p) {
+ strcpy(p, ev);
+ } /* if (p) */
+ ev = p;
+ PR_LOG(_pr_io_lm, PR_LOG_NOTICE, ("linker path '%s'", ev));
+
+ }
+#else
+ /* AFAIK there isn't a library path with the HP SHL interface --Rob */
+ ev = strdup("");
+#endif
+#endif
+
+ /*
+ * If ev is NULL, we have run out of memory
+ */
+ _pr_currentLibPath = ev;
+
+exit:
+ if (_pr_currentLibPath) {
+ copy = strdup(_pr_currentLibPath);
+ }
+ PR_ExitMonitor(pr_linker_lock);
+ if (!copy) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ return copy;
+}
+
+/*
+** Build library name from path, lib and extensions
+*/
+PR_IMPLEMENT(char*)
+PR_GetLibraryName(const char *path, const char *lib)
+{
+ char *fullname;
+
+#ifdef XP_PC
+ if (strstr(lib, PR_DLL_SUFFIX) == NULL)
+ {
+ if (path) {
+ fullname = PR_smprintf("%s\\%s%s", path, lib, PR_DLL_SUFFIX);
+ } else {
+ fullname = PR_smprintf("%s%s", lib, PR_DLL_SUFFIX);
+ }
+ } else {
+ if (path) {
+ fullname = PR_smprintf("%s\\%s", path, lib);
+ } else {
+ fullname = PR_smprintf("%s", lib);
+ }
+ }
+#endif /* XP_PC */
+#if defined(XP_UNIX)
+ if (strstr(lib, PR_DLL_SUFFIX) == NULL)
+ {
+ if (path) {
+ fullname = PR_smprintf("%s/lib%s%s", path, lib, PR_DLL_SUFFIX);
+ } else {
+ fullname = PR_smprintf("lib%s%s", lib, PR_DLL_SUFFIX);
+ }
+ } else {
+ if (path) {
+ fullname = PR_smprintf("%s/%s", path, lib);
+ } else {
+ fullname = PR_smprintf("%s", lib);
+ }
+ }
+#endif /* XP_UNIX */
+ return fullname;
+}
+
+/*
+** Free the memory allocated, for the caller, by PR_GetLibraryName
+*/
+PR_IMPLEMENT(void)
+PR_FreeLibraryName(char *mem)
+{
+ PR_smprintf_free(mem);
+}
+
+static PRLibrary*
+pr_UnlockedFindLibrary(const char *name)
+{
+ PRLibrary* lm = pr_loadmap;
+ const char* np = strrchr(name, PR_DIRECTORY_SEPARATOR);
+ np = np ? np + 1 : name;
+ while (lm) {
+ const char* cp = strrchr(lm->name, PR_DIRECTORY_SEPARATOR);
+ cp = cp ? cp + 1 : lm->name;
+#ifdef WIN32
+ /* Windows DLL names are case insensitive... */
+ if (strcmpi(np, cp) == 0)
+#elif defined(XP_OS2)
+ if (stricmp(np, cp) == 0)
+#else
+ if (strcmp(np, cp) == 0)
+#endif
+ {
+ /* found */
+ lm->refCount++;
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN,
+ ("%s incr => %d (find lib)",
+ lm->name, lm->refCount));
+ return lm;
+ }
+ lm = lm->next;
+ }
+ return NULL;
+}
+
+PR_IMPLEMENT(PRLibrary*)
+PR_LoadLibraryWithFlags(PRLibSpec libSpec, PRIntn flags)
+{
+ if (flags == 0) {
+ flags = _PR_DEFAULT_LD_FLAGS;
+ }
+ switch (libSpec.type) {
+ case PR_LibSpec_Pathname:
+ return pr_LoadLibraryByPathname(libSpec.value.pathname, flags);
+#ifdef WIN32
+ case PR_LibSpec_PathnameU:
+ /*
+ * cast to |char *| and set PR_LD_PATHW flag so that
+ * it can be cast back to PRUnichar* in the callee.
+ */
+ return pr_LoadLibraryByPathname((const char*)
+ libSpec.value.pathname_u,
+ flags | PR_LD_PATHW);
+#endif
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+}
+
+PR_IMPLEMENT(PRLibrary*)
+PR_LoadLibrary(const char *name)
+{
+ PRLibSpec libSpec;
+
+ libSpec.type = PR_LibSpec_Pathname;
+ libSpec.value.pathname = name;
+ return PR_LoadLibraryWithFlags(libSpec, 0);
+}
+
+/*
+** Dynamically load a library. Only load libraries once, so scan the load
+** map first.
+*/
+static PRLibrary*
+pr_LoadLibraryByPathname(const char *name, PRIntn flags)
+{
+ PRLibrary *lm;
+ PRLibrary* result = NULL;
+ PRInt32 oserr;
+#ifdef WIN32
+ char utf8name_stack[MAX_PATH];
+ char *utf8name_malloc = NULL;
+ char *utf8name = utf8name_stack;
+ PRUnichar wname_stack[MAX_PATH];
+ PRUnichar *wname_malloc = NULL;
+ PRUnichar *wname = wname_stack;
+ int len;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ /* See if library is already loaded */
+ PR_EnterMonitor(pr_linker_lock);
+
+#ifdef WIN32
+ if (flags & PR_LD_PATHW) {
+ /* cast back what's cast to |char *| for the argument passing. */
+ wname = (LPWSTR) name;
+ } else {
+ int wlen = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0);
+ if (wlen > MAX_PATH) {
+ wname = wname_malloc = PR_Malloc(wlen * sizeof(PRUnichar));
+ }
+ if (wname == NULL ||
+ !MultiByteToWideChar(CP_ACP, 0, name, -1, wname, wlen)) {
+ oserr = _MD_ERRNO();
+ goto unlock;
+ }
+ }
+ len = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL);
+ if (len > MAX_PATH) {
+ utf8name = utf8name_malloc = PR_Malloc(len);
+ }
+ if (utf8name == NULL ||
+ !WideCharToMultiByte(CP_UTF8, 0, wname, -1,
+ utf8name, len, NULL, NULL)) {
+ oserr = _MD_ERRNO();
+ goto unlock;
+ }
+ /* the list of loaded library names are always kept in UTF-8
+ * on Win32 platforms */
+ result = pr_UnlockedFindLibrary(utf8name);
+#else
+ result = pr_UnlockedFindLibrary(name);
+#endif
+
+ if (result != NULL) {
+ goto unlock;
+ }
+
+ lm = PR_NEWZAP(PRLibrary);
+ if (lm == NULL) {
+ oserr = _MD_ERRNO();
+ goto unlock;
+ }
+ lm->staticTable = NULL;
+
+#ifdef XP_OS2 /* Why isn't all this stuff in MD code?! */
+ {
+ HMODULE h;
+ UCHAR pszError[_MAX_PATH];
+ ULONG ulRc = NO_ERROR;
+
+ ulRc = DosLoadModule(pszError, _MAX_PATH, (PSZ) name, &h);
+ if (ulRc != NO_ERROR) {
+ oserr = ulRc;
+ PR_DELETE(lm);
+ goto unlock;
+ }
+ lm->name = strdup(name);
+ lm->dlh = h;
+ lm->next = pr_loadmap;
+ pr_loadmap = lm;
+ }
+#endif /* XP_OS2 */
+
+#ifdef WIN32
+ {
+ HINSTANCE h;
+
+ h = LoadLibraryExW(wname, NULL,
+ (flags & PR_LD_ALT_SEARCH_PATH) ?
+ LOAD_WITH_ALTERED_SEARCH_PATH : 0);
+ if (h == NULL) {
+ oserr = _MD_ERRNO();
+ PR_DELETE(lm);
+ goto unlock;
+ }
+ lm->name = strdup(utf8name);
+ lm->dlh = h;
+ lm->next = pr_loadmap;
+ pr_loadmap = lm;
+ }
+#endif /* WIN32 */
+
+#if defined(XP_UNIX)
+#ifdef HAVE_DLL
+ {
+#if defined(USE_DLFCN)
+#ifdef NTO
+ /* Neutrino needs RTLD_GROUP to load Netscape plugins. (bug 71179) */
+ int dl_flags = RTLD_GROUP;
+#elif defined(AIX)
+ /* AIX needs RTLD_MEMBER to load an archive member. (bug 228899) */
+ int dl_flags = RTLD_MEMBER;
+#else
+ int dl_flags = 0;
+#endif
+ void *h = NULL;
+#if defined(DARWIN)
+ PRBool okToLoad = PR_FALSE;
+#endif
+
+ if (flags & PR_LD_LAZY) {
+ dl_flags |= RTLD_LAZY;
+ }
+ if (flags & PR_LD_NOW) {
+ dl_flags |= RTLD_NOW;
+ }
+ if (flags & PR_LD_GLOBAL) {
+ dl_flags |= RTLD_GLOBAL;
+ }
+ if (flags & PR_LD_LOCAL) {
+ dl_flags |= RTLD_LOCAL;
+ }
+#if defined(DARWIN)
+ /* If the file contains an absolute or relative path (slash)
+ * and the path doesn't look like a System path, then require
+ * the file exists.
+ * The reason is that DARWIN's dlopen ignores the provided path
+ * and checks for the plain filename in DYLD_LIBRARY_PATH,
+ * which could load an unexpected version of a library. */
+ if (strchr(name, PR_DIRECTORY_SEPARATOR) == NULL) {
+ /* no slash, allow to load from any location */
+ okToLoad = PR_TRUE;
+ } else {
+ const char systemPrefix1[] = "/System/";
+ const size_t systemPrefixLen1 = strlen(systemPrefix1);
+ const char systemPrefix2[] = "/usr/lib/";
+ const size_t systemPrefixLen2 = strlen(systemPrefix2);
+ const size_t name_len = strlen(name);
+ if (((name_len > systemPrefixLen1) &&
+ (strncmp(name, systemPrefix1, systemPrefixLen1) == 0)) ||
+ ((name_len > systemPrefixLen2) &&
+ (strncmp(name, systemPrefix2, systemPrefixLen2) == 0))) {
+ /* found at beginning, it's a system library.
+ * Skip filesystem check (required for macOS 11),
+ * allow loading from any location */
+ okToLoad = PR_TRUE;
+ } else if (PR_Access(name, PR_ACCESS_EXISTS) == PR_SUCCESS) {
+ /* file exists, allow to load */
+ okToLoad = PR_TRUE;
+ }
+ }
+ if (okToLoad) {
+ h = dlopen(name, dl_flags);
+ }
+#else
+ h = dlopen(name, dl_flags);
+#endif
+#elif defined(USE_HPSHL)
+ int shl_flags = 0;
+ shl_t h;
+
+ /*
+ * Use the DYNAMIC_PATH flag only if 'name' is a plain file
+ * name (containing no directory) to match the behavior of
+ * dlopen().
+ */
+ if (strchr(name, PR_DIRECTORY_SEPARATOR) == NULL) {
+ shl_flags |= DYNAMIC_PATH;
+ }
+ if (flags & PR_LD_LAZY) {
+ shl_flags |= BIND_DEFERRED;
+ }
+ if (flags & PR_LD_NOW) {
+ shl_flags |= BIND_IMMEDIATE;
+ }
+ /* No equivalent of PR_LD_GLOBAL and PR_LD_LOCAL. */
+ h = shl_load(name, shl_flags, 0L);
+#else
+#error Configuration error
+#endif
+ if (!h) {
+ oserr = _MD_ERRNO();
+ PR_DELETE(lm);
+ goto unlock;
+ }
+ lm->name = strdup(name);
+ lm->dlh = h;
+ lm->next = pr_loadmap;
+ pr_loadmap = lm;
+ }
+#endif /* HAVE_DLL */
+#endif /* XP_UNIX */
+
+ lm->refCount = 1;
+
+ result = lm; /* success */
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (load lib)", lm->name));
+
+unlock:
+ if (result == NULL) {
+ PR_SetError(PR_LOAD_LIBRARY_ERROR, oserr);
+ DLLErrorInternal(oserr); /* sets error text */
+ }
+#ifdef WIN32
+ if (utf8name_malloc) {
+ PR_Free(utf8name_malloc);
+ }
+ if (wname_malloc) {
+ PR_Free(wname_malloc);
+ }
+#endif
+ PR_ExitMonitor(pr_linker_lock);
+ return result;
+}
+
+/*
+** Unload a shared library which was loaded via PR_LoadLibrary
+*/
+PR_IMPLEMENT(PRStatus)
+PR_UnloadLibrary(PRLibrary *lib)
+{
+ int result = 0;
+ PRStatus status = PR_SUCCESS;
+
+ if (lib == 0) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ PR_EnterMonitor(pr_linker_lock);
+
+ if (lib->refCount <= 0) {
+ PR_ExitMonitor(pr_linker_lock);
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (--lib->refCount > 0) {
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN,
+ ("%s decr => %d",
+ lib->name, lib->refCount));
+ goto done;
+ }
+
+#ifdef XP_UNIX
+#ifdef HAVE_DLL
+#ifdef USE_DLFCN
+ result = dlclose(lib->dlh);
+#elif defined(USE_HPSHL)
+ result = shl_unload(lib->dlh);
+#else
+#error Configuration error
+#endif
+#endif /* HAVE_DLL */
+#endif /* XP_UNIX */
+#ifdef XP_PC
+ if (lib->dlh) {
+ FreeLibrary((HINSTANCE)(lib->dlh));
+ lib->dlh = (HINSTANCE)NULL;
+ }
+#endif /* XP_PC */
+
+ /* unlink from library search list */
+ if (pr_loadmap == lib) {
+ pr_loadmap = pr_loadmap->next;
+ }
+ else if (pr_loadmap != NULL) {
+ PRLibrary* prev = pr_loadmap;
+ PRLibrary* next = pr_loadmap->next;
+ while (next != NULL) {
+ if (next == lib) {
+ prev->next = next->next;
+ goto freeLib;
+ }
+ prev = next;
+ next = next->next;
+ }
+ /*
+ * fail (the library is not on the _pr_loadmap list),
+ * but don't wipe out an error from dlclose/shl_unload.
+ */
+ PR_NOT_REACHED("_pr_loadmap and lib->refCount inconsistent");
+ if (result == 0) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ status = PR_FAILURE;
+ }
+ }
+ /*
+ * We free the PRLibrary structure whether dlclose/shl_unload
+ * succeeds or not.
+ */
+
+freeLib:
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Unloaded library %s", lib->name));
+ free(lib->name);
+ lib->name = NULL;
+ PR_DELETE(lib);
+ if (result != 0) {
+ PR_SetError(PR_UNLOAD_LIBRARY_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ status = PR_FAILURE;
+ }
+
+done:
+ PR_ExitMonitor(pr_linker_lock);
+ return status;
+}
+
+static void*
+pr_FindSymbolInLib(PRLibrary *lm, const char *name)
+{
+ void *f = NULL;
+#ifdef XP_OS2
+ int rc;
+#endif
+
+ if (lm->staticTable != NULL) {
+ const PRStaticLinkTable* tp;
+ for (tp = lm->staticTable; tp->name; tp++) {
+ if (strcmp(name, tp->name) == 0) {
+ return (void*) tp->fp;
+ }
+ }
+ /*
+ ** If the symbol was not found in the static table then check if
+ ** the symbol was exported in the DLL... Win16 only!!
+ */
+#if !defined(WIN16)
+ PR_SetError(PR_FIND_SYMBOL_ERROR, 0);
+ return (void*)NULL;
+#endif
+ }
+
+#ifdef XP_OS2
+ rc = DosQueryProcAddr(lm->dlh, 0, (PSZ) name, (PFN *) &f);
+#if defined(NEED_LEADING_UNDERSCORE)
+ /*
+ * Older plugins (not built using GCC) will have symbols that are not
+ * underscore prefixed. We check for that here.
+ */
+ if (rc != NO_ERROR) {
+ name++;
+ DosQueryProcAddr(lm->dlh, 0, (PSZ) name, (PFN *) &f);
+ }
+#endif
+#endif /* XP_OS2 */
+
+#ifdef WIN32
+ f = GetProcAddress(lm->dlh, name);
+#endif /* WIN32 */
+
+#ifdef XP_UNIX
+#ifdef HAVE_DLL
+#ifdef USE_DLFCN
+ f = dlsym(lm->dlh, name);
+#elif defined(USE_HPSHL)
+ if (shl_findsym(&lm->dlh, name, TYPE_PROCEDURE, &f) == -1) {
+ f = NULL;
+ }
+#endif
+#endif /* HAVE_DLL */
+#endif /* XP_UNIX */
+ if (f == NULL) {
+ PR_SetError(PR_FIND_SYMBOL_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ }
+ return f;
+}
+
+/*
+** Called by class loader to resolve missing native's
+*/
+PR_IMPLEMENT(void*)
+PR_FindSymbol(PRLibrary *lib, const char *raw_name)
+{
+ void *f = NULL;
+#if defined(NEED_LEADING_UNDERSCORE)
+ char *name;
+#else
+ const char *name;
+#endif
+ /*
+ ** Mangle the raw symbol name in any way that is platform specific.
+ */
+#if defined(NEED_LEADING_UNDERSCORE)
+ /* Need a leading _ */
+ name = PR_smprintf("_%s", raw_name);
+#elif defined(AIX)
+ /*
+ ** AIX with the normal linker put's a "." in front of the symbol
+ ** name. When use "svcc" and "svld" then the "." disappears. Go
+ ** figure.
+ */
+ name = raw_name;
+#else
+ name = raw_name;
+#endif
+
+ PR_EnterMonitor(pr_linker_lock);
+ PR_ASSERT(lib != NULL);
+ f = pr_FindSymbolInLib(lib, name);
+
+#if defined(NEED_LEADING_UNDERSCORE)
+ PR_smprintf_free(name);
+#endif
+
+ PR_ExitMonitor(pr_linker_lock);
+ return f;
+}
+
+/*
+** Return the address of the function 'raw_name' in the library 'lib'
+*/
+PR_IMPLEMENT(PRFuncPtr)
+PR_FindFunctionSymbol(PRLibrary *lib, const char *raw_name)
+{
+ return ((PRFuncPtr) PR_FindSymbol(lib, raw_name));
+}
+
+PR_IMPLEMENT(void*)
+PR_FindSymbolAndLibrary(const char *raw_name, PRLibrary* *lib)
+{
+ void *f = NULL;
+#if defined(NEED_LEADING_UNDERSCORE)
+ char *name;
+#else
+ const char *name;
+#endif
+ PRLibrary* lm;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ /*
+ ** Mangle the raw symbol name in any way that is platform specific.
+ */
+#if defined(NEED_LEADING_UNDERSCORE)
+ /* Need a leading _ */
+ name = PR_smprintf("_%s", raw_name);
+#elif defined(AIX)
+ /*
+ ** AIX with the normal linker put's a "." in front of the symbol
+ ** name. When use "svcc" and "svld" then the "." disappears. Go
+ ** figure.
+ */
+ name = raw_name;
+#else
+ name = raw_name;
+#endif
+
+ PR_EnterMonitor(pr_linker_lock);
+
+ /* search all libraries */
+ for (lm = pr_loadmap; lm != NULL; lm = lm->next) {
+ f = pr_FindSymbolInLib(lm, name);
+ if (f != NULL) {
+ *lib = lm;
+ lm->refCount++;
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN,
+ ("%s incr => %d (for %s)",
+ lm->name, lm->refCount, name));
+ break;
+ }
+ }
+#if defined(NEED_LEADING_UNDERSCORE)
+ PR_smprintf_free(name);
+#endif
+
+ PR_ExitMonitor(pr_linker_lock);
+ return f;
+}
+
+PR_IMPLEMENT(PRFuncPtr)
+PR_FindFunctionSymbolAndLibrary(const char *raw_name, PRLibrary* *lib)
+{
+ return ((PRFuncPtr) PR_FindSymbolAndLibrary(raw_name, lib));
+}
+
+/*
+** Add a static library to the list of loaded libraries. If LoadLibrary
+** is called with the name then we will pretend it was already loaded
+*/
+PR_IMPLEMENT(PRLibrary*)
+PR_LoadStaticLibrary(const char *name, const PRStaticLinkTable *slt)
+{
+ PRLibrary *lm=NULL;
+ PRLibrary* result = NULL;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ /* See if library is already loaded */
+ PR_EnterMonitor(pr_linker_lock);
+
+ /* If the lbrary is already loaded, then add the static table information... */
+ result = pr_UnlockedFindLibrary(name);
+ if (result != NULL) {
+ PR_ASSERT( (result->staticTable == NULL) || (result->staticTable == slt) );
+ result->staticTable = slt;
+ goto unlock;
+ }
+
+ /* Add library to list...Mark it static */
+ lm = PR_NEWZAP(PRLibrary);
+ if (lm == NULL) {
+ goto unlock;
+ }
+
+ lm->name = strdup(name);
+ lm->refCount = 1;
+ lm->dlh = pr_exe_loadmap ? pr_exe_loadmap->dlh : 0;
+ lm->staticTable = slt;
+ lm->next = pr_loadmap;
+ pr_loadmap = lm;
+
+ result = lm; /* success */
+ PR_ASSERT(lm->refCount == 1);
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (static lib)", lm->name));
+unlock:
+ PR_ExitMonitor(pr_linker_lock);
+ return result;
+}
+
+PR_IMPLEMENT(char *)
+PR_GetLibraryFilePathname(const char *name, PRFuncPtr addr)
+{
+#if defined(USE_DLFCN) && defined(HAVE_DLADDR)
+ Dl_info dli;
+ char *result;
+
+ if (dladdr((void *)addr, &dli) == 0) {
+ PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ return NULL;
+ }
+ result = PR_Malloc(strlen(dli.dli_fname)+1);
+ if (result != NULL) {
+ strcpy(result, dli.dli_fname);
+ }
+ return result;
+#elif defined(AIX)
+ char *result;
+#define LD_INFO_INCREMENT 64
+ struct ld_info *info;
+ unsigned int info_length = LD_INFO_INCREMENT * sizeof(struct ld_info);
+ struct ld_info *infop;
+ int loadflags = L_GETINFO | L_IGNOREUNLOAD;
+
+ for (;;) {
+ info = PR_Malloc(info_length);
+ if (info == NULL) {
+ return NULL;
+ }
+ /* If buffer is too small, loadquery fails with ENOMEM. */
+ if (loadquery(loadflags, info, info_length) != -1) {
+ break;
+ }
+ /*
+ * Calling loadquery when compiled for 64-bit with the
+ * L_IGNOREUNLOAD flag can cause an invalid argument error
+ * on AIX 5.1. Detect this error the first time that
+ * loadquery is called, and try calling it again without
+ * this flag set.
+ */
+ if (errno == EINVAL && (loadflags & L_IGNOREUNLOAD)) {
+ loadflags &= ~L_IGNOREUNLOAD;
+ if (loadquery(loadflags, info, info_length) != -1) {
+ break;
+ }
+ }
+ PR_Free(info);
+ if (errno != ENOMEM) {
+ /* should not happen */
+ _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO());
+ return NULL;
+ }
+ /* retry with a larger buffer */
+ info_length += LD_INFO_INCREMENT * sizeof(struct ld_info);
+ }
+
+ for (infop = info;
+ ;
+ infop = (struct ld_info *)((char *)infop + infop->ldinfo_next)) {
+ unsigned long start = (unsigned long)infop->ldinfo_dataorg;
+ unsigned long end = start + infop->ldinfo_datasize;
+ if (start <= (unsigned long)addr && end > (unsigned long)addr) {
+ result = PR_Malloc(strlen(infop->ldinfo_filename)+1);
+ if (result != NULL) {
+ strcpy(result, infop->ldinfo_filename);
+ }
+ break;
+ }
+ if (!infop->ldinfo_next) {
+ PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, 0);
+ result = NULL;
+ break;
+ }
+ }
+ PR_Free(info);
+ return result;
+#elif defined(HPUX) && defined(USE_HPSHL)
+ int index;
+ struct shl_descriptor desc;
+ char *result;
+
+ for (index = 0; shl_get_r(index, &desc) == 0; index++) {
+ if (strstr(desc.filename, name) != NULL) {
+ result = PR_Malloc(strlen(desc.filename)+1);
+ if (result != NULL) {
+ strcpy(result, desc.filename);
+ }
+ return result;
+ }
+ }
+ /*
+ * Since the index value of a library is decremented if
+ * a library preceding it in the shared library search
+ * list was unloaded, it is possible that we missed some
+ * libraries as we went up the list. So we should go
+ * down the list to be sure that we not miss anything.
+ */
+ for (index--; index >= 0; index--) {
+ if ((shl_get_r(index, &desc) == 0)
+ && (strstr(desc.filename, name) != NULL)) {
+ result = PR_Malloc(strlen(desc.filename)+1);
+ if (result != NULL) {
+ strcpy(result, desc.filename);
+ }
+ return result;
+ }
+ }
+ PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, 0);
+ return NULL;
+#elif defined(HPUX) && defined(USE_DLFCN)
+ struct load_module_desc desc;
+ char *result;
+ const char *module_name;
+
+ if (dlmodinfo((unsigned long)addr, &desc, sizeof desc, NULL, 0, 0) == 0) {
+ PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ return NULL;
+ }
+ module_name = dlgetname(&desc, sizeof desc, NULL, 0, 0);
+ if (module_name == NULL) {
+ /* should not happen */
+ _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ return NULL;
+ }
+ result = PR_Malloc(strlen(module_name)+1);
+ if (result != NULL) {
+ strcpy(result, module_name);
+ }
+ return result;
+#elif defined(WIN32)
+ PRUnichar wname[MAX_PATH];
+ HMODULE handle = NULL;
+ PRUnichar module_name[MAX_PATH];
+ int len;
+ char *result;
+
+ if (MultiByteToWideChar(CP_ACP, 0, name, -1, wname, MAX_PATH)) {
+ handle = GetModuleHandleW(wname);
+ }
+ if (handle == NULL) {
+ PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ return NULL;
+ }
+ if (GetModuleFileNameW(handle, module_name, MAX_PATH) == 0) {
+ /* should not happen */
+ _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO());
+ return NULL;
+ }
+ len = WideCharToMultiByte(CP_ACP, 0, module_name, -1,
+ NULL, 0, NULL, NULL);
+ if (len == 0) {
+ _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO());
+ return NULL;
+ }
+ result = PR_Malloc(len * sizeof(PRUnichar));
+ if (result != NULL) {
+ WideCharToMultiByte(CP_ACP, 0, module_name, -1,
+ result, len, NULL, NULL);
+ }
+ return result;
+#elif defined(XP_OS2)
+ HMODULE module = NULL;
+ char module_name[_MAX_PATH];
+ char *result;
+ APIRET ulrc = DosQueryModFromEIP(&module, NULL, 0, NULL, NULL, (ULONG) addr);
+ if ((NO_ERROR != ulrc) || (NULL == module) ) {
+ PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ return NULL;
+ }
+ ulrc = DosQueryModuleName(module, sizeof module_name, module_name);
+ if (NO_ERROR != ulrc) {
+ /* should not happen */
+ _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO());
+ return NULL;
+ }
+ result = PR_Malloc(strlen(module_name)+1);
+ if (result != NULL) {
+ strcpy(result, module_name);
+ }
+ return result;
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+#endif
+}
diff --git a/nsprpub/pr/src/malloc/Makefile.in b/nsprpub/pr/src/malloc/Makefile.in
new file mode 100644
index 0000000000..51f2a5a52a
--- /dev/null
+++ b/nsprpub/pr/src/malloc/Makefile.in
@@ -0,0 +1,28 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+CSRCS = prmalloc.c prmem.c
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
diff --git a/nsprpub/pr/src/malloc/prmalloc.c b/nsprpub/pr/src/malloc/prmalloc.c
new file mode 100644
index 0000000000..c03b7d11e5
--- /dev/null
+++ b/nsprpub/pr/src/malloc/prmalloc.c
@@ -0,0 +1,1200 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+/*
+** We override malloc etc. on any platform which has preemption +
+** nspr20 user level threads. When we're debugging, we can make our
+** version of malloc fail occasionally.
+*/
+#ifdef _PR_OVERRIDE_MALLOC
+
+/*
+** Thread safe version of malloc, calloc, realloc, free
+*/
+#include <stdarg.h>
+
+#ifdef DEBUG
+#define SANITY
+#define EXTRA_SANITY
+#else
+#undef SANITY
+#undef EXTRA_SANITY
+#endif
+
+/* Forward decls */
+void *_PR_UnlockedMalloc(size_t size);
+void _PR_UnlockedFree(void *ptr);
+void *_PR_UnlockedRealloc(void *ptr, size_t size);
+void *_PR_UnlockedCalloc(size_t n, size_t elsize);
+
+/************************************************************************/
+
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ */
+
+/*
+ * Defining SANITY will enable some checks which will tell you if the users
+ * program did botch something
+ */
+
+/*
+ * Defining EXTRA_SANITY will enable some checks which are mostly related
+ * to internal conditions in malloc.c
+ */
+
+/*
+ * Very verbose progress on stdout...
+ */
+#if 0
+# define TRACE(foo) printf foo
+static int malloc_event;
+#else
+# define TRACE(foo)
+#endif
+
+/* XXX Pick a number, any number */
+# define malloc_pagesize 4096UL
+# define malloc_pageshift 12UL
+
+#ifdef XP_UNIX
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#endif
+
+/*
+ * This structure describes a page's worth of chunks.
+ */
+
+struct pginfo {
+ struct pginfo *next; /* next on the free list */
+ char *page; /* Pointer to the page */
+ u_short size; /* size of this page's chunks */
+ u_short shift; /* How far to shift for this size chunks */
+ u_short free; /* How many free chunks */
+ u_short total; /* How many chunk */
+ u_long bits[1]; /* Which chunks are free */
+};
+
+struct pgfree {
+ struct pgfree *next; /* next run of free pages */
+ struct pgfree *prev; /* prev run of free pages */
+ char *page; /* pointer to free pages */
+ char *end; /* pointer to end of free pages */
+ u_long size; /* number of bytes free */
+};
+
+/*
+ * How many bits per u_long in the bitmap.
+ * Change only if not 8 bits/byte
+ */
+#define MALLOC_BITS (8*sizeof(u_long))
+
+/*
+ * Magic values to put in the page_directory
+ */
+#define MALLOC_NOT_MINE ((struct pginfo*) 0)
+#define MALLOC_FREE ((struct pginfo*) 1)
+#define MALLOC_FIRST ((struct pginfo*) 2)
+#define MALLOC_FOLLOW ((struct pginfo*) 3)
+#define MALLOC_MAGIC ((struct pginfo*) 4)
+
+/*
+ * Set to one when malloc_init has been called
+ */
+static unsigned initialized;
+
+/*
+ * The size of a page.
+ * Must be a integral multiplum of the granularity of mmap(2).
+ * Your toes will curl if it isn't a power of two
+ */
+#define malloc_pagemask ((malloc_pagesize)-1)
+
+/*
+ * The size of the largest chunk.
+ * Half a page.
+ */
+#define malloc_maxsize ((malloc_pagesize)>>1)
+
+/*
+ * malloc_pagesize == 1 << malloc_pageshift
+ */
+#ifndef malloc_pageshift
+static unsigned malloc_pageshift;
+#endif /* malloc_pageshift */
+
+/*
+ * The smallest allocation we bother about.
+ * Must be power of two
+ */
+#ifndef malloc_minsize
+static unsigned malloc_minsize;
+#endif /* malloc_minsize */
+
+/*
+ * The largest chunk we care about.
+ * Must be smaller than pagesize
+ * Must be power of two
+ */
+#ifndef malloc_maxsize
+static unsigned malloc_maxsize;
+#endif /* malloc_maxsize */
+
+#ifndef malloc_cache
+static unsigned malloc_cache;
+#endif /* malloc_cache */
+
+/*
+ * The offset from pagenumber to index into the page directory
+ */
+static u_long malloc_origo;
+
+/*
+ * The last index in the page directory we care about
+ */
+static u_long last_index;
+
+/*
+ * Pointer to page directory.
+ * Allocated "as if with" malloc
+ */
+static struct pginfo **page_dir;
+
+/*
+ * How many slots in the page directory
+ */
+static unsigned malloc_ninfo;
+
+/*
+ * Free pages line up here
+ */
+static struct pgfree free_list;
+
+/*
+ * Abort() if we fail to get VM ?
+ */
+static int malloc_abort;
+
+#ifdef SANITY
+/*
+ * Are we trying to die ?
+ */
+static int suicide;
+#endif
+
+/*
+ * dump statistics
+ */
+static int malloc_stats;
+
+/*
+ * always realloc ?
+ */
+static int malloc_realloc;
+
+/*
+ * my last break.
+ */
+static void *malloc_brk;
+
+/*
+ * one location cache for free-list holders
+ */
+static struct pgfree *px;
+
+static int set_pgdir(void *ptr, struct pginfo *info);
+static int extend_page_directory(u_long index);
+
+#ifdef SANITY
+void
+malloc_dump(FILE *fd)
+{
+ struct pginfo **pd;
+ struct pgfree *pf;
+ int j;
+
+ pd = page_dir;
+
+ /* print out all the pages */
+ for(j=0; j<=last_index; j++) {
+ fprintf(fd,"%08lx %5d ",(j+malloc_origo) << malloc_pageshift,j);
+ if (pd[j] == MALLOC_NOT_MINE) {
+ for(j++; j<=last_index && pd[j] == MALLOC_NOT_MINE; j++)
+ ;
+ j--;
+ fprintf(fd,".. %5d not mine\n", j);
+ } else if (pd[j] == MALLOC_FREE) {
+ for(j++; j<=last_index && pd[j] == MALLOC_FREE; j++)
+ ;
+ j--;
+ fprintf(fd,".. %5d free\n", j);
+ } else if (pd[j] == MALLOC_FIRST) {
+ for(j++; j<=last_index && pd[j] == MALLOC_FOLLOW; j++)
+ ;
+ j--;
+ fprintf(fd,".. %5d in use\n", j);
+ } else if (pd[j] < MALLOC_MAGIC) {
+ fprintf(fd,"(%p)\n", pd[j]);
+ } else {
+ fprintf(fd,"%p %d (of %d) x %d @ %p --> %p\n",
+ pd[j],pd[j]->free, pd[j]->total,
+ pd[j]->size, pd[j]->page, pd[j]->next);
+ }
+ }
+
+ for(pf=free_list.next; pf; pf=pf->next) {
+ fprintf(fd,"Free: @%p [%p...%p[ %ld ->%p <-%p\n",
+ pf,pf->page,pf->end,pf->size,pf->prev,pf->next);
+ if (pf == pf->next) {
+ fprintf(fd,"Free_list loops.\n");
+ break;
+ }
+ }
+
+ /* print out various info */
+ fprintf(fd,"Minsize\t%d\n",malloc_minsize);
+ fprintf(fd,"Maxsize\t%ld\n",malloc_maxsize);
+ fprintf(fd,"Pagesize\t%ld\n",malloc_pagesize);
+ fprintf(fd,"Pageshift\t%ld\n",malloc_pageshift);
+ fprintf(fd,"FirstPage\t%ld\n",malloc_origo);
+ fprintf(fd,"LastPage\t%ld %lx\n",last_index+malloc_pageshift,
+ (last_index + malloc_pageshift) << malloc_pageshift);
+ fprintf(fd,"Break\t%ld\n",(u_long)sbrk(0) >> malloc_pageshift);
+}
+
+static void wrterror(char *fmt, ...)
+{
+ char *q = "malloc() error: ";
+ char buf[100];
+ va_list ap;
+
+ suicide = 1;
+
+ va_start(ap, fmt);
+ PR_vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ fputs(q, stderr);
+ fputs(buf, stderr);
+
+ malloc_dump(stderr);
+ PR_Abort();
+}
+
+static void wrtwarning(char *fmt, ...)
+{
+ char *q = "malloc() warning: ";
+ char buf[100];
+ va_list ap;
+
+ va_start(ap, fmt);
+ PR_vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ fputs(q, stderr);
+ fputs(buf, stderr);
+}
+#endif /* SANITY */
+
+
+/*
+ * Allocate a number of pages from the OS
+ */
+static caddr_t
+map_pages(int pages, int update)
+{
+ caddr_t result,tail;
+
+ result = ((caddr_t)sbrk(0)) + malloc_pagemask - 1;
+ result = (caddr_t) ((u_long)result & ~malloc_pagemask);
+ tail = result + (pages << malloc_pageshift);
+ if (!brk(tail)) {
+ last_index = ((u_long)tail >> malloc_pageshift) - malloc_origo -1;
+ malloc_brk = tail;
+ TRACE(("%6d S %p .. %p\n",malloc_event++, result, tail));
+ if (!update || last_index < malloc_ninfo ||
+ extend_page_directory(last_index)) {
+ return result;
+ }
+ }
+ TRACE(("%6d s %d %p %d\n",malloc_event++,pages,sbrk(0),errno));
+#ifdef EXTRA_SANITY
+ wrterror("map_pages fails\n");
+#endif
+ return 0;
+}
+
+#define set_bit(_pi,_bit) \
+ (_pi)->bits[(_bit)/MALLOC_BITS] |= 1L<<((_bit)%MALLOC_BITS)
+
+#define clr_bit(_pi,_bit) \
+ (_pi)->bits[(_bit)/MALLOC_BITS] &= ~(1L<<((_bit)%MALLOC_BITS));
+
+#define tst_bit(_pi,_bit) \
+ ((_pi)->bits[(_bit)/MALLOC_BITS] & (1L<<((_bit)%MALLOC_BITS)))
+
+/*
+ * Extend page directory
+ */
+static int
+extend_page_directory(u_long index)
+{
+ struct pginfo **young, **old;
+ int i;
+
+ TRACE(("%6d E %lu\n",malloc_event++,index));
+
+ /* Make it this many pages */
+ i = index * sizeof *page_dir;
+ i /= malloc_pagesize;
+ i += 2;
+
+ /* Get new pages, if you used this much mem you don't care :-) */
+ young = (struct pginfo**) map_pages(i,0);
+ if (!young) {
+ return 0;
+ }
+
+ /* Copy the old stuff */
+ memset(young, 0, i * malloc_pagesize);
+ memcpy(young, page_dir,
+ malloc_ninfo * sizeof *page_dir);
+
+ /* register the new size */
+ malloc_ninfo = i * malloc_pagesize / sizeof *page_dir;
+
+ /* swap the pointers */
+ old = page_dir;
+ page_dir = young;
+
+ /* Mark the pages */
+ index = ((u_long)young >> malloc_pageshift) - malloc_origo;
+ page_dir[index] = MALLOC_FIRST;
+ while (--i) {
+ page_dir[++index] = MALLOC_FOLLOW;
+ }
+
+ /* Now free the old stuff */
+ _PR_UnlockedFree(old);
+ return 1;
+}
+
+/*
+ * Set entry in page directory.
+ * Extend page directory if need be.
+ */
+static int
+set_pgdir(void *ptr, struct pginfo *info)
+{
+ u_long index = ((u_long)ptr >> malloc_pageshift) - malloc_origo;
+
+ if (index >= malloc_ninfo && !extend_page_directory(index)) {
+ return 0;
+ }
+ page_dir[index] = info;
+ return 1;
+}
+
+/*
+ * Initialize the world
+ */
+static void
+malloc_init (void)
+{
+ int i;
+ char *p;
+
+ TRACE(("%6d I\n",malloc_event++));
+#ifdef DEBUG
+ for (p=getenv("MALLOC_OPTIONS"); p && *p; p++) {
+ switch (*p) {
+ case 'a': malloc_abort = 0; break;
+ case 'A': malloc_abort = 1; break;
+ case 'd': malloc_stats = 0; break;
+ case 'D': malloc_stats = 1; break;
+ case 'r': malloc_realloc = 0; break;
+ case 'R': malloc_realloc = 1; break;
+ default:
+ wrtwarning("Unknown chars in MALLOC_OPTIONS\n");
+ break;
+ }
+ }
+#endif
+
+#ifndef malloc_pagesize
+ /* determine our pagesize */
+ malloc_pagesize = getpagesize();
+#endif /* malloc_pagesize */
+
+#ifndef malloc_pageshift
+ /* determine how much we shift by to get there */
+ for (i = malloc_pagesize; i > 1; i >>= 1) {
+ malloc_pageshift++;
+ }
+#endif /* malloc_pageshift */
+
+#ifndef malloc_cache
+ malloc_cache = 50 << malloc_pageshift;
+#endif /* malloc_cache */
+
+#ifndef malloc_minsize
+ /*
+ * find the smallest size allocation we will bother about.
+ * this is determined as the smallest allocation that can hold
+ * it's own pginfo;
+ */
+ i = 2;
+ for(;;) {
+ int j;
+
+ /* Figure out the size of the bits */
+ j = malloc_pagesize/i;
+ j /= 8;
+ if (j < sizeof(u_long)) {
+ j = sizeof (u_long);
+ }
+ if (sizeof(struct pginfo) + j - sizeof (u_long) <= i) {
+ break;
+ }
+ i += i;
+ }
+ malloc_minsize = i;
+#endif /* malloc_minsize */
+
+
+ /* Allocate one page for the page directory */
+ page_dir = (struct pginfo **) map_pages(1,0);
+#ifdef SANITY
+ if (!page_dir) {
+ wrterror("fatal: my first mmap failed. (check limits ?)\n");
+ }
+#endif
+
+ /*
+ * We need a maximum of malloc_pageshift buckets, steal these from the
+ * front of the page_directory;
+ */
+ malloc_origo = (u_long) page_dir >> malloc_pageshift;
+ malloc_origo -= malloc_pageshift;
+
+ /* Clear it */
+ memset(page_dir,0,malloc_pagesize);
+
+ /* Find out how much it tells us */
+ malloc_ninfo = malloc_pagesize / sizeof *page_dir;
+
+ /* Plug the page directory into itself */
+ i = set_pgdir(page_dir,MALLOC_FIRST);
+#ifdef SANITY
+ if (!i) {
+ wrterror("fatal: couldn't set myself in the page directory\n");
+ }
+#endif
+
+ /* Been here, done that */
+ initialized++;
+}
+
+/*
+ * Allocate a number of complete pages
+ */
+static void *malloc_pages(size_t size)
+{
+ void *p,*delay_free = 0;
+ int i;
+ struct pgfree *pf;
+ u_long index;
+
+ /* How many pages ? */
+ size += (malloc_pagesize-1);
+ size &= ~malloc_pagemask;
+
+ p = 0;
+ /* Look for free pages before asking for more */
+ for(pf = free_list.next; pf; pf = pf->next) {
+#ifdef EXTRA_SANITY
+ if (pf->page == pf->end) {
+ wrterror("zero entry on free_list\n");
+ }
+ if (pf->page > pf->end) {
+ TRACE(("%6d !s %p %p %p <%d>\n",malloc_event++,
+ pf,pf->page,pf->end,__LINE__));
+ wrterror("sick entry on free_list\n");
+ }
+ if ((void*)pf->page >= (void*)sbrk(0)) {
+ wrterror("entry on free_list past brk\n");
+ }
+ if (page_dir[((u_long)pf->page >> malloc_pageshift) - malloc_origo]
+ != MALLOC_FREE) {
+ TRACE(("%6d !f %p %p %p <%d>\n",malloc_event++,
+ pf,pf->page,pf->end,__LINE__));
+ wrterror("non-free first page on free-list\n");
+ }
+ if (page_dir[((u_long)pf->end >> malloc_pageshift) - 1 - malloc_origo]
+ != MALLOC_FREE) {
+ wrterror("non-free last page on free-list\n");
+ }
+#endif /* EXTRA_SANITY */
+ if (pf->size < size) {
+ continue;
+ }
+ else if (pf->size == size) {
+ p = pf->page;
+ if (pf->next) {
+ pf->next->prev = pf->prev;
+ }
+ pf->prev->next = pf->next;
+ delay_free = pf;
+ break;
+ } else {
+ p = pf->page;
+ pf->page += size;
+ pf->size -= size;
+ break;
+ }
+ }
+#ifdef EXTRA_SANITY
+ if (p && page_dir[((u_long)p >> malloc_pageshift) - malloc_origo]
+ != MALLOC_FREE) {
+ wrterror("allocated non-free page on free-list\n");
+ }
+#endif /* EXTRA_SANITY */
+
+ size >>= malloc_pageshift;
+
+ /* Map new pages */
+ if (!p) {
+ p = map_pages(size,1);
+ }
+
+ if (p) {
+ /* Mark the pages in the directory */
+ index = ((u_long)p >> malloc_pageshift) - malloc_origo;
+ page_dir[index] = MALLOC_FIRST;
+ for (i=1; i<size; i++) {
+ page_dir[index+i] = MALLOC_FOLLOW;
+ }
+ }
+ if (delay_free) {
+ if (!px) {
+ px = (struct pgfree*)delay_free;
+ }
+ else {
+ _PR_UnlockedFree(delay_free);
+ }
+ }
+ return p;
+}
+
+/*
+ * Allocate a page of fragments
+ */
+
+static int
+malloc_make_chunks(int bits)
+{
+ struct pginfo *bp;
+ void *pp;
+ int i,k,l;
+
+ /* Allocate a new bucket */
+ pp = malloc_pages(malloc_pagesize);
+ if (!pp) {
+ return 0;
+ }
+ l = sizeof *bp - sizeof(u_long);
+ l += sizeof(u_long) *
+ (((malloc_pagesize >> bits)+MALLOC_BITS-1) / MALLOC_BITS);
+ if ((1<<(bits)) <= l+l) {
+ bp = (struct pginfo *)pp;
+ } else {
+ bp = (struct pginfo *)_PR_UnlockedMalloc(l);
+ }
+ if (!bp) {
+ return 0;
+ }
+ bp->size = (1<<bits);
+ bp->shift = bits;
+ bp->total = bp->free = malloc_pagesize >> bits;
+ bp->next = page_dir[bits];
+ bp->page = (char*)pp;
+ i = set_pgdir(pp,bp);
+ if (!i) {
+ return 0;
+ }
+
+ /* We can safely assume that there is nobody in this chain */
+ page_dir[bits] = bp;
+
+ /* set all valid bits in the bits */
+ k = bp->total;
+ i = 0;
+ /*
+ for(;k-i >= MALLOC_BITS; i += MALLOC_BITS)
+ bp->bits[i / MALLOC_BITS] = ~0;
+ */
+ for(; i < k; i++) {
+ set_bit(bp,i);
+ }
+
+ if (bp != pp) {
+ return 1;
+ }
+
+ /* We may have used the first ones already */
+ for(i=0; l > 0; i++) {
+ clr_bit(bp,i);
+ bp->free--;
+ bp->total--;
+ l -= (1 << bits);
+ }
+ return 1;
+}
+
+/*
+ * Allocate a fragment
+ */
+static void *malloc_bytes(size_t size)
+{
+ size_t s;
+ int j;
+ struct pginfo *bp;
+ int k;
+ u_long *lp, bf;
+
+ /* Don't bother with anything less than this */
+ if (size < malloc_minsize) {
+ size = malloc_minsize;
+ }
+
+ /* Find the right bucket */
+ j = 1;
+ s = size - 1;
+ while (s >>= 1) {
+ j++;
+ }
+
+ /* If it's empty, make a page more of that size chunks */
+ if (!page_dir[j] && !malloc_make_chunks(j)) {
+ return 0;
+ }
+
+ /* Find first word of bitmap which isn't empty */
+ bp = page_dir[j];
+ for (lp = bp->bits; !*lp; lp++)
+ ;
+
+ /* Find that bit */
+ bf = *lp;
+ k = 0;
+ while ((bf & 1) == 0) {
+ bf >>= 1;
+ k++;
+ }
+
+ *lp ^= 1L<<k; /* clear it */
+ bp->free--;
+ if (!bp->free) {
+ page_dir[j] = bp->next;
+ bp->next = 0;
+ }
+ k += (lp - bp->bits)*MALLOC_BITS;
+ return bp->page + (k << bp->shift);
+}
+
+void *_PR_UnlockedMalloc(size_t size)
+{
+ void *result;
+
+ /* Round up to a multiple of 8 bytes */
+ if (size & 7) {
+ size = size + 8 - (size & 7);
+ }
+
+ if (!initialized) {
+ malloc_init();
+ }
+
+#ifdef SANITY
+ if (suicide) {
+ PR_Abort();
+ }
+#endif
+
+ if (size <= malloc_maxsize) {
+ result = malloc_bytes(size);
+ }
+ else {
+ result = malloc_pages(size);
+ }
+#ifdef SANITY
+ if (malloc_abort && !result) {
+ wrterror("malloc() returns NULL\n");
+ }
+#endif
+ TRACE(("%6d M %p %d\n",malloc_event++,result,size));
+
+ return result;
+}
+
+void *_PR_UnlockedMemalign(size_t alignment, size_t size)
+{
+ void *result;
+
+ /*
+ * alignment has to be a power of 2
+ */
+
+ if ((size <= alignment) && (alignment <= malloc_maxsize)) {
+ size = alignment;
+ }
+ else {
+ size += alignment - 1;
+ }
+
+ /* Round up to a multiple of 8 bytes */
+ if (size & 7) {
+ size = size + 8 - (size & 7);
+ }
+
+ if (!initialized) {
+ malloc_init();
+ }
+
+#ifdef SANITY
+ if (suicide) {
+ abort();
+ }
+#endif
+
+ if (size <= malloc_maxsize) {
+ result = malloc_bytes(size);
+ }
+ else {
+ result = malloc_pages(size);
+ }
+#ifdef SANITY
+ if (malloc_abort && !result) {
+ wrterror("malloc() returns NULL\n");
+ }
+#endif
+ TRACE(("%6d A %p %d\n",malloc_event++,result,size));
+
+ if ((u_long)result & (alignment - 1)) {
+ return ((void *)(((u_long)result + alignment) & ~(alignment - 1)));
+ }
+ else {
+ return result;
+ }
+}
+
+void *_PR_UnlockedCalloc(size_t n, size_t nelem)
+{
+ void *p;
+
+ /* Compute total size and then round up to a double word amount */
+ n *= nelem;
+ if (n & 7) {
+ n = n + 8 - (n & 7);
+ }
+
+ /* Get the memory */
+ p = _PR_UnlockedMalloc(n);
+ if (p) {
+ /* Zero it */
+ memset(p, 0, n);
+ }
+ return p;
+}
+
+/*
+ * Change an allocation's size
+ */
+void *_PR_UnlockedRealloc(void *ptr, size_t size)
+{
+ void *p;
+ u_long osize,page,index,tmp_index;
+ struct pginfo **mp;
+
+ if (!initialized) {
+ malloc_init();
+ }
+
+#ifdef SANITY
+ if (suicide) {
+ PR_Abort();
+ }
+#endif
+
+ /* used as free() */
+ TRACE(("%6d R %p %d\n",malloc_event++, ptr, size));
+ if (ptr && !size) {
+ _PR_UnlockedFree(ptr);
+ return _PR_UnlockedMalloc (1);
+ }
+
+ /* used as malloc() */
+ if (!ptr) {
+ p = _PR_UnlockedMalloc(size);
+ return p;
+ }
+
+ /* Find the page directory entry for the page in question */
+ page = (u_long)ptr >> malloc_pageshift;
+ index = page - malloc_origo;
+
+ /*
+ * check if memory was allocated by memalign
+ */
+ tmp_index = index;
+ while (page_dir[tmp_index] == MALLOC_FOLLOW) {
+ tmp_index--;
+ }
+ if (tmp_index != index) {
+ /*
+ * memalign-allocated memory
+ */
+ index = tmp_index;
+ page = index + malloc_origo;
+ ptr = (void *) (page << malloc_pageshift);
+ }
+ TRACE(("%6d R2 %p %d\n",malloc_event++, ptr, size));
+
+ /* make sure it makes sense in some fashion */
+ if (index < malloc_pageshift || index > last_index) {
+#ifdef SANITY
+ wrtwarning("junk pointer passed to realloc()\n");
+#endif
+ return 0;
+ }
+
+ /* find the size of that allocation, and see if we need to relocate */
+ mp = &page_dir[index];
+ if (*mp == MALLOC_FIRST) {
+ osize = malloc_pagesize;
+ while (mp[1] == MALLOC_FOLLOW) {
+ osize += malloc_pagesize;
+ mp++;
+ }
+ if (!malloc_realloc &&
+ size < osize &&
+ size > malloc_maxsize &&
+ size > (osize - malloc_pagesize)) {
+ return ptr;
+ }
+ } else if (*mp >= MALLOC_MAGIC) {
+ osize = (*mp)->size;
+ if (!malloc_realloc &&
+ size < osize &&
+ (size > (*mp)->size/2 || (*mp)->size == malloc_minsize)) {
+ return ptr;
+ }
+ } else {
+#ifdef SANITY
+ wrterror("realloc() of wrong page.\n");
+#endif
+ }
+
+ /* try to reallocate */
+ p = _PR_UnlockedMalloc(size);
+
+ if (p) {
+ /* copy the lesser of the two sizes */
+ if (osize < size) {
+ memcpy(p,ptr,osize);
+ }
+ else {
+ memcpy(p,ptr,size);
+ }
+ _PR_UnlockedFree(ptr);
+ }
+#ifdef DEBUG
+ else if (malloc_abort) {
+ wrterror("realloc() returns NULL\n");
+ }
+#endif
+
+ return p;
+}
+
+/*
+ * Free a sequence of pages
+ */
+
+static void
+free_pages(char *ptr, u_long page, int index, struct pginfo *info)
+{
+ int i;
+ struct pgfree *pf,*pt;
+ u_long l;
+ char *tail;
+
+ TRACE(("%6d FP %p %d\n",malloc_event++, ptr, page));
+ /* Is it free already ? */
+ if (info == MALLOC_FREE) {
+#ifdef SANITY
+ wrtwarning("freeing free page at %p.\n", ptr);
+#endif
+ return;
+ }
+
+#ifdef SANITY
+ /* Is it not the right place to begin ? */
+ if (info != MALLOC_FIRST) {
+ wrterror("freeing wrong page.\n");
+ }
+
+ /* Is this really a pointer to a page ? */
+ if ((u_long)ptr & malloc_pagemask) {
+ wrterror("freeing messed up page pointer.\n");
+ }
+#endif
+
+ /* Count how many pages it is anyway */
+ page_dir[index] = MALLOC_FREE;
+ for (i = 1; page_dir[index+i] == MALLOC_FOLLOW; i++) {
+ page_dir[index + i] = MALLOC_FREE;
+ }
+
+ l = i << malloc_pageshift;
+
+ tail = ptr+l;
+
+ /* add to free-list */
+ if (!px) {
+ px = (struct pgfree*)_PR_UnlockedMalloc(sizeof *pt);
+ }
+ /* XXX check success */
+ px->page = ptr;
+ px->end = tail;
+ px->size = l;
+ if (!free_list.next) {
+ px->next = free_list.next;
+ px->prev = &free_list;
+ free_list.next = px;
+ pf = px;
+ px = 0;
+ } else {
+ tail = ptr+l;
+ for(pf = free_list.next; pf->next && pf->end < ptr; pf = pf->next)
+ ;
+ for(; pf; pf = pf->next) {
+ if (pf->end == ptr ) {
+ /* append to entry */
+ pf->end += l;
+ pf->size += l;
+ if (pf->next && pf->end == pf->next->page ) {
+ pt = pf->next;
+ pf->end = pt->end;
+ pf->size += pt->size;
+ pf->next = pt->next;
+ if (pf->next) {
+ pf->next->prev = pf;
+ }
+ _PR_UnlockedFree(pt);
+ }
+ } else if (pf->page == tail) {
+ /* prepend to entry */
+ pf->size += l;
+ pf->page = ptr;
+ } else if (pf->page > ptr) {
+ px->next = pf;
+ px->prev = pf->prev;
+ pf->prev = px;
+ px->prev->next = px;
+ pf = px;
+ px = 0;
+ } else if (!pf->next) {
+ px->next = 0;
+ px->prev = pf;
+ pf->next = px;
+ pf = px;
+ px = 0;
+ } else {
+ continue;
+ }
+ break;
+ }
+ }
+ if (!pf->next &&
+ pf->size > malloc_cache &&
+ pf->end == malloc_brk &&
+ malloc_brk == (void*)sbrk(0)) {
+ pf->end = pf->page + malloc_cache;
+ pf->size = malloc_cache;
+ TRACE(("%6d U %p %d\n",malloc_event++,pf->end,pf->end - pf->page));
+ brk(pf->end);
+ malloc_brk = pf->end;
+ /* Find the page directory entry for the page in question */
+ page = (u_long)pf->end >> malloc_pageshift;
+ index = page - malloc_origo;
+ /* Now update the directory */
+ for(i=index; i <= last_index;) {
+ page_dir[i++] = MALLOC_NOT_MINE;
+ }
+ last_index = index - 1;
+ }
+}
+
+/*
+ * Free a chunk, and possibly the page it's on, if the page becomes empty.
+ */
+
+static void
+free_bytes(void *ptr, u_long page, int index, struct pginfo *info)
+{
+ int i;
+ struct pginfo **mp;
+ void *vp;
+
+ /* Make sure that pointer is multiplum of chunk-size */
+#ifdef SANITY
+ if ((u_long)ptr & (info->size - 1)) {
+ wrterror("freeing messed up chunk pointer\n");
+ }
+#endif
+
+ /* Find the chunk number on the page */
+ i = ((u_long)ptr & malloc_pagemask) >> info->shift;
+
+ /* See if it's free already */
+ if (tst_bit(info,i)) {
+#ifdef SANITY
+ wrtwarning("freeing free chunk at %p\n", ptr);
+#endif
+ return;
+ }
+
+ /* Mark it free */
+ set_bit(info,i);
+ info->free++;
+
+ /* If the page was full before, we need to put it on the queue now */
+ if (info->free == 1) {
+ mp = page_dir + info->shift;
+ while (*mp && (*mp)->next && (*mp)->next->page < info->page) {
+ mp = &(*mp)->next;
+ }
+ info->next = *mp;
+ *mp = info;
+ return;
+ }
+
+ /* If this page isn't empty, don't do anything. */
+ if (info->free != info->total) {
+ return;
+ }
+
+ /* We may want to keep at least one page of each size chunks around. */
+ mp = page_dir + info->shift;
+ if (0 && (*mp == info) && !info->next) {
+ return;
+ }
+
+ /* Find & remove this page in the queue */
+ while (*mp != info) {
+ mp = &((*mp)->next);
+#ifdef EXTRA_SANITY
+ if (!*mp) {
+ TRACE(("%6d !q %p\n",malloc_event++,info));
+ wrterror("Not on queue\n");
+ }
+#endif
+ }
+ *mp = info->next;
+
+ /* Free the page & the info structure if need be */
+ set_pgdir(info->page,MALLOC_FIRST);
+ if((void*)info->page == (void*)info) {
+ _PR_UnlockedFree(info->page);
+ } else {
+ vp = info->page;
+ _PR_UnlockedFree(info);
+ _PR_UnlockedFree(vp);
+ }
+}
+
+void _PR_UnlockedFree(void *ptr)
+{
+ u_long page;
+ struct pginfo *info;
+ int index, tmp_index;
+
+ TRACE(("%6d F %p\n",malloc_event++,ptr));
+ /* This is legal */
+ if (!ptr) {
+ return;
+ }
+
+#ifdef SANITY
+ /* There wouldn't be anything to free */
+ if (!initialized) {
+ wrtwarning("free() called before malloc() ever got called\n");
+ return;
+ }
+#endif
+
+#ifdef SANITY
+ if (suicide) {
+ PR_Abort();
+ }
+#endif
+
+ /* Find the page directory entry for the page in question */
+ page = (u_long)ptr >> malloc_pageshift;
+ index = page - malloc_origo;
+
+ /*
+ * check if memory was allocated by memalign
+ */
+ tmp_index = index;
+ while (page_dir[tmp_index] == MALLOC_FOLLOW) {
+ tmp_index--;
+ }
+ if (tmp_index != index) {
+ /*
+ * memalign-allocated memory
+ */
+ index = tmp_index;
+ page = index + malloc_origo;
+ ptr = (void *) (page << malloc_pageshift);
+ }
+ /* make sure it makes sense in some fashion */
+ if (index < malloc_pageshift) {
+#ifdef SANITY
+ wrtwarning("junk pointer %p (low) passed to free()\n", ptr);
+#endif
+ return;
+ }
+ if (index > last_index) {
+#ifdef SANITY
+ wrtwarning("junk pointer %p (high) passed to free()\n", ptr);
+#endif
+ return;
+ }
+
+ /* handle as page-allocation or chunk allocation */
+ info = page_dir[index];
+ if (info < MALLOC_MAGIC) {
+ free_pages((char*)ptr, page, index, info);
+ }
+ else {
+ free_bytes(ptr,page,index,info);
+ }
+ return;
+}
+#endif /* _PR_OVERRIDE_MALLOC */
diff --git a/nsprpub/pr/src/malloc/prmem.c b/nsprpub/pr/src/malloc/prmem.c
new file mode 100644
index 0000000000..08a700f7a9
--- /dev/null
+++ b/nsprpub/pr/src/malloc/prmem.c
@@ -0,0 +1,678 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Thread safe versions of malloc, free, realloc, calloc and cfree.
+*/
+
+#include "primpl.h"
+
+#ifdef _PR_ZONE_ALLOCATOR
+
+/*
+** The zone allocator code must use native mutexes and cannot
+** use PRLocks because PR_NewLock calls PR_Calloc, resulting
+** in cyclic dependency of initialization.
+*/
+
+#include <string.h>
+
+union memBlkHdrUn;
+
+typedef struct MemoryZoneStr {
+ union memBlkHdrUn *head; /* free list */
+ pthread_mutex_t lock;
+ size_t blockSize; /* size of blocks on this free list */
+ PRUint32 locked; /* current state of lock */
+ PRUint32 contention; /* counter: had to wait for lock */
+ PRUint32 hits; /* allocated from free list */
+ PRUint32 misses; /* had to call malloc */
+ PRUint32 elements; /* on free list */
+} MemoryZone;
+
+typedef union memBlkHdrUn {
+ unsigned char filler[48]; /* fix the size of this beast */
+ struct memBlkHdrStr {
+ union memBlkHdrUn *next;
+ MemoryZone *zone;
+ size_t blockSize;
+ size_t requestedSize;
+ PRUint32 magic;
+ } s;
+} MemBlockHdr;
+
+#define MEM_ZONES 7
+#define THREAD_POOLS 11 /* prime number for modulus */
+#define ZONE_MAGIC 0x0BADC0DE
+
+static MemoryZone zones[MEM_ZONES][THREAD_POOLS];
+
+static PRBool use_zone_allocator = PR_FALSE;
+
+static void pr_ZoneFree(void *ptr);
+
+void
+_PR_DestroyZones(void)
+{
+ int i, j;
+
+ if (!use_zone_allocator) {
+ return;
+ }
+
+ for (j = 0; j < THREAD_POOLS; j++) {
+ for (i = 0; i < MEM_ZONES; i++) {
+ MemoryZone *mz = &zones[i][j];
+ pthread_mutex_destroy(&mz->lock);
+ while (mz->head) {
+ MemBlockHdr *hdr = mz->head;
+ mz->head = hdr->s.next; /* unlink it */
+ free(hdr);
+ mz->elements--;
+ }
+ }
+ }
+ use_zone_allocator = PR_FALSE;
+}
+
+/*
+** pr_FindSymbolInProg
+**
+** Find the specified data symbol in the program and return
+** its address.
+*/
+
+#ifdef HAVE_DLL
+
+#if defined(USE_DLFCN) && !defined(NO_DLOPEN_NULL)
+
+#include <dlfcn.h>
+
+static void *
+pr_FindSymbolInProg(const char *name)
+{
+ void *h;
+ void *sym;
+
+ h = dlopen(0, RTLD_LAZY);
+ if (h == NULL) {
+ return NULL;
+ }
+ sym = dlsym(h, name);
+ (void)dlclose(h);
+ return sym;
+}
+
+#elif defined(USE_HPSHL)
+
+#include <dl.h>
+
+static void *
+pr_FindSymbolInProg(const char *name)
+{
+ shl_t h = NULL;
+ void *sym;
+
+ if (shl_findsym(&h, name, TYPE_DATA, &sym) == -1) {
+ return NULL;
+ }
+ return sym;
+}
+
+#elif defined(USE_MACH_DYLD) || defined(NO_DLOPEN_NULL)
+
+static void *
+pr_FindSymbolInProg(const char *name)
+{
+ /* FIXME: not implemented */
+ return NULL;
+}
+
+#else
+
+#error "The zone allocator is not supported on this platform"
+
+#endif
+
+#else /* !defined(HAVE_DLL) */
+
+static void *
+pr_FindSymbolInProg(const char *name)
+{
+ /* can't be implemented */
+ return NULL;
+}
+
+#endif /* HAVE_DLL */
+
+void
+_PR_InitZones(void)
+{
+ int i, j;
+ char *envp;
+ PRBool *sym;
+
+ if ((sym = (PRBool *)pr_FindSymbolInProg("nspr_use_zone_allocator")) != NULL) {
+ use_zone_allocator = *sym;
+ } else if ((envp = getenv("NSPR_USE_ZONE_ALLOCATOR")) != NULL) {
+ use_zone_allocator = (atoi(envp) == 1);
+ }
+
+ if (!use_zone_allocator) {
+ return;
+ }
+
+ for (j = 0; j < THREAD_POOLS; j++) {
+ for (i = 0; i < MEM_ZONES; i++) {
+ MemoryZone *mz = &zones[i][j];
+ int rv = pthread_mutex_init(&mz->lock, NULL);
+ PR_ASSERT(0 == rv);
+ if (rv != 0) {
+ goto loser;
+ }
+ mz->blockSize = 16 << ( 2 * i);
+ }
+ }
+ return;
+
+loser:
+ _PR_DestroyZones();
+ return;
+}
+
+PR_IMPLEMENT(void)
+PR_FPrintZoneStats(PRFileDesc *debug_out)
+{
+ int i, j;
+
+ for (j = 0; j < THREAD_POOLS; j++) {
+ for (i = 0; i < MEM_ZONES; i++) {
+ MemoryZone *mz = &zones[i][j];
+ MemoryZone zone = *mz;
+ if (zone.elements || zone.misses || zone.hits) {
+ PR_fprintf(debug_out,
+ "pool: %d, zone: %d, size: %d, free: %d, hit: %d, miss: %d, contend: %d\n",
+ j, i, zone.blockSize, zone.elements,
+ zone.hits, zone.misses, zone.contention);
+ }
+ }
+ }
+}
+
+static void *
+pr_ZoneMalloc(PRUint32 size)
+{
+ void *rv;
+ unsigned int zone;
+ size_t blockSize;
+ MemBlockHdr *mb, *mt;
+ MemoryZone *mz;
+
+ /* Always allocate a non-zero amount of bytes */
+ if (size < 1) {
+ size = 1;
+ }
+ for (zone = 0, blockSize = 16; zone < MEM_ZONES; ++zone, blockSize <<= 2) {
+ if (size <= blockSize) {
+ break;
+ }
+ }
+ if (zone < MEM_ZONES) {
+ pthread_t me = pthread_self();
+ unsigned int pool = (PRUptrdiff)me % THREAD_POOLS;
+ PRUint32 wasLocked;
+ mz = &zones[zone][pool];
+ wasLocked = mz->locked;
+ pthread_mutex_lock(&mz->lock);
+ mz->locked = 1;
+ if (wasLocked) {
+ mz->contention++;
+ }
+ if (mz->head) {
+ mb = mz->head;
+ PR_ASSERT(mb->s.magic == ZONE_MAGIC);
+ PR_ASSERT(mb->s.zone == mz);
+ PR_ASSERT(mb->s.blockSize == blockSize);
+ PR_ASSERT(mz->blockSize == blockSize);
+
+ mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize);
+ PR_ASSERT(mt->s.magic == ZONE_MAGIC);
+ PR_ASSERT(mt->s.zone == mz);
+ PR_ASSERT(mt->s.blockSize == blockSize);
+
+ mz->hits++;
+ mz->elements--;
+ mz->head = mb->s.next; /* take off free list */
+ mz->locked = 0;
+ pthread_mutex_unlock(&mz->lock);
+
+ mt->s.next = mb->s.next = NULL;
+ mt->s.requestedSize = mb->s.requestedSize = size;
+
+ rv = (void *)(mb + 1);
+ return rv;
+ }
+
+ mz->misses++;
+ mz->locked = 0;
+ pthread_mutex_unlock(&mz->lock);
+
+ mb = (MemBlockHdr *)malloc(blockSize + 2 * (sizeof *mb));
+ if (!mb) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ mb->s.next = NULL;
+ mb->s.zone = mz;
+ mb->s.magic = ZONE_MAGIC;
+ mb->s.blockSize = blockSize;
+ mb->s.requestedSize = size;
+
+ mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize);
+ memcpy(mt, mb, sizeof *mb);
+
+ rv = (void *)(mb + 1);
+ return rv;
+ }
+
+ /* size was too big. Create a block with no zone */
+ blockSize = (size & 15) ? size + 16 - (size & 15) : size;
+ mb = (MemBlockHdr *)malloc(blockSize + 2 * (sizeof *mb));
+ if (!mb) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ mb->s.next = NULL;
+ mb->s.zone = NULL;
+ mb->s.magic = ZONE_MAGIC;
+ mb->s.blockSize = blockSize;
+ mb->s.requestedSize = size;
+
+ mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize);
+ memcpy(mt, mb, sizeof *mb);
+
+ rv = (void *)(mb + 1);
+ return rv;
+}
+
+
+static void *
+pr_ZoneCalloc(PRUint32 nelem, PRUint32 elsize)
+{
+ PRUint32 size = nelem * elsize;
+ void *p = pr_ZoneMalloc(size);
+ if (p) {
+ memset(p, 0, size);
+ }
+ return p;
+}
+
+static void *
+pr_ZoneRealloc(void *oldptr, PRUint32 bytes)
+{
+ void *rv;
+ MemBlockHdr *mb;
+ int ours;
+ MemBlockHdr phony;
+
+ if (!oldptr) {
+ return pr_ZoneMalloc(bytes);
+ }
+ mb = (MemBlockHdr *)((char *)oldptr - (sizeof *mb));
+ if (mb->s.magic != ZONE_MAGIC) {
+ /* Maybe this just came from ordinary malloc */
+#ifdef DEBUG
+ fprintf(stderr,
+ "Warning: reallocing memory block %p from ordinary malloc\n",
+ oldptr);
+#endif
+ /*
+ * We are going to realloc oldptr. If realloc succeeds, the
+ * original value of oldptr will point to freed memory. So this
+ * function must not fail after a successfull realloc call. We
+ * must perform any operation that may fail before the realloc
+ * call.
+ */
+ rv = pr_ZoneMalloc(bytes); /* this may fail */
+ if (!rv) {
+ return rv;
+ }
+
+ /* We don't know how big it is. But we can fix that. */
+ oldptr = realloc(oldptr, bytes);
+ /*
+ * If realloc returns NULL, this function loses the original
+ * value of oldptr. This isn't a leak because the caller of
+ * this function still has the original value of oldptr.
+ */
+ if (!oldptr) {
+ if (bytes) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ pr_ZoneFree(rv);
+ return oldptr;
+ }
+ }
+ phony.s.requestedSize = bytes;
+ mb = &phony;
+ ours = 0;
+ } else {
+ size_t blockSize = mb->s.blockSize;
+ MemBlockHdr *mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize);
+
+ PR_ASSERT(mt->s.magic == ZONE_MAGIC);
+ PR_ASSERT(mt->s.zone == mb->s.zone);
+ PR_ASSERT(mt->s.blockSize == blockSize);
+
+ if (bytes <= blockSize) {
+ /* The block is already big enough. */
+ mt->s.requestedSize = mb->s.requestedSize = bytes;
+ return oldptr;
+ }
+ ours = 1;
+ rv = pr_ZoneMalloc(bytes);
+ if (!rv) {
+ return rv;
+ }
+ }
+
+ if (oldptr && mb->s.requestedSize) {
+ memcpy(rv, oldptr, mb->s.requestedSize);
+ }
+ if (ours) {
+ pr_ZoneFree(oldptr);
+ }
+ else if (oldptr) {
+ free(oldptr);
+ }
+ return rv;
+}
+
+static void
+pr_ZoneFree(void *ptr)
+{
+ MemBlockHdr *mb, *mt;
+ MemoryZone *mz;
+ size_t blockSize;
+ PRUint32 wasLocked;
+
+ if (!ptr) {
+ return;
+ }
+
+ mb = (MemBlockHdr *)((char *)ptr - (sizeof *mb));
+
+ if (mb->s.magic != ZONE_MAGIC) {
+ /* maybe this came from ordinary malloc */
+#ifdef DEBUG
+ fprintf(stderr,
+ "Warning: freeing memory block %p from ordinary malloc\n", ptr);
+#endif
+ free(ptr);
+ return;
+ }
+
+ blockSize = mb->s.blockSize;
+ mz = mb->s.zone;
+ mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize);
+ PR_ASSERT(mt->s.magic == ZONE_MAGIC);
+ PR_ASSERT(mt->s.zone == mz);
+ PR_ASSERT(mt->s.blockSize == blockSize);
+ if (!mz) {
+ PR_ASSERT(blockSize > 65536);
+ /* This block was not in any zone. Just free it. */
+ free(mb);
+ return;
+ }
+ PR_ASSERT(mz->blockSize == blockSize);
+ wasLocked = mz->locked;
+ pthread_mutex_lock(&mz->lock);
+ mz->locked = 1;
+ if (wasLocked) {
+ mz->contention++;
+ }
+ mt->s.next = mb->s.next = mz->head; /* put on head of list */
+ mz->head = mb;
+ mz->elements++;
+ mz->locked = 0;
+ pthread_mutex_unlock(&mz->lock);
+}
+
+PR_IMPLEMENT(void *) PR_Malloc(PRUint32 size)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ return use_zone_allocator ? pr_ZoneMalloc(size) : malloc(size);
+}
+
+PR_IMPLEMENT(void *) PR_Calloc(PRUint32 nelem, PRUint32 elsize)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ return use_zone_allocator ?
+ pr_ZoneCalloc(nelem, elsize) : calloc(nelem, elsize);
+}
+
+PR_IMPLEMENT(void *) PR_Realloc(void *ptr, PRUint32 size)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ return use_zone_allocator ? pr_ZoneRealloc(ptr, size) : realloc(ptr, size);
+}
+
+PR_IMPLEMENT(void) PR_Free(void *ptr)
+{
+ if (use_zone_allocator) {
+ pr_ZoneFree(ptr);
+ }
+ else {
+ free(ptr);
+ }
+}
+
+#else /* !defined(_PR_ZONE_ALLOCATOR) */
+
+/*
+** The PR_Malloc, PR_Calloc, PR_Realloc, and PR_Free functions simply
+** call their libc equivalents now. This may seem redundant, but it
+** ensures that we are calling into the same runtime library. On
+** Win32, it is possible to have multiple runtime libraries (e.g.,
+** objects compiled with /MD and /MDd) in the same process, and
+** they maintain separate heaps, which cannot be mixed.
+*/
+PR_IMPLEMENT(void *) PR_Malloc(PRUint32 size)
+{
+#if defined (WIN16)
+ return PR_MD_malloc( (size_t) size);
+#else
+ return malloc(size);
+#endif
+}
+
+PR_IMPLEMENT(void *) PR_Calloc(PRUint32 nelem, PRUint32 elsize)
+{
+#if defined (WIN16)
+ return PR_MD_calloc( (size_t)nelem, (size_t)elsize );
+
+#else
+ return calloc(nelem, elsize);
+#endif
+}
+
+PR_IMPLEMENT(void *) PR_Realloc(void *ptr, PRUint32 size)
+{
+#if defined (WIN16)
+ return PR_MD_realloc( ptr, (size_t) size);
+#else
+ return realloc(ptr, size);
+#endif
+}
+
+PR_IMPLEMENT(void) PR_Free(void *ptr)
+{
+#if defined (WIN16)
+ PR_MD_free( ptr );
+#else
+ free(ptr);
+#endif
+}
+
+#endif /* _PR_ZONE_ALLOCATOR */
+
+/*
+** Complexity alert!
+**
+** If malloc/calloc/free (etc.) were implemented to use pr lock's then
+** the entry points could block when called if some other thread had the
+** lock.
+**
+** Most of the time this isn't a problem. However, in the case that we
+** are using the thread safe malloc code after PR_Init but before
+** PR_AttachThread has been called (on a native thread that nspr has yet
+** to be told about) we could get royally screwed if the lock was busy
+** and we tried to context switch the thread away. In this scenario
+** PR_CURRENT_THREAD() == NULL
+**
+** To avoid this unfortunate case, we use the low level locking
+** facilities for malloc protection instead of the slightly higher level
+** locking. This makes malloc somewhat faster so maybe it's a good thing
+** anyway.
+*/
+#ifdef _PR_OVERRIDE_MALLOC
+
+/* Imports */
+extern void *_PR_UnlockedMalloc(size_t size);
+extern void *_PR_UnlockedMemalign(size_t alignment, size_t size);
+extern void _PR_UnlockedFree(void *ptr);
+extern void *_PR_UnlockedRealloc(void *ptr, size_t size);
+extern void *_PR_UnlockedCalloc(size_t n, size_t elsize);
+
+static PRBool _PR_malloc_initialised = PR_FALSE;
+
+#ifdef _PR_PTHREADS
+static pthread_mutex_t _PR_MD_malloc_crustylock;
+
+#define _PR_Lock_Malloc() { \
+ if(PR_TRUE == _PR_malloc_initialised) { \
+ PRStatus rv; \
+ rv = pthread_mutex_lock(&_PR_MD_malloc_crustylock); \
+ PR_ASSERT(0 == rv); \
+ }
+
+#define _PR_Unlock_Malloc() if(PR_TRUE == _PR_malloc_initialised) { \
+ PRStatus rv; \
+ rv = pthread_mutex_unlock(&_PR_MD_malloc_crustylock); \
+ PR_ASSERT(0 == rv); \
+ } \
+ }
+#else /* _PR_PTHREADS */
+static _MDLock _PR_MD_malloc_crustylock;
+
+#define _PR_Lock_Malloc() { \
+ PRIntn _is; \
+ if(PR_TRUE == _PR_malloc_initialised) { \
+ if (_PR_MD_CURRENT_THREAD() && \
+ !_PR_IS_NATIVE_THREAD( \
+ _PR_MD_CURRENT_THREAD())) \
+ _PR_INTSOFF(_is); \
+ _PR_MD_LOCK(&_PR_MD_malloc_crustylock); \
+ }
+
+#define _PR_Unlock_Malloc() if(PR_TRUE == _PR_malloc_initialised) { \
+ _PR_MD_UNLOCK(&_PR_MD_malloc_crustylock); \
+ if (_PR_MD_CURRENT_THREAD() && \
+ !_PR_IS_NATIVE_THREAD( \
+ _PR_MD_CURRENT_THREAD())) \
+ _PR_INTSON(_is); \
+ } \
+ }
+#endif /* _PR_PTHREADS */
+
+PR_IMPLEMENT(PRStatus) _PR_MallocInit(void)
+{
+ PRStatus rv = PR_SUCCESS;
+
+ if( PR_TRUE == _PR_malloc_initialised ) {
+ return PR_SUCCESS;
+ }
+
+#ifdef _PR_PTHREADS
+ {
+ int status;
+ pthread_mutexattr_t mattr;
+
+ status = _PT_PTHREAD_MUTEXATTR_INIT(&mattr);
+ PR_ASSERT(0 == status);
+ status = _PT_PTHREAD_MUTEX_INIT(_PR_MD_malloc_crustylock, mattr);
+ PR_ASSERT(0 == status);
+ status = _PT_PTHREAD_MUTEXATTR_DESTROY(&mattr);
+ PR_ASSERT(0 == status);
+ }
+#else /* _PR_PTHREADS */
+ _MD_NEW_LOCK(&_PR_MD_malloc_crustylock);
+#endif /* _PR_PTHREADS */
+
+ if( PR_SUCCESS == rv )
+ {
+ _PR_malloc_initialised = PR_TRUE;
+ }
+
+ return rv;
+}
+
+void *malloc(size_t size)
+{
+ void *p;
+ _PR_Lock_Malloc();
+ p = _PR_UnlockedMalloc(size);
+ _PR_Unlock_Malloc();
+ return p;
+}
+
+void free(void *ptr)
+{
+ _PR_Lock_Malloc();
+ _PR_UnlockedFree(ptr);
+ _PR_Unlock_Malloc();
+}
+
+void *realloc(void *ptr, size_t size)
+{
+ void *p;
+ _PR_Lock_Malloc();
+ p = _PR_UnlockedRealloc(ptr, size);
+ _PR_Unlock_Malloc();
+ return p;
+}
+
+void *calloc(size_t n, size_t elsize)
+{
+ void *p;
+ _PR_Lock_Malloc();
+ p = _PR_UnlockedCalloc(n, elsize);
+ _PR_Unlock_Malloc();
+ return p;
+}
+
+void cfree(void *p)
+{
+ _PR_Lock_Malloc();
+ _PR_UnlockedFree(p);
+ _PR_Unlock_Malloc();
+}
+
+void _PR_InitMem(void)
+{
+ PRStatus rv;
+ rv = _PR_MallocInit();
+ PR_ASSERT(PR_SUCCESS == rv);
+}
+
+#endif /* _PR_OVERRIDE_MALLOC */
diff --git a/nsprpub/pr/src/md/Makefile.in b/nsprpub/pr/src/md/Makefile.in
new file mode 100644
index 0000000000..e72bc65ab4
--- /dev/null
+++ b/nsprpub/pr/src/md/Makefile.in
@@ -0,0 +1,32 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+DIRS = $(PR_MD_ARCH_DIR)
+
+CSRCS = \
+ prosdep.c \
+ $(NULL)
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include
+
+DEFINES += -D_NSPR_BUILD_
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
diff --git a/nsprpub/pr/src/md/os2/Makefile.in b/nsprpub/pr/src/md/os2/Makefile.in
new file mode 100644
index 0000000000..9a7648ef8b
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/Makefile.in
@@ -0,0 +1,47 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+ifeq ($(OS_TARGET), OS2)
+CSRCS = \
+ os2misc.c \
+ os2sem.c \
+ os2inrval.c \
+ os2gc.c \
+ os2thred.c \
+ os2io.c \
+ os2cv.c \
+ os2sock.c \
+ os2_errors.c \
+ os2poll.c \
+ os2rng.c \
+ $(NULL)
+endif
+
+ASFILES = os2emx.s os2vaclegacy.s
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
+
+
+
diff --git a/nsprpub/pr/src/md/os2/objs.mk b/nsprpub/pr/src/md/os2/objs.mk
new file mode 100644
index 0000000000..c1a40e3f7d
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/objs.mk
@@ -0,0 +1,27 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# This makefile appends to the variable OBJS the platform-dependent
+# object modules that will be part of the nspr20 library.
+
+CSRCS = \
+ os2io.c \
+ os2sock.c \
+ os2thred.c \
+ os2cv.c \
+ os2gc.c \
+ os2misc.c \
+ os2inrval.c \
+ os2sem.c \
+ os2_errors.c \
+ os2poll.c \
+ os2rng.c \
+ $(NULL)
+
+ASFILES = os2emx.s os2vaclegacy.s
+
+OBJS += $(addprefix md/os2/$(OBJDIR)/,$(CSRCS:.c=.$(OBJ_SUFFIX))) \
+ $(addprefix md/os2/$(OBJDIR)/,$(ASFILES:.$(ASM_SUFFIX)=.$(OBJ_SUFFIX)))
+
diff --git a/nsprpub/pr/src/md/os2/os2_errors.c b/nsprpub/pr/src/md/os2/os2_errors.c
new file mode 100644
index 0000000000..dfcca6108d
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/os2_errors.c
@@ -0,0 +1,1095 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prerror.h"
+#include "primpl.h"
+
+void _MD_os2_map_default_error(PRInt32 err)
+{
+ switch (err) {
+ case EWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EMSGSIZE:
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case EISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+void _MD_os2_map_opendir_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ case ERROR_INVALID_ACCESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ case ERROR_INVALID_PARAMETER:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ case ERROR_NOT_DOS_DISK:
+ case ERROR_NOT_READY:
+ case ERROR_OPEN_FAILED:
+ case ERROR_PATH_BUSY:
+ case ERROR_CANNOT_MAKE:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_DEVICE_IN_USE:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_SHARING_BUFFER_EXCEEDED:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_closedir_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_ACCESS_DENIED:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_readdir_error(PRInt32 err)
+{
+
+ switch (err) {
+ case ERROR_NO_MORE_FILES:
+ PR_SetError(PR_NO_MORE_FILES_ERROR, err);
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_DOS_DISK:
+ case ERROR_LOCK_VIOLATION:
+ case ERROR_BROKEN_PIPE:
+ case ERROR_NOT_READY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_delete_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+/* The error code for stat() is in errno. */
+void _MD_os2_map_stat_error(PRInt32 err)
+{
+ switch (err) {
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ }
+}
+
+void _MD_os2_map_fstat_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_rename_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+/* The error code for access() is in errno. */
+void _MD_os2_map_access_error(PRInt32 err)
+{
+ switch (err) {
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ }
+}
+
+void _MD_os2_map_mkdir_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_rmdir_error(PRInt32 err)
+{
+
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_read_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_transmitfile_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_write_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ case ENOSPC:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EMSGSIZE:
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case EISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_lseek_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_SEEK_ON_DEVICE:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_fsync_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_close_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_INVALID_HANDLE:
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_socket_error(PRInt32 err)
+{
+ switch (err) {
+ case EPROTONOSUPPORT:
+ PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_recv_error(PRInt32 err)
+{
+ switch (err) {
+ case EWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_recvfrom_error(PRInt32 err)
+{
+ switch (err) {
+ case EWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_send_error(PRInt32 err)
+{
+ switch (err) {
+ case EWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EMSGSIZE:
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case EISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_sendto_error(PRInt32 err)
+{
+ _MD_os2_map_default_error(err);
+}
+
+void _MD_os2_map_writev_error(int err)
+{
+ _MD_os2_map_default_error(err);
+}
+
+void _MD_os2_map_accept_error(PRInt32 err)
+{
+ _MD_os2_map_default_error(err);
+}
+
+void _MD_os2_map_acceptex_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+/*
+ * An error code of 0 means that the nonblocking connect succeeded.
+ */
+
+int _MD_os2_get_nonblocking_connect_error(int osfd)
+{
+ int err;
+ int len = sizeof(err);
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char *) &err, &len) == -1) {
+ return sock_errno();
+ } else {
+ return err;
+ }
+}
+
+void _MD_os2_map_connect_error(PRInt32 err)
+{
+ switch (err) {
+ case EWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EINPROGRESS:
+ PR_SetError(PR_IN_PROGRESS_ERROR, err);
+ break;
+ case EALREADY:
+ case EINVAL:
+ PR_SetError(PR_ALREADY_INITIATED_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EAFNOSUPPORT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case ETIMEDOUT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, err);
+ break;
+ case ECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case ENETUNREACH:
+ PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err);
+ break;
+ case EADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case EISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_bind_error(PRInt32 err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ case EADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case EADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_SOCKET_ADDRESS_IS_BOUND_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_listen_error(PRInt32 err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EOPNOTSUPP:
+ PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_shutdown_error(PRInt32 err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case ENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_socketpair_error(PRInt32 err)
+{
+ switch (err) {
+ case ENOMEM:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case EAFNOSUPPORT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case EPROTONOSUPPORT:
+ PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err);
+ break;
+ case EOPNOTSUPP:
+ PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err);
+ break;
+ case EPROTOTYPE:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ default:
+ _MD_os2_map_default_error(err);
+ return;
+ }
+}
+
+void _MD_os2_map_getsockname_error(PRInt32 err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_getpeername_error(PRInt32 err)
+{
+
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case ENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_getsockopt_error(PRInt32 err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case ENOPROTOOPT:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ case EINVAL:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_setsockopt_error(PRInt32 err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case ENOPROTOOPT:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ case EINVAL:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_open_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_OPEN_FAILED:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_gethostname_error(PRInt32 err)
+{
+ switch (err) {
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#endif
+ case ENETDOWN:
+ case EINPROGRESS:
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_select_error(PRInt32 err)
+{
+ PRErrorCode prerror;
+
+ switch (err) {
+ /*
+ * OS/2 select() only works on sockets. So in this
+ * context, ENOTSOCK is equivalent to EBADF on Unix.
+ */
+ case ENOTSOCK:
+ prerror = PR_BAD_DESCRIPTOR_ERROR;
+ break;
+ case EINVAL:
+ prerror = PR_INVALID_ARGUMENT_ERROR;
+ break;
+#ifdef SOCEFAULT
+ case SOCEFAULT:
+ prerror = PR_ACCESS_FAULT_ERROR;
+ break;
+#endif
+ default:
+ prerror = PR_UNKNOWN_ERROR;
+ }
+ PR_SetError(prerror, err);
+}
+
+void _MD_os2_map_lockf_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
diff --git a/nsprpub/pr/src/md/os2/os2cv.c b/nsprpub/pr/src/md/os2/os2cv.c
new file mode 100644
index 0000000000..91e85c0ebd
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/os2cv.c
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * os2cv.c -- OS/2 Machine-Dependent Code for Condition Variables
+ *
+ * We implement our own condition variable wait queue. Each thread
+ * has a semaphore object (thread->md.blocked_sema) to block on while
+ * waiting on a condition variable.
+ *
+ * We use a deferred condition notify algorithm. When PR_NotifyCondVar
+ * or PR_NotifyAllCondVar is called, the condition notifies are simply
+ * recorded in the _MDLock structure. We defer the condition notifies
+ * until right after we unlock the lock. This way the awakened threads
+ * have a better chance to reaquire the lock.
+ */
+
+#include "primpl.h"
+
+/*
+ * AddThreadToCVWaitQueueInternal --
+ *
+ * Add the thread to the end of the condition variable's wait queue.
+ * The CV's lock must be locked when this function is called.
+ */
+
+static void
+AddThreadToCVWaitQueueInternal(PRThread *thred, struct _MDCVar *cv)
+{
+ PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
+ || (cv->waitTail == NULL && cv->waitHead == NULL));
+ cv->nwait += 1;
+ thred->md.inCVWaitQueue = PR_TRUE;
+ thred->md.next = NULL;
+ thred->md.prev = cv->waitTail;
+ if (cv->waitHead == NULL) {
+ cv->waitHead = thred;
+ } else {
+ cv->waitTail->md.next = thred;
+ }
+ cv->waitTail = thred;
+}
+
+/*
+ * md_UnlockAndPostNotifies --
+ *
+ * Unlock the lock, and then do the deferred condition notifies.
+ * If waitThred and waitCV are not NULL, waitThred is added to
+ * the wait queue of waitCV before the lock is unlocked.
+ *
+ * This function is called by _PR_MD_WAIT_CV and _PR_MD_UNLOCK,
+ * the two places where a lock is unlocked.
+ */
+void
+md_UnlockAndPostNotifies(
+ _MDLock *lock,
+ PRThread *waitThred,
+ _MDCVar *waitCV)
+{
+ PRIntn index;
+ _MDNotified post;
+ _MDNotified *notified, *prev = NULL;
+
+ /*
+ * Time to actually notify any conditions that were affected
+ * while the lock was held. Get a copy of the list that's in
+ * the lock structure and then zero the original. If it's
+ * linked to other such structures, we own that storage.
+ */
+ post = lock->notified; /* a safe copy; we own the lock */
+
+#if defined(DEBUG)
+ memset(&lock->notified, 0, sizeof(_MDNotified)); /* reset */
+#else
+ lock->notified.length = 0; /* these are really sufficient */
+ lock->notified.link = NULL;
+#endif
+
+ /*
+ * Figure out how many threads we need to wake up.
+ */
+ notified = &post; /* this is where we start */
+ do {
+ for (index = 0; index < notified->length; ++index) {
+ _MDCVar *cv = notified->cv[index].cv;
+ PRThread *thred;
+ int i;
+
+ /* Fast special case: no waiting threads */
+ if (cv->waitHead == NULL) {
+ notified->cv[index].notifyHead = NULL;
+ continue;
+ }
+
+ /* General case */
+ if (-1 == notified->cv[index].times) {
+ /* broadcast */
+ thred = cv->waitHead;
+ while (thred != NULL) {
+ thred->md.inCVWaitQueue = PR_FALSE;
+ thred = thred->md.next;
+ }
+ notified->cv[index].notifyHead = cv->waitHead;
+ cv->waitHead = cv->waitTail = NULL;
+ cv->nwait = 0;
+ } else {
+ thred = cv->waitHead;
+ i = notified->cv[index].times;
+ while (thred != NULL && i > 0) {
+ thred->md.inCVWaitQueue = PR_FALSE;
+ thred = thred->md.next;
+ i--;
+ }
+ notified->cv[index].notifyHead = cv->waitHead;
+ cv->waitHead = thred;
+ if (cv->waitHead == NULL) {
+ cv->waitTail = NULL;
+ } else {
+ if (cv->waitHead->md.prev != NULL) {
+ cv->waitHead->md.prev->md.next = NULL;
+ cv->waitHead->md.prev = NULL;
+ }
+ }
+ cv->nwait -= notified->cv[index].times - i;
+ }
+ }
+ notified = notified->link;
+ } while (NULL != notified);
+
+ if (waitThred) {
+ AddThreadToCVWaitQueueInternal(waitThred, waitCV);
+ }
+
+ /* Release the lock before notifying */
+ DosReleaseMutexSem(lock->mutex);
+
+ notified = &post; /* this is where we start */
+ do {
+ for (index = 0; index < notified->length; ++index) {
+ PRThread *thred;
+ PRThread *next;
+
+ thred = notified->cv[index].notifyHead;
+ while (thred != NULL) {
+ BOOL rv;
+
+ next = thred->md.next;
+ thred->md.prev = thred->md.next = NULL;
+ rv = DosPostEventSem(thred->md.blocked_sema);
+ PR_ASSERT(rv == NO_ERROR);
+ thred = next;
+ }
+ }
+ prev = notified;
+ notified = notified->link;
+ if (&post != prev) {
+ PR_DELETE(prev);
+ }
+ } while (NULL != notified);
+}
+
+/*
+ * Notifies just get posted to the protecting mutex. The
+ * actual notification is done when the lock is released so that
+ * MP systems don't contend for a lock that they can't have.
+ */
+static void md_PostNotifyToCvar(_MDCVar *cvar, _MDLock *lock,
+ PRBool broadcast)
+{
+ PRIntn index = 0;
+ _MDNotified *notified = &lock->notified;
+
+ while (1) {
+ for (index = 0; index < notified->length; ++index) {
+ if (notified->cv[index].cv == cvar) {
+ if (broadcast) {
+ notified->cv[index].times = -1;
+ } else if (-1 != notified->cv[index].times) {
+ notified->cv[index].times += 1;
+ }
+ return;
+ }
+ }
+ /* if not full, enter new CV in this array */
+ if (notified->length < _MD_CV_NOTIFIED_LENGTH) {
+ break;
+ }
+
+ /* if there's no link, create an empty array and link it */
+ if (NULL == notified->link) {
+ notified->link = PR_NEWZAP(_MDNotified);
+ }
+
+ notified = notified->link;
+ }
+
+ /* A brand new entry in the array */
+ notified->cv[index].times = (broadcast) ? -1 : 1;
+ notified->cv[index].cv = cvar;
+ notified->length += 1;
+}
+
+/*
+ * _PR_MD_NEW_CV() -- Creating new condition variable
+ * ... Solaris uses cond_init() in similar function.
+ *
+ * returns: -1 on failure
+ * 0 when it succeeds.
+ *
+ */
+PRInt32
+_PR_MD_NEW_CV(_MDCVar *cv)
+{
+ cv->magic = _MD_MAGIC_CV;
+ /*
+ * The waitHead, waitTail, and nwait fields are zeroed
+ * when the PRCondVar structure is created.
+ */
+ return 0;
+}
+
+void _PR_MD_FREE_CV(_MDCVar *cv)
+{
+ cv->magic = (PRUint32)-1;
+ return;
+}
+
+/*
+ * _PR_MD_WAIT_CV() -- Wait on condition variable
+ */
+void
+_PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout )
+{
+ PRThread *thred = _PR_MD_CURRENT_THREAD();
+ ULONG rv, count;
+ ULONG msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ?
+ SEM_INDEFINITE_WAIT : PR_IntervalToMilliseconds(timeout);
+
+ /*
+ * If we have pending notifies, post them now.
+ */
+ if (0 != lock->notified.length) {
+ md_UnlockAndPostNotifies(lock, thred, cv);
+ } else {
+ AddThreadToCVWaitQueueInternal(thred, cv);
+ DosReleaseMutexSem(lock->mutex);
+ }
+
+ /* Wait for notification or timeout; don't really care which */
+ rv = DosWaitEventSem(thred->md.blocked_sema, msecs);
+ if (rv == NO_ERROR) {
+ DosResetEventSem(thred->md.blocked_sema, &count);
+ }
+
+ DosRequestMutexSem((lock->mutex), SEM_INDEFINITE_WAIT);
+
+ PR_ASSERT(rv == NO_ERROR || rv == ERROR_TIMEOUT);
+
+ if(rv == ERROR_TIMEOUT)
+ {
+ if (thred->md.inCVWaitQueue) {
+ PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
+ || (cv->waitTail == NULL && cv->waitHead == NULL));
+ cv->nwait -= 1;
+ thred->md.inCVWaitQueue = PR_FALSE;
+ if (cv->waitHead == thred) {
+ cv->waitHead = thred->md.next;
+ if (cv->waitHead == NULL) {
+ cv->waitTail = NULL;
+ } else {
+ cv->waitHead->md.prev = NULL;
+ }
+ } else {
+ PR_ASSERT(thred->md.prev != NULL);
+ thred->md.prev->md.next = thred->md.next;
+ if (thred->md.next != NULL) {
+ thred->md.next->md.prev = thred->md.prev;
+ } else {
+ PR_ASSERT(cv->waitTail == thred);
+ cv->waitTail = thred->md.prev;
+ }
+ }
+ thred->md.next = thred->md.prev = NULL;
+ } else {
+ /*
+ * This thread must have been notified, but the
+ * SemRelease call happens after SemRequest
+ * times out. Wait on the semaphore again to make it
+ * non-signaled. We assume this wait won't take long.
+ */
+ rv = DosWaitEventSem(thred->md.blocked_sema, SEM_INDEFINITE_WAIT);
+ if (rv == NO_ERROR) {
+ DosResetEventSem(thred->md.blocked_sema, &count);
+ }
+ PR_ASSERT(rv == NO_ERROR);
+ }
+ }
+ PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE);
+ return;
+} /* --- end _PR_MD_WAIT_CV() --- */
+
+void
+_PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock)
+{
+ md_PostNotifyToCvar(cv, lock, PR_FALSE);
+ return;
+}
+
+PRStatus
+_PR_MD_NEW_LOCK(_MDLock *lock)
+{
+ DosCreateMutexSem(0, &(lock->mutex), 0, 0);
+ (lock)->notified.length=0;
+ (lock)->notified.link=NULL;
+ return PR_SUCCESS;
+}
+
+void
+_PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock)
+{
+ md_PostNotifyToCvar(cv, lock, PR_TRUE);
+ return;
+}
+
+void _PR_MD_UNLOCK(_MDLock *lock)
+{
+ if (0 != lock->notified.length) {
+ md_UnlockAndPostNotifies(lock, NULL, NULL);
+ } else {
+ DosReleaseMutexSem(lock->mutex);
+ }
+}
diff --git a/nsprpub/pr/src/md/os2/os2emx.s b/nsprpub/pr/src/md/os2/os2emx.s
new file mode 100644
index 0000000000..ef18e3bb14
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/os2emx.s
@@ -0,0 +1,82 @@
+/ -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+/
+/ This Source Code Form is subject to the terms of the Mozilla Public
+/ License, v. 2.0. If a copy of the MPL was not distributed with this
+/ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/ PRInt32 __PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
+/
+/ Atomically increment the integer pointed to by 'val' and return
+/ the result of the increment.
+/
+ .text
+ .globl __PR_MD_ATOMIC_INCREMENT
+ .align 4
+__PR_MD_ATOMIC_INCREMENT:
+ movl 4(%esp), %ecx
+ movl $1, %eax
+ lock
+ xaddl %eax, (%ecx)
+ incl %eax
+ ret
+
+/ PRInt32 __PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
+/
+/ Atomically decrement the integer pointed to by 'val' and return
+/ the result of the decrement.
+/
+ .text
+ .globl __PR_MD_ATOMIC_DECREMENT
+ .align 4
+__PR_MD_ATOMIC_DECREMENT:
+ movl 4(%esp), %ecx
+ movl $-1, %eax
+ lock
+ xaddl %eax, (%ecx)
+ decl %eax
+ ret
+
+/ PRInt32 __PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+/
+/ Atomically set the integer pointed to by 'val' to the new
+/ value 'newval' and return the old value.
+/
+/ An alternative implementation:
+/ .text
+/ .globl __PR_MD_ATOMIC_SET
+/ .align 4
+/__PR_MD_ATOMIC_SET:
+/ movl 4(%esp), %ecx
+/ movl 8(%esp), %edx
+/ movl (%ecx), %eax
+/retry:
+/ lock
+/ cmpxchgl %edx, (%ecx)
+/ jne retry
+/ ret
+/
+ .text
+ .globl __PR_MD_ATOMIC_SET
+ .align 4
+__PR_MD_ATOMIC_SET:
+ movl 4(%esp), %ecx
+ movl 8(%esp), %eax
+ xchgl %eax, (%ecx)
+ ret
+
+/ PRInt32 __PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
+/
+/ Atomically add 'val' to the integer pointed to by 'ptr'
+/ and return the result of the addition.
+/
+ .text
+ .globl __PR_MD_ATOMIC_ADD
+ .align 4
+__PR_MD_ATOMIC_ADD:
+ movl 4(%esp), %ecx
+ movl 8(%esp), %eax
+ movl %eax, %edx
+ lock
+ xaddl %eax, (%ecx)
+ addl %edx, %eax
+ ret
diff --git a/nsprpub/pr/src/md/os2/os2gc.c b/nsprpub/pr/src/md/os2/os2gc.c
new file mode 100644
index 0000000000..79b6de5736
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/os2gc.c
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * GC related routines
+ *
+ */
+#include "primpl.h"
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ CONTEXTRECORD context;
+ context.ContextFlags = CONTEXT_INTEGER;
+
+ if (_PR_IS_NATIVE_THREAD(t)) {
+ context.ContextFlags |= CONTEXT_CONTROL;
+ if (QueryThreadContext(t->md.handle, CONTEXT_CONTROL, &context)) {
+ t->md.gcContext[0] = context.ctx_RegEax;
+ t->md.gcContext[1] = context.ctx_RegEbx;
+ t->md.gcContext[2] = context.ctx_RegEcx;
+ t->md.gcContext[3] = context.ctx_RegEdx;
+ t->md.gcContext[4] = context.ctx_RegEsi;
+ t->md.gcContext[5] = context.ctx_RegEdi;
+ t->md.gcContext[6] = context.ctx_RegEsp;
+ t->md.gcContext[7] = context.ctx_RegEbp;
+ *np = PR_NUM_GCREGS;
+ } else {
+ PR_ASSERT(0);/* XXX */
+ }
+ }
+ return (PRWord *)&t->md.gcContext;
+}
+
+/* This function is not used right now, but is left as a reference.
+ * If you ever need to get the fiberID from the currently running fiber,
+ * this is it.
+ */
+void *
+GetMyFiberID()
+{
+ void *fiberData = 0;
+
+ /* A pointer to our tib entry is found at FS:[18]
+ * At offset 10h is the fiberData pointer. The context of the
+ * fiber is stored in there.
+ */
+#ifdef HAVE_ASM
+ __asm {
+ mov EDX, FS:[18h]
+ mov EAX, DWORD PTR [EDX+10h]
+ mov [fiberData], EAX
+ }
+#endif
+
+ return fiberData;
+}
diff --git a/nsprpub/pr/src/md/os2/os2inrval.c b/nsprpub/pr/src/md/os2/os2inrval.c
new file mode 100644
index 0000000000..bd283f06d9
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/os2inrval.c
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * OS/2 interval timers
+ *
+ */
+
+#include "primpl.h"
+
+static PRBool useHighResTimer = PR_FALSE;
+PRIntervalTime _os2_ticksPerSec = -1;
+PRIntn _os2_bitShift = 0;
+PRInt32 _os2_highMask = 0;
+
+void
+_PR_MD_INTERVAL_INIT()
+{
+ char *envp;
+ ULONG timerFreq;
+ APIRET rc;
+
+ if ((envp = getenv("NSPR_OS2_NO_HIRES_TIMER")) != NULL) {
+ if (atoi(envp) == 1) {
+ return;
+ }
+ }
+
+ timerFreq = 0; /* OS/2 high-resolution timer frequency in Hz */
+ rc = DosTmrQueryFreq(&timerFreq);
+ if (NO_ERROR == rc) {
+ useHighResTimer = PR_TRUE;
+ PR_ASSERT(timerFreq != 0);
+ while (timerFreq > PR_INTERVAL_MAX) {
+ timerFreq >>= 1;
+ _os2_bitShift++;
+ _os2_highMask = (_os2_highMask << 1)+1;
+ }
+
+ _os2_ticksPerSec = timerFreq;
+ PR_ASSERT(_os2_ticksPerSec > PR_INTERVAL_MIN);
+ }
+}
+
+PRIntervalTime
+_PR_MD_GET_INTERVAL()
+{
+ if (useHighResTimer) {
+ QWORD timestamp;
+ PRInt32 top;
+ APIRET rc = DosTmrQueryTime(&timestamp);
+ if (NO_ERROR != rc) {
+ return -1;
+ }
+ /* Sadly, nspr requires the interval to range from 1000 ticks per
+ * second to only 100000 ticks per second. DosTmrQueryTime is too
+ * high resolution...
+ */
+ top = timestamp.ulHi & _os2_highMask;
+ top = top << (32 - _os2_bitShift);
+ timestamp.ulLo = timestamp.ulLo >> _os2_bitShift;
+ timestamp.ulLo = timestamp.ulLo + top;
+ return (PRUint32)timestamp.ulLo;
+ } else {
+ ULONG msCount = -1;
+ DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &msCount, sizeof(msCount));
+ return msCount;
+ }
+}
+
+PRIntervalTime
+_PR_MD_INTERVAL_PER_SEC()
+{
+ if (useHighResTimer) {
+ return _os2_ticksPerSec;
+ } else {
+ return 1000;
+ }
+}
diff --git a/nsprpub/pr/src/md/os2/os2io.c b/nsprpub/pr/src/md/os2/os2io.c
new file mode 100644
index 0000000000..639a0d3880
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/os2io.c
@@ -0,0 +1,968 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* OS2 IO module
+ *
+ * Assumes synchronous I/O.
+ *
+ */
+
+#include "primpl.h"
+#include "prio.h"
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <io.h>
+
+struct _MDLock _pr_ioq_lock;
+
+static PRBool isWSEB = PR_FALSE; /* whether we are using an OS/2 kernel that supports large files */
+
+typedef APIRET (*DosOpenLType)(PSZ pszFileName, PHFILE pHf, PULONG pulAction,
+ LONGLONG cbFile, ULONG ulAttribute,
+ ULONG fsOpenFlags, ULONG fsOpenMode,
+ PEAOP2 peaop2);
+
+typedef APIRET (*DosSetFileLocksLType)(HFILE hFile, PFILELOCKL pflUnlock,
+ PFILELOCKL pflLock, ULONG timeout,
+ ULONG flags);
+
+typedef APIRET (*DosSetFilePtrLType)(HFILE hFile, LONGLONG ib, ULONG method,
+ PLONGLONG ibActual);
+
+DosOpenLType myDosOpenL;
+DosSetFileLocksLType myDosSetFileLocksL;
+DosSetFilePtrLType myDosSetFilePtrL;
+
+void
+_PR_MD_INIT_IO()
+{
+ APIRET rc;
+ HMODULE module;
+
+ sock_init();
+
+ rc = DosLoadModule(NULL, 0, "DOSCALL1", &module);
+ if (rc != NO_ERROR)
+ {
+ return;
+ }
+ rc = DosQueryProcAddr(module, 981, NULL, (PFN*) &myDosOpenL);
+ if (rc != NO_ERROR)
+ {
+ return;
+ }
+ rc = DosQueryProcAddr(module, 986, NULL, (PFN*) &myDosSetFileLocksL);
+ if (rc != NO_ERROR)
+ {
+ return;
+ }
+ rc = DosQueryProcAddr(module, 988, NULL, (PFN*) &myDosSetFilePtrL);
+ if (rc != NO_ERROR)
+ {
+ return;
+ }
+ isWSEB = PR_TRUE;
+}
+
+PRStatus
+_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PRInt32 rv;
+ ULONG count;
+
+ PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
+ SEM_INDEFINITE_WAIT : PR_IntervalToMilliseconds(ticks);
+ rv = DosWaitEventSem(thread->md.blocked_sema, msecs);
+ DosResetEventSem(thread->md.blocked_sema, &count);
+ switch(rv)
+ {
+ case NO_ERROR:
+ return PR_SUCCESS;
+ break;
+ case ERROR_TIMEOUT:
+ _PR_THREAD_LOCK(thread);
+ if (thread->state == _PR_IO_WAIT) {
+ ;
+ } else {
+ if (thread->wait.cvar != NULL) {
+ thread->wait.cvar = NULL;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The CVAR was notified just as the timeout
+ * occurred. This led to us being notified twice.
+ * call SemRequest() to clear the semaphore.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ rv = DosWaitEventSem(thread->md.blocked_sema, 0);
+ DosResetEventSem(thread->md.blocked_sema, &count);
+ PR_ASSERT(rv == NO_ERROR);
+ }
+ }
+ return PR_SUCCESS;
+ break;
+ default:
+ break;
+ }
+ return PR_FAILURE;
+}
+PRStatus
+_PR_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if ( _PR_IS_NATIVE_THREAD(thread) )
+ {
+ if (DosPostEventSem(thread->md.blocked_sema) != NO_ERROR) {
+ return PR_FAILURE;
+ }
+ else {
+ return PR_SUCCESS;
+ }
+ }
+}
+
+
+/* --- FILE IO ----------------------------------------------------------- */
+/*
+ * _PR_MD_OPEN() -- Open a file
+ *
+ * returns: a fileHandle
+ *
+ * The NSPR open flags (osflags) are translated into flags for OS/2
+ *
+ * Mode seems to be passed in as a unix style file permissions argument
+ * as in 0666, in the case of opening the logFile.
+ *
+ */
+PRInt32
+_PR_MD_OPEN(const char *name, PRIntn osflags, int mode)
+{
+ HFILE file;
+ PRInt32 access = OPEN_SHARE_DENYNONE;
+ PRInt32 flags = 0L;
+ APIRET rc = 0;
+ PRUword actionTaken;
+
+#ifdef MOZ_OS2_HIGH_MEMORY
+ /*
+ * All the pointer arguments (&file, &actionTaken and name) have to be in
+ * low memory for DosOpen to use them.
+ * The following moves name to low memory.
+ */
+ if ((ULONG)name >= 0x20000000)
+ {
+ size_t len = strlen(name) + 1;
+ char *copy = (char *)alloca(len);
+ memcpy(copy, name, len);
+ name = copy;
+ }
+#endif
+
+ if (osflags & PR_SYNC) {
+ access |= OPEN_FLAGS_WRITE_THROUGH;
+ }
+
+ if (osflags & PR_RDONLY) {
+ access |= OPEN_ACCESS_READONLY;
+ }
+ else if (osflags & PR_WRONLY) {
+ access |= OPEN_ACCESS_WRITEONLY;
+ }
+ else if(osflags & PR_RDWR) {
+ access |= OPEN_ACCESS_READWRITE;
+ }
+
+ if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL )
+ {
+ flags = OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_FAIL_IF_EXISTS;
+ }
+ else if (osflags & PR_CREATE_FILE)
+ {
+ if (osflags & PR_TRUNCATE) {
+ flags = OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS;
+ }
+ else {
+ flags = OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
+ }
+ }
+ else
+ {
+ if (osflags & PR_TRUNCATE) {
+ flags = OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS;
+ }
+ else {
+ flags = OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
+ }
+ }
+
+ do {
+ if (isWSEB)
+ {
+ rc = myDosOpenL((char*)name,
+ &file, /* file handle if successful */
+ &actionTaken, /* reason for failure */
+ 0, /* initial size of new file */
+ FILE_NORMAL, /* file system attributes */
+ flags, /* Open flags */
+ access, /* Open mode and rights */
+ 0); /* OS/2 Extended Attributes */
+ }
+ else
+ {
+ rc = DosOpen((char*)name,
+ &file, /* file handle if successful */
+ &actionTaken, /* reason for failure */
+ 0, /* initial size of new file */
+ FILE_NORMAL, /* file system attributes */
+ flags, /* Open flags */
+ access, /* Open mode and rights */
+ 0); /* OS/2 Extended Attributes */
+ };
+ if (rc == ERROR_TOO_MANY_OPEN_FILES) {
+ ULONG CurMaxFH = 0;
+ LONG ReqCount = 20;
+ APIRET rc2;
+ rc2 = DosSetRelMaxFH(&ReqCount, &CurMaxFH);
+ if (rc2 != NO_ERROR) {
+ break;
+ }
+ }
+ } while (rc == ERROR_TOO_MANY_OPEN_FILES);
+
+ if (rc != NO_ERROR) {
+ _PR_MD_MAP_OPEN_ERROR(rc);
+ return -1;
+ }
+
+ return (PRInt32)file;
+}
+
+PRInt32
+_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ ULONG bytes;
+ int rv;
+
+ rv = DosRead((HFILE)fd->secret->md.osfd,
+ (PVOID)buf,
+ len,
+ &bytes);
+
+ if (rv != NO_ERROR)
+ {
+ /* ERROR_HANDLE_EOF can only be returned by async io */
+ PR_ASSERT(rv != ERROR_HANDLE_EOF);
+ if (rv == ERROR_BROKEN_PIPE) {
+ return 0;
+ }
+ else {
+ _PR_MD_MAP_READ_ERROR(rv);
+ return -1;
+ }
+ }
+ return (PRInt32)bytes;
+}
+
+PRInt32
+_PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len)
+{
+ PRInt32 bytes;
+ int rv;
+
+ rv = DosWrite((HFILE)fd->secret->md.osfd,
+ (PVOID)buf,
+ len,
+ (PULONG)&bytes);
+
+ if (rv != NO_ERROR)
+ {
+ _PR_MD_MAP_WRITE_ERROR(rv);
+ return -1;
+ }
+
+ if (len != bytes) {
+ rv = ERROR_DISK_FULL;
+ _PR_MD_MAP_WRITE_ERROR(rv);
+ return -1;
+ }
+
+ return bytes;
+} /* --- end _PR_MD_WRITE() --- */
+
+PRInt32
+_PR_MD_LSEEK(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence)
+{
+ PRInt32 rv;
+ PRUword newLocation;
+
+ rv = DosSetFilePtr((HFILE)fd->secret->md.osfd, offset, whence, &newLocation);
+
+ if (rv != NO_ERROR) {
+ _PR_MD_MAP_LSEEK_ERROR(rv);
+ return -1;
+ } else {
+ return newLocation;
+ }
+}
+
+PRInt64
+_PR_MD_LSEEK64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence)
+{
+#ifdef NO_LONG_LONG
+ PRInt64 result;
+ PRInt32 rv, low = offset.lo, hi = offset.hi;
+ PRUword newLocation;
+
+ rv = DosSetFilePtr((HFILE)fd->secret->md.osfd, low, whence, &newLocation);
+ rv = DosSetFilePtr((HFILE)fd->secret->md.osfd, hi, FILE_CURRENT, &newLocation);
+
+ if (rv != NO_ERROR) {
+ _PR_MD_MAP_LSEEK_ERROR(rv);
+ hi = newLocation = -1;
+ }
+
+ result.lo = newLocation;
+ result.hi = hi;
+ return result;
+
+#else
+ PRInt32 where, rc, lo = (PRInt32)offset, hi = (PRInt32)(offset >> 32);
+ PRUint64 rv;
+ PRUint32 newLocation, uhi;
+ PRUint64 newLocationL;
+
+ switch (whence)
+ {
+ case PR_SEEK_SET:
+ where = FILE_BEGIN;
+ break;
+ case PR_SEEK_CUR:
+ where = FILE_CURRENT;
+ break;
+ case PR_SEEK_END:
+ where = FILE_END;
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+ if (isWSEB)
+ {
+ rc = myDosSetFilePtrL((HFILE)fd->secret->md.osfd, offset, where, (PLONGLONG)&newLocationL);
+ }
+ else
+ {
+ rc = DosSetFilePtr((HFILE)fd->secret->md.osfd, lo, where, (PULONG)&newLocation);
+ }
+
+ if (rc != NO_ERROR) {
+ _PR_MD_MAP_LSEEK_ERROR(rc);
+ return -1;
+ }
+
+ if (isWSEB)
+ {
+ return newLocationL;
+ }
+
+ uhi = (PRUint32)hi;
+ PR_ASSERT((PRInt32)uhi >= 0);
+ rv = uhi;
+ PR_ASSERT((PRInt64)rv >= 0);
+ rv = (rv << 32);
+ PR_ASSERT((PRInt64)rv >= 0);
+ rv += newLocation;
+ PR_ASSERT((PRInt64)rv >= 0);
+ return (PRInt64)rv;
+#endif
+}
+
+PRInt32
+_PR_MD_FSYNC(PRFileDesc *fd)
+{
+ PRInt32 rc = DosResetBuffer((HFILE)fd->secret->md.osfd);
+
+ if (rc != NO_ERROR) {
+ if (rc != ERROR_ACCESS_DENIED) {
+ _PR_MD_MAP_FSYNC_ERROR(rc);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+PRInt32
+_MD_CloseFile(PRInt32 osfd)
+{
+ PRInt32 rv;
+
+ rv = DosClose((HFILE)osfd);
+ if (rv != NO_ERROR) {
+ _PR_MD_MAP_CLOSE_ERROR(rv);
+ }
+ return rv;
+}
+
+
+/* --- DIR IO ------------------------------------------------------------ */
+#define GetFileFromDIR(d) (isWSEB?(d)->d_entry.large.achName:(d)->d_entry.small.achName)
+#define GetFileAttr(d) (isWSEB?(d)->d_entry.large.attrFile:(d)->d_entry.small.attrFile)
+
+void FlipSlashes(char *cp, int len)
+{
+ while (--len >= 0) {
+ if (cp[0] == '/') {
+ cp[0] = PR_DIRECTORY_SEPARATOR;
+ }
+ cp++;
+ }
+}
+
+/*
+**
+** Local implementations of standard Unix RTL functions which are not provided
+** by the VAC RTL.
+**
+*/
+
+PRInt32
+_PR_MD_CLOSE_DIR(_MDDir *d)
+{
+ PRInt32 rc;
+
+ if ( d ) {
+ rc = DosFindClose(d->d_hdl);
+ if(rc == NO_ERROR) {
+ d->magic = (PRUint32)-1;
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_CLOSEDIR_ERROR(rc);
+ return PR_FAILURE;
+ }
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+}
+
+
+PRStatus
+_PR_MD_OPEN_DIR(_MDDir *d, const char *name)
+{
+ char filename[ CCHMAXPATH ];
+ PRUword numEntries, rc;
+
+ numEntries = 1;
+
+ PR_snprintf(filename, CCHMAXPATH, "%s%s%s",
+ name, PR_DIRECTORY_SEPARATOR_STR, "*.*");
+ FlipSlashes( filename, strlen(filename) );
+
+ d->d_hdl = HDIR_CREATE;
+
+ if (isWSEB)
+ {
+ rc = DosFindFirst( filename,
+ &d->d_hdl,
+ FILE_DIRECTORY | FILE_HIDDEN,
+ &(d->d_entry.large),
+ sizeof(d->d_entry.large),
+ &numEntries,
+ FIL_STANDARDL);
+ }
+ else
+ {
+ rc = DosFindFirst( filename,
+ &d->d_hdl,
+ FILE_DIRECTORY | FILE_HIDDEN,
+ &(d->d_entry.small),
+ sizeof(d->d_entry.small),
+ &numEntries,
+ FIL_STANDARD);
+ }
+ if ( rc != NO_ERROR ) {
+ _PR_MD_MAP_OPENDIR_ERROR(rc);
+ return PR_FAILURE;
+ }
+ d->firstEntry = PR_TRUE;
+ d->magic = _MD_MAGIC_DIR;
+ return PR_SUCCESS;
+}
+
+char *
+_PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
+{
+ PRUword numFiles = 1;
+ BOOL rv;
+ char *fileName;
+ USHORT fileAttr;
+
+ if ( d ) {
+ while (1) {
+ if (d->firstEntry) {
+ d->firstEntry = PR_FALSE;
+ rv = NO_ERROR;
+ } else {
+ rv = DosFindNext(d->d_hdl,
+ &(d->d_entry),
+ sizeof(d->d_entry),
+ &numFiles);
+ }
+ if (rv != NO_ERROR) {
+ break;
+ }
+ fileName = GetFileFromDIR(d);
+ fileAttr = GetFileAttr(d);
+ if ( (flags & PR_SKIP_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '\0')) {
+ continue;
+ }
+ if ( (flags & PR_SKIP_DOT_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '.') &&
+ (fileName[2] == '\0')) {
+ continue;
+ }
+ /*
+ * XXX
+ * Is this the correct definition of a hidden file on OS/2?
+ */
+ if ((flags & PR_SKIP_NONE) && (fileAttr & FILE_HIDDEN)) {
+ return fileName;
+ }
+ else if ((flags & PR_SKIP_HIDDEN) && (fileAttr & FILE_HIDDEN)) {
+ continue;
+ }
+ return fileName;
+ }
+ PR_ASSERT(NO_ERROR != rv);
+ _PR_MD_MAP_READDIR_ERROR(rv);
+ return NULL;
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+}
+
+PRInt32
+_PR_MD_DELETE(const char *name)
+{
+ PRInt32 rc = DosDelete((char*)name);
+ if(rc == NO_ERROR) {
+ return 0;
+ } else {
+ _PR_MD_MAP_DELETE_ERROR(rc);
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_STAT(const char *fn, struct stat *info)
+{
+ PRInt32 rv;
+ char filename[CCHMAXPATH];
+
+ PR_snprintf(filename, CCHMAXPATH, "%s", fn);
+ FlipSlashes(filename, strlen(filename));
+
+ rv = _stat((char*)filename, info);
+ if (-1 == rv) {
+ /*
+ * Check for MSVC runtime library _stat() bug.
+ * (It's really a bug in FindFirstFile().)
+ * If a pathname ends in a backslash or slash,
+ * e.g., c:\temp\ or c:/temp/, _stat() will fail.
+ * Note: a pathname ending in a slash (e.g., c:/temp/)
+ * can be handled by _stat() on NT but not on Win95.
+ *
+ * We remove the backslash or slash at the end and
+ * try again.
+ *
+ * Not sure if this happens on OS/2 or not,
+ * but it doesn't hurt to be careful.
+ */
+
+ int len = strlen(fn);
+ if (len > 0 && len <= _MAX_PATH
+ && (fn[len - 1] == '\\' || fn[len - 1] == '/')) {
+ char newfn[_MAX_PATH + 1];
+
+ strcpy(newfn, fn);
+ newfn[len - 1] = '\0';
+ rv = _stat(newfn, info);
+ }
+ }
+
+ if (-1 == rv) {
+ _PR_MD_MAP_STAT_ERROR(errno);
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
+{
+ struct stat sb;
+ PRInt32 rv;
+ PRInt64 s, s2us;
+
+ if ( (rv = _PR_MD_STAT(fn, &sb)) == 0 ) {
+ if (info) {
+ if (S_IFREG & sb.st_mode) {
+ info->type = PR_FILE_FILE ;
+ }
+ else if (S_IFDIR & sb.st_mode) {
+ info->type = PR_FILE_DIRECTORY;
+ }
+ else {
+ info->type = PR_FILE_OTHER;
+ }
+ info->size = sb.st_size;
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, sb.st_mtime);
+ LL_MUL(s, s, s2us);
+ info->modifyTime = s;
+ LL_I2L(s, sb.st_ctime);
+ LL_MUL(s, s, s2us);
+ info->creationTime = s;
+ }
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
+{
+ PRFileInfo info32;
+ PRInt32 rv = _PR_MD_GETFILEINFO(fn, &info32);
+ if (rv != 0)
+ {
+ return rv;
+ }
+ info->type = info32.type;
+ LL_UI2L(info->size,info32.size);
+ info->modifyTime = info32.modifyTime;
+ info->creationTime = info32.creationTime;
+
+ if (isWSEB)
+ {
+ APIRET rc ;
+ FILESTATUS3L fstatus;
+
+ rc = DosQueryPathInfo(fn, FIL_STANDARDL, &fstatus, sizeof(fstatus));
+
+ if (NO_ERROR != rc)
+ {
+ _PR_MD_MAP_OPEN_ERROR(rc);
+ return -1;
+ }
+
+ if (! (fstatus.attrFile & FILE_DIRECTORY))
+ {
+ info->size = fstatus.cbFile;
+ }
+ }
+
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
+{
+ /* For once, the VAC compiler/library did a nice thing.
+ * The file handle used by the C runtime is the same one
+ * returned by the OS when you call DosOpen(). This means
+ * that you can take an OS HFILE and use it with C file
+ * functions. The only caveat is that you have to call
+ * _setmode() first to initialize some junk. This is
+ * immensely useful because I did not have a clue how to
+ * implement this function otherwise. The windows folks
+ * took the source from the Microsoft C library source, but
+ * IBM wasn't kind enough to ship the source with VAC.
+ * On second thought, the needed function could probably
+ * be gotten from the OS/2 GNU library source, but the
+ * point is now moot.
+ */
+ struct stat hinfo;
+ PRInt64 s, s2us;
+
+ _setmode(fd->secret->md.osfd, O_BINARY);
+ if(fstat((int)fd->secret->md.osfd, &hinfo) != NO_ERROR) {
+ _PR_MD_MAP_FSTAT_ERROR(errno);
+ return -1;
+ }
+
+ if (hinfo.st_mode & S_IFDIR) {
+ info->type = PR_FILE_DIRECTORY;
+ }
+ else {
+ info->type = PR_FILE_FILE;
+ }
+
+ info->size = hinfo.st_size;
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, hinfo.st_mtime);
+ LL_MUL(s, s, s2us);
+ info->modifyTime = s;
+ LL_I2L(s, hinfo.st_ctime);
+ LL_MUL(s, s, s2us);
+ info->creationTime = s;
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
+{
+ PRFileInfo info32;
+ PRInt32 rv = _PR_MD_GETOPENFILEINFO(fd, &info32);
+ if (0 == rv)
+ {
+ info->type = info32.type;
+ LL_UI2L(info->size,info32.size);
+
+ info->modifyTime = info32.modifyTime;
+ info->creationTime = info32.creationTime;
+ }
+
+ if (isWSEB)
+ {
+ APIRET rc ;
+ FILESTATUS3L fstatus;
+
+ rc = DosQueryFileInfo(fd->secret->md.osfd, FIL_STANDARDL, &fstatus, sizeof(fstatus));
+
+ if (NO_ERROR != rc)
+ {
+ _PR_MD_MAP_OPEN_ERROR(rc);
+ return -1;
+ }
+
+ if (! (fstatus.attrFile & FILE_DIRECTORY))
+ {
+ info->size = fstatus.cbFile;
+ }
+ }
+
+ return rv;
+}
+
+
+PRInt32
+_PR_MD_RENAME(const char *from, const char *to)
+{
+ PRInt32 rc;
+ /* Does this work with dot-relative pathnames? */
+ if ( (rc = DosMove((char *)from, (char *)to)) == NO_ERROR) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RENAME_ERROR(rc);
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_ACCESS(const char *name, PRAccessHow how)
+{
+ PRInt32 rv;
+ switch (how) {
+ case PR_ACCESS_WRITE_OK:
+ rv = access(name, 02);
+ break;
+ case PR_ACCESS_READ_OK:
+ rv = access(name, 04);
+ break;
+ case PR_ACCESS_EXISTS:
+ return access(name, 00);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_ACCESS_ERROR(errno);
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_MKDIR(const char *name, PRIntn mode)
+{
+ PRInt32 rc;
+ /* XXXMB - how to translate the "mode"??? */
+ if ((rc = DosCreateDir((char *)name, NULL))== NO_ERROR) {
+ return 0;
+ } else {
+ _PR_MD_MAP_MKDIR_ERROR(rc);
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_RMDIR(const char *name)
+{
+ PRInt32 rc;
+ if ( (rc = DosDeleteDir((char *)name)) == NO_ERROR) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RMDIR_ERROR(rc);
+ return -1;
+ }
+}
+
+PRStatus
+_PR_MD_LOCKFILE(PRInt32 f)
+{
+ PRInt32 rv;
+ FILELOCK lock, unlock;
+ FILELOCKL lockL, unlockL;
+
+ lock.lOffset = 0;
+ lockL.lOffset = 0;
+ lock.lRange = 0xffffffff;
+ lockL.lRange = 0xffffffffffffffff;
+ unlock.lOffset = 0;
+ unlock.lRange = 0;
+ unlockL.lOffset = 0;
+ unlockL.lRange = 0;
+
+ /*
+ * loop trying to DosSetFileLocks(),
+ * pause for a few miliseconds when can't get the lock
+ * and try again
+ */
+ for( rv = FALSE; rv == FALSE; /* do nothing */ )
+ {
+ if (isWSEB)
+ {
+ rv = myDosSetFileLocksL( (HFILE) f,
+ &unlockL, &lockL,
+ 0, 0);
+ }
+ else
+ {
+ rv = DosSetFileLocks( (HFILE) f,
+ &unlock, &lock,
+ 0, 0);
+ }
+ if ( rv != NO_ERROR )
+ {
+ DosSleep( 50 ); /* Sleep() a few milisecs and try again. */
+ }
+ } /* end for() */
+ return PR_SUCCESS;
+} /* end _PR_MD_LOCKFILE() */
+
+PRStatus
+_PR_MD_TLOCKFILE(PRInt32 f)
+{
+ return _PR_MD_LOCKFILE(f);
+} /* end _PR_MD_TLOCKFILE() */
+
+
+PRStatus
+_PR_MD_UNLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv;
+ FILELOCK lock, unlock;
+ FILELOCKL lockL, unlockL;
+
+ lock.lOffset = 0;
+ lockL.lOffset = 0;
+ lock.lRange = 0;
+ lockL.lRange = 0;
+ unlock.lOffset = 0;
+ unlockL.lOffset = 0;
+ unlock.lRange = 0xffffffff;
+ unlockL.lRange = 0xffffffffffffffff;
+
+ if (isWSEB)
+ {
+ rv = myDosSetFileLocksL( (HFILE) f,
+ &unlockL, &lockL,
+ 0, 0);
+ }
+ else
+ {
+ rv = DosSetFileLocks( (HFILE) f,
+ &unlock, &lock,
+ 0, 0);
+ }
+
+ if ( rv != NO_ERROR )
+ {
+ return PR_SUCCESS;
+ }
+ else
+ {
+ return PR_FAILURE;
+ }
+} /* end _PR_MD_UNLOCKFILE() */
+
+PRStatus
+_PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable)
+{
+ APIRET rc = 0;
+ ULONG flags;
+ switch (fd->methods->file_type)
+ {
+ case PR_DESC_PIPE:
+ case PR_DESC_FILE:
+ rc = DosQueryFHState((HFILE)fd->secret->md.osfd, &flags);
+ if (rc != NO_ERROR) {
+ PR_SetError(PR_UNKNOWN_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+
+ if (inheritable) {
+ flags &= ~OPEN_FLAGS_NOINHERIT;
+ }
+ else {
+ flags |= OPEN_FLAGS_NOINHERIT;
+ }
+
+ /* Mask off flags DosSetFHState don't want. */
+ flags &= (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT);
+ rc = DosSetFHState((HFILE)fd->secret->md.osfd, flags);
+ if (rc != NO_ERROR) {
+ PR_SetError(PR_UNKNOWN_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+ break;
+
+ case PR_DESC_LAYERED:
+ /* what to do here? */
+ PR_SetError(PR_UNKNOWN_ERROR, 87 /*ERROR_INVALID_PARAMETER*/);
+ return PR_FAILURE;
+
+ case PR_DESC_SOCKET_TCP:
+ case PR_DESC_SOCKET_UDP:
+ /* These are global on OS/2. */
+ break;
+ }
+
+ return PR_SUCCESS;
+}
+
+void
+_PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported)
+{
+ /* XXX this function needs to be implemented */
+ fd->secret->inheritable = _PR_TRI_UNKNOWN;
+}
+
+void
+_PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd)
+{
+ /* XXX this function needs to be reviewed */
+ ULONG flags;
+
+ PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable);
+ if (DosQueryFHState((HFILE)fd->secret->md.osfd, &flags) == 0) {
+ if (flags & OPEN_FLAGS_NOINHERIT) {
+ fd->secret->inheritable = _PR_TRI_FALSE;
+ } else {
+ fd->secret->inheritable = _PR_TRI_TRUE;
+ }
+ }
+}
diff --git a/nsprpub/pr/src/md/os2/os2misc.c b/nsprpub/pr/src/md/os2/os2misc.c
new file mode 100644
index 0000000000..240f0905c9
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/os2misc.c
@@ -0,0 +1,620 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * os2misc.c
+ *
+ */
+
+#ifdef MOZ_OS2_HIGH_MEMORY
+/* os2safe.h has to be included before os2.h, needed for high mem */
+#include <os2safe.h>
+#endif
+
+#include <string.h>
+#include "primpl.h"
+
+extern int _CRT_init(void);
+extern void _CRT_term(void);
+extern void __ctordtorInit(int flag);
+extern void __ctordtorTerm(int flag);
+
+char *
+_PR_MD_GET_ENV(const char *name)
+{
+ return getenv(name);
+}
+
+PRIntn
+_PR_MD_PUT_ENV(const char *name)
+{
+ return putenv(name);
+}
+
+
+/*
+ **************************************************************************
+ **************************************************************************
+ **
+ ** Date and time routines
+ **
+ **************************************************************************
+ **************************************************************************
+ */
+
+#include <sys/timeb.h>
+/*
+ *-----------------------------------------------------------------------
+ *
+ * PR_Now --
+ *
+ * Returns the current time in microseconds since the epoch.
+ * The epoch is midnight January 1, 1970 GMT.
+ * The implementation is machine dependent. This is the
+ * implementation for OS/2.
+ * Cf. time_t time(time_t *tp)
+ *
+ *-----------------------------------------------------------------------
+ */
+
+PR_IMPLEMENT(PRTime)
+PR_Now(void)
+{
+ PRInt64 s, ms, ms2us, s2us;
+ struct timeb b;
+
+ ftime(&b);
+ LL_I2L(ms2us, PR_USEC_PER_MSEC);
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, b.time);
+ LL_I2L(ms, b.millitm);
+ LL_MUL(ms, ms, ms2us);
+ LL_MUL(s, s, s2us);
+ LL_ADD(s, s, ms);
+ return s;
+}
+
+
+/*
+ ***********************************************************************
+ ***********************************************************************
+ *
+ * Process creation routines
+ *
+ ***********************************************************************
+ ***********************************************************************
+ */
+
+/*
+ * Assemble the command line by concatenating the argv array.
+ * Special characters intentionally do not get escaped, and it is
+ * expected that the caller wraps arguments in quotes if needed
+ * (e.g. for filename with spaces).
+ *
+ * On success, this function returns 0 and the resulting command
+ * line is returned in *cmdLine. On failure, it returns -1.
+ */
+static int assembleCmdLine(char *const *argv, char **cmdLine)
+{
+ char *const *arg;
+ int cmdLineSize;
+
+ /*
+ * Find out how large the command line buffer should be.
+ */
+ cmdLineSize = 1; /* final null */
+ for (arg = argv+1; *arg; arg++) {
+ cmdLineSize += strlen(*arg) + 1; /* space in between */
+ }
+ *cmdLine = PR_MALLOC(cmdLineSize);
+ if (*cmdLine == NULL) {
+ return -1;
+ }
+
+ (*cmdLine)[0] = '\0';
+
+ for (arg = argv+1; *arg; arg++) {
+ if (arg > argv +1) {
+ strcat(*cmdLine, " ");
+ }
+ strcat(*cmdLine, *arg);
+ }
+ return 0;
+}
+
+/*
+ * Assemble the environment block by concatenating the envp array
+ * (preserving the terminating null byte in each array element)
+ * and adding a null byte at the end.
+ *
+ * Returns 0 on success. The resulting environment block is returned
+ * in *envBlock. Note that if envp is NULL, a NULL pointer is returned
+ * in *envBlock. Returns -1 on failure.
+ */
+static int assembleEnvBlock(char **envp, char **envBlock)
+{
+ char *p;
+ char *q;
+ char **env;
+ char *curEnv;
+ char *cwdStart, *cwdEnd;
+ int envBlockSize;
+
+ PPIB ppib = NULL;
+ PTIB ptib = NULL;
+
+ if (envp == NULL) {
+ *envBlock = NULL;
+ return 0;
+ }
+
+ if(DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) {
+ return -1;
+ }
+
+ curEnv = ppib->pib_pchenv;
+
+ cwdStart = curEnv;
+ while (*cwdStart) {
+ if (cwdStart[0] == '=' && cwdStart[1] != '\0'
+ && cwdStart[2] == ':' && cwdStart[3] == '=') {
+ break;
+ }
+ cwdStart += strlen(cwdStart) + 1;
+ }
+ cwdEnd = cwdStart;
+ if (*cwdEnd) {
+ cwdEnd += strlen(cwdEnd) + 1;
+ while (*cwdEnd) {
+ if (cwdEnd[0] != '=' || cwdEnd[1] == '\0'
+ || cwdEnd[2] != ':' || cwdEnd[3] != '=') {
+ break;
+ }
+ cwdEnd += strlen(cwdEnd) + 1;
+ }
+ }
+ envBlockSize = cwdEnd - cwdStart;
+
+ for (env = envp; *env; env++) {
+ envBlockSize += strlen(*env) + 1;
+ }
+ envBlockSize++;
+
+ p = *envBlock = PR_MALLOC(envBlockSize);
+ if (p == NULL) {
+ return -1;
+ }
+
+ q = cwdStart;
+ while (q < cwdEnd) {
+ *p++ = *q++;
+ }
+
+ for (env = envp; *env; env++) {
+ q = *env;
+ while (*q) {
+ *p++ = *q++;
+ }
+ *p++ = '\0';
+ }
+ *p = '\0';
+ return 0;
+}
+
+/*
+ * For qsort. We sort (case-insensitive) the environment strings
+ * before generating the environment block.
+ */
+static int compare(const void *arg1, const void *arg2)
+{
+ return stricmp(* (char**)arg1, * (char**)arg2);
+}
+
+PRProcess * _PR_CreateOS2Process(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ PRProcess *proc = NULL;
+ char *cmdLine = NULL;
+ char **newEnvp = NULL;
+ char *envBlock = NULL;
+
+ STARTDATA startData = {0};
+ APIRET rc;
+ ULONG ulAppType = 0;
+ PID pid = 0;
+ char *pszComSpec;
+ char pszEXEName[CCHMAXPATH] = "";
+ char pszFormatString[CCHMAXPATH];
+ char pszObjectBuffer[CCHMAXPATH];
+ char *pszFormatResult = NULL;
+
+ /*
+ * Variables for DosExecPgm
+ */
+ char szFailed[CCHMAXPATH];
+ char *pszCmdLine = NULL;
+ RESULTCODES procInfo;
+ HFILE hStdIn = 0,
+ hStdOut = 0,
+ hStdErr = 0;
+ HFILE hStdInSave = -1,
+ hStdOutSave = -1,
+ hStdErrSave = -1;
+
+ proc = PR_NEW(PRProcess);
+ if (!proc) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+ if (assembleCmdLine(argv, &cmdLine) == -1) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+#ifdef MOZ_OS2_HIGH_MEMORY
+ /*
+ * DosQueryAppType() fails if path (the char* in the first argument) is in
+ * high memory. If that is the case, the following moves it to low memory.
+ */
+ if ((ULONG)path >= 0x20000000) {
+ size_t len = strlen(path) + 1;
+ char *copy = (char *)alloca(len);
+ memcpy(copy, path, len);
+ path = copy;
+ }
+#endif
+
+ if (envp == NULL) {
+ newEnvp = NULL;
+ } else {
+ int i;
+ int numEnv = 0;
+ while (envp[numEnv]) {
+ numEnv++;
+ }
+ newEnvp = (char **) PR_MALLOC((numEnv+1) * sizeof(char *));
+ for (i = 0; i <= numEnv; i++) {
+ newEnvp[i] = envp[i];
+ }
+ qsort((void *) newEnvp, (size_t) numEnv, sizeof(char *), compare);
+ }
+ if (assembleEnvBlock(newEnvp, &envBlock) == -1) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+ rc = DosQueryAppType(path, &ulAppType);
+ if (rc != NO_ERROR) {
+ char *pszDot = strrchr(path, '.');
+ if (pszDot) {
+ /* If it is a CMD file, launch the users command processor */
+ if (!stricmp(pszDot, ".cmd")) {
+ rc = DosScanEnv("COMSPEC", (PSZ *)&pszComSpec);
+ if (!rc) {
+ strcpy(pszFormatString, "/C %s %s");
+ strcpy(pszEXEName, pszComSpec);
+ ulAppType = FAPPTYP_WINDOWCOMPAT;
+ }
+ }
+ }
+ }
+ if (ulAppType == 0) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ goto errorExit;
+ }
+
+ if ((ulAppType & FAPPTYP_WINDOWAPI) == FAPPTYP_WINDOWAPI) {
+ startData.SessionType = SSF_TYPE_PM;
+ }
+ else if (ulAppType & FAPPTYP_WINDOWCOMPAT) {
+ startData.SessionType = SSF_TYPE_WINDOWABLEVIO;
+ }
+ else {
+ startData.SessionType = SSF_TYPE_DEFAULT;
+ }
+
+ if (ulAppType & (FAPPTYP_WINDOWSPROT31 | FAPPTYP_WINDOWSPROT | FAPPTYP_WINDOWSREAL))
+ {
+ strcpy(pszEXEName, "WINOS2.COM");
+ startData.SessionType = PROG_31_STDSEAMLESSVDM;
+ strcpy(pszFormatString, "/3 %s %s");
+ }
+
+ startData.InheritOpt = SSF_INHERTOPT_SHELL;
+
+ if (pszEXEName[0]) {
+ pszFormatResult = PR_MALLOC(strlen(pszFormatString)+strlen(path)+strlen(cmdLine));
+ sprintf(pszFormatResult, pszFormatString, path, cmdLine);
+ startData.PgmInputs = pszFormatResult;
+ } else {
+ strcpy(pszEXEName, path);
+ startData.PgmInputs = cmdLine;
+ }
+ startData.PgmName = pszEXEName;
+
+ startData.Length = sizeof(startData);
+ startData.Related = SSF_RELATED_INDEPENDENT;
+ startData.ObjectBuffer = pszObjectBuffer;
+ startData.ObjectBuffLen = CCHMAXPATH;
+ startData.Environment = envBlock;
+
+ if (attr) {
+ /* On OS/2, there is really no way to pass file handles for stdin,
+ * stdout, and stderr to a new process. Instead, we can make it
+ * a child process and make the given file handles a copy of our
+ * stdin, stdout, and stderr. The child process then inherits
+ * ours, and we set ours back. Twisted and gross I know. If you
+ * know a better way, please use it.
+ */
+ if (attr->stdinFd) {
+ hStdIn = 0;
+ DosDupHandle(hStdIn, &hStdInSave);
+ DosDupHandle((HFILE) attr->stdinFd->secret->md.osfd, &hStdIn);
+ }
+
+ if (attr->stdoutFd) {
+ hStdOut = 1;
+ DosDupHandle(hStdOut, &hStdOutSave);
+ DosDupHandle((HFILE) attr->stdoutFd->secret->md.osfd, &hStdOut);
+ }
+
+ if (attr->stderrFd) {
+ hStdErr = 2;
+ DosDupHandle(hStdErr, &hStdErrSave);
+ DosDupHandle((HFILE) attr->stderrFd->secret->md.osfd, &hStdErr);
+ }
+ /*
+ * Build up the Command Line for DosExecPgm
+ */
+ pszCmdLine = PR_MALLOC(strlen(pszEXEName) +
+ strlen(startData.PgmInputs) + 3);
+ sprintf(pszCmdLine, "%s%c%s%c", pszEXEName, '\0',
+ startData.PgmInputs, '\0');
+ rc = DosExecPgm(szFailed,
+ CCHMAXPATH,
+ EXEC_ASYNCRESULT,
+ pszCmdLine,
+ envBlock,
+ &procInfo,
+ pszEXEName);
+ PR_DELETE(pszCmdLine);
+
+ /* Restore our old values. Hope this works */
+ if (hStdInSave != -1) {
+ DosDupHandle(hStdInSave, &hStdIn);
+ DosClose(hStdInSave);
+ }
+
+ if (hStdOutSave != -1) {
+ DosDupHandle(hStdOutSave, &hStdOut);
+ DosClose(hStdOutSave);
+ }
+
+ if (hStdErrSave != -1) {
+ DosDupHandle(hStdErrSave, &hStdErr);
+ DosClose(hStdErrSave);
+ }
+
+ if (rc != NO_ERROR) {
+ /* XXX what error code? */
+ PR_SetError(PR_UNKNOWN_ERROR, rc);
+ goto errorExit;
+ }
+
+ proc->md.pid = procInfo.codeTerminate;
+ } else {
+ /*
+ * If no STDIN/STDOUT redirection is not needed, use DosStartSession
+ * to create a new, independent session
+ */
+ rc = DosStartSession(&startData, &ulAppType, &pid);
+
+ if ((rc != NO_ERROR) && (rc != ERROR_SMG_START_IN_BACKGROUND)) {
+ PR_SetError(PR_UNKNOWN_ERROR, rc);
+ goto errorExit;
+ }
+
+ proc->md.pid = pid;
+ }
+
+ if (pszFormatResult) {
+ PR_DELETE(pszFormatResult);
+ }
+
+ PR_DELETE(cmdLine);
+ if (newEnvp) {
+ PR_DELETE(newEnvp);
+ }
+ if (envBlock) {
+ PR_DELETE(envBlock);
+ }
+ return proc;
+
+errorExit:
+ if (cmdLine) {
+ PR_DELETE(cmdLine);
+ }
+ if (newEnvp) {
+ PR_DELETE(newEnvp);
+ }
+ if (envBlock) {
+ PR_DELETE(envBlock);
+ }
+ if (proc) {
+ PR_DELETE(proc);
+ }
+ return NULL;
+} /* _PR_CreateOS2Process */
+
+PRStatus _PR_DetachOS2Process(PRProcess *process)
+{
+ /* On OS/2, a process is either created as a child or not.
+ * You can't 'detach' it later on.
+ */
+ PR_DELETE(process);
+ return PR_SUCCESS;
+}
+
+/*
+ * XXX: This will currently only work on a child process.
+ */
+PRStatus _PR_WaitOS2Process(PRProcess *process,
+ PRInt32 *exitCode)
+{
+ ULONG ulRetVal;
+ RESULTCODES results;
+ PID pidEnded = 0;
+
+ ulRetVal = DosWaitChild(DCWA_PROCESS, DCWW_WAIT,
+ &results,
+ &pidEnded, process->md.pid);
+
+ if (ulRetVal != NO_ERROR) {
+ printf("\nDosWaitChild rc = %lu\n", ulRetVal);
+ PR_SetError(PR_UNKNOWN_ERROR, ulRetVal);
+ return PR_FAILURE;
+ }
+ PR_DELETE(process);
+ return PR_SUCCESS;
+}
+
+PRStatus _PR_KillOS2Process(PRProcess *process)
+{
+ ULONG ulRetVal;
+ if ((ulRetVal = DosKillProcess(DKP_PROCESS, process->md.pid)) == NO_ERROR) {
+ return PR_SUCCESS;
+ }
+ PR_SetError(PR_UNKNOWN_ERROR, ulRetVal);
+ return PR_FAILURE;
+}
+
+PRStatus _MD_OS2GetHostName(char *name, PRUint32 namelen)
+{
+ PRIntn rv;
+
+ rv = gethostname(name, (PRInt32) namelen);
+ if (0 == rv) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_GETHOSTNAME_ERROR(sock_errno());
+ return PR_FAILURE;
+}
+
+void
+_PR_MD_WAKEUP_CPUS( void )
+{
+ return;
+}
+
+/*
+ **********************************************************************
+ *
+ * Memory-mapped files
+ *
+ * A credible emulation of memory-mapped i/o on OS/2 would require
+ * an exception-handler on each thread that might access the mapped
+ * memory. In the Mozilla environment, that would be impractical.
+ * Instead, the following simulates those modes which don't modify
+ * the mapped file. It reads the entire mapped file segment at once
+ * when MemMap is called, and frees it on MemUnmap. CreateFileMap
+ * only does sanity-checks, while CloseFileMap does almost nothing.
+ *
+ **********************************************************************
+ */
+
+PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
+{
+ PRFileInfo64 info;
+
+ /* assert on PR_PROT_READWRITE which modifies the underlying file */
+ PR_ASSERT(fmap->prot == PR_PROT_READONLY ||
+ fmap->prot == PR_PROT_WRITECOPY);
+ if (fmap->prot != PR_PROT_READONLY &&
+ fmap->prot != PR_PROT_WRITECOPY) {
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (PR_GetOpenFileInfo64(fmap->fd, &info) == PR_FAILURE) {
+ return PR_FAILURE;
+ }
+ /* reject zero-byte mappings & zero-byte files */
+ if (!size || !info.size) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ /* file size rounded up to the next page boundary */
+ fmap->md.maxExtent = (info.size + 0xfff) & ~(0xfff);
+
+ return PR_SUCCESS;
+}
+
+PRInt32 _MD_GetMemMapAlignment(void)
+{
+ /* OS/2 pages are 4k */
+ return 0x1000;
+}
+
+void * _MD_MemMap(PRFileMap *fmap, PROffset64 offset, PRUint32 len)
+{
+ PRUint32 rv;
+ void *addr;
+
+ /* prevent mappings beyond EOF + remainder of page */
+ if (offset + len > fmap->md.maxExtent) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+ if (PR_Seek64(fmap->fd, offset, PR_SEEK_SET) == -1) {
+ return NULL;
+ }
+ /* try for high memory, fall back to low memory if hi-mem fails */
+ rv = DosAllocMem(&addr, len, OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE);
+ if (rv) {
+ rv = DosAllocMem(&addr, len, PAG_COMMIT | PAG_READ | PAG_WRITE);
+ if (rv) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, rv);
+ return NULL;
+ }
+ }
+ if (PR_Read(fmap->fd, addr, len) == -1) {
+ DosFreeMem(addr);
+ return NULL;
+ }
+ /* don't permit writes if readonly */
+ if (fmap->prot == PR_PROT_READONLY) {
+ rv = DosSetMem(addr, len, PAG_READ);
+ if (rv) {
+ DosFreeMem(addr);
+ PR_SetError(PR_UNKNOWN_ERROR, rv);
+ return NULL;
+ }
+ }
+ return addr;
+}
+
+PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
+{
+ PRUint32 rv;
+
+ /* we just have to trust that addr & len are those used by MemMap */
+ rv = DosFreeMem(addr);
+ if (rv) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, rv);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PRStatus _MD_CloseFileMap(PRFileMap *fmap)
+{
+ /* nothing to do except free the PRFileMap struct */
+ PR_Free(fmap);
+ return PR_SUCCESS;
+}
+
diff --git a/nsprpub/pr/src/md/os2/os2poll.c b/nsprpub/pr/src/md/os2/os2poll.c
new file mode 100644
index 0000000000..47d9710454
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/os2poll.c
@@ -0,0 +1,361 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file implements _PR_MD_PR_POLL for OS/2.
+ */
+
+#include <sys/time.h> /* For timeval. */
+
+#include "primpl.h"
+
+#ifndef BSD_SELECT
+/* Utility functions called when using OS/2 select */
+
+PRBool IsSocketSet( PRInt32 osfd, int* socks, int start, int count )
+{
+ int i;
+ PRBool isSet = PR_FALSE;
+
+ for( i = start; i < start+count; i++ )
+ {
+ if( socks[i] == osfd ) {
+ isSet = PR_TRUE;
+ }
+ }
+
+ return isSet;
+}
+#endif
+
+PRInt32 _PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+#ifdef BSD_SELECT
+ fd_set rd, wt, ex;
+#else
+ int rd, wt, ex;
+ int* socks;
+ unsigned long msecs;
+ int i, j;
+#endif
+ PRFileDesc *bottom;
+ PRPollDesc *pd, *epd;
+ PRInt32 maxfd = -1, ready, err;
+ PRIntervalTime remaining, elapsed, start;
+
+#ifdef BSD_SELECT
+ struct timeval tv, *tvp = NULL;
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wt);
+ FD_ZERO(&ex);
+#else
+ rd = 0;
+ wt = 0;
+ ex = 0;
+ socks = (int) PR_MALLOC( npds * 3 * sizeof(int) );
+
+ if (!socks)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+#endif
+
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ if (pd->in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pd->fd->methods->poll)(
+ pd->fd, pd->in_flags & ~PR_POLL_WRITE, &out_flags_read);
+ }
+ if (pd->in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pd->fd->methods->poll)(
+ pd->fd, pd->in_flags & ~PR_POLL_READ, &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read)) ||
+ (0 != (in_flags_write & out_flags_write)))
+ {
+ /* this one's ready right now */
+ if (0 == ready)
+ {
+ /*
+ * We will have to return without calling the
+ * system poll/select function. So zero the
+ * out_flags fields of all the poll descriptors
+ * before this one.
+ */
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1;
+ pd->out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ pd->out_flags = 0; /* pre-condition */
+
+ /* make sure this is an NSPR supported stack */
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom); /* what to do about that? */
+ if ((NULL != bottom) &&
+ (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ if (0 == ready)
+ {
+ PRInt32 osfd = bottom->secret->md.osfd;
+ if (osfd > maxfd) {
+ maxfd = osfd;
+ }
+ if (in_flags_read & PR_POLL_READ)
+ {
+ pd->out_flags |= _PR_POLL_READ_SYS_READ;
+#ifdef BSD_SELECT
+ FD_SET(osfd, &rd);
+#else
+ socks[rd] = osfd;
+ rd++;
+#endif
+ }
+ if (in_flags_read & PR_POLL_WRITE)
+ {
+ pd->out_flags |= _PR_POLL_READ_SYS_WRITE;
+#ifdef BSD_SELECT
+ FD_SET(osfd, &wt);
+#else
+ socks[npds+wt] = osfd;
+ wt++;
+#endif
+ }
+ if (in_flags_write & PR_POLL_READ)
+ {
+ pd->out_flags |= _PR_POLL_WRITE_SYS_READ;
+#ifdef BSD_SELECT
+ FD_SET(osfd, &rd);
+#else
+ socks[rd] = osfd;
+ rd++;
+#endif
+ }
+ if (in_flags_write & PR_POLL_WRITE)
+ {
+ pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE;
+#ifdef BSD_SELECT
+ FD_SET(osfd, &wt);
+#else
+ socks[npds+wt] = osfd;
+ wt++;
+#endif
+ }
+ if (pd->in_flags & PR_POLL_EXCEPT)
+ {
+#ifdef BSD_SELECT
+ FD_SET(osfd, &ex);
+#else
+ socks[npds*2+ex] = osfd;
+ ex++;
+#endif
+ }
+ }
+ }
+ else
+ {
+ if (0 == ready)
+ {
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1; /* this will cause an abrupt return */
+ pd->out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+ else
+ {
+ pd->out_flags = 0;
+ }
+ }
+
+ if (0 != ready)
+ {
+#ifndef BSD_SELECT
+ PR_Free(socks);
+#endif
+ return ready; /* no need to block */
+ }
+
+ remaining = timeout;
+ start = PR_IntervalNow();
+
+retry:
+#ifdef BSD_SELECT
+ if (timeout != PR_INTERVAL_NO_TIMEOUT)
+ {
+ PRInt32 ticksPerSecond = PR_TicksPerSecond();
+ tv.tv_sec = remaining / ticksPerSecond;
+ tv.tv_usec = PR_IntervalToMicroseconds( remaining % ticksPerSecond );
+ tvp = &tv;
+ }
+
+ ready = bsdselect(maxfd + 1, &rd, &wt, &ex, tvp);
+#else
+ switch (timeout)
+ {
+ case PR_INTERVAL_NO_WAIT:
+ msecs = 0;
+ break;
+ case PR_INTERVAL_NO_TIMEOUT:
+ msecs = -1;
+ break;
+ default:
+ msecs = PR_IntervalToMilliseconds(remaining);
+ }
+
+ /* compact array */
+ for( i = rd, j = npds; j < npds+wt; i++,j++ ) {
+ socks[i] = socks[j];
+ }
+ for( i = rd+wt, j = npds*2; j < npds*2+ex; i++,j++ ) {
+ socks[i] = socks[j];
+ }
+
+ ready = os2_select(socks, rd, wt, ex, msecs);
+#endif
+
+ if (ready == -1 && errno == EINTR)
+ {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ goto retry;
+ }
+ else
+ {
+ elapsed = (PRIntervalTime) (PR_IntervalNow() - start);
+ if (elapsed > timeout) {
+ ready = 0; /* timed out */
+ }
+ else
+ {
+ remaining = timeout - elapsed;
+ goto retry;
+ }
+ }
+ }
+
+ /*
+ ** Now to unravel the select sets back into the client's poll
+ ** descriptor list. Is this possibly an area for pissing away
+ ** a few cycles or what?
+ */
+ if (ready > 0)
+ {
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRInt16 out_flags = 0;
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ PRInt32 osfd;
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+
+ osfd = bottom->secret->md.osfd;
+
+#ifdef BSD_SELECT
+ if (FD_ISSET(osfd, &rd))
+#else
+ if( IsSocketSet(osfd, socks, 0, rd) )
+#endif
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_READ) {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_READ) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+
+#ifdef BSD_SELECT
+ if (FD_ISSET(osfd, &wt))
+#else
+ if( IsSocketSet(osfd, socks, rd, wt) )
+#endif
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_WRITE) {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+
+#ifdef BSD_SELECT
+ if (FD_ISSET(osfd, &ex))
+#else
+ if( IsSocketSet(osfd, socks, rd+wt, ex) )
+#endif
+ {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ }
+ pd->out_flags = out_flags;
+ if (out_flags) {
+ ready++;
+ }
+ }
+ PR_ASSERT(ready > 0);
+ }
+ else if (ready < 0)
+ {
+ err = _MD_ERRNO();
+ if (err == EBADF)
+ {
+ /* Find the bad fds */
+ int optval;
+ int optlen = sizeof(optval);
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ pd->out_flags = 0;
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ if (getsockopt(bottom->secret->md.osfd, SOL_SOCKET,
+ SO_TYPE, (char *) &optval, &optlen) == -1)
+ {
+ PR_ASSERT(sock_errno() == ENOTSOCK);
+ if (sock_errno() == ENOTSOCK)
+ {
+ pd->out_flags = PR_POLL_NVAL;
+ ready++;
+ }
+ }
+ }
+ }
+ PR_ASSERT(ready > 0);
+ }
+ else {
+ _PR_MD_MAP_SELECT_ERROR(err);
+ }
+ }
+
+#ifndef BSD_SELECT
+ PR_Free(socks);
+#endif
+ return ready;
+}
+
diff --git a/nsprpub/pr/src/md/os2/os2rng.c b/nsprpub/pr/src/md/os2/os2rng.c
new file mode 100644
index 0000000000..aaa9693817
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/os2rng.c
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#define INCL_DOS
+#define INCL_DOSERRORS
+#include <os2.h>
+#include <stdlib.h>
+#include <time.h>
+#include "primpl.h"
+
+static BOOL clockTickTime(unsigned long *phigh, unsigned long *plow)
+{
+ APIRET rc = NO_ERROR;
+ QWORD qword = {0,0};
+
+ rc = DosTmrQueryTime(&qword);
+ if (rc != NO_ERROR) {
+ return FALSE;
+ }
+
+ *phigh = qword.ulHi;
+ *plow = qword.ulLo;
+
+ return TRUE;
+}
+
+extern PRSize _PR_MD_GetRandomNoise(void *buf, PRSize size )
+{
+ unsigned long high = 0;
+ unsigned long low = 0;
+ clock_t val = 0;
+ int n = 0;
+ int nBytes = 0;
+ time_t sTime;
+
+ if (size <= 0) {
+ return 0;
+ }
+
+ clockTickTime(&high, &low);
+
+ /* get the maximally changing bits first */
+ nBytes = sizeof(low) > size ? size : sizeof(low);
+ memcpy(buf, &low, nBytes);
+ n += nBytes;
+ size -= nBytes;
+
+ if (size <= 0) {
+ return n;
+ }
+
+ nBytes = sizeof(high) > size ? size : sizeof(high);
+ memcpy(((char *)buf) + n, &high, nBytes);
+ n += nBytes;
+ size -= nBytes;
+
+ if (size <= 0) {
+ return n;
+ }
+
+ /* get the number of milliseconds that have elapsed since application started */
+ val = clock();
+
+ nBytes = sizeof(val) > size ? size : sizeof(val);
+ memcpy(((char *)buf) + n, &val, nBytes);
+ n += nBytes;
+ size -= nBytes;
+
+ if (size <= 0) {
+ return n;
+ }
+
+ /* get the time in seconds since midnight Jan 1, 1970 */
+ time(&sTime);
+ nBytes = sizeof(sTime) > size ? size : sizeof(sTime);
+ memcpy(((char *)buf) + n, &sTime, nBytes);
+ n += nBytes;
+
+ return n;
+}
diff --git a/nsprpub/pr/src/md/os2/os2sem.c b/nsprpub/pr/src/md/os2/os2sem.c
new file mode 100644
index 0000000000..a0dd88e4bd
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/os2sem.c
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * OS/2-specific semaphore handling code.
+ *
+ */
+
+#include "primpl.h"
+
+
+void
+_PR_MD_NEW_SEM(_MDSemaphore *md, PRUintn value)
+{
+ int rv;
+
+ /* Our Sems don't support a value > 1 */
+ PR_ASSERT(value <= 1);
+
+ rv = DosCreateEventSem(NULL, &md->sem, 0, 0);
+ PR_ASSERT(rv == NO_ERROR);
+}
+
+void
+_PR_MD_DESTROY_SEM(_MDSemaphore *md)
+{
+ int rv;
+ rv = DosCloseEventSem(md->sem);
+ PR_ASSERT(rv == NO_ERROR);
+
+}
+
+PRStatus
+_PR_MD_TIMED_WAIT_SEM(_MDSemaphore *md, PRIntervalTime ticks)
+{
+ int rv;
+ rv = DosWaitEventSem(md->sem, PR_IntervalToMilliseconds(ticks));
+
+ if (rv == NO_ERROR) {
+ return PR_SUCCESS;
+ }
+ else {
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_WAIT_SEM(_MDSemaphore *md)
+{
+ return _PR_MD_TIMED_WAIT_SEM(md, PR_INTERVAL_NO_TIMEOUT);
+}
+
+void
+_PR_MD_POST_SEM(_MDSemaphore *md)
+{
+ int rv;
+ rv = DosPostEventSem(md->sem);
+ PR_ASSERT(rv == NO_ERROR);
+}
+
+
diff --git a/nsprpub/pr/src/md/os2/os2sock.c b/nsprpub/pr/src/md/os2/os2sock.c
new file mode 100644
index 0000000000..c6b3ea3a52
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/os2sock.c
@@ -0,0 +1,669 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* OS/2 Sockets module
+ *
+ */
+
+/*Note from DSR111297 - it should be noted that there are two flavors of select() on OS/2 */
+/*There is standard BSD (which is kind of slow) and a new flavor of select() that takes */
+/*an integer list of sockets, the number of read sockets, write sockets, except sockets, and */
+/*a millisecond count for timeout. In the interest of performance I have choosen the OS/2 */
+/*specific version of select(). See OS/2 TCP/IP Programmer's Toolkit for more info. */
+
+#include "primpl.h"
+
+#include <sys/time.h> /* For timeval. */
+
+#define _PR_INTERRUPT_CHECK_INTERVAL_SECS 5
+#define READ_FD 1
+#define WRITE_FD 2
+
+/* --- SOCKET IO --------------------------------------------------------- */
+
+
+PRInt32
+_PR_MD_SOCKET(int domain, int type, int flags)
+{
+ PRInt32 osfd, err;
+
+ osfd = socket(domain, type, flags);
+
+ if (osfd == -1)
+ {
+ err = sock_errno();
+ _PR_MD_MAP_SOCKET_ERROR(err);
+ }
+
+ return(osfd);
+}
+
+/*
+** _MD_CloseSocket() -- Close a socket
+**
+*/
+PRInt32
+_MD_CloseSocket(PRInt32 osfd)
+{
+ PRInt32 rv, err;
+
+ rv = soclose(osfd);
+ if (rv == -1) {
+ err = sock_errno();
+ _PR_MD_MAP_CLOSE_ERROR(err);
+ }
+ return rv;
+}
+
+PRInt32
+_MD_SocketAvailable(PRFileDesc *fd)
+{
+ PRInt32 result;
+
+ if (so_ioctl(fd->secret->md.osfd, FIONREAD, (char *) &result, sizeof(result)) < 0) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, sock_errno());
+ return -1;
+ }
+ return result;
+}
+
+static PRInt32
+socket_io_wait( PRInt32 osfd, PRInt32 fd_type, PRIntervalTime timeout )
+{
+ PRInt32 rv = -1;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntervalTime epoch, now, elapsed, remaining;
+ PRBool wait_for_remaining;
+ PRInt32 syserror;
+#ifdef BSD_SELECT
+ struct timeval tv;
+ fd_set rd_wr;
+#else
+ int socks[1];
+ long lTimeout;
+#endif
+
+ switch (timeout) {
+ case PR_INTERVAL_NO_WAIT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ break;
+ case PR_INTERVAL_NO_TIMEOUT:
+ /*
+ * This is a special case of the 'default' case below.
+ * Please see the comments there.
+ */
+#ifdef BSD_SELECT
+ tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
+ tv.tv_usec = 0;
+ FD_ZERO(&rd_wr);
+ do {
+ FD_SET(osfd, &rd_wr);
+ if (fd_type == READ_FD) {
+ rv = bsdselect(osfd + 1, &rd_wr, NULL, NULL, &tv);
+ }
+ else {
+ rv = bsdselect(osfd + 1, NULL, &rd_wr, NULL, &tv);
+ }
+#else
+ lTimeout = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000;
+ do {
+ socks[0] = osfd;
+ if (fd_type == READ_FD) {
+ rv = os2_select(socks, 1, 0, 0, lTimeout);
+ }
+ else {
+ rv = os2_select(socks, 0, 1, 0, lTimeout);
+ }
+#endif
+ if (rv == -1 && (syserror = sock_errno()) != EINTR) {
+ _PR_MD_MAP_SELECT_ERROR(syserror);
+ break;
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ } while (rv == 0 || (rv == -1 && syserror == EINTR));
+ break;
+ default:
+ now = epoch = PR_IntervalNow();
+ remaining = timeout;
+#ifdef BSD_SELECT
+ FD_ZERO(&rd_wr);
+#endif
+ do {
+ /*
+ * We block in select for at most
+ * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds,
+ * so that there is an upper limit on the delay
+ * before the interrupt bit is checked.
+ */
+#ifdef BSD_SELECT
+ wait_for_remaining = PR_TRUE;
+ tv.tv_sec = PR_IntervalToSeconds(remaining);
+ if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) {
+ wait_for_remaining = PR_FALSE;
+ tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
+ tv.tv_usec = 0;
+ } else {
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ remaining -
+ PR_SecondsToInterval(tv.tv_sec));
+ }
+ FD_SET(osfd, &rd_wr);
+ if (fd_type == READ_FD) {
+ rv = bsdselect(osfd + 1, &rd_wr, NULL, NULL, &tv);
+ }
+ else {
+ rv = bsdselect(osfd + 1, NULL, &rd_wr, NULL, &tv);
+ }
+#else
+ wait_for_remaining = PR_TRUE;
+ lTimeout = PR_IntervalToMilliseconds(remaining);
+ if (lTimeout > _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000) {
+ wait_for_remaining = PR_FALSE;
+ lTimeout = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000;
+ }
+ socks[0] = osfd;
+ if (fd_type == READ_FD) {
+ rv = os2_select(socks, 1, 0, 0, lTimeout);
+ }
+ else {
+ rv = os2_select(socks, 0, 1, 0, lTimeout);
+ }
+#endif
+ /*
+ * we don't consider EINTR a real error
+ */
+ if (rv == -1 && (syserror = sock_errno()) != EINTR) {
+ _PR_MD_MAP_SELECT_ERROR(syserror);
+ break;
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ /*
+ * We loop again if select timed out or got interrupted
+ * by a signal, and the timeout deadline has not passed yet.
+ */
+ if (rv == 0 || (rv == -1 && syserror == EINTR)) {
+ /*
+ * If select timed out, we know how much time
+ * we spent in blocking, so we can avoid a
+ * PR_IntervalNow() call.
+ */
+ if (rv == 0) {
+ if (wait_for_remaining) {
+ now += remaining;
+ } else {
+#ifdef BSD_SELECT
+ now += PR_SecondsToInterval(tv.tv_sec)
+ + PR_MicrosecondsToInterval(tv.tv_usec);
+#else
+ now += PR_MillisecondsToInterval(lTimeout);
+#endif
+ }
+ } else {
+ now = PR_IntervalNow();
+ }
+ elapsed = (PRIntervalTime) (now - epoch);
+ if (elapsed >= timeout) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ } else {
+ remaining = timeout - elapsed;
+ }
+ }
+ } while (rv == 0 || (rv == -1 && syserror == EINTR));
+ break;
+ }
+ return(rv);
+}
+
+PRInt32
+_MD_Accept(PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ while ((rv = accept(osfd, (struct sockaddr*) addr, (int*)addrlen)) == -1)
+ {
+ err = sock_errno();
+ if ((err == EWOULDBLOCK) || (err == ECONNABORTED))
+ {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) {
+ goto done;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32
+_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRNetAddr addrCopy = *addr; /* Work around a bug in OS/2 where connect
+ * modifies the sockaddr structure.
+ * See Bugzilla bug 100776. */
+
+ /*
+ * We initiate the connection setup by making a nonblocking connect()
+ * call. If the connect() call fails, there are two cases we handle
+ * specially:
+ * 1. The connect() call was interrupted by a signal. In this case
+ * we simply retry connect().
+ * 2. The NSPR socket is nonblocking and connect() fails with
+ * EINPROGRESS. We first wait until the socket becomes writable.
+ * Then we try to find out whether the connection setup succeeded
+ * or failed.
+ */
+
+retry:
+ if ((rv = connect(osfd, (struct sockaddr *)&addrCopy, addrlen)) == -1)
+ {
+ err = sock_errno();
+
+ if (err == EINTR) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ goto retry;
+ }
+
+ if (!fd->secret->nonblocking && (err == EINPROGRESS))
+ {
+ /*
+ * socket_io_wait() may return -1 or 1.
+ */
+
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if (rv == -1) {
+ return -1;
+ }
+
+ PR_ASSERT(rv == 1);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ err = _MD_os2_get_nonblocking_connect_error(osfd);
+ if (err != 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ return -1;
+ }
+ return 0;
+ }
+
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+
+ return rv;
+} /* _MD_connect */
+
+PRInt32
+_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 rv, err;
+ rv = bind(fd->secret->md.osfd, (struct sockaddr *) addr, (int )addrlen);
+ if (rv < 0) {
+ err = sock_errno();
+ _PR_MD_MAP_BIND_ERROR(err);
+ }
+ return(rv);
+}
+
+
+PRInt32
+_PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
+{
+ PRInt32 rv, err;
+ rv = listen(fd->secret->md.osfd, backlog);
+ if (rv < 0) {
+ err = sock_errno();
+ _PR_MD_MAP_DEFAULT_ERROR(err);
+ }
+ return(rv);
+}
+
+
+PRInt32
+_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ while ((rv = recv(osfd,buf,amount,flags)) == -1)
+ {
+ err = sock_errno();
+ if ((err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) {
+ goto done;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_RECV_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32
+_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ while ((rv = send(osfd,buf,amount,flags)) == -1)
+ {
+ err = sock_errno();
+ if ((err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if ((rv = socket_io_wait(osfd, WRITE_FD, timeout)) < 0) {
+ goto done;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ /*
+ * optimization; if bytes sent is less than "amount" call
+ * select before returning. This is because it is likely that
+ * the next send() call will return EWOULDBLOCK.
+ */
+ if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount)
+ && (timeout != PR_INTERVAL_NO_WAIT))
+ {
+ if (socket_io_wait(osfd, WRITE_FD, timeout)< 0) {
+ rv = -1;
+ goto done;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_SEND_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32
+_PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ while ((rv = sendto(osfd, buf, amount, flags,
+ (struct sockaddr *) addr, addrlen)) == -1)
+ {
+ err = sock_errno();
+ if ((err == EWOULDBLOCK))
+ {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if ((rv = socket_io_wait(osfd, WRITE_FD, timeout)) < 0) {
+ goto done;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32
+_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ while( (*addrlen = PR_NETADDR_SIZE(addr)),
+ ((rv = recvfrom(osfd, buf, amount, flags,
+ (struct sockaddr *) addr, (int *)addrlen)) == -1))
+ {
+ err = sock_errno();
+ if ((err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) {
+ goto done;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32
+_PR_MD_WRITEV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 index, amount = 0;
+ PRInt32 osfd = fd->secret->md.osfd;
+ struct iovec osiov[PR_MAX_IOVECTOR_SIZE];
+
+ /* Ensured by PR_Writev */
+ PR_ASSERT(iov_size <= PR_MAX_IOVECTOR_SIZE);
+
+ /*
+ * We can't pass iov to so_writev because PRIOVec and struct iovec
+ * may not be binary compatible. Make osiov a copy of iov and
+ * pass osiov to so_writev .
+ */
+ for (index = 0; index < iov_size; index++) {
+ osiov[index].iov_base = iov[index].iov_base;
+ osiov[index].iov_len = iov[index].iov_len;
+ }
+
+ /*
+ * Calculate the total number of bytes to be sent; needed for
+ * optimization later.
+ * We could avoid this if this number was passed in; but it is
+ * probably not a big deal because iov_size is usually small (less than
+ * 3)
+ */
+ if (!fd->secret->nonblocking) {
+ for (index=0; index<iov_size; index++) {
+ amount += iov[index].iov_len;
+ }
+ }
+
+ while ((rv = so_writev(osfd, osiov, iov_size)) == -1) {
+ err = sock_errno();
+ if ((err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))<0) {
+ goto done;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ /*
+ * optimization; if bytes sent is less than "amount" call
+ * select before returning. This is because it is likely that
+ * the next writev() call will return EWOULDBLOCK.
+ */
+ if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount)
+ && (timeout != PR_INTERVAL_NO_WAIT)) {
+ if (socket_io_wait(osfd, WRITE_FD, timeout) < 0) {
+ rv = -1;
+ goto done;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_WRITEV_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32
+_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
+{
+ PRInt32 rv;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+ if (rv < 0) {
+ _PR_MD_MAP_SHUTDOWN_ERROR(sock_errno());
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_SOCKETPAIR(int af, int type, int flags, PRInt32 *osfd)
+{
+ PRInt32 rv, err;
+
+ rv = socketpair(af, type, flags, osfd);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_SOCKETPAIR_ERROR(err);
+ }
+ return rv;
+}
+
+PRStatus
+_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen)
+{
+ PRInt32 rv, err;
+
+ rv = getsockname(fd->secret->md.osfd,
+ (struct sockaddr *) addr, (int *)addrlen);
+ if (rv < 0) {
+ err = sock_errno();
+ _PR_MD_MAP_GETSOCKNAME_ERROR(err);
+ }
+ return rv==0?PR_SUCCESS:PR_FAILURE;
+}
+
+PRStatus
+_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen)
+{
+ PRInt32 rv, err;
+
+ rv = getpeername(fd->secret->md.osfd,
+ (struct sockaddr *) addr, (int *)addrlen);
+ if (rv < 0) {
+ err = sock_errno();
+ _PR_MD_MAP_GETPEERNAME_ERROR(err);
+ }
+ return rv==0?PR_SUCCESS:PR_FAILURE;
+}
+
+PRStatus
+_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname,
+ char* optval, PRInt32* optlen)
+{
+ PRInt32 rv, err;
+
+ rv = getsockopt(fd->secret->md.osfd, level, optname, optval, (int *)optlen);
+ if (rv < 0) {
+ err = sock_errno();
+ _PR_MD_MAP_GETSOCKOPT_ERROR(err);
+ }
+ return rv==0?PR_SUCCESS:PR_FAILURE;
+}
+
+PRStatus
+_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname,
+ const char* optval, PRInt32 optlen)
+{
+ PRInt32 rv, err;
+
+ rv = setsockopt(fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv < 0) {
+ err = sock_errno();
+ _PR_MD_MAP_SETSOCKOPT_ERROR(err);
+ }
+ return rv==0?PR_SUCCESS:PR_FAILURE;
+}
+
+void
+_MD_MakeNonblock(PRFileDesc *fd)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 err;
+ PRUint32 one = 1;
+
+ if (osfd <= 2) {
+ /* Don't mess around with stdin, stdout or stderr */
+ return;
+ }
+
+ err = so_ioctl( osfd, FIONBIO, (char *) &one, sizeof(one));
+ if ( err != 0 )
+ {
+ err = sock_errno();
+ _PR_MD_MAP_SOCKET_ERROR(err);
+ }
+}
diff --git a/nsprpub/pr/src/md/os2/os2thred.c b/nsprpub/pr/src/md/os2/os2thred.c
new file mode 100644
index 0000000000..aa929a1a23
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/os2thred.c
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#include <process.h> /* for _beginthread() */
+#include <signal.h>
+#include <float.h>
+
+/* --- globals ------------------------------------------------ */
+_NSPR_TLS* pThreadLocalStorage = 0;
+_PRInterruptTable _pr_interruptTable[] = { { 0 } };
+APIRET (* APIENTRY QueryThreadContext)(TID, ULONG, PCONTEXTRECORD);
+
+void
+_PR_MD_ENSURE_TLS(void)
+{
+ if(!pThreadLocalStorage)
+ {
+ /* Allocate thread local storage (TLS). Note, that only 32 bytes can
+ * be allocated at a time.
+ */
+ int rc = DosAllocThreadLocalMemory(sizeof(_NSPR_TLS) / 4, (PULONG*)&pThreadLocalStorage);
+ PR_ASSERT(rc == NO_ERROR);
+ memset(pThreadLocalStorage, 0, sizeof(_NSPR_TLS));
+ }
+}
+
+void
+_PR_MD_EARLY_INIT()
+{
+ HMODULE hmod;
+
+ if (DosLoadModule(NULL, 0, "DOSCALL1", &hmod) == 0)
+ DosQueryProcAddr(hmod, 877, "DOSQUERYTHREADCONTEXT",
+ (PFN *)&QueryThreadContext);
+}
+
+static void
+_pr_SetThreadMDHandle(PRThread *thread)
+{
+ PTIB ptib;
+ PPIB ppib;
+ PRUword rc;
+
+ rc = DosGetInfoBlocks(&ptib, &ppib);
+
+ thread->md.handle = ptib->tib_ptib2->tib2_ultid;
+}
+
+/* On OS/2, some system function calls seem to change the FPU control word,
+ * such that we crash with a floating underflow exception. The FIX_FPU() call
+ * in jsnum.c does not always work, as sometimes FIX_FPU() is called BEFORE the
+ * OS/2 system call that horks the FPU control word. So, we set an exception
+ * handler that covers any floating point exceptions and resets the FPU CW to
+ * the required value.
+ */
+static ULONG
+_System OS2_FloatExcpHandler(PEXCEPTIONREPORTRECORD p1,
+ PEXCEPTIONREGISTRATIONRECORD p2,
+ PCONTEXTRECORD p3,
+ PVOID pv)
+{
+#ifdef DEBUG_pedemonte
+ printf("Entering exception handler; ExceptionNum = %x\n", p1->ExceptionNum);
+ switch(p1->ExceptionNum) {
+ case XCPT_FLOAT_DENORMAL_OPERAND:
+ printf("got XCPT_FLOAT_DENORMAL_OPERAND\n");
+ break;
+ case XCPT_FLOAT_DIVIDE_BY_ZERO:
+ printf("got XCPT_FLOAT_DIVIDE_BY_ZERO\n");
+ break;
+ case XCPT_FLOAT_INEXACT_RESULT:
+ printf("got XCPT_FLOAT_INEXACT_RESULT\n");
+ break;
+ case XCPT_FLOAT_INVALID_OPERATION:
+ printf("got XCPT_FLOAT_INVALID_OPERATION\n");
+ break;
+ case XCPT_FLOAT_OVERFLOW:
+ printf("got XCPT_FLOAT_OVERFLOW\n");
+ break;
+ case XCPT_FLOAT_STACK_CHECK:
+ printf("got XCPT_FLOAT_STACK_CHECK\n");
+ break;
+ case XCPT_FLOAT_UNDERFLOW:
+ printf("got XCPT_FLOAT_UNDERFLOW\n");
+ break;
+ }
+#endif
+
+ switch(p1->ExceptionNum) {
+ case XCPT_FLOAT_DENORMAL_OPERAND:
+ case XCPT_FLOAT_DIVIDE_BY_ZERO:
+ case XCPT_FLOAT_INEXACT_RESULT:
+ case XCPT_FLOAT_INVALID_OPERATION:
+ case XCPT_FLOAT_OVERFLOW:
+ case XCPT_FLOAT_STACK_CHECK:
+ case XCPT_FLOAT_UNDERFLOW:
+ {
+ unsigned cw = p3->ctx_env[0];
+ if ((cw & MCW_EM) != MCW_EM) {
+ /* Mask out all floating point exceptions */
+ p3->ctx_env[0] |= MCW_EM;
+ /* Following two lines set precision to 53 bit mantissa. See jsnum.c */
+ p3->ctx_env[0] &= ~MCW_PC;
+ p3->ctx_env[0] |= PC_53;
+ return XCPT_CONTINUE_EXECUTION;
+ }
+ }
+ }
+ return XCPT_CONTINUE_SEARCH;
+}
+
+PR_IMPLEMENT(void)
+PR_OS2_SetFloatExcpHandler(EXCEPTIONREGISTRATIONRECORD* excpreg)
+{
+ /* setup the exception handler for the thread */
+ APIRET rv;
+ excpreg->ExceptionHandler = OS2_FloatExcpHandler;
+ excpreg->prev_structure = NULL;
+ rv = DosSetExceptionHandler(excpreg);
+ PR_ASSERT(rv == NO_ERROR);
+}
+
+PR_IMPLEMENT(void)
+PR_OS2_UnsetFloatExcpHandler(EXCEPTIONREGISTRATIONRECORD* excpreg)
+{
+ /* unset exception handler */
+ APIRET rv = DosUnsetExceptionHandler(excpreg);
+ PR_ASSERT(rv == NO_ERROR);
+}
+
+PRStatus
+_PR_MD_INIT_THREAD(PRThread *thread)
+{
+ APIRET rv;
+
+ if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) {
+ _pr_SetThreadMDHandle(thread);
+ }
+
+ /* Create the blocking IO semaphore */
+ rv = DosCreateEventSem(NULL, &(thread->md.blocked_sema), 0, 0);
+ return (rv == NO_ERROR) ? PR_SUCCESS : PR_FAILURE;
+}
+
+typedef struct param_store
+{
+ void (*start)(void *);
+ PRThread* thread;
+} PARAMSTORE;
+
+/* This is a small intermediate function that sets/unsets the exception
+ handler before calling the initial thread function */
+static void
+ExcpStartFunc(void* arg)
+{
+ EXCEPTIONREGISTRATIONRECORD excpreg;
+ PARAMSTORE params, *pParams = arg;
+
+ PR_OS2_SetFloatExcpHandler(&excpreg);
+
+ params = *pParams;
+ PR_Free(pParams);
+ params.start(params.thread);
+
+ PR_OS2_UnsetFloatExcpHandler(&excpreg);
+}
+
+PRStatus
+_PR_MD_CREATE_THREAD(PRThread *thread,
+ void (*start)(void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PARAMSTORE* params = PR_Malloc(sizeof(PARAMSTORE));
+ params->start = start;
+ params->thread = thread;
+ thread->md.handle = thread->id = (TID) _beginthread(ExcpStartFunc,
+ NULL,
+ thread->stack->stackSize,
+ params);
+ if(thread->md.handle == -1) {
+ return PR_FAILURE;
+ }
+
+ /*
+ * On OS/2, a thread is created with a thread priority of
+ * THREAD_PRIORITY_NORMAL
+ */
+
+ if (priority != PR_PRIORITY_NORMAL) {
+ _PR_MD_SET_PRIORITY(&(thread->md), priority);
+ }
+
+ return PR_SUCCESS;
+}
+
+void
+_PR_MD_YIELD(void)
+{
+ /* Isn't there some problem with DosSleep(0) on OS/2? */
+ DosSleep(0);
+}
+
+void
+_PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri)
+{
+ int nativePri = PRTYC_NOCHANGE;
+ BOOL rv;
+
+ if (newPri < PR_PRIORITY_FIRST) {
+ newPri = PR_PRIORITY_FIRST;
+ } else if (newPri > PR_PRIORITY_LAST) {
+ newPri = PR_PRIORITY_LAST;
+ }
+ switch (newPri) {
+ case PR_PRIORITY_LOW:
+ case PR_PRIORITY_NORMAL:
+ nativePri = PRTYC_REGULAR;
+ break;
+ case PR_PRIORITY_HIGH:
+ nativePri = PRTYC_FOREGROUNDSERVER;
+ break;
+ case PR_PRIORITY_URGENT:
+ nativePri = PRTYC_TIMECRITICAL;
+ }
+ rv = DosSetPriority(PRTYS_THREAD, nativePri, 0, thread->handle);
+ PR_ASSERT(rv == NO_ERROR);
+ if (rv != NO_ERROR) {
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("PR_SetThreadPriority: can't set thread priority\n"));
+ }
+ return;
+}
+
+void
+_PR_MD_CLEAN_THREAD(PRThread *thread)
+{
+ APIRET rv;
+
+ if (thread->md.blocked_sema) {
+ rv = DosCloseEventSem(thread->md.blocked_sema);
+ PR_ASSERT(rv == NO_ERROR);
+ thread->md.blocked_sema = 0;
+ }
+
+ if (thread->md.handle) {
+ thread->md.handle = 0;
+ }
+}
+
+void
+_PR_MD_EXIT_THREAD(PRThread *thread)
+{
+ _PR_MD_CLEAN_THREAD(thread);
+ _PR_MD_SET_CURRENT_THREAD(NULL);
+}
+
+
+void
+_PR_MD_EXIT(PRIntn status)
+{
+ _exit(status);
+}
+
+#ifdef HAVE_THREAD_AFFINITY
+PR_EXTERN(PRInt32)
+_PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask )
+{
+ /* Can we do this on OS/2? Only on SMP versions? */
+ PR_NOT_REACHED("Not implemented");
+ return 0;
+
+ /* This is what windows does:
+ int rv;
+
+ rv = SetThreadAffinityMask(thread->md.handle, mask);
+
+ return rv?0:-1;
+ */
+}
+
+PR_EXTERN(PRInt32)
+_PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask)
+{
+ /* Can we do this on OS/2? Only on SMP versions? */
+ PR_NOT_REACHED("Not implemented");
+ return 0;
+
+ /* This is what windows does:
+ PRInt32 rv, system_mask;
+
+ rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask);
+
+ return rv?0:-1;
+ */
+}
+#endif /* HAVE_THREAD_AFFINITY */
+
+void
+_PR_MD_SUSPEND_CPU(_PRCPU *cpu)
+{
+ _PR_MD_SUSPEND_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_RESUME_CPU(_PRCPU *cpu)
+{
+ _PR_MD_RESUME_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_SUSPEND_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ APIRET rc;
+
+ /* XXXMB - DosSuspendThread() is not a blocking call; how do we
+ * know when the thread is *REALLY* suspended?
+ */
+ rc = DosSuspendThread(thread->md.handle);
+ PR_ASSERT(rc == NO_ERROR);
+ }
+}
+
+void
+_PR_MD_RESUME_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ DosResumeThread(thread->md.handle);
+ }
+}
+
+
+PRThread*
+_MD_CURRENT_THREAD(void)
+{
+ PRThread *thread;
+
+ thread = _MD_GET_ATTACHED_THREAD();
+
+ if (NULL == thread) {
+ thread = _PRI_AttachThread(PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0);
+ }
+
+ PR_ASSERT(thread != NULL);
+ return thread;
+}
+
diff --git a/nsprpub/pr/src/md/os2/os2vaclegacy.s b/nsprpub/pr/src/md/os2/os2vaclegacy.s
new file mode 100644
index 0000000000..3dc1468752
--- /dev/null
+++ b/nsprpub/pr/src/md/os2/os2vaclegacy.s
@@ -0,0 +1,45 @@
+/ -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+/
+/ This Source Code Form is subject to the terms of the Mozilla Public
+/ License, v. 2.0. If a copy of the MPL was not distributed with this
+/ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ .text
+ .align 4
+ .globl PR_NewMonitor
+PR_NewMonitor:
+ jmp _PR_NewMonitor
+
+ .align 4
+ .globl PR_EnterMonitor
+PR_EnterMonitor:
+ mov %eax, 4(%esp)
+ jmp _PR_EnterMonitor
+
+ .align 4
+ .globl PR_ExitMonitor
+PR_ExitMonitor:
+ mov %eax, 4(%esp)
+ jmp _PR_ExitMonitor
+
+
+
+ .align 4
+ .globl PR_AttachThread
+PR_AttachThread:
+ mov %eax, 4(%esp)
+ mov %edx, 8(%esp)
+ mov %ecx, 12(%esp)
+ jmp _PR_AttachThread
+
+ .align 4
+ .globl PR_DetachThread
+PR_DetachThread:
+ jmp _PR_DetachThread
+
+ .align 4
+ .globl PR_GetCurrentThread
+PR_GetCurrentThread:
+ jmp _PR_GetCurrentThread
+
+
diff --git a/nsprpub/pr/src/md/prosdep.c b/nsprpub/pr/src/md/prosdep.c
new file mode 100644
index 0000000000..0b1f3e597a
--- /dev/null
+++ b/nsprpub/pr/src/md/prosdep.c
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prbit.h"
+#include "prsystem.h"
+
+#ifdef XP_UNIX
+#include <unistd.h>
+#endif
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+PRInt32 _pr_pageShift;
+PRInt32 _pr_pageSize;
+
+/*
+** Get system page size
+*/
+static void GetPageSize(void)
+{
+ PRInt32 pageSize;
+
+ /* Get page size */
+#ifdef XP_UNIX
+#if defined BSDI || defined AIX \
+ || defined LINUX || defined __GNU__ || defined __GLIBC__ \
+ || defined FREEBSD || defined NETBSD || defined OPENBSD \
+ || defined DARWIN
+ _pr_pageSize = getpagesize();
+#elif defined(HPUX)
+ /* I have no idea. Don't get me started. --Rob */
+ _pr_pageSize = sysconf(_SC_PAGE_SIZE);
+#else
+ _pr_pageSize = sysconf(_SC_PAGESIZE);
+#endif
+#endif /* XP_UNIX */
+
+#ifdef XP_PC
+#ifdef _WIN32
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+ _pr_pageSize = info.dwPageSize;
+#else
+ _pr_pageSize = 4096;
+#endif
+#endif /* XP_PC */
+
+ pageSize = _pr_pageSize;
+ PR_CEILING_LOG2(_pr_pageShift, pageSize);
+}
+
+PR_IMPLEMENT(PRInt32) PR_GetPageShift(void)
+{
+ if (!_pr_pageSize) {
+ GetPageSize();
+ }
+ return _pr_pageShift;
+}
+
+PR_IMPLEMENT(PRInt32) PR_GetPageSize(void)
+{
+ if (!_pr_pageSize) {
+ GetPageSize();
+ }
+ return _pr_pageSize;
+}
diff --git a/nsprpub/pr/src/md/unix/Makefile.in b/nsprpub/pr/src/md/unix/Makefile.in
new file mode 100644
index 0000000000..f241840f8b
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/Makefile.in
@@ -0,0 +1,99 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+MOD_DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+CSRCS = \
+ unix.c \
+ unix_errors.c \
+ uxproces.c \
+ uxrng.c \
+ uxshm.c \
+ uxwrap.c \
+ $(NULL)
+
+ifneq ($(USE_PTHREADS),1)
+CSRCS += uxpoll.c
+endif
+
+ifeq ($(PTHREADS_USER),1)
+CSRCS += pthreads_user.c
+endif
+
+CSRCS += $(PR_MD_CSRCS)
+ASFILES += $(PR_MD_ASFILES)
+
+TARGETS = $(OBJS)
+
+ifeq ($(OS_ARCH),SunOS)
+ ifeq ($(CPU_ARCH),sparc)
+ ifdef USE_64
+ ULTRASPARC_ASFILES = os_SunOS_sparcv9.s
+ ULTRASPARC_ASOBJS = $(addprefix $(OBJDIR)/,$(ULTRASPARC_ASFILES:.s=.$(OBJ_SUFFIX)))
+ else
+ LIBRARY_NAME = $(ULTRASPARC_LIBRARY)
+ LIBRARY_VERSION = $(MOD_MAJOR_VERSION)
+ ULTRASPARC_ASFILES = os_SunOS_ultrasparc.s
+ ULTRASPARC_ASOBJS = $(addprefix $(OBJDIR)/,$(ULTRASPARC_ASFILES:.s=.$(OBJ_SUFFIX)))
+ TARGETS += $(ULTRASPARC_ASOBJS) $(SHARED_LIBRARY)
+ RELEASE_LIBS = $(SHARED_LIBRARY)
+ RELEASE_LIBS_DEST = $(RELEASE_LIB_DIR)/cpu/sparcv8plus
+ lib_subdir = cpu/sparcv8plus
+ endif
+ endif
+endif
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(CPU_ARCH),sparc)
+
+ifdef USE_64
+$(ULTRASPARC_ASOBJS): $(ULTRASPARC_ASFILES)
+ /usr/ccs/bin/as -o $@ -K PIC -P -D_ASM -D__STDC__=0 -xarch=v9 $<
+else
+$(SHARED_LIBRARY): $(ULTRASPARC_ASOBJS)
+ $(LD) -G -z text -z endfiltee -o $@ $(ULTRASPARC_ASOBJS)
+ $(INSTALL) -m 444 $@ $(dist_libdir)/cpu/sparcv8plus
+ $(INSTALL) -m 444 $@ $(dist_bindir)/cpu/sparcv8plus
+#
+# The -f $(ORIGIN)/... linker flag uses the real file, after symbolic links
+# are resolved, as the origin. If NSDISTMODE is not "copy", libnspr4.so
+# will be installed as a symbolic link in $(dist_libdir), pointing to the
+# real libnspr4.so file in pr/src. Therefore we need to install an
+# additional copy of libnspr_flt4.so in pr/src/cpu/sparcv8plus.
+#
+ifneq ($(NSDISTMODE),copy)
+ $(INSTALL) -m 444 $@ ../../cpu/sparcv8plus
+endif
+
+ifneq ($(NSDISTMODE),copy)
+clobber realclean clobber_all distclean::
+ rm -rf ../../cpu
+endif
+
+$(ULTRASPARC_ASOBJS): $(ULTRASPARC_ASFILES)
+ /usr/ccs/bin/as -o $@ -K PIC -P -D_ASM -D__STDC__=0 -xarch=v8plus $<
+
+clean::
+ rm -rf $(ULTRASPARC_ASOBJS)
+endif
+
+endif
+endif
diff --git a/nsprpub/pr/src/md/unix/aix.c b/nsprpub/pr/src/md/unix/aix.c
new file mode 100644
index 0000000000..c632c5790e
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/aix.c
@@ -0,0 +1,302 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#ifdef AIX_HAVE_ATOMIC_OP_H
+#include <sys/atomic_op.h>
+
+PRInt32 _AIX_AtomicSet(PRInt32 *val, PRInt32 newval)
+{
+ PRIntn oldval;
+ boolean_t stored;
+ oldval = fetch_and_add((atomic_p)val, 0);
+ do
+ {
+ stored = compare_and_swap((atomic_p)val, &oldval, newval);
+ } while (!stored);
+ return oldval;
+} /* _AIX_AtomicSet */
+#endif /* AIX_HAVE_ATOMIC_OP_H */
+
+#if defined(AIX_TIMERS)
+
+#include <sys/time.h>
+
+static PRUint32 _aix_baseline_epoch;
+
+static void _MD_AixIntervalInit(void)
+{
+ timebasestruct_t real_time;
+ read_real_time(&real_time, TIMEBASE_SZ);
+ (void)time_base_to_time(&real_time, TIMEBASE_SZ);
+ _aix_baseline_epoch = real_time.tb_high;
+} /* _MD_AixIntervalInit */
+
+PRIntervalTime _MD_AixGetInterval(void)
+{
+ PRIntn rv;
+ PRUint64 temp;
+ timebasestruct_t real_time;
+ read_real_time(&real_time, TIMEBASE_SZ);
+ (void)time_base_to_time(&real_time, TIMEBASE_SZ);
+ /* tb_high is in seconds, tb_low in 10(-9)seconds */
+ temp = 1000000000ULL * (PRUint64)(real_time.tb_high - _aix_baseline_epoch);
+ temp += (PRUint64)real_time.tb_low; /* everything's 10(-9) seconds */
+ temp >>= 16; /* now it's something way different */
+ return (PRIntervalTime)temp;
+} /* _MD_AixGetInterval */
+
+PRIntervalTime _MD_AixIntervalPerSec(void)
+{
+ return 1000000000ULL >> 16; /* that's 15258, I think */
+} /* _MD_AixIntervalPerSec */
+
+#endif /* defined(AIX_TIMERS) */
+
+#if !defined(PTHREADS_USER)
+
+#if defined(_PR_PTHREADS)
+
+/*
+ * AIX 4.3 has sched_yield(). AIX 4.2 has pthread_yield().
+ * So we look up the appropriate function pointer at run time.
+ */
+
+#include <dlfcn.h>
+
+int (*_PT_aix_yield_fcn)() = NULL;
+int _pr_aix_send_file_use_disabled = 0;
+
+void _MD_EarlyInit(void)
+{
+ void *main_app_handle;
+ char *evp;
+
+ main_app_handle = dlopen(NULL, RTLD_NOW);
+ PR_ASSERT(NULL != main_app_handle);
+
+ _PT_aix_yield_fcn = (int(*)())dlsym(main_app_handle, "sched_yield");
+ if (!_PT_aix_yield_fcn) {
+ _PT_aix_yield_fcn = (int(*)())dlsym(main_app_handle,"pthread_yield");
+ PR_ASSERT(NULL != _PT_aix_yield_fcn);
+ }
+ dlclose(main_app_handle);
+
+ if (evp = getenv("NSPR_AIX_SEND_FILE_USE_DISABLED")) {
+ if (1 == atoi(evp)) {
+ _pr_aix_send_file_use_disabled = 1;
+ }
+ }
+
+#if defined(AIX_TIMERS)
+ _MD_AixIntervalInit();
+#endif
+}
+
+#else /* _PR_PTHREADS */
+
+void _MD_EarlyInit(void)
+{
+#if defined(AIX_TIMERS)
+ _MD_AixIntervalInit();
+#endif
+}
+
+#endif /* _PR_PTHREADS */
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifndef _PR_PTHREADS
+PR_IMPLEMENT(void)
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for AIX */
+PR_IMPLEMENT(void)
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for AIX.");
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for AIX.");
+}
+#endif /* _PR_PTHREADS */
+#endif /* PTHREADS_USER */
+
+/*
+ * NSPR 2.0 overrides the system select() and poll() functions.
+ * On AIX 4.2, we use dlopen("/unix", RTLD_NOW) and dlsym() to get
+ * at the original system select() and poll() functions.
+ */
+
+#if !defined(AIX_RENAME_SELECT)
+
+#include <sys/select.h>
+#include <sys/poll.h>
+#include <dlfcn.h>
+
+static int (*aix_select_fcn)() = NULL;
+static int (*aix_poll_fcn)() = NULL;
+
+int _MD_SELECT(int width, fd_set *r, fd_set *w, fd_set *e, struct timeval *t)
+{
+ int rv;
+
+ if (!aix_select_fcn) {
+ void *aix_handle;
+
+ aix_handle = dlopen("/unix", RTLD_NOW);
+ if (!aix_handle) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ aix_select_fcn = (int(*)())dlsym(aix_handle,"select");
+ dlclose(aix_handle);
+ if (!aix_select_fcn) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ }
+ rv = (*aix_select_fcn)(width, r, w, e, t);
+ return rv;
+}
+
+int _MD_POLL(void *listptr, unsigned long nfds, long timeout)
+{
+ int rv;
+
+ if (!aix_poll_fcn) {
+ void *aix_handle;
+
+ aix_handle = dlopen("/unix", RTLD_NOW);
+ if (!aix_handle) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ aix_poll_fcn = (int(*)())dlsym(aix_handle,"poll");
+ dlclose(aix_handle);
+ if (!aix_poll_fcn) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ }
+ rv = (*aix_poll_fcn)(listptr, nfds, timeout);
+ return rv;
+}
+
+#else
+
+/*
+ * In AIX versions prior to 4.2, we use the two-step rename/link trick.
+ * The binary must contain at least one "poll" symbol for linker's rename
+ * to work. So we must have this dummy function that references poll().
+ */
+#include <sys/poll.h>
+void _pr_aix_dummy()
+{
+ poll(0,0,0);
+}
+
+#endif /* !defined(AIX_RENAME_SELECT) */
+
+#ifdef _PR_HAVE_ATOMIC_CAS
+
+#include "pratom.h"
+
+#define _PR_AIX_ATOMIC_LOCK -1
+
+PR_IMPLEMENT(void)
+PR_StackPush(PRStack *stack, PRStackElem *stack_elem)
+{
+ PRStackElem *addr;
+ boolean_t locked = TRUE;
+
+ /* Is it safe to cast a pointer to an int? */
+ PR_ASSERT(sizeof(int) == sizeof(PRStackElem *));
+ do {
+ while ((addr = stack->prstk_head.prstk_elem_next) ==
+ (PRStackElem *)_PR_AIX_ATOMIC_LOCK)
+ ;
+ locked = _check_lock((atomic_p) &stack->prstk_head.prstk_elem_next,
+ (int) addr, _PR_AIX_ATOMIC_LOCK);
+ } while (locked == TRUE);
+ stack_elem->prstk_elem_next = addr;
+ _clear_lock((atomic_p)&stack->prstk_head.prstk_elem_next, (int)stack_elem);
+ return;
+}
+
+PR_IMPLEMENT(PRStackElem *)
+PR_StackPop(PRStack *stack)
+{
+ PRStackElem *element;
+ boolean_t locked = TRUE;
+
+ /* Is it safe to cast a pointer to an int? */
+ PR_ASSERT(sizeof(int) == sizeof(PRStackElem *));
+ do {
+ while ((element = stack->prstk_head.prstk_elem_next) ==
+ (PRStackElem *) _PR_AIX_ATOMIC_LOCK)
+ ;
+ locked = _check_lock((atomic_p) &stack->prstk_head.prstk_elem_next,
+ (int)element, _PR_AIX_ATOMIC_LOCK);
+ } while (locked == TRUE);
+
+ if (element == NULL) {
+ _clear_lock((atomic_p) &stack->prstk_head.prstk_elem_next, NULL);
+ } else {
+ _clear_lock((atomic_p) &stack->prstk_head.prstk_elem_next,
+ (int) element->prstk_elem_next);
+ }
+ return element;
+}
+
+#endif /* _PR_HAVE_ATOMIC_CAS */
diff --git a/nsprpub/pr/src/md/unix/aixwrap.c b/nsprpub/pr/src/md/unix/aixwrap.c
new file mode 100644
index 0000000000..853825196b
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/aixwrap.c
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: aixwrap.c
+ * Description:
+ * This file contains a single function, _MD_SELECT(), which simply
+ * invokes the select() function. This file is used in an ugly
+ * hack to override the system select() function on AIX releases
+ * prior to 4.2. (On AIX 4.2, we use a different mechanism to
+ * override select().)
+ */
+
+#ifndef AIX_RENAME_SELECT
+#error aixwrap.c should only be used on AIX 3.2 or 4.1
+#else
+
+#include <sys/select.h>
+#include <sys/poll.h>
+
+int _MD_SELECT(int width, fd_set *r, fd_set *w, fd_set *e, struct timeval *t)
+{
+ return select(width, r, w, e, t);
+}
+
+int _MD_POLL(void *listptr, unsigned long nfds, long timeout)
+{
+ return poll(listptr, nfds, timeout);
+}
+
+#endif /* AIX_RENAME_SELECT */
diff --git a/nsprpub/pr/src/md/unix/bsdi.c b/nsprpub/pr/src/md/unix/bsdi.c
new file mode 100644
index 0000000000..e625003f73
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/bsdi.c
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <signal.h>
+
+void _MD_EarlyInit(void)
+{
+ /*
+ * Ignore FPE because coercion of a NaN to an int causes SIGFPE
+ * to be raised.
+ */
+ struct sigaction act;
+
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGFPE, &act, 0);
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifndef _PR_PTHREADS
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for BSDI */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for BSDI.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for BSDI.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
diff --git a/nsprpub/pr/src/md/unix/darwin.c b/nsprpub/pr/src/md/unix/darwin.c
new file mode 100644
index 0000000000..6135e082ff
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/darwin.c
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <mach/mach_time.h>
+
+void _MD_EarlyInit(void)
+{
+}
+
+/*
+ * The multiplier (as a fraction) for converting the Mach absolute time
+ * unit to nanoseconds.
+ */
+static mach_timebase_info_data_t machTimebaseInfo;
+
+void _PR_Mach_IntervalInit(void)
+{
+ kern_return_t rv;
+
+ rv = mach_timebase_info(&machTimebaseInfo);
+ PR_ASSERT(rv == KERN_SUCCESS);
+}
+
+PRIntervalTime _PR_Mach_GetInterval(void)
+{
+ uint64_t time;
+
+ /*
+ * mach_absolute_time returns the time in the Mach absolute time unit.
+ * Convert it to milliseconds. See Mac Technical Q&A QA1398.
+ */
+ time = mach_absolute_time();
+ time = time * machTimebaseInfo.numer / machTimebaseInfo.denom /
+ PR_NSEC_PER_MSEC;
+ return (PRIntervalTime)time;
+} /* _PR_Mach_GetInterval */
+
+PRIntervalTime _PR_Mach_TicksPerSecond(void)
+{
+ return 1000;
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#if !defined(_PR_PTHREADS)
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#if !defined(_PR_PTHREADS)
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for Darwin */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for Darwin.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Darwin.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
+
+/* darwin.c */
+
diff --git a/nsprpub/pr/src/md/unix/dgux.c b/nsprpub/pr/src/md/unix/dgux.c
new file mode 100644
index 0000000000..a1b3602c5a
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/dgux.c
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+/*
+ * using only NSPR threads here
+ *
+ * Copied from the UnixWare implementation. Should be kept in sync
+ * with ../../../include/md/_dgux.h.
+ */
+
+#include <setjmp.h>
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for DG/UX */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for DG/UX.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for DG/UX.");
+}
+
diff --git a/nsprpub/pr/src/md/unix/freebsd.c b/nsprpub/pr/src/md/unix/freebsd.c
new file mode 100644
index 0000000000..19af17a440
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/freebsd.c
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <signal.h>
+
+void _MD_EarlyInit(void)
+{
+ /*
+ * Ignore FPE because coercion of a NaN to an int causes SIGFPE
+ * to be raised.
+ */
+ struct sigaction act;
+
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGFPE, &act, 0);
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) sigsetjmp(CONTEXT(t), 1);
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifndef _PR_PTHREADS
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for FreeBSD */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for FreeBSD.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for FreeBSD.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
diff --git a/nsprpub/pr/src/md/unix/hpux.c b/nsprpub/pr/src/md/unix/hpux.c
new file mode 100644
index 0000000000..bf72567fd5
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/hpux.c
@@ -0,0 +1,231 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#include <setjmp.h>
+
+#if defined(HPUX_LW_TIMER)
+
+#include <machine/inline.h>
+#include <machine/clock.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/pstat.h>
+
+int __lw_get_thread_times(int which, int64_t *sample, int64_t *time);
+
+static double msecond_per_itick;
+
+void _PR_HPUX_LW_IntervalInit(void)
+{
+ struct pst_processor psp;
+ int iticksperclktick, clk_tck;
+ int rv;
+
+ rv = pstat_getprocessor(&psp, sizeof(psp), 1, 0);
+ PR_ASSERT(rv != -1);
+
+ iticksperclktick = psp.psp_iticksperclktick;
+ clk_tck = sysconf(_SC_CLK_TCK);
+ msecond_per_itick = (1000.0)/(double)(iticksperclktick * clk_tck);
+}
+
+PRIntervalTime _PR_HPUX_LW_GetInterval(void)
+{
+ int64_t time, sample;
+
+ __lw_get_thread_times(1, &sample, &time);
+ /*
+ * Division is slower than float multiplication.
+ * return (time / iticks_per_msecond);
+ */
+ return (time * msecond_per_itick);
+}
+#endif /* HPUX_LW_TIMER */
+
+#if !defined(PTHREADS_USER)
+
+void _MD_EarlyInit(void)
+{
+#ifndef _PR_PTHREADS
+ /*
+ * The following piece of code is taken from ns/nspr/src/md_HP-UX.c.
+ * In the comment for revision 1.6, dated 1995/09/11 23:33:34,
+ * robm says:
+ * This version has some problems which need to be addressed.
+ * First, intercept all system calls and prevent them from
+ * executing the library code which performs stack switches
+ * before normal system call invocation. In order for library
+ * calls which make system calls to work (like stdio), however,
+ * we must also allocate our own stack and switch the primordial
+ * stack to use it. This isn't so bad, except that I fudged the
+ * backtrace length when copying the old stack to the new one.
+ *
+ * This is the original comment of robm in the code:
+ * XXXrobm Horrific. To avoid a problem with HP's system call
+ * code, we allocate a new stack for the primordial thread and
+ * use it. However, we don't know how far back the original stack
+ * goes. We should create a routine that performs a backtrace and
+ * finds out just how much we need to copy. As a temporary measure,
+ * I just copy an arbitrary guess.
+ *
+ * In an email to servereng dated 2 Jan 1997, Mike Patnode (mikep)
+ * suggests that this only needs to be done for HP-UX 9.
+ */
+#ifdef HPUX9
+#define PIDOOMA_STACK_SIZE 524288
+#define BACKTRACE_SIZE 8192
+ {
+ jmp_buf jb;
+ char *newstack;
+ char *oldstack;
+
+ if(!setjmp(jb)) {
+ newstack = (char *) PR_MALLOC(PIDOOMA_STACK_SIZE);
+ oldstack = (char *) (*(((int *) jb) + 1) - BACKTRACE_SIZE);
+ memcpy(newstack, oldstack, BACKTRACE_SIZE);
+ *(((int *) jb) + 1) = (int) (newstack + BACKTRACE_SIZE);
+ longjmp(jb, 1);
+ }
+ }
+#endif /* HPUX9 */
+#endif /* !_PR_PTHREADS */
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifndef _PR_PTHREADS
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for HP-UX */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for HP-UX.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for HP-UX.");
+}
+#endif /* _PR_PTHREADS */
+
+void
+_MD_suspend_thread(PRThread *thread)
+{
+#ifdef _PR_PTHREADS
+#endif
+}
+
+void
+_MD_resume_thread(PRThread *thread)
+{
+#ifdef _PR_PTHREADS
+#endif
+}
+#endif /* PTHREADS_USER */
+
+/*
+ * The HP version of strchr is buggy. It looks past the end of the
+ * string and causes a segmentation fault when our (NSPR) version
+ * of malloc is used.
+ *
+ * A better solution might be to put a cushion in our malloc just in
+ * case HP's version of strchr somehow gets used instead of this one.
+ */
+char *
+strchr(const char *s, int c)
+{
+ char ch;
+
+ if (!s) {
+ return NULL;
+ }
+
+ ch = (char) c;
+
+ while ((*s) && ((*s) != ch)) {
+ s++;
+ }
+
+ if ((*s) == ch) {
+ return (char *) s;
+ }
+
+ return NULL;
+}
+
+/*
+ * Implemementation of memcmp in HP-UX (verified on releases A.09.03,
+ * A.09.07, and B.10.10) dumps core if called with:
+ * 1. First operand with address = 1(mod 4).
+ * 2. Size = 1(mod 4)
+ * 3. Last byte of the second operand is the last byte of the page and
+ * next page is not accessible(not mapped or protected)
+ * Thus, using the following naive version (tons of optimizations are
+ * possible;^)
+ */
+
+int memcmp(const void *s1, const void *s2, size_t n)
+{
+ register unsigned char *p1 = (unsigned char *) s1,
+ *p2 = (unsigned char *) s2;
+
+ while (n-- > 0) {
+ register int r = ((int) ((unsigned int) *p1))
+ - ((int) ((unsigned int) *p2));
+ if (r) {
+ return r;
+ }
+ p1++; p2++;
+ }
+ return 0;
+}
diff --git a/nsprpub/pr/src/md/unix/linux.c b/nsprpub/pr/src/md/unix/linux.c
new file mode 100644
index 0000000000..6bfc7a8024
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/linux.c
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifdef _PR_PTHREADS
+
+extern void _MD_unix_terminate_waitpid_daemon(void);
+
+void _MD_CleanupBeforeExit(void)
+{
+ _MD_unix_terminate_waitpid_daemon();
+}
+
+#else /* ! _PR_PTHREADS */
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ /*
+ * set the pointers to the stack-pointer and frame-pointer words in the
+ * context structure; this is for debugging use.
+ */
+ thread->md.sp = _MD_GET_SP_PTR(thread);
+ thread->md.fp = _MD_GET_FP_PTR(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for Linux */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for Linux.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Linux.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
diff --git a/nsprpub/pr/src/md/unix/netbsd.c b/nsprpub/pr/src/md/unix/netbsd.c
new file mode 100644
index 0000000000..aa3618a31a
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/netbsd.c
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <signal.h>
+#include <poll.h>
+#include <sys/syscall.h>
+
+void _MD_EarlyInit(void)
+{
+ /*
+ * Ignore FPE because coercion of a NaN to an int causes SIGFPE
+ * to be raised.
+ */
+ struct sigaction act;
+
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGFPE, &act, 0);
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) sigsetjmp(CONTEXT(t), 1);
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifndef _PR_PTHREADS
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for NetBSD */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for NetBSD.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for NetBSD.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
diff --git a/nsprpub/pr/src/md/unix/nto.c b/nsprpub/pr/src/md/unix/nto.c
new file mode 100644
index 0000000000..8ab8b1eab8
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/nto.c
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <setjmp.h>
+
+/* Fake this out */
+int socketpair (int foo, int foo2, int foo3, int sv[2])
+{
+ printf("error in socketpair\n");
+ exit (-1);
+}
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
diff --git a/nsprpub/pr/src/md/unix/objs.mk b/nsprpub/pr/src/md/unix/objs.mk
new file mode 100644
index 0000000000..77eaa6d13b
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/objs.mk
@@ -0,0 +1,31 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# This makefile appends to the variable OBJS the platform-dependent
+# object modules that will be part of the nspr20 library.
+
+CSRCS = \
+ unix.c \
+ unix_errors.c \
+ uxproces.c \
+ uxrng.c \
+ uxshm.c \
+ uxwrap.c \
+ $(NULL)
+
+ifneq ($(USE_PTHREADS),1)
+CSRCS += uxpoll.c
+endif
+
+ifeq ($(PTHREADS_USER),1)
+CSRCS += pthreads_user.c
+endif
+
+CSRCS += $(PR_MD_CSRCS)
+ASFILES += $(PR_MD_ASFILES)
+
+OBJS += $(addprefix md/unix/$(OBJDIR)/,$(CSRCS:.c=.$(OBJ_SUFFIX))) \
+ $(addprefix md/unix/$(OBJDIR)/,$(ASFILES:.s=.$(OBJ_SUFFIX)))
+
diff --git a/nsprpub/pr/src/md/unix/openbsd.c b/nsprpub/pr/src/md/unix/openbsd.c
new file mode 100644
index 0000000000..b7f0a29954
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/openbsd.c
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <signal.h>
+#include <poll.h>
+#include <sys/syscall.h>
+
+void _MD_EarlyInit(void)
+{
+ /*
+ * Ignore FPE because coercion of a NaN to an int causes SIGFPE
+ * to be raised.
+ */
+ struct sigaction act;
+
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGFPE, &act, 0);
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) sigsetjmp(CONTEXT(t), 1);
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifndef _PR_PTHREADS
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for OpenBSD */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for OpenBSD.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for OpenBSD.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
diff --git a/nsprpub/pr/src/md/unix/os_AIX.s b/nsprpub/pr/src/md/unix/os_AIX.s
new file mode 100644
index 0000000000..0227cc47f8
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_AIX.s
@@ -0,0 +1,91 @@
+# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+.set r0,0; .set SP,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+
+
+ .rename H.10.NO_SYMBOL{PR},""
+ .rename H.18.longjmp{TC},"longjmp"
+
+ .lglobl H.10.NO_SYMBOL{PR}
+ .globl .longjmp
+ .globl longjmp{DS}
+ .extern .sigcleanup
+ .extern .jmprestfpr
+
+# .text section
+
+ .csect H.10.NO_SYMBOL{PR}
+.longjmp:
+ mr r13,r3
+ mr r14,r4
+ stu SP,-56(SP)
+ bl .sigcleanup
+ l RTOC,0x14(SP)
+ cal SP,0x38(SP)
+ mr r3,r13
+ mr r4,r14
+ l r5,0x8(r3)
+ l SP,0xc(r3)
+ l r7,0xf8(r3)
+ st r7,0x0(SP)
+ l RTOC,0x10(r3)
+ bl .jmprestfpr
+# 1 == cr0 in disassembly
+ cmpi 1,r4,0x0
+ mtlr r5
+ lm r13,0x14(r3)
+ l r5,0x60(r3)
+ mtcrf 0x38,r5
+ mr r3,r4
+ bne __L1
+ lil r3,0x1
+__L1:
+ br
+
+# traceback table
+ .long 0x00000000
+ .byte 0x00 # VERSION=0
+ .byte 0x00 # LANG=TB_C
+ .byte 0x20 # IS_GL=0,IS_EPROL=0,HAS_TBOFF=1
+ # INT_PROC=0,HAS_CTL=0,TOCLESS=0
+ # FP_PRESENT=0,LOG_ABORT=0
+ .byte 0x40 # INT_HNDL=0,NAME_PRESENT=1
+ # USES_ALLOCA=0,CL_DIS_INV=WALK_ONCOND
+ # SAVES_CR=0,SAVES_LR=0
+ .byte 0x80 # STORES_BC=1,FPR_SAVED=0
+ .byte 0x00 # GPR_SAVED=0
+ .byte 0x02 # FIXEDPARMS=2
+ .byte 0x01 # FLOATPARMS=0,PARMSONSTK=1
+ .long 0x00000000 #
+ .long 0x00000014 # TB_OFFSET
+ .short 7 # NAME_LEN
+ .byte "longjmp"
+ .byte 0 # padding
+ .byte 0 # padding
+ .byte 0 # padding
+# End of traceback table
+ .long 0x00000000 # "\0\0\0\0"
+
+# .data section
+
+ .toc # 0x00000038
+T.18.longjmp:
+ .tc H.18.longjmp{TC},longjmp{DS}
+
+ .csect longjmp{DS}
+ .long .longjmp # "\0\0\0\0"
+ .long TOC{TC0} # "\0\0\0008"
+ .long 0x00000000 # "\0\0\0\0"
+# End csect longjmp{DS}
+
+# .bss section
diff --git a/nsprpub/pr/src/md/unix/os_BSD_386_2.s b/nsprpub/pr/src/md/unix/os_BSD_386_2.s
new file mode 100644
index 0000000000..fa36599e73
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_BSD_386_2.s
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * os_BSD_386_2.s
+ * We need to define our own setjmp/longjmp on BSDI 2.x because libc's
+ * implementation does some sanity checking that defeats user level threads.
+ * This should no longer be necessary in BSDI 3.0.
+ */
+
+.globl _setjmp
+.align 2
+_setjmp:
+ movl 4(%esp),%eax
+ movl 0(%esp),%edx
+ movl %edx, 0(%eax) /* rta */
+ movl %ebx, 4(%eax)
+ movl %esp, 8(%eax)
+ movl %ebp,12(%eax)
+ movl %esi,16(%eax)
+ movl %edi,20(%eax)
+ movl $0,%eax
+ ret
+
+.globl _longjmp
+.align 2
+_longjmp:
+ movl 4(%esp),%edx
+ movl 8(%esp),%eax
+ movl 0(%edx),%ecx
+ movl 4(%edx),%ebx
+ movl 8(%edx),%esp
+ movl 12(%edx),%ebp
+ movl 16(%edx),%esi
+ movl 20(%edx),%edi
+ cmpl $0,%eax
+ jne 1f
+ movl $1,%eax
+1: movl %ecx,0(%esp)
+ ret
diff --git a/nsprpub/pr/src/md/unix/os_Darwin.s b/nsprpub/pr/src/md/unix/os_Darwin.s
new file mode 100644
index 0000000000..26a0b9226e
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_Darwin.s
@@ -0,0 +1,13 @@
+# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifdef __i386__
+#include "os_Darwin_x86.s"
+#elif defined(__x86_64__)
+#include "os_Darwin_x86_64.s"
+#elif defined(__ppc__)
+#include "os_Darwin_ppc.s"
+#endif
diff --git a/nsprpub/pr/src/md/unix/os_Darwin_ppc.s b/nsprpub/pr/src/md/unix/os_Darwin_ppc.s
new file mode 100644
index 0000000000..8479dec01f
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_Darwin_ppc.s
@@ -0,0 +1,68 @@
+# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#
+# Based on the programming examples in The PowerPC Architecture:
+# A Specification for A New Family of RISC Processors, 2nd Ed.,
+# Book I, Section E.1, "Synchronization," pp. 249-256, May 1994.
+#
+
+.text
+
+#
+# PRInt32 __PR_DarwinPPC_AtomicIncrement(PRInt32 *val);
+#
+ .align 2
+ .globl __PR_DarwinPPC_AtomicIncrement
+ .private_extern __PR_DarwinPPC_AtomicIncrement
+__PR_DarwinPPC_AtomicIncrement:
+ lwarx r4,0,r3
+ addi r0,r4,1
+ stwcx. r0,0,r3
+ bne- __PR_DarwinPPC_AtomicIncrement
+ mr r3,r0
+ blr
+
+#
+# PRInt32 __PR_DarwinPPC_AtomicDecrement(PRInt32 *val);
+#
+ .align 2
+ .globl __PR_DarwinPPC_AtomicDecrement
+ .private_extern __PR_DarwinPPC_AtomicDecrement
+__PR_DarwinPPC_AtomicDecrement:
+ lwarx r4,0,r3
+ addi r0,r4,-1
+ stwcx. r0,0,r3
+ bne- __PR_DarwinPPC_AtomicDecrement
+ mr r3,r0
+ blr
+
+#
+# PRInt32 __PR_DarwinPPC_AtomicSet(PRInt32 *val, PRInt32 newval);
+#
+ .align 2
+ .globl __PR_DarwinPPC_AtomicSet
+ .private_extern __PR_DarwinPPC_AtomicSet
+__PR_DarwinPPC_AtomicSet:
+ lwarx r5,0,r3
+ stwcx. r4,0,r3
+ bne- __PR_DarwinPPC_AtomicSet
+ mr r3,r5
+ blr
+
+#
+# PRInt32 __PR_DarwinPPC_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+#
+ .align 2
+ .globl __PR_DarwinPPC_AtomicAdd
+ .private_extern __PR_DarwinPPC_AtomicAdd
+__PR_DarwinPPC_AtomicAdd:
+ lwarx r5,0,r3
+ add r0,r4,r5
+ stwcx. r0,0,r3
+ bne- __PR_DarwinPPC_AtomicAdd
+ mr r3,r0
+ blr
diff --git a/nsprpub/pr/src/md/unix/os_Darwin_x86.s b/nsprpub/pr/src/md/unix/os_Darwin_x86.s
new file mode 100644
index 0000000000..fc6379f952
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_Darwin_x86.s
@@ -0,0 +1,80 @@
+# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#
+# Based on os_Linux_x86.s
+#
+
+#
+# PRInt32 __PR_Darwin_x86_AtomicIncrement(PRInt32 *val);
+#
+# Atomically increment the integer pointed to by 'val' and return
+# the result of the increment.
+#
+ .text
+ .globl __PR_Darwin_x86_AtomicIncrement
+ .private_extern __PR_Darwin_x86_AtomicIncrement
+ .align 4
+__PR_Darwin_x86_AtomicIncrement:
+ movl 4(%esp), %ecx
+ movl $1, %eax
+ lock
+ xaddl %eax, (%ecx)
+ incl %eax
+ ret
+
+#
+# PRInt32 __PR_Darwin_x86_AtomicDecrement(PRInt32 *val);
+#
+# Atomically decrement the integer pointed to by 'val' and return
+# the result of the decrement.
+#
+ .text
+ .globl __PR_Darwin_x86_AtomicDecrement
+ .private_extern __PR_Darwin_x86_AtomicDecrement
+ .align 4
+__PR_Darwin_x86_AtomicDecrement:
+ movl 4(%esp), %ecx
+ movl $-1, %eax
+ lock
+ xaddl %eax, (%ecx)
+ decl %eax
+ ret
+
+#
+# PRInt32 __PR_Darwin_x86_AtomicSet(PRInt32 *val, PRInt32 newval);
+#
+# Atomically set the integer pointed to by 'val' to the new
+# value 'newval' and return the old value.
+#
+ .text
+ .globl __PR_Darwin_x86_AtomicSet
+ .private_extern __PR_Darwin_x86_AtomicSet
+ .align 4
+__PR_Darwin_x86_AtomicSet:
+ movl 4(%esp), %ecx
+ movl 8(%esp), %eax
+ xchgl %eax, (%ecx)
+ ret
+
+#
+# PRInt32 __PR_Darwin_x86_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+#
+# Atomically add 'val' to the integer pointed to by 'ptr'
+# and return the result of the addition.
+#
+ .text
+ .globl __PR_Darwin_x86_AtomicAdd
+ .private_extern __PR_Darwin_x86_AtomicAdd
+ .align 4
+__PR_Darwin_x86_AtomicAdd:
+ movl 4(%esp), %ecx
+ movl 8(%esp), %eax
+ movl %eax, %edx
+ lock
+ xaddl %eax, (%ecx)
+ addl %edx, %eax
+ ret
diff --git a/nsprpub/pr/src/md/unix/os_Darwin_x86_64.s b/nsprpub/pr/src/md/unix/os_Darwin_x86_64.s
new file mode 100644
index 0000000000..449aaa5db6
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_Darwin_x86_64.s
@@ -0,0 +1,67 @@
+# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# PRInt32 __PR_Darwin_x86_64_AtomicIncrement(PRInt32 *val)
+#
+# Atomically increment the integer pointed to by 'val' and return
+# the result of the increment.
+#
+ .text
+ .globl __PR_Darwin_x86_64_AtomicIncrement
+ .private_extern __PR_Darwin_x86_64_AtomicIncrement
+ .align 4
+__PR_Darwin_x86_64_AtomicIncrement:
+ movl $1, %eax
+ lock
+ xaddl %eax, (%rdi)
+ incl %eax
+ ret
+
+# PRInt32 __PR_Darwin_x86_64_AtomicDecrement(PRInt32 *val)
+#
+# Atomically decrement the integer pointed to by 'val' and return
+# the result of the decrement.
+#
+ .text
+ .globl __PR_Darwin_x86_64_AtomicDecrement
+ .private_extern __PR_Darwin_x86_64_AtomicDecrement
+ .align 4
+__PR_Darwin_x86_64_AtomicDecrement:
+ movl $-1, %eax
+ lock
+ xaddl %eax, (%rdi)
+ decl %eax
+ ret
+
+# PRInt32 __PR_Darwin_x86_64_AtomicSet(PRInt32 *val, PRInt32 newval)
+#
+# Atomically set the integer pointed to by 'val' to the new
+# value 'newval' and return the old value.
+#
+ .text
+ .globl __PR_Darwin_x86_64_AtomicSet
+ .private_extern __PR_Darwin_x86_64_AtomicSet
+ .align 4
+__PR_Darwin_x86_64_AtomicSet:
+ movl %esi, %eax
+ xchgl %eax, (%rdi)
+ ret
+
+# PRInt32 __PR_Darwin_x86_64_AtomicAdd(PRInt32 *ptr, PRInt32 val)
+#
+# Atomically add 'val' to the integer pointed to by 'ptr'
+# and return the result of the addition.
+#
+ .text
+ .globl __PR_Darwin_x86_64_AtomicAdd
+ .private_extern __PR_Darwin_x86_64_AtomicAdd
+ .align 4
+__PR_Darwin_x86_64_AtomicAdd:
+ movl %esi, %eax
+ lock
+ xaddl %eax, (%rdi)
+ addl %esi, %eax
+ ret
diff --git a/nsprpub/pr/src/md/unix/os_HPUX.s b/nsprpub/pr/src/md/unix/os_HPUX.s
new file mode 100644
index 0000000000..f8ecabf9b0
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_HPUX.s
@@ -0,0 +1,25 @@
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifdef __LP64__
+ .LEVEL 2.0W
+#else
+ .LEVEL 1.1
+#endif
+
+ .CODE ; equivalent to the following two lines
+; .SPACE $TEXT$,SORT=8
+; .SUBSPA $CODE$,QUAD=0,ALIGN=4,ACCESS=0x2c,CODE_ONLY,SORT=24
+
+ret_cr16
+ .PROC
+ .CALLINFO FRAME=0, NO_CALLS
+ .EXPORT ret_cr16,ENTRY
+ .ENTRY
+ BV %r0(%rp)
+ .EXIT
+ MFCTL %cr16,%ret0
+ .PROCEND
+ .END
diff --git a/nsprpub/pr/src/md/unix/os_HPUX_ia64.s b/nsprpub/pr/src/md/unix/os_HPUX_ia64.s
new file mode 100644
index 0000000000..302ae7687d
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_HPUX_ia64.s
@@ -0,0 +1,80 @@
+// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+.text
+
+// PRInt32 _PR_ia64_AtomicIncrement(PRInt32 *val)
+//
+// Atomically increment the integer pointed to by 'val' and return
+// the result of the increment.
+//
+ .align 16
+ .global _PR_ia64_AtomicIncrement#
+ .proc _PR_ia64_AtomicIncrement#
+_PR_ia64_AtomicIncrement:
+#ifndef _LP64
+ addp4 r32 = 0, r32 ;;
+#endif
+ fetchadd4.acq r8 = [r32], 1 ;;
+ adds r8 = 1, r8
+ br.ret.sptk.many b0
+ .endp _PR_ia64_AtomicIncrement#
+
+// PRInt32 _PR_ia64_AtomicDecrement(PRInt32 *val)
+//
+// Atomically decrement the integer pointed to by 'val' and return
+// the result of the decrement.
+//
+ .align 16
+ .global _PR_ia64_AtomicDecrement#
+ .proc _PR_ia64_AtomicDecrement#
+_PR_ia64_AtomicDecrement:
+#ifndef _LP64
+ addp4 r32 = 0, r32 ;;
+#endif
+ fetchadd4.rel r8 = [r32], -1 ;;
+ adds r8 = -1, r8
+ br.ret.sptk.many b0
+ .endp _PR_ia64_AtomicDecrement#
+
+// PRInt32 _PR_ia64_AtomicAdd(PRInt32 *ptr, PRInt32 val)
+//
+// Atomically add 'val' to the integer pointed to by 'ptr'
+// and return the result of the addition.
+//
+ .align 16
+ .global _PR_ia64_AtomicAdd#
+ .proc _PR_ia64_AtomicAdd#
+_PR_ia64_AtomicAdd:
+#ifndef _LP64
+ addp4 r32 = 0, r32 ;;
+#endif
+ ld4 r15 = [r32] ;;
+.L3:
+ mov r14 = r15
+ mov ar.ccv = r15
+ add r8 = r15, r33 ;;
+ cmpxchg4.acq r15 = [r32], r8, ar.ccv ;;
+ cmp4.ne p6, p7 = r15, r14
+ (p6) br.cond.dptk .L3
+ br.ret.sptk.many b0
+ .endp _PR_ia64_AtomicAdd#
+
+// PRInt32 _PR_ia64_AtomicSet(PRInt32 *val, PRInt32 newval)
+//
+// Atomically set the integer pointed to by 'val' to the new
+// value 'newval' and return the old value.
+//
+ .align 16
+ .global _PR_ia64_AtomicSet#
+ .proc _PR_ia64_AtomicSet#
+_PR_ia64_AtomicSet:
+#ifndef _LP64
+ addp4 r32 = 0, r32 ;;
+#endif
+ xchg4 r8 = [r32], r33
+ br.ret.sptk.many b0
+ .endp _PR_ia64_AtomicSet#
diff --git a/nsprpub/pr/src/md/unix/os_Linux_ia64.s b/nsprpub/pr/src/md/unix/os_Linux_ia64.s
new file mode 100644
index 0000000000..39b724ae47
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_Linux_ia64.s
@@ -0,0 +1,71 @@
+// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+.text
+
+// PRInt32 _PR_ia64_AtomicIncrement(PRInt32 *val)
+//
+// Atomically increment the integer pointed to by 'val' and return
+// the result of the increment.
+//
+ .align 16
+ .global _PR_ia64_AtomicIncrement#
+ .proc _PR_ia64_AtomicIncrement#
+_PR_ia64_AtomicIncrement:
+ fetchadd4.acq r8 = [r32], 1 ;;
+ adds r8 = 1, r8
+ br.ret.sptk.many b0
+ .endp _PR_ia64_AtomicIncrement#
+
+// PRInt32 _PR_ia64_AtomicDecrement(PRInt32 *val)
+//
+// Atomically decrement the integer pointed to by 'val' and return
+// the result of the decrement.
+//
+ .align 16
+ .global _PR_ia64_AtomicDecrement#
+ .proc _PR_ia64_AtomicDecrement#
+_PR_ia64_AtomicDecrement:
+ fetchadd4.rel r8 = [r32], -1 ;;
+ adds r8 = -1, r8
+ br.ret.sptk.many b0
+ .endp _PR_ia64_AtomicDecrement#
+
+// PRInt32 _PR_ia64_AtomicAdd(PRInt32 *ptr, PRInt32 val)
+//
+// Atomically add 'val' to the integer pointed to by 'ptr'
+// and return the result of the addition.
+//
+ .align 16
+ .global _PR_ia64_AtomicAdd#
+ .proc _PR_ia64_AtomicAdd#
+_PR_ia64_AtomicAdd:
+ ld4 r15 = [r32] ;;
+.L3:
+ mov r14 = r15
+ mov ar.ccv = r15
+ add r8 = r15, r33 ;;
+ cmpxchg4.acq r15 = [r32], r8, ar.ccv ;;
+ cmp4.ne p6, p7 = r15, r14
+ (p6) br.cond.dptk .L3
+ br.ret.sptk.many b0
+ .endp _PR_ia64_AtomicAdd#
+
+// PRInt32 _PR_ia64_AtomicSet(PRInt32 *val, PRInt32 newval)
+//
+// Atomically set the integer pointed to by 'val' to the new
+// value 'newval' and return the old value.
+//
+ .align 16
+ .global _PR_ia64_AtomicSet#
+ .proc _PR_ia64_AtomicSet#
+_PR_ia64_AtomicSet:
+ xchg4 r8 = [r32], r33
+ br.ret.sptk.many b0
+ .endp _PR_ia64_AtomicSet#
+
+// Magic indicating no need for an executable stack
+.section .note.GNU-stack, "", @progbits
diff --git a/nsprpub/pr/src/md/unix/os_Linux_ppc.s b/nsprpub/pr/src/md/unix/os_Linux_ppc.s
new file mode 100644
index 0000000000..76da33bffc
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_Linux_ppc.s
@@ -0,0 +1,75 @@
+# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#
+# Based on the programming examples in The PowerPC Architecture:
+# A Specification for A New Family of RISC Processors, 2nd Ed.,
+# Book I, Section E.1, "Synchronization," pp. 249-256, May 1994.
+#
+
+ .section ".text"
+
+#
+# PRInt32 _PR_ppc_AtomicIncrement(PRInt32 *val);
+#
+ .align 2
+ .globl _PR_ppc_AtomicIncrement
+ .type _PR_ppc_AtomicIncrement,@function
+_PR_ppc_AtomicIncrement:
+.Lfd1: lwarx 4,0,3
+ addi 0,4,1
+ stwcx. 0,0,3
+ bne- .Lfd1
+ mr 3,0
+ blr
+.Lfe1: .size _PR_ppc_AtomicIncrement,.Lfe1-_PR_ppc_AtomicIncrement
+
+#
+# PRInt32 _PR_ppc_AtomicDecrement(PRInt32 *val);
+#
+ .align 2
+ .globl _PR_ppc_AtomicDecrement
+ .type _PR_ppc_AtomicDecrement,@function
+_PR_ppc_AtomicDecrement:
+.Lfd2: lwarx 4,0,3
+ addi 0,4,-1
+ stwcx. 0,0,3
+ bne- .Lfd2
+ mr 3,0
+ blr
+.Lfe2: .size _PR_ppc_AtomicDecrement,.Lfe2-_PR_ppc_AtomicDecrement
+
+#
+# PRInt32 _PR_ppc_AtomicSet(PRInt32 *val, PRInt32 newval);
+#
+ .align 2
+ .globl _PR_ppc_AtomicSet
+ .type _PR_ppc_AtomicSet,@function
+_PR_ppc_AtomicSet:
+.Lfd3: lwarx 5,0,3
+ stwcx. 4,0,3
+ bne- .Lfd3
+ mr 3,5
+ blr
+.Lfe3: .size _PR_ppc_AtomicSet,.Lfe3-_PR_ppc_AtomicSet
+
+#
+# PRInt32 _PR_ppc_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+#
+ .align 2
+ .globl _PR_ppc_AtomicAdd
+ .type _PR_ppc_AtomicAdd,@function
+_PR_ppc_AtomicAdd:
+.Lfd4: lwarx 5,0,3
+ add 0,4,5
+ stwcx. 0,0,3
+ bne- .Lfd4
+ mr 3,0
+ blr
+.Lfe4: .size _PR_ppc_AtomicAdd,.Lfe4-_PR_ppc_AtomicAdd
+
+# Magic indicating no need for an executable stack
+.section .note.GNU-stack, "", @progbits
diff --git a/nsprpub/pr/src/md/unix/os_Linux_x86.s b/nsprpub/pr/src/md/unix/os_Linux_x86.s
new file mode 100644
index 0000000000..83e10b4552
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_Linux_x86.s
@@ -0,0 +1,85 @@
+// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// PRInt32 _PR_x86_AtomicIncrement(PRInt32 *val)
+//
+// Atomically increment the integer pointed to by 'val' and return
+// the result of the increment.
+//
+ .text
+ .globl _PR_x86_AtomicIncrement
+ .align 4
+_PR_x86_AtomicIncrement:
+ movl 4(%esp), %ecx
+ movl $1, %eax
+ lock
+ xaddl %eax, (%ecx)
+ incl %eax
+ ret
+
+// PRInt32 _PR_x86_AtomicDecrement(PRInt32 *val)
+//
+// Atomically decrement the integer pointed to by 'val' and return
+// the result of the decrement.
+//
+ .text
+ .globl _PR_x86_AtomicDecrement
+ .align 4
+_PR_x86_AtomicDecrement:
+ movl 4(%esp), %ecx
+ movl $-1, %eax
+ lock
+ xaddl %eax, (%ecx)
+ decl %eax
+ ret
+
+// PRInt32 _PR_x86_AtomicSet(PRInt32 *val, PRInt32 newval)
+//
+// Atomically set the integer pointed to by 'val' to the new
+// value 'newval' and return the old value.
+//
+// An alternative implementation:
+// .text
+// .globl _PR_x86_AtomicSet
+// .align 4
+//_PR_x86_AtomicSet:
+// movl 4(%esp), %ecx
+// movl 8(%esp), %edx
+// movl (%ecx), %eax
+//retry:
+// lock
+// cmpxchgl %edx, (%ecx)
+// jne retry
+// ret
+//
+ .text
+ .globl _PR_x86_AtomicSet
+ .align 4
+_PR_x86_AtomicSet:
+ movl 4(%esp), %ecx
+ movl 8(%esp), %eax
+ xchgl %eax, (%ecx)
+ ret
+
+// PRInt32 _PR_x86_AtomicAdd(PRInt32 *ptr, PRInt32 val)
+//
+// Atomically add 'val' to the integer pointed to by 'ptr'
+// and return the result of the addition.
+//
+ .text
+ .globl _PR_x86_AtomicAdd
+ .align 4
+_PR_x86_AtomicAdd:
+ movl 4(%esp), %ecx
+ movl 8(%esp), %eax
+ movl %eax, %edx
+ lock
+ xaddl %eax, (%ecx)
+ addl %edx, %eax
+ ret
+
+// Magic indicating no need for an executable stack
+.section .note.GNU-stack, "", @progbits
diff --git a/nsprpub/pr/src/md/unix/os_Linux_x86_64.s b/nsprpub/pr/src/md/unix/os_Linux_x86_64.s
new file mode 100644
index 0000000000..f30e75d538
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_Linux_x86_64.s
@@ -0,0 +1,74 @@
+// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// PRInt32 _PR_x86_64_AtomicIncrement(PRInt32 *val)
+//
+// Atomically increment the integer pointed to by 'val' and return
+// the result of the increment.
+//
+ .text
+ .globl _PR_x86_64_AtomicIncrement
+ .type _PR_x86_64_AtomicIncrement, @function
+ .align 4
+_PR_x86_64_AtomicIncrement:
+ movl $1, %eax
+ lock
+ xaddl %eax, (%rdi)
+ incl %eax
+ ret
+ .size _PR_x86_64_AtomicIncrement, .-_PR_x86_64_AtomicIncrement
+
+// PRInt32 _PR_x86_64_AtomicDecrement(PRInt32 *val)
+//
+// Atomically decrement the integer pointed to by 'val' and return
+// the result of the decrement.
+//
+ .text
+ .globl _PR_x86_64_AtomicDecrement
+ .type _PR_x86_64_AtomicDecrement, @function
+ .align 4
+_PR_x86_64_AtomicDecrement:
+ movl $-1, %eax
+ lock
+ xaddl %eax, (%rdi)
+ decl %eax
+ ret
+ .size _PR_x86_64_AtomicDecrement, .-_PR_x86_64_AtomicDecrement
+
+// PRInt32 _PR_x86_64_AtomicSet(PRInt32 *val, PRInt32 newval)
+//
+// Atomically set the integer pointed to by 'val' to the new
+// value 'newval' and return the old value.
+//
+ .text
+ .globl _PR_x86_64_AtomicSet
+ .type _PR_x86_64_AtomicSet, @function
+ .align 4
+_PR_x86_64_AtomicSet:
+ movl %esi, %eax
+ xchgl %eax, (%rdi)
+ ret
+ .size _PR_x86_64_AtomicSet, .-_PR_x86_64_AtomicSet
+
+// PRInt32 _PR_x86_64_AtomicAdd(PRInt32 *ptr, PRInt32 val)
+//
+// Atomically add 'val' to the integer pointed to by 'ptr'
+// and return the result of the addition.
+//
+ .text
+ .globl _PR_x86_64_AtomicAdd
+ .type _PR_x86_64_AtomicAdd, @function
+ .align 4
+_PR_x86_64_AtomicAdd:
+ movl %esi, %eax
+ lock
+ xaddl %eax, (%rdi)
+ addl %esi, %eax
+ ret
+ .size _PR_x86_64_AtomicAdd, .-_PR_x86_64_AtomicAdd
+
+// Magic indicating no need for an executable stack
+.section .note.GNU-stack, "", @progbits
diff --git a/nsprpub/pr/src/md/unix/os_SunOS_sparcv9.s b/nsprpub/pr/src/md/unix/os_SunOS_sparcv9.s
new file mode 100644
index 0000000000..8bc75af88d
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_SunOS_sparcv9.s
@@ -0,0 +1,173 @@
+! -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+!
+! This Source Code Form is subject to the terms of the Mozilla Public
+! License, v. 2.0. If a copy of the MPL was not distributed with this
+! file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+!
+! atomic increment, decrement and swap routines for V8+ sparc (ultrasparc)
+! using CAS (compare-and-swap) atomic instructions
+!
+! this MUST be compiled with an ultrasparc-aware assembler
+!
+! standard asm linkage macros; this module must be compiled
+! with the -P option (use C preprocessor)
+
+#include <sys/asm_linkage.h>
+
+! ======================================================================
+!
+! Perform the sequence a = a + 1 atomically with respect to other
+! fetch-and-adds to location a in a wait-free fashion.
+!
+! usage : val = PR_AtomicIncrement(address)
+! return: current value (you'd think this would be old val)
+!
+! -----------------------
+! Note on REGISTER USAGE:
+! as this is a LEAF procedure, a new stack frame is not created;
+! we use the caller's stack frame so what would normally be %i (input)
+! registers are actually %o (output registers). Also, we must not
+! overwrite the contents of %l (local) registers as they are not
+! assumed to be volatile during calls.
+!
+! So, the registers used are:
+! %o0 [input] - the address of the value to increment
+! %o1 [local] - work register
+! %o2 [local] - work register
+! %o3 [local] - work register
+! -----------------------
+
+ ENTRY(_MD_AtomicIncrement) ! standard assembler/ELF prologue
+
+retryAI:
+ ld [%o0], %o2 ! set o2 to the current value
+ add %o2, 0x1, %o3 ! calc the new value
+ mov %o3, %o1 ! save the return value
+ cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAI ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o1, %o0 ! set the return code to the new value
+
+ SET_SIZE(_MD_AtomicIncrement) ! standard assembler/ELF epilogue
+
+!
+! end
+!
+! ======================================================================
+!
+
+! ======================================================================
+!
+! Perform the sequence a = a - 1 atomically with respect to other
+! fetch-and-decs to location a in a wait-free fashion.
+!
+! usage : val = PR_AtomicDecrement(address)
+! return: current value (you'd think this would be old val)
+!
+! -----------------------
+! Note on REGISTER USAGE:
+! as this is a LEAF procedure, a new stack frame is not created;
+! we use the caller's stack frame so what would normally be %i (input)
+! registers are actually %o (output registers). Also, we must not
+! overwrite the contents of %l (local) registers as they are not
+! assumed to be volatile during calls.
+!
+! So, the registers used are:
+! %o0 [input] - the address of the value to increment
+! %o1 [local] - work register
+! %o2 [local] - work register
+! %o3 [local] - work register
+! -----------------------
+
+ ENTRY(_MD_AtomicDecrement) ! standard assembler/ELF prologue
+
+retryAD:
+ ld [%o0], %o2 ! set o2 to the current value
+ sub %o2, 0x1, %o3 ! calc the new value
+ mov %o3, %o1 ! save the return value
+ cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAD ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o1, %o0 ! set the return code to the new value
+
+ SET_SIZE(_MD_AtomicDecrement) ! standard assembler/ELF epilogue
+
+!
+! end
+!
+! ======================================================================
+!
+
+! ======================================================================
+!
+! Perform the sequence a = b atomically with respect to other
+! fetch-and-stores to location a in a wait-free fashion.
+!
+! usage : old_val = PR_AtomicSet(address, newval)
+!
+! -----------------------
+! Note on REGISTER USAGE:
+! as this is a LEAF procedure, a new stack frame is not created;
+! we use the caller's stack frame so what would normally be %i (input)
+! registers are actually %o (output registers). Also, we must not
+! overwrite the contents of %l (local) registers as they are not
+! assumed to be volatile during calls.
+!
+! So, the registers used are:
+! %o0 [input] - the address of the value to increment
+! %o1 [input] - the new value to set for [%o0]
+! %o2 [local] - work register
+! %o3 [local] - work register
+! -----------------------
+
+ ENTRY(_MD_AtomicSet) ! standard assembler/ELF prologue
+
+retryAS:
+ ld [%o0], %o2 ! set o2 to the current value
+ mov %o1, %o3 ! set up the new value
+ cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAS ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o3, %o0 ! set the return code to the prev value
+
+ SET_SIZE(_MD_AtomicSet) ! standard assembler/ELF epilogue
+
+!
+! end
+!
+! ======================================================================
+!
+
+! ======================================================================
+!
+! Perform the sequence a = a + b atomically with respect to other
+! fetch-and-adds to location a in a wait-free fashion.
+!
+! usage : newval = PR_AtomicAdd(address, val)
+! return: the value after addition
+!
+ ENTRY(_MD_AtomicAdd) ! standard assembler/ELF prologue
+
+retryAA:
+ ld [%o0], %o2 ! set o2 to the current value
+ add %o2, %o1, %o3 ! calc the new value
+ mov %o3, %o4 ! save the return value
+ cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAA ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o4, %o0 ! set the return code to the new value
+
+ SET_SIZE(_MD_AtomicAdd) ! standard assembler/ELF epilogue
+
+!
+! end
+!
diff --git a/nsprpub/pr/src/md/unix/os_SunOS_ultrasparc.s b/nsprpub/pr/src/md/unix/os_SunOS_ultrasparc.s
new file mode 100644
index 0000000000..32bf7884ac
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_SunOS_ultrasparc.s
@@ -0,0 +1,173 @@
+! -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+!
+! This Source Code Form is subject to the terms of the Mozilla Public
+! License, v. 2.0. If a copy of the MPL was not distributed with this
+! file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+!
+! atomic increment, decrement and swap routines for V8+ sparc (ultrasparc)
+! using CAS (compare-and-swap) atomic instructions
+!
+! this MUST be compiled with an ultrasparc-aware assembler
+!
+! standard asm linkage macros; this module must be compiled
+! with the -P option (use C preprocessor)
+
+#include <sys/asm_linkage.h>
+
+! ======================================================================
+!
+! Perform the sequence a = a + 1 atomically with respect to other
+! fetch-and-adds to location a in a wait-free fashion.
+!
+! usage : val = PR_AtomicIncrement(address)
+! return: current value (you'd think this would be old val)
+!
+! -----------------------
+! Note on REGISTER USAGE:
+! as this is a LEAF procedure, a new stack frame is not created;
+! we use the caller's stack frame so what would normally be %i (input)
+! registers are actually %o (output registers). Also, we must not
+! overwrite the contents of %l (local) registers as they are not
+! assumed to be volatile during calls.
+!
+! So, the registers used are:
+! %o0 [input] - the address of the value to increment
+! %o1 [local] - work register
+! %o2 [local] - work register
+! %o3 [local] - work register
+! -----------------------
+
+ ENTRY(PR_AtomicIncrement) ! standard assembler/ELF prologue
+
+retryAI:
+ ld [%o0], %o2 ! set o2 to the current value
+ add %o2, 0x1, %o3 ! calc the new value
+ mov %o3, %o1 ! save the return value
+ cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAI ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o1, %o0 ! set the return code to the new value
+
+ SET_SIZE(PR_AtomicIncrement) ! standard assembler/ELF epilogue
+
+!
+! end
+!
+! ======================================================================
+!
+
+! ======================================================================
+!
+! Perform the sequence a = a - 1 atomically with respect to other
+! fetch-and-decs to location a in a wait-free fashion.
+!
+! usage : val = PR_AtomicDecrement(address)
+! return: current value (you'd think this would be old val)
+!
+! -----------------------
+! Note on REGISTER USAGE:
+! as this is a LEAF procedure, a new stack frame is not created;
+! we use the caller's stack frame so what would normally be %i (input)
+! registers are actually %o (output registers). Also, we must not
+! overwrite the contents of %l (local) registers as they are not
+! assumed to be volatile during calls.
+!
+! So, the registers used are:
+! %o0 [input] - the address of the value to increment
+! %o1 [local] - work register
+! %o2 [local] - work register
+! %o3 [local] - work register
+! -----------------------
+
+ ENTRY(PR_AtomicDecrement) ! standard assembler/ELF prologue
+
+retryAD:
+ ld [%o0], %o2 ! set o2 to the current value
+ sub %o2, 0x1, %o3 ! calc the new value
+ mov %o3, %o1 ! save the return value
+ cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAD ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o1, %o0 ! set the return code to the new value
+
+ SET_SIZE(PR_AtomicDecrement) ! standard assembler/ELF epilogue
+
+!
+! end
+!
+! ======================================================================
+!
+
+! ======================================================================
+!
+! Perform the sequence a = b atomically with respect to other
+! fetch-and-stores to location a in a wait-free fashion.
+!
+! usage : old_val = PR_AtomicSet(address, newval)
+!
+! -----------------------
+! Note on REGISTER USAGE:
+! as this is a LEAF procedure, a new stack frame is not created;
+! we use the caller's stack frame so what would normally be %i (input)
+! registers are actually %o (output registers). Also, we must not
+! overwrite the contents of %l (local) registers as they are not
+! assumed to be volatile during calls.
+!
+! So, the registers used are:
+! %o0 [input] - the address of the value to increment
+! %o1 [input] - the new value to set for [%o0]
+! %o2 [local] - work register
+! %o3 [local] - work register
+! -----------------------
+
+ ENTRY(PR_AtomicSet) ! standard assembler/ELF prologue
+
+retryAS:
+ ld [%o0], %o2 ! set o2 to the current value
+ mov %o1, %o3 ! set up the new value
+ cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAS ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o3, %o0 ! set the return code to the prev value
+
+ SET_SIZE(PR_AtomicSet) ! standard assembler/ELF epilogue
+
+!
+! end
+!
+! ======================================================================
+!
+
+! ======================================================================
+!
+! Perform the sequence a = a + b atomically with respect to other
+! fetch-and-adds to location a in a wait-free fashion.
+!
+! usage : newval = PR_AtomicAdd(address, val)
+! return: the value after addition
+!
+ ENTRY(PR_AtomicAdd) ! standard assembler/ELF prologue
+
+retryAA:
+ ld [%o0], %o2 ! set o2 to the current value
+ add %o2, %o1, %o3 ! calc the new value
+ mov %o3, %o4 ! save the return value
+ cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAA ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o4, %o0 ! set the return code to the new value
+
+ SET_SIZE(PR_AtomicAdd) ! standard assembler/ELF epilogue
+
+!
+! end
+!
diff --git a/nsprpub/pr/src/md/unix/os_SunOS_x86.s b/nsprpub/pr/src/md/unix/os_SunOS_x86.s
new file mode 100644
index 0000000000..dcd7535bf6
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_SunOS_x86.s
@@ -0,0 +1,126 @@
+// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ .text
+
+ .globl getedi
+getedi:
+ movl %edi,%eax
+ ret
+ .type getedi,@function
+ .size getedi,.-getedi
+
+ .globl setedi
+setedi:
+ movl 4(%esp),%edi
+ ret
+ .type setedi,@function
+ .size setedi,.-setedi
+
+ .globl __MD_FlushRegisterWindows
+ .globl _MD_FlushRegisterWindows
+
+__MD_FlushRegisterWindows:
+_MD_FlushRegisterWindows:
+
+ ret
+
+//
+// sol_getsp()
+//
+// Return the current sp (for debugging)
+//
+ .globl sol_getsp
+sol_getsp:
+ movl %esp, %eax
+ ret
+
+//
+// sol_curthread()
+//
+// Return a unique identifier for the currently active thread.
+//
+ .globl sol_curthread
+sol_curthread:
+ movl %ecx, %eax
+ ret
+
+// PRInt32 _MD_AtomicIncrement(PRInt32 *val)
+//
+// Atomically increment the integer pointed to by 'val' and return
+// the result of the increment.
+//
+ .text
+ .globl _MD_AtomicIncrement
+ .align 4
+_MD_AtomicIncrement:
+ movl 4(%esp), %ecx
+ movl $1, %eax
+ lock
+ xaddl %eax, (%ecx)
+ incl %eax
+ ret
+
+// PRInt32 _MD_AtomicDecrement(PRInt32 *val)
+//
+// Atomically decrement the integer pointed to by 'val' and return
+// the result of the decrement.
+//
+ .text
+ .globl _MD_AtomicDecrement
+ .align 4
+_MD_AtomicDecrement:
+ movl 4(%esp), %ecx
+ movl $-1, %eax
+ lock
+ xaddl %eax, (%ecx)
+ decl %eax
+ ret
+
+// PRInt32 _MD_AtomicSet(PRInt32 *val, PRInt32 newval)
+//
+// Atomically set the integer pointed to by 'val' to the new
+// value 'newval' and return the old value.
+//
+// An alternative implementation:
+// .text
+// .globl _MD_AtomicSet
+// .align 4
+//_MD_AtomicSet:
+// movl 4(%esp), %ecx
+// movl 8(%esp), %edx
+// movl (%ecx), %eax
+//retry:
+// lock
+// cmpxchgl %edx, (%ecx)
+// jne retry
+// ret
+//
+ .text
+ .globl _MD_AtomicSet
+ .align 4
+_MD_AtomicSet:
+ movl 4(%esp), %ecx
+ movl 8(%esp), %eax
+ xchgl %eax, (%ecx)
+ ret
+
+// PRInt32 _MD_AtomicAdd(PRInt32 *ptr, PRInt32 val)
+//
+// Atomically add 'val' to the integer pointed to by 'ptr'
+// and return the result of the addition.
+//
+ .text
+ .globl _MD_AtomicAdd
+ .align 4
+_MD_AtomicAdd:
+ movl 4(%esp), %ecx
+ movl 8(%esp), %eax
+ movl %eax, %edx
+ lock
+ xaddl %eax, (%ecx)
+ addl %edx, %eax
+ ret
diff --git a/nsprpub/pr/src/md/unix/os_SunOS_x86_64.s b/nsprpub/pr/src/md/unix/os_SunOS_x86_64.s
new file mode 100644
index 0000000000..5e3df049fe
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/os_SunOS_x86_64.s
@@ -0,0 +1,63 @@
+// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// PRInt32 _MD_AtomicIncrement(PRInt32 *val)
+//
+// Atomically increment the integer pointed to by 'val' and return
+// the result of the increment.
+//
+ .text
+ .globl _MD_AtomicIncrement
+ .align 4
+_MD_AtomicIncrement:
+ movl $1, %eax
+ lock
+ xaddl %eax, (%rdi)
+ incl %eax
+ ret
+
+// PRInt32 _MD_AtomicDecrement(PRInt32 *val)
+//
+// Atomically decrement the integer pointed to by 'val' and return
+// the result of the decrement.
+//
+ .text
+ .globl _MD_AtomicDecrement
+ .align 4
+_MD_AtomicDecrement:
+ movl $-1, %eax
+ lock
+ xaddl %eax, (%rdi)
+ decl %eax
+ ret
+
+// PRInt32 _MD_AtomicSet(PRInt32 *val, PRInt32 newval)
+//
+// Atomically set the integer pointed to by 'val' to the new
+// value 'newval' and return the old value.
+//
+ .text
+ .globl _MD_AtomicSet
+ .align 4
+_MD_AtomicSet:
+ movl %esi, %eax
+ xchgl %eax, (%rdi)
+ ret
+
+// PRInt32 _MD_AtomicAdd(PRInt32 *ptr, PRInt32 val)
+//
+// Atomically add 'val' to the integer pointed to by 'ptr'
+// and return the result of the addition.
+//
+ .text
+ .globl _MD_AtomicAdd
+ .align 4
+_MD_AtomicAdd:
+ movl %esi, %eax
+ lock
+ xaddl %eax, (%rdi)
+ addl %esi, %eax
+ ret
diff --git a/nsprpub/pr/src/md/unix/pthreads_user.c b/nsprpub/pr/src/md/unix/pthreads_user.c
new file mode 100644
index 0000000000..525c1576ff
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/pthreads_user.c
@@ -0,0 +1,464 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <pthread.h>
+
+
+sigset_t ints_off;
+pthread_mutex_t _pr_heapLock;
+pthread_key_t current_thread_key;
+pthread_key_t current_cpu_key;
+pthread_key_t last_thread_key;
+pthread_key_t intsoff_key;
+
+
+PRInt32 _pr_md_pthreads_created, _pr_md_pthreads_failed;
+PRInt32 _pr_md_pthreads = 1;
+
+void _MD_EarlyInit(void)
+{
+ extern PRInt32 _nspr_noclock;
+
+ if (pthread_key_create(&current_thread_key, NULL) != 0) {
+ perror("pthread_key_create failed");
+ exit(1);
+ }
+ if (pthread_key_create(&current_cpu_key, NULL) != 0) {
+ perror("pthread_key_create failed");
+ exit(1);
+ }
+ if (pthread_key_create(&last_thread_key, NULL) != 0) {
+ perror("pthread_key_create failed");
+ exit(1);
+ }
+ if (pthread_key_create(&intsoff_key, NULL) != 0) {
+ perror("pthread_key_create failed");
+ exit(1);
+ }
+
+ sigemptyset(&ints_off);
+ sigaddset(&ints_off, SIGALRM);
+ sigaddset(&ints_off, SIGIO);
+ sigaddset(&ints_off, SIGCLD);
+
+ /*
+ * disable clock interrupts
+ */
+ _nspr_noclock = 1;
+
+}
+
+void _MD_InitLocks()
+{
+ if (pthread_mutex_init(&_pr_heapLock, NULL) != 0) {
+ perror("pthread_mutex_init failed");
+ exit(1);
+ }
+}
+
+PR_IMPLEMENT(void) _MD_FREE_LOCK(struct _MDLock *lockp)
+{
+ PRIntn _is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me && !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(_is);
+ }
+ pthread_mutex_destroy(&lockp->mutex);
+ if (me && !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_FAST_INTSON(_is);
+ }
+}
+
+
+
+PR_IMPLEMENT(PRStatus) _MD_NEW_LOCK(struct _MDLock *lockp)
+{
+ PRStatus rv;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me && !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ rv = pthread_mutex_init(&lockp->mutex, NULL);
+ if (me && !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_FAST_INTSON(is);
+ }
+ return (rv == 0) ? PR_SUCCESS : PR_FAILURE;
+}
+
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+
+PR_IMPLEMENT(void)
+_MD_SetPriority(_MDThread *thread, PRThreadPriority newPri)
+{
+ /*
+ * XXX - to be implemented
+ */
+ return;
+}
+
+PR_IMPLEMENT(PRStatus) _MD_InitThread(struct PRThread *thread)
+{
+ struct sigaction sigact;
+
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ thread->md.pthread = pthread_self();
+#if 0
+ /*
+ * set up SIGUSR1 handler; this is used to save state
+ * during PR_SuspendAll
+ */
+ sigact.sa_handler = save_context_and_block;
+ sigact.sa_flags = SA_RESTART;
+ /*
+ * Must mask clock interrupts
+ */
+ sigact.sa_mask = timer_set;
+ sigaction(SIGUSR1, &sigact, 0);
+#endif
+ }
+
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(void) _MD_ExitThread(struct PRThread *thread)
+{
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ _MD_CLEAN_THREAD(thread);
+ _MD_SET_CURRENT_THREAD(NULL);
+ }
+}
+
+PR_IMPLEMENT(void) _MD_CleanThread(struct PRThread *thread)
+{
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ pthread_mutex_destroy(&thread->md.pthread_mutex);
+ pthread_cond_destroy(&thread->md.pthread_cond);
+ }
+}
+
+PR_IMPLEMENT(void) _MD_SuspendThread(struct PRThread *thread)
+{
+ PRInt32 rv;
+
+ PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) &&
+ _PR_IS_GCABLE_THREAD(thread));
+#if 0
+ thread->md.suspending_id = getpid();
+ rv = kill(thread->md.id, SIGUSR1);
+ PR_ASSERT(rv == 0);
+ /*
+ * now, block the current thread/cpu until woken up by the suspended
+ * thread from it's SIGUSR1 signal handler
+ */
+ blockproc(getpid());
+#endif
+}
+
+PR_IMPLEMENT(void) _MD_ResumeThread(struct PRThread *thread)
+{
+ PRInt32 rv;
+
+ PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) &&
+ _PR_IS_GCABLE_THREAD(thread));
+#if 0
+ rv = unblockproc(thread->md.id);
+#endif
+}
+
+PR_IMPLEMENT(void) _MD_SuspendCPU(struct _PRCPU *thread)
+{
+ PRInt32 rv;
+
+#if 0
+ cpu->md.suspending_id = getpid();
+ rv = kill(cpu->md.id, SIGUSR1);
+ PR_ASSERT(rv == 0);
+ /*
+ * now, block the current thread/cpu until woken up by the suspended
+ * thread from it's SIGUSR1 signal handler
+ */
+ blockproc(getpid());
+#endif
+}
+
+PR_IMPLEMENT(void) _MD_ResumeCPU(struct _PRCPU *thread)
+{
+#if 0
+ unblockproc(cpu->md.id);
+#endif
+}
+
+
+#define PT_NANOPERMICRO 1000UL
+#define PT_BILLION 1000000000UL
+
+PR_IMPLEMENT(PRStatus)
+_pt_wait(PRThread *thread, PRIntervalTime timeout)
+{
+ int rv;
+ struct timeval now;
+ struct timespec tmo;
+ PRUint32 ticks = PR_TicksPerSecond();
+
+
+ if (timeout != PR_INTERVAL_NO_TIMEOUT) {
+ tmo.tv_sec = timeout / ticks;
+ tmo.tv_nsec = timeout - (tmo.tv_sec * ticks);
+ tmo.tv_nsec = PR_IntervalToMicroseconds(PT_NANOPERMICRO *
+ tmo.tv_nsec);
+
+ /* pthreads wants this in absolute time, off we go ... */
+ (void)GETTIMEOFDAY(&now);
+ /* that one's usecs, this one's nsecs - grrrr! */
+ tmo.tv_sec += now.tv_sec;
+ tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec);
+ tmo.tv_sec += tmo.tv_nsec / PT_BILLION;
+ tmo.tv_nsec %= PT_BILLION;
+ }
+
+ pthread_mutex_lock(&thread->md.pthread_mutex);
+ thread->md.wait--;
+ if (thread->md.wait < 0) {
+ if (timeout != PR_INTERVAL_NO_TIMEOUT) {
+ rv = pthread_cond_timedwait(&thread->md.pthread_cond,
+ &thread->md.pthread_mutex, &tmo);
+ }
+ else
+ rv = pthread_cond_wait(&thread->md.pthread_cond,
+ &thread->md.pthread_mutex);
+ if (rv != 0) {
+ thread->md.wait++;
+ }
+ } else {
+ rv = 0;
+ }
+ pthread_mutex_unlock(&thread->md.pthread_mutex);
+
+ return (rv == 0) ? PR_SUCCESS : PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_wait(PRThread *thread, PRIntervalTime ticks)
+{
+ if ( thread->flags & _PR_GLOBAL_SCOPE ) {
+ _MD_CHECK_FOR_EXIT();
+ if (_pt_wait(thread, ticks) == PR_FAILURE) {
+ _MD_CHECK_FOR_EXIT();
+ /*
+ * wait timed out
+ */
+ _PR_THREAD_LOCK(thread);
+ if (thread->wait.cvar) {
+ /*
+ * The thread will remove itself from the waitQ
+ * of the cvar in _PR_WaitCondVar
+ */
+ thread->wait.cvar = NULL;
+ thread->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ _pt_wait(thread, PR_INTERVAL_NO_TIMEOUT);
+ _PR_THREAD_UNLOCK(thread);
+ }
+ }
+ } else {
+ _PR_MD_SWITCH_CONTEXT(thread);
+ }
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_WakeupWaiter(PRThread *thread)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 pid, rv;
+ PRIntn is;
+
+ PR_ASSERT(_pr_md_idle_cpus >= 0);
+ if (thread == NULL) {
+ if (_pr_md_idle_cpus) {
+ _MD_Wakeup_CPUs();
+ }
+ } else if (!_PR_IS_NATIVE_THREAD(thread)) {
+ /*
+ * If the thread is on my cpu's runq there is no need to
+ * wakeup any cpus
+ */
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if (me->cpu != thread->cpu) {
+ if (_pr_md_idle_cpus) {
+ _MD_Wakeup_CPUs();
+ }
+ }
+ } else {
+ if (_pr_md_idle_cpus) {
+ _MD_Wakeup_CPUs();
+ }
+ }
+ } else {
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(thread));
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+
+ pthread_mutex_lock(&thread->md.pthread_mutex);
+ thread->md.wait++;
+ rv = pthread_cond_signal(&thread->md.pthread_cond);
+ PR_ASSERT(rv == 0);
+ pthread_mutex_unlock(&thread->md.pthread_mutex);
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_FAST_INTSON(is);
+ }
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for AIX */
+PR_IMPLEMENT(void)
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for AIX.");
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_CreateThread(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PRIntn is;
+ int rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ pthread_attr_t attr;
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+
+ if (pthread_mutex_init(&thread->md.pthread_mutex, NULL) != 0) {
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_FAST_INTSON(is);
+ }
+ return PR_FAILURE;
+ }
+
+ if (pthread_cond_init(&thread->md.pthread_cond, NULL) != 0) {
+ pthread_mutex_destroy(&thread->md.pthread_mutex);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_FAST_INTSON(is);
+ }
+ return PR_FAILURE;
+ }
+ thread->flags |= _PR_GLOBAL_SCOPE;
+
+ pthread_attr_init(&attr); /* initialize attr with default attributes */
+ if (pthread_attr_setstacksize(&attr, (size_t) stackSize) != 0) {
+ pthread_mutex_destroy(&thread->md.pthread_mutex);
+ pthread_cond_destroy(&thread->md.pthread_cond);
+ pthread_attr_destroy(&attr);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_FAST_INTSON(is);
+ }
+ return PR_FAILURE;
+ }
+
+ thread->md.wait = 0;
+ rv = pthread_create(&thread->md.pthread, &attr, start, (void *)thread);
+ if (0 == rv) {
+ _MD_ATOMIC_INCREMENT(&_pr_md_pthreads_created);
+ _MD_ATOMIC_INCREMENT(&_pr_md_pthreads);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_FAST_INTSON(is);
+ }
+ return PR_SUCCESS;
+ } else {
+ pthread_mutex_destroy(&thread->md.pthread_mutex);
+ pthread_cond_destroy(&thread->md.pthread_cond);
+ pthread_attr_destroy(&attr);
+ _MD_ATOMIC_INCREMENT(&_pr_md_pthreads_failed);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_FAST_INTSON(is);
+ }
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, rv);
+ return PR_FAILURE;
+ }
+}
+
+PR_IMPLEMENT(void)
+_MD_InitRunningCPU(struct _PRCPU *cpu)
+{
+ extern int _pr_md_pipefd[2];
+
+ _MD_unix_init_running_cpu(cpu);
+ cpu->md.pthread = pthread_self();
+ if (_pr_md_pipefd[0] >= 0) {
+ _PR_IOQ_MAX_OSFD(cpu) = _pr_md_pipefd[0];
+#ifndef _PR_USE_POLL
+ FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(cpu));
+#endif
+ }
+}
+
+
+void
+_MD_CleanupBeforeExit(void)
+{
+#if 0
+ extern PRInt32 _pr_cpus_exit;
+
+ _pr_irix_exit_now = 1;
+ if (_pr_numCPU > 1) {
+ /*
+ * Set a global flag, and wakeup all cpus which will notice the flag
+ * and exit.
+ */
+ _pr_cpus_exit = getpid();
+ _MD_Wakeup_CPUs();
+ while(_pr_numCPU > 1) {
+ _PR_WAIT_SEM(_pr_irix_exit_sem);
+ _pr_numCPU--;
+ }
+ }
+ /*
+ * cause global threads on the recycle list to exit
+ */
+ _PR_DEADQ_LOCK;
+ if (_PR_NUM_DEADNATIVE != 0) {
+ PRThread *thread;
+ PRCList *ptr;
+
+ ptr = _PR_DEADNATIVEQ.next;
+ while( ptr != &_PR_DEADNATIVEQ ) {
+ thread = _PR_THREAD_PTR(ptr);
+ _MD_CVAR_POST_SEM(thread);
+ ptr = ptr->next;
+ }
+ }
+ _PR_DEADQ_UNLOCK;
+ while(_PR_NUM_DEADNATIVE > 1) {
+ _PR_WAIT_SEM(_pr_irix_exit_sem);
+ _PR_DEC_DEADNATIVE;
+ }
+#endif
+}
diff --git a/nsprpub/pr/src/md/unix/qnx.c b/nsprpub/pr/src/md/unix/qnx.c
new file mode 100644
index 0000000000..aef97a25a0
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/qnx.c
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <setjmp.h>
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for Unixware */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for Unixware.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRUintn priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Unixware.");
+ return PR_FAILURE;
+}
diff --git a/nsprpub/pr/src/md/unix/riscos.c b/nsprpub/pr/src/md/unix/riscos.c
new file mode 100644
index 0000000000..a041188753
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/riscos.c
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifdef _PR_PTHREADS
+
+void _MD_CleanupBeforeExit(void)
+{
+}
+
+#else /* ! _PR_PTHREADS */
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ /*
+ * set the pointers to the stack-pointer and frame-pointer words in the
+ * context structure; this is for debugging use.
+ */
+ thread->md.sp = _MD_GET_SP_PTR(thread);
+ thread->md.fp = _MD_GET_FP_PTR(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for RISC OS */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for RISC OS.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for RISC OS.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
diff --git a/nsprpub/pr/src/md/unix/scoos.c b/nsprpub/pr/src/md/unix/scoos.c
new file mode 100644
index 0000000000..43b256d82b
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/scoos.c
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * SCO ODT 5.0 - originally created by mikep
+ */
+#include "primpl.h"
+
+#include <setjmp.h>
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+
+#ifdef ALARMS_BREAK_TCP /* I don't think they do */
+
+PRInt32 _MD_connect(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+
+ _MD_BLOCK_CLOCK_INTERRUPTS();
+ rv = _connect(osfd,addr,addrlen);
+ _MD_UNBLOCK_CLOCK_INTERRUPTS();
+}
+
+PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+
+ _MD_BLOCK_CLOCK_INTERRUPTS();
+ rv = _accept(osfd,addr,addrlen);
+ _MD_UNBLOCK_CLOCK_INTERRUPTS();
+ return(rv);
+}
+#endif
+
+/*
+ * These are also implemented in pratom.c using NSPR locks. Any reason
+ * this might be better or worse? If you like this better, define
+ * _PR_HAVE_ATOMIC_OPS in include/md/unixware.h
+ */
+#ifdef _PR_HAVE_ATOMIC_OPS
+/* Atomic operations */
+#include <stdio.h>
+static FILE *_uw_semf;
+
+void
+_MD_INIT_ATOMIC(void)
+{
+ /* Sigh. Sure wish SYSV semaphores weren't such a pain to use */
+ if ((_uw_semf = tmpfile()) == NULL) {
+ PR_ASSERT(0);
+ }
+
+ return;
+}
+
+void
+_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ flockfile(_uw_semf);
+ (*val)++;
+ unflockfile(_uw_semf);
+}
+
+void
+_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
+{
+ flockfile(_uw_semf);
+ (*ptr) += val;
+ unflockfile(_uw_semf);
+}
+
+void
+_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ flockfile(_uw_semf);
+ (*val)--;
+ unflockfile(_uw_semf);
+}
+
+void
+_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ flockfile(_uw_semf);
+ *val = newval;
+ unflockfile(_uw_semf);
+}
+#endif
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for SCO */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for SCO.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for SCO.");
+}
diff --git a/nsprpub/pr/src/md/unix/solaris.c b/nsprpub/pr/src/md/unix/solaris.c
new file mode 100644
index 0000000000..383f49d044
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/solaris.c
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+
+extern PRBool suspendAllOn;
+extern PRThread *suspendAllThread;
+
+extern void _MD_SET_PRIORITY(_MDThread *md, PRThreadPriority newPri);
+
+PRIntervalTime _MD_Solaris_TicksPerSecond(void)
+{
+ /*
+ * Ticks have a 10-microsecond resolution. So there are
+ * 100000 ticks per second.
+ */
+ return 100000UL;
+}
+
+/* Interval timers, implemented using gethrtime() */
+
+PRIntervalTime _MD_Solaris_GetInterval(void)
+{
+ union {
+ hrtime_t hrt; /* hrtime_t is a 64-bit (long long) integer */
+ PRInt64 pr64;
+ } time;
+ PRInt64 resolution;
+ PRIntervalTime ticks;
+
+ time.hrt = gethrtime(); /* in nanoseconds */
+ /*
+ * Convert from nanoseconds to ticks. A tick's resolution is
+ * 10 microseconds, or 10000 nanoseconds.
+ */
+ LL_I2L(resolution, 10000);
+ LL_DIV(time.pr64, time.pr64, resolution);
+ LL_L2UI(ticks, time.pr64);
+ return ticks;
+}
+
+#ifdef _PR_PTHREADS
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, PRIntn isCurrent, PRIntn *np)
+{
+ *np = 0;
+ return NULL;
+}
+#endif /* _PR_PTHREADS */
+
+#if defined(_PR_LOCAL_THREADS_ONLY)
+
+void _MD_EarlyInit(void)
+{
+}
+
+void _MD_SolarisInit()
+{
+ _PR_UnixInit();
+}
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ PR_ASSERT((thread == NULL) || (!(thread->flags & _PR_GLOBAL_SCOPE)));
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for Solaris */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for Solaris");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Solaris");
+ return(PR_FAILURE);
+}
+
+#ifdef USE_SETJMP
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+#else
+PRWord *_MD_HomeGCRegisters(PRThread *t, PRIntn isCurrent, PRIntn *np)
+{
+ if (isCurrent) {
+ (void) getcontext(CONTEXT(t));
+ }
+ *np = NGREG;
+ return (PRWord*) &t->md.context.uc_mcontext.gregs[0];
+}
+#endif /* USE_SETJMP */
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+#ifndef _PR_PTHREADS
+#if defined(i386) && defined(SOLARIS2_4)
+/*
+ * Because clock_gettime() on Solaris/x86 2.4 always generates a
+ * segmentation fault, we use an emulated version _pr_solx86_clock_gettime(),
+ * which is implemented using gettimeofday().
+ */
+
+int
+_pr_solx86_clock_gettime(clockid_t clock_id, struct timespec *tp)
+{
+ struct timeval tv;
+
+ if (clock_id != CLOCK_REALTIME) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ gettimeofday(&tv, NULL);
+ tp->tv_sec = tv.tv_sec;
+ tp->tv_nsec = tv.tv_usec * 1000;
+ return 0;
+}
+#endif /* i386 && SOLARIS2_4 */
+#endif /* _PR_PTHREADS */
diff --git a/nsprpub/pr/src/md/unix/unix.c b/nsprpub/pr/src/md/unix/unix.c
new file mode 100644
index 0000000000..70bb8e8744
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/unix.c
@@ -0,0 +1,3744 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#ifdef _PR_POLL_AVAILABLE
+#include <poll.h>
+#endif
+
+#if defined(ANDROID)
+#include <android/api-level.h>
+#endif
+
+/* To get FIONREAD */
+#if defined(UNIXWARE)
+#include <sys/filio.h>
+#endif
+
+#if defined(NTO)
+#include <sys/statvfs.h>
+#endif
+
+/*
+ * Make sure _PRSockLen_t is 32-bit, because we will cast a PRUint32* or
+ * PRInt32* pointer to a _PRSockLen_t* pointer.
+ */
+#if defined(HAVE_SOCKLEN_T) \
+ || (defined(__GLIBC__) && __GLIBC__ >= 2)
+#define _PRSockLen_t socklen_t
+#elif defined(HPUX) || defined(SOLARIS) \
+ || defined(AIX4_1) || defined(LINUX) \
+ || defined(BSDI) || defined(SCO) \
+ || defined(DARWIN) \
+ || defined(QNX)
+#define _PRSockLen_t int
+#elif (defined(AIX) && !defined(AIX4_1)) || defined(FREEBSD) \
+ || defined(NETBSD) || defined(OPENBSD) || defined(UNIXWARE) \
+ || defined(NTO) || defined(RISCOS)
+#define _PRSockLen_t size_t
+#else
+#error "Cannot determine architecture"
+#endif
+
+/*
+** Global lock variable used to bracket calls into rusty libraries that
+** aren't thread safe (like libc, libX, etc).
+*/
+static PRLock *_pr_unix_rename_lock = NULL;
+static PRMonitor *_pr_Xfe_mon = NULL;
+
+static PRInt64 minus_one;
+
+sigset_t timer_set;
+
+#if !defined(_PR_PTHREADS)
+
+static sigset_t empty_set;
+
+#ifdef SOLARIS
+#include <sys/file.h>
+#include <sys/filio.h>
+#endif
+
+#ifndef PIPE_BUF
+#define PIPE_BUF 512
+#endif
+
+/*
+ * _nspr_noclock - if set clock interrupts are disabled
+ */
+int _nspr_noclock = 1;
+
+/*
+ * There is an assertion in this code that NSPR's definition of PRIOVec
+ * is bit compatible with UNIX' definition of a struct iovec. This is
+ * applicable to the 'writev()' operations where the types are casually
+ * cast to avoid warnings.
+ */
+
+int _pr_md_pipefd[2] = { -1, -1 };
+static char _pr_md_pipebuf[PIPE_BUF];
+static PRInt32 local_io_wait(PRInt32 osfd, PRInt32 wait_flag,
+ PRIntervalTime timeout);
+
+_PRInterruptTable _pr_interruptTable[] = {
+ {
+ "clock", _PR_MISSED_CLOCK, _PR_ClockInterrupt,
+ },
+ {
+ 0
+ }
+};
+
+void _MD_unix_init_running_cpu(_PRCPU *cpu)
+{
+ PR_INIT_CLIST(&(cpu->md.md_unix.ioQ));
+ cpu->md.md_unix.ioq_max_osfd = -1;
+ cpu->md.md_unix.ioq_timeout = PR_INTERVAL_NO_TIMEOUT;
+}
+
+PRStatus _MD_open_dir(_MDDir *d, const char *name)
+{
+ int err;
+
+ d->d = opendir(name);
+ if (!d->d) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_OPENDIR_ERROR(err);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PRInt32 _MD_close_dir(_MDDir *d)
+{
+ int rv = 0, err;
+
+ if (d->d) {
+ rv = closedir(d->d);
+ if (rv == -1) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_CLOSEDIR_ERROR(err);
+ }
+ }
+ return rv;
+}
+
+char * _MD_read_dir(_MDDir *d, PRIntn flags)
+{
+ struct dirent *de;
+ int err;
+
+ for (;;) {
+ /*
+ * XXX: readdir() is not MT-safe. There is an MT-safe version
+ * readdir_r() on some systems.
+ */
+ _MD_ERRNO() = 0;
+ de = readdir(d->d);
+ if (!de) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_READDIR_ERROR(err);
+ return 0;
+ }
+ if ((flags & PR_SKIP_DOT) &&
+ (de->d_name[0] == '.') && (de->d_name[1] == 0)) {
+ continue;
+ }
+ if ((flags & PR_SKIP_DOT_DOT) &&
+ (de->d_name[0] == '.') && (de->d_name[1] == '.') &&
+ (de->d_name[2] == 0)) {
+ continue;
+ }
+ if ((flags & PR_SKIP_HIDDEN) && (de->d_name[0] == '.')) {
+ continue;
+ }
+ break;
+ }
+ return de->d_name;
+}
+
+PRInt32 _MD_delete(const char *name)
+{
+ PRInt32 rv, err;
+#ifdef UNIXWARE
+ sigset_t set, oset;
+#endif
+
+#ifdef UNIXWARE
+ sigfillset(&set);
+ sigprocmask(SIG_SETMASK, &set, &oset);
+#endif
+ rv = unlink(name);
+#ifdef UNIXWARE
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+#endif
+ if (rv == -1) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_UNLINK_ERROR(err);
+ }
+ return(rv);
+}
+
+PRInt32 _MD_rename(const char *from, const char *to)
+{
+ PRInt32 rv = -1, err;
+
+ /*
+ ** This is trying to enforce the semantics of WINDOZE' rename
+ ** operation. That means one is not allowed to rename over top
+ ** of an existing file. Holding a lock across these two function
+ ** and the open function is known to be a bad idea, but ....
+ */
+ if (NULL != _pr_unix_rename_lock) {
+ PR_Lock(_pr_unix_rename_lock);
+ }
+ if (0 == access(to, F_OK)) {
+ PR_SetError(PR_FILE_EXISTS_ERROR, 0);
+ }
+ else
+ {
+ rv = rename(from, to);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_RENAME_ERROR(err);
+ }
+ }
+ if (NULL != _pr_unix_rename_lock) {
+ PR_Unlock(_pr_unix_rename_lock);
+ }
+ return rv;
+}
+
+PRInt32 _MD_access(const char *name, PRAccessHow how)
+{
+ PRInt32 rv, err;
+ int amode;
+
+ switch (how) {
+ case PR_ACCESS_WRITE_OK:
+ amode = W_OK;
+ break;
+ case PR_ACCESS_READ_OK:
+ amode = R_OK;
+ break;
+ case PR_ACCESS_EXISTS:
+ amode = F_OK;
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ rv = access(name, amode);
+
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_ACCESS_ERROR(err);
+ }
+
+done:
+ return(rv);
+}
+
+PRInt32 _MD_mkdir(const char *name, PRIntn mode)
+{
+ int rv, err;
+
+ /*
+ ** This lock is used to enforce rename semantics as described
+ ** in PR_Rename. Look there for more fun details.
+ */
+ if (NULL !=_pr_unix_rename_lock) {
+ PR_Lock(_pr_unix_rename_lock);
+ }
+ rv = mkdir(name, mode);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_MKDIR_ERROR(err);
+ }
+ if (NULL !=_pr_unix_rename_lock) {
+ PR_Unlock(_pr_unix_rename_lock);
+ }
+ return rv;
+}
+
+PRInt32 _MD_rmdir(const char *name)
+{
+ int rv, err;
+
+ rv = rmdir(name);
+ if (rv == -1) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_RMDIR_ERROR(err);
+ }
+ return rv;
+}
+
+PRInt32 _MD_read(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv, err;
+#ifndef _PR_USE_POLL
+ fd_set rd;
+#else
+ struct pollfd pfd;
+#endif /* _PR_USE_POLL */
+ PRInt32 osfd = fd->secret->md.osfd;
+
+#ifndef _PR_USE_POLL
+ FD_ZERO(&rd);
+ FD_SET(osfd, &rd);
+#else
+ pfd.fd = osfd;
+ pfd.events = POLLIN;
+#endif /* _PR_USE_POLL */
+ while ((rv = read(osfd,buf,amount)) == -1) {
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ,
+ PR_INTERVAL_NO_TIMEOUT)) < 0) {
+ goto done;
+ }
+ } else {
+#ifndef _PR_USE_POLL
+ while ((rv = _MD_SELECT(osfd + 1, &rd, NULL, NULL, NULL))
+ == -1 && (err = _MD_ERRNO()) == EINTR) {
+ /* retry _MD_SELECT() if it is interrupted */
+ }
+#else /* _PR_USE_POLL */
+ while ((rv = _MD_POLL(&pfd, 1, -1))
+ == -1 && (err = _MD_ERRNO()) == EINTR) {
+ /* retry _MD_POLL() if it is interrupted */
+ }
+#endif /* _PR_USE_POLL */
+ if (rv == -1) {
+ break;
+ }
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ break;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ _PR_MD_MAP_READ_ERROR(err);
+ }
+ }
+done:
+ return(rv);
+}
+
+PRInt32 _MD_write(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv, err;
+#ifndef _PR_USE_POLL
+ fd_set wd;
+#else
+ struct pollfd pfd;
+#endif /* _PR_USE_POLL */
+ PRInt32 osfd = fd->secret->md.osfd;
+
+#ifndef _PR_USE_POLL
+ FD_ZERO(&wd);
+ FD_SET(osfd, &wd);
+#else
+ pfd.fd = osfd;
+ pfd.events = POLLOUT;
+#endif /* _PR_USE_POLL */
+ while ((rv = write(osfd,buf,amount)) == -1) {
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE,
+ PR_INTERVAL_NO_TIMEOUT)) < 0) {
+ goto done;
+ }
+ } else {
+#ifndef _PR_USE_POLL
+ while ((rv = _MD_SELECT(osfd + 1, NULL, &wd, NULL, NULL))
+ == -1 && (err = _MD_ERRNO()) == EINTR) {
+ /* retry _MD_SELECT() if it is interrupted */
+ }
+#else /* _PR_USE_POLL */
+ while ((rv = _MD_POLL(&pfd, 1, -1))
+ == -1 && (err = _MD_ERRNO()) == EINTR) {
+ /* retry _MD_POLL() if it is interrupted */
+ }
+#endif /* _PR_USE_POLL */
+ if (rv == -1) {
+ break;
+ }
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ break;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ _PR_MD_MAP_WRITE_ERROR(err);
+ }
+ }
+done:
+ return(rv);
+}
+
+PRInt32 _MD_fsync(PRFileDesc *fd)
+{
+ PRInt32 rv, err;
+
+ rv = fsync(fd->secret->md.osfd);
+ if (rv == -1) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_FSYNC_ERROR(err);
+ }
+ return(rv);
+}
+
+PRInt32 _MD_close(PRInt32 osfd)
+{
+ PRInt32 rv, err;
+
+ rv = close(osfd);
+ if (rv == -1) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_CLOSE_ERROR(err);
+ }
+ return(rv);
+}
+
+PRInt32 _MD_socket(PRInt32 domain, PRInt32 type, PRInt32 proto)
+{
+ PRInt32 osfd, err;
+
+ osfd = socket(domain, type, proto);
+
+ if (osfd == -1) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_SOCKET_ERROR(err);
+ return(osfd);
+ }
+
+ return(osfd);
+}
+
+PRInt32 _MD_socketavailable(PRFileDesc *fd)
+{
+ PRInt32 result;
+
+ if (ioctl(fd->secret->md.osfd, FIONREAD, &result) < 0) {
+ _PR_MD_MAP_SOCKETAVAILABLE_ERROR(_MD_ERRNO());
+ return -1;
+ }
+ return result;
+}
+
+PRInt64 _MD_socketavailable64(PRFileDesc *fd)
+{
+ PRInt64 result;
+ LL_I2L(result, _MD_socketavailable(fd));
+ return result;
+} /* _MD_socketavailable64 */
+
+#define READ_FD 1
+#define WRITE_FD 2
+
+/*
+ * socket_io_wait --
+ *
+ * wait for socket i/o, periodically checking for interrupt
+ *
+ * The first implementation uses select(), for platforms without
+ * poll(). The second (preferred) implementation uses poll().
+ */
+
+#ifndef _PR_USE_POLL
+
+static PRInt32 socket_io_wait(PRInt32 osfd, PRInt32 fd_type,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv = -1;
+ struct timeval tv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntervalTime epoch, now, elapsed, remaining;
+ PRBool wait_for_remaining;
+ PRInt32 syserror;
+ fd_set rd_wr;
+
+ switch (timeout) {
+ case PR_INTERVAL_NO_WAIT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ break;
+ case PR_INTERVAL_NO_TIMEOUT:
+ /*
+ * This is a special case of the 'default' case below.
+ * Please see the comments there.
+ */
+ tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
+ tv.tv_usec = 0;
+ FD_ZERO(&rd_wr);
+ do {
+ FD_SET(osfd, &rd_wr);
+ if (fd_type == READ_FD) {
+ rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, &tv);
+ }
+ else {
+ rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv);
+ }
+ if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
+ _PR_MD_MAP_SELECT_ERROR(syserror);
+ break;
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ } while (rv == 0 || (rv == -1 && syserror == EINTR));
+ break;
+ default:
+ now = epoch = PR_IntervalNow();
+ remaining = timeout;
+ FD_ZERO(&rd_wr);
+ do {
+ /*
+ * We block in _MD_SELECT for at most
+ * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds,
+ * so that there is an upper limit on the delay
+ * before the interrupt bit is checked.
+ */
+ wait_for_remaining = PR_TRUE;
+ tv.tv_sec = PR_IntervalToSeconds(remaining);
+ if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) {
+ wait_for_remaining = PR_FALSE;
+ tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
+ tv.tv_usec = 0;
+ } else {
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ remaining -
+ PR_SecondsToInterval(tv.tv_sec));
+ }
+ FD_SET(osfd, &rd_wr);
+ if (fd_type == READ_FD) {
+ rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, &tv);
+ }
+ else {
+ rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv);
+ }
+ /*
+ * we don't consider EINTR a real error
+ */
+ if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
+ _PR_MD_MAP_SELECT_ERROR(syserror);
+ break;
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ /*
+ * We loop again if _MD_SELECT timed out or got interrupted
+ * by a signal, and the timeout deadline has not passed yet.
+ */
+ if (rv == 0 || (rv == -1 && syserror == EINTR)) {
+ /*
+ * If _MD_SELECT timed out, we know how much time
+ * we spent in blocking, so we can avoid a
+ * PR_IntervalNow() call.
+ */
+ if (rv == 0) {
+ if (wait_for_remaining) {
+ now += remaining;
+ } else {
+ now += PR_SecondsToInterval(tv.tv_sec)
+ + PR_MicrosecondsToInterval(tv.tv_usec);
+ }
+ } else {
+ now = PR_IntervalNow();
+ }
+ elapsed = (PRIntervalTime) (now - epoch);
+ if (elapsed >= timeout) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ } else {
+ remaining = timeout - elapsed;
+ }
+ }
+ } while (rv == 0 || (rv == -1 && syserror == EINTR));
+ break;
+ }
+ return(rv);
+}
+
+#else /* _PR_USE_POLL */
+
+static PRInt32 socket_io_wait(PRInt32 osfd, PRInt32 fd_type,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv = -1;
+ int msecs;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntervalTime epoch, now, elapsed, remaining;
+ PRBool wait_for_remaining;
+ PRInt32 syserror;
+ struct pollfd pfd;
+
+ switch (timeout) {
+ case PR_INTERVAL_NO_WAIT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ break;
+ case PR_INTERVAL_NO_TIMEOUT:
+ /*
+ * This is a special case of the 'default' case below.
+ * Please see the comments there.
+ */
+ msecs = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000;
+ pfd.fd = osfd;
+ if (fd_type == READ_FD) {
+ pfd.events = POLLIN;
+ } else {
+ pfd.events = POLLOUT;
+ }
+ do {
+ rv = _MD_POLL(&pfd, 1, msecs);
+ if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
+ _PR_MD_MAP_POLL_ERROR(syserror);
+ break;
+ }
+ /*
+ * If POLLERR is set, don't process it; retry the operation
+ */
+ if ((rv == 1) && (pfd.revents & (POLLHUP | POLLNVAL))) {
+ rv = -1;
+ _PR_MD_MAP_POLL_REVENTS_ERROR(pfd.revents);
+ break;
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ } while (rv == 0 || (rv == -1 && syserror == EINTR));
+ break;
+ default:
+ now = epoch = PR_IntervalNow();
+ remaining = timeout;
+ pfd.fd = osfd;
+ if (fd_type == READ_FD) {
+ pfd.events = POLLIN;
+ } else {
+ pfd.events = POLLOUT;
+ }
+ do {
+ /*
+ * We block in _MD_POLL for at most
+ * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds,
+ * so that there is an upper limit on the delay
+ * before the interrupt bit is checked.
+ */
+ wait_for_remaining = PR_TRUE;
+ msecs = PR_IntervalToMilliseconds(remaining);
+ if (msecs > _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000) {
+ wait_for_remaining = PR_FALSE;
+ msecs = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000;
+ }
+ rv = _MD_POLL(&pfd, 1, msecs);
+ /*
+ * we don't consider EINTR a real error
+ */
+ if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
+ _PR_MD_MAP_POLL_ERROR(syserror);
+ break;
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ /*
+ * If POLLERR is set, don't process it; retry the operation
+ */
+ if ((rv == 1) && (pfd.revents & (POLLHUP | POLLNVAL))) {
+ rv = -1;
+ _PR_MD_MAP_POLL_REVENTS_ERROR(pfd.revents);
+ break;
+ }
+ /*
+ * We loop again if _MD_POLL timed out or got interrupted
+ * by a signal, and the timeout deadline has not passed yet.
+ */
+ if (rv == 0 || (rv == -1 && syserror == EINTR)) {
+ /*
+ * If _MD_POLL timed out, we know how much time
+ * we spent in blocking, so we can avoid a
+ * PR_IntervalNow() call.
+ */
+ if (rv == 0) {
+ if (wait_for_remaining) {
+ now += remaining;
+ } else {
+ now += PR_MillisecondsToInterval(msecs);
+ }
+ } else {
+ now = PR_IntervalNow();
+ }
+ elapsed = (PRIntervalTime) (now - epoch);
+ if (elapsed >= timeout) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ } else {
+ remaining = timeout - elapsed;
+ }
+ }
+ } while (rv == 0 || (rv == -1 && syserror == EINTR));
+ break;
+ }
+ return(rv);
+}
+
+#endif /* _PR_USE_POLL */
+
+static PRInt32 local_io_wait(
+ PRInt32 osfd,
+ PRInt32 wait_flag,
+ PRIntervalTime timeout)
+{
+ _PRUnixPollDesc pd;
+ PRInt32 rv;
+
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("waiting to %s on osfd=%d",
+ (wait_flag == _PR_UNIX_POLL_READ) ? "read" : "write",
+ osfd));
+
+ if (timeout == PR_INTERVAL_NO_WAIT) {
+ return 0;
+ }
+
+ pd.osfd = osfd;
+ pd.in_flags = wait_flag;
+ pd.out_flags = 0;
+
+ rv = _PR_WaitForMultipleFDs(&pd, 1, timeout);
+
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ }
+ return rv;
+}
+
+
+PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRInt32 flags, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ /*
+ * Many OS's (Solaris, Unixware) have a broken recv which won't read
+ * from socketpairs. As long as we don't use flags on socketpairs, this
+ * is a decent fix. - mikep
+ */
+#if defined(UNIXWARE) || defined(SOLARIS)
+ while ((rv = read(osfd,buf,amount)) == -1) {
+#else
+ while ((rv = recv(osfd,buf,amount,flags)) == -1) {
+#endif
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if ((rv = local_io_wait(osfd,_PR_UNIX_POLL_READ,timeout)) < 0) {
+ goto done;
+ }
+ } else {
+ if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) {
+ goto done;
+ }
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_RECV_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ while ((*addrlen = PR_NETADDR_SIZE(addr)),
+ ((rv = recvfrom(osfd, buf, amount, flags,
+ (struct sockaddr *) addr, (_PRSockLen_t *)addrlen)) == -1)) {
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ, timeout)) < 0) {
+ goto done;
+ }
+ } else {
+ if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) {
+ goto done;
+ }
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ }
+done:
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ if (rv != -1) {
+ /* ignore the sa_len field of struct sockaddr */
+ if (addr) {
+ addr->raw.family = ((struct sockaddr *) addr)->sa_family;
+ }
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+ return(rv);
+}
+
+PRInt32 _MD_send(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRInt32 flags, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+#if defined(SOLARIS)
+ PRInt32 tmp_amount = amount;
+#endif
+
+ /*
+ * On pre-2.6 Solaris, send() is much slower than write().
+ * On 2.6 and beyond, with in-kernel sockets, send() and
+ * write() are fairly equivalent in performance.
+ */
+#if defined(SOLARIS)
+ PR_ASSERT(0 == flags);
+ while ((rv = write(osfd,buf,tmp_amount)) == -1) {
+#else
+ while ((rv = send(osfd,buf,amount,flags)) == -1) {
+#endif
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) {
+ goto done;
+ }
+ } else {
+ if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))< 0) {
+ goto done;
+ }
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+#if defined(SOLARIS)
+ /*
+ * The write system call has been reported to return the ERANGE
+ * error on occasion. Try to write in smaller chunks to workaround
+ * this bug.
+ */
+ if (err == ERANGE) {
+ if (tmp_amount > 1) {
+ tmp_amount = tmp_amount/2; /* half the bytes */
+ continue;
+ }
+ }
+#endif
+ break;
+ }
+ }
+ /*
+ * optimization; if bytes sent is less than "amount" call
+ * select before returning. This is because it is likely that
+ * the next send() call will return EWOULDBLOCK.
+ */
+ if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount)
+ && (timeout != PR_INTERVAL_NO_WAIT)) {
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ if (socket_io_wait(osfd, WRITE_FD, timeout)< 0) {
+ rv = -1;
+ goto done;
+ }
+ } else {
+ if (local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout) < 0) {
+ rv = -1;
+ goto done;
+ }
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_SEND_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32 _MD_sendto(
+ PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ PRNetAddr addrCopy;
+
+ addrCopy = *addr;
+ ((struct sockaddr *) &addrCopy)->sa_len = addrlen;
+ ((struct sockaddr *) &addrCopy)->sa_family = addr->raw.family;
+
+ while ((rv = sendto(osfd, buf, amount, flags,
+ (struct sockaddr *) &addrCopy, addrlen)) == -1) {
+#else
+ while ((rv = sendto(osfd, buf, amount, flags,
+ (struct sockaddr *) addr, addrlen)) == -1) {
+#endif
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) {
+ goto done;
+ }
+ } else {
+ if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))< 0) {
+ goto done;
+ }
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32 _MD_writev(
+ PRFileDesc *fd, const PRIOVec *iov,
+ PRInt32 iov_size, PRIntervalTime timeout)
+{
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 index, amount = 0;
+ PRInt32 osfd = fd->secret->md.osfd;
+
+ /*
+ * Calculate the total number of bytes to be sent; needed for
+ * optimization later.
+ * We could avoid this if this number was passed in; but it is
+ * probably not a big deal because iov_size is usually small (less than
+ * 3)
+ */
+ if (!fd->secret->nonblocking) {
+ for (index=0; index<iov_size; index++) {
+ amount += iov[index].iov_len;
+ }
+ }
+
+ while ((rv = writev(osfd, (const struct iovec*)iov, iov_size)) == -1) {
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) {
+ goto done;
+ }
+ } else {
+ if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))<0) {
+ goto done;
+ }
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ /*
+ * optimization; if bytes sent is less than "amount" call
+ * select before returning. This is because it is likely that
+ * the next writev() call will return EWOULDBLOCK.
+ */
+ if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount)
+ && (timeout != PR_INTERVAL_NO_WAIT)) {
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ if (socket_io_wait(osfd, WRITE_FD, timeout) < 0) {
+ rv = -1;
+ goto done;
+ }
+ } else {
+ if (local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout) < 0) {
+ rv = -1;
+ goto done;
+ }
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_WRITEV_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ while ((rv = accept(osfd, (struct sockaddr *) addr,
+ (_PRSockLen_t *)addrlen)) == -1) {
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK) || (err == ECONNABORTED)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ, timeout)) < 0) {
+ goto done;
+ }
+ } else {
+ if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) {
+ goto done;
+ }
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+done:
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ if (rv != -1) {
+ /* ignore the sa_len field of struct sockaddr */
+ if (addr) {
+ addr->raw.family = ((struct sockaddr *) addr)->sa_family;
+ }
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+ return(rv);
+}
+
+extern int _connect (int s, const struct sockaddr *name, int namelen);
+PRInt32 _MD_connect(
+ PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 osfd = fd->secret->md.osfd;
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ PRNetAddr addrCopy;
+
+ addrCopy = *addr;
+ ((struct sockaddr *) &addrCopy)->sa_len = addrlen;
+ ((struct sockaddr *) &addrCopy)->sa_family = addr->raw.family;
+#endif
+
+ /*
+ * We initiate the connection setup by making a nonblocking connect()
+ * call. If the connect() call fails, there are two cases we handle
+ * specially:
+ * 1. The connect() call was interrupted by a signal. In this case
+ * we simply retry connect().
+ * 2. The NSPR socket is nonblocking and connect() fails with
+ * EINPROGRESS. We first wait until the socket becomes writable.
+ * Then we try to find out whether the connection setup succeeded
+ * or failed.
+ */
+
+retry:
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ if ((rv = connect(osfd, (struct sockaddr *)&addrCopy, addrlen)) == -1) {
+#else
+ if ((rv = connect(osfd, (struct sockaddr *)addr, addrlen)) == -1) {
+#endif
+ err = _MD_ERRNO();
+
+ if (err == EINTR) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ goto retry;
+ }
+
+ if (!fd->secret->nonblocking && (err == EINPROGRESS)) {
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+
+ if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) {
+ return -1;
+ }
+ } else {
+ /*
+ * socket_io_wait() may return -1 or 1.
+ */
+
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if (rv == -1) {
+ return -1;
+ }
+ }
+
+ PR_ASSERT(rv == 1);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ err = _MD_unix_get_nonblocking_connect_error(osfd);
+ if (err != 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ return -1;
+ }
+ return 0;
+ }
+
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+
+ return rv;
+} /* _MD_connect */
+
+PRInt32 _MD_bind(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 rv, err;
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ PRNetAddr addrCopy;
+
+ addrCopy = *addr;
+ ((struct sockaddr *) &addrCopy)->sa_len = addrlen;
+ ((struct sockaddr *) &addrCopy)->sa_family = addr->raw.family;
+ rv = bind(fd->secret->md.osfd, (struct sockaddr *) &addrCopy, (int )addrlen);
+#else
+ rv = bind(fd->secret->md.osfd, (struct sockaddr *) addr, (int )addrlen);
+#endif
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_BIND_ERROR(err);
+ }
+ return(rv);
+}
+
+PRInt32 _MD_listen(PRFileDesc *fd, PRIntn backlog)
+{
+ PRInt32 rv, err;
+
+ rv = listen(fd->secret->md.osfd, backlog);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_LISTEN_ERROR(err);
+ }
+ return(rv);
+}
+
+PRInt32 _MD_shutdown(PRFileDesc *fd, PRIntn how)
+{
+ PRInt32 rv, err;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_SHUTDOWN_ERROR(err);
+ }
+ return(rv);
+}
+
+PRInt32 _MD_socketpair(int af, int type, int flags,
+ PRInt32 *osfd)
+{
+ PRInt32 rv, err;
+
+ rv = socketpair(af, type, flags, osfd);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_SOCKETPAIR_ERROR(err);
+ }
+ return rv;
+}
+
+PRStatus _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen)
+{
+ PRInt32 rv, err;
+
+ rv = getsockname(fd->secret->md.osfd,
+ (struct sockaddr *) addr, (_PRSockLen_t *)addrlen);
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ if (rv == 0) {
+ /* ignore the sa_len field of struct sockaddr */
+ if (addr) {
+ addr->raw.family = ((struct sockaddr *) addr)->sa_family;
+ }
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_GETSOCKNAME_ERROR(err);
+ }
+ return rv==0?PR_SUCCESS:PR_FAILURE;
+}
+
+PRStatus _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen)
+{
+ PRInt32 rv, err;
+
+ rv = getpeername(fd->secret->md.osfd,
+ (struct sockaddr *) addr, (_PRSockLen_t *)addrlen);
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ if (rv == 0) {
+ /* ignore the sa_len field of struct sockaddr */
+ if (addr) {
+ addr->raw.family = ((struct sockaddr *) addr)->sa_family;
+ }
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_GETPEERNAME_ERROR(err);
+ }
+ return rv==0?PR_SUCCESS:PR_FAILURE;
+}
+
+PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level,
+ PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ PRInt32 rv, err;
+
+ rv = getsockopt(fd->secret->md.osfd, level, optname, optval, (_PRSockLen_t *)optlen);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_GETSOCKOPT_ERROR(err);
+ }
+ return rv==0?PR_SUCCESS:PR_FAILURE;
+}
+
+PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level,
+ PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ PRInt32 rv, err;
+
+ rv = setsockopt(fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_SETSOCKOPT_ERROR(err);
+ }
+ return rv==0?PR_SUCCESS:PR_FAILURE;
+}
+
+PRStatus _MD_set_fd_inheritable(PRFileDesc *fd, PRBool inheritable)
+{
+ int rv;
+
+ rv = fcntl(fd->secret->md.osfd, F_SETFD, inheritable ? 0 : FD_CLOEXEC);
+ if (-1 == rv) {
+ PR_SetError(PR_UNKNOWN_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+void _MD_init_fd_inheritable(PRFileDesc *fd, PRBool imported)
+{
+ if (imported) {
+ fd->secret->inheritable = _PR_TRI_UNKNOWN;
+ } else {
+ /* By default, a Unix fd is not closed on exec. */
+#ifdef DEBUG
+ {
+ int flags = fcntl(fd->secret->md.osfd, F_GETFD, 0);
+ PR_ASSERT(0 == flags);
+ }
+#endif
+ fd->secret->inheritable = _PR_TRI_TRUE;
+ }
+}
+
+/************************************************************************/
+#if !defined(_PR_USE_POLL)
+
+/*
+** Scan through io queue and find any bad fd's that triggered the error
+** from _MD_SELECT
+*/
+static void FindBadFDs(void)
+{
+ PRCList *q;
+ PRThread *me = _MD_CURRENT_THREAD();
+
+ PR_ASSERT(!_PR_IS_NATIVE_THREAD(me));
+ q = (_PR_IOQ(me->cpu)).next;
+ _PR_IOQ_MAX_OSFD(me->cpu) = -1;
+ _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
+ while (q != &_PR_IOQ(me->cpu)) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ PRBool notify = PR_FALSE;
+ _PRUnixPollDesc *pds = pq->pds;
+ _PRUnixPollDesc *epds = pds + pq->npds;
+ PRInt32 pq_max_osfd = -1;
+
+ q = q->next;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ pds->out_flags = 0;
+ PR_ASSERT(osfd >= 0 || pds->in_flags == 0);
+ if (pds->in_flags == 0) {
+ continue; /* skip this fd */
+ }
+ if (fcntl(osfd, F_GETFL, 0) == -1) {
+ /* Found a bad descriptor, remove it from the fd_sets. */
+ PR_LOG(_pr_io_lm, PR_LOG_MAX,
+ ("file descriptor %d is bad", osfd));
+ pds->out_flags = _PR_UNIX_POLL_NVAL;
+ notify = PR_TRUE;
+ }
+ if (osfd > pq_max_osfd) {
+ pq_max_osfd = osfd;
+ }
+ }
+
+ if (notify) {
+ PRIntn pri;
+ PR_REMOVE_LINK(&pq->links);
+ pq->on_ioq = PR_FALSE;
+
+ /*
+ * Decrement the count of descriptors for each desciptor/event
+ * because this I/O request is being removed from the
+ * ioq
+ */
+ pds = pq->pds;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ PRInt16 in_flags = pds->in_flags;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if (in_flags & _PR_UNIX_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0) {
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ }
+ if (in_flags & _PR_UNIX_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0) {
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ }
+ if (in_flags & _PR_UNIX_POLL_EXCEPT) {
+ if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0) {
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+ }
+
+ _PR_THREAD_LOCK(pq->thr);
+ if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
+ _PRCPU *cpu = pq->thr->cpu;
+ _PR_SLEEPQ_LOCK(pq->thr->cpu);
+ _PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(pq->thr->cpu);
+
+ if (pq->thr->flags & _PR_SUSPENDING) {
+ /*
+ * set thread state to SUSPENDED;
+ * a Resume operation on the thread
+ * will move it to the runQ
+ */
+ pq->thr->state = _PR_SUSPENDED;
+ _PR_MISCQ_LOCK(pq->thr->cpu);
+ _PR_ADD_SUSPENDQ(pq->thr, pq->thr->cpu);
+ _PR_MISCQ_UNLOCK(pq->thr->cpu);
+ } else {
+ pri = pq->thr->priority;
+ pq->thr->state = _PR_RUNNABLE;
+
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(pq->thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ }
+ }
+ _PR_THREAD_UNLOCK(pq->thr);
+ } else {
+ if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu)) {
+ _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
+ }
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd) {
+ _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
+ }
+ }
+ }
+ if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) {
+ _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0];
+ }
+ }
+}
+#endif /* !defined(_PR_USE_POLL) */
+
+/************************************************************************/
+
+/*
+** Called by the scheduler when there is nothing to do. This means that
+** all threads are blocked on some monitor somewhere.
+**
+** Note: this code doesn't release the scheduler lock.
+*/
+/*
+** Pause the current CPU. longjmp to the cpu's pause stack
+**
+** This must be called with the scheduler locked
+*/
+void _MD_PauseCPU(PRIntervalTime ticks)
+{
+ PRThread *me = _MD_CURRENT_THREAD();
+#ifdef _PR_USE_POLL
+ int timeout;
+ struct pollfd *pollfds; /* an array of pollfd structures */
+ struct pollfd *pollfdPtr; /* a pointer that steps through the array */
+ unsigned long npollfds; /* number of pollfd structures in array */
+ unsigned long pollfds_size;
+ int nfd; /* to hold the return value of poll() */
+#else
+ struct timeval timeout, *tvp;
+ fd_set r, w, e;
+ fd_set *rp, *wp, *ep;
+ PRInt32 max_osfd, nfd;
+#endif /* _PR_USE_POLL */
+ PRInt32 rv;
+ PRCList *q;
+ PRUint32 min_timeout;
+ sigset_t oldset;
+
+ PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);
+
+ _PR_MD_IOQ_LOCK();
+
+#ifdef _PR_USE_POLL
+ /* Build up the pollfd structure array to wait on */
+
+ /* Find out how many pollfd structures are needed */
+ npollfds = _PR_IOQ_OSFD_CNT(me->cpu);
+ PR_ASSERT(npollfds >= 0);
+
+ /*
+ * We use a pipe to wake up a native thread. An fd is needed
+ * for the pipe and we poll it for reading.
+ */
+ if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
+ npollfds++;
+ }
+
+ /*
+ * if the cpu's pollfd array is not big enough, release it and allocate a new one
+ */
+ if (npollfds > _PR_IOQ_POLLFDS_SIZE(me->cpu)) {
+ if (_PR_IOQ_POLLFDS(me->cpu) != NULL) {
+ PR_DELETE(_PR_IOQ_POLLFDS(me->cpu));
+ }
+ pollfds_size = PR_MAX(_PR_IOQ_MIN_POLLFDS_SIZE(me->cpu), npollfds);
+ pollfds = (struct pollfd *) PR_MALLOC(pollfds_size * sizeof(struct pollfd));
+ _PR_IOQ_POLLFDS(me->cpu) = pollfds;
+ _PR_IOQ_POLLFDS_SIZE(me->cpu) = pollfds_size;
+ } else {
+ pollfds = _PR_IOQ_POLLFDS(me->cpu);
+ }
+ pollfdPtr = pollfds;
+
+ /*
+ * If we need to poll the pipe for waking up a native thread,
+ * the pipe's fd is the first element in the pollfds array.
+ */
+ if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
+ pollfdPtr->fd = _pr_md_pipefd[0];
+ pollfdPtr->events = POLLIN;
+ pollfdPtr++;
+ }
+
+ min_timeout = PR_INTERVAL_NO_TIMEOUT;
+ for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ _PRUnixPollDesc *pds = pq->pds;
+ _PRUnixPollDesc *epds = pds + pq->npds;
+
+ if (pq->timeout < min_timeout) {
+ min_timeout = pq->timeout;
+ }
+ for (; pds < epds; pds++, pollfdPtr++) {
+ /*
+ * Assert that the pollfdPtr pointer does not go
+ * beyond the end of the pollfds array
+ */
+ PR_ASSERT(pollfdPtr < pollfds + npollfds);
+ pollfdPtr->fd = pds->osfd;
+ /* direct copy of poll flags */
+ pollfdPtr->events = pds->in_flags;
+ }
+ }
+ _PR_IOQ_TIMEOUT(me->cpu) = min_timeout;
+#else
+ /*
+ * assigment of fd_sets
+ */
+ r = _PR_FD_READ_SET(me->cpu);
+ w = _PR_FD_WRITE_SET(me->cpu);
+ e = _PR_FD_EXCEPTION_SET(me->cpu);
+
+ rp = &r;
+ wp = &w;
+ ep = &e;
+
+ max_osfd = _PR_IOQ_MAX_OSFD(me->cpu) + 1;
+ min_timeout = _PR_IOQ_TIMEOUT(me->cpu);
+#endif /* _PR_USE_POLL */
+ /*
+ ** Compute the minimum timeout value: make it the smaller of the
+ ** timeouts specified by the i/o pollers or the timeout of the first
+ ** sleeping thread.
+ */
+ q = _PR_SLEEPQ(me->cpu).next;
+
+ if (q != &_PR_SLEEPQ(me->cpu)) {
+ PRThread *t = _PR_THREAD_PTR(q);
+
+ if (t->sleep < min_timeout) {
+ min_timeout = t->sleep;
+ }
+ }
+ if (min_timeout > ticks) {
+ min_timeout = ticks;
+ }
+
+#ifdef _PR_USE_POLL
+ if (min_timeout == PR_INTERVAL_NO_TIMEOUT) {
+ timeout = -1;
+ }
+ else {
+ timeout = PR_IntervalToMilliseconds(min_timeout);
+ }
+#else
+ if (min_timeout == PR_INTERVAL_NO_TIMEOUT) {
+ tvp = NULL;
+ } else {
+ timeout.tv_sec = PR_IntervalToSeconds(min_timeout);
+ timeout.tv_usec = PR_IntervalToMicroseconds(min_timeout)
+ % PR_USEC_PER_SEC;
+ tvp = &timeout;
+ }
+#endif /* _PR_USE_POLL */
+
+ _PR_MD_IOQ_UNLOCK();
+ _MD_CHECK_FOR_EXIT();
+ /*
+ * check for i/o operations
+ */
+#ifndef _PR_NO_CLOCK_TIMER
+ /*
+ * Disable the clock interrupts while we are in select, if clock interrupts
+ * are enabled. Otherwise, when the select/poll calls are interrupted, the
+ * timer value starts ticking from zero again when the system call is restarted.
+ */
+ if (!_nspr_noclock) {
+ PR_ASSERT(sigismember(&timer_set, SIGALRM));
+ }
+ sigprocmask(SIG_BLOCK, &timer_set, &oldset);
+#endif /* !_PR_NO_CLOCK_TIMER */
+
+#ifndef _PR_USE_POLL
+ PR_ASSERT(FD_ISSET(_pr_md_pipefd[0],rp));
+ nfd = _MD_SELECT(max_osfd, rp, wp, ep, tvp);
+#else
+ nfd = _MD_POLL(pollfds, npollfds, timeout);
+#endif /* !_PR_USE_POLL */
+
+#ifndef _PR_NO_CLOCK_TIMER
+ if (!_nspr_noclock) {
+ sigprocmask(SIG_SETMASK, &oldset, 0);
+ }
+#endif /* !_PR_NO_CLOCK_TIMER */
+
+ _MD_CHECK_FOR_EXIT();
+
+ _PR_MD_primordial_cpu();
+
+ _PR_MD_IOQ_LOCK();
+ /*
+ ** Notify monitors that are associated with the selected descriptors.
+ */
+#ifdef _PR_USE_POLL
+ if (nfd > 0) {
+ pollfdPtr = pollfds;
+ if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
+ /*
+ * Assert that the pipe is the first element in the
+ * pollfds array.
+ */
+ PR_ASSERT(pollfds[0].fd == _pr_md_pipefd[0]);
+ if ((pollfds[0].revents & POLLIN) && (nfd == 1)) {
+ /*
+ * woken up by another thread; read all the data
+ * in the pipe to empty the pipe
+ */
+ while ((rv = read(_pr_md_pipefd[0], _pr_md_pipebuf,
+ PIPE_BUF)) == PIPE_BUF) {
+ }
+ PR_ASSERT((rv > 0) || ((rv == -1) && (errno == EAGAIN)));
+ }
+ pollfdPtr++;
+ }
+ for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ PRBool notify = PR_FALSE;
+ _PRUnixPollDesc *pds = pq->pds;
+ _PRUnixPollDesc *epds = pds + pq->npds;
+
+ for (; pds < epds; pds++, pollfdPtr++) {
+ /*
+ * Assert that the pollfdPtr pointer does not go beyond
+ * the end of the pollfds array.
+ */
+ PR_ASSERT(pollfdPtr < pollfds + npollfds);
+ /*
+ * Assert that the fd's in the pollfds array (stepped
+ * through by pollfdPtr) are in the same order as
+ * the fd's in _PR_IOQ() (stepped through by q and pds).
+ * This is how the pollfds array was created earlier.
+ */
+ PR_ASSERT(pollfdPtr->fd == pds->osfd);
+ pds->out_flags = pollfdPtr->revents;
+ /* Negative fd's are ignored by poll() */
+ if (pds->osfd >= 0 && pds->out_flags) {
+ notify = PR_TRUE;
+ }
+ }
+ if (notify) {
+ PRIntn pri;
+ PRThread *thred;
+
+ PR_REMOVE_LINK(&pq->links);
+ pq->on_ioq = PR_FALSE;
+
+ thred = pq->thr;
+ _PR_THREAD_LOCK(thred);
+ if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
+ _PRCPU *cpu = pq->thr->cpu;
+ _PR_SLEEPQ_LOCK(pq->thr->cpu);
+ _PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(pq->thr->cpu);
+
+ if (pq->thr->flags & _PR_SUSPENDING) {
+ /*
+ * set thread state to SUSPENDED;
+ * a Resume operation on the thread
+ * will move it to the runQ
+ */
+ pq->thr->state = _PR_SUSPENDED;
+ _PR_MISCQ_LOCK(pq->thr->cpu);
+ _PR_ADD_SUSPENDQ(pq->thr, pq->thr->cpu);
+ _PR_MISCQ_UNLOCK(pq->thr->cpu);
+ } else {
+ pri = pq->thr->priority;
+ pq->thr->state = _PR_RUNNABLE;
+
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(pq->thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ if (_pr_md_idle_cpus > 1) {
+ _PR_MD_WAKEUP_WAITER(thred);
+ }
+ }
+ }
+ _PR_THREAD_UNLOCK(thred);
+ _PR_IOQ_OSFD_CNT(me->cpu) -= pq->npds;
+ PR_ASSERT(_PR_IOQ_OSFD_CNT(me->cpu) >= 0);
+ }
+ }
+ } else if (nfd == -1) {
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("poll() failed with errno %d", errno));
+ }
+
+#else
+ if (nfd > 0) {
+ q = _PR_IOQ(me->cpu).next;
+ _PR_IOQ_MAX_OSFD(me->cpu) = -1;
+ _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
+ while (q != &_PR_IOQ(me->cpu)) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ PRBool notify = PR_FALSE;
+ _PRUnixPollDesc *pds = pq->pds;
+ _PRUnixPollDesc *epds = pds + pq->npds;
+ PRInt32 pq_max_osfd = -1;
+
+ q = q->next;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ PRInt16 in_flags = pds->in_flags;
+ PRInt16 out_flags = 0;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if ((in_flags & _PR_UNIX_POLL_READ) && FD_ISSET(osfd, rp)) {
+ out_flags |= _PR_UNIX_POLL_READ;
+ }
+ if ((in_flags & _PR_UNIX_POLL_WRITE) && FD_ISSET(osfd, wp)) {
+ out_flags |= _PR_UNIX_POLL_WRITE;
+ }
+ if ((in_flags & _PR_UNIX_POLL_EXCEPT) && FD_ISSET(osfd, ep)) {
+ out_flags |= _PR_UNIX_POLL_EXCEPT;
+ }
+ pds->out_flags = out_flags;
+ if (out_flags) {
+ notify = PR_TRUE;
+ }
+ if (osfd > pq_max_osfd) {
+ pq_max_osfd = osfd;
+ }
+ }
+ if (notify == PR_TRUE) {
+ PRIntn pri;
+ PRThread *thred;
+
+ PR_REMOVE_LINK(&pq->links);
+ pq->on_ioq = PR_FALSE;
+
+ /*
+ * Decrement the count of descriptors for each desciptor/event
+ * because this I/O request is being removed from the
+ * ioq
+ */
+ pds = pq->pds;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ PRInt16 in_flags = pds->in_flags;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if (in_flags & _PR_UNIX_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0) {
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ }
+ if (in_flags & _PR_UNIX_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0) {
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ }
+ if (in_flags & _PR_UNIX_POLL_EXCEPT) {
+ if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0) {
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+ }
+
+ /*
+ * Because this thread can run on a different cpu right
+ * after being added to the run queue, do not dereference
+ * pq
+ */
+ thred = pq->thr;
+ _PR_THREAD_LOCK(thred);
+ if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
+ _PRCPU *cpu = thred->cpu;
+ _PR_SLEEPQ_LOCK(pq->thr->cpu);
+ _PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(pq->thr->cpu);
+
+ if (pq->thr->flags & _PR_SUSPENDING) {
+ /*
+ * set thread state to SUSPENDED;
+ * a Resume operation on the thread
+ * will move it to the runQ
+ */
+ pq->thr->state = _PR_SUSPENDED;
+ _PR_MISCQ_LOCK(pq->thr->cpu);
+ _PR_ADD_SUSPENDQ(pq->thr, pq->thr->cpu);
+ _PR_MISCQ_UNLOCK(pq->thr->cpu);
+ } else {
+ pri = pq->thr->priority;
+ pq->thr->state = _PR_RUNNABLE;
+
+ pq->thr->cpu = cpu;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(pq->thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ if (_pr_md_idle_cpus > 1) {
+ _PR_MD_WAKEUP_WAITER(thred);
+ }
+ }
+ }
+ _PR_THREAD_UNLOCK(thred);
+ } else {
+ if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu)) {
+ _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
+ }
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd) {
+ _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
+ }
+ }
+ }
+ if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
+ if ((FD_ISSET(_pr_md_pipefd[0], rp)) && (nfd == 1)) {
+ /*
+ * woken up by another thread; read all the data
+ * in the pipe to empty the pipe
+ */
+ while ((rv =
+ read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF))
+ == PIPE_BUF) {
+ }
+ PR_ASSERT((rv > 0) ||
+ ((rv == -1) && (errno == EAGAIN)));
+ }
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) {
+ _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0];
+ }
+ }
+ } else if (nfd < 0) {
+ if (errno == EBADF) {
+ FindBadFDs();
+ } else {
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("select() failed with errno %d",
+ errno));
+ }
+ } else {
+ PR_ASSERT(nfd == 0);
+ /*
+ * compute the new value of _PR_IOQ_TIMEOUT
+ */
+ q = _PR_IOQ(me->cpu).next;
+ _PR_IOQ_MAX_OSFD(me->cpu) = -1;
+ _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
+ while (q != &_PR_IOQ(me->cpu)) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ _PRUnixPollDesc *pds = pq->pds;
+ _PRUnixPollDesc *epds = pds + pq->npds;
+ PRInt32 pq_max_osfd = -1;
+
+ q = q->next;
+ for (; pds < epds; pds++) {
+ if (pds->osfd > pq_max_osfd) {
+ pq_max_osfd = pds->osfd;
+ }
+ }
+ if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu)) {
+ _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
+ }
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd) {
+ _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
+ }
+ }
+ if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) {
+ _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0];
+ }
+ }
+ }
+#endif /* _PR_USE_POLL */
+ _PR_MD_IOQ_UNLOCK();
+}
+
+void _MD_Wakeup_CPUs()
+{
+ PRInt32 rv, data;
+
+ data = 0;
+ rv = write(_pr_md_pipefd[1], &data, 1);
+
+ while ((rv < 0) && (errno == EAGAIN)) {
+ /*
+ * pipe full, read all data in pipe to empty it
+ */
+ while ((rv =
+ read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF))
+ == PIPE_BUF) {
+ }
+ PR_ASSERT((rv > 0) ||
+ ((rv == -1) && (errno == EAGAIN)));
+ rv = write(_pr_md_pipefd[1], &data, 1);
+ }
+}
+
+
+void _MD_InitCPUS()
+{
+ PRInt32 rv, flags;
+ PRThread *me = _MD_CURRENT_THREAD();
+
+ rv = pipe(_pr_md_pipefd);
+ PR_ASSERT(rv == 0);
+ _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0];
+#ifndef _PR_USE_POLL
+ FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(me->cpu));
+#endif
+
+ flags = fcntl(_pr_md_pipefd[0], F_GETFL, 0);
+ fcntl(_pr_md_pipefd[0], F_SETFL, flags | O_NONBLOCK);
+ flags = fcntl(_pr_md_pipefd[1], F_GETFL, 0);
+ fcntl(_pr_md_pipefd[1], F_SETFL, flags | O_NONBLOCK);
+}
+
+/*
+** Unix SIGALRM (clock) signal handler
+*/
+static void ClockInterruptHandler()
+{
+ int olderrno;
+ PRUintn pri;
+ _PRCPU *cpu = _PR_MD_CURRENT_CPU();
+ PRThread *me = _MD_CURRENT_THREAD();
+
+#ifdef SOLARIS
+ if (!me || _PR_IS_NATIVE_THREAD(me)) {
+ _pr_primordialCPU->u.missed[_pr_primordialCPU->where] |= _PR_MISSED_CLOCK;
+ return;
+ }
+#endif
+
+ if (_PR_MD_GET_INTSOFF() != 0) {
+ cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK;
+ return;
+ }
+ _PR_MD_SET_INTSOFF(1);
+
+ olderrno = errno;
+ _PR_ClockInterrupt();
+ errno = olderrno;
+
+ /*
+ ** If the interrupt wants a resched or if some other thread at
+ ** the same priority needs the cpu, reschedule.
+ */
+ pri = me->priority;
+ if ((cpu->u.missed[3] || (_PR_RUNQREADYMASK(me->cpu) >> pri))) {
+#ifdef _PR_NO_PREEMPT
+ cpu->resched = PR_TRUE;
+ if (pr_interruptSwitchHook) {
+ (*pr_interruptSwitchHook)(pr_interruptSwitchHookArg);
+ }
+#else /* _PR_NO_PREEMPT */
+ /*
+ ** Re-enable unix interrupts (so that we can use
+ ** setjmp/longjmp for context switching without having to
+ ** worry about the signal state)
+ */
+ sigprocmask(SIG_SETMASK, &empty_set, 0);
+ PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("clock caused context switch"));
+
+ if(!(me->flags & _PR_IDLE_THREAD)) {
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_RUNNABLE;
+ me->cpu = cpu;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(me, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ _PR_THREAD_UNLOCK(me);
+ } else {
+ me->state = _PR_RUNNABLE;
+ }
+ _MD_SWITCH_CONTEXT(me);
+ PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("clock back from context switch"));
+#endif /* _PR_NO_PREEMPT */
+ }
+ /*
+ * Because this thread could be running on a different cpu after
+ * a context switch the current cpu should be accessed and the
+ * value of the 'cpu' variable should not be used.
+ */
+ _PR_MD_SET_INTSOFF(0);
+}
+
+/*
+ * On HP-UX 9, we have to use the sigvector() interface to restart
+ * interrupted system calls, because sigaction() does not have the
+ * SA_RESTART flag.
+ */
+
+#ifdef HPUX9
+static void HPUX9_ClockInterruptHandler(
+ int sig,
+ int code,
+ struct sigcontext *scp)
+{
+ ClockInterruptHandler();
+ scp->sc_syscall_action = SIG_RESTART;
+}
+#endif /* HPUX9 */
+
+/* # of milliseconds per clock tick that we will use */
+#define MSEC_PER_TICK 50
+
+
+void _MD_StartInterrupts()
+{
+ char *eval;
+
+ if ((eval = getenv("NSPR_NOCLOCK")) != NULL) {
+ if (atoi(eval) == 0) {
+ _nspr_noclock = 0;
+ }
+ else {
+ _nspr_noclock = 1;
+ }
+ }
+
+#ifndef _PR_NO_CLOCK_TIMER
+ if (!_nspr_noclock) {
+ _MD_EnableClockInterrupts();
+ }
+#endif
+}
+
+void _MD_StopInterrupts()
+{
+ sigprocmask(SIG_BLOCK, &timer_set, 0);
+}
+
+void _MD_EnableClockInterrupts()
+{
+ struct itimerval itval;
+ extern PRUintn _pr_numCPU;
+#ifdef HPUX9
+ struct sigvec vec;
+
+ vec.sv_handler = (void (*)()) HPUX9_ClockInterruptHandler;
+ vec.sv_mask = 0;
+ vec.sv_flags = 0;
+ sigvector(SIGALRM, &vec, 0);
+#else
+ struct sigaction vtact;
+
+ vtact.sa_handler = (void (*)()) ClockInterruptHandler;
+ sigemptyset(&vtact.sa_mask);
+ vtact.sa_flags = SA_RESTART;
+ sigaction(SIGALRM, &vtact, 0);
+#endif /* HPUX9 */
+
+ PR_ASSERT(_pr_numCPU == 1);
+ itval.it_interval.tv_sec = 0;
+ itval.it_interval.tv_usec = MSEC_PER_TICK * PR_USEC_PER_MSEC;
+ itval.it_value = itval.it_interval;
+ setitimer(ITIMER_REAL, &itval, 0);
+}
+
+void _MD_DisableClockInterrupts()
+{
+ struct itimerval itval;
+ extern PRUintn _pr_numCPU;
+
+ PR_ASSERT(_pr_numCPU == 1);
+ itval.it_interval.tv_sec = 0;
+ itval.it_interval.tv_usec = 0;
+ itval.it_value = itval.it_interval;
+ setitimer(ITIMER_REAL, &itval, 0);
+}
+
+void _MD_BlockClockInterrupts()
+{
+ sigprocmask(SIG_BLOCK, &timer_set, 0);
+}
+
+void _MD_UnblockClockInterrupts()
+{
+ sigprocmask(SIG_UNBLOCK, &timer_set, 0);
+}
+
+void _MD_MakeNonblock(PRFileDesc *fd)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ int flags;
+
+ if (osfd <= 2) {
+ /* Don't mess around with stdin, stdout or stderr */
+ return;
+ }
+ flags = fcntl(osfd, F_GETFL, 0);
+
+ /*
+ * Use O_NONBLOCK (POSIX-style non-blocking I/O) whenever possible.
+ * On SunOS 4, we must use FNDELAY (BSD-style non-blocking I/O),
+ * otherwise connect() still blocks and can be interrupted by SIGALRM.
+ */
+
+ fcntl(osfd, F_SETFL, flags | O_NONBLOCK);
+}
+
+PRInt32 _MD_open(const char *name, PRIntn flags, PRIntn mode)
+{
+ PRInt32 osflags;
+ PRInt32 rv, err;
+
+ if (flags & PR_RDWR) {
+ osflags = O_RDWR;
+ } else if (flags & PR_WRONLY) {
+ osflags = O_WRONLY;
+ } else {
+ osflags = O_RDONLY;
+ }
+
+ if (flags & PR_EXCL) {
+ osflags |= O_EXCL;
+ }
+ if (flags & PR_APPEND) {
+ osflags |= O_APPEND;
+ }
+ if (flags & PR_TRUNCATE) {
+ osflags |= O_TRUNC;
+ }
+ if (flags & PR_SYNC) {
+#if defined(O_SYNC)
+ osflags |= O_SYNC;
+#elif defined(O_FSYNC)
+ osflags |= O_FSYNC;
+#else
+#error "Neither O_SYNC nor O_FSYNC is defined on this platform"
+#endif
+ }
+
+ /*
+ ** On creations we hold the 'create' lock in order to enforce
+ ** the semantics of PR_Rename. (see the latter for more details)
+ */
+ if (flags & PR_CREATE_FILE)
+ {
+ osflags |= O_CREAT;
+ if (NULL !=_pr_unix_rename_lock) {
+ PR_Lock(_pr_unix_rename_lock);
+ }
+ }
+
+#if defined(ANDROID)
+ osflags |= O_LARGEFILE;
+#endif
+
+ rv = _md_iovector._open64(name, osflags, mode);
+
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_OPEN_ERROR(err);
+ }
+
+ if ((flags & PR_CREATE_FILE) && (NULL !=_pr_unix_rename_lock)) {
+ PR_Unlock(_pr_unix_rename_lock);
+ }
+ return rv;
+}
+
+PRIntervalTime intr_timeout_ticks;
+
+#if defined(SOLARIS)
+static void sigsegvhandler() {
+ fprintf(stderr,"Received SIGSEGV\n");
+ fflush(stderr);
+ pause();
+}
+
+static void sigaborthandler() {
+ fprintf(stderr,"Received SIGABRT\n");
+ fflush(stderr);
+ pause();
+}
+
+static void sigbushandler() {
+ fprintf(stderr,"Received SIGBUS\n");
+ fflush(stderr);
+ pause();
+}
+#endif /* SOLARIS */
+
+#endif /* !defined(_PR_PTHREADS) */
+
+void _MD_query_fd_inheritable(PRFileDesc *fd)
+{
+ int flags;
+
+ PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable);
+ flags = fcntl(fd->secret->md.osfd, F_GETFD, 0);
+ PR_ASSERT(-1 != flags);
+ fd->secret->inheritable = (flags & FD_CLOEXEC) ?
+ _PR_TRI_FALSE : _PR_TRI_TRUE;
+}
+
+PROffset32 _MD_lseek(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence)
+{
+ PROffset32 rv, where;
+
+ switch (whence) {
+ case PR_SEEK_SET:
+ where = SEEK_SET;
+ break;
+ case PR_SEEK_CUR:
+ where = SEEK_CUR;
+ break;
+ case PR_SEEK_END:
+ where = SEEK_END;
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ rv = lseek(fd->secret->md.osfd,offset,where);
+ if (rv == -1)
+ {
+ PRInt32 syserr = _MD_ERRNO();
+ _PR_MD_MAP_LSEEK_ERROR(syserr);
+ }
+done:
+ return(rv);
+}
+
+PROffset64 _MD_lseek64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence)
+{
+ PRInt32 where;
+ PROffset64 rv;
+
+ switch (whence)
+ {
+ case PR_SEEK_SET:
+ where = SEEK_SET;
+ break;
+ case PR_SEEK_CUR:
+ where = SEEK_CUR;
+ break;
+ case PR_SEEK_END:
+ where = SEEK_END;
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = minus_one;
+ goto done;
+ }
+ rv = _md_iovector._lseek64(fd->secret->md.osfd, offset, where);
+ if (LL_EQ(rv, minus_one))
+ {
+ PRInt32 syserr = _MD_ERRNO();
+ _PR_MD_MAP_LSEEK_ERROR(syserr);
+ }
+done:
+ return rv;
+} /* _MD_lseek64 */
+
+/*
+** _MD_set_fileinfo_times --
+** Set the modifyTime and creationTime of the PRFileInfo
+** structure using the values in struct stat.
+**
+** _MD_set_fileinfo64_times --
+** Set the modifyTime and creationTime of the PRFileInfo64
+** structure using the values in _MDStat64.
+*/
+
+#if defined(_PR_STAT_HAS_ST_ATIM)
+/*
+** struct stat has st_atim, st_mtim, and st_ctim fields of
+** type timestruc_t.
+*/
+static void _MD_set_fileinfo_times(
+ const struct stat *sb,
+ PRFileInfo *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtim.tv_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtim.tv_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctim.tv_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctim.tv_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+
+static void _MD_set_fileinfo64_times(
+ const _MDStat64 *sb,
+ PRFileInfo64 *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtim.tv_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtim.tv_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctim.tv_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctim.tv_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+#elif defined(_PR_STAT_HAS_ST_ATIM_UNION)
+/*
+** The st_atim, st_mtim, and st_ctim fields in struct stat are
+** unions with a st__tim union member of type timestruc_t.
+*/
+static void _MD_set_fileinfo_times(
+ const struct stat *sb,
+ PRFileInfo *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtim.st__tim.tv_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtim.st__tim.tv_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctim.st__tim.tv_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctim.st__tim.tv_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+
+static void _MD_set_fileinfo64_times(
+ const _MDStat64 *sb,
+ PRFileInfo64 *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtim.st__tim.tv_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtim.st__tim.tv_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctim.st__tim.tv_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctim.st__tim.tv_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+#elif defined(_PR_STAT_HAS_ST_ATIMESPEC)
+/*
+** struct stat has st_atimespec, st_mtimespec, and st_ctimespec
+** fields of type struct timespec.
+*/
+#if defined(_PR_TIMESPEC_HAS_TS_SEC)
+static void _MD_set_fileinfo_times(
+ const struct stat *sb,
+ PRFileInfo *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtimespec.ts_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtimespec.ts_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctimespec.ts_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctimespec.ts_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+
+static void _MD_set_fileinfo64_times(
+ const _MDStat64 *sb,
+ PRFileInfo64 *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtimespec.ts_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtimespec.ts_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctimespec.ts_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctimespec.ts_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+#else /* _PR_TIMESPEC_HAS_TS_SEC */
+/*
+** The POSIX timespec structure has tv_sec and tv_nsec.
+*/
+static void _MD_set_fileinfo_times(
+ const struct stat *sb,
+ PRFileInfo *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtimespec.tv_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtimespec.tv_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctimespec.tv_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctimespec.tv_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+
+static void _MD_set_fileinfo64_times(
+ const _MDStat64 *sb,
+ PRFileInfo64 *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtimespec.tv_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtimespec.tv_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctimespec.tv_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctimespec.tv_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+#endif /* _PR_TIMESPEC_HAS_TS_SEC */
+#elif defined(_PR_STAT_HAS_ONLY_ST_ATIME)
+/*
+** struct stat only has st_atime, st_mtime, and st_ctime fields
+** of type time_t.
+*/
+static void _MD_set_fileinfo_times(
+ const struct stat *sb,
+ PRFileInfo *info)
+{
+ PRInt64 s, s2us;
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, sb->st_mtime);
+ LL_MUL(s, s, s2us);
+ info->modifyTime = s;
+ LL_I2L(s, sb->st_ctime);
+ LL_MUL(s, s, s2us);
+ info->creationTime = s;
+}
+
+static void _MD_set_fileinfo64_times(
+ const _MDStat64 *sb,
+ PRFileInfo64 *info)
+{
+ PRInt64 s, s2us;
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, sb->st_mtime);
+ LL_MUL(s, s, s2us);
+ info->modifyTime = s;
+ LL_I2L(s, sb->st_ctime);
+ LL_MUL(s, s, s2us);
+ info->creationTime = s;
+}
+#else
+#error "I don't know yet"
+#endif
+
+static int _MD_convert_stat_to_fileinfo(
+ const struct stat *sb,
+ PRFileInfo *info)
+{
+ if (S_IFREG & sb->st_mode) {
+ info->type = PR_FILE_FILE;
+ }
+ else if (S_IFDIR & sb->st_mode) {
+ info->type = PR_FILE_DIRECTORY;
+ }
+ else {
+ info->type = PR_FILE_OTHER;
+ }
+
+#if defined(_PR_HAVE_LARGE_OFF_T)
+ if (0x7fffffffL < sb->st_size)
+ {
+ PR_SetError(PR_FILE_TOO_BIG_ERROR, 0);
+ return -1;
+ }
+#endif /* defined(_PR_HAVE_LARGE_OFF_T) */
+ info->size = sb->st_size;
+
+ _MD_set_fileinfo_times(sb, info);
+ return 0;
+} /* _MD_convert_stat_to_fileinfo */
+
+static int _MD_convert_stat64_to_fileinfo64(
+ const _MDStat64 *sb,
+ PRFileInfo64 *info)
+{
+ if (S_IFREG & sb->st_mode) {
+ info->type = PR_FILE_FILE;
+ }
+ else if (S_IFDIR & sb->st_mode) {
+ info->type = PR_FILE_DIRECTORY;
+ }
+ else {
+ info->type = PR_FILE_OTHER;
+ }
+
+ LL_I2L(info->size, sb->st_size);
+
+ _MD_set_fileinfo64_times(sb, info);
+ return 0;
+} /* _MD_convert_stat64_to_fileinfo64 */
+
+PRInt32 _MD_getfileinfo(const char *fn, PRFileInfo *info)
+{
+ PRInt32 rv;
+ struct stat sb;
+
+ rv = stat(fn, &sb);
+ if (rv < 0) {
+ _PR_MD_MAP_STAT_ERROR(_MD_ERRNO());
+ }
+ else if (NULL != info) {
+ rv = _MD_convert_stat_to_fileinfo(&sb, info);
+ }
+ return rv;
+}
+
+PRInt32 _MD_getfileinfo64(const char *fn, PRFileInfo64 *info)
+{
+ _MDStat64 sb;
+ PRInt32 rv = _md_iovector._stat64(fn, &sb);
+ if (rv < 0) {
+ _PR_MD_MAP_STAT_ERROR(_MD_ERRNO());
+ }
+ else if (NULL != info) {
+ rv = _MD_convert_stat64_to_fileinfo64(&sb, info);
+ }
+ return rv;
+}
+
+PRInt32 _MD_getopenfileinfo(const PRFileDesc *fd, PRFileInfo *info)
+{
+ struct stat sb;
+ PRInt32 rv = fstat(fd->secret->md.osfd, &sb);
+ if (rv < 0) {
+ _PR_MD_MAP_FSTAT_ERROR(_MD_ERRNO());
+ }
+ else if (NULL != info) {
+ rv = _MD_convert_stat_to_fileinfo(&sb, info);
+ }
+ return rv;
+}
+
+PRInt32 _MD_getopenfileinfo64(const PRFileDesc *fd, PRFileInfo64 *info)
+{
+ _MDStat64 sb;
+ PRInt32 rv = _md_iovector._fstat64(fd->secret->md.osfd, &sb);
+ if (rv < 0) {
+ _PR_MD_MAP_FSTAT_ERROR(_MD_ERRNO());
+ }
+ else if (NULL != info) {
+ rv = _MD_convert_stat64_to_fileinfo64(&sb, info);
+ }
+ return rv;
+}
+
+/*
+ * _md_iovector._open64 must be initialized to 'open' so that _PR_InitLog can
+ * open the log file during NSPR initialization, before _md_iovector is
+ * initialized by _PR_MD_FINAL_INIT. This means the log file cannot be a
+ * large file on some platforms.
+ */
+struct _MD_IOVector _md_iovector = { open };
+
+/*
+** These implementations are to emulate large file routines on systems that
+** don't have them. Their goal is to check in case overflow occurs. Otherwise
+** they will just operate as normal using 32-bit file routines.
+**
+** The checking might be pre- or post-op, depending on the semantics.
+*/
+
+#if defined(SOLARIS2_5)
+
+static PRIntn _MD_solaris25_fstat64(PRIntn osfd, _MDStat64 *buf)
+{
+ PRInt32 rv;
+ struct stat sb;
+
+ rv = fstat(osfd, &sb);
+ if (rv >= 0)
+ {
+ /*
+ ** I'm only copying the fields that are immediately needed.
+ ** If somebody else calls this function, some of the fields
+ ** may not be defined.
+ */
+ (void)memset(buf, 0, sizeof(_MDStat64));
+ buf->st_mode = sb.st_mode;
+ buf->st_ctim = sb.st_ctim;
+ buf->st_mtim = sb.st_mtim;
+ buf->st_size = sb.st_size;
+ }
+ return rv;
+} /* _MD_solaris25_fstat64 */
+
+static PRIntn _MD_solaris25_stat64(const char *fn, _MDStat64 *buf)
+{
+ PRInt32 rv;
+ struct stat sb;
+
+ rv = stat(fn, &sb);
+ if (rv >= 0)
+ {
+ /*
+ ** I'm only copying the fields that are immediately needed.
+ ** If somebody else calls this function, some of the fields
+ ** may not be defined.
+ */
+ (void)memset(buf, 0, sizeof(_MDStat64));
+ buf->st_mode = sb.st_mode;
+ buf->st_ctim = sb.st_ctim;
+ buf->st_mtim = sb.st_mtim;
+ buf->st_size = sb.st_size;
+ }
+ return rv;
+} /* _MD_solaris25_stat64 */
+#endif /* defined(SOLARIS2_5) */
+
+#if defined(_PR_NO_LARGE_FILES) || defined(SOLARIS2_5)
+
+static PROffset64 _MD_Unix_lseek64(PRIntn osfd, PROffset64 offset, PRIntn whence)
+{
+ PRUint64 maxoff;
+ PROffset64 rv = minus_one;
+ LL_I2L(maxoff, 0x7fffffff);
+ if (LL_CMP(offset, <=, maxoff))
+ {
+ off_t off;
+ LL_L2I(off, offset);
+ LL_I2L(rv, lseek(osfd, off, whence));
+ }
+ else {
+ errno = EFBIG; /* we can't go there */
+ }
+ return rv;
+} /* _MD_Unix_lseek64 */
+
+static void* _MD_Unix_mmap64(
+ void *addr, PRSize len, PRIntn prot, PRIntn flags,
+ PRIntn fildes, PRInt64 offset)
+{
+ PR_SetError(PR_FILE_TOO_BIG_ERROR, 0);
+ return NULL;
+} /* _MD_Unix_mmap64 */
+#endif /* defined(_PR_NO_LARGE_FILES) || defined(SOLARIS2_5) */
+
+/* NDK non-unified headers for API < 21 don't have mmap64. However,
+ * NDK unified headers do provide mmap64 for all API versions when building
+ * with clang. Therefore, we should provide mmap64 here for API < 21 if we're
+ * not using clang or if we're using non-unified headers. We check for
+ * non-unified headers by the lack of __ANDROID_API_L__ macro. */
+#if defined(ANDROID) && __ANDROID_API__ < 21 && \
+ (!defined(__clang__) || !defined(__ANDROID_API_L__))
+PR_IMPORT(void) *__mmap2(void *, size_t, int, int, int, size_t);
+
+#define ANDROID_PAGE_SIZE 4096
+
+static void *
+mmap64(void *addr, size_t len, int prot, int flags, int fd, loff_t offset)
+{
+ if (offset & (ANDROID_PAGE_SIZE - 1)) {
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+ return __mmap2(addr, len, prot, flags, fd, offset / ANDROID_PAGE_SIZE);
+}
+#endif
+
+static void _PR_InitIOV(void)
+{
+#if defined(SOLARIS2_5)
+ PRLibrary *lib;
+ void *open64_func;
+
+ open64_func = PR_FindSymbolAndLibrary("open64", &lib);
+ if (NULL != open64_func)
+ {
+ PR_ASSERT(NULL != lib);
+ _md_iovector._open64 = (_MD_Open64)open64_func;
+ _md_iovector._mmap64 = (_MD_Mmap64)PR_FindSymbol(lib, "mmap64");
+ _md_iovector._fstat64 = (_MD_Fstat64)PR_FindSymbol(lib, "fstat64");
+ _md_iovector._stat64 = (_MD_Stat64)PR_FindSymbol(lib, "stat64");
+ _md_iovector._lseek64 = (_MD_Lseek64)PR_FindSymbol(lib, "lseek64");
+ (void)PR_UnloadLibrary(lib);
+ }
+ else
+ {
+ _md_iovector._open64 = open;
+ _md_iovector._mmap64 = _MD_Unix_mmap64;
+ _md_iovector._fstat64 = _MD_solaris25_fstat64;
+ _md_iovector._stat64 = _MD_solaris25_stat64;
+ _md_iovector._lseek64 = _MD_Unix_lseek64;
+ }
+#elif defined(_PR_NO_LARGE_FILES)
+ _md_iovector._open64 = open;
+ _md_iovector._mmap64 = _MD_Unix_mmap64;
+ _md_iovector._fstat64 = fstat;
+ _md_iovector._stat64 = stat;
+ _md_iovector._lseek64 = _MD_Unix_lseek64;
+#elif defined(_PR_HAVE_OFF64_T)
+#if (defined(ANDROID) && __ANDROID_API__ < 21)
+ /*
+ * Android < 21 doesn't have open64. We pass the O_LARGEFILE flag to open
+ * in _MD_open.
+ */
+ _md_iovector._open64 = open;
+#else
+ _md_iovector._open64 = open64;
+#endif
+ _md_iovector._mmap64 = mmap64;
+#if (defined(ANDROID) && __ANDROID_API__ < 21)
+ /* Same as the open64 case for Android. */
+ _md_iovector._fstat64 = (_MD_Fstat64)fstat;
+ _md_iovector._stat64 = (_MD_Stat64)stat;
+#else
+ _md_iovector._fstat64 = fstat64;
+ _md_iovector._stat64 = stat64;
+#endif
+ _md_iovector._lseek64 = lseek64;
+#elif defined(_PR_HAVE_LARGE_OFF_T)
+ _md_iovector._open64 = open;
+ _md_iovector._mmap64 = mmap;
+ _md_iovector._fstat64 = fstat;
+ _md_iovector._stat64 = stat;
+ _md_iovector._lseek64 = lseek;
+#else
+#error "I don't know yet"
+#endif
+ LL_I2L(minus_one, -1);
+} /* _PR_InitIOV */
+
+void _PR_UnixInit(void)
+{
+ struct sigaction sigact;
+ int rv;
+
+ sigemptyset(&timer_set);
+
+#if !defined(_PR_PTHREADS)
+
+ sigaddset(&timer_set, SIGALRM);
+ sigemptyset(&empty_set);
+ intr_timeout_ticks =
+ PR_SecondsToInterval(_PR_INTERRUPT_CHECK_INTERVAL_SECS);
+
+#if defined(SOLARIS)
+
+ if (getenv("NSPR_SIGSEGV_HANDLE")) {
+ sigact.sa_handler = sigsegvhandler;
+ sigact.sa_flags = 0;
+ sigact.sa_mask = timer_set;
+ sigaction(SIGSEGV, &sigact, 0);
+ }
+
+ if (getenv("NSPR_SIGABRT_HANDLE")) {
+ sigact.sa_handler = sigaborthandler;
+ sigact.sa_flags = 0;
+ sigact.sa_mask = timer_set;
+ sigaction(SIGABRT, &sigact, 0);
+ }
+
+ if (getenv("NSPR_SIGBUS_HANDLE")) {
+ sigact.sa_handler = sigbushandler;
+ sigact.sa_flags = 0;
+ sigact.sa_mask = timer_set;
+ sigaction(SIGBUS, &sigact, 0);
+ }
+
+#endif
+#endif /* !defined(_PR_PTHREADS) */
+
+ sigact.sa_handler = SIG_IGN;
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ rv = sigaction(SIGPIPE, &sigact, 0);
+ PR_ASSERT(0 == rv);
+
+ _pr_unix_rename_lock = PR_NewLock();
+ PR_ASSERT(NULL != _pr_unix_rename_lock);
+ _pr_Xfe_mon = PR_NewMonitor();
+ PR_ASSERT(NULL != _pr_Xfe_mon);
+
+ _PR_InitIOV(); /* one last hack */
+}
+
+void _PR_UnixCleanup(void)
+{
+ if (_pr_unix_rename_lock) {
+ PR_DestroyLock(_pr_unix_rename_lock);
+ _pr_unix_rename_lock = NULL;
+ }
+ if (_pr_Xfe_mon) {
+ PR_DestroyMonitor(_pr_Xfe_mon);
+ _pr_Xfe_mon = NULL;
+ }
+}
+
+#if !defined(_PR_PTHREADS)
+
+/*
+ * Variables used by the GC code, initialized in _MD_InitSegs().
+ */
+static PRInt32 _pr_zero_fd = -1;
+static PRLock *_pr_md_lock = NULL;
+
+/*
+ * _MD_InitSegs --
+ *
+ * This is Unix's version of _PR_MD_INIT_SEGS(), which is
+ * called by _PR_InitSegs(), which in turn is called by
+ * PR_Init().
+ */
+void _MD_InitSegs(void)
+{
+#ifdef DEBUG
+ /*
+ ** Disable using mmap(2) if NSPR_NO_MMAP is set
+ */
+ if (getenv("NSPR_NO_MMAP")) {
+ _pr_zero_fd = -2;
+ return;
+ }
+#endif
+ _pr_zero_fd = open("/dev/zero",O_RDWR, 0);
+ /* Prevent the fd from being inherited by child processes */
+ fcntl(_pr_zero_fd, F_SETFD, FD_CLOEXEC);
+ _pr_md_lock = PR_NewLock();
+}
+
+PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size, void *vaddr)
+{
+ static char *lastaddr = (char*) _PR_STACK_VMBASE;
+ PRStatus retval = PR_SUCCESS;
+ int prot;
+ void *rv;
+
+ PR_ASSERT(seg != 0);
+ PR_ASSERT(size != 0);
+
+ PR_Lock(_pr_md_lock);
+ if (_pr_zero_fd < 0) {
+from_heap:
+ seg->vaddr = PR_MALLOC(size);
+ if (!seg->vaddr) {
+ retval = PR_FAILURE;
+ }
+ else {
+ seg->size = size;
+ }
+ goto exit;
+ }
+
+ prot = PROT_READ|PROT_WRITE;
+ /*
+ * On Alpha Linux, the user-level thread stack needs
+ * to be made executable because longjmp/signal seem
+ * to put machine instructions on the stack.
+ */
+#if defined(LINUX) && defined(__alpha)
+ prot |= PROT_EXEC;
+#endif
+ rv = mmap((vaddr != 0) ? vaddr : lastaddr, size, prot,
+ _MD_MMAP_FLAGS,
+ _pr_zero_fd, 0);
+ if (rv == (void*)-1) {
+ goto from_heap;
+ }
+ lastaddr += size;
+ seg->vaddr = rv;
+ seg->size = size;
+ seg->flags = _PR_SEG_VM;
+
+exit:
+ PR_Unlock(_pr_md_lock);
+ return retval;
+}
+
+void _MD_FreeSegment(PRSegment *seg)
+{
+ if (seg->flags & _PR_SEG_VM) {
+ (void) munmap(seg->vaddr, seg->size);
+ }
+ else {
+ PR_DELETE(seg->vaddr);
+ }
+}
+
+#endif /* _PR_PTHREADS */
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * PR_Now --
+ *
+ * Returns the current time in microseconds since the epoch.
+ * The epoch is midnight January 1, 1970 GMT.
+ * The implementation is machine dependent. This is the Unix
+ * implementation.
+ * Cf. time_t time(time_t *tp)
+ *
+ *-----------------------------------------------------------------------
+ */
+
+PR_IMPLEMENT(PRTime)
+PR_Now(void)
+{
+ struct timeval tv;
+ PRInt64 s, us, s2us;
+
+ GETTIMEOFDAY(&tv);
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, tv.tv_sec);
+ LL_I2L(us, tv.tv_usec);
+ LL_MUL(s, s, s2us);
+ LL_ADD(s, s, us);
+ return s;
+}
+
+#if defined(_MD_INTERVAL_USE_GTOD)
+/*
+ * This version of interval times is based on the time of day
+ * capability offered by the system. This isn't valid for two reasons:
+ * 1) The time of day is neither linear nor montonically increasing
+ * 2) The units here are milliseconds. That's not appropriate for our use.
+ */
+PRIntervalTime _PR_UNIX_GetInterval()
+{
+ struct timeval time;
+ PRIntervalTime ticks;
+
+ (void)GETTIMEOFDAY(&time); /* fallicy of course */
+ ticks = (PRUint32)time.tv_sec * PR_MSEC_PER_SEC; /* that's in milliseconds */
+ ticks += (PRUint32)time.tv_usec / PR_USEC_PER_MSEC; /* so's that */
+ return ticks;
+} /* _PR_UNIX_GetInterval */
+
+PRIntervalTime _PR_UNIX_TicksPerSecond()
+{
+ return 1000; /* this needs some work :) */
+}
+#endif
+
+#if defined(_PR_HAVE_CLOCK_MONOTONIC)
+PRIntervalTime _PR_UNIX_GetInterval2()
+{
+ struct timespec time;
+ PRIntervalTime ticks;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &time) != 0) {
+ fprintf(stderr, "clock_gettime failed: %d\n", errno);
+ abort();
+ }
+
+ ticks = (PRUint32)time.tv_sec * PR_MSEC_PER_SEC;
+ ticks += (PRUint32)time.tv_nsec / PR_NSEC_PER_MSEC;
+ return ticks;
+}
+
+PRIntervalTime _PR_UNIX_TicksPerSecond2()
+{
+ return 1000;
+}
+#endif
+
+#if !defined(_PR_PTHREADS)
+/*
+ * Wait for I/O on multiple descriptors.
+ *
+ * Return 0 if timed out, return -1 if interrupted,
+ * else return the number of ready descriptors.
+ */
+PRInt32 _PR_WaitForMultipleFDs(
+ _PRUnixPollDesc *unixpds,
+ PRInt32 pdcnt,
+ PRIntervalTime timeout)
+{
+ PRPollQueue pq;
+ PRIntn is;
+ PRInt32 rv;
+ _PRCPU *io_cpu;
+ _PRUnixPollDesc *unixpd, *eunixpd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+
+ pq.pds = unixpds;
+ pq.npds = pdcnt;
+
+ _PR_INTSOFF(is);
+ _PR_MD_IOQ_LOCK();
+ _PR_THREAD_LOCK(me);
+
+ pq.thr = me;
+ io_cpu = me->cpu;
+ pq.on_ioq = PR_TRUE;
+ pq.timeout = timeout;
+ _PR_ADD_TO_IOQ(pq, me->cpu);
+
+#if !defined(_PR_USE_POLL)
+ eunixpd = unixpds + pdcnt;
+ for (unixpd = unixpds; unixpd < eunixpd; unixpd++) {
+ PRInt32 osfd = unixpd->osfd;
+ if (unixpd->in_flags & _PR_UNIX_POLL_READ) {
+ FD_SET(osfd, &_PR_FD_READ_SET(me->cpu));
+ _PR_FD_READ_CNT(me->cpu)[osfd]++;
+ }
+ if (unixpd->in_flags & _PR_UNIX_POLL_WRITE) {
+ FD_SET(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ (_PR_FD_WRITE_CNT(me->cpu))[osfd]++;
+ }
+ if (unixpd->in_flags & _PR_UNIX_POLL_EXCEPT) {
+ FD_SET(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ (_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]++;
+ }
+ if (osfd > _PR_IOQ_MAX_OSFD(me->cpu)) {
+ _PR_IOQ_MAX_OSFD(me->cpu) = osfd;
+ }
+ }
+#endif /* !defined(_PR_USE_POLL) */
+
+ if (_PR_IOQ_TIMEOUT(me->cpu) > timeout) {
+ _PR_IOQ_TIMEOUT(me->cpu) = timeout;
+ }
+
+ _PR_IOQ_OSFD_CNT(me->cpu) += pdcnt;
+
+ _PR_SLEEPQ_LOCK(me->cpu);
+ _PR_ADD_SLEEPQ(me, timeout);
+ me->state = _PR_IO_WAIT;
+ me->io_pending = PR_TRUE;
+ me->io_suspended = PR_FALSE;
+ _PR_SLEEPQ_UNLOCK(me->cpu);
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_IOQ_UNLOCK();
+
+ _PR_MD_WAIT(me, timeout);
+
+ me->io_pending = PR_FALSE;
+ me->io_suspended = PR_FALSE;
+
+ /*
+ * This thread should run on the same cpu on which it was blocked; when
+ * the IO request times out the fd sets and fd counts for the
+ * cpu are updated below.
+ */
+ PR_ASSERT(me->cpu == io_cpu);
+
+ /*
+ ** If we timed out the pollq might still be on the ioq. Remove it
+ ** before continuing.
+ */
+ if (pq.on_ioq) {
+ _PR_MD_IOQ_LOCK();
+ /*
+ * Need to check pq.on_ioq again
+ */
+ if (pq.on_ioq) {
+ PR_REMOVE_LINK(&pq.links);
+#ifndef _PR_USE_POLL
+ eunixpd = unixpds + pdcnt;
+ for (unixpd = unixpds; unixpd < eunixpd; unixpd++) {
+ PRInt32 osfd = unixpd->osfd;
+ PRInt16 in_flags = unixpd->in_flags;
+
+ if (in_flags & _PR_UNIX_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0) {
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ }
+ if (in_flags & _PR_UNIX_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0) {
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ }
+ if (in_flags & _PR_UNIX_POLL_EXCEPT) {
+ if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0) {
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+ }
+#endif /* _PR_USE_POLL */
+ PR_ASSERT(pq.npds == pdcnt);
+ _PR_IOQ_OSFD_CNT(me->cpu) -= pdcnt;
+ PR_ASSERT(_PR_IOQ_OSFD_CNT(me->cpu) >= 0);
+ }
+ _PR_MD_IOQ_UNLOCK();
+ }
+ /* XXX Should we use _PR_FAST_INTSON or _PR_INTSON? */
+ if (1 == pdcnt) {
+ _PR_FAST_INTSON(is);
+ } else {
+ _PR_INTSON(is);
+ }
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+
+ rv = 0;
+ if (pq.on_ioq == PR_FALSE) {
+ /* Count the number of ready descriptors */
+ while (--pdcnt >= 0) {
+ if (unixpds->out_flags != 0) {
+ rv++;
+ }
+ unixpds++;
+ }
+ }
+
+ return rv;
+}
+
+/*
+ * Unblock threads waiting for I/O
+ * used when interrupting threads
+ *
+ * NOTE: The thread lock should held when this function is called.
+ * On return, the thread lock is released.
+ */
+void _PR_Unblock_IO_Wait(PRThread *thr)
+{
+ int pri = thr->priority;
+ _PRCPU *cpu = thr->cpu;
+
+ /*
+ * GLOBAL threads wakeup periodically to check for interrupt
+ */
+ if (_PR_IS_NATIVE_THREAD(thr)) {
+ _PR_THREAD_UNLOCK(thr);
+ return;
+ }
+
+ PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
+ _PR_SLEEPQ_LOCK(cpu);
+ _PR_DEL_SLEEPQ(thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(cpu);
+
+ PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
+ thr->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ _PR_THREAD_UNLOCK(thr);
+ _PR_MD_WAKEUP_WAITER(thr);
+}
+#endif /* !defined(_PR_PTHREADS) */
+
+/*
+ * When a nonblocking connect has completed, determine whether it
+ * succeeded or failed, and if it failed, what the error code is.
+ *
+ * The function returns the error code. An error code of 0 means
+ * that the nonblocking connect succeeded.
+ */
+
+int _MD_unix_get_nonblocking_connect_error(int osfd)
+{
+#if defined(NTO)
+ /* Neutrino does not support the SO_ERROR socket option */
+ PRInt32 rv;
+ PRNetAddr addr;
+ _PRSockLen_t addrlen = sizeof(addr);
+
+ /* Test to see if we are using the Tiny TCP/IP Stack or the Full one. */
+ struct statvfs superblock;
+ rv = fstatvfs(osfd, &superblock);
+ if (rv == 0) {
+ if (strcmp(superblock.f_basetype, "ttcpip") == 0) {
+ /* Using the Tiny Stack! */
+ rv = getpeername(osfd, (struct sockaddr *) &addr,
+ (_PRSockLen_t *) &addrlen);
+ if (rv == -1) {
+ int errno_copy = errno; /* make a copy so I don't
+ * accidentally reset */
+
+ if (errno_copy == ENOTCONN) {
+ struct stat StatInfo;
+ rv = fstat(osfd, &StatInfo);
+ if (rv == 0) {
+ time_t current_time = time(NULL);
+
+ /*
+ * this is a real hack, can't explain why it
+ * works it just does
+ */
+ if (abs(current_time - StatInfo.st_atime) < 5) {
+ return ECONNREFUSED;
+ } else {
+ return ETIMEDOUT;
+ }
+ } else {
+ return ECONNREFUSED;
+ }
+ } else {
+ return errno_copy;
+ }
+ } else {
+ /* No Error */
+ return 0;
+ }
+ } else {
+ /* Have the FULL Stack which supports SO_ERROR */
+ /* Hasn't been written yet, never been tested! */
+ /* Jerry.Kirk@Nexwarecorp.com */
+
+ int err;
+ _PRSockLen_t optlen = sizeof(err);
+
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
+ (char *) &err, &optlen) == -1) {
+ return errno;
+ } else {
+ return err;
+ }
+ }
+ } else {
+ return ECONNREFUSED;
+ }
+#elif defined(UNIXWARE)
+ /*
+ * getsockopt() fails with EPIPE, so use getmsg() instead.
+ */
+
+ int rv;
+ int flags = 0;
+ rv = getmsg(osfd, NULL, NULL, &flags);
+ PR_ASSERT(-1 == rv || 0 == rv);
+ if (-1 == rv && errno != EAGAIN && errno != EWOULDBLOCK) {
+ return errno;
+ }
+ return 0; /* no error */
+#else
+ int err;
+ _PRSockLen_t optlen = sizeof(err);
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char*)&err, &optlen) == -1) {
+ return errno;
+ }
+ return err;
+
+#endif
+}
+
+/************************************************************************/
+
+/*
+** Special hacks for xlib. Xlib/Xt/Xm is not re-entrant nor is it thread
+** safe. Unfortunately, neither is mozilla. To make these programs work
+** in a pre-emptive threaded environment, we need to use a lock.
+*/
+
+void _PR_XLock(void)
+{
+ PR_EnterMonitor(_pr_Xfe_mon);
+}
+
+void _PR_XUnlock(void)
+{
+ PR_ExitMonitor(_pr_Xfe_mon);
+}
+
+PRBool _PR_XIsLocked(void)
+{
+ return (PR_InMonitor(_pr_Xfe_mon)) ? PR_TRUE : PR_FALSE;
+}
+
+#if defined(HAVE_FCNTL_FILE_LOCKING)
+
+PRStatus
+_MD_LockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ struct flock arg;
+
+ arg.l_type = F_WRLCK;
+ arg.l_whence = SEEK_SET;
+ arg.l_start = 0;
+ arg.l_len = 0; /* until EOF */
+ rv = fcntl(f, F_SETLKW, &arg);
+ if (rv == 0) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+
+PRStatus
+_MD_TLockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ struct flock arg;
+
+ arg.l_type = F_WRLCK;
+ arg.l_whence = SEEK_SET;
+ arg.l_start = 0;
+ arg.l_len = 0; /* until EOF */
+ rv = fcntl(f, F_SETLK, &arg);
+ if (rv == 0) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+
+PRStatus
+_MD_UnlockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ struct flock arg;
+
+ arg.l_type = F_UNLCK;
+ arg.l_whence = SEEK_SET;
+ arg.l_start = 0;
+ arg.l_len = 0; /* until EOF */
+ rv = fcntl(f, F_SETLK, &arg);
+ if (rv == 0) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+
+#elif defined(HAVE_BSD_FLOCK)
+
+#include <sys/file.h>
+
+PRStatus
+_MD_LockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ rv = flock(f, LOCK_EX);
+ if (rv == 0) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+
+PRStatus
+_MD_TLockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ rv = flock(f, LOCK_EX|LOCK_NB);
+ if (rv == 0) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+
+PRStatus
+_MD_UnlockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ rv = flock(f, LOCK_UN);
+ if (rv == 0) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+#else
+
+PRStatus
+_MD_LockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ rv = lockf(f, F_LOCK, 0);
+ if (rv == 0) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+
+PRStatus
+_MD_TLockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ rv = lockf(f, F_TLOCK, 0);
+ if (rv == 0) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+
+PRStatus
+_MD_UnlockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ rv = lockf(f, F_ULOCK, 0);
+ if (rv == 0) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+#endif
+
+PRStatus _MD_gethostname(char *name, PRUint32 namelen)
+{
+ PRIntn rv;
+
+ rv = gethostname(name, namelen);
+ if (0 == rv) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_GETHOSTNAME_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+
+PRStatus _MD_getsysinfo(PRSysInfo cmd, char *name, PRUint32 namelen)
+{
+ struct utsname info;
+
+ PR_ASSERT((cmd == PR_SI_SYSNAME) || (cmd == PR_SI_RELEASE) ||
+ (cmd == PR_SI_RELEASE_BUILD));
+
+ if (uname(&info) == -1) {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return PR_FAILURE;
+ }
+ if (PR_SI_SYSNAME == cmd) {
+ (void)PR_snprintf(name, namelen, info.sysname);
+ }
+ else if (PR_SI_RELEASE == cmd) {
+ (void)PR_snprintf(name, namelen, info.release);
+ }
+ else if (PR_SI_RELEASE_BUILD == cmd) {
+ (void)PR_snprintf(name, namelen, info.version);
+ }
+ else {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+/*
+ *******************************************************************
+ *
+ * Memory-mapped files
+ *
+ *******************************************************************
+ */
+
+PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
+{
+ PRFileInfo info;
+ PRUint32 sz;
+
+ LL_L2UI(sz, size);
+ if (sz) {
+ if (PR_GetOpenFileInfo(fmap->fd, &info) == PR_FAILURE) {
+ return PR_FAILURE;
+ }
+ if (sz > info.size) {
+ /*
+ * Need to extend the file
+ */
+ if (fmap->prot != PR_PROT_READWRITE) {
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (PR_Seek(fmap->fd, sz - 1, PR_SEEK_SET) == -1) {
+ return PR_FAILURE;
+ }
+ if (PR_Write(fmap->fd, "", 1) != 1) {
+ return PR_FAILURE;
+ }
+ }
+ }
+ if (fmap->prot == PR_PROT_READONLY) {
+ fmap->md.prot = PROT_READ;
+#if defined(DARWIN) || defined(ANDROID)
+ /*
+ * This is needed on OS X because its implementation of
+ * POSIX shared memory returns an error for MAP_PRIVATE, even
+ * when the mapping is read-only.
+ *
+ * And this is needed on Android, because mapping ashmem with
+ * MAP_PRIVATE creates a mapping of zeroed memory instead of
+ * the shm contents.
+ */
+ fmap->md.flags = MAP_SHARED;
+#else
+ fmap->md.flags = MAP_PRIVATE;
+#endif
+ } else if (fmap->prot == PR_PROT_READWRITE) {
+ fmap->md.prot = PROT_READ | PROT_WRITE;
+ fmap->md.flags = MAP_SHARED;
+ } else {
+ PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY);
+ fmap->md.prot = PROT_READ | PROT_WRITE;
+ fmap->md.flags = MAP_PRIVATE;
+ }
+ return PR_SUCCESS;
+}
+
+void * _MD_MemMap(
+ PRFileMap *fmap,
+ PRInt64 offset,
+ PRUint32 len)
+{
+ PRInt32 off;
+ void *addr;
+
+ LL_L2I(off, offset);
+ if ((addr = mmap(0, len, fmap->md.prot, fmap->md.flags,
+ fmap->fd->secret->md.osfd, off)) == (void *) -1) {
+ _PR_MD_MAP_MMAP_ERROR(_MD_ERRNO());
+ addr = NULL;
+ }
+ return addr;
+}
+
+PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
+{
+ if (munmap(addr, len) == 0) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return PR_FAILURE;
+}
+
+PRStatus _MD_CloseFileMap(PRFileMap *fmap)
+{
+ if ( PR_TRUE == fmap->md.isAnonFM ) {
+ PRStatus rc = PR_Close( fmap->fd );
+ if ( PR_FAILURE == rc ) {
+ PR_LOG( _pr_io_lm, PR_LOG_DEBUG,
+ ("_MD_CloseFileMap(): error closing anonymnous file map osfd"));
+ return PR_FAILURE;
+ }
+ }
+ PR_DELETE(fmap);
+ return PR_SUCCESS;
+}
+
+PRStatus _MD_SyncMemMap(
+ PRFileDesc *fd,
+ void *addr,
+ PRUint32 len)
+{
+ /* msync(..., MS_SYNC) alone is sufficient to flush modified data to disk
+ * synchronously. It is not necessary to call fsync. */
+ if (msync(addr, len, MS_SYNC) == 0) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return PR_FAILURE;
+}
+
+#if defined(_PR_NEED_FAKE_POLL)
+
+/*
+ * Some platforms don't have poll(). For easier porting of code
+ * that calls poll(), we emulate poll() using select().
+ */
+
+int poll(struct pollfd *filedes, unsigned long nfds, int timeout)
+{
+ int i;
+ int rv;
+ int maxfd;
+ fd_set rd, wr, ex;
+ struct timeval tv, *tvp;
+
+ if (timeout < 0 && timeout != -1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (timeout == -1) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ tvp = &tv;
+ }
+
+ maxfd = -1;
+ FD_ZERO(&rd);
+ FD_ZERO(&wr);
+ FD_ZERO(&ex);
+
+ for (i = 0; i < nfds; i++) {
+ int osfd = filedes[i].fd;
+ int events = filedes[i].events;
+ PRBool fdHasEvent = PR_FALSE;
+
+ PR_ASSERT(osfd < FD_SETSIZE);
+ if (osfd < 0 || osfd >= FD_SETSIZE) {
+ continue; /* Skip this osfd. */
+ }
+
+ /*
+ * Map the poll events to the select fd_sets.
+ * POLLIN, POLLRDNORM ===> readable
+ * POLLOUT, POLLWRNORM ===> writable
+ * POLLPRI, POLLRDBAND ===> exception
+ * POLLNORM, POLLWRBAND (and POLLMSG on some platforms)
+ * are ignored.
+ *
+ * The output events POLLERR and POLLHUP are never turned on.
+ * POLLNVAL may be turned on.
+ */
+
+ if (events & (POLLIN | POLLRDNORM)) {
+ FD_SET(osfd, &rd);
+ fdHasEvent = PR_TRUE;
+ }
+ if (events & (POLLOUT | POLLWRNORM)) {
+ FD_SET(osfd, &wr);
+ fdHasEvent = PR_TRUE;
+ }
+ if (events & (POLLPRI | POLLRDBAND)) {
+ FD_SET(osfd, &ex);
+ fdHasEvent = PR_TRUE;
+ }
+ if (fdHasEvent && osfd > maxfd) {
+ maxfd = osfd;
+ }
+ }
+
+ rv = select(maxfd + 1, &rd, &wr, &ex, tvp);
+
+ /* Compute poll results */
+ if (rv > 0) {
+ rv = 0;
+ for (i = 0; i < nfds; i++) {
+ PRBool fdHasEvent = PR_FALSE;
+
+ filedes[i].revents = 0;
+ if (filedes[i].fd < 0) {
+ continue;
+ }
+ if (filedes[i].fd >= FD_SETSIZE) {
+ filedes[i].revents |= POLLNVAL;
+ continue;
+ }
+ if (FD_ISSET(filedes[i].fd, &rd)) {
+ if (filedes[i].events & POLLIN) {
+ filedes[i].revents |= POLLIN;
+ }
+ if (filedes[i].events & POLLRDNORM) {
+ filedes[i].revents |= POLLRDNORM;
+ }
+ fdHasEvent = PR_TRUE;
+ }
+ if (FD_ISSET(filedes[i].fd, &wr)) {
+ if (filedes[i].events & POLLOUT) {
+ filedes[i].revents |= POLLOUT;
+ }
+ if (filedes[i].events & POLLWRNORM) {
+ filedes[i].revents |= POLLWRNORM;
+ }
+ fdHasEvent = PR_TRUE;
+ }
+ if (FD_ISSET(filedes[i].fd, &ex)) {
+ if (filedes[i].events & POLLPRI) {
+ filedes[i].revents |= POLLPRI;
+ }
+ if (filedes[i].events & POLLRDBAND) {
+ filedes[i].revents |= POLLRDBAND;
+ }
+ fdHasEvent = PR_TRUE;
+ }
+ if (fdHasEvent) {
+ rv++;
+ }
+ }
+ PR_ASSERT(rv > 0);
+ } else if (rv == -1 && errno == EBADF) {
+ rv = 0;
+ for (i = 0; i < nfds; i++) {
+ filedes[i].revents = 0;
+ if (filedes[i].fd < 0) {
+ continue;
+ }
+ if (fcntl(filedes[i].fd, F_GETFL, 0) == -1) {
+ filedes[i].revents = POLLNVAL;
+ rv++;
+ }
+ }
+ PR_ASSERT(rv > 0);
+ }
+ PR_ASSERT(-1 != timeout || rv != 0);
+
+ return rv;
+}
+#endif /* _PR_NEED_FAKE_POLL */
diff --git a/nsprpub/pr/src/md/unix/unix_errors.c b/nsprpub/pr/src/md/unix/unix_errors.c
new file mode 100644
index 0000000000..1d87659699
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/unix_errors.c
@@ -0,0 +1,833 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#if defined(_PR_POLL_AVAILABLE)
+#include <poll.h>
+#endif
+#include <errno.h>
+
+void _MD_unix_map_default_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err ) {
+ case EACCES:
+ prError = PR_NO_ACCESS_RIGHTS_ERROR;
+ break;
+ case EADDRINUSE:
+ prError = PR_ADDRESS_IN_USE_ERROR;
+ break;
+ case EADDRNOTAVAIL:
+ prError = PR_ADDRESS_NOT_AVAILABLE_ERROR;
+ break;
+ case EAFNOSUPPORT:
+ prError = PR_ADDRESS_NOT_SUPPORTED_ERROR;
+ break;
+ case EAGAIN:
+ prError = PR_WOULD_BLOCK_ERROR;
+ break;
+ /*
+ * On QNX and Neutrino, EALREADY is defined as EBUSY.
+ */
+#if EALREADY != EBUSY
+ case EALREADY:
+ prError = PR_ALREADY_INITIATED_ERROR;
+ break;
+#endif
+ case EBADF:
+ prError = PR_BAD_DESCRIPTOR_ERROR;
+ break;
+#ifdef EBADMSG
+ case EBADMSG:
+ prError = PR_IO_ERROR;
+ break;
+#endif
+ case EBUSY:
+ prError = PR_FILESYSTEM_MOUNTED_ERROR;
+ break;
+ case ECONNABORTED:
+ prError = PR_CONNECT_ABORTED_ERROR;
+ break;
+ case ECONNREFUSED:
+ prError = PR_CONNECT_REFUSED_ERROR;
+ break;
+ case ECONNRESET:
+ prError = PR_CONNECT_RESET_ERROR;
+ break;
+ case EDEADLK:
+ prError = PR_DEADLOCK_ERROR;
+ break;
+#ifdef EDIRCORRUPTED
+ case EDIRCORRUPTED:
+ prError = PR_DIRECTORY_CORRUPTED_ERROR;
+ break;
+#endif
+#ifdef EDQUOT
+ case EDQUOT:
+ prError = PR_NO_DEVICE_SPACE_ERROR;
+ break;
+#endif
+ case EEXIST:
+ prError = PR_FILE_EXISTS_ERROR;
+ break;
+ case EFAULT:
+ prError = PR_ACCESS_FAULT_ERROR;
+ break;
+ case EFBIG:
+ prError = PR_FILE_TOO_BIG_ERROR;
+ break;
+ case EHOSTUNREACH:
+ case EHOSTDOWN:
+ prError = PR_HOST_UNREACHABLE_ERROR;
+ break;
+ case EINPROGRESS:
+ prError = PR_IN_PROGRESS_ERROR;
+ break;
+ case EINTR:
+ prError = PR_PENDING_INTERRUPT_ERROR;
+ break;
+ case EINVAL:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case EIO:
+ prError = PR_IO_ERROR;
+ break;
+ case EISCONN:
+ prError = PR_IS_CONNECTED_ERROR;
+ break;
+ case EISDIR:
+ prError = PR_IS_DIRECTORY_ERROR;
+ break;
+ case ELOOP:
+ prError = PR_LOOP_ERROR;
+ break;
+ case EMFILE:
+ prError = PR_PROC_DESC_TABLE_FULL_ERROR;
+ break;
+ case EMLINK:
+ prError = PR_MAX_DIRECTORY_ENTRIES_ERROR;
+ break;
+ case EMSGSIZE:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+#ifdef EMULTIHOP
+ case EMULTIHOP:
+ prError = PR_REMOTE_FILE_ERROR;
+ break;
+#endif
+ case ENAMETOOLONG:
+ prError = PR_NAME_TOO_LONG_ERROR;
+ break;
+ case ENETUNREACH:
+ prError = PR_NETWORK_UNREACHABLE_ERROR;
+ break;
+ case ENFILE:
+ prError = PR_SYS_DESC_TABLE_FULL_ERROR;
+ break;
+ /*
+ * On SCO OpenServer 5, ENOBUFS is defined as ENOSR.
+ */
+#if defined(ENOBUFS) && (ENOBUFS != ENOSR)
+ case ENOBUFS:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+#endif
+ case ENODEV:
+ prError = PR_FILE_NOT_FOUND_ERROR;
+ break;
+ case ENOENT:
+ prError = PR_FILE_NOT_FOUND_ERROR;
+ break;
+ case ENOLCK:
+ prError = PR_FILE_IS_LOCKED_ERROR;
+ break;
+#ifdef ENOLINK
+ case ENOLINK:
+ prError = PR_REMOTE_FILE_ERROR;
+ break;
+#endif
+ case ENOMEM:
+ prError = PR_OUT_OF_MEMORY_ERROR;
+ break;
+ case ENOPROTOOPT:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case ENOSPC:
+ prError = PR_NO_DEVICE_SPACE_ERROR;
+ break;
+#ifdef ENOSR
+ case ENOSR:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+#endif
+ case ENOSYS:
+ prError = PR_NOT_IMPLEMENTED_ERROR;
+ break;
+ case ENOTCONN:
+ prError = PR_NOT_CONNECTED_ERROR;
+ break;
+ case ENOTDIR:
+ prError = PR_NOT_DIRECTORY_ERROR;
+ break;
+ case ENOTSOCK:
+ prError = PR_NOT_SOCKET_ERROR;
+ break;
+ case ENXIO:
+ prError = PR_FILE_NOT_FOUND_ERROR;
+ break;
+ case EOPNOTSUPP:
+ prError = PR_NOT_TCP_SOCKET_ERROR;
+ break;
+#ifdef EOVERFLOW
+ case EOVERFLOW:
+ prError = PR_BUFFER_OVERFLOW_ERROR;
+ break;
+#endif
+ case EPERM:
+ prError = PR_NO_ACCESS_RIGHTS_ERROR;
+ break;
+ case EPIPE:
+ prError = PR_CONNECT_RESET_ERROR;
+ break;
+#ifdef EPROTO
+ case EPROTO:
+ prError = PR_IO_ERROR;
+ break;
+#endif
+ case EPROTONOSUPPORT:
+ prError = PR_PROTOCOL_NOT_SUPPORTED_ERROR;
+ break;
+ case EPROTOTYPE:
+ prError = PR_ADDRESS_NOT_SUPPORTED_ERROR;
+ break;
+ case ERANGE:
+ prError = PR_INVALID_METHOD_ERROR;
+ break;
+ case EROFS:
+ prError = PR_READ_ONLY_FILESYSTEM_ERROR;
+ break;
+ case ESPIPE:
+ prError = PR_INVALID_METHOD_ERROR;
+ break;
+ case ETIMEDOUT:
+ prError = PR_IO_TIMEOUT_ERROR;
+ break;
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+ prError = PR_WOULD_BLOCK_ERROR;
+ break;
+#endif
+ case EXDEV:
+ prError = PR_NOT_SAME_DEVICE_ERROR;
+ break;
+ default:
+ prError = PR_UNKNOWN_ERROR;
+ break;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_opendir_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+
+void _MD_unix_map_closedir_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EINVAL:
+ prError = PR_BAD_DESCRIPTOR_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_readdir_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case 0:
+ case ENOENT:
+ prError = PR_NO_MORE_FILES_ERROR;
+ break;
+#ifdef EOVERFLOW
+ case EOVERFLOW:
+ prError = PR_IO_ERROR;
+ break;
+#endif
+ case EINVAL:
+ prError = PR_IO_ERROR;
+ break;
+ case ENXIO:
+ prError = PR_IO_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_unlink_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EPERM:
+ prError = PR_IS_DIRECTORY_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_stat_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case ETIMEDOUT:
+ prError = PR_REMOTE_FILE_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_fstat_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case ETIMEDOUT:
+ prError = PR_REMOTE_FILE_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_rename_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EEXIST:
+ prError = PR_DIRECTORY_NOT_EMPTY_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_access_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case ETIMEDOUT:
+ prError = PR_REMOTE_FILE_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_mkdir_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+
+void _MD_unix_map_rmdir_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ /*
+ * On AIX 4.3, ENOTEMPTY is defined as EEXIST.
+ */
+#if ENOTEMPTY != EEXIST
+ case ENOTEMPTY:
+ prError = PR_DIRECTORY_NOT_EMPTY_ERROR;
+ break;
+#endif
+ case EEXIST:
+ prError = PR_DIRECTORY_NOT_EMPTY_ERROR;
+ break;
+ case EINVAL:
+ prError = PR_DIRECTORY_NOT_EMPTY_ERROR;
+ break;
+ case ETIMEDOUT:
+ prError = PR_REMOTE_FILE_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_read_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EINVAL:
+ prError = PR_INVALID_METHOD_ERROR;
+ break;
+ case ENXIO:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_write_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EINVAL:
+ prError = PR_INVALID_METHOD_ERROR;
+ break;
+ case ENXIO:
+ prError = PR_INVALID_METHOD_ERROR;
+ break;
+ case ETIMEDOUT:
+ prError = PR_REMOTE_FILE_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_lseek_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+
+void _MD_unix_map_fsync_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case ETIMEDOUT:
+ prError = PR_REMOTE_FILE_ERROR;
+ break;
+ case EINVAL:
+ prError = PR_INVALID_METHOD_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_close_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case ETIMEDOUT:
+ prError = PR_REMOTE_FILE_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_socket_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case ENOMEM:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_socketavailable_error(int err)
+{
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+}
+
+void _MD_unix_map_recv_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+
+void _MD_unix_map_recvfrom_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+
+void _MD_unix_map_send_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+
+void _MD_unix_map_sendto_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+
+void _MD_unix_map_writev_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+
+void _MD_unix_map_accept_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case ENODEV:
+ prError = PR_NOT_TCP_SOCKET_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_connect_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+#if defined(UNIXWARE)
+ /*
+ * On some platforms, if we connect to a port on the local host
+ * (the loopback address) that no process is listening on, we get
+ * EIO instead of ECONNREFUSED.
+ */
+ case EIO:
+ prError = PR_CONNECT_REFUSED_ERROR;
+ break;
+#endif
+ case ENXIO:
+ prError = PR_IO_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_bind_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EINVAL:
+ prError = PR_SOCKET_ADDRESS_IS_BOUND_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_listen_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+
+void _MD_unix_map_shutdown_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+
+void _MD_unix_map_socketpair_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case ENOMEM:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_getsockname_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case ENOMEM:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_getpeername_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case ENOMEM:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_getsockopt_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EINVAL:
+ prError = PR_BUFFER_OVERFLOW_ERROR;
+ break;
+ case ENOMEM:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_setsockopt_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EINVAL:
+ prError = PR_BUFFER_OVERFLOW_ERROR;
+ break;
+ case ENOMEM:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_open_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EAGAIN:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ case EBUSY:
+ prError = PR_IO_ERROR;
+ break;
+ case ENODEV:
+ prError = PR_FILE_NOT_FOUND_ERROR;
+ break;
+ case ENOMEM:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+#ifdef EOVERFLOW
+ case EOVERFLOW:
+ prError = PR_FILE_TOO_BIG_ERROR;
+ break;
+#endif
+ case ETIMEDOUT:
+ prError = PR_REMOTE_FILE_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_mmap_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EAGAIN:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ case EMFILE:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ case ENODEV:
+ prError = PR_OPERATION_NOT_SUPPORTED_ERROR;
+ break;
+ case ENXIO:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_gethostname_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+
+void _MD_unix_map_select_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+
+#if defined(_PR_POLL_AVAILABLE) || defined(_PR_NEED_FAKE_POLL)
+void _MD_unix_map_poll_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EAGAIN:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_poll_revents_error(int err)
+{
+ if (err & POLLNVAL) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, EBADF);
+ }
+ else if (err & POLLHUP) {
+ PR_SetError(PR_CONNECT_RESET_ERROR, EPIPE);
+ }
+ else if (err & POLLERR) {
+ PR_SetError(PR_IO_ERROR, EIO);
+ }
+ else {
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ }
+}
+#endif /* _PR_POLL_AVAILABLE || _PR_NEED_FAKE_POLL */
+
+
+void _MD_unix_map_flock_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EINVAL:
+ prError = PR_BAD_DESCRIPTOR_ERROR;
+ break;
+ case EWOULDBLOCK:
+ prError = PR_FILE_IS_LOCKED_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_unix_map_lockf_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EACCES:
+ prError = PR_FILE_IS_LOCKED_ERROR;
+ break;
+ case EDEADLK:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ default:
+ _MD_unix_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+#ifdef AIX
+void _MD_aix_map_sendfile_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+#endif /* AIX */
+
+#ifdef HPUX11
+void _MD_hpux_map_sendfile_error(int err)
+{
+ _MD_unix_map_default_error(err);
+}
+#endif /* HPUX11 */
+
+#ifdef SOLARIS
+void _MD_solaris_map_sendfile_error(int err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ /*
+ * Solaris defines a 0 return value for sendfile to mean end-of-file.
+ */
+ case 0:
+ prError = PR_END_OF_FILE_ERROR;
+ break;
+
+ default:
+ _MD_unix_map_default_error(err) ;
+ return;
+ }
+ PR_SetError(prError, err);
+}
+#endif /* SOLARIS */
+
+#ifdef LINUX
+void _MD_linux_map_sendfile_error(int err)
+{
+ _MD_unix_map_default_error(err) ;
+}
+#endif /* LINUX */
diff --git a/nsprpub/pr/src/md/unix/unixware.c b/nsprpub/pr/src/md/unix/unixware.c
new file mode 100644
index 0000000000..d6958558b2
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/unixware.c
@@ -0,0 +1,559 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#if !defined (USE_SVR4_THREADS)
+
+/*
+ * using only NSPR threads here
+ */
+
+#include <setjmp.h>
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+
+#ifdef ALARMS_BREAK_TCP /* I don't think they do */
+
+PRInt32 _MD_connect(PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+
+ _MD_BLOCK_CLOCK_INTERRUPTS();
+ rv = _connect(osfd,addr,addrlen);
+ _MD_UNBLOCK_CLOCK_INTERRUPTS();
+}
+
+PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+
+ _MD_BLOCK_CLOCK_INTERRUPTS();
+ rv = _accept(osfd,addr,addrlen);
+ _MD_UNBLOCK_CLOCK_INTERRUPTS();
+ return(rv);
+}
+#endif
+
+/*
+ * These are also implemented in pratom.c using NSPR locks. Any reason
+ * this might be better or worse? If you like this better, define
+ * _PR_HAVE_ATOMIC_OPS in include/md/unixware.h
+ */
+#ifdef _PR_HAVE_ATOMIC_OPS
+/* Atomic operations */
+#include <stdio.h>
+static FILE *_uw_semf;
+
+void
+_MD_INIT_ATOMIC(void)
+{
+ /* Sigh. Sure wish SYSV semaphores weren't such a pain to use */
+ if ((_uw_semf = tmpfile()) == NULL) {
+ PR_ASSERT(0);
+ }
+
+ return;
+}
+
+void
+_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ flockfile(_uw_semf);
+ (*val)++;
+ unflockfile(_uw_semf);
+}
+
+void
+_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
+{
+ flockfile(_uw_semf);
+ (*ptr) += val;
+ unflockfile(_uw_semf);
+}
+
+void
+_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ flockfile(_uw_semf);
+ (*val)--;
+ unflockfile(_uw_semf);
+}
+
+void
+_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ flockfile(_uw_semf);
+ *val = newval;
+ unflockfile(_uw_semf);
+}
+#endif
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for Unixware */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for Unixware.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Unixware.");
+}
+
+#else /* USE_SVR4_THREADS */
+
+/* NOTE:
+ * SPARC v9 (Ultras) do have an atomic test-and-set operation. But
+ * SPARC v8 doesn't. We should detect in the init if we are running on
+ * v8 or v9, and then use assembly where we can.
+ */
+
+#include <thread.h>
+#include <synch.h>
+
+static mutex_t _unixware_atomic = DEFAULTMUTEX;
+
+#define TEST_THEN_ADD(where, inc) \
+ if (mutex_lock(&_unixware_atomic) != 0)\
+ PR_ASSERT(0);\
+ *where += inc;\
+ if (mutex_unlock(&_unixware_atomic) != 0)\
+ PR_ASSERT(0);
+
+#define TEST_THEN_SET(where, val) \
+ if (mutex_lock(&_unixware_atomic) != 0)\
+ PR_ASSERT(0);\
+ *where = val;\
+ if (mutex_unlock(&_unixware_atomic) != 0)\
+ PR_ASSERT(0);
+
+void
+_MD_INIT_ATOMIC(void)
+{
+}
+
+void
+_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ TEST_THEN_ADD(val, 1);
+}
+
+void
+_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
+{
+ TEST_THEN_ADD(ptr, val);
+}
+
+void
+_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ TEST_THEN_ADD(val, 0xffffffff);
+}
+
+void
+_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ TEST_THEN_SET(val, newval);
+}
+
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/lwp.h>
+#include <sys/procfs.h>
+#include <sys/syscall.h>
+
+
+THREAD_KEY_T threadid_key;
+THREAD_KEY_T cpuid_key;
+THREAD_KEY_T last_thread_key;
+static sigset_t set, oldset;
+
+void _MD_EarlyInit(void)
+{
+ THR_KEYCREATE(&threadid_key, NULL);
+ THR_KEYCREATE(&cpuid_key, NULL);
+ THR_KEYCREATE(&last_thread_key, NULL);
+ sigemptyset(&set);
+ sigaddset(&set, SIGALRM);
+}
+
+PRStatus _MD_CREATE_THREAD(PRThread *thread,
+ void (*start)(void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ long flags;
+
+ /* mask out SIGALRM for native thread creation */
+ thr_sigsetmask(SIG_BLOCK, &set, &oldset);
+
+ flags = (state == PR_JOINABLE_THREAD ? THR_SUSPENDED/*|THR_NEW_LWP*/
+ : THR_SUSPENDED|THR_DETACHED/*|THR_NEW_LWP*/);
+ if (_PR_IS_GCABLE_THREAD(thread) ||
+ (scope == PR_GLOBAL_BOUND_THREAD)) {
+ flags |= THR_BOUND;
+ }
+
+ if (thr_create(NULL, thread->stack->stackSize,
+ (void *(*)(void *)) start, (void *) thread,
+ flags,
+ &thread->md.handle)) {
+ thr_sigsetmask(SIG_SETMASK, &oldset, NULL);
+ return PR_FAILURE;
+ }
+
+
+ /* When the thread starts running, then the lwpid is set to the right
+ * value. Until then we want to mark this as 'uninit' so that
+ * its register state is initialized properly for GC */
+
+ thread->md.lwpid = -1;
+ thr_sigsetmask(SIG_SETMASK, &oldset, NULL);
+ _MD_NEW_SEM(&thread->md.waiter_sem, 0);
+
+ if ((scope == PR_GLOBAL_THREAD) || (scope == PR_GLOBAL_BOUND_THREAD)) {
+ thread->flags |= _PR_GLOBAL_SCOPE;
+ }
+
+ /*
+ ** Set the thread priority. This will also place the thread on
+ ** the runQ.
+ **
+ ** Force PR_SetThreadPriority to set the priority by
+ ** setting thread->priority to 100.
+ */
+ {
+ int pri;
+ pri = thread->priority;
+ thread->priority = 100;
+ PR_SetThreadPriority( thread, pri );
+
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("(0X%x)[Start]: on to runq at priority %d",
+ thread, thread->priority));
+ }
+
+ /* Activate the thread */
+ if (thr_continue( thread->md.handle ) ) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+void _MD_cleanup_thread(PRThread *thread)
+{
+ thread_t hdl;
+ PRMonitor *mon;
+
+ hdl = thread->md.handle;
+
+ /*
+ ** First, suspend the thread (unless it's the active one)
+ ** Because we suspend it first, we don't have to use LOCK_SCHEDULER to
+ ** prevent both of us modifying the thread structure at the same time.
+ */
+ if ( thread != _PR_MD_CURRENT_THREAD() ) {
+ thr_suspend(hdl);
+ }
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("(0X%x)[DestroyThread]\n", thread));
+
+ _MD_DESTROY_SEM(&thread->md.waiter_sem);
+}
+
+void _MD_SET_PRIORITY(_MDThread *md_thread, PRUintn newPri)
+{
+ if(thr_setprio((thread_t)md_thread->handle, newPri)) {
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("_PR_SetThreadPriority: can't set thread priority\n"));
+ }
+}
+
+void _MD_WAIT_CV(
+ struct _MDCVar *md_cv, struct _MDLock *md_lock, PRIntervalTime timeout)
+{
+ struct timespec tt;
+ PRUint32 msec;
+ int rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ msec = PR_IntervalToMilliseconds(timeout);
+
+ GETTIME (&tt);
+
+ tt.tv_sec += msec / PR_MSEC_PER_SEC;
+ tt.tv_nsec += (msec % PR_MSEC_PER_SEC) * PR_NSEC_PER_MSEC;
+ /* Check for nsec overflow - otherwise we'll get an EINVAL */
+ if (tt.tv_nsec >= PR_NSEC_PER_SEC) {
+ tt.tv_sec++;
+ tt.tv_nsec -= PR_NSEC_PER_SEC;
+ }
+ me->md.sp = unixware_getsp();
+
+
+ /* XXX Solaris 2.5.x gives back EINTR occasionally for no reason
+ * hence ignore EINTR for now */
+
+ COND_TIMEDWAIT(&md_cv->cv, &md_lock->lock, &tt);
+}
+
+void _MD_lock(struct _MDLock *md_lock)
+{
+ mutex_lock(&md_lock->lock);
+}
+
+void _MD_unlock(struct _MDLock *md_lock)
+{
+ mutex_unlock(&((md_lock)->lock));
+}
+
+
+PRThread *_pr_current_thread_tls()
+{
+ PRThread *ret;
+
+ thr_getspecific(threadid_key, (void **)&ret);
+ return ret;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ _MD_WAIT_SEM(&thread->md.waiter_sem);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread == NULL) {
+ return PR_SUCCESS;
+ }
+ _MD_POST_SEM(&thread->md.waiter_sem);
+ return PR_SUCCESS;
+}
+
+_PRCPU *_pr_current_cpu_tls()
+{
+ _PRCPU *ret;
+
+ thr_getspecific(cpuid_key, (void **)&ret);
+ return ret;
+}
+
+PRThread *_pr_last_thread_tls()
+{
+ PRThread *ret;
+
+ thr_getspecific(last_thread_key, (void **)&ret);
+ return ret;
+}
+
+_MDLock _pr_ioq_lock;
+
+void _MD_INIT_IO (void)
+{
+ _MD_NEW_LOCK(&_pr_ioq_lock);
+}
+
+PRStatus _MD_InitializeThread(PRThread *thread)
+{
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+ return;
+ }
+ /* prime the sp; substract 4 so we don't hit the assert that
+ * curr sp > base_stack
+ */
+ thread->md.sp = (uint_t) thread->stack->allocBase - sizeof(long);
+ thread->md.lwpid = _lwp_self();
+ thread->md.handle = THR_SELF();
+
+ /* all threads on Solaris are global threads from NSPR's perspective
+ * since all of them are mapped to Solaris threads.
+ */
+ thread->flags |= _PR_GLOBAL_SCOPE;
+
+ /* For primordial/attached thread, we don't create an underlying native thread.
+ * So, _MD_CREATE_THREAD() does not get called. We need to do initialization
+ * like allocating thread's synchronization variables and set the underlying
+ * native thread's priority.
+ */
+ if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) {
+ _MD_NEW_SEM(&thread->md.waiter_sem, 0);
+ _MD_SET_PRIORITY(&(thread->md), thread->priority);
+ }
+ return PR_SUCCESS;
+}
+
+static sigset_t old_mask; /* store away original gc thread sigmask */
+static int gcprio; /* store away original gc thread priority */
+static lwpid_t *all_lwps=NULL; /* list of lwps that we suspended */
+static int num_lwps ;
+static int suspendAllOn = 0;
+
+#define VALID_SP(sp, bottom, top) \
+ (((uint_t)(sp)) > ((uint_t)(bottom)) && ((uint_t)(sp)) < ((uint_t)(top)))
+
+void unixware_preempt_off()
+{
+ sigset_t set;
+ (void)sigfillset(&set);
+ sigprocmask (SIG_SETMASK, &set, &old_mask);
+}
+
+void unixware_preempt_on()
+{
+ sigprocmask (SIG_SETMASK, &old_mask, NULL);
+}
+
+void _MD_Begin_SuspendAll()
+{
+ unixware_preempt_off();
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin_SuspendAll\n"));
+ /* run at highest prio so I cannot be preempted */
+ thr_getprio(thr_self(), &gcprio);
+ thr_setprio(thr_self(), 0x7fffffff);
+ suspendAllOn = 1;
+}
+
+void _MD_End_SuspendAll()
+{
+}
+
+void _MD_End_ResumeAll()
+{
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End_ResumeAll\n"));
+ thr_setprio(thr_self(), gcprio);
+ unixware_preempt_on();
+ suspendAllOn = 0;
+}
+
+void _MD_Suspend(PRThread *thr)
+{
+ int lwp_fd, result;
+ int lwp_main_proc_fd = 0;
+
+ thr_suspend(thr->md.handle);
+ if (!_PR_IS_GCABLE_THREAD(thr)) {
+ return;
+ }
+ /* XXX Primordial thread can't be bound to an lwp, hence there is no
+ * way we can assume that we can get the lwp status for primordial
+ * thread reliably. Hence we skip this for primordial thread, hoping
+ * that the SP is saved during lock and cond. wait.
+ * XXX - Again this is concern only for java interpreter, not for the
+ * server, 'cause primordial thread in the server does not do java work
+ */
+ if (thr->flags & _PR_PRIMORDIAL) {
+ return;
+ }
+
+ /* if the thread is not started yet then don't do anything */
+ if (!suspendAllOn || thr->md.lwpid == -1) {
+ return;
+ }
+
+}
+void _MD_Resume(PRThread *thr)
+{
+ if (!_PR_IS_GCABLE_THREAD(thr) || !suspendAllOn) {
+ /*XXX When the suspendAllOn is set, we will be trying to do lwp_suspend
+ * during that time we can't call any thread lib or libc calls. Hence
+ * make sure that no resume is requested for Non gcable thread
+ * during suspendAllOn */
+ PR_ASSERT(!suspendAllOn);
+ thr_continue(thr->md.handle);
+ return;
+ }
+ if (thr->md.lwpid == -1) {
+ return;
+ }
+
+ if ( _lwp_continue(thr->md.lwpid) < 0) {
+ PR_ASSERT(0); /* ARGH, we are hosed! */
+ }
+}
+
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) getcontext(CONTEXT(t)); /* XXX tune me: set md_IRIX.c */
+ }
+ *np = NGREG;
+ if (t->md.lwpid == -1) {
+ memset(&t->md.context.uc_mcontext.gregs[0], 0, NGREG * sizeof(PRWord));
+ }
+ return (PRWord*) &t->md.context.uc_mcontext.gregs[0];
+}
+
+int
+_pr_unixware_clock_gettime (struct timespec *tp)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ tp->tv_sec = tv.tv_sec;
+ tp->tv_nsec = tv.tv_usec * 1000;
+ return 0;
+}
+
+
+#endif /* USE_SVR4_THREADS */
diff --git a/nsprpub/pr/src/md/unix/uxpoll.c b/nsprpub/pr/src/md/unix/uxpoll.c
new file mode 100644
index 0000000000..d01121cbf1
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/uxpoll.c
@@ -0,0 +1,722 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if defined(_PR_PTHREADS)
+
+#error "This file should not be compiled"
+
+#else /* defined(_PR_PTHREADS) */
+
+#include "primpl.h"
+
+#include <sys/time.h>
+
+#include <fcntl.h>
+#ifdef _PR_USE_POLL
+#include <poll.h>
+#endif
+
+#if defined(_PR_USE_POLL)
+static PRInt32 NativeThreadPoll(
+ PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ /*
+ * This function is mostly duplicated from ptio.s's PR_Poll().
+ */
+ PRInt32 ready = 0;
+ /*
+ * For restarting poll() if it is interrupted by a signal.
+ * We use these variables to figure out how much time has
+ * elapsed and how much of the timeout still remains.
+ */
+ PRIntn index, msecs;
+ struct pollfd *syspoll = NULL;
+ PRIntervalTime start, elapsed, remaining;
+
+ syspoll = (struct pollfd*)PR_MALLOC(npds * sizeof(struct pollfd));
+ if (NULL == syspoll)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ for (index = 0; index < npds; ++index)
+ {
+ PRFileDesc *bottom;
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if ((NULL != pds[index].fd) && (0 != pds[index].in_flags))
+ {
+ if (pds[index].in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pds[index].fd->methods->poll)(
+ pds[index].fd,
+ pds[index].in_flags & ~PR_POLL_WRITE,
+ &out_flags_read);
+ }
+ if (pds[index].in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pds[index].fd->methods->poll)(
+ pds[index].fd,
+ pds[index].in_flags & ~PR_POLL_READ,
+ &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read))
+ || (0 != (in_flags_write & out_flags_write)))
+ {
+ /* this one is ready right now */
+ if (0 == ready)
+ {
+ /*
+ * We will return without calling the system
+ * poll function. So zero the out_flags
+ * fields of all the poll descriptors before
+ * this one.
+ */
+ int i;
+ for (i = 0; i < index; i++)
+ {
+ pds[i].out_flags = 0;
+ }
+ }
+ ready += 1;
+ pds[index].out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ pds[index].out_flags = 0; /* pre-condition */
+ /* now locate the NSPR layer at the bottom of the stack */
+ bottom = PR_GetIdentitiesLayer(pds[index].fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom); /* what to do about that? */
+ if ((NULL != bottom)
+ && (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ if (0 == ready)
+ {
+ syspoll[index].fd = bottom->secret->md.osfd;
+ syspoll[index].events = 0; /* pre-condition */
+ if (in_flags_read & PR_POLL_READ)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_READ_SYS_READ;
+ syspoll[index].events |= POLLIN;
+ }
+ if (in_flags_read & PR_POLL_WRITE)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_READ_SYS_WRITE;
+ syspoll[index].events |= POLLOUT;
+ }
+ if (in_flags_write & PR_POLL_READ)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_WRITE_SYS_READ;
+ syspoll[index].events |= POLLIN;
+ }
+ if (in_flags_write & PR_POLL_WRITE)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_WRITE_SYS_WRITE;
+ syspoll[index].events |= POLLOUT;
+ }
+ if (pds[index].in_flags & PR_POLL_EXCEPT) {
+ syspoll[index].events |= POLLPRI;
+ }
+ }
+ }
+ else
+ {
+ if (0 == ready)
+ {
+ int i;
+ for (i = 0; i < index; i++)
+ {
+ pds[i].out_flags = 0;
+ }
+ }
+ ready += 1; /* this will cause an abrupt return */
+ pds[index].out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+ else
+ {
+ /* make poll() ignore this entry */
+ syspoll[index].fd = -1;
+ syspoll[index].events = 0;
+ pds[index].out_flags = 0;
+ }
+ }
+
+ if (0 == ready)
+ {
+ switch (timeout)
+ {
+ case PR_INTERVAL_NO_WAIT: msecs = 0; break;
+ case PR_INTERVAL_NO_TIMEOUT: msecs = -1; break;
+ default:
+ msecs = PR_IntervalToMilliseconds(timeout);
+ start = PR_IntervalNow();
+ }
+
+retry:
+ ready = _MD_POLL(syspoll, npds, msecs);
+ if (-1 == ready)
+ {
+ PRIntn oserror = errno;
+
+ if (EINTR == oserror)
+ {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ goto retry;
+ }
+ else if (timeout == PR_INTERVAL_NO_WAIT) {
+ ready = 0;
+ }
+ else
+ {
+ elapsed = (PRIntervalTime)(PR_IntervalNow() - start);
+ if (elapsed > timeout) {
+ ready = 0; /* timed out */
+ }
+ else
+ {
+ remaining = timeout - elapsed;
+ msecs = PR_IntervalToMilliseconds(remaining);
+ goto retry;
+ }
+ }
+ }
+ else {
+ _PR_MD_MAP_POLL_ERROR(oserror);
+ }
+ }
+ else if (ready > 0)
+ {
+ for (index = 0; index < npds; ++index)
+ {
+ PRInt16 out_flags = 0;
+ if ((NULL != pds[index].fd) && (0 != pds[index].in_flags))
+ {
+ if (0 != syspoll[index].revents)
+ {
+ /*
+ ** Set up the out_flags so that it contains the
+ ** bits that the highest layer thinks are nice
+ ** to have. Then the client of that layer will
+ ** call the appropriate I/O function and maybe
+ ** the protocol will make progress.
+ */
+ if (syspoll[index].revents & POLLIN)
+ {
+ if (pds[index].out_flags
+ & _PR_POLL_READ_SYS_READ)
+ {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pds[index].out_flags
+ & _PR_POLL_WRITE_SYS_READ)
+ {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (syspoll[index].revents & POLLOUT)
+ {
+ if (pds[index].out_flags
+ & _PR_POLL_READ_SYS_WRITE)
+ {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pds[index].out_flags
+ & _PR_POLL_WRITE_SYS_WRITE)
+ {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (syspoll[index].revents & POLLPRI) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ if (syspoll[index].revents & POLLERR) {
+ out_flags |= PR_POLL_ERR;
+ }
+ if (syspoll[index].revents & POLLNVAL) {
+ out_flags |= PR_POLL_NVAL;
+ }
+ if (syspoll[index].revents & POLLHUP) {
+ out_flags |= PR_POLL_HUP;
+ }
+ }
+ }
+ pds[index].out_flags = out_flags;
+ }
+ }
+ }
+
+ PR_DELETE(syspoll);
+ return ready;
+
+} /* NativeThreadPoll */
+#endif /* defined(_PR_USE_POLL) */
+
+#if !defined(_PR_USE_POLL)
+static PRInt32 NativeThreadSelect(
+ PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ /*
+ * This code is almost a duplicate of w32poll.c's _PR_MD_PR_POLL().
+ */
+ fd_set rd, wt, ex;
+ PRFileDesc *bottom;
+ PRPollDesc *pd, *epd;
+ PRInt32 maxfd = -1, ready, err;
+ PRIntervalTime remaining, elapsed, start;
+
+ struct timeval tv, *tvp = NULL;
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wt);
+ FD_ZERO(&ex);
+
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ if (pd->in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pd->fd->methods->poll)(
+ pd->fd, pd->in_flags & ~PR_POLL_WRITE, &out_flags_read);
+ }
+ if (pd->in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pd->fd->methods->poll)(
+ pd->fd, pd->in_flags & ~PR_POLL_READ, &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read))
+ || (0 != (in_flags_write & out_flags_write)))
+ {
+ /* this one's ready right now */
+ if (0 == ready)
+ {
+ /*
+ * We will have to return without calling the
+ * system poll/select function. So zero the
+ * out_flags fields of all the poll descriptors
+ * before this one.
+ */
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1;
+ pd->out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ pd->out_flags = 0; /* pre-condition */
+
+ /* make sure this is an NSPR supported stack */
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom); /* what to do about that? */
+ if ((NULL != bottom)
+ && (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ if (0 == ready)
+ {
+ PRInt32 osfd = bottom->secret->md.osfd;
+ if (osfd > maxfd) {
+ maxfd = osfd;
+ }
+ if (in_flags_read & PR_POLL_READ)
+ {
+ pd->out_flags |= _PR_POLL_READ_SYS_READ;
+ FD_SET(osfd, &rd);
+ }
+ if (in_flags_read & PR_POLL_WRITE)
+ {
+ pd->out_flags |= _PR_POLL_READ_SYS_WRITE;
+ FD_SET(osfd, &wt);
+ }
+ if (in_flags_write & PR_POLL_READ)
+ {
+ pd->out_flags |= _PR_POLL_WRITE_SYS_READ;
+ FD_SET(osfd, &rd);
+ }
+ if (in_flags_write & PR_POLL_WRITE)
+ {
+ pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE;
+ FD_SET(osfd, &wt);
+ }
+ if (pd->in_flags & PR_POLL_EXCEPT) {
+ FD_SET(osfd, &ex);
+ }
+ }
+ }
+ else
+ {
+ if (0 == ready)
+ {
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1; /* this will cause an abrupt return */
+ pd->out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+ else
+ {
+ pd->out_flags = 0;
+ }
+ }
+
+ if (0 != ready) {
+ return ready; /* no need to block */
+ }
+
+ remaining = timeout;
+ start = PR_IntervalNow();
+
+retry:
+ if (timeout != PR_INTERVAL_NO_TIMEOUT)
+ {
+ PRInt32 ticksPerSecond = PR_TicksPerSecond();
+ tv.tv_sec = remaining / ticksPerSecond;
+ tv.tv_usec = PR_IntervalToMicroseconds( remaining % ticksPerSecond );
+ tvp = &tv;
+ }
+
+ ready = _MD_SELECT(maxfd + 1, &rd, &wt, &ex, tvp);
+
+ if (ready == -1 && errno == EINTR)
+ {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ goto retry;
+ }
+ else
+ {
+ elapsed = (PRIntervalTime) (PR_IntervalNow() - start);
+ if (elapsed > timeout) {
+ ready = 0; /* timed out */
+ }
+ else
+ {
+ remaining = timeout - elapsed;
+ goto retry;
+ }
+ }
+ }
+
+ /*
+ ** Now to unravel the select sets back into the client's poll
+ ** descriptor list. Is this possibly an area for pissing away
+ ** a few cycles or what?
+ */
+ if (ready > 0)
+ {
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRInt16 out_flags = 0;
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ PRInt32 osfd;
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+
+ osfd = bottom->secret->md.osfd;
+
+ if (FD_ISSET(osfd, &rd))
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_READ) {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_READ) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (FD_ISSET(osfd, &wt))
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_WRITE) {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (FD_ISSET(osfd, &ex)) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ }
+ pd->out_flags = out_flags;
+ if (out_flags) {
+ ready++;
+ }
+ }
+ PR_ASSERT(ready > 0);
+ }
+ else if (ready < 0)
+ {
+ err = _MD_ERRNO();
+ if (err == EBADF)
+ {
+ /* Find the bad fds */
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ pd->out_flags = 0;
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ if (fcntl(bottom->secret->md.osfd, F_GETFL, 0) == -1)
+ {
+ pd->out_flags = PR_POLL_NVAL;
+ ready++;
+ }
+ }
+ }
+ PR_ASSERT(ready > 0);
+ }
+ else {
+ _PR_MD_MAP_SELECT_ERROR(err);
+ }
+ }
+
+ return ready;
+} /* NativeThreadSelect */
+#endif /* !defined(_PR_USE_POLL) */
+
+static PRInt32 LocalThreads(
+ PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ PRPollDesc *pd, *epd;
+ PRInt32 ready, pdcnt;
+ _PRUnixPollDesc *unixpds, *unixpd;
+
+ /*
+ * XXX
+ * PRPollDesc has a PRFileDesc field, fd, while the IOQ
+ * is a list of PRPollQueue structures, each of which contains
+ * a _PRUnixPollDesc. A _PRUnixPollDesc struct contains
+ * the OS file descriptor, osfd, and not a PRFileDesc.
+ * So, we have allocate memory for _PRUnixPollDesc structures,
+ * copy the flags information from the pds list and have pq
+ * point to this list of _PRUnixPollDesc structures.
+ *
+ * It would be better if the memory allocation can be avoided.
+ */
+
+ unixpd = unixpds = (_PRUnixPollDesc*)
+ PR_MALLOC(npds * sizeof(_PRUnixPollDesc));
+ if (NULL == unixpds)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+
+ ready = 0;
+ for (pdcnt = 0, pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRFileDesc *bottom;
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ if (pd->in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pd->fd->methods->poll)(
+ pd->fd, pd->in_flags & ~PR_POLL_WRITE, &out_flags_read);
+ }
+ if (pd->in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pd->fd->methods->poll)(
+ pd->fd, pd->in_flags & ~PR_POLL_READ, &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read))
+ || (0 != (in_flags_write & out_flags_write)))
+ {
+ /* this one's ready right now */
+ if (0 == ready)
+ {
+ /*
+ * We will have to return without calling the
+ * system poll/select function. So zero the
+ * out_flags fields of all the poll descriptors
+ * before this one.
+ */
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1;
+ pd->out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ pd->out_flags = 0; /* pre-condition */
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom); /* what to do about that? */
+ if ((NULL != bottom)
+ && (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ if (0 == ready)
+ {
+ unixpd->osfd = bottom->secret->md.osfd;
+ unixpd->in_flags = 0;
+ if (in_flags_read & PR_POLL_READ)
+ {
+ unixpd->in_flags |= _PR_UNIX_POLL_READ;
+ pd->out_flags |= _PR_POLL_READ_SYS_READ;
+ }
+ if (in_flags_read & PR_POLL_WRITE)
+ {
+ unixpd->in_flags |= _PR_UNIX_POLL_WRITE;
+ pd->out_flags |= _PR_POLL_READ_SYS_WRITE;
+ }
+ if (in_flags_write & PR_POLL_READ)
+ {
+ unixpd->in_flags |= _PR_UNIX_POLL_READ;
+ pd->out_flags |= _PR_POLL_WRITE_SYS_READ;
+ }
+ if (in_flags_write & PR_POLL_WRITE)
+ {
+ unixpd->in_flags |= _PR_UNIX_POLL_WRITE;
+ pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE;
+ }
+ if ((in_flags_read | in_flags_write) & PR_POLL_EXCEPT)
+ {
+ unixpd->in_flags |= _PR_UNIX_POLL_EXCEPT;
+ }
+ unixpd++; pdcnt++;
+ }
+ }
+ else
+ {
+ if (0 == ready)
+ {
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1; /* this will cause an abrupt return */
+ pd->out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+ }
+
+ if (0 != ready)
+ {
+ /* no need to block */
+ PR_DELETE(unixpds);
+ return ready;
+ }
+
+ ready = _PR_WaitForMultipleFDs(unixpds, pdcnt, timeout);
+
+ /*
+ * Copy the out_flags from the _PRUnixPollDesc structures to the
+ * user's PRPollDesc structures and free the allocated memory
+ */
+ unixpd = unixpds;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRInt16 out_flags = 0;
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ /*
+ * take errors from the poll operation,
+ * the R/W bits from the request
+ */
+ if (0 != unixpd->out_flags)
+ {
+ if (unixpd->out_flags & _PR_UNIX_POLL_READ)
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_READ) {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_READ) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_WRITE)
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_WRITE) {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_EXCEPT) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_ERR) {
+ out_flags |= PR_POLL_ERR;
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) {
+ out_flags |= PR_POLL_NVAL;
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_HUP) {
+ out_flags |= PR_POLL_HUP;
+ }
+ }
+ unixpd++;
+ }
+ pd->out_flags = out_flags;
+ }
+
+ PR_DELETE(unixpds);
+
+ return ready;
+} /* LocalThreads */
+
+#if defined(_PR_USE_POLL)
+#define NativeThreads NativeThreadPoll
+#else
+#define NativeThreads NativeThreadSelect
+#endif
+
+PRInt32 _MD_pr_poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ PRInt32 rv = 0;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (0 == npds) {
+ PR_Sleep(timeout);
+ }
+ else if (_PR_IS_NATIVE_THREAD(me)) {
+ rv = NativeThreads(pds, npds, timeout);
+ }
+ else {
+ rv = LocalThreads(pds, npds, timeout);
+ }
+
+ return rv;
+} /* _MD_pr_poll */
+
+#endif /* defined(_PR_PTHREADS) */
+
+/* uxpoll.c */
+
diff --git a/nsprpub/pr/src/md/unix/uxproces.c b/nsprpub/pr/src/md/unix/uxproces.c
new file mode 100644
index 0000000000..4216c04424
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/uxproces.c
@@ -0,0 +1,877 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <string.h>
+#if defined(AIX)
+#include <dlfcn.h> /* For dlopen, dlsym, dlclose */
+#endif
+
+#if defined(DARWIN)
+#if defined(HAVE_CRT_EXTERNS_H)
+#include <crt_externs.h>
+#endif
+#else
+PR_IMPORT_DATA(char **) environ;
+#endif
+
+/*
+ * HP-UX 9 doesn't have the SA_RESTART flag.
+ */
+#ifndef SA_RESTART
+#define SA_RESTART 0
+#endif
+
+/*
+ **********************************************************************
+ *
+ * The Unix process routines
+ *
+ **********************************************************************
+ */
+
+#define _PR_SIGNALED_EXITSTATUS 256
+
+typedef enum pr_PidState {
+ _PR_PID_DETACHED,
+ _PR_PID_REAPED,
+ _PR_PID_WAITING
+} pr_PidState;
+
+typedef struct pr_PidRecord {
+ pid_t pid;
+ int exitStatus;
+ pr_PidState state;
+ PRCondVar *reapedCV;
+ struct pr_PidRecord *next;
+} pr_PidRecord;
+
+/*
+ * LinuxThreads are actually a kind of processes
+ * that can share the virtual address space and file descriptors.
+ */
+#if ((defined(LINUX) || defined(__GNU__) || defined(__GLIBC__)) \
+ && defined(_PR_PTHREADS))
+#define _PR_SHARE_CLONES
+#endif
+
+/*
+ * The macro _PR_NATIVE_THREADS indicates that we are
+ * using native threads only, so waitpid() blocks just the
+ * calling thread, not the process. In this case, the waitpid
+ * daemon thread can safely block in waitpid(). So we don't
+ * need to catch SIGCHLD, and the pipe to unblock PR_Poll() is
+ * also not necessary.
+ */
+
+#if defined(_PR_GLOBAL_THREADS_ONLY) \
+ || (defined(_PR_PTHREADS) \
+ && !defined(LINUX) && !defined(__GNU__) && !defined(__GLIBC__))
+#define _PR_NATIVE_THREADS
+#endif
+
+/*
+ * All the static variables used by the Unix process routines are
+ * collected in this structure.
+ */
+
+static struct {
+ PRCallOnceType once;
+ PRThread *thread;
+ PRLock *ml;
+#if defined(_PR_NATIVE_THREADS)
+ PRInt32 numProcs;
+ PRCondVar *cv;
+#else
+ int pipefd[2];
+#endif
+ pr_PidRecord **pidTable;
+
+#ifdef _PR_SHARE_CLONES
+ struct pr_CreateProcOp *opHead, *opTail;
+#endif
+
+#ifdef AIX
+ pid_t (*forkptr)(void); /* Newer versions of AIX (starting in 4.3.2)
+ * have f_fork, which is faster than the
+ * regular fork in a multithreaded process
+ * because it skips calling the fork handlers.
+ * So we look up the f_fork symbol to see if
+ * it's available and fall back on fork.
+ */
+#endif /* AIX */
+} pr_wp;
+
+#ifdef _PR_SHARE_CLONES
+static int pr_waitpid_daemon_exit;
+
+void
+_MD_unix_terminate_waitpid_daemon(void)
+{
+ if (pr_wp.thread) {
+ pr_waitpid_daemon_exit = 1;
+ write(pr_wp.pipefd[1], "", 1);
+ PR_JoinThread(pr_wp.thread);
+ }
+}
+#endif
+
+static PRStatus _MD_InitProcesses(void);
+#if !defined(_PR_NATIVE_THREADS)
+static void pr_InstallSigchldHandler(void);
+#endif
+
+static PRProcess *
+ForkAndExec(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ PRProcess *process;
+ int nEnv, idx;
+ char *const *childEnvp;
+ char **newEnvp = NULL;
+ int flags;
+
+ process = PR_NEW(PRProcess);
+ if (!process) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+
+ childEnvp = envp;
+ if (attr && attr->fdInheritBuffer) {
+ PRBool found = PR_FALSE;
+
+ if (NULL == childEnvp) {
+#ifdef DARWIN
+#ifdef HAVE_CRT_EXTERNS_H
+ childEnvp = *(_NSGetEnviron());
+#else
+ /* _NSGetEnviron() is not available on iOS. */
+ PR_DELETE(process);
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+#endif
+#else
+ childEnvp = environ;
+#endif
+ }
+
+ for (nEnv = 0; childEnvp[nEnv]; nEnv++) {
+ }
+ newEnvp = (char **) PR_MALLOC((nEnv + 2) * sizeof(char *));
+ if (NULL == newEnvp) {
+ PR_DELETE(process);
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ for (idx = 0; idx < nEnv; idx++) {
+ newEnvp[idx] = childEnvp[idx];
+ if (!found && !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) {
+ newEnvp[idx] = attr->fdInheritBuffer;
+ found = PR_TRUE;
+ }
+ }
+ if (!found) {
+ newEnvp[idx++] = attr->fdInheritBuffer;
+ }
+ newEnvp[idx] = NULL;
+ childEnvp = newEnvp;
+ }
+
+#ifdef AIX
+ process->md.pid = (*pr_wp.forkptr)();
+#elif defined(NTO)
+ /*
+ * fork() & exec() does not work in a multithreaded process.
+ * Use spawn() instead.
+ */
+ {
+ int fd_map[3] = { 0, 1, 2 };
+
+ if (attr) {
+ if (attr->stdinFd && attr->stdinFd->secret->md.osfd != 0) {
+ fd_map[0] = dup(attr->stdinFd->secret->md.osfd);
+ flags = fcntl(fd_map[0], F_GETFL, 0);
+ if (flags & O_NONBLOCK) {
+ fcntl(fd_map[0], F_SETFL, flags & ~O_NONBLOCK);
+ }
+ }
+ if (attr->stdoutFd && attr->stdoutFd->secret->md.osfd != 1) {
+ fd_map[1] = dup(attr->stdoutFd->secret->md.osfd);
+ flags = fcntl(fd_map[1], F_GETFL, 0);
+ if (flags & O_NONBLOCK) {
+ fcntl(fd_map[1], F_SETFL, flags & ~O_NONBLOCK);
+ }
+ }
+ if (attr->stderrFd && attr->stderrFd->secret->md.osfd != 2) {
+ fd_map[2] = dup(attr->stderrFd->secret->md.osfd);
+ flags = fcntl(fd_map[2], F_GETFL, 0);
+ if (flags & O_NONBLOCK) {
+ fcntl(fd_map[2], F_SETFL, flags & ~O_NONBLOCK);
+ }
+ }
+
+ PR_ASSERT(attr->currentDirectory == NULL); /* not implemented */
+ }
+
+ process->md.pid = spawn(path, 3, fd_map, NULL, argv, childEnvp);
+
+ if (fd_map[0] != 0) {
+ close(fd_map[0]);
+ }
+ if (fd_map[1] != 1) {
+ close(fd_map[1]);
+ }
+ if (fd_map[2] != 2) {
+ close(fd_map[2]);
+ }
+ }
+#else
+ process->md.pid = fork();
+#endif
+ if ((pid_t) -1 == process->md.pid) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, errno);
+ PR_DELETE(process);
+ if (newEnvp) {
+ PR_DELETE(newEnvp);
+ }
+ return NULL;
+ }
+ if (0 == process->md.pid) { /* the child process */
+ /*
+ * If the child process needs to exit, it must call _exit().
+ * Do not call exit(), because exit() will flush and close
+ * the standard I/O file descriptors, and hence corrupt
+ * the parent process's standard I/O data structures.
+ */
+
+#if !defined(NTO)
+ if (attr) {
+ /* the osfd's to redirect stdin, stdout, and stderr to */
+ int in_osfd = -1, out_osfd = -1, err_osfd = -1;
+
+ if (attr->stdinFd
+ && attr->stdinFd->secret->md.osfd != 0) {
+ in_osfd = attr->stdinFd->secret->md.osfd;
+ if (dup2(in_osfd, 0) != 0) {
+ _exit(1); /* failed */
+ }
+ flags = fcntl(0, F_GETFL, 0);
+ if (flags & O_NONBLOCK) {
+ fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
+ }
+ }
+ if (attr->stdoutFd
+ && attr->stdoutFd->secret->md.osfd != 1) {
+ out_osfd = attr->stdoutFd->secret->md.osfd;
+ if (dup2(out_osfd, 1) != 1) {
+ _exit(1); /* failed */
+ }
+ flags = fcntl(1, F_GETFL, 0);
+ if (flags & O_NONBLOCK) {
+ fcntl(1, F_SETFL, flags & ~O_NONBLOCK);
+ }
+ }
+ if (attr->stderrFd
+ && attr->stderrFd->secret->md.osfd != 2) {
+ err_osfd = attr->stderrFd->secret->md.osfd;
+ if (dup2(err_osfd, 2) != 2) {
+ _exit(1); /* failed */
+ }
+ flags = fcntl(2, F_GETFL, 0);
+ if (flags & O_NONBLOCK) {
+ fcntl(2, F_SETFL, flags & ~O_NONBLOCK);
+ }
+ }
+ if (in_osfd != -1) {
+ close(in_osfd);
+ }
+ if (out_osfd != -1 && out_osfd != in_osfd) {
+ close(out_osfd);
+ }
+ if (err_osfd != -1 && err_osfd != in_osfd
+ && err_osfd != out_osfd) {
+ close(err_osfd);
+ }
+ if (attr->currentDirectory) {
+ if (chdir(attr->currentDirectory) < 0) {
+ _exit(1); /* failed */
+ }
+ }
+ }
+
+ if (childEnvp) {
+ (void)execve(path, argv, childEnvp);
+ } else {
+ /* Inherit the environment of the parent. */
+ (void)execv(path, argv);
+ }
+ /* Whoops! It returned. That's a bad sign. */
+ _exit(1);
+#endif /* !NTO */
+ }
+
+ if (newEnvp) {
+ PR_DELETE(newEnvp);
+ }
+
+#if defined(_PR_NATIVE_THREADS)
+ PR_Lock(pr_wp.ml);
+ if (0 == pr_wp.numProcs++) {
+ PR_NotifyCondVar(pr_wp.cv);
+ }
+ PR_Unlock(pr_wp.ml);
+#endif
+ return process;
+}
+
+#ifdef _PR_SHARE_CLONES
+
+struct pr_CreateProcOp {
+ const char *path;
+ char *const *argv;
+ char *const *envp;
+ const PRProcessAttr *attr;
+ PRProcess *process;
+ PRErrorCode prerror;
+ PRInt32 oserror;
+ PRBool done;
+ PRCondVar *doneCV;
+ struct pr_CreateProcOp *next;
+};
+
+PRProcess *
+_MD_CreateUnixProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ struct pr_CreateProcOp *op;
+ PRProcess *proc;
+ int rv;
+
+ if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) {
+ return NULL;
+ }
+
+ op = PR_NEW(struct pr_CreateProcOp);
+ if (NULL == op) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ op->path = path;
+ op->argv = argv;
+ op->envp = envp;
+ op->attr = attr;
+ op->done = PR_FALSE;
+ op->doneCV = PR_NewCondVar(pr_wp.ml);
+ if (NULL == op->doneCV) {
+ PR_DELETE(op);
+ return NULL;
+ }
+ PR_Lock(pr_wp.ml);
+
+ /* add to the tail of op queue */
+ op->next = NULL;
+ if (pr_wp.opTail) {
+ pr_wp.opTail->next = op;
+ pr_wp.opTail = op;
+ } else {
+ PR_ASSERT(NULL == pr_wp.opHead);
+ pr_wp.opHead = pr_wp.opTail = op;
+ }
+
+ /* wake up the daemon thread */
+ do {
+ rv = write(pr_wp.pipefd[1], "", 1);
+ } while (-1 == rv && EINTR == errno);
+
+ while (op->done == PR_FALSE) {
+ PR_WaitCondVar(op->doneCV, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(pr_wp.ml);
+ PR_DestroyCondVar(op->doneCV);
+ proc = op->process;
+ if (!proc) {
+ PR_SetError(op->prerror, op->oserror);
+ }
+ PR_DELETE(op);
+ return proc;
+}
+
+#else /* ! _PR_SHARE_CLONES */
+
+PRProcess *
+_MD_CreateUnixProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) {
+ return NULL;
+ }
+ return ForkAndExec(path, argv, envp, attr);
+} /* _MD_CreateUnixProcess */
+
+#endif /* _PR_SHARE_CLONES */
+
+/*
+ * The pid table is a hashtable.
+ *
+ * The number of buckets in the hashtable (NBUCKETS) must be a power of 2.
+ */
+#define NBUCKETS_LOG2 6
+#define NBUCKETS (1 << NBUCKETS_LOG2)
+#define PID_HASH_MASK ((pid_t) (NBUCKETS - 1))
+
+static pr_PidRecord *
+FindPidTable(pid_t pid)
+{
+ pr_PidRecord *pRec;
+ int keyHash = (int) (pid & PID_HASH_MASK);
+
+ pRec = pr_wp.pidTable[keyHash];
+ while (pRec) {
+ if (pRec->pid == pid) {
+ break;
+ }
+ pRec = pRec->next;
+ }
+ return pRec;
+}
+
+static void
+InsertPidTable(pr_PidRecord *pRec)
+{
+ int keyHash = (int) (pRec->pid & PID_HASH_MASK);
+
+ pRec->next = pr_wp.pidTable[keyHash];
+ pr_wp.pidTable[keyHash] = pRec;
+}
+
+static void
+DeletePidTable(pr_PidRecord *pRec)
+{
+ int keyHash = (int) (pRec->pid & PID_HASH_MASK);
+
+ if (pr_wp.pidTable[keyHash] == pRec) {
+ pr_wp.pidTable[keyHash] = pRec->next;
+ } else {
+ pr_PidRecord *pred, *cur; /* predecessor and current */
+
+ pred = pr_wp.pidTable[keyHash];
+ cur = pred->next;
+ while (cur) {
+ if (cur == pRec) {
+ pred->next = cur->next;
+ break;
+ }
+ pred = cur;
+ cur = cur->next;
+ }
+ PR_ASSERT(cur != NULL);
+ }
+}
+
+static int
+ExtractExitStatus(int rawExitStatus)
+{
+ /*
+ * We did not specify the WCONTINUED and WUNTRACED options
+ * for waitpid, so these two events should not be reported.
+ */
+ PR_ASSERT(!WIFSTOPPED(rawExitStatus));
+#ifdef WIFCONTINUED
+ PR_ASSERT(!WIFCONTINUED(rawExitStatus));
+#endif
+ if (WIFEXITED(rawExitStatus)) {
+ return WEXITSTATUS(rawExitStatus);
+ }
+ PR_ASSERT(WIFSIGNALED(rawExitStatus));
+ return _PR_SIGNALED_EXITSTATUS;
+}
+
+static void
+ProcessReapedChildInternal(pid_t pid, int status)
+{
+ pr_PidRecord *pRec;
+
+ pRec = FindPidTable(pid);
+ if (NULL == pRec) {
+ pRec = PR_NEW(pr_PidRecord);
+ pRec->pid = pid;
+ pRec->state = _PR_PID_REAPED;
+ pRec->exitStatus = ExtractExitStatus(status);
+ pRec->reapedCV = NULL;
+ InsertPidTable(pRec);
+ } else {
+ PR_ASSERT(pRec->state != _PR_PID_REAPED);
+ if (_PR_PID_DETACHED == pRec->state) {
+ PR_ASSERT(NULL == pRec->reapedCV);
+ DeletePidTable(pRec);
+ PR_DELETE(pRec);
+ } else {
+ PR_ASSERT(_PR_PID_WAITING == pRec->state);
+ PR_ASSERT(NULL != pRec->reapedCV);
+ pRec->exitStatus = ExtractExitStatus(status);
+ pRec->state = _PR_PID_REAPED;
+ PR_NotifyCondVar(pRec->reapedCV);
+ }
+ }
+}
+
+#if defined(_PR_NATIVE_THREADS)
+
+/*
+ * If all the threads are native threads, the daemon thread is
+ * simpler. We don't need to catch the SIGCHLD signal. We can
+ * just have the daemon thread block in waitpid().
+ */
+
+static void WaitPidDaemonThread(void *unused)
+{
+ pid_t pid;
+ int status;
+
+ while (1) {
+ PR_Lock(pr_wp.ml);
+ while (0 == pr_wp.numProcs) {
+ PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(pr_wp.ml);
+
+ while (1) {
+ do {
+ pid = waitpid((pid_t) -1, &status, 0);
+ } while ((pid_t) -1 == pid && EINTR == errno);
+
+ /*
+ * waitpid() cannot return 0 because we did not invoke it
+ * with the WNOHANG option.
+ */
+ PR_ASSERT(0 != pid);
+
+ /*
+ * The only possible error code is ECHILD. But if we do
+ * our accounting correctly, we should only call waitpid()
+ * when there is a child process to wait for.
+ */
+ PR_ASSERT((pid_t) -1 != pid);
+ if ((pid_t) -1 == pid) {
+ break;
+ }
+
+ PR_Lock(pr_wp.ml);
+ ProcessReapedChildInternal(pid, status);
+ pr_wp.numProcs--;
+ while (0 == pr_wp.numProcs) {
+ PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(pr_wp.ml);
+ }
+ }
+}
+
+#else /* _PR_NATIVE_THREADS */
+
+static void WaitPidDaemonThread(void *unused)
+{
+ PRPollDesc pd;
+ PRFileDesc *fd;
+ int rv;
+ char buf[128];
+ pid_t pid;
+ int status;
+#ifdef _PR_SHARE_CLONES
+ struct pr_CreateProcOp *op;
+#endif
+
+#ifdef _PR_SHARE_CLONES
+ pr_InstallSigchldHandler();
+#endif
+
+ fd = PR_ImportFile(pr_wp.pipefd[0]);
+ PR_ASSERT(NULL != fd);
+ pd.fd = fd;
+ pd.in_flags = PR_POLL_READ;
+
+ while (1) {
+ rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(1 == rv);
+
+#ifdef _PR_SHARE_CLONES
+ if (pr_waitpid_daemon_exit) {
+ return;
+ }
+ PR_Lock(pr_wp.ml);
+#endif
+
+ do {
+ rv = read(pr_wp.pipefd[0], buf, sizeof(buf));
+ } while (sizeof(buf) == rv || (-1 == rv && EINTR == errno));
+
+#ifdef _PR_SHARE_CLONES
+ while ((op = pr_wp.opHead) != NULL) {
+ PR_Unlock(pr_wp.ml);
+ op->process = ForkAndExec(op->path, op->argv,
+ op->envp, op->attr);
+ if (NULL == op->process) {
+ op->prerror = PR_GetError();
+ op->oserror = PR_GetOSError();
+ }
+ PR_Lock(pr_wp.ml);
+ pr_wp.opHead = op->next;
+ if (NULL == pr_wp.opHead) {
+ pr_wp.opTail = NULL;
+ }
+ op->done = PR_TRUE;
+ PR_NotifyCondVar(op->doneCV);
+ }
+ PR_Unlock(pr_wp.ml);
+#endif
+
+ while (1) {
+ do {
+ pid = waitpid((pid_t) -1, &status, WNOHANG);
+ } while ((pid_t) -1 == pid && EINTR == errno);
+ if (0 == pid) {
+ break;
+ }
+ if ((pid_t) -1 == pid) {
+ /* must be because we have no child processes */
+ PR_ASSERT(ECHILD == errno);
+ break;
+ }
+
+ PR_Lock(pr_wp.ml);
+ ProcessReapedChildInternal(pid, status);
+ PR_Unlock(pr_wp.ml);
+ }
+ }
+}
+
+static void pr_SigchldHandler(int sig)
+{
+ int errnoCopy;
+ int rv;
+
+ errnoCopy = errno;
+
+ do {
+ rv = write(pr_wp.pipefd[1], "", 1);
+ } while (-1 == rv && EINTR == errno);
+
+#ifdef DEBUG
+ if (-1 == rv && EAGAIN != errno && EWOULDBLOCK != errno) {
+ char *msg = "cannot write to pipe\n";
+ write(2, msg, strlen(msg) + 1);
+ _exit(1);
+ }
+#endif
+
+ errno = errnoCopy;
+}
+
+static void pr_InstallSigchldHandler()
+{
+ struct sigaction act, oact;
+ int rv;
+
+ act.sa_handler = pr_SigchldHandler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+ rv = sigaction(SIGCHLD, &act, &oact);
+ PR_ASSERT(0 == rv);
+ /* Make sure we are not overriding someone else's SIGCHLD handler */
+#ifndef _PR_SHARE_CLONES
+ PR_ASSERT(oact.sa_handler == SIG_DFL);
+#endif
+}
+
+#endif /* !defined(_PR_NATIVE_THREADS) */
+
+static PRStatus _MD_InitProcesses(void)
+{
+#if !defined(_PR_NATIVE_THREADS)
+ int rv;
+ int flags;
+#endif
+
+#ifdef AIX
+ {
+ void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
+ pr_wp.forkptr = (pid_t (*)(void)) dlsym(handle, "f_fork");
+ if (!pr_wp.forkptr) {
+ pr_wp.forkptr = fork;
+ }
+ dlclose(handle);
+ }
+#endif /* AIX */
+
+ pr_wp.ml = PR_NewLock();
+ PR_ASSERT(NULL != pr_wp.ml);
+
+#if defined(_PR_NATIVE_THREADS)
+ pr_wp.numProcs = 0;
+ pr_wp.cv = PR_NewCondVar(pr_wp.ml);
+ PR_ASSERT(NULL != pr_wp.cv);
+#else
+ rv = pipe(pr_wp.pipefd);
+ PR_ASSERT(0 == rv);
+ flags = fcntl(pr_wp.pipefd[0], F_GETFL, 0);
+ fcntl(pr_wp.pipefd[0], F_SETFL, flags | O_NONBLOCK);
+ flags = fcntl(pr_wp.pipefd[1], F_GETFL, 0);
+ fcntl(pr_wp.pipefd[1], F_SETFL, flags | O_NONBLOCK);
+
+#ifndef _PR_SHARE_CLONES
+ pr_InstallSigchldHandler();
+#endif
+#endif /* !_PR_NATIVE_THREADS */
+
+ pr_wp.thread = PR_CreateThread(PR_SYSTEM_THREAD,
+ WaitPidDaemonThread, NULL, PR_PRIORITY_NORMAL,
+#ifdef _PR_SHARE_CLONES
+ PR_GLOBAL_THREAD,
+#else
+ PR_LOCAL_THREAD,
+#endif
+ PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(NULL != pr_wp.thread);
+
+ pr_wp.pidTable = (pr_PidRecord**)PR_CALLOC(NBUCKETS * sizeof(pr_PidRecord *));
+ PR_ASSERT(NULL != pr_wp.pidTable);
+ return PR_SUCCESS;
+}
+
+PRStatus _MD_DetachUnixProcess(PRProcess *process)
+{
+ PRStatus retVal = PR_SUCCESS;
+ pr_PidRecord *pRec;
+
+ PR_Lock(pr_wp.ml);
+ pRec = FindPidTable(process->md.pid);
+ if (NULL == pRec) {
+ pRec = PR_NEW(pr_PidRecord);
+ if (NULL == pRec) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ retVal = PR_FAILURE;
+ goto done;
+ }
+ pRec->pid = process->md.pid;
+ pRec->state = _PR_PID_DETACHED;
+ pRec->reapedCV = NULL;
+ InsertPidTable(pRec);
+ } else {
+ PR_ASSERT(_PR_PID_REAPED == pRec->state);
+ if (_PR_PID_REAPED != pRec->state) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ retVal = PR_FAILURE;
+ } else {
+ DeletePidTable(pRec);
+ PR_ASSERT(NULL == pRec->reapedCV);
+ PR_DELETE(pRec);
+ }
+ }
+ PR_DELETE(process);
+
+done:
+ PR_Unlock(pr_wp.ml);
+ return retVal;
+}
+
+PRStatus _MD_WaitUnixProcess(
+ PRProcess *process,
+ PRInt32 *exitCode)
+{
+ pr_PidRecord *pRec;
+ PRStatus retVal = PR_SUCCESS;
+ PRBool interrupted = PR_FALSE;
+
+ PR_Lock(pr_wp.ml);
+ pRec = FindPidTable(process->md.pid);
+ if (NULL == pRec) {
+ pRec = PR_NEW(pr_PidRecord);
+ if (NULL == pRec) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ retVal = PR_FAILURE;
+ goto done;
+ }
+ pRec->pid = process->md.pid;
+ pRec->state = _PR_PID_WAITING;
+ pRec->reapedCV = PR_NewCondVar(pr_wp.ml);
+ if (NULL == pRec->reapedCV) {
+ PR_DELETE(pRec);
+ retVal = PR_FAILURE;
+ goto done;
+ }
+ InsertPidTable(pRec);
+ while (!interrupted && _PR_PID_REAPED != pRec->state) {
+ if (PR_WaitCondVar(pRec->reapedCV,
+ PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE
+ && PR_GetError() == PR_PENDING_INTERRUPT_ERROR) {
+ interrupted = PR_TRUE;
+ }
+ }
+ if (_PR_PID_REAPED == pRec->state) {
+ if (exitCode) {
+ *exitCode = pRec->exitStatus;
+ }
+ } else {
+ PR_ASSERT(interrupted);
+ retVal = PR_FAILURE;
+ }
+ DeletePidTable(pRec);
+ PR_DestroyCondVar(pRec->reapedCV);
+ PR_DELETE(pRec);
+ } else {
+ PR_ASSERT(_PR_PID_REAPED == pRec->state);
+ PR_ASSERT(NULL == pRec->reapedCV);
+ DeletePidTable(pRec);
+ if (exitCode) {
+ *exitCode = pRec->exitStatus;
+ }
+ PR_DELETE(pRec);
+ }
+ PR_DELETE(process);
+
+done:
+ PR_Unlock(pr_wp.ml);
+ return retVal;
+} /* _MD_WaitUnixProcess */
+
+PRStatus _MD_KillUnixProcess(PRProcess *process)
+{
+ PRErrorCode prerror;
+ PRInt32 oserror;
+
+ if (kill(process->md.pid, SIGKILL) == 0) {
+ return PR_SUCCESS;
+ }
+ oserror = errno;
+ switch (oserror) {
+ case EPERM:
+ prerror = PR_NO_ACCESS_RIGHTS_ERROR;
+ break;
+ case ESRCH:
+ prerror = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ default:
+ prerror = PR_UNKNOWN_ERROR;
+ break;
+ }
+ PR_SetError(prerror, oserror);
+ return PR_FAILURE;
+} /* _MD_KillUnixProcess */
diff --git a/nsprpub/pr/src/md/unix/uxrng.c b/nsprpub/pr/src/md/unix/uxrng.c
new file mode 100644
index 0000000000..479859000a
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/uxrng.c
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "primpl.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+
+
+#if defined(SOLARIS)
+
+static size_t
+GetHighResClock(void *buf, size_t maxbytes)
+{
+ hrtime_t t;
+ t = gethrtime();
+ if (t) {
+ return _pr_CopyLowBits(buf, maxbytes, &t, sizeof(t));
+ }
+ return 0;
+}
+
+#elif defined(HPUX)
+
+#ifdef __ia64
+#include <ia64/sys/inline.h>
+
+static size_t
+GetHighResClock(void *buf, size_t maxbytes)
+{
+ PRUint64 t;
+
+#ifdef __GNUC__
+ __asm__ __volatile__("mov %0 = ar.itc" : "=r" (t));
+#else
+ t = _Asm_mov_from_ar(_AREG44);
+#endif
+ return _pr_CopyLowBits(buf, maxbytes, &t, sizeof(t));
+}
+#else
+static size_t
+GetHighResClock(void *buf, size_t maxbytes)
+{
+ extern int ret_cr16();
+ int cr16val;
+
+ cr16val = ret_cr16();
+ return(_pr_CopyLowBits(buf, maxbytes, &cr16val, sizeof(cr16val)));
+}
+#endif
+
+#elif defined(AIX)
+
+static size_t
+GetHighResClock(void *buf, size_t maxbytes)
+{
+ return 0;
+}
+
+#elif (defined(LINUX) || defined(FREEBSD) || defined(__FreeBSD_kernel__) \
+ || defined(NETBSD) || defined(__NetBSD_kernel__) || defined(OPENBSD) \
+ || defined(__GNU__))
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static int fdDevURandom;
+static PRCallOnceType coOpenDevURandom;
+
+static PRStatus OpenDevURandom( void )
+{
+ fdDevURandom = open( "/dev/urandom", O_RDONLY );
+ return((-1 == fdDevURandom)? PR_FAILURE : PR_SUCCESS );
+} /* end OpenDevURandom() */
+
+static size_t GetDevURandom( void *buf, size_t size )
+{
+ int bytesIn;
+ int rc;
+
+ rc = PR_CallOnce( &coOpenDevURandom, OpenDevURandom );
+ if ( PR_FAILURE == rc ) {
+ _PR_MD_MAP_OPEN_ERROR( errno );
+ return(0);
+ }
+
+ bytesIn = read( fdDevURandom, buf, size );
+ if ( -1 == bytesIn ) {
+ _PR_MD_MAP_READ_ERROR( errno );
+ return(0);
+ }
+
+ return( bytesIn );
+} /* end GetDevURandom() */
+
+static size_t
+GetHighResClock(void *buf, size_t maxbytes)
+{
+ return(GetDevURandom( buf, maxbytes ));
+}
+
+#elif defined(SCO) || defined(UNIXWARE) || defined(BSDI) || defined(NTO) \
+ || defined(QNX) || defined(DARWIN) || defined(RISCOS)
+#include <sys/times.h>
+
+static size_t
+GetHighResClock(void *buf, size_t maxbytes)
+{
+ int ticks;
+ struct tms buffer;
+
+ ticks=times(&buffer);
+ return _pr_CopyLowBits(buf, maxbytes, &ticks, sizeof(ticks));
+}
+#else
+#error! Platform undefined
+#endif /* defined(SOLARIS) */
+
+extern PRSize _PR_MD_GetRandomNoise( void *buf, PRSize size )
+{
+ struct timeval tv;
+ int n = 0;
+ int s;
+
+ n += GetHighResClock(buf, size);
+ size -= n;
+
+ GETTIMEOFDAY(&tv);
+
+ if ( size > 0 ) {
+ s = _pr_CopyLowBits((char*)buf+n, size, &tv.tv_usec, sizeof(tv.tv_usec));
+ size -= s;
+ n += s;
+ }
+ if ( size > 0 ) {
+ s = _pr_CopyLowBits((char*)buf+n, size, &tv.tv_sec, sizeof(tv.tv_usec));
+ size -= s;
+ n += s;
+ }
+
+ return n;
+} /* end _PR_MD_GetRandomNoise() */
diff --git a/nsprpub/pr/src/md/unix/uxshm.c b/nsprpub/pr/src/md/unix/uxshm.c
new file mode 100644
index 0000000000..29a6030f44
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/uxshm.c
@@ -0,0 +1,632 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** uxshm.c -- Unix Implementations NSPR Named Shared Memory
+**
+**
+** lth. Jul-1999.
+**
+*/
+#include <string.h>
+#include <prshm.h>
+#include <prerr.h>
+#include <prmem.h>
+#include "primpl.h"
+#include <fcntl.h>
+
+extern PRLogModuleInfo *_pr_shm_lm;
+
+
+#define NSPR_IPC_SHM_KEY 'b'
+/*
+** Implementation for System V
+*/
+#if defined PR_HAVE_SYSV_NAMED_SHARED_MEMORY
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define _MD_OPEN_SHARED_MEMORY _MD_OpenSharedMemory
+#define _MD_ATTACH_SHARED_MEMORY _MD_AttachSharedMemory
+#define _MD_DETACH_SHARED_MEMORY _MD_DetachSharedMemory
+#define _MD_CLOSE_SHARED_MEMORY _MD_CloseSharedMemory
+#define _MD_DELETE_SHARED_MEMORY _MD_DeleteSharedMemory
+
+extern PRSharedMemory * _MD_OpenSharedMemory(
+ const char *name,
+ PRSize size,
+ PRIntn flags,
+ PRIntn mode
+)
+{
+ PRStatus rc = PR_SUCCESS;
+ key_t key;
+ PRSharedMemory *shm;
+ char ipcname[PR_IPC_NAME_SIZE];
+
+ rc = _PR_MakeNativeIPCName( name, ipcname, PR_IPC_NAME_SIZE, _PRIPCShm );
+ if ( PR_FAILURE == rc )
+ {
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_OpenSharedMemory(): _PR_MakeNativeIPCName() failed: %s", name ));
+ return( NULL );
+ }
+
+ shm = PR_NEWZAP( PRSharedMemory );
+ if ( NULL == shm )
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0 );
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: New PRSharedMemory out of memory"));
+ return( NULL );
+ }
+
+ shm->ipcname = (char*)PR_MALLOC( strlen( ipcname ) + 1 );
+ if ( NULL == shm->ipcname )
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0 );
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: New shm->ipcname out of memory"));
+ PR_DELETE( shm );
+ return( NULL );
+ }
+
+ /* copy args to struct */
+ strcpy( shm->ipcname, ipcname );
+ shm->size = size;
+ shm->mode = mode;
+ shm->flags = flags;
+ shm->ident = _PR_SHM_IDENT;
+
+ /* create the file first */
+ if ( flags & PR_SHM_CREATE ) {
+ int osfd = open( shm->ipcname, (O_RDWR | O_CREAT), shm->mode );
+ if ( -1 == osfd ) {
+ _PR_MD_MAP_OPEN_ERROR( errno );
+ PR_FREEIF( shm->ipcname );
+ PR_DELETE( shm );
+ return( NULL );
+ }
+ if ( close(osfd) == -1 ) {
+ _PR_MD_MAP_CLOSE_ERROR( errno );
+ PR_FREEIF( shm->ipcname );
+ PR_DELETE( shm );
+ return( NULL );
+ }
+ }
+
+ /* hash the shm.name to an ID */
+ key = ftok( shm->ipcname, NSPR_IPC_SHM_KEY );
+ if ( -1 == key )
+ {
+ rc = PR_FAILURE;
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_OpenSharedMemory(): ftok() failed on name: %s", shm->ipcname));
+ PR_FREEIF( shm->ipcname );
+ PR_DELETE( shm );
+ return( NULL );
+ }
+
+ /* get the shared memory */
+ if ( flags & PR_SHM_CREATE ) {
+ shm->id = shmget( key, shm->size, ( shm->mode | IPC_CREAT|IPC_EXCL));
+ if ( shm->id >= 0 ) {
+ return( shm );
+ }
+ if ((errno == EEXIST) && (flags & PR_SHM_EXCL)) {
+ PR_SetError( PR_FILE_EXISTS_ERROR, errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_OpenSharedMemory(): shmget() exclusive failed, errno: %d", errno));
+ PR_FREEIF(shm->ipcname);
+ PR_DELETE(shm);
+ return(NULL);
+ }
+ }
+
+ shm->id = shmget( key, shm->size, shm->mode );
+ if ( -1 == shm->id ) {
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_OpenSharedMemory(): shmget() failed, errno: %d", errno));
+ PR_FREEIF(shm->ipcname);
+ PR_DELETE(shm);
+ return(NULL);
+ }
+
+ return( shm );
+} /* end _MD_OpenSharedMemory() */
+
+extern void * _MD_AttachSharedMemory( PRSharedMemory *shm, PRIntn flags )
+{
+ void *addr;
+ PRUint32 aFlags = shm->mode;
+
+ PR_ASSERT( shm->ident == _PR_SHM_IDENT );
+
+ aFlags |= (flags & PR_SHM_READONLY )? SHM_RDONLY : 0;
+
+ addr = shmat( shm->id, NULL, aFlags );
+ if ( (void*)-1 == addr )
+ {
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_AttachSharedMemory(): shmat() failed on name: %s, OsError: %d",
+ shm->ipcname, PR_GetOSError() ));
+ addr = NULL;
+ }
+
+ return addr;
+}
+
+extern PRStatus _MD_DetachSharedMemory( PRSharedMemory *shm, void *addr )
+{
+ PRStatus rc = PR_SUCCESS;
+ PRIntn urc;
+
+ PR_ASSERT( shm->ident == _PR_SHM_IDENT );
+
+ urc = shmdt( addr );
+ if ( -1 == urc )
+ {
+ rc = PR_FAILURE;
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_DetachSharedMemory(): shmdt() failed on name: %s", shm->ipcname ));
+ }
+
+ return rc;
+}
+
+extern PRStatus _MD_CloseSharedMemory( PRSharedMemory *shm )
+{
+ PR_ASSERT( shm->ident == _PR_SHM_IDENT );
+
+ PR_FREEIF(shm->ipcname);
+ PR_DELETE(shm);
+
+ return PR_SUCCESS;
+}
+
+extern PRStatus _MD_DeleteSharedMemory( const char *name )
+{
+ PRStatus rc = PR_SUCCESS;
+ key_t key;
+ int id;
+ PRIntn urc;
+ char ipcname[PR_IPC_NAME_SIZE];
+
+ rc = _PR_MakeNativeIPCName( name, ipcname, PR_IPC_NAME_SIZE, _PRIPCShm );
+ if ( PR_FAILURE == rc )
+ {
+ PR_SetError( PR_UNKNOWN_ERROR, errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_DeleteSharedMemory(): _PR_MakeNativeIPCName() failed: %s", name ));
+ return(PR_FAILURE);
+ }
+
+ /* create the file first */
+ {
+ int osfd = open( ipcname, (O_RDWR | O_CREAT), 0666 );
+ if ( -1 == osfd ) {
+ _PR_MD_MAP_OPEN_ERROR( errno );
+ return( PR_FAILURE );
+ }
+ if ( close(osfd) == -1 ) {
+ _PR_MD_MAP_CLOSE_ERROR( errno );
+ return( PR_FAILURE );
+ }
+ }
+
+ /* hash the shm.name to an ID */
+ key = ftok( ipcname, NSPR_IPC_SHM_KEY );
+ if ( -1 == key )
+ {
+ rc = PR_FAILURE;
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_DeleteSharedMemory(): ftok() failed on name: %s", ipcname));
+ }
+
+ id = shmget( key, 0, 0 );
+ if ( -1 == id ) {
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_DeleteSharedMemory(): shmget() failed, errno: %d", errno));
+ return(PR_FAILURE);
+ }
+
+ urc = shmctl( id, IPC_RMID, NULL );
+ if ( -1 == urc )
+ {
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_DeleteSharedMemory(): shmctl() failed on name: %s", ipcname ));
+ return(PR_FAILURE);
+ }
+
+ urc = unlink( ipcname );
+ if ( -1 == urc ) {
+ _PR_MD_MAP_UNLINK_ERROR( errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_DeleteSharedMemory(): unlink() failed: %s", ipcname ));
+ return(PR_FAILURE);
+ }
+
+ return rc;
+} /* end _MD_DeleteSharedMemory() */
+
+/*
+** Implementation for Posix
+*/
+#elif defined PR_HAVE_POSIX_NAMED_SHARED_MEMORY
+#include <sys/mman.h>
+
+#define _MD_OPEN_SHARED_MEMORY _MD_OpenSharedMemory
+#define _MD_ATTACH_SHARED_MEMORY _MD_AttachSharedMemory
+#define _MD_DETACH_SHARED_MEMORY _MD_DetachSharedMemory
+#define _MD_CLOSE_SHARED_MEMORY _MD_CloseSharedMemory
+#define _MD_DELETE_SHARED_MEMORY _MD_DeleteSharedMemory
+
+struct _MDSharedMemory {
+ int handle;
+};
+
+extern PRSharedMemory * _MD_OpenSharedMemory(
+ const char *name,
+ PRSize size,
+ PRIntn flags,
+ PRIntn mode
+)
+{
+ PRStatus rc = PR_SUCCESS;
+ PRInt32 end;
+ PRSharedMemory *shm;
+ char ipcname[PR_IPC_NAME_SIZE];
+
+ rc = _PR_MakeNativeIPCName( name, ipcname, PR_IPC_NAME_SIZE, _PRIPCShm );
+ if ( PR_FAILURE == rc )
+ {
+ PR_SetError( PR_UNKNOWN_ERROR, errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_OpenSharedMemory(): _PR_MakeNativeIPCName() failed: %s", name ));
+ return( NULL );
+ }
+
+ shm = PR_NEWZAP( PRSharedMemory );
+ if ( NULL == shm )
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0 );
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: New PRSharedMemory out of memory"));
+ return( NULL );
+ }
+
+ shm->ipcname = PR_MALLOC( strlen( ipcname ) + 1 );
+ if ( NULL == shm->ipcname )
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0 );
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: New shm->ipcname out of memory"));
+ return( NULL );
+ }
+
+ /* copy args to struct */
+ strcpy( shm->ipcname, ipcname );
+ shm->size = size;
+ shm->mode = mode;
+ shm->flags = flags;
+ shm->ident = _PR_SHM_IDENT;
+
+ /*
+ ** Create the shared memory
+ */
+ if ( flags & PR_SHM_CREATE ) {
+ int oflag = (O_CREAT | O_RDWR);
+
+ if ( flags & PR_SHM_EXCL ) {
+ oflag |= O_EXCL;
+ }
+ shm->id = shm_open( shm->ipcname, oflag, shm->mode );
+ } else {
+ shm->id = shm_open( shm->ipcname, O_RDWR, shm->mode );
+ }
+
+ if ( -1 == shm->id ) {
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_OpenSharedMemory(): shm_open failed: %s, OSError: %d",
+ shm->ipcname, PR_GetOSError()));
+ PR_DELETE( shm->ipcname );
+ PR_DELETE( shm );
+ return(NULL);
+ }
+
+ end = ftruncate( shm->id, shm->size );
+ if ( -1 == end ) {
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_OpenSharedMemory(): ftruncate failed, OSError: %d",
+ PR_GetOSError()));
+ PR_DELETE( shm->ipcname );
+ PR_DELETE( shm );
+ return(NULL);
+ }
+
+ return(shm);
+} /* end _MD_OpenSharedMemory() */
+
+extern void * _MD_AttachSharedMemory( PRSharedMemory *shm, PRIntn flags )
+{
+ void *addr;
+ PRIntn prot = (PROT_READ | PROT_WRITE);
+
+ PR_ASSERT( shm->ident == _PR_SHM_IDENT );
+
+ if ( PR_SHM_READONLY == flags) {
+ prot ^= PROT_WRITE;
+ }
+
+ addr = mmap( (void*)0, shm->size, prot, MAP_SHARED, shm->id, 0 );
+ if ((void*)-1 == addr )
+ {
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_AttachSharedMemory(): mmap failed: %s, errno: %d",
+ shm->ipcname, PR_GetOSError()));
+ addr = NULL;
+ } else {
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_AttachSharedMemory(): name: %s, attached at: %p", shm->ipcname, addr));
+ }
+
+ return addr;
+}
+
+extern PRStatus _MD_DetachSharedMemory( PRSharedMemory *shm, void *addr )
+{
+ PRStatus rc = PR_SUCCESS;
+ PRIntn urc;
+
+ PR_ASSERT( shm->ident == _PR_SHM_IDENT );
+
+ urc = munmap( addr, shm->size );
+ if ( -1 == urc )
+ {
+ rc = PR_FAILURE;
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_DetachSharedMemory(): munmap failed: %s, errno: %d",
+ shm->ipcname, PR_GetOSError()));
+ }
+ return rc;
+}
+
+extern PRStatus _MD_CloseSharedMemory( PRSharedMemory *shm )
+{
+ int urc;
+
+ PR_ASSERT( shm->ident == _PR_SHM_IDENT );
+
+ urc = close( shm->id );
+ if ( -1 == urc ) {
+ _PR_MD_MAP_CLOSE_ERROR( errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_CloseSharedMemory(): close() failed, error: %d", PR_GetOSError()));
+ return(PR_FAILURE);
+ }
+ PR_DELETE( shm->ipcname );
+ PR_DELETE( shm );
+ return PR_SUCCESS;
+}
+
+extern PRStatus _MD_DeleteSharedMemory( const char *name )
+{
+ PRStatus rc = PR_SUCCESS;
+ PRUintn urc;
+ char ipcname[PR_IPC_NAME_SIZE];
+
+ rc = _PR_MakeNativeIPCName( name, ipcname, PR_IPC_NAME_SIZE, _PRIPCShm );
+ if ( PR_FAILURE == rc )
+ {
+ PR_SetError( PR_UNKNOWN_ERROR, errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_OpenSharedMemory(): _PR_MakeNativeIPCName() failed: %s", name ));
+ return rc;
+ }
+
+ urc = shm_unlink( ipcname );
+ if ( -1 == urc ) {
+ rc = PR_FAILURE;
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_DeleteSharedMemory(): shm_unlink failed: %s, errno: %d",
+ ipcname, PR_GetOSError()));
+ } else {
+ PR_LOG( _pr_shm_lm, PR_LOG_DEBUG,
+ ("_MD_DeleteSharedMemory(): %s, success", ipcname));
+ }
+
+ return rc;
+} /* end _MD_DeleteSharedMemory() */
+#endif
+
+
+
+/*
+** Unix implementation for anonymous memory (file) mapping
+*/
+extern PRLogModuleInfo *_pr_shma_lm;
+
+#include <unistd.h>
+
+extern PRFileMap* _md_OpenAnonFileMap(
+ const char *dirName,
+ PRSize size,
+ PRFileMapProtect prot
+)
+{
+ PRFileMap *fm = NULL;
+ PRFileDesc *fd;
+ int osfd;
+ PRIntn urc;
+ PRIntn mode = 0600;
+ char *genName;
+ pid_t pid = getpid(); /* for generating filename */
+ PRThread *tid = PR_GetCurrentThread(); /* for generating filename */
+ int incr; /* for generating filename */
+ const int maxTries = 20; /* maximum # attempts at a unique filename */
+ PRInt64 size64; /* 64-bit version of 'size' */
+
+ /*
+ ** generate a filename from input and runtime environment
+ ** open the file, unlink the file.
+ ** make maxTries number of attempts at uniqueness in the filename
+ */
+ for ( incr = 0; incr < maxTries ; incr++ ) {
+#define NSPR_AFM_FILENAME "%s/.NSPR-AFM-%d-%p.%d"
+ genName = PR_smprintf( NSPR_AFM_FILENAME,
+ dirName, (int) pid, tid, incr );
+ if ( NULL == genName ) {
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_OpenAnonFileMap(): PR_snprintf(): failed, generating filename"));
+ goto Finished;
+ }
+
+ /* create the file */
+ osfd = open(genName, (O_CREAT | O_EXCL | O_RDWR), mode);
+ if (-1 == osfd) {
+ if (EEXIST == errno) {
+ PR_smprintf_free(genName);
+ continue; /* name exists, try again */
+ }
+ _PR_MD_MAP_OPEN_ERROR(errno);
+ PR_LOG(
+ _pr_shma_lm,
+ PR_LOG_DEBUG,
+ ("_md_OpenAnonFileMap(): open(): failed, filename: %s, errno: %d",
+ genName,
+ PR_GetOSError()));
+ PR_smprintf_free(genName);
+ goto Finished;
+ }
+ break; /* name generation and open successful, break; */
+ } /* end for() */
+
+ if (incr == maxTries) {
+ PR_ASSERT(-1 == osfd);
+ PR_ASSERT(EEXIST == errno);
+ _PR_MD_MAP_OPEN_ERROR(errno);
+ goto Finished;
+ }
+
+ urc = unlink( genName );
+ if ( -1 == urc ) {
+ _PR_MD_MAP_UNLINK_ERROR( errno );
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_OpenAnonFileMap(): failed on unlink(), errno: %d", errno));
+ PR_smprintf_free( genName );
+ close( osfd );
+ goto Finished;
+ }
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_OpenAnonFileMap(): unlink(): %s", genName ));
+
+ PR_smprintf_free( genName );
+
+ fd = PR_ImportFile( osfd );
+ if ( NULL == fd ) {
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_OpenAnonFileMap(): PR_ImportFile(): failed"));
+ goto Finished;
+ }
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_OpenAnonFileMap(): fd: %p", fd ));
+
+ urc = ftruncate( fd->secret->md.osfd, size );
+ if ( -1 == urc ) {
+ _PR_MD_MAP_DEFAULT_ERROR( errno );
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_OpenAnonFileMap(): failed on ftruncate(), errno: %d", errno));
+ PR_Close( fd );
+ goto Finished;
+ }
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_OpenAnonFileMap(): ftruncate(): size: %d", size ));
+
+ LL_UI2L(size64, size); /* PRSize (size_t) is unsigned */
+ fm = PR_CreateFileMap( fd, size64, prot );
+ if ( NULL == fm ) {
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("PR_OpenAnonFileMap(): failed"));
+ PR_Close( fd );
+ goto Finished;
+ }
+ fm->md.isAnonFM = PR_TRUE; /* set fd close */
+
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_OpenAnonFileMap(): PR_CreateFileMap(): fm: %p", fm ));
+
+Finished:
+ return(fm);
+} /* end md_OpenAnonFileMap() */
+
+/*
+** _md_ExportFileMapAsString()
+**
+**
+*/
+extern PRStatus _md_ExportFileMapAsString(
+ PRFileMap *fm,
+ PRSize bufSize,
+ char *buf
+)
+{
+ PRIntn written;
+ PRIntn prot = (PRIntn)fm->prot;
+
+ written = PR_snprintf( buf, bufSize, "%ld:%d",
+ fm->fd->secret->md.osfd, prot );
+
+ return((written == -1)? PR_FAILURE : PR_SUCCESS);
+} /* end _md_ExportFileMapAsString() */
+
+
+extern PRFileMap * _md_ImportFileMapFromString(
+ const char *fmstring
+)
+{
+ PRStatus rc;
+ PRInt32 osfd;
+ PRIntn prot; /* really: a PRFileMapProtect */
+ PRFileDesc *fd;
+ PRFileMap *fm = NULL; /* default return value */
+ PRFileInfo64 info;
+
+ PR_sscanf( fmstring, "%ld:%d", &osfd, &prot );
+
+ /* import the os file descriptor */
+ fd = PR_ImportFile( osfd );
+ if ( NULL == fd ) {
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_ImportFileMapFromString(): PR_ImportFile() failed"));
+ goto Finished;
+ }
+
+ rc = PR_GetOpenFileInfo64( fd, &info );
+ if ( PR_FAILURE == rc ) {
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_ImportFileMapFromString(): PR_GetOpenFileInfo64() failed"));
+ goto Finished;
+ }
+
+ fm = PR_CreateFileMap( fd, info.size, (PRFileMapProtect)prot );
+ if ( NULL == fm ) {
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_ImportFileMapFromString(): PR_CreateFileMap() failed"));
+ }
+
+Finished:
+ return(fm);
+} /* end _md_ImportFileMapFromString() */
diff --git a/nsprpub/pr/src/md/unix/uxwrap.c b/nsprpub/pr/src/md/unix/uxwrap.c
new file mode 100644
index 0000000000..e0ddc50eb8
--- /dev/null
+++ b/nsprpub/pr/src/md/unix/uxwrap.c
@@ -0,0 +1,512 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *------------------------------------------------------------------------
+ * File: uxwrap.c
+ *
+ * Our wrapped versions of the Unix select() and poll() system calls.
+ *
+ *------------------------------------------------------------------------
+ */
+
+#include "primpl.h"
+
+#if defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) || defined(QNX)
+/* Do not wrap select() and poll(). */
+#else /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */
+/* The include files for select() */
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#define ZAP_SET(_to, _width) \
+ PR_BEGIN_MACRO \
+ memset(_to, 0, \
+ ((_width + 8*sizeof(int)-1) / (8*sizeof(int))) \
+ * sizeof(int) \
+ ); \
+ PR_END_MACRO
+
+/* see comments in ns/cmd/xfe/mozilla.c (look for "PR_XGetXtHackFD") */
+static int _pr_xt_hack_fd = -1;
+
+int PR_XGetXtHackFD(void)
+{
+ int fds[2];
+
+ if (_pr_xt_hack_fd == -1) {
+ if (!pipe(fds)) {
+ _pr_xt_hack_fd = fds[0];
+ }
+ }
+ return _pr_xt_hack_fd;
+}
+
+static int (*_pr_xt_hack_okayToReleaseXLock)(void) = 0;
+
+void PR_SetXtHackOkayToReleaseXLockFn(int (*fn)(void))
+{
+ _pr_xt_hack_okayToReleaseXLock = fn;
+}
+
+
+/*
+ *-----------------------------------------------------------------------
+ * select() --
+ *
+ * Wrap up the select system call so that we can deschedule
+ * a thread that tries to wait for i/o.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+#if defined(HPUX9)
+int select(size_t width, int *rl, int *wl, int *el, const struct timeval *tv)
+#elif defined(AIX_RENAME_SELECT)
+int wrap_select(unsigned long width, void *rl, void *wl, void *el,
+ struct timeval *tv)
+#elif defined(_PR_SELECT_CONST_TIMEVAL)
+int select(int width, fd_set *rd, fd_set *wr, fd_set *ex,
+ const struct timeval *tv)
+#else
+int select(int width, fd_set *rd, fd_set *wr, fd_set *ex, struct timeval *tv)
+#endif
+{
+ int osfd;
+ _PRUnixPollDesc *unixpds, *unixpd, *eunixpd;
+ PRInt32 pdcnt;
+ PRIntervalTime timeout;
+ int retVal;
+#if defined(HPUX9) || defined(AIX_RENAME_SELECT)
+ fd_set *rd = (fd_set*) rl;
+ fd_set *wr = (fd_set*) wl;
+ fd_set *ex = (fd_set*) el;
+#endif
+
+#if 0
+ /*
+ * Easy special case: zero timeout. Simply call the native
+ * select() with no fear of blocking.
+ */
+ if (tv != NULL && tv->tv_sec == 0 && tv->tv_usec == 0) {
+#if defined(HPUX9) || defined(AIX_RENAME_SELECT)
+ return _MD_SELECT(width, rl, wl, el, tv);
+#else
+ return _MD_SELECT(width, rd, wr, ex, tv);
+#endif
+ }
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+#ifndef _PR_LOCAL_THREADS_ONLY
+ if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) {
+ return _MD_SELECT(width, rd, wr, ex, tv);
+ }
+#endif
+
+ if (width < 0 || width > FD_SETSIZE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Compute timeout */
+ if (tv) {
+ /*
+ * These acceptable ranges for t_sec and t_usec are taken
+ * from the select() man pages.
+ */
+ if (tv->tv_sec < 0 || tv->tv_sec > 100000000
+ || tv->tv_usec < 0 || tv->tv_usec >= 1000000) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Convert microseconds to ticks */
+ timeout = PR_MicrosecondsToInterval(1000000*tv->tv_sec + tv->tv_usec);
+ } else {
+ /* tv being a NULL pointer means blocking indefinitely */
+ timeout = PR_INTERVAL_NO_TIMEOUT;
+ }
+
+ /* Check for no descriptors case (just doing a timeout) */
+ if ((!rd && !wr && !ex) || !width) {
+ PR_Sleep(timeout);
+ return 0;
+ }
+
+ /*
+ * Set up for PR_Poll(). The PRPollDesc array is allocated
+ * dynamically. If this turns out to have high performance
+ * penalty, one can change to use a large PRPollDesc array
+ * on the stack, and allocate dynamically only when it turns
+ * out to be not large enough.
+ *
+ * I allocate an array of size 'width', which is the maximum
+ * number of fds we may need to poll.
+ */
+ unixpds = (_PRUnixPollDesc *) PR_CALLOC(width * sizeof(_PRUnixPollDesc));
+ if (!unixpds) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ pdcnt = 0;
+ unixpd = unixpds;
+ for (osfd = 0; osfd < width; osfd++) {
+ int in_flags = 0;
+ if (rd && FD_ISSET(osfd, rd)) {
+ in_flags |= _PR_UNIX_POLL_READ;
+ }
+ if (wr && FD_ISSET(osfd, wr)) {
+ in_flags |= _PR_UNIX_POLL_WRITE;
+ }
+ if (ex && FD_ISSET(osfd, ex)) {
+ in_flags |= _PR_UNIX_POLL_EXCEPT;
+ }
+ if (in_flags) {
+ unixpd->osfd = osfd;
+ unixpd->in_flags = in_flags;
+ unixpd->out_flags = 0;
+ unixpd++;
+ pdcnt++;
+ }
+ }
+
+ /*
+ * see comments in mozilla/cmd/xfe/mozilla.c (look for
+ * "PR_XGetXtHackFD")
+ */
+ {
+ int needToLockXAgain;
+
+ needToLockXAgain = 0;
+ if (rd && (_pr_xt_hack_fd != -1)
+ && FD_ISSET(_pr_xt_hack_fd, rd) && _PR_XIsLocked()
+ && (!_pr_xt_hack_okayToReleaseXLock
+ || _pr_xt_hack_okayToReleaseXLock())) {
+ _PR_XUnlock();
+ needToLockXAgain = 1;
+ }
+
+ /* This is the potentially blocking step */
+ retVal = _PR_WaitForMultipleFDs(unixpds, pdcnt, timeout);
+
+ if (needToLockXAgain) {
+ _PR_XLock();
+ }
+ }
+
+ if (retVal > 0) {
+ /* Compute select results */
+ if (rd) {
+ ZAP_SET(rd, width);
+ }
+ if (wr) {
+ ZAP_SET(wr, width);
+ }
+ if (ex) {
+ ZAP_SET(ex, width);
+ }
+
+ /*
+ * The return value can be either the number of ready file
+ * descriptors or the number of set bits in the three fd_set's.
+ */
+ retVal = 0; /* we're going to recompute */
+ eunixpd = unixpds + pdcnt;
+ for (unixpd = unixpds; unixpd < eunixpd; unixpd++) {
+ if (unixpd->out_flags) {
+ int nbits = 0; /* The number of set bits on for this fd */
+
+ if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) {
+ errno = EBADF;
+ PR_LOG(_pr_io_lm, PR_LOG_ERROR,
+ ("select returns EBADF for %d", unixpd->osfd));
+ retVal = -1;
+ break;
+ }
+ /*
+ * If a socket has a pending error, it is considered
+ * both readable and writable. (See W. Richard Stevens,
+ * Unix Network Programming, Vol. 1, 2nd Ed., Section 6.3,
+ * pp. 153-154.) We also consider a socket readable if
+ * it has a hangup condition.
+ */
+ if (rd && (unixpd->in_flags & _PR_UNIX_POLL_READ)
+ && (unixpd->out_flags & (_PR_UNIX_POLL_READ
+ | _PR_UNIX_POLL_ERR | _PR_UNIX_POLL_HUP))) {
+ FD_SET(unixpd->osfd, rd);
+ nbits++;
+ }
+ if (wr && (unixpd->in_flags & _PR_UNIX_POLL_WRITE)
+ && (unixpd->out_flags & (_PR_UNIX_POLL_WRITE
+ | _PR_UNIX_POLL_ERR))) {
+ FD_SET(unixpd->osfd, wr);
+ nbits++;
+ }
+ if (ex && (unixpd->in_flags & _PR_UNIX_POLL_WRITE)
+ && (unixpd->out_flags & PR_POLL_EXCEPT)) {
+ FD_SET(unixpd->osfd, ex);
+ nbits++;
+ }
+ PR_ASSERT(nbits > 0);
+#if defined(HPUX) || defined(SOLARIS) || defined(AIX)
+ retVal += nbits;
+#endif
+ }
+ }
+ }
+
+ PR_ASSERT(tv || retVal != 0);
+ PR_LOG(_pr_io_lm, PR_LOG_MIN, ("select returns %d", retVal));
+ PR_DELETE(unixpds);
+
+ return retVal;
+}
+
+/*
+ * Redefine poll, when supported on platforms, for local threads
+ */
+
+/*
+ * I am commenting out the poll() wrapper for Linux for now
+ * because it is difficult to define _MD_POLL that works on all
+ * Linux varieties. People reported that glibc 2.0.7 on Debian
+ * 2.0 Linux machines doesn't have the __syscall_poll symbol
+ * defined. (WTC 30 Nov. 1998)
+ */
+#if defined(_PR_POLL_AVAILABLE) && !defined(LINUX)
+
+/*
+ *-----------------------------------------------------------------------
+ * poll() --
+ *
+ * RETURN VALUES:
+ * -1: fails, errno indicates the error.
+ * 0: timed out, the revents bitmasks are not set.
+ * positive value: the number of file descriptors for which poll()
+ * has set the revents bitmask.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+#include <poll.h>
+
+#if defined(AIX_RENAME_SELECT)
+int wrap_poll(void *listptr, unsigned long nfds, long timeout)
+#elif (defined(AIX) && !defined(AIX_RENAME_SELECT))
+int poll(void *listptr, unsigned long nfds, long timeout)
+#elif defined(HPUX) && !defined(HPUX9)
+int poll(struct pollfd filedes[], unsigned int nfds, int timeout)
+#elif defined(HPUX9)
+int poll(struct pollfd filedes[], int nfds, int timeout)
+#elif defined(NETBSD)
+int poll(struct pollfd *filedes, nfds_t nfds, int timeout)
+#elif defined(OPENBSD)
+int poll(struct pollfd filedes[], nfds_t nfds, int timeout)
+#elif defined(FREEBSD)
+int poll(struct pollfd *filedes, unsigned nfds, int timeout)
+#else
+int poll(struct pollfd *filedes, unsigned long nfds, int timeout)
+#endif
+{
+#ifdef AIX
+ struct pollfd *filedes = (struct pollfd *) listptr;
+#endif
+ struct pollfd *pfd, *epfd;
+ _PRUnixPollDesc *unixpds, *unixpd, *eunixpd;
+ PRIntervalTime ticks;
+ PRInt32 pdcnt;
+ int ready;
+
+ /*
+ * Easy special case: zero timeout. Simply call the native
+ * poll() with no fear of blocking.
+ */
+ if (timeout == 0) {
+#if defined(AIX)
+ return _MD_POLL(listptr, nfds, timeout);
+#else
+ return _MD_POLL(filedes, nfds, timeout);
+#endif
+ }
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+#ifndef _PR_LOCAL_THREADS_ONLY
+ if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) {
+ return _MD_POLL(filedes, nfds, timeout);
+ }
+#endif
+
+ /* We do not support the pollmsg structures on AIX */
+#ifdef AIX
+ PR_ASSERT((nfds & 0xff00) == 0);
+#endif
+
+ if (timeout < 0 && timeout != -1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Convert timeout from miliseconds to ticks */
+ if (timeout == -1) {
+ ticks = PR_INTERVAL_NO_TIMEOUT;
+ } else {
+ ticks = PR_MillisecondsToInterval(timeout);
+ }
+
+ /* Check for no descriptor case (just do a timeout) */
+ if (nfds == 0) {
+ PR_Sleep(ticks);
+ return 0;
+ }
+
+ unixpds = (_PRUnixPollDesc *)
+ PR_MALLOC(nfds * sizeof(_PRUnixPollDesc));
+ if (NULL == unixpds) {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ pdcnt = 0;
+ epfd = filedes + nfds;
+ unixpd = unixpds;
+ for (pfd = filedes; pfd < epfd; pfd++) {
+ /*
+ * poll() ignores negative fd's.
+ */
+ if (pfd->fd >= 0) {
+ unixpd->osfd = pfd->fd;
+#ifdef _PR_USE_POLL
+ unixpd->in_flags = pfd->events;
+#else
+ /*
+ * Map the poll events to one of the three that can be
+ * represented by the select fd_sets:
+ * POLLIN, POLLRDNORM ===> readable
+ * POLLOUT, POLLWRNORM ===> writable
+ * POLLPRI, POLLRDBAND ===> exception
+ * POLLNORM, POLLWRBAND (and POLLMSG on some platforms)
+ * are ignored.
+ *
+ * The output events POLLERR and POLLHUP are never turned on.
+ * POLLNVAL may be turned on.
+ */
+ unixpd->in_flags = 0;
+ if (pfd->events & (POLLIN
+#ifdef POLLRDNORM
+ | POLLRDNORM
+#endif
+ )) {
+ unixpd->in_flags |= _PR_UNIX_POLL_READ;
+ }
+ if (pfd->events & (POLLOUT
+#ifdef POLLWRNORM
+ | POLLWRNORM
+#endif
+ )) {
+ unixpd->in_flags |= _PR_UNIX_POLL_WRITE;
+ }
+ if (pfd->events & (POLLPRI
+#ifdef POLLRDBAND
+ | POLLRDBAND
+#endif
+ )) {
+ unixpd->in_flags |= PR_POLL_EXCEPT;
+ }
+#endif /* _PR_USE_POLL */
+ unixpd->out_flags = 0;
+ unixpd++;
+ pdcnt++;
+ }
+ }
+
+ ready = _PR_WaitForMultipleFDs(unixpds, pdcnt, ticks);
+ if (-1 == ready) {
+ if (PR_GetError() == PR_PENDING_INTERRUPT_ERROR) {
+ errno = EINTR; /* XXX we aren't interrupted by a signal, but... */
+ } else {
+ errno = PR_GetOSError();
+ }
+ }
+ if (ready <= 0) {
+ goto done;
+ }
+
+ /*
+ * Copy the out_flags from the _PRUnixPollDesc structures to the
+ * user's pollfd structures and free the allocated memory
+ */
+ unixpd = unixpds;
+ for (pfd = filedes; pfd < epfd; pfd++) {
+ pfd->revents = 0;
+ if (pfd->fd >= 0) {
+#ifdef _PR_USE_POLL
+ pfd->revents = unixpd->out_flags;
+#else
+ if (0 != unixpd->out_flags) {
+ if (unixpd->out_flags & _PR_UNIX_POLL_READ) {
+ if (pfd->events & POLLIN) {
+ pfd->revents |= POLLIN;
+ }
+#ifdef POLLRDNORM
+ if (pfd->events & POLLRDNORM) {
+ pfd->revents |= POLLRDNORM;
+ }
+#endif
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_WRITE) {
+ if (pfd->events & POLLOUT) {
+ pfd->revents |= POLLOUT;
+ }
+#ifdef POLLWRNORM
+ if (pfd->events & POLLWRNORM) {
+ pfd->revents |= POLLWRNORM;
+ }
+#endif
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_EXCEPT) {
+ if (pfd->events & POLLPRI) {
+ pfd->revents |= POLLPRI;
+ }
+#ifdef POLLRDBAND
+ if (pfd->events & POLLRDBAND) {
+ pfd->revents |= POLLRDBAND;
+ }
+#endif
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_ERR) {
+ pfd->revents |= POLLERR;
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) {
+ pfd->revents |= POLLNVAL;
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_HUP) {
+ pfd->revents |= POLLHUP;
+ }
+ }
+#endif /* _PR_USE_POLL */
+ unixpd++;
+ }
+ }
+
+done:
+ PR_DELETE(unixpds);
+ return ready;
+}
+
+#endif /* !defined(LINUX) */
+
+#endif /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */
+
+/* uxwrap.c */
+
diff --git a/nsprpub/pr/src/md/windows/Makefile.in b/nsprpub/pr/src/md/windows/Makefile.in
new file mode 100644
index 0000000000..04bd716d9a
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/Makefile.in
@@ -0,0 +1,72 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+ifeq (,$(filter-out WIN95 WINCE WINMO, $(OS_TARGET)))
+CSRCS = \
+ ntmisc.c \
+ ntsec.c \
+ ntsem.c \
+ ntinrval.c \
+ ntgc.c \
+ w95thred.c \
+ w95io.c \
+ w95cv.c \
+ w32rng.c \
+ w95sock.c \
+ win32_errors.c \
+ w32ipcsem.c \
+ w32poll.c \
+ w32shm.c \
+ w95dllmain.c \
+ $(NULL)
+else
+CSRCS = \
+ ntdllmn.c \
+ ntmisc.c \
+ ntsec.c \
+ ntsem.c \
+ ntinrval.c \
+ ntgc.c \
+ ntthread.c \
+ ntio.c \
+ win32_errors.c \
+ w32ipcsem.c \
+ w32poll.c \
+ w32rng.c \
+ w32shm.c \
+ $(NULL)
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
+# Bug 122433 workaround: disable global optimization (-Og-) on ntio.c.
+ifdef MOZ_OPTIMIZE
+ifeq ($(OS_TARGET), WINNT)
+ifndef NS_USE_GCC
+$(OBJDIR)/ntio.$(OBJ_SUFFIX): ntio.c
+ @$(MAKE_OBJDIR)
+ $(CC) -Fo$@ -c $(CFLAGS) -Og- $<
+endif
+endif
+endif
diff --git a/nsprpub/pr/src/md/windows/ntdllmn.c b/nsprpub/pr/src/md/windows/ntdllmn.c
new file mode 100644
index 0000000000..e64b312d09
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/ntdllmn.c
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * The DLL entry point (DllMain) for NSPR.
+ *
+ * The only reason we use DLLMain() now is to find out whether
+ * the NSPR DLL is statically or dynamically loaded. When
+ * dynamically loaded, we cannot use static thread-local storage.
+ * However, static TLS is faster than the TlsXXX() functions.
+ * So we want to use static TLS whenever we can. A global
+ * variable _pr_use_static_tls is set in DllMain() during process
+ * attachment to indicate whether it is safe to use static TLS
+ * or not.
+ */
+
+#include <windows.h>
+#include <primpl.h>
+
+extern BOOL _pr_use_static_tls; /* defined in ntthread.c */
+
+BOOL WINAPI DllMain(
+ HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved)
+{
+ PRThread *me;
+
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ /*
+ * If lpvReserved is NULL, we are dynamically loaded
+ * and therefore can't use static thread-local storage.
+ */
+ if (lpvReserved == NULL) {
+ _pr_use_static_tls = FALSE;
+ } else {
+ _pr_use_static_tls = TRUE;
+ }
+ break;
+ case DLL_THREAD_ATTACH:
+ break;
+ case DLL_THREAD_DETACH:
+ if (_pr_initialized) {
+ me = _MD_GET_ATTACHED_THREAD();
+ if ((me != NULL) && (me->flags & _PR_ATTACHED)) {
+ _PRI_DetachThread();
+ }
+ }
+ break;
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
diff --git a/nsprpub/pr/src/md/windows/ntgc.c b/nsprpub/pr/src/md/windows/ntgc.c
new file mode 100644
index 0000000000..20ad8a81e4
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/ntgc.c
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * GC related routines
+ *
+ */
+#include <windows.h>
+#include "primpl.h"
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#if defined(_X86_)
+ CONTEXT context;
+ context.ContextFlags = CONTEXT_INTEGER;
+
+ if (_PR_IS_NATIVE_THREAD(t)) {
+ context.ContextFlags |= CONTEXT_CONTROL;
+ if (GetThreadContext(t->md.handle, &context)) {
+ t->md.gcContext[0] = context.Eax;
+ t->md.gcContext[1] = context.Ebx;
+ t->md.gcContext[2] = context.Ecx;
+ t->md.gcContext[3] = context.Edx;
+ t->md.gcContext[4] = context.Esi;
+ t->md.gcContext[5] = context.Edi;
+ t->md.gcContext[6] = context.Esp;
+ t->md.gcContext[7] = context.Ebp;
+ *np = PR_NUM_GCREGS;
+ } else {
+ PR_ASSERT(0);/* XXX */
+ }
+ } else {
+ /* WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ *
+ * This code is extremely machine dependant and completely
+ * undocumented by MS. Its only known to work experimentally.
+ * Ready for a walk on the wild * side?
+ *
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING */
+
+#if !defined WIN95 // Win95 does not have fibers
+ int *fiberData = t->md.fiber_id;
+
+ /* I found these offsets by disassembling SwitchToFiber().
+ * Are your palms sweating yet?
+ */
+
+ /*
+ ** EAX is on the stack (ESP+0)
+ ** EDX is on the stack (ESP+4)
+ ** ECX is on the stack (ESP+8)
+ */
+ t->md.gcContext[0] = 0; /* context.Eax */
+ t->md.gcContext[1] = fiberData[0x2e]; /* context.Ebx */
+ t->md.gcContext[2] = 0; /* context.Ecx */
+ t->md.gcContext[3] = 0; /* context.Edx */
+ t->md.gcContext[4] = fiberData[0x2d]; /* context.Esi */
+ t->md.gcContext[5] = fiberData[0x2c]; /* context.Edi */
+ t->md.gcContext[6] = fiberData[0x36]; /* context.Esp */
+ t->md.gcContext[7] = fiberData[0x32]; /* context.Ebp */
+ *np = PR_NUM_GCREGS;
+#endif
+ }
+ return (PRWord *)&t->md.gcContext;
+#else
+ PR_NOT_REACHED("not implemented");
+ return NULL;
+#endif /* defined(_X86_) */
+}
+
+/* This function is not used right now, but is left as a reference.
+ * If you ever need to get the fiberID from the currently running fiber,
+ * this is it.
+ */
+void *
+GetMyFiberID()
+{
+#if defined(_X86_) && !defined(__MINGW32__)
+ void *fiberData;
+
+ /* A pointer to our tib entry is found at FS:[18]
+ * At offset 10h is the fiberData pointer. The context of the
+ * fiber is stored in there.
+ */
+ __asm {
+ mov EDX, FS:[18h]
+ mov EAX, DWORD PTR [EDX+10h]
+ mov [fiberData], EAX
+ }
+
+ return fiberData;
+#else
+ PR_NOT_REACHED("not implemented");
+ return NULL;
+#endif /* defined(_X86_) */
+}
diff --git a/nsprpub/pr/src/md/windows/ntinrval.c b/nsprpub/pr/src/md/windows/ntinrval.c
new file mode 100644
index 0000000000..a447804558
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/ntinrval.c
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * NT interval timers
+ *
+ */
+
+/* Mozilla's build system defines this globally. */
+#ifdef WIN32_LEAN_AND_MEAN
+#undef WIN32_LEAN_AND_MEAN
+#endif
+#include "primpl.h"
+
+#ifdef WINCE
+typedef DWORD (*IntervalFuncType)(void);
+static IntervalFuncType intervalFunc;
+#endif
+
+void
+_PR_MD_INTERVAL_INIT()
+{
+#ifdef WINCE
+ HMODULE mmtimerlib = LoadLibraryW(L"mmtimer.dll"); /* XXX leaked! */
+ if (mmtimerlib) {
+ intervalFunc = (IntervalFuncType)GetProcAddress(mmtimerlib,
+ "timeGetTime");
+ } else {
+ intervalFunc = &GetTickCount;
+ }
+#endif
+}
+
+PRIntervalTime
+_PR_MD_GET_INTERVAL()
+{
+ /* milliseconds since system start */
+#ifdef WINCE
+ return (*intervalFunc)();
+#else
+ return timeGetTime();
+#endif
+}
+
+PRIntervalTime
+_PR_MD_INTERVAL_PER_SEC()
+{
+ return 1000;
+}
diff --git a/nsprpub/pr/src/md/windows/ntio.c b/nsprpub/pr/src/md/windows/ntio.c
new file mode 100644
index 0000000000..40f5200789
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/ntio.c
@@ -0,0 +1,4761 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Windows NT IO module
+ *
+ * This module handles IO for LOCAL_SCOPE and GLOBAL_SCOPE threads.
+ * For LOCAL_SCOPE threads, we're using NT fibers. For GLOBAL_SCOPE threads
+ * we're using NT-native threads.
+ *
+ * When doing IO, we want to use completion ports for optimal performance
+ * with fibers. But if we use completion ports for all IO, it is difficult
+ * to project a blocking model with GLOBAL_SCOPE threads. To handle this
+ * we create an extra thread for completing IO for GLOBAL_SCOPE threads.
+ * We don't really want to complete IO on a separate thread for LOCAL_SCOPE
+ * threads because it means extra context switches, which are really slow
+ * on NT... Since we're using a single completion port, some IO will
+ * be incorrectly completed on the GLOBAL_SCOPE IO thread; this will mean
+ * extra context switching; but I don't think there is anything I can do
+ * about it.
+ */
+
+#include "primpl.h"
+#include "pprmwait.h"
+#include <direct.h>
+#include <mbstring.h>
+
+static HANDLE _pr_completion_port;
+static PRThread *_pr_io_completion_thread;
+
+#define RECYCLE_SIZE 512
+static struct _MDLock _pr_recycle_lock;
+static PRInt32 _pr_recycle_INET_array[RECYCLE_SIZE];
+static PRInt32 _pr_recycle_INET_tail = 0;
+static PRInt32 _pr_recycle_INET6_array[RECYCLE_SIZE];
+static PRInt32 _pr_recycle_INET6_tail = 0;
+
+__declspec(thread) PRThread *_pr_io_restarted_io = NULL;
+DWORD _pr_io_restartedIOIndex; /* The thread local storage slot for each
+ * thread is initialized to NULL. */
+
+PRBool _nt_version_gets_lockfile_completion;
+
+struct _MDLock _pr_ioq_lock;
+extern _MDLock _nt_idleLock;
+extern PRCList _nt_idleList;
+extern PRUint32 _nt_idleCount;
+
+#define CLOSE_TIMEOUT PR_SecondsToInterval(5)
+
+/*
+ * NSPR-to-NT access right mapping table for files.
+ */
+static DWORD fileAccessTable[] = {
+ FILE_GENERIC_READ,
+ FILE_GENERIC_WRITE,
+ FILE_GENERIC_EXECUTE
+};
+
+/*
+ * NSPR-to-NT access right mapping table for directories.
+ */
+static DWORD dirAccessTable[] = {
+ FILE_GENERIC_READ,
+ FILE_GENERIC_WRITE|FILE_DELETE_CHILD,
+ FILE_GENERIC_EXECUTE
+};
+
+static PRBool IsPrevCharSlash(const char *str, const char *current);
+
+#define _NEED_351_FILE_LOCKING_HACK
+#ifdef _NEED_351_FILE_LOCKING_HACK
+#define _PR_LOCAL_FILE 1
+#define _PR_REMOTE_FILE 2
+PRBool IsFileLocalInit();
+PRInt32 IsFileLocal(HANDLE hFile);
+#endif /* _NEED_351_FILE_LOCKING_HACK */
+
+static PRInt32 _md_MakeNonblock(HANDLE);
+
+static PROsfd _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr *addr, int *addrlen, PRIntervalTime);
+static PRInt32 _nt_nonblock_connect(PRFileDesc *fd, struct sockaddr *addr, int addrlen, PRIntervalTime);
+static PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, int flags, PRIntervalTime);
+static PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime);
+static PRInt32 _nt_nonblock_writev(PRFileDesc *fd, const PRIOVec *iov, int size, PRIntervalTime);
+static PRInt32 _nt_nonblock_sendto(PRFileDesc *, const char *, int, const struct sockaddr *, int, PRIntervalTime);
+static PRInt32 _nt_nonblock_recvfrom(PRFileDesc *, char *, int, struct sockaddr *, int *, PRIntervalTime);
+
+/*
+ * We cannot associate a fd (a socket) with an I/O completion port
+ * if the fd is nonblocking or inheritable.
+ *
+ * Nonblocking socket I/O won't work if the socket is associated with
+ * an I/O completion port.
+ *
+ * An inheritable fd cannot be associated with an I/O completion port
+ * because the completion notification of async I/O initiated by the
+ * child process is still posted to the I/O completion port in the
+ * parent process.
+ */
+#define _NT_USE_NB_IO(fd) \
+ ((fd)->secret->nonblocking || (fd)->secret->inheritable == _PR_TRI_TRUE)
+
+/*
+ * UDP support
+ *
+ * UDP is supported on NT by the continuation thread mechanism.
+ * The code is borrowed from ptio.c in pthreads nspr, hence the
+ * PT and pt prefixes. This mechanism is in fact general and
+ * not limited to UDP. For now, only UDP's recvfrom and sendto
+ * go through the continuation thread if they get WSAEWOULDBLOCK
+ * on first try. Recv and send on a connected UDP socket still
+ * goes through asychronous io.
+ */
+
+#define PT_DEFAULT_SELECT_MSEC 100
+
+typedef struct pt_Continuation pt_Continuation;
+typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revent);
+
+typedef enum pr_ContuationStatus
+{
+ pt_continuation_sumbitted,
+ pt_continuation_inprogress,
+ pt_continuation_abort,
+ pt_continuation_done
+} pr_ContuationStatus;
+
+struct pt_Continuation
+{
+ /* These objects are linked in ascending timeout order */
+ pt_Continuation *next, *prev; /* self linked list of these things */
+
+ /* The building of the continuation operation */
+ ContinuationFn function; /* what function to continue */
+ union {
+ SOCKET osfd;
+ } arg1; /* #1 - the op's fd */
+ union {
+ void* buffer;
+ } arg2; /* #2 - primary transfer buffer */
+ union {
+ PRIntn amount;
+ } arg3; /* #3 - size of 'buffer' */
+ union {
+ PRIntn flags;
+ } arg4; /* #4 - read/write flags */
+ union {
+ PRNetAddr *addr;
+ } arg5; /* #5 - send/recv address */
+
+ PRIntervalTime timeout; /* representation of the timeout */
+
+ PRIntn event; /* flags for select()'s events */
+
+ /*
+ ** The representation and notification of the results of the operation.
+ ** These function can either return an int return code or a pointer to
+ ** some object.
+ */
+ union {
+ PRIntn code;
+ void *object;
+ } result;
+
+ PRIntn syserrno; /* in case it failed, why (errno) */
+ pr_ContuationStatus status; /* the status of the operation */
+ PRCondVar *complete; /* to notify the initiating thread */
+};
+
+static struct pt_TimedQueue
+{
+ PRLock *ml; /* a little protection */
+ PRThread *thread; /* internal thread's identification */
+ PRCondVar *new_op; /* new operation supplied */
+ PRCondVar *finish_op; /* an existing operation finished */
+ PRUintn op_count; /* number of operations in the list */
+ pt_Continuation *head, *tail; /* head/tail of list of operations */
+
+ pt_Continuation *op; /* timed operation furthest in future */
+ PRIntervalTime epoch; /* the epoch of 'timed' */
+} pt_tq;
+
+#if defined(DEBUG)
+static struct pt_debug_s
+{
+ PRIntn predictionsFoiled;
+ PRIntn pollingListMax;
+ PRIntn continuationsServed;
+} pt_debug;
+#endif /* DEBUG */
+
+static void ContinuationThread(void *arg);
+static PRInt32 pt_SendTo(
+ SOCKET osfd, const void *buf,
+ PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
+ PRIntn addrlen, PRIntervalTime timeout);
+static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
+ PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout);
+
+
+/* The key returned from GetQueuedCompletionStatus() is used to determine what
+ * type of completion we have. We differentiate between IO completions and
+ * CVAR completions.
+ */
+#define KEY_IO 0xaaaaaaaa
+#define KEY_CVAR 0xbbbbbbbb
+
+PRInt32
+_PR_MD_PAUSE_CPU(PRIntervalTime ticks)
+{
+ int awoken = 0;
+ unsigned long bytes, key;
+ int rv;
+ LPOVERLAPPED olp;
+ _MDOverlapped *mdOlp;
+ PRUint32 timeout;
+
+ if (_nt_idleCount > 0) {
+ PRThread *deadThread;
+
+ _MD_LOCK(&_nt_idleLock);
+ while( !PR_CLIST_IS_EMPTY(&_nt_idleList) ) {
+ deadThread = _PR_THREAD_PTR(PR_LIST_HEAD(&_nt_idleList));
+ PR_REMOVE_LINK(&deadThread->links);
+
+ PR_ASSERT(deadThread->state == _PR_DEAD_STATE);
+
+ /* XXXMB - cleanup to do here? */
+ if ( !_PR_IS_NATIVE_THREAD(deadThread) ) {
+ /* Spinlock while user thread is still running.
+ * There is no way to use a condition variable here. The thread
+ * is dead, and we have to wait until we switch off the dead
+ * thread before we can kill the fiber completely.
+ */
+ while ( deadThread->no_sched)
+ ;
+
+ DeleteFiber(deadThread->md.fiber_id);
+ }
+ memset(deadThread, 0xa, sizeof(PRThread)); /* debugging */
+ if (!deadThread->threadAllocatedOnStack) {
+ PR_DELETE(deadThread);
+ }
+ _nt_idleCount--;
+ }
+ _MD_UNLOCK(&_nt_idleLock);
+ }
+
+ if (ticks == PR_INTERVAL_NO_TIMEOUT)
+#if 0
+ timeout = INFINITE;
+#else
+ /*
+ * temporary hack to poll the runq every 5 seconds because of bug in
+ * native threads creating user threads and not poking the right cpu.
+ *
+ * A local thread that was interrupted is bound to its current
+ * cpu but there is no easy way for the interrupter to poke the
+ * right cpu. This is a hack to poll the runq every 5 seconds.
+ */
+ timeout = 5000;
+#endif
+ else {
+ timeout = PR_IntervalToMilliseconds(ticks);
+ }
+
+ /*
+ * The idea of looping here is to complete as many IOs as possible before
+ * returning. This should minimize trips to the idle thread.
+ */
+ while(1) {
+ rv = GetQueuedCompletionStatus(
+ _pr_completion_port,
+ &bytes,
+ &key,
+ &olp,
+ timeout);
+ if (rv == 0 && olp == NULL) {
+ /* Error in GetQueuedCompetionStatus */
+ if (GetLastError() != WAIT_TIMEOUT) {
+ /* ARGH - what can we do here? Log an error? XXXMB */
+ return -1;
+ } else {
+ /* If awoken == 0, then we just had a timeout */
+ return awoken;
+ }
+ }
+
+ if (olp == NULL) {
+ return 0;
+ }
+
+ mdOlp = (_MDOverlapped *)olp;
+
+ if (mdOlp->ioModel == _MD_MultiWaitIO) {
+ PRRecvWait *desc;
+ PRWaitGroup *group;
+ PRThread *thred = NULL;
+ PRMWStatus mwstatus;
+
+ desc = mdOlp->data.mw.desc;
+ PR_ASSERT(desc != NULL);
+ mwstatus = rv ? PR_MW_SUCCESS : PR_MW_FAILURE;
+ if (InterlockedCompareExchange((PVOID *)&desc->outcome,
+ (PVOID)mwstatus, (PVOID)PR_MW_PENDING)
+ == (PVOID)PR_MW_PENDING) {
+ if (mwstatus == PR_MW_SUCCESS) {
+ desc->bytesRecv = bytes;
+ } else {
+ mdOlp->data.mw.error = GetLastError();
+ }
+ }
+ group = mdOlp->data.mw.group;
+ PR_ASSERT(group != NULL);
+
+ _PR_MD_LOCK(&group->mdlock);
+ PR_APPEND_LINK(&mdOlp->data.mw.links, &group->io_ready);
+ PR_ASSERT(desc->fd != NULL);
+ NT_HashRemoveInternal(group, desc->fd);
+ if (!PR_CLIST_IS_EMPTY(&group->wait_list)) {
+ thred = _PR_THREAD_CONDQ_PTR(PR_LIST_HEAD(&group->wait_list));
+ PR_REMOVE_LINK(&thred->waitQLinks);
+ }
+ _PR_MD_UNLOCK(&group->mdlock);
+
+ if (thred) {
+ if (!_PR_IS_NATIVE_THREAD(thred)) {
+ int pri = thred->priority;
+ _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU();
+ _PR_THREAD_LOCK(thred);
+ if (thred->flags & _PR_ON_PAUSEQ) {
+ _PR_SLEEPQ_LOCK(thred->cpu);
+ _PR_DEL_SLEEPQ(thred, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(thred->cpu);
+ _PR_THREAD_UNLOCK(thred);
+ thred->cpu = lockedCPU;
+ thred->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(lockedCPU);
+ _PR_ADD_RUNQ(thred, lockedCPU, pri);
+ _PR_RUNQ_UNLOCK(lockedCPU);
+ } else {
+ /*
+ * The thread was just interrupted and moved
+ * from the pause queue to the run queue.
+ */
+ _PR_THREAD_UNLOCK(thred);
+ }
+ } else {
+ _PR_THREAD_LOCK(thred);
+ thred->state = _PR_RUNNABLE;
+ _PR_THREAD_UNLOCK(thred);
+ ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
+ }
+ }
+ } else {
+ PRThread *completed_io;
+
+ PR_ASSERT(mdOlp->ioModel == _MD_BlockingIO);
+ completed_io = _PR_THREAD_MD_TO_PTR(mdOlp->data.mdThread);
+ completed_io->md.blocked_io_status = rv;
+ if (rv == 0) {
+ completed_io->md.blocked_io_error = GetLastError();
+ }
+ completed_io->md.blocked_io_bytes = bytes;
+
+ if ( !_PR_IS_NATIVE_THREAD(completed_io) ) {
+ int pri = completed_io->priority;
+ _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU();
+
+ /* The KEY_CVAR notification only occurs when a native thread
+ * is notifying a user thread. For user-user notifications
+ * the wakeup occurs by having the notifier place the thread
+ * on the runq directly; for native-native notifications the
+ * wakeup occurs by calling ReleaseSemaphore.
+ */
+ if ( key == KEY_CVAR ) {
+ PR_ASSERT(completed_io->io_pending == PR_FALSE);
+ PR_ASSERT(completed_io->io_suspended == PR_FALSE);
+ PR_ASSERT(completed_io->md.thr_bound_cpu == NULL);
+
+ /* Thread has already been deleted from sleepQ */
+
+ /* Switch CPU and add to runQ */
+ completed_io->cpu = lockedCPU;
+ completed_io->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(lockedCPU);
+ _PR_ADD_RUNQ(completed_io, lockedCPU, pri);
+ _PR_RUNQ_UNLOCK(lockedCPU);
+ } else {
+ PR_ASSERT(key == KEY_IO);
+ PR_ASSERT(completed_io->io_pending == PR_TRUE);
+
+ _PR_THREAD_LOCK(completed_io);
+
+ completed_io->io_pending = PR_FALSE;
+
+ /* If io_suspended is true, then this IO has already resumed.
+ * We don't need to do anything; because the thread is
+ * already running.
+ */
+ if (completed_io->io_suspended == PR_FALSE) {
+ if (completed_io->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) {
+ _PR_SLEEPQ_LOCK(completed_io->cpu);
+ _PR_DEL_SLEEPQ(completed_io, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(completed_io->cpu);
+
+ _PR_THREAD_UNLOCK(completed_io);
+
+ /*
+ * If an I/O operation is suspended, the thread
+ * must be running on the same cpu on which the
+ * I/O operation was issued.
+ */
+ PR_ASSERT(!completed_io->md.thr_bound_cpu ||
+ (completed_io->cpu == completed_io->md.thr_bound_cpu));
+
+ if (!completed_io->md.thr_bound_cpu) {
+ completed_io->cpu = lockedCPU;
+ }
+ completed_io->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(completed_io->cpu);
+ _PR_ADD_RUNQ(completed_io, completed_io->cpu, pri);
+ _PR_RUNQ_UNLOCK(completed_io->cpu);
+ } else {
+ _PR_THREAD_UNLOCK(completed_io);
+ }
+ } else {
+ _PR_THREAD_UNLOCK(completed_io);
+ }
+ }
+ } else {
+ /* For native threads, they are only notified through this loop
+ * when completing IO. So, don't worry about this being a CVAR
+ * notification, because that is not possible.
+ */
+ _PR_THREAD_LOCK(completed_io);
+ completed_io->io_pending = PR_FALSE;
+ if (completed_io->io_suspended == PR_FALSE) {
+ completed_io->state = _PR_RUNNABLE;
+ _PR_THREAD_UNLOCK(completed_io);
+ rv = ReleaseSemaphore(completed_io->md.blocked_sema,
+ 1, NULL);
+ PR_ASSERT(0 != rv);
+ } else {
+ _PR_THREAD_UNLOCK(completed_io);
+ }
+ }
+ }
+
+ awoken++;
+ timeout = 0; /* Don't block on subsequent trips through the loop */
+ }
+
+ /* never reached */
+ return 0;
+}
+
+static PRStatus
+_native_thread_md_wait(PRThread *thread, PRIntervalTime ticks)
+{
+ DWORD rv;
+ PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
+ INFINITE : PR_IntervalToMilliseconds(ticks);
+
+ /*
+ * thread waiting for a cvar or a joining thread
+ */
+ rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
+ switch(rv) {
+ case WAIT_OBJECT_0:
+ return PR_SUCCESS;
+ break;
+ case WAIT_TIMEOUT:
+ _PR_THREAD_LOCK(thread);
+ PR_ASSERT (thread->state != _PR_IO_WAIT);
+ if (thread->wait.cvar != NULL) {
+ PR_ASSERT(thread->state == _PR_COND_WAIT);
+ thread->wait.cvar = NULL;
+ thread->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The CVAR was notified just as the timeout
+ * occurred. This left the semaphore in the
+ * signaled state. Call WaitForSingleObject()
+ * to clear the semaphore.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ return PR_SUCCESS;
+ break;
+ default:
+ return PR_FAILURE;
+ break;
+ }
+
+ return PR_SUCCESS;
+}
+
+PRStatus
+_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ DWORD rv;
+
+ if (_native_threads_only) {
+ return(_native_thread_md_wait(thread, ticks));
+ }
+ if ( thread->flags & _PR_GLOBAL_SCOPE ) {
+ PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
+ INFINITE : PR_IntervalToMilliseconds(ticks);
+ rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
+ switch(rv) {
+ case WAIT_OBJECT_0:
+ return PR_SUCCESS;
+ break;
+ case WAIT_TIMEOUT:
+ _PR_THREAD_LOCK(thread);
+ if (thread->state == _PR_IO_WAIT) {
+ if (thread->io_pending == PR_TRUE) {
+ thread->state = _PR_RUNNING;
+ thread->io_suspended = PR_TRUE;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The IO completed just at the same time the timeout
+ * occurred. This left the semaphore in the signaled
+ * state. Call WaitForSingleObject() to clear the
+ * semaphore.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ } else {
+ if (thread->wait.cvar != NULL) {
+ PR_ASSERT(thread->state == _PR_COND_WAIT);
+ thread->wait.cvar = NULL;
+ thread->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The CVAR was notified just as the timeout
+ * occurred. This left the semaphore in the
+ * signaled state. Call WaitForSingleObject()
+ * to clear the semaphore.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ }
+ return PR_SUCCESS;
+ break;
+ default:
+ return PR_FAILURE;
+ break;
+ }
+ } else {
+ PRInt32 is;
+
+ _PR_INTSOFF(is);
+ _PR_MD_SWITCH_CONTEXT(thread);
+ }
+
+ return PR_SUCCESS;
+}
+
+static void
+_native_thread_io_nowait(
+ PRThread *thread,
+ int rv,
+ int bytes)
+{
+ int rc;
+
+ PR_ASSERT(rv != 0);
+ _PR_THREAD_LOCK(thread);
+ if (thread->state == _PR_IO_WAIT) {
+ PR_ASSERT(thread->io_suspended == PR_FALSE);
+ PR_ASSERT(thread->io_pending == PR_TRUE);
+ thread->state = _PR_RUNNING;
+ thread->io_pending = PR_FALSE;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The IO completed just at the same time the
+ * thread was interrupted. This left the semaphore
+ * in the signaled state. Call WaitForSingleObject()
+ * to clear the semaphore.
+ */
+ PR_ASSERT(thread->io_suspended == PR_TRUE);
+ PR_ASSERT(thread->io_pending == PR_TRUE);
+ thread->io_pending = PR_FALSE;
+ _PR_THREAD_UNLOCK(thread);
+ rc = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
+ PR_ASSERT(rc == WAIT_OBJECT_0);
+ }
+
+ thread->md.blocked_io_status = rv;
+ thread->md.blocked_io_bytes = bytes;
+ rc = ResetEvent(thread->md.thr_event);
+ PR_ASSERT(rc != 0);
+ return;
+}
+
+static PRStatus
+_native_thread_io_wait(PRThread *thread, PRIntervalTime ticks)
+{
+ DWORD rv, bytes;
+#define _NATIVE_IO_WAIT_HANDLES 2
+#define _NATIVE_WAKEUP_EVENT_INDEX 0
+#define _NATIVE_IO_EVENT_INDEX 1
+
+ HANDLE wait_handles[_NATIVE_IO_WAIT_HANDLES];
+
+ PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
+ INFINITE : PR_IntervalToMilliseconds(ticks);
+
+ PR_ASSERT(thread->flags & _PR_GLOBAL_SCOPE);
+
+ wait_handles[0] = thread->md.blocked_sema;
+ wait_handles[1] = thread->md.thr_event;
+ rv = WaitForMultipleObjects(_NATIVE_IO_WAIT_HANDLES, wait_handles,
+ FALSE, msecs);
+
+ switch(rv) {
+ case WAIT_OBJECT_0 + _NATIVE_IO_EVENT_INDEX:
+ /*
+ * I/O op completed
+ */
+ _PR_THREAD_LOCK(thread);
+ if (thread->state == _PR_IO_WAIT) {
+
+ PR_ASSERT(thread->io_suspended == PR_FALSE);
+ PR_ASSERT(thread->io_pending == PR_TRUE);
+ thread->state = _PR_RUNNING;
+ thread->io_pending = PR_FALSE;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The IO completed just at the same time the
+ * thread was interrupted. This led to us being
+ * notified twice. Call WaitForSingleObject()
+ * to clear the semaphore.
+ */
+ PR_ASSERT(thread->io_suspended == PR_TRUE);
+ PR_ASSERT(thread->io_pending == PR_TRUE);
+ thread->io_pending = PR_FALSE;
+ _PR_THREAD_UNLOCK(thread);
+ rv = WaitForSingleObject(thread->md.blocked_sema,
+ INFINITE);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+
+ rv = GetOverlappedResult((HANDLE) thread->io_fd,
+ &thread->md.overlapped.overlapped, &bytes, FALSE);
+
+ thread->md.blocked_io_status = rv;
+ if (rv != 0) {
+ thread->md.blocked_io_bytes = bytes;
+ } else {
+ thread->md.blocked_io_error = GetLastError();
+ PR_ASSERT(ERROR_IO_PENDING != thread->md.blocked_io_error);
+ }
+ rv = ResetEvent(thread->md.thr_event);
+ PR_ASSERT(rv != 0);
+ break;
+ case WAIT_OBJECT_0 + _NATIVE_WAKEUP_EVENT_INDEX:
+ /*
+ * I/O interrupted;
+ */
+#ifdef DEBUG
+ _PR_THREAD_LOCK(thread);
+ PR_ASSERT(thread->io_suspended == PR_TRUE);
+ _PR_THREAD_UNLOCK(thread);
+#endif
+ break;
+ case WAIT_TIMEOUT:
+ _PR_THREAD_LOCK(thread);
+ if (thread->state == _PR_IO_WAIT) {
+ thread->state = _PR_RUNNING;
+ thread->io_suspended = PR_TRUE;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /*
+ * The thread was interrupted just as the timeout
+ * occurred. This left the semaphore in the signaled
+ * state. Call WaitForSingleObject() to clear the
+ * semaphore.
+ */
+ PR_ASSERT(thread->io_suspended == PR_TRUE);
+ _PR_THREAD_UNLOCK(thread);
+ rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ break;
+ default:
+ return PR_FAILURE;
+ break;
+ }
+
+ return PR_SUCCESS;
+}
+
+
+static PRStatus
+_NT_IO_WAIT(PRThread *thread, PRIntervalTime timeout)
+{
+ PRBool fWait = PR_TRUE;
+
+ if (_native_threads_only) {
+ return(_native_thread_io_wait(thread, timeout));
+ }
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+
+ _PR_THREAD_LOCK(thread);
+
+ /* The IO may have already completed; if so, don't add to sleepQ,
+ * since we are already on the runQ!
+ */
+ if (thread->io_pending == PR_TRUE) {
+ _PR_SLEEPQ_LOCK(thread->cpu);
+ _PR_ADD_SLEEPQ(thread, timeout);
+ _PR_SLEEPQ_UNLOCK(thread->cpu);
+ } else {
+ fWait = PR_FALSE;
+ }
+ _PR_THREAD_UNLOCK(thread);
+ }
+ if (fWait) {
+ return _PR_MD_WAIT(thread, timeout);
+ }
+ else {
+ return PR_SUCCESS;
+ }
+}
+
+/*
+ * Unblock threads waiting for I/O
+ * used when interrupting threads
+ *
+ * NOTE: The thread lock should held when this function is called.
+ * On return, the thread lock is released.
+ */
+void _PR_Unblock_IO_Wait(PRThread *thr)
+{
+ PRStatus rv;
+ _PRCPU *cpu = thr->cpu;
+
+ PR_ASSERT(thr->state == _PR_IO_WAIT);
+ /*
+ * A thread for which an I/O timed out or was interrupted cannot be
+ * in an IO_WAIT state except as a result of calling PR_Close or
+ * PR_NT_CancelIo for the FD. For these two cases, _PR_IO_WAIT state
+ * is not interruptible
+ */
+ if (thr->md.interrupt_disabled == PR_TRUE) {
+ _PR_THREAD_UNLOCK(thr);
+ return;
+ }
+ thr->io_suspended = PR_TRUE;
+ thr->state = _PR_RUNNABLE;
+
+ if (!_PR_IS_NATIVE_THREAD(thr)) {
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
+ _PR_SLEEPQ_LOCK(cpu);
+ _PR_DEL_SLEEPQ(thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(cpu);
+ /*
+ * this thread will continue to run on the same cpu until the
+ * I/O is aborted by closing the FD or calling CancelIO
+ */
+ thr->md.thr_bound_cpu = cpu;
+
+ PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
+ _PR_AddThreadToRunQ(me, thr);
+ }
+ _PR_THREAD_UNLOCK(thr);
+ rv = _PR_MD_WAKEUP_WAITER(thr);
+ PR_ASSERT(PR_SUCCESS == rv);
+}
+
+/* Resume an outstanding IO; requires that after the switch, we disable */
+static PRStatus
+_NT_ResumeIO(PRThread *thread, PRIntervalTime ticks)
+{
+ PRBool fWait = PR_TRUE;
+
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+ if (_pr_use_static_tls) {
+ _pr_io_restarted_io = thread;
+ } else {
+ TlsSetValue(_pr_io_restartedIOIndex, thread);
+ }
+ } else {
+ _PR_THREAD_LOCK(thread);
+ if (!thread->io_pending) {
+ fWait = PR_FALSE;
+ }
+ thread->io_suspended = PR_FALSE;
+
+ _PR_THREAD_UNLOCK(thread);
+ }
+ /* We don't put ourselves back on the sleepQ yet; until we
+ * set the suspended bit to false, we can't do that. Just save
+ * the sleep time here, and then continue. The restarted_io handler
+ * will add us to the sleepQ if needed.
+ */
+ thread->sleep = ticks;
+
+ if (fWait) {
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+ return _PR_MD_WAIT(thread, ticks);
+ }
+ else {
+ return _NT_IO_WAIT(thread, ticks);
+ }
+ }
+ return PR_SUCCESS;
+}
+
+PRStatus
+_PR_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread == NULL) {
+ /* If thread is NULL, we aren't waking a thread, we're just poking
+ * idle thread
+ */
+ if ( PostQueuedCompletionStatus(_pr_completion_port, 0,
+ KEY_CVAR, NULL) == FALSE) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+ }
+
+ if ( _PR_IS_NATIVE_THREAD(thread) ) {
+ if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE) {
+ return PR_FAILURE;
+ }
+ else {
+ return PR_SUCCESS;
+ }
+ } else {
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ /* When a Native thread has to awaken a user thread, it has to poke
+ * the completion port because all user threads might be idle, and
+ * thus the CPUs are just waiting for a completion.
+ *
+ * XXXMB - can we know when we are truely idle (and not checking
+ * the runq)?
+ */
+ if ((_PR_IS_NATIVE_THREAD(me) || (thread->cpu != me->cpu)) &&
+ (!thread->md.thr_bound_cpu)) {
+ /* The thread should not be in any queue */
+ PR_ASSERT(thread->queueCount == 0);
+ if ( PostQueuedCompletionStatus(_pr_completion_port, 0,
+ KEY_CVAR, &(thread->md.overlapped.overlapped)) == FALSE) {
+ return PR_FAILURE;
+ }
+ }
+ return PR_SUCCESS;
+ }
+}
+
+void
+_PR_MD_INIT_IO()
+{
+ WORD WSAVersion = 0x0101;
+ WSADATA WSAData;
+ int err;
+ OSVERSIONINFO OSversion;
+
+ err = WSAStartup( WSAVersion, &WSAData );
+ PR_ASSERT(0 == err);
+
+ _pr_completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
+ NULL,
+ 0,
+ 0);
+
+ _MD_NEW_LOCK(&_pr_recycle_lock);
+ _MD_NEW_LOCK(&_pr_ioq_lock);
+
+ OSversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (GetVersionEx(&OSversion)) {
+ _nt_version_gets_lockfile_completion = PR_FALSE;
+ if (OSversion.dwMajorVersion >= 4) {
+ _nt_version_gets_lockfile_completion = PR_TRUE;
+ }
+ } else {
+ PR_ASSERT(0);
+ }
+
+#ifdef _NEED_351_FILE_LOCKING_HACK
+ IsFileLocalInit();
+#endif /* _NEED_351_FILE_LOCKING_HACK */
+
+ /*
+ * UDP support: start up the continuation thread
+ */
+
+ pt_tq.op_count = 0;
+ pt_tq.head = pt_tq.tail = NULL;
+ pt_tq.ml = PR_NewLock();
+ PR_ASSERT(NULL != pt_tq.ml);
+ pt_tq.new_op = PR_NewCondVar(pt_tq.ml);
+ PR_ASSERT(NULL != pt_tq.new_op);
+#if defined(DEBUG)
+ memset(&pt_debug, 0, sizeof(struct pt_debug_s));
+#endif
+
+ pt_tq.thread = PR_CreateThread(
+ PR_SYSTEM_THREAD, ContinuationThread, NULL,
+ PR_PRIORITY_URGENT, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ PR_ASSERT(NULL != pt_tq.thread);
+
+#ifdef DEBUG
+ /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */
+ {
+ SYSTEMTIME systime;
+ union {
+ PRTime prt;
+ FILETIME ft;
+ } filetime;
+ BOOL rv;
+
+ systime.wYear = 1970;
+ systime.wMonth = 1;
+ /* wDayOfWeek is ignored */
+ systime.wDay = 1;
+ systime.wHour = 0;
+ systime.wMinute = 0;
+ systime.wSecond = 0;
+ systime.wMilliseconds = 0;
+
+ rv = SystemTimeToFileTime(&systime, &filetime.ft);
+ PR_ASSERT(0 != rv);
+ PR_ASSERT(filetime.prt == _pr_filetime_offset);
+ }
+#endif /* DEBUG */
+
+ _PR_NT_InitSids();
+}
+
+/* --- SOCKET IO --------------------------------------------------------- */
+
+/* _md_get_recycled_socket()
+ * Get a socket from the recycle bin; if no sockets are in the bin,
+ * create one. The socket will be passed to AcceptEx() as the
+ * second argument.
+ */
+static SOCKET
+_md_get_recycled_socket(int af)
+{
+ SOCKET rv;
+
+ _MD_LOCK(&_pr_recycle_lock);
+ if (af == AF_INET && _pr_recycle_INET_tail) {
+ _pr_recycle_INET_tail--;
+ rv = _pr_recycle_INET_array[_pr_recycle_INET_tail];
+ _MD_UNLOCK(&_pr_recycle_lock);
+ return rv;
+ }
+ if (af == AF_INET6 && _pr_recycle_INET6_tail) {
+ _pr_recycle_INET6_tail--;
+ rv = _pr_recycle_INET6_array[_pr_recycle_INET6_tail];
+ _MD_UNLOCK(&_pr_recycle_lock);
+ return rv;
+ }
+ _MD_UNLOCK(&_pr_recycle_lock);
+
+ rv = _PR_MD_SOCKET(af, SOCK_STREAM, 0);
+ if (rv != INVALID_SOCKET && _md_Associate((HANDLE)rv) == 0) {
+ closesocket(rv);
+ return INVALID_SOCKET;
+ }
+ return rv;
+}
+
+/* _md_put_recycled_socket()
+ * Add a socket to the recycle bin.
+ */
+static void
+_md_put_recycled_socket(SOCKET newsock, int af)
+{
+ PR_ASSERT(_pr_recycle_INET_tail >= 0);
+ PR_ASSERT(_pr_recycle_INET6_tail >= 0);
+
+ _MD_LOCK(&_pr_recycle_lock);
+ if (af == AF_INET && _pr_recycle_INET_tail < RECYCLE_SIZE) {
+ _pr_recycle_INET_array[_pr_recycle_INET_tail] = newsock;
+ _pr_recycle_INET_tail++;
+ _MD_UNLOCK(&_pr_recycle_lock);
+ } else if (af == AF_INET6 && _pr_recycle_INET6_tail < RECYCLE_SIZE) {
+ _pr_recycle_INET6_array[_pr_recycle_INET6_tail] = newsock;
+ _pr_recycle_INET6_tail++;
+ _MD_UNLOCK(&_pr_recycle_lock);
+ } else {
+ _MD_UNLOCK(&_pr_recycle_lock);
+ closesocket(newsock);
+ }
+
+ return;
+}
+
+/* _md_Associate()
+ * Associates a file with the completion port.
+ * Returns 0 on failure, 1 on success.
+ */
+PRInt32
+_md_Associate(HANDLE file)
+{
+ HANDLE port;
+
+ if (!_native_threads_only) {
+ port = CreateIoCompletionPort((HANDLE)file,
+ _pr_completion_port,
+ KEY_IO,
+ 0);
+
+ /* XXX should map error codes on failures */
+ return (port == _pr_completion_port);
+ } else {
+ return 1;
+ }
+}
+
+/*
+ * _md_MakeNonblock()
+ * Make a socket nonblocking.
+ * Returns 0 on failure, 1 on success.
+ */
+static PRInt32
+_md_MakeNonblock(HANDLE file)
+{
+ int rv;
+ u_long one = 1;
+
+ rv = ioctlsocket((SOCKET)file, FIONBIO, &one);
+ /* XXX should map error codes on failures */
+ return (rv == 0);
+}
+
+static int missing_completions = 0;
+static int max_wait_loops = 0;
+
+static PRInt32
+_NT_IO_ABORT(PROsfd sock)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRBool fWait;
+ PRInt32 rv;
+ int loop_count;
+
+ /* This is a clumsy way to abort the IO, but it is all we can do.
+ * It looks a bit racy, but we handle all the cases.
+ * case 1: IO completes before calling closesocket
+ * case 1a: fWait is set to PR_FALSE
+ * This should e the most likely case. We'll properly
+ * not wait call _NT_IO_WAIT, since the closesocket()
+ * won't be forcing a completion.
+ * case 1b: fWait is set to PR_TRUE
+ * This hopefully won't happen much. When it does, this
+ * thread will timeout in _NT_IO_WAIT for CLOSE_INTERVAL
+ * before cleaning up.
+ * case 2: IO does not complete before calling closesocket
+ * case 2a: IO never completes
+ * This is the likely case. We'll close it and wait
+ * for the completion forced by the close. Return should
+ * be immediate.
+ * case 2b: IO completes just after calling closesocket
+ * Since the closesocket is issued, we'll either get a
+ * completion back for the real IO or for the close. We
+ * don't really care. It may not even be possible to get
+ * a real completion here. In any event, we'll awaken
+ * from NT_IO_WAIT immediately.
+ */
+
+ _PR_THREAD_LOCK(me);
+ fWait = me->io_pending;
+ if (fWait) {
+ /*
+ * If there's still I/O pending, it should have already timed
+ * out once before this function is called.
+ */
+ PR_ASSERT(me->io_suspended == PR_TRUE);
+
+ /* Set up to wait for I/O completion again */
+ me->state = _PR_IO_WAIT;
+ me->io_suspended = PR_FALSE;
+ me->md.interrupt_disabled = PR_TRUE;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ /* Close the socket if there is one */
+ if (sock != INVALID_SOCKET) {
+ rv = closesocket((SOCKET)sock);
+ }
+
+ /* If there was I/O pending before the close, wait for it to complete */
+ if (fWait) {
+
+ /* Wait and wait for the I/O to complete */
+ for (loop_count = 0; fWait; ++loop_count) {
+
+ _NT_IO_WAIT(me, CLOSE_TIMEOUT);
+
+ _PR_THREAD_LOCK(me);
+ fWait = me->io_pending;
+ if (fWait) {
+ PR_ASSERT(me->io_suspended == PR_TRUE);
+ me->state = _PR_IO_WAIT;
+ me->io_suspended = PR_FALSE;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ if (loop_count > max_wait_loops) {
+ max_wait_loops = loop_count;
+ }
+ }
+
+ if (loop_count > 1) {
+ ++missing_completions;
+ }
+
+ me->md.interrupt_disabled = PR_FALSE;
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+ me->md.thr_bound_cpu = NULL;
+ me->io_suspended = PR_FALSE;
+
+ return rv;
+}
+
+
+PROsfd
+_PR_MD_SOCKET(int af, int type, int flags)
+{
+ SOCKET sock;
+
+ sock = socket(af, type, flags);
+
+ if (sock == INVALID_SOCKET) {
+ _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
+ }
+
+ return (PROsfd)sock;
+}
+
+struct connect_data_s {
+ PRInt32 status;
+ PRInt32 error;
+ PROsfd osfd;
+ struct sockaddr *addr;
+ PRUint32 addrlen;
+ PRIntervalTime timeout;
+};
+
+void
+_PR_MD_connect_thread(void *cdata)
+{
+ struct connect_data_s *cd = (struct connect_data_s *)cdata;
+
+ cd->status = connect(cd->osfd, cd->addr, cd->addrlen);
+
+ if (cd->status == SOCKET_ERROR) {
+ cd->error = WSAGetLastError();
+ }
+
+ return;
+}
+
+
+PRInt32
+_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ u_long nbio;
+ PRInt32 rc;
+
+ if (fd->secret->nonblocking) {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1) {
+ err = WSAGetLastError();
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+ return rv;
+ }
+
+ /*
+ * Temporarily make the socket non-blocking so that we can
+ * initiate a non-blocking connect and wait for its completion
+ * (with a timeout) in select.
+ */
+ PR_ASSERT(!fd->secret->md.io_model_committed);
+ nbio = 1;
+ rv = ioctlsocket((SOCKET)osfd, FIONBIO, &nbio);
+ PR_ASSERT(0 == rv);
+
+ rc = _nt_nonblock_connect(fd, (struct sockaddr *) addr, addrlen, timeout);
+
+ /* Set the socket back to blocking. */
+ nbio = 0;
+ rv = ioctlsocket((SOCKET)osfd, FIONBIO, &nbio);
+ PR_ASSERT(0 == rv);
+
+ return rc;
+}
+
+PRInt32
+_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 rv;
+#if 0
+ int one = 1;
+#endif
+
+ rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
+
+ if (rv == SOCKET_ERROR) {
+ _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
+ return -1;
+ }
+
+#if 0
+ /* Disable nagle- so far unknown if this is good or not...
+ */
+ rv = setsockopt(fd->secret->md.osfd,
+ SOL_SOCKET,
+ TCP_NODELAY,
+ (const char *)&one,
+ sizeof(one));
+ PR_ASSERT(rv == 0);
+#endif
+
+ return 0;
+}
+
+void _PR_MD_UPDATE_ACCEPT_CONTEXT(PROsfd accept_sock, PROsfd listen_sock)
+{
+ /* Sockets accept()'d with AcceptEx need to call this setsockopt before
+ * calling anything other than ReadFile(), WriteFile(), send(), recv(),
+ * Transmitfile(), and closesocket(). In order to call any other
+ * winsock functions, we have to make this setsockopt call.
+ *
+ * XXXMB - For the server, we *NEVER* need this in
+ * the "normal" code path. But now we have to call it. This is a waste
+ * of a system call. We'd like to only call it before calling the
+ * obscure socket calls, but since we don't know at that point what the
+ * original socket was (or even if it is still alive) we can't do it
+ * at that point...
+ */
+ setsockopt((SOCKET)accept_sock,
+ SOL_SOCKET,
+ SO_UPDATE_ACCEPT_CONTEXT,
+ (char *)&listen_sock,
+ sizeof(listen_sock));
+
+}
+
+#define INET_ADDR_PADDED (sizeof(PRNetAddr) + 16)
+PROsfd
+_PR_MD_FAST_ACCEPT(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
+ PRIntervalTime timeout, PRBool fast,
+ _PR_AcceptTimeoutCallback callback, void *callbackArg)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ SOCKET accept_sock;
+ int bytes;
+ PRNetAddr *Laddr;
+ PRNetAddr *Raddr;
+ PRUint32 llen, err;
+ int rv;
+
+ if (_NT_USE_NB_IO(fd)) {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ /*
+ * The accepted socket inherits the nonblocking and
+ * inheritable (HANDLE_FLAG_INHERIT) attributes of
+ * the listening socket.
+ */
+ accept_sock = _nt_nonblock_accept(fd, (struct sockaddr *)raddr, rlen, timeout);
+ if (!fd->secret->nonblocking) {
+ u_long zero = 0;
+
+ rv = ioctlsocket(accept_sock, FIONBIO, &zero);
+ PR_ASSERT(0 == rv);
+ }
+ return accept_sock;
+ }
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ if (!me->md.acceptex_buf) {
+ me->md.acceptex_buf = PR_MALLOC(2*INET_ADDR_PADDED);
+ if (!me->md.acceptex_buf) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ }
+
+ accept_sock = _md_get_recycled_socket(fd->secret->af);
+ if (accept_sock == INVALID_SOCKET) {
+ return -1;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+ if (_native_threads_only) {
+ me->md.overlapped.overlapped.hEvent = me->md.thr_event;
+ }
+
+ _PR_THREAD_LOCK(me);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ closesocket(accept_sock);
+ return -1;
+ }
+ me->io_pending = PR_TRUE;
+ me->state = _PR_IO_WAIT;
+ _PR_THREAD_UNLOCK(me);
+ me->io_fd = osfd;
+
+ rv = AcceptEx((SOCKET)osfd,
+ accept_sock,
+ me->md.acceptex_buf,
+ 0,
+ INET_ADDR_PADDED,
+ INET_ADDR_PADDED,
+ &bytes,
+ &(me->md.overlapped.overlapped));
+
+ if ( (rv == 0) && ((err = WSAGetLastError()) != ERROR_IO_PENDING)) {
+ /* Argh! The IO failed */
+ closesocket(accept_sock);
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ _PR_MD_MAP_ACCEPTEX_ERROR(err);
+ return -1;
+ }
+
+ if (_native_threads_only && rv) {
+ _native_thread_io_nowait(me, rv, bytes);
+ } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ closesocket(accept_sock);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ closesocket(accept_sock);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ closesocket(accept_sock);
+ _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ if (!fast) {
+ _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)accept_sock, (SOCKET)osfd);
+ }
+
+ /* IO is done */
+ GetAcceptExSockaddrs(
+ me->md.acceptex_buf,
+ 0,
+ INET_ADDR_PADDED,
+ INET_ADDR_PADDED,
+ (LPSOCKADDR *)&(Laddr),
+ &llen,
+ (LPSOCKADDR *)&(Raddr),
+ (unsigned int *)rlen);
+
+ if (raddr != NULL) {
+ memcpy((char *)raddr, (char *)&Raddr->inet, *rlen);
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return accept_sock;
+}
+
+PRInt32
+_PR_MD_FAST_ACCEPT_READ(PRFileDesc *sd, PROsfd *newSock, PRNetAddr **raddr,
+ void *buf, PRInt32 amount, PRIntervalTime timeout,
+ PRBool fast, _PR_AcceptTimeoutCallback callback,
+ void *callbackArg)
+{
+ PROsfd sock = sd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ int bytes;
+ PRNetAddr *Laddr;
+ PRUint32 llen, rlen, err;
+ int rv;
+ PRBool isConnected;
+ PRBool madeCallback = PR_FALSE;
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ if (!sd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)sock);
+ PR_ASSERT(0 != rv);
+ sd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ *newSock = _md_get_recycled_socket(sd->secret->af);
+ if (*newSock == INVALID_SOCKET) {
+ return -1;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+ if (_native_threads_only) {
+ me->md.overlapped.overlapped.hEvent = me->md.thr_event;
+ }
+
+ _PR_THREAD_LOCK(me);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ closesocket(*newSock);
+ return -1;
+ }
+ me->io_pending = PR_TRUE;
+ me->state = _PR_IO_WAIT;
+ _PR_THREAD_UNLOCK(me);
+ me->io_fd = sock;
+
+ rv = AcceptEx((SOCKET)sock,
+ *newSock,
+ buf,
+ amount,
+ INET_ADDR_PADDED,
+ INET_ADDR_PADDED,
+ &bytes,
+ &(me->md.overlapped.overlapped));
+
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
+ closesocket(*newSock);
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ _PR_MD_MAP_ACCEPTEX_ERROR(err);
+ return -1;
+ }
+
+ if (_native_threads_only && rv) {
+ _native_thread_io_nowait(me, rv, bytes);
+ } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ closesocket(*newSock);
+ return -1;
+ }
+
+retry:
+ if (me->io_suspended) {
+ PRInt32 err;
+ INT seconds;
+ INT bytes = sizeof(seconds);
+
+ PR_ASSERT(timeout != PR_INTERVAL_NO_TIMEOUT);
+
+ err = getsockopt(*newSock,
+ SOL_SOCKET,
+ SO_CONNECT_TIME,
+ (char *)&seconds,
+ (PINT)&bytes);
+ if ( err == NO_ERROR ) {
+ PRIntervalTime elapsed = PR_SecondsToInterval(seconds);
+
+ if (seconds == 0xffffffff) {
+ isConnected = PR_FALSE;
+ }
+ else {
+ isConnected = PR_TRUE;
+ }
+
+ if (!isConnected) {
+ if (madeCallback == PR_FALSE && callback) {
+ callback(callbackArg);
+ }
+ madeCallback = PR_TRUE;
+ me->state = _PR_IO_WAIT;
+ if (_NT_ResumeIO(me, timeout) == PR_FAILURE) {
+ closesocket(*newSock);
+ return -1;
+ }
+ goto retry;
+ }
+
+ if (elapsed < timeout) {
+ /* Socket is connected but time not elapsed, RESUME IO */
+ timeout -= elapsed;
+ me->state = _PR_IO_WAIT;
+ if (_NT_ResumeIO(me, timeout) == PR_FAILURE) {
+ closesocket(*newSock);
+ return -1;
+ }
+ goto retry;
+ }
+ } else {
+ /* What to do here? Assume socket not open?*/
+ PR_ASSERT(0);
+ isConnected = PR_FALSE;
+ }
+
+ rv = _NT_IO_ABORT(*newSock);
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+ PR_ASSERT(me->md.thr_bound_cpu == NULL);
+ /* If the IO is still suspended, it means we didn't get any
+ * completion from NT_IO_WAIT. This is not disasterous, I hope,
+ * but it may mean we still have an IO outstanding... Try to
+ * recover by just allowing ourselves to continue.
+ */
+ me->io_suspended = PR_FALSE;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ me->state = _PR_RUNNING;
+ closesocket(*newSock);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+ PR_ASSERT(me->md.thr_bound_cpu == NULL);
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
+ closesocket(*newSock);
+ return -1;
+ }
+
+ if (!fast) {
+ _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)*newSock, (SOCKET)sock);
+ }
+
+ /* IO is done */
+ GetAcceptExSockaddrs(
+ buf,
+ amount,
+ INET_ADDR_PADDED,
+ INET_ADDR_PADDED,
+ (LPSOCKADDR *)&(Laddr),
+ &llen,
+ (LPSOCKADDR *)(raddr),
+ (unsigned int *)&rlen);
+
+ return me->md.blocked_io_bytes;
+}
+
+PRInt32
+_PR_MD_SENDFILE(PRFileDesc *sock, PRSendFileData *sfd,
+ PRInt32 flags, PRIntervalTime timeout)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 tflags;
+ int rv, err;
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ if (!sock->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)sock->secret->md.osfd);
+ PR_ASSERT(0 != rv);
+ sock->secret->md.io_model_committed = PR_TRUE;
+ }
+ if (!me->md.xmit_bufs) {
+ me->md.xmit_bufs = PR_NEW(TRANSMIT_FILE_BUFFERS);
+ if (!me->md.xmit_bufs) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ }
+ me->md.xmit_bufs->Head = (void *)sfd->header;
+ me->md.xmit_bufs->HeadLength = sfd->hlen;
+ me->md.xmit_bufs->Tail = (void *)sfd->trailer;
+ me->md.xmit_bufs->TailLength = sfd->tlen;
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+ me->md.overlapped.overlapped.Offset = sfd->file_offset;
+ if (_native_threads_only) {
+ me->md.overlapped.overlapped.hEvent = me->md.thr_event;
+ }
+
+ tflags = 0;
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
+ tflags = TF_DISCONNECT | TF_REUSE_SOCKET;
+ }
+
+ _PR_THREAD_LOCK(me);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ me->io_pending = PR_TRUE;
+ me->state = _PR_IO_WAIT;
+ _PR_THREAD_UNLOCK(me);
+ me->io_fd = sock->secret->md.osfd;
+
+ rv = TransmitFile((SOCKET)sock->secret->md.osfd,
+ (HANDLE)sfd->fd->secret->md.osfd,
+ (DWORD)sfd->file_nbytes,
+ (DWORD)0,
+ (LPOVERLAPPED)&(me->md.overlapped.overlapped),
+ (TRANSMIT_FILE_BUFFERS *)me->md.xmit_bufs,
+ (DWORD)tflags);
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ _PR_MD_MAP_TRANSMITFILE_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_TRANSMITFILE_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
+ _md_put_recycled_socket(sock->secret->md.osfd, sock->secret->af);
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+}
+
+PRInt32
+_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ int bytes;
+ int rv, err;
+
+ if (_NT_USE_NB_IO(fd)) {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ return _nt_nonblock_recv(fd, buf, amount, flags, timeout);
+ }
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+ if (_native_threads_only) {
+ me->md.overlapped.overlapped.hEvent = me->md.thr_event;
+ }
+
+ _PR_THREAD_LOCK(me);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ me->io_pending = PR_TRUE;
+ me->state = _PR_IO_WAIT;
+ _PR_THREAD_UNLOCK(me);
+ me->io_fd = osfd;
+
+ rv = ReadFile((HANDLE)osfd,
+ buf,
+ amount,
+ &bytes,
+ &(me->md.overlapped.overlapped));
+ if ( (rv == 0) && (GetLastError() != ERROR_IO_PENDING) ) {
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ if ((err = GetLastError()) == ERROR_HANDLE_EOF) {
+ return 0;
+ }
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ }
+
+ if (_native_threads_only && rv) {
+ _native_thread_io_nowait(me, rv, bytes);
+ } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ if (me->md.blocked_io_error == ERROR_HANDLE_EOF) {
+ return 0;
+ }
+ _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+}
+
+PRInt32
+_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ int bytes;
+ int rv, err;
+
+ if (_NT_USE_NB_IO(fd)) {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ return _nt_nonblock_send(fd, (char *)buf, amount, timeout);
+ }
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+ if (_native_threads_only) {
+ me->md.overlapped.overlapped.hEvent = me->md.thr_event;
+ }
+
+ _PR_THREAD_LOCK(me);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ me->io_pending = PR_TRUE;
+ me->state = _PR_IO_WAIT;
+ _PR_THREAD_UNLOCK(me);
+ me->io_fd = osfd;
+
+ rv = WriteFile((HANDLE)osfd,
+ buf,
+ amount,
+ &bytes,
+ &(me->md.overlapped.overlapped));
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ _PR_MD_MAP_WRITE_ERROR(err);
+ return -1;
+ }
+
+ if (_native_threads_only && rv) {
+ _native_thread_io_nowait(me, rv, bytes);
+ } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+}
+
+PRInt32
+_PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv;
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ if (_NT_USE_NB_IO(fd)) {
+ return _nt_nonblock_sendto(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
+ }
+ else {
+ return pt_SendTo(osfd, buf, amount, flags, addr, addrlen, timeout);
+ }
+}
+
+PRInt32
+_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv;
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ if (_NT_USE_NB_IO(fd)) {
+ return _nt_nonblock_recvfrom(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
+ }
+ else {
+ return pt_RecvFrom(osfd, buf, amount, flags, addr, addrlen, timeout);
+ }
+}
+
+/* XXXMB - for now this is a sockets call only */
+PRInt32
+_PR_MD_WRITEV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ int index;
+ int sent = 0;
+ int rv;
+
+ if (_NT_USE_NB_IO(fd)) {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ return _nt_nonblock_writev(fd, iov, iov_size, timeout);
+ }
+
+ for (index=0; index<iov_size; index++) {
+ rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0,
+ timeout);
+ if (rv > 0) {
+ sent += rv;
+ }
+ if ( rv != iov[index].iov_len ) {
+ if (sent <= 0) {
+ return -1;
+ }
+ return -1;
+ }
+ }
+
+ return sent;
+}
+
+PRInt32
+_PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
+{
+ PRInt32 rv;
+
+ rv = listen(fd->secret->md.osfd, backlog);
+ if (rv < 0) {
+ _PR_MD_MAP_LISTEN_ERROR(WSAGetLastError());
+ }
+ return(rv);
+}
+
+PRInt32
+_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
+{
+ PRInt32 rv;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+ if (rv < 0) {
+ _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
+ }
+ return(rv);
+}
+
+PRStatus
+_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
+ if (rv==0) {
+ return PR_SUCCESS;
+ }
+ else {
+ _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ /*
+ * NT has a bug that, when invoked on a socket accepted by
+ * AcceptEx(), getpeername() returns an all-zero peer address.
+ * To work around this bug, we store the peer's address (returned
+ * by AcceptEx()) with the socket fd and use the cached peer
+ * address if the socket is an accepted socket.
+ */
+
+ if (fd->secret->md.accepted_socket) {
+ INT seconds;
+ INT bytes = sizeof(seconds);
+
+ /*
+ * Determine if the socket is connected.
+ */
+
+ rv = getsockopt(fd->secret->md.osfd,
+ SOL_SOCKET,
+ SO_CONNECT_TIME,
+ (char *) &seconds,
+ (PINT) &bytes);
+ if (rv == NO_ERROR) {
+ if (seconds == 0xffffffff) {
+ PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+ *len = PR_NETADDR_SIZE(&fd->secret->md.peer_addr);
+ memcpy(addr, &fd->secret->md.peer_addr, *len);
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+ } else {
+ rv = getpeername((SOCKET)fd->secret->md.osfd,
+ (struct sockaddr *) addr, len);
+ if (rv == 0) {
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+ }
+}
+
+PRStatus
+_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ PRInt32 rv;
+
+ rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0) {
+ return PR_SUCCESS;
+ }
+ else {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ PRInt32 rv;
+
+ rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0) {
+ return PR_SUCCESS;
+ }
+ else {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+/* --- FILE IO ----------------------------------------------------------- */
+
+PROsfd
+_PR_MD_OPEN(const char *name, PRIntn osflags, PRIntn mode)
+{
+ HANDLE file;
+ PRInt32 access = 0;
+ PRInt32 flags = 0;
+ PRInt32 flag6 = 0;
+
+ if (osflags & PR_SYNC) {
+ flag6 = FILE_FLAG_WRITE_THROUGH;
+ }
+
+ if (osflags & PR_RDONLY || osflags & PR_RDWR) {
+ access |= GENERIC_READ;
+ }
+ if (osflags & PR_WRONLY || osflags & PR_RDWR) {
+ access |= GENERIC_WRITE;
+ }
+
+ if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) {
+ flags = CREATE_NEW;
+ }
+ else if (osflags & PR_CREATE_FILE) {
+ flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS;
+ }
+ else if (osflags & PR_TRUNCATE) {
+ flags = TRUNCATE_EXISTING;
+ }
+ else {
+ flags = OPEN_EXISTING;
+ }
+
+
+ flag6 |= FILE_FLAG_OVERLAPPED;
+
+ file = CreateFile(name,
+ access,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ flags,
+ flag6,
+ NULL);
+ if (file == INVALID_HANDLE_VALUE) {
+ _PR_MD_MAP_OPEN_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (osflags & PR_APPEND) {
+ if ( SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF ) {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ CloseHandle(file);
+ return -1;
+ }
+ }
+
+ return (PROsfd)file;
+}
+
+PROsfd
+_PR_MD_OPEN_FILE(const char *name, PRIntn osflags, PRIntn mode)
+{
+ HANDLE file;
+ PRInt32 access = 0;
+ PRInt32 flags = 0;
+ PRInt32 flag6 = 0;
+ SECURITY_ATTRIBUTES sa;
+ LPSECURITY_ATTRIBUTES lpSA = NULL;
+ PSECURITY_DESCRIPTOR pSD = NULL;
+ PACL pACL = NULL;
+
+ if (osflags & PR_SYNC) {
+ flag6 = FILE_FLAG_WRITE_THROUGH;
+ }
+
+ if (osflags & PR_RDONLY || osflags & PR_RDWR) {
+ access |= GENERIC_READ;
+ }
+ if (osflags & PR_WRONLY || osflags & PR_RDWR) {
+ access |= GENERIC_WRITE;
+ }
+
+ if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) {
+ flags = CREATE_NEW;
+ }
+ else if (osflags & PR_CREATE_FILE) {
+ flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS;
+ }
+ else if (osflags & PR_TRUNCATE) {
+ flags = TRUNCATE_EXISTING;
+ }
+ else {
+ flags = OPEN_EXISTING;
+ }
+
+
+ flag6 |= FILE_FLAG_OVERLAPPED;
+
+ if (osflags & PR_CREATE_FILE) {
+ if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable,
+ &pSD, &pACL) == PR_SUCCESS) {
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = pSD;
+ sa.bInheritHandle = FALSE;
+ lpSA = &sa;
+ }
+ }
+ file = CreateFile(name,
+ access,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ lpSA,
+ flags,
+ flag6,
+ NULL);
+ if (lpSA != NULL) {
+ _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
+ }
+ if (file == INVALID_HANDLE_VALUE) {
+ _PR_MD_MAP_OPEN_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (osflags & PR_APPEND) {
+ if ( SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF ) {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ CloseHandle(file);
+ return -1;
+ }
+ }
+
+ return (PROsfd)file;
+}
+
+PRInt32
+_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ PROsfd f = fd->secret->md.osfd;
+ PRUint32 bytes;
+ int rv, err;
+ LONG hiOffset = 0;
+ LONG loOffset;
+ LARGE_INTEGER offset; /* use for a normalized add of len to offset */
+
+ if (!fd->secret->md.sync_file_io) {
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+
+ me->md.overlapped.overlapped.Offset = SetFilePointer((HANDLE)f, 0, &me->md.overlapped.overlapped.OffsetHigh, FILE_CURRENT);
+ PR_ASSERT((me->md.overlapped.overlapped.Offset != 0xffffffff) || (GetLastError() == NO_ERROR));
+
+ if (fd->secret->inheritable == _PR_TRI_TRUE) {
+ rv = ReadFile((HANDLE)f,
+ (LPVOID)buf,
+ len,
+ &bytes,
+ &me->md.overlapped.overlapped);
+ if (rv != 0) {
+ loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
+ PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
+ return bytes;
+ }
+ err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ rv = GetOverlappedResult((HANDLE)f,
+ &me->md.overlapped.overlapped, &bytes, TRUE);
+ if (rv != 0) {
+ loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
+ PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
+ return bytes;
+ }
+ err = GetLastError();
+ }
+ if (err == ERROR_HANDLE_EOF) {
+ return 0;
+ } else {
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ }
+ } else {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)f);
+ PR_ASSERT(rv != 0);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ if (_native_threads_only) {
+ me->md.overlapped.overlapped.hEvent = me->md.thr_event;
+ }
+
+ _PR_THREAD_LOCK(me);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ me->io_pending = PR_TRUE;
+ me->state = _PR_IO_WAIT;
+ _PR_THREAD_UNLOCK(me);
+ me->io_fd = f;
+
+ rv = ReadFile((HANDLE)f,
+ (LPVOID)buf,
+ len,
+ &bytes,
+ &me->md.overlapped.overlapped);
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ if (err == ERROR_HANDLE_EOF) {
+ return 0;
+ }
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ }
+
+ if (_native_threads_only && rv) {
+ _native_thread_io_nowait(me, rv, bytes);
+ } else if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ if (me->md.blocked_io_error == ERROR_HANDLE_EOF) {
+ return 0;
+ }
+ _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ /* Apply the workaround from bug 70765 (see _PR_MD_WRITE)
+ * to the reading code, too. */
+
+ offset.LowPart = me->md.overlapped.overlapped.Offset;
+ offset.HighPart = me->md.overlapped.overlapped.OffsetHigh;
+ offset.QuadPart += me->md.blocked_io_bytes;
+
+ SetFilePointer((HANDLE)f, offset.LowPart, &offset.HighPart, FILE_BEGIN);
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+ }
+ } else {
+
+ rv = ReadFile((HANDLE)f,
+ (LPVOID)buf,
+ len,
+ &bytes,
+ NULL);
+ if (rv == 0) {
+ err = GetLastError();
+ /* ERROR_HANDLE_EOF can only be returned by async io */
+ PR_ASSERT(err != ERROR_HANDLE_EOF);
+ if (err == ERROR_BROKEN_PIPE) {
+ /* The write end of the pipe has been closed. */
+ return 0;
+ }
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ }
+ return bytes;
+ }
+}
+
+PRInt32
+_PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len)
+{
+ PROsfd f = fd->secret->md.osfd;
+ PRInt32 bytes;
+ int rv, err;
+ LONG hiOffset = 0;
+ LONG loOffset;
+ LARGE_INTEGER offset; /* use for the calculation of the new offset */
+
+ if (!fd->secret->md.sync_file_io) {
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+
+ me->md.overlapped.overlapped.Offset = SetFilePointer((HANDLE)f, 0, &me->md.overlapped.overlapped.OffsetHigh, FILE_CURRENT);
+ PR_ASSERT((me->md.overlapped.overlapped.Offset != 0xffffffff) || (GetLastError() == NO_ERROR));
+
+ if (fd->secret->inheritable == _PR_TRI_TRUE) {
+ rv = WriteFile((HANDLE)f,
+ (LPVOID)buf,
+ len,
+ &bytes,
+ &me->md.overlapped.overlapped);
+ if (rv != 0) {
+ loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
+ PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
+ return bytes;
+ }
+ err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ rv = GetOverlappedResult((HANDLE)f,
+ &me->md.overlapped.overlapped, &bytes, TRUE);
+ if (rv != 0) {
+ loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
+ PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
+ return bytes;
+ }
+ err = GetLastError();
+ }
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ } else {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)f);
+ PR_ASSERT(rv != 0);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ if (_native_threads_only) {
+ me->md.overlapped.overlapped.hEvent = me->md.thr_event;
+ }
+
+ _PR_THREAD_LOCK(me);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ me->io_pending = PR_TRUE;
+ me->state = _PR_IO_WAIT;
+ _PR_THREAD_UNLOCK(me);
+ me->io_fd = f;
+
+ rv = WriteFile((HANDLE)f,
+ buf,
+ len,
+ &bytes,
+ &(me->md.overlapped.overlapped));
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ _PR_MD_MAP_WRITE_ERROR(err);
+ return -1;
+ }
+
+ if (_native_threads_only && rv) {
+ _native_thread_io_nowait(me, rv, bytes);
+ } else if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ /*
+ * Moving the file pointer by a relative offset (FILE_CURRENT)
+ * does not work with a file on a network drive exported by a
+ * Win2K system. We still don't know why. A workaround is to
+ * move the file pointer by an absolute offset (FILE_BEGIN).
+ * (Bugzilla bug 70765)
+ */
+ offset.LowPart = me->md.overlapped.overlapped.Offset;
+ offset.HighPart = me->md.overlapped.overlapped.OffsetHigh;
+ offset.QuadPart += me->md.blocked_io_bytes;
+
+ SetFilePointer((HANDLE)f, offset.LowPart, &offset.HighPart, FILE_BEGIN);
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+ }
+ } else {
+ rv = WriteFile((HANDLE)f,
+ buf,
+ len,
+ &bytes,
+ NULL);
+ if (rv == 0) {
+ _PR_MD_MAP_WRITE_ERROR(GetLastError());
+ return -1;
+ }
+ return bytes;
+ }
+}
+
+PRInt32
+_PR_MD_SOCKETAVAILABLE(PRFileDesc *fd)
+{
+ PRInt32 result;
+
+ if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
+ return -1;
+ }
+ return result;
+}
+
+PRInt32
+_PR_MD_PIPEAVAILABLE(PRFileDesc *fd)
+{
+ if (NULL == fd) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ }
+ else {
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ }
+ return -1;
+}
+
+PROffset32
+_PR_MD_LSEEK(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence)
+{
+ DWORD moveMethod;
+ PROffset32 rv;
+
+ switch (whence) {
+ case PR_SEEK_SET:
+ moveMethod = FILE_BEGIN;
+ break;
+ case PR_SEEK_CUR:
+ moveMethod = FILE_CURRENT;
+ break;
+ case PR_SEEK_END:
+ moveMethod = FILE_END;
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+
+ rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, moveMethod);
+
+ /*
+ * If the lpDistanceToMoveHigh argument (third argument) is
+ * NULL, SetFilePointer returns 0xffffffff on failure.
+ */
+ if (-1 == rv) {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ }
+ return rv;
+}
+
+PROffset64
+_PR_MD_LSEEK64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence)
+{
+ DWORD moveMethod;
+ LARGE_INTEGER li;
+ DWORD err;
+
+ switch (whence) {
+ case PR_SEEK_SET:
+ moveMethod = FILE_BEGIN;
+ break;
+ case PR_SEEK_CUR:
+ moveMethod = FILE_CURRENT;
+ break;
+ case PR_SEEK_END:
+ moveMethod = FILE_END;
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+
+ li.QuadPart = offset;
+ li.LowPart = SetFilePointer((HANDLE)fd->secret->md.osfd,
+ li.LowPart, &li.HighPart, moveMethod);
+
+ if (0xffffffff == li.LowPart && (err = GetLastError()) != NO_ERROR) {
+ _PR_MD_MAP_LSEEK_ERROR(err);
+ li.QuadPart = -1;
+ }
+ return li.QuadPart;
+}
+
+/*
+ * This is documented to succeed on read-only files, but Win32's
+ * FlushFileBuffers functions fails with "access denied" in such a
+ * case. So we only signal an error if the error is *not* "access
+ * denied".
+ */
+PRInt32
+_PR_MD_FSYNC(PRFileDesc *fd)
+{
+ /*
+ * From the documentation:
+ *
+ * On Windows NT, the function FlushFileBuffers fails if hFile
+ * is a handle to console output. That is because console
+ * output is not buffered. The function returns FALSE, and
+ * GetLastError returns ERROR_INVALID_HANDLE.
+ *
+ * On the other hand, on Win95, it returns without error. I cannot
+ * assume that 0, 1, and 2 are console, because if someone closes
+ * System.out and then opens a file, they might get file descriptor
+ * 1. An error on *that* version of 1 should be reported, whereas
+ * an error on System.out (which was the original 1) should be
+ * ignored. So I use isatty() to ensure that such an error was
+ * because of this, and if it was, I ignore the error.
+ */
+
+ BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd);
+
+ if (!ok) {
+ DWORD err = GetLastError();
+
+ if (err != ERROR_ACCESS_DENIED) { /* from winerror.h */
+ _PR_MD_MAP_FSYNC_ERROR(err);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+PRInt32
+_PR_MD_CLOSE(PROsfd osfd, PRBool socket)
+{
+ PRInt32 rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (socket) {
+ rv = closesocket((SOCKET)osfd);
+ if (rv < 0) {
+ _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
+ }
+ } else {
+ rv = CloseHandle((HANDLE)osfd)?0:-1;
+ if (rv < 0) {
+ _PR_MD_MAP_CLOSE_ERROR(GetLastError());
+ }
+ }
+
+ if (rv == 0 && me->io_suspended) {
+ if (me->io_fd == osfd) {
+ PRBool fWait;
+
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_IO_WAIT;
+ /* The IO could have completed on another thread just after
+ * calling closesocket while the io_suspended flag was true.
+ * So we now grab the lock to do a safe check on io_pending to
+ * see if we need to wait or not.
+ */
+ fWait = me->io_pending;
+ me->io_suspended = PR_FALSE;
+ me->md.interrupt_disabled = PR_TRUE;
+ _PR_THREAD_UNLOCK(me);
+
+ if (fWait) {
+ _NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+ PR_ASSERT(me->io_pending == PR_FALSE);
+ /*
+ * I/O operation is no longer pending; the thread can now
+ * run on any cpu
+ */
+ _PR_THREAD_LOCK(me);
+ me->md.interrupt_disabled = PR_FALSE;
+ me->md.thr_bound_cpu = NULL;
+ me->io_suspended = PR_FALSE;
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(me);
+ }
+ }
+ return rv;
+}
+
+PRStatus
+_PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable)
+{
+ BOOL rv;
+
+ if (fd->secret->md.io_model_committed) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ rv = SetHandleInformation(
+ (HANDLE)fd->secret->md.osfd,
+ HANDLE_FLAG_INHERIT,
+ inheritable ? HANDLE_FLAG_INHERIT : 0);
+ if (0 == rv) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+void
+_PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported)
+{
+ if (imported) {
+ fd->secret->inheritable = _PR_TRI_UNKNOWN;
+ } else {
+ fd->secret->inheritable = _PR_TRI_FALSE;
+ }
+}
+
+void
+_PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd)
+{
+ DWORD flags;
+
+ PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable);
+ if (fd->secret->md.io_model_committed) {
+ return;
+ }
+ if (GetHandleInformation((HANDLE)fd->secret->md.osfd, &flags)) {
+ if (flags & HANDLE_FLAG_INHERIT) {
+ fd->secret->inheritable = _PR_TRI_TRUE;
+ } else {
+ fd->secret->inheritable = _PR_TRI_FALSE;
+ }
+ }
+}
+
+
+/* --- DIR IO ------------------------------------------------------------ */
+#define GetFileFromDIR(d) (d)->d_entry.cFileName
+#define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
+
+void FlipSlashes(char *cp, int len)
+{
+ while (--len >= 0) {
+ if (cp[0] == '/') {
+ cp[0] = PR_DIRECTORY_SEPARATOR;
+ }
+ cp = _mbsinc(cp);
+ }
+} /* end FlipSlashes() */
+
+/*
+**
+** Local implementations of standard Unix RTL functions which are not provided
+** by the VC RTL.
+**
+*/
+
+PRInt32
+_PR_MD_CLOSE_DIR(_MDDir *d)
+{
+ if ( d ) {
+ if (FindClose( d->d_hdl )) {
+ d->magic = (PRUint32)-1;
+ return 0;
+ } else {
+ _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
+ return -1;
+ }
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+}
+
+
+PRStatus
+_PR_MD_OPEN_DIR(_MDDir *d, const char *name)
+{
+ char filename[ MAX_PATH ];
+ int len;
+
+ len = strlen(name);
+ /* Need 5 bytes for \*.* and the trailing null byte. */
+ if (len + 5 > MAX_PATH) {
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
+ return PR_FAILURE;
+ }
+ strcpy(filename, name);
+
+ /*
+ * If 'name' ends in a slash or backslash, do not append
+ * another backslash.
+ */
+ if (IsPrevCharSlash(filename, filename + len)) {
+ len--;
+ }
+ strcpy(&filename[len], "\\*.*");
+ FlipSlashes( filename, strlen(filename) );
+
+ d->d_hdl = FindFirstFile( filename, &(d->d_entry) );
+ if ( d->d_hdl == INVALID_HANDLE_VALUE ) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ d->firstEntry = PR_TRUE;
+ d->magic = _MD_MAGIC_DIR;
+ return PR_SUCCESS;
+}
+
+char *
+_PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
+{
+ PRInt32 err;
+ BOOL rv;
+ char *fileName;
+
+ if ( d ) {
+ while (1) {
+ if (d->firstEntry) {
+ d->firstEntry = PR_FALSE;
+ rv = 1;
+ } else {
+ rv = FindNextFile(d->d_hdl, &(d->d_entry));
+ }
+ if (rv == 0) {
+ break;
+ }
+ fileName = GetFileFromDIR(d);
+ if ( (flags & PR_SKIP_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '\0')) {
+ continue;
+ }
+ if ( (flags & PR_SKIP_DOT_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '.') &&
+ (fileName[2] == '\0')) {
+ continue;
+ }
+ if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) {
+ continue;
+ }
+ return fileName;
+ }
+ err = GetLastError();
+ PR_ASSERT(NO_ERROR != err);
+ _PR_MD_MAP_READDIR_ERROR(err);
+ return NULL;
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+}
+
+PRInt32
+_PR_MD_DELETE(const char *name)
+{
+ if (DeleteFile(name)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_DELETE_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+void
+_PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
+{
+ PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
+ CopyMemory(prtm, filetime, sizeof(PRTime));
+#ifdef __GNUC__
+ *prtm = (*prtm - _pr_filetime_offset) / 10LL;
+#else
+ *prtm = (*prtm - _pr_filetime_offset) / 10i64;
+#endif
+
+#ifdef DEBUG
+ /* Doublecheck our calculation. */
+ {
+ SYSTEMTIME systime;
+ PRExplodedTime etm;
+ PRTime cmp; /* for comparison */
+ BOOL rv;
+
+ rv = FileTimeToSystemTime(filetime, &systime);
+ PR_ASSERT(0 != rv);
+
+ /*
+ * PR_ImplodeTime ignores wday and yday.
+ */
+ etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC;
+ etm.tm_sec = systime.wSecond;
+ etm.tm_min = systime.wMinute;
+ etm.tm_hour = systime.wHour;
+ etm.tm_mday = systime.wDay;
+ etm.tm_month = systime.wMonth - 1;
+ etm.tm_year = systime.wYear;
+ /*
+ * It is not well-documented what time zone the FILETIME's
+ * are in. WIN32_FIND_DATA is documented to be in UTC (GMT).
+ * But BY_HANDLE_FILE_INFORMATION is unclear about this.
+ * By our best judgement, we assume that FILETIME is in UTC.
+ */
+ etm.tm_params.tp_gmt_offset = 0;
+ etm.tm_params.tp_dst_offset = 0;
+ cmp = PR_ImplodeTime(&etm);
+
+ /*
+ * SYSTEMTIME is in milliseconds precision, so we convert PRTime's
+ * microseconds to milliseconds before doing the comparison.
+ */
+ PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC));
+ }
+#endif /* DEBUG */
+}
+
+PRInt32
+_PR_MD_STAT(const char *fn, struct stat *info)
+{
+ PRInt32 rv;
+
+ rv = _stat(fn, (struct _stat *)info);
+ if (-1 == rv) {
+ /*
+ * Check for MSVC runtime library _stat() bug.
+ * (It's really a bug in FindFirstFile().)
+ * If a pathname ends in a backslash or slash,
+ * e.g., c:\temp\ or c:/temp/, _stat() will fail.
+ * Note: a pathname ending in a slash (e.g., c:/temp/)
+ * can be handled by _stat() on NT but not on Win95.
+ *
+ * We remove the backslash or slash at the end and
+ * try again.
+ */
+
+ int len = strlen(fn);
+ if (len > 0 && len <= _MAX_PATH
+ && IsPrevCharSlash(fn, fn + len)) {
+ char newfn[_MAX_PATH + 1];
+
+ strcpy(newfn, fn);
+ newfn[len - 1] = '\0';
+ rv = _stat(newfn, (struct _stat *)info);
+ }
+ }
+
+ if (-1 == rv) {
+ _PR_MD_MAP_STAT_ERROR(errno);
+ }
+ return rv;
+}
+
+#define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
+
+static PRBool
+IsPrevCharSlash(const char *str, const char *current)
+{
+ const char *prev;
+
+ if (str >= current) {
+ return PR_FALSE;
+ }
+ prev = _mbsdec(str, current);
+ return (prev == current - 1) && _PR_IS_SLASH(*prev);
+}
+
+/*
+ * IsRootDirectory --
+ *
+ * Return PR_TRUE if the pathname 'fn' is a valid root directory,
+ * else return PR_FALSE. The char buffer pointed to by 'fn' must
+ * be writable. During the execution of this function, the contents
+ * of the buffer pointed to by 'fn' may be modified, but on return
+ * the original contents will be restored. 'buflen' is the size of
+ * the buffer pointed to by 'fn'.
+ *
+ * Root directories come in three formats:
+ * 1. / or \, meaning the root directory of the current drive.
+ * 2. C:/ or C:\, where C is a drive letter.
+ * 3. \\<server name>\<share point name>\ or
+ * \\<server name>\<share point name>, meaning the root directory
+ * of a UNC (Universal Naming Convention) name.
+ */
+
+static PRBool
+IsRootDirectory(char *fn, size_t buflen)
+{
+ char *p;
+ PRBool slashAdded = PR_FALSE;
+ PRBool rv = PR_FALSE;
+
+ if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') {
+ return PR_TRUE;
+ }
+
+ if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2])
+ && fn[3] == '\0') {
+ rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
+ return rv;
+ }
+
+ /* The UNC root directory */
+
+ if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) {
+ /* The 'server' part should have at least one character. */
+ p = &fn[2];
+ if (*p == '\0' || _PR_IS_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the next slash */
+ do {
+ p = _mbsinc(p);
+ } while (*p != '\0' && !_PR_IS_SLASH(*p));
+ if (*p == '\0') {
+ return PR_FALSE;
+ }
+
+ /* The 'share' part should have at least one character. */
+ p++;
+ if (*p == '\0' || _PR_IS_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the final slash */
+ do {
+ p = _mbsinc(p);
+ } while (*p != '\0' && !_PR_IS_SLASH(*p));
+ if (_PR_IS_SLASH(*p) && p[1] != '\0') {
+ return PR_FALSE;
+ }
+ if (*p == '\0') {
+ /*
+ * GetDriveType() doesn't work correctly if the
+ * path is of the form \\server\share, so we add
+ * a final slash temporarily.
+ */
+ if ((p + 1) < (fn + buflen)) {
+ *p++ = '\\';
+ *p = '\0';
+ slashAdded = PR_TRUE;
+ } else {
+ return PR_FALSE; /* name too long */
+ }
+ }
+ rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
+ /* restore the 'fn' buffer */
+ if (slashAdded) {
+ *--p = '\0';
+ }
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
+{
+ WIN32_FILE_ATTRIBUTE_DATA findFileData;
+
+ if (NULL == fn || '\0' == *fn) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+
+ if (!GetFileAttributesEx(fn, GetFileExInfoStandard, &findFileData)) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ info->type = PR_FILE_DIRECTORY;
+ } else {
+ info->type = PR_FILE_FILE;
+ }
+
+ info->size = findFileData.nFileSizeHigh;
+ info->size = (info->size << 32) + findFileData.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
+
+ if (0 == findFileData.ftCreationTime.dwLowDateTime &&
+ 0 == findFileData.ftCreationTime.dwHighDateTime) {
+ info->creationTime = info->modifyTime;
+ } else {
+ _PR_FileTimeToPRTime(&findFileData.ftCreationTime,
+ &info->creationTime);
+ }
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
+{
+ PRFileInfo64 info64;
+ PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64);
+ if (0 == rv)
+ {
+ info->type = info64.type;
+ info->size = (PRUint32) info64.size;
+ info->modifyTime = info64.modifyTime;
+ info->creationTime = info64.creationTime;
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
+{
+ int rv;
+
+ BY_HANDLE_FILE_INFORMATION hinfo;
+
+ rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
+ if (rv == FALSE) {
+ _PR_MD_MAP_FSTAT_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ info->type = PR_FILE_DIRECTORY;
+ }
+ else {
+ info->type = PR_FILE_FILE;
+ }
+
+ info->size = hinfo.nFileSizeHigh;
+ info->size = (info->size << 32) + hinfo.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
+ _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
+{
+ int rv;
+
+ BY_HANDLE_FILE_INFORMATION hinfo;
+
+ rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
+ if (rv == FALSE) {
+ _PR_MD_MAP_FSTAT_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ info->type = PR_FILE_DIRECTORY;
+ }
+ else {
+ info->type = PR_FILE_FILE;
+ }
+
+ info->size = hinfo.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
+ _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_RENAME(const char *from, const char *to)
+{
+ /* Does this work with dot-relative pathnames? */
+ if (MoveFile(from, to)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RENAME_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_ACCESS(const char *name, PRAccessHow how)
+{
+ PRInt32 rv;
+
+ switch (how) {
+ case PR_ACCESS_WRITE_OK:
+ rv = _access(name, 02);
+ break;
+ case PR_ACCESS_READ_OK:
+ rv = _access(name, 04);
+ break;
+ case PR_ACCESS_EXISTS:
+ rv = _access(name, 00);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_ACCESS_ERROR(errno);
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_MKDIR(const char *name, PRIntn mode)
+{
+ /* XXXMB - how to translate the "mode"??? */
+ if (CreateDirectory(name, NULL)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_MKDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_MAKE_DIR(const char *name, PRIntn mode)
+{
+ BOOL rv;
+ SECURITY_ATTRIBUTES sa;
+ LPSECURITY_ATTRIBUTES lpSA = NULL;
+ PSECURITY_DESCRIPTOR pSD = NULL;
+ PACL pACL = NULL;
+
+ if (_PR_NT_MakeSecurityDescriptorACL(mode, dirAccessTable,
+ &pSD, &pACL) == PR_SUCCESS) {
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = pSD;
+ sa.bInheritHandle = FALSE;
+ lpSA = &sa;
+ }
+ rv = CreateDirectory(name, lpSA);
+ if (lpSA != NULL) {
+ _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
+ }
+ if (rv) {
+ return 0;
+ } else {
+ _PR_MD_MAP_MKDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_RMDIR(const char *name)
+{
+ if (RemoveDirectory(name)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RMDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRStatus
+_PR_MD_LOCKFILE(PROsfd f)
+{
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+
+ _PR_THREAD_LOCK(me);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ me->io_pending = PR_TRUE;
+ me->state = _PR_IO_WAIT;
+ _PR_THREAD_UNLOCK(me);
+
+ rv = LockFileEx((HANDLE)f,
+ LOCKFILE_EXCLUSIVE_LOCK,
+ 0,
+ 0x7fffffff,
+ 0,
+ &me->md.overlapped.overlapped);
+
+ if (_native_threads_only) {
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return PR_FAILURE;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ if (rv == FALSE) {
+ err = GetLastError();
+ PR_ASSERT(err != ERROR_IO_PENDING);
+ _PR_MD_MAP_LOCKF_ERROR(err);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+ }
+
+ /* HACK AROUND NT BUG
+ * NT 3.51 has a bug. In NT 3.51, if LockFileEx returns true, you
+ * don't get any completion on the completion port. This is a bug.
+ *
+ * They fixed it on NT4.0 so that you do get a completion.
+ *
+ * If we pretend we won't get a completion, NSPR gets confused later
+ * when the unexpected completion arrives. If we assume we do get
+ * a completion, we hang on 3.51. Worse, Microsoft informs me that the
+ * behavior varies on 3.51 depending on if you are using a network
+ * file system or a local disk!
+ *
+ * Solution: For now, _nt_version_gets_lockfile_completion is set
+ * depending on whether or not this system is EITHER
+ * - running NT 4.0
+ * - running NT 3.51 with a service pack greater than 5.
+ *
+ * In the meantime, this code may not work on network file systems.
+ *
+ */
+
+ if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return PR_FAILURE;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ _PR_MD_MAP_LOCKF_ERROR(err);
+ return PR_FAILURE;
+ }
+#ifdef _NEED_351_FILE_LOCKING_HACK
+ else if (rv) {
+ /* If this is NT 3.51 and the file is local, then we won't get a
+ * completion back from LockFile when it succeeded.
+ */
+ if (_nt_version_gets_lockfile_completion == PR_FALSE) {
+ if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ return PR_SUCCESS;
+ }
+ }
+ }
+#endif /* _NEED_351_FILE_LOCKING_HACK */
+
+ if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(me);
+ return PR_FAILURE;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+PRStatus
+_PR_MD_TLOCKFILE(PROsfd f)
+{
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+
+ _PR_THREAD_LOCK(me);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return -1;
+ }
+ me->io_pending = PR_TRUE;
+ me->state = _PR_IO_WAIT;
+ _PR_THREAD_UNLOCK(me);
+
+ rv = LockFileEx((HANDLE)f,
+ LOCKFILE_FAIL_IMMEDIATELY|LOCKFILE_EXCLUSIVE_LOCK,
+ 0,
+ 0x7fffffff,
+ 0,
+ &me->md.overlapped.overlapped);
+ if (_native_threads_only) {
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return PR_FAILURE;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ if (rv == FALSE) {
+ err = GetLastError();
+ PR_ASSERT(err != ERROR_IO_PENDING);
+ _PR_MD_MAP_LOCKF_ERROR(err);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+ }
+ if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return PR_FAILURE;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ _PR_MD_MAP_LOCKF_ERROR(err);
+ return PR_FAILURE;
+ }
+#ifdef _NEED_351_FILE_LOCKING_HACK
+ else if (rv) {
+ /* If this is NT 3.51 and the file is local, then we won't get a
+ * completion back from LockFile when it succeeded.
+ */
+ if (_nt_version_gets_lockfile_completion == PR_FALSE) {
+ if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return PR_FAILURE;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ return PR_SUCCESS;
+ }
+ }
+ }
+#endif /* _NEED_351_FILE_LOCKING_HACK */
+
+ if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ _PR_THREAD_LOCK(me);
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ return PR_FAILURE;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ return PR_FAILURE;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+
+PRStatus
+_PR_MD_UNLOCKFILE(PROsfd f)
+{
+ PRInt32 rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+
+ rv = UnlockFileEx((HANDLE)f,
+ 0,
+ 0x7fffffff,
+ 0,
+ &me->md.overlapped.overlapped);
+
+ if (rv) {
+ return PR_SUCCESS;
+ }
+ else {
+ int err = GetLastError();
+ _PR_MD_MAP_LOCKF_ERROR(err);
+ return PR_FAILURE;
+ }
+}
+
+void
+_PR_MD_MAKE_NONBLOCK(PRFileDesc *f)
+{
+ /*
+ * On NT, we either call _md_Associate() or _md_MakeNonblock(),
+ * depending on whether the socket is blocking or not.
+ *
+ * Once we associate a socket with the io completion port,
+ * there is no way to disassociate it from the io completion
+ * port. So we have to call _md_Associate/_md_MakeNonblock
+ * lazily.
+ */
+}
+
+#ifdef _NEED_351_FILE_LOCKING_HACK
+/***************
+**
+** Lockfile hacks
+**
+** The following code is a hack to work around a microsoft bug with lockfile.
+** The problem is that on NT 3.51, if LockFileEx() succeeds, you never
+** get a completion back for files that are on local disks. So, we need to
+** know if a file is local or remote so we can tell if we should expect
+** a completion.
+**
+** The only way to check if a file is local or remote based on the handle is
+** to get the serial number for the volume it is mounted on and then to
+** compare that with mounted drives. This code caches the volume numbers of
+** fixed disks and does a relatively quick check.
+**
+** Locking: Since the only thing we ever do when multithreaded is a 32bit
+** assignment, we probably don't need locking. It is included just
+** case anyway.
+**
+** Limitations: Does not work on floppies because they are too slow
+** Unknown if it will work on wierdo 3rd party file systems
+**
+****************
+*/
+
+/* There can only be 26 drive letters on NT */
+#define _PR_MAX_DRIVES 26
+
+_MDLock cachedVolumeLock;
+DWORD dwCachedVolumeSerialNumbers[_PR_MAX_DRIVES] = {0};
+DWORD dwLastCachedDrive = 0;
+DWORD dwRemoveableDrivesToCheck = 0; /* bitmask for removeable drives */
+
+PRBool IsFileLocalInit()
+{
+ TCHAR lpBuffer[_PR_MAX_DRIVES*5];
+ DWORD nBufferLength = _PR_MAX_DRIVES*5;
+ DWORD nBufferNeeded = GetLogicalDriveStrings(0, NULL);
+ DWORD dwIndex = 0;
+ DWORD dwDriveType;
+ DWORD dwVolumeSerialNumber;
+ DWORD dwDriveIndex = 0;
+ DWORD oldmode = (DWORD) -1;
+
+ _MD_NEW_LOCK(&cachedVolumeLock);
+
+ nBufferNeeded = GetLogicalDriveStrings(nBufferLength, lpBuffer);
+ if (nBufferNeeded == 0 || nBufferNeeded > nBufferLength) {
+ return PR_FALSE;
+ }
+
+ // Calling GetVolumeInformation on a removeable drive where the
+ // disk is currently removed will cause a dialog box to the
+ // console. This is not good.
+ // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
+ // damn dialog.
+
+ dwCachedVolumeSerialNumbers[dwDriveIndex] = 0;
+ oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
+
+ // now loop through the logical drives
+ while(lpBuffer[dwIndex] != TEXT('\0'))
+ {
+ // skip the floppy drives. This is *SLOW*
+ if ((lpBuffer[dwIndex] == TEXT('A')) || (lpBuffer[dwIndex] == TEXT('B')))
+ /* Skip over floppies */;
+ else
+ {
+ dwDriveIndex = (lpBuffer[dwIndex] - TEXT('A'));
+
+ dwDriveType = GetDriveType(&lpBuffer[dwIndex]);
+
+ switch(dwDriveType)
+ {
+ // Ignore these drive types
+ case 0:
+ case 1:
+ case DRIVE_REMOTE:
+ default: // If the drive type is unknown, ignore it.
+ break;
+
+ // Removable media drives can have different serial numbers
+ // at different times, so cache the current serial number
+ // but keep track of them so they can be rechecked if necessary.
+ case DRIVE_REMOVABLE:
+
+ // CDROM is a removable media
+ case DRIVE_CDROM:
+
+ // no idea if ramdisks can change serial numbers or not
+ // but it doesn't hurt to treat them as removable.
+
+ case DRIVE_RAMDISK:
+
+
+ // Here is where we keep track of removable drives.
+ dwRemoveableDrivesToCheck |= 1 << dwDriveIndex;
+
+ // removable drives fall through to fixed drives and get cached.
+
+ case DRIVE_FIXED:
+
+ // cache volume serial numbers.
+ if (GetVolumeInformation(
+ &lpBuffer[dwIndex],
+ NULL, 0,
+ &dwVolumeSerialNumber,
+ NULL, NULL, NULL, 0)
+ )
+ {
+ if (dwLastCachedDrive < dwDriveIndex) {
+ dwLastCachedDrive = dwDriveIndex;
+ }
+ dwCachedVolumeSerialNumbers[dwDriveIndex] = dwVolumeSerialNumber;
+ }
+
+ break;
+ }
+ }
+
+ dwIndex += lstrlen(&lpBuffer[dwIndex]) +1;
+ }
+
+ if (oldmode != (DWORD) -1) {
+ SetErrorMode(oldmode);
+ oldmode = (DWORD) -1;
+ }
+
+ return PR_TRUE;
+}
+
+PRInt32 IsFileLocal(HANDLE hFile)
+{
+ DWORD dwIndex = 0, dwMask;
+ BY_HANDLE_FILE_INFORMATION Info;
+ TCHAR szDrive[4] = TEXT("C:\\");
+ DWORD dwVolumeSerialNumber;
+ DWORD oldmode = (DWORD) -1;
+ int rv = _PR_REMOTE_FILE;
+
+ if (!GetFileInformationByHandle(hFile, &Info)) {
+ return -1;
+ }
+
+ // look to see if the volume serial number has been cached.
+ _MD_LOCK(&cachedVolumeLock);
+ while(dwIndex <= dwLastCachedDrive)
+ if (dwCachedVolumeSerialNumbers[dwIndex++] == Info.dwVolumeSerialNumber)
+ {
+ _MD_UNLOCK(&cachedVolumeLock);
+ return _PR_LOCAL_FILE;
+ }
+ _MD_UNLOCK(&cachedVolumeLock);
+
+ // volume serial number not found in the cache. Check removable files.
+ // removable drives are noted as a bitmask. If the bit associated with
+ // a specific drive is set, then we should query its volume serial number
+ // as its possible it has changed.
+ dwMask = dwRemoveableDrivesToCheck;
+ dwIndex = 0;
+
+ while(dwMask)
+ {
+ while(!(dwMask & 1))
+ {
+ dwIndex++;
+ dwMask = dwMask >> 1;
+ }
+
+ szDrive[0] = TEXT('A')+ (TCHAR) dwIndex;
+
+ // Calling GetVolumeInformation on a removeable drive where the
+ // disk is currently removed will cause a dialog box to the
+ // console. This is not good.
+ // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
+ // dialog.
+
+ oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
+
+ if (GetVolumeInformation(
+ szDrive,
+ NULL, 0,
+ &dwVolumeSerialNumber,
+ NULL, NULL, NULL, 0)
+ )
+ {
+ if (dwVolumeSerialNumber == Info.dwVolumeSerialNumber)
+ {
+ _MD_LOCK(&cachedVolumeLock);
+ if (dwLastCachedDrive < dwIndex) {
+ dwLastCachedDrive = dwIndex;
+ }
+ dwCachedVolumeSerialNumbers[dwIndex] = dwVolumeSerialNumber;
+ _MD_UNLOCK(&cachedVolumeLock);
+ rv = _PR_LOCAL_FILE;
+ }
+ }
+ if (oldmode != (DWORD) -1) {
+ SetErrorMode(oldmode);
+ oldmode = (DWORD) -1;
+ }
+
+ if (rv == _PR_LOCAL_FILE) {
+ return _PR_LOCAL_FILE;
+ }
+
+ dwIndex++;
+ dwMask = dwMask >> 1;
+ }
+
+ return _PR_REMOTE_FILE;
+}
+#endif /* _NEED_351_FILE_LOCKING_HACK */
+
+PR_IMPLEMENT(PRStatus) PR_NT_CancelIo(PRFileDesc *fd)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRBool fWait;
+ PRFileDesc *bottom;
+
+ bottom = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
+ if (!me->io_suspended || (NULL == bottom) ||
+ (me->io_fd != bottom->secret->md.osfd)) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return PR_FAILURE;
+ }
+ /*
+ * The CancelIO operation has to be issued by the same NT thread that
+ * issued the I/O operation
+ */
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || (me->cpu == me->md.thr_bound_cpu));
+ if (me->io_pending) {
+ if (!CancelIo((HANDLE)bottom->secret->md.osfd)) {
+ PR_SetError(PR_INVALID_STATE_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ }
+ _PR_THREAD_LOCK(me);
+ fWait = me->io_pending;
+ me->io_suspended = PR_FALSE;
+ me->state = _PR_IO_WAIT;
+ me->md.interrupt_disabled = PR_TRUE;
+ _PR_THREAD_UNLOCK(me);
+ if (fWait) {
+ _NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ _PR_THREAD_LOCK(me);
+ me->md.interrupt_disabled = PR_FALSE;
+ me->md.thr_bound_cpu = NULL;
+ me->io_suspended = PR_FALSE;
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(me);
+ return PR_SUCCESS;
+}
+
+static PROsfd _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr *addr, int *addrlen, PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ SOCKET sock;
+ PRInt32 rv, err;
+ fd_set rd;
+ struct timeval tv, *tvp;
+
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ while ((sock = accept(osfd, addr, addrlen)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL,
+ NULL)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ } else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ break;
+ }
+ }
+ } else if (timeout == PR_INTERVAL_NO_WAIT) {
+ if ((sock = accept(osfd, addr, addrlen)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ } else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+ }
+ } else {
+retry:
+ if ((sock = accept(osfd, addr, addrlen)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+
+ rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL, tvp);
+ if (rv > 0) {
+ goto retry;
+ } else if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ } else {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ }
+ } else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+ }
+ }
+ return (PROsfd)sock;
+}
+
+static PRInt32 _nt_nonblock_connect(PRFileDesc *fd, struct sockaddr *addr, int addrlen, PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv;
+ int err;
+ fd_set wr, ex;
+ struct timeval tv, *tvp;
+ int len;
+
+ if ((rv = connect(osfd, addr, addrlen)) == -1) {
+ if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wr);
+ FD_ZERO(&ex);
+ FD_SET((SOCKET)osfd, &wr);
+ FD_SET((SOCKET)osfd, &ex);
+ if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wr, &ex,
+ tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return rv;
+ }
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ /* Call Sleep(0) to work around a Winsock timeing bug. */
+ Sleep(0);
+ if (FD_ISSET((SOCKET)osfd, &ex)) {
+ len = sizeof(err);
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
+ (char *) &err, &len) == SOCKET_ERROR) {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return -1;
+ }
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ return -1;
+ }
+ PR_ASSERT(FD_ISSET((SOCKET)osfd, &wr));
+ rv = 0;
+ } else {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+ }
+ return rv;
+}
+
+static PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, int flags, PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set rd;
+ int osflags;
+
+ if (0 == flags) {
+ osflags = 0;
+ } else {
+ PR_ASSERT(PR_MSG_PEEK == flags);
+ osflags = MSG_PEEK;
+ }
+ while ((rv = recv(osfd,buf,len,osflags)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL,
+ tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ } else if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ } else {
+ _PR_MD_MAP_RECV_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+}
+
+static PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set wd;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < len) {
+ while ((rv = send(osfd,buf,len,0)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL,
+ tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return -1;
+ }
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ } else {
+ _PR_MD_MAP_SEND_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (bytesSent < len) {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL,
+ tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return -1;
+ }
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+static PRInt32 _nt_nonblock_writev(PRFileDesc *fd, const PRIOVec *iov, int size, PRIntervalTime timeout)
+{
+ int index;
+ int sent = 0;
+ int rv;
+
+ for (index=0; index<size; index++) {
+ rv = _nt_nonblock_send(fd, iov[index].iov_base, iov[index].iov_len, timeout);
+ if (rv > 0) {
+ sent += rv;
+ }
+ if ( rv != iov[index].iov_len ) {
+ if (rv < 0) {
+ if (fd->secret->nonblocking
+ && (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ && (sent > 0)) {
+ return sent;
+ } else {
+ return -1;
+ }
+ }
+ /* Only a nonblocking socket can have partial sends */
+ PR_ASSERT(fd->secret->nonblocking);
+ return sent;
+ }
+ }
+
+ return sent;
+}
+
+static PRInt32 _nt_nonblock_sendto(
+ PRFileDesc *fd, const char *buf, int len,
+ const struct sockaddr *addr, int addrlen, PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set wd;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < len) {
+ while ((rv = sendto(osfd,buf,len,0, addr, addrlen)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL,
+ tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return -1;
+ }
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ } else {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (bytesSent < len) {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL,
+ tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return -1;
+ }
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+static PRInt32 _nt_nonblock_recvfrom(PRFileDesc *fd, char *buf, int len, struct sockaddr *addr, int *addrlen, PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set rd;
+
+ while ((rv = recvfrom(osfd,buf,len,0,addr, addrlen)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL,
+ tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ } else if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ } else {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+}
+
+/*
+ * UDP support: the continuation thread functions and recvfrom and sendto.
+ */
+
+static void pt_InsertTimedInternal(pt_Continuation *op)
+{
+ PRInt32 delta = 0;
+ pt_Continuation *t_op = NULL;
+ PRIntervalTime now = PR_IntervalNow(), op_tmo, qd_tmo;
+
+ /*
+ * If this element operation isn't timed, it gets queued at the
+ * end of the list (just after pt_tq.tail) and we're
+ * finishd early.
+ */
+ if (PR_INTERVAL_NO_TIMEOUT == op->timeout)
+ {
+ t_op = pt_tq.tail; /* put it at the end */
+ goto done;
+ }
+
+ /*
+ * The rest of this routine actaully deals with timed ops.
+ */
+
+ if (NULL != pt_tq.op)
+ {
+ /*
+ * To find where in the list to put the new operation, form
+ * the absolute time the operations in question will expire.
+ *
+ * The new operation ('op') will expire at now() + op->timeout.
+ *
+ * The operation that will time out furthest in the future will
+ * do so at pt_tq.epoch + pt_tq.op->timeout.
+ *
+ * Subsequently earlier timeouts are computed based on the latter
+ * knowledge by subracting the timeout deltas that are stored in
+ * the operation list. There are operation[n]->timeout ticks
+ * between the expiration of operation[n-1] and operation[n].e e
+ *
+ * Therefore, the operation[n-1] will expire operation[n]->timeout
+ * ticks prior to operation[n].
+ *
+ * This should be easy!
+ */
+ t_op = pt_tq.op; /* running pointer to queued op */
+ op_tmo = now + op->timeout; /* that's in absolute ticks */
+ qd_tmo = pt_tq.epoch + t_op->timeout; /* likewise */
+
+ do
+ {
+ /*
+ * If 'op' expires later than t_op, then insert 'op' just
+ * ahead of t_op. Otherwise, compute when operation[n-1]
+ * expires and try again.
+ *
+ * The actual different between the expiriation of 'op'
+ * and the current operation what becomes the new operaton's
+ * timeout interval. That interval is also subtracted from
+ * the interval of the operation immediately following where
+ * we stick 'op' (unless the next one isn't timed). The new
+ * timeout assigned to 'op' takes into account the values of
+ * now() and when the previous intervals were compured.
+ */
+ delta = op_tmo - qd_tmo;
+ if (delta >= 0)
+ {
+ op->timeout += (now - pt_tq.epoch);
+ goto done;
+ }
+
+ qd_tmo -= t_op->timeout; /* previous operaton expiration */
+ t_op = t_op->prev; /* point to previous operation */
+ if (NULL != t_op) {
+ qd_tmo += t_op->timeout;
+ }
+ } while (NULL != t_op);
+
+ /*
+ * If we got here we backed off the head of the list. That means that
+ * this timed entry has to go at the head of the list. This is just
+ * about like having an empty timer list.
+ */
+ delta = op->timeout; /* $$$ is this right? */
+ }
+
+done:
+
+ /*
+ * Insert 'op' into the queue just after t_op or if t_op is null,
+ * at the head of the list.
+ *
+ * If t_op is NULL, the list is currently empty and this is pretty
+ * easy.
+ */
+ if (NULL == t_op)
+ {
+ op->prev = NULL;
+ op->next = pt_tq.head;
+ pt_tq.head = op;
+ if (NULL == pt_tq.tail) {
+ pt_tq.tail = op;
+ }
+ else {
+ op->next->prev = op;
+ }
+ }
+ else
+ {
+ op->prev = t_op;
+ op->next = t_op->next;
+ if (NULL != op->prev) {
+ op->prev->next = op;
+ }
+ if (NULL != op->next) {
+ op->next->prev = op;
+ }
+ if (t_op == pt_tq.tail) {
+ pt_tq.tail = op;
+ }
+ }
+
+ /*
+ * Are we adjusting our epoch, etc? Are we replacing
+ * what was previously the element due to expire furthest
+ * out in the future? Is this even a timed operation?
+ */
+ if (PR_INTERVAL_NO_TIMEOUT != op->timeout)
+ {
+ if ((NULL == pt_tq.op) /* we're the one and only */
+ || (t_op == pt_tq.op)) /* we're replacing */
+ {
+ pt_tq.op = op;
+ pt_tq.epoch = now;
+ }
+ }
+
+ pt_tq.op_count += 1;
+
+} /* pt_InsertTimedInternal */
+
+/*
+ * function: pt_FinishTimed
+ *
+ * Takes the finished operation out of the timed queue. It
+ * notifies the initiating thread that the opertions is
+ * complete and returns to the caller the value of the next
+ * operation in the list (or NULL).
+ */
+static pt_Continuation *pt_FinishTimedInternal(pt_Continuation *op)
+{
+ pt_Continuation *next;
+
+ /* remove this one from the list */
+ if (NULL == op->prev) {
+ pt_tq.head = op->next;
+ }
+ else {
+ op->prev->next = op->next;
+ }
+ if (NULL == op->next) {
+ pt_tq.tail = op->prev;
+ }
+ else {
+ op->next->prev = op->prev;
+ }
+
+ /* did we happen to hit the timed op? */
+ if (op == pt_tq.op) {
+ pt_tq.op = op->prev;
+ }
+
+ next = op->next;
+ op->next = op->prev = NULL;
+ op->status = pt_continuation_done;
+
+ pt_tq.op_count -= 1;
+#if defined(DEBUG)
+ pt_debug.continuationsServed += 1;
+#endif
+ PR_NotifyCondVar(op->complete);
+
+ return next;
+} /* pt_FinishTimedInternal */
+
+static void ContinuationThread(void *arg)
+{
+ /* initialization */
+ fd_set readSet, writeSet, exceptSet;
+ struct timeval tv;
+ SOCKET *pollingList = 0; /* list built for polling */
+ PRIntn pollingListUsed; /* # entries used in the list */
+ PRIntn pollingListNeeded; /* # entries needed this time */
+ PRIntn pollingSlotsAllocated = 0; /* # entries available in list */
+ PRIntervalTime mx_select_ticks = PR_MillisecondsToInterval(PT_DEFAULT_SELECT_MSEC);
+
+ /* do some real work */
+ while (1)
+ {
+ PRIntn rv;
+ PRStatus status;
+ PRIntn pollIndex;
+ pt_Continuation *op;
+ PRIntervalTime now = PR_IntervalNow();
+ PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
+
+ PR_Lock(pt_tq.ml);
+ while (NULL == pt_tq.head)
+ {
+ status = PR_WaitCondVar(pt_tq.new_op, PR_INTERVAL_NO_TIMEOUT);
+ if ((PR_FAILURE == status)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
+ break;
+ }
+ }
+ pollingListNeeded = pt_tq.op_count;
+ PR_Unlock(pt_tq.ml);
+
+ /* Okay. We're history */
+ if ((PR_FAILURE == status)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
+ break;
+ }
+
+ /*
+ * We are not holding the pt_tq.ml lock now, so more items may
+ * get added to pt_tq during this window of time. We hope
+ * that 10 more spaces in the polling list should be enough.
+ */
+
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+ FD_ZERO(&exceptSet);
+ pollingListNeeded += 10;
+ if (pollingListNeeded > pollingSlotsAllocated)
+ {
+ if (NULL != pollingList) {
+ PR_DELETE(pollingList);
+ }
+ pollingList = PR_MALLOC(pollingListNeeded * sizeof(PRPollDesc));
+ PR_ASSERT(NULL != pollingList);
+ pollingSlotsAllocated = pollingListNeeded;
+ }
+
+#if defined(DEBUG)
+ if (pollingListNeeded > pt_debug.pollingListMax) {
+ pt_debug.pollingListMax = pollingListUsed;
+ }
+#endif
+
+ /*
+ * Build up a polling list.
+ * This list is sorted on time. Operations that have been
+ * interrupted are completed and not included in the list.
+ * There is an assertion that the operation is in progress.
+ */
+ pollingListUsed = 0;
+ PR_Lock(pt_tq.ml);
+
+ for (op = pt_tq.head; NULL != op;)
+ {
+ if (pt_continuation_abort == op->status)
+ {
+ op->result.code = -1;
+ op->syserrno = WSAEINTR;
+ op = pt_FinishTimedInternal(op);
+ }
+ else
+ {
+ PR_ASSERT(pt_continuation_done != op->status);
+ op->status = pt_continuation_inprogress;
+ if (op->event & PR_POLL_READ) {
+ FD_SET(op->arg1.osfd, &readSet);
+ }
+ if (op->event & PR_POLL_WRITE) {
+ FD_SET(op->arg1.osfd, &writeSet);
+ }
+ if (op->event & PR_POLL_EXCEPT) {
+ FD_SET(op->arg1.osfd, &exceptSet);
+ }
+ pollingList[pollingListUsed] = op->arg1.osfd;
+ pollingListUsed += 1;
+ if (pollingListUsed == pollingSlotsAllocated) {
+ break;
+ }
+ op = op->next;
+ }
+ }
+
+ PR_Unlock(pt_tq.ml);
+
+ /*
+ * If 'op' isn't NULL at this point, then we didn't get to
+ * the end of the list. That means that more items got added
+ * to the list than we anticipated. So, forget this iteration,
+ * go around the horn again.
+ * One would hope this doesn't happen all that often.
+ */
+ if (NULL != op)
+ {
+#if defined(DEBUG)
+ pt_debug.predictionsFoiled += 1; /* keep track */
+#endif
+ continue; /* make it rethink things */
+ }
+
+ /* there's a chance that all ops got blown away */
+ if (NULL == pt_tq.head) {
+ continue;
+ }
+ /* if not, we know this is the shortest timeout */
+ timeout = pt_tq.head->timeout;
+
+ /*
+ * We don't want to wait forever on this poll. So keep
+ * the interval down. The operations, if they are timed,
+ * still have to timeout, while those that are not timed
+ * should persist forever. But they may be aborted. That's
+ * what this anxiety is all about.
+ */
+ if (timeout > mx_select_ticks) {
+ timeout = mx_select_ticks;
+ }
+
+ if (PR_INTERVAL_NO_TIMEOUT != pt_tq.head->timeout) {
+ pt_tq.head->timeout -= timeout;
+ }
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC;
+
+ rv = select(0, &readSet, &writeSet, &exceptSet, &tv);
+
+ if (0 == rv) /* poll timed out - what about leading op? */
+ {
+ if (0 == pt_tq.head->timeout)
+ {
+ /*
+ * The leading element of the timed queue has timed
+ * out. Get rid of it. In any case go around the
+ * loop again, computing the polling list, checking
+ * for interrupted operations.
+ */
+ PR_Lock(pt_tq.ml);
+ do
+ {
+ pt_tq.head->result.code = -1;
+ pt_tq.head->syserrno = WSAETIMEDOUT;
+ op = pt_FinishTimedInternal(pt_tq.head);
+ } while ((NULL != op) && (0 == op->timeout));
+ PR_Unlock(pt_tq.ml);
+ }
+ continue;
+ }
+
+ if (-1 == rv && (WSAGetLastError() == WSAEINTR
+ || WSAGetLastError() == WSAEINPROGRESS))
+ {
+ continue; /* go around the loop again */
+ }
+
+ /*
+ * select() says that something in our list is ready for some more
+ * action or is an invalid fd. Find it, load up the operation and
+ * see what happens.
+ */
+
+ PR_ASSERT(rv > 0 || WSAGetLastError() == WSAENOTSOCK);
+
+
+ /*
+ * $$$ There's a problem here. I'm running the operations list
+ * and I'm not holding any locks. I don't want to hold the lock
+ * and do the operation, so this is really messed up..
+ *
+ * This may work out okay. The rule is that only this thread,
+ * the continuation thread, can remove elements from the list.
+ * Therefore, the list is at worst, longer than when we built
+ * the polling list.
+ */
+ op = pt_tq.head;
+ for (pollIndex = 0; pollIndex < pollingListUsed; ++pollIndex)
+ {
+ PRInt16 revents = 0;
+
+ PR_ASSERT(NULL != op);
+
+ /*
+ * This one wants attention. Redo the operation.
+ * We know that there can only be more elements
+ * in the op list than we knew about when we created
+ * the poll list. Therefore, we might have to skip
+ * a few ops to find the right one to operation on.
+ */
+ while (pollingList[pollIndex] != op->arg1.osfd )
+ {
+ op = op->next;
+ PR_ASSERT(NULL != op);
+ }
+
+ if (FD_ISSET(op->arg1.osfd, &readSet)) {
+ revents |= PR_POLL_READ;
+ }
+ if (FD_ISSET(op->arg1.osfd, &writeSet)) {
+ revents |= PR_POLL_WRITE;
+ }
+ if (FD_ISSET(op->arg1.osfd, &exceptSet)) {
+ revents |= PR_POLL_EXCEPT;
+ }
+
+ /*
+ * Sip over all those not in progress. They'll be
+ * pruned next time we build a polling list. Call
+ * the continuation function. If it reports completion,
+ * finish off the operation.
+ */
+ if (revents && (pt_continuation_inprogress == op->status)
+ && (op->function(op, revents)))
+ {
+ PR_Lock(pt_tq.ml);
+ op = pt_FinishTimedInternal(op);
+ PR_Unlock(pt_tq.ml);
+ }
+ }
+ }
+ if (NULL != pollingList) {
+ PR_DELETE(pollingList);
+ }
+} /* ContinuationThread */
+
+static int pt_Continue(pt_Continuation *op)
+{
+ PRStatus rv;
+ /* Finish filling in the blank slots */
+ op->status = pt_continuation_sumbitted;
+ op->complete = PR_NewCondVar(pt_tq.ml);
+
+ PR_Lock(pt_tq.ml); /* we provide the locking */
+
+ pt_InsertTimedInternal(op); /* insert in the structure */
+
+ PR_NotifyCondVar(pt_tq.new_op); /* notify the continuation thread */
+
+ while (pt_continuation_done != op->status) /* wait for completion */
+ {
+ rv = PR_WaitCondVar(op->complete, PR_INTERVAL_NO_TIMEOUT);
+ /*
+ * If we get interrupted, we set state the continuation thread will
+ * see and allow it to finish the I/O operation w/ error. That way
+ * the rule that only the continuation thread is removing elements
+ * from the list is still valid.
+ *
+ * Don't call interrupt on the continuation thread. That'll just
+ * piss him off. He's cycling around at least every mx_select_ticks
+ * anyhow and should notice the request in there.
+ */
+ if ((PR_FAILURE == rv)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
+ op->status = pt_continuation_abort; /* our status */
+ }
+ }
+
+ PR_Unlock(pt_tq.ml); /* we provide the locking */
+
+ PR_DestroyCondVar(op->complete);
+
+ return op->result.code; /* and the primary answer */
+} /* pt_Continue */
+
+static PRBool pt_sendto_cont(pt_Continuation *op, PRInt16 revents)
+{
+ PRIntn bytes = sendto(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags,
+ (struct sockaddr*)op->arg5.addr, sizeof(*(op->arg5.addr)));
+ op->syserrno = WSAGetLastError();
+ if (bytes > 0) /* this is progress */
+ {
+ char *bp = op->arg2.buffer;
+ bp += bytes; /* adjust the buffer pointer */
+ op->arg2.buffer = bp;
+ op->result.code += bytes; /* accumulate the number sent */
+ op->arg3.amount -= bytes; /* and reduce the required count */
+ return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
+ }
+ else return ((-1 == bytes) && (WSAEWOULDBLOCK == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_sendto_cont */
+
+static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents)
+{
+ PRIntn addr_len = sizeof(*(op->arg5.addr));
+ op->result.code = recvfrom(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount,
+ op->arg4.flags, (struct sockaddr*)op->arg5.addr, &addr_len);
+ op->syserrno = WSAGetLastError();
+ return ((-1 == op->result.code) && (WSAEWOULDBLOCK == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_recvfrom_cont */
+
+static PRInt32 pt_SendTo(
+ SOCKET osfd, const void *buf,
+ PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
+ PRIntn addrlen, PRIntervalTime timeout)
+{
+ PRInt32 bytes = -1, err;
+ PRBool fNeedContinue = PR_FALSE;
+
+ bytes = sendto(
+ osfd, buf, amount, flags,
+ (struct sockaddr*)addr, PR_NETADDR_SIZE(addr));
+ if (bytes == -1) {
+ if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) {
+ fNeedContinue = PR_TRUE;
+ }
+ else {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ }
+ }
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = osfd;
+ op.arg2.buffer = (void*)buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.arg5.addr = (PRNetAddr*)addr;
+ op.timeout = timeout;
+ op.result.code = 0; /* initialize the number sent */
+ op.function = pt_sendto_cont;
+ op.event = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ bytes = pt_Continue(&op);
+ if (bytes < 0) {
+ WSASetLastError(op.syserrno);
+ _PR_MD_MAP_SENDTO_ERROR(op.syserrno);
+ }
+ }
+ return bytes;
+} /* pt_SendTo */
+
+static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
+ PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout)
+{
+ PRInt32 bytes = -1, err;
+ PRBool fNeedContinue = PR_FALSE;
+
+ bytes = recvfrom(
+ osfd, buf, amount, flags,
+ (struct sockaddr*)addr, addr_len);
+ if (bytes == -1) {
+ if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) {
+ fNeedContinue = PR_TRUE;
+ }
+ else {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ }
+ }
+
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = osfd;
+ op.arg2.buffer = buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.arg5.addr = addr;
+ op.timeout = timeout;
+ op.function = pt_recvfrom_cont;
+ op.event = PR_POLL_READ | PR_POLL_EXCEPT;
+ bytes = pt_Continue(&op);
+ if (bytes < 0) {
+ WSASetLastError(op.syserrno);
+ _PR_MD_MAP_RECVFROM_ERROR(op.syserrno);
+ }
+ }
+ return bytes;
+} /* pt_RecvFrom */
diff --git a/nsprpub/pr/src/md/windows/ntmisc.c b/nsprpub/pr/src/md/windows/ntmisc.c
new file mode 100644
index 0000000000..839e3de8d8
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/ntmisc.c
@@ -0,0 +1,1230 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * ntmisc.c
+ *
+ */
+
+#include "primpl.h"
+#include <math.h> /* for fabs() */
+#include <windows.h>
+
+char *_PR_MD_GET_ENV(const char *name)
+{
+ return getenv(name);
+}
+
+/*
+** _PR_MD_PUT_ENV() -- add or change environment variable
+**
+**
+*/
+PRIntn _PR_MD_PUT_ENV(const char *name)
+{
+ return(putenv(name));
+}
+
+
+/*
+ **************************************************************************
+ **************************************************************************
+ **
+ ** Date and time routines
+ **
+ **************************************************************************
+ **************************************************************************
+ */
+
+/*
+ * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME.
+ * We store the value in a PRTime variable for convenience.
+ */
+#ifdef __GNUC__
+const PRTime _pr_filetime_offset = 116444736000000000LL;
+const PRTime _pr_filetime_divisor = 10LL;
+#else
+const PRTime _pr_filetime_offset = 116444736000000000i64;
+const PRTime _pr_filetime_divisor = 10i64;
+#endif
+
+#ifdef WINCE
+
+#define FILETIME_TO_INT64(ft) \
+ (((PRInt64)ft.dwHighDateTime) << 32 | (PRInt64)ft.dwLowDateTime)
+
+static void
+LowResTime(LPFILETIME lpft)
+{
+ GetCurrentFT(lpft);
+}
+
+typedef struct CalibrationData {
+ long double freq; /* The performance counter frequency */
+ long double offset; /* The low res 'epoch' */
+ long double timer_offset; /* The high res 'epoch' */
+
+ /* The last high res time that we returned since recalibrating */
+ PRInt64 last;
+
+ PRBool calibrated;
+
+ CRITICAL_SECTION data_lock;
+ CRITICAL_SECTION calibration_lock;
+ PRInt64 granularity;
+} CalibrationData;
+
+static CalibrationData calibration;
+
+typedef void (*GetSystemTimeAsFileTimeFcn)(LPFILETIME);
+static GetSystemTimeAsFileTimeFcn ce6_GetSystemTimeAsFileTime = NULL;
+
+static void
+NowCalibrate(void)
+{
+ FILETIME ft, ftStart;
+ LARGE_INTEGER liFreq, now;
+
+ if (calibration.freq == 0.0) {
+ if(!QueryPerformanceFrequency(&liFreq)) {
+ /* High-performance timer is unavailable */
+ calibration.freq = -1.0;
+ } else {
+ calibration.freq = (long double) liFreq.QuadPart;
+ }
+ }
+ if (calibration.freq > 0.0) {
+ PRInt64 calibrationDelta = 0;
+ /*
+ * By wrapping a timeBegin/EndPeriod pair of calls around this loop,
+ * the loop seems to take much less time (1 ms vs 15ms) on Vista.
+ */
+ timeBeginPeriod(1);
+ LowResTime(&ftStart);
+ do {
+ LowResTime(&ft);
+ } while (memcmp(&ftStart,&ft, sizeof(ft)) == 0);
+ timeEndPeriod(1);
+
+ calibration.granularity =
+ (FILETIME_TO_INT64(ft) - FILETIME_TO_INT64(ftStart))/10;
+
+ QueryPerformanceCounter(&now);
+
+ calibration.offset = (long double) FILETIME_TO_INT64(ft);
+ calibration.timer_offset = (long double) now.QuadPart;
+ /*
+ * The windows epoch is around 1600. The unix epoch is around 1970.
+ * _pr_filetime_offset is the difference (in windows time units which
+ * are 10 times more highres than the JS time unit)
+ */
+ calibration.offset -= _pr_filetime_offset;
+ calibration.offset *= 0.1;
+ calibration.last = 0;
+
+ calibration.calibrated = PR_TRUE;
+ }
+}
+
+#define CALIBRATIONLOCK_SPINCOUNT 0
+#define DATALOCK_SPINCOUNT 4096
+#define LASTLOCK_SPINCOUNT 4096
+
+void
+_MD_InitTime(void)
+{
+ /* try for CE6 GetSystemTimeAsFileTime first */
+ HANDLE h = GetModuleHandleW(L"coredll.dll");
+ ce6_GetSystemTimeAsFileTime = (GetSystemTimeAsFileTimeFcn)
+ GetProcAddressA(h, "GetSystemTimeAsFileTime");
+
+ /* otherwise go the slow route */
+ if (ce6_GetSystemTimeAsFileTime == NULL) {
+ memset(&calibration, 0, sizeof(calibration));
+ NowCalibrate();
+ InitializeCriticalSection(&calibration.calibration_lock);
+ InitializeCriticalSection(&calibration.data_lock);
+ }
+}
+
+void
+_MD_CleanupTime(void)
+{
+ if (ce6_GetSystemTimeAsFileTime == NULL) {
+ DeleteCriticalSection(&calibration.calibration_lock);
+ DeleteCriticalSection(&calibration.data_lock);
+ }
+}
+
+#define MUTEX_SETSPINCOUNT(m, c)
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * PR_Now --
+ *
+ * Returns the current time in microseconds since the epoch.
+ * The epoch is midnight January 1, 1970 GMT.
+ * The implementation is machine dependent. This is the
+ * implementation for Windows.
+ * Cf. time_t time(time_t *tp)
+ *
+ *-----------------------------------------------------------------------
+ */
+
+PR_IMPLEMENT(PRTime)
+PR_Now(void)
+{
+ long double lowresTime, highresTimerValue;
+ FILETIME ft;
+ LARGE_INTEGER now;
+ PRBool calibrated = PR_FALSE;
+ PRBool needsCalibration = PR_FALSE;
+ PRInt64 returnedTime;
+ long double cachedOffset = 0.0;
+
+ if (ce6_GetSystemTimeAsFileTime) {
+ union {
+ FILETIME ft;
+ PRTime prt;
+ } currentTime;
+
+ PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
+
+ ce6_GetSystemTimeAsFileTime(&currentTime.ft);
+
+ /* written this way on purpose, since the second term becomes
+ * a constant, and the entire expression is faster to execute.
+ */
+ return currentTime.prt/_pr_filetime_divisor -
+ _pr_filetime_offset/_pr_filetime_divisor;
+ }
+
+ do {
+ if (!calibration.calibrated || needsCalibration) {
+ EnterCriticalSection(&calibration.calibration_lock);
+ EnterCriticalSection(&calibration.data_lock);
+
+ /* Recalibrate only if no one else did before us */
+ if (calibration.offset == cachedOffset) {
+ /*
+ * Since calibration can take a while, make any other
+ * threads immediately wait
+ */
+ MUTEX_SETSPINCOUNT(&calibration.data_lock, 0);
+
+ NowCalibrate();
+
+ calibrated = PR_TRUE;
+
+ /* Restore spin count */
+ MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT);
+ }
+ LeaveCriticalSection(&calibration.data_lock);
+ LeaveCriticalSection(&calibration.calibration_lock);
+ }
+
+ /* Calculate a low resolution time */
+ LowResTime(&ft);
+ lowresTime =
+ ((long double)(FILETIME_TO_INT64(ft) - _pr_filetime_offset)) * 0.1;
+
+ if (calibration.freq > 0.0) {
+ long double highresTime, diff;
+ DWORD timeAdjustment, timeIncrement;
+ BOOL timeAdjustmentDisabled;
+
+ /* Default to 15.625 ms if the syscall fails */
+ long double skewThreshold = 15625.25;
+
+ /* Grab high resolution time */
+ QueryPerformanceCounter(&now);
+ highresTimerValue = (long double)now.QuadPart;
+
+ EnterCriticalSection(&calibration.data_lock);
+ highresTime = calibration.offset + 1000000L *
+ (highresTimerValue-calibration.timer_offset)/calibration.freq;
+ cachedOffset = calibration.offset;
+
+ /*
+ * On some dual processor/core systems, we might get an earlier
+ * time so we cache the last time that we returned.
+ */
+ calibration.last = PR_MAX(calibration.last,(PRInt64)highresTime);
+ returnedTime = calibration.last;
+ LeaveCriticalSection(&calibration.data_lock);
+
+ /* Get an estimate of clock ticks per second from our own test */
+ skewThreshold = calibration.granularity;
+ /* Check for clock skew */
+ diff = lowresTime - highresTime;
+
+ /*
+ * For some reason that I have not determined, the skew can be
+ * up to twice a kernel tick. This does not seem to happen by
+ * itself, but I have only seen it triggered by another program
+ * doing some kind of file I/O. The symptoms are a negative diff
+ * followed by an equally large positive diff.
+ */
+ if (fabs(diff) > 2*skewThreshold) {
+ if (calibrated) {
+ /*
+ * If we already calibrated once this instance, and the
+ * clock is still skewed, then either the processor(s) are
+ * wildly changing clockspeed or the system is so busy that
+ * we get switched out for long periods of time. In either
+ * case, it would be infeasible to make use of high
+ * resolution results for anything, so let's resort to old
+ * behavior for this call. It's possible that in the
+ * future, the user will want the high resolution timer, so
+ * we don't disable it entirely.
+ */
+ returnedTime = (PRInt64)lowresTime;
+ needsCalibration = PR_FALSE;
+ } else {
+ /*
+ * It is possible that when we recalibrate, we will return
+ * a value less than what we have returned before; this is
+ * unavoidable. We cannot tell the different between a
+ * faulty QueryPerformanceCounter implementation and user
+ * changes to the operating system time. Since we must
+ * respect user changes to the operating system time, we
+ * cannot maintain the invariant that Date.now() never
+ * decreases; the old implementation has this behavior as
+ * well.
+ */
+ needsCalibration = PR_TRUE;
+ }
+ } else {
+ /* No detectable clock skew */
+ returnedTime = (PRInt64)highresTime;
+ needsCalibration = PR_FALSE;
+ }
+ } else {
+ /* No high resolution timer is available, so fall back */
+ returnedTime = (PRInt64)lowresTime;
+ }
+ } while (needsCalibration);
+
+ return returnedTime;
+}
+
+#else
+
+PR_IMPLEMENT(PRTime)
+PR_Now(void)
+{
+ PRTime prt;
+ FILETIME ft;
+ SYSTEMTIME st;
+
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, &ft);
+ _PR_FileTimeToPRTime(&ft, &prt);
+ return prt;
+}
+
+#endif
+
+/*
+ ***********************************************************************
+ ***********************************************************************
+ *
+ * Process creation routines
+ *
+ ***********************************************************************
+ ***********************************************************************
+ */
+
+/*
+ * Assemble the command line by concatenating the argv array.
+ * On success, this function returns 0 and the resulting command
+ * line is returned in *cmdLine. On failure, it returns -1.
+ */
+static int assembleCmdLine(char *const *argv, char **cmdLine)
+{
+ char *const *arg;
+ char *p, *q;
+ size_t cmdLineSize;
+ int numBackslashes;
+ int i;
+ int argNeedQuotes;
+
+ /*
+ * Find out how large the command line buffer should be.
+ */
+ cmdLineSize = 0;
+ for (arg = argv; *arg; arg++) {
+ /*
+ * \ and " need to be escaped by a \. In the worst case,
+ * every character is a \ or ", so the string of length
+ * may double. If we quote an argument, that needs two ".
+ * Finally, we need a space between arguments, and
+ * a null byte at the end of command line.
+ */
+ cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */
+ + 2 /* we quote every argument */
+ + 1; /* space in between, or final null */
+ }
+ p = *cmdLine = PR_MALLOC((PRUint32) cmdLineSize);
+ if (p == NULL) {
+ return -1;
+ }
+
+ for (arg = argv; *arg; arg++) {
+ /* Add a space to separates the arguments */
+ if (arg != argv) {
+ *p++ = ' ';
+ }
+ q = *arg;
+ numBackslashes = 0;
+ argNeedQuotes = 0;
+
+ /*
+ * If the argument is empty or contains white space, it needs to
+ * be quoted.
+ */
+ if (**arg == '\0' || strpbrk(*arg, " \f\n\r\t\v")) {
+ argNeedQuotes = 1;
+ }
+
+ if (argNeedQuotes) {
+ *p++ = '"';
+ }
+ while (*q) {
+ if (*q == '\\') {
+ numBackslashes++;
+ q++;
+ } else if (*q == '"') {
+ if (numBackslashes) {
+ /*
+ * Double the backslashes since they are followed
+ * by a quote
+ */
+ for (i = 0; i < 2 * numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ numBackslashes = 0;
+ }
+ /* To escape the quote */
+ *p++ = '\\';
+ *p++ = *q++;
+ } else {
+ if (numBackslashes) {
+ /*
+ * Backslashes are not followed by a quote, so
+ * don't need to double the backslashes.
+ */
+ for (i = 0; i < numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ numBackslashes = 0;
+ }
+ *p++ = *q++;
+ }
+ }
+
+ /* Now we are at the end of this argument */
+ if (numBackslashes) {
+ /*
+ * Double the backslashes if we have a quote string
+ * delimiter at the end.
+ */
+ if (argNeedQuotes) {
+ numBackslashes *= 2;
+ }
+ for (i = 0; i < numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ }
+ if (argNeedQuotes) {
+ *p++ = '"';
+ }
+ }
+
+ *p = '\0';
+ return 0;
+}
+
+/*
+ * Assemble the environment block by concatenating the envp array
+ * (preserving the terminating null byte in each array element)
+ * and adding a null byte at the end.
+ *
+ * Returns 0 on success. The resulting environment block is returned
+ * in *envBlock. Note that if envp is NULL, a NULL pointer is returned
+ * in *envBlock. Returns -1 on failure.
+ */
+static int assembleEnvBlock(char **envp, char **envBlock)
+{
+ char *p;
+ char *q;
+ char **env;
+ char *curEnv;
+ char *cwdStart, *cwdEnd;
+ size_t envBlockSize;
+
+ if (envp == NULL) {
+ *envBlock = NULL;
+ return 0;
+ }
+
+#ifdef WINCE
+ {
+ PRUnichar *wideCurEnv = mozce_GetEnvString();
+ int len = WideCharToMultiByte(CP_ACP, 0, wideCurEnv, -1,
+ NULL, 0, NULL, NULL);
+ curEnv = (char *) PR_MALLOC(len * sizeof(char));
+ WideCharToMultiByte(CP_ACP, 0, wideCurEnv, -1,
+ curEnv, len, NULL, NULL);
+ free(wideCurEnv);
+ }
+#else
+ curEnv = GetEnvironmentStrings();
+#endif
+
+ cwdStart = curEnv;
+ while (*cwdStart) {
+ if (cwdStart[0] == '=' && cwdStart[1] != '\0'
+ && cwdStart[2] == ':' && cwdStart[3] == '=') {
+ break;
+ }
+ cwdStart += strlen(cwdStart) + 1;
+ }
+ cwdEnd = cwdStart;
+ if (*cwdEnd) {
+ cwdEnd += strlen(cwdEnd) + 1;
+ while (*cwdEnd) {
+ if (cwdEnd[0] != '=' || cwdEnd[1] == '\0'
+ || cwdEnd[2] != ':' || cwdEnd[3] != '=') {
+ break;
+ }
+ cwdEnd += strlen(cwdEnd) + 1;
+ }
+ }
+ envBlockSize = cwdEnd - cwdStart;
+
+ for (env = envp; *env; env++) {
+ envBlockSize += strlen(*env) + 1;
+ }
+ envBlockSize++;
+
+ p = *envBlock = PR_MALLOC((PRUint32) envBlockSize);
+ if (p == NULL) {
+#ifdef WINCE
+ PR_Free(curEnv);
+#else
+ FreeEnvironmentStrings(curEnv);
+#endif
+ return -1;
+ }
+
+ q = cwdStart;
+ while (q < cwdEnd) {
+ *p++ = *q++;
+ }
+#ifdef WINCE
+ PR_Free(curEnv);
+#else
+ FreeEnvironmentStrings(curEnv);
+#endif
+
+ for (env = envp; *env; env++) {
+ q = *env;
+ while (*q) {
+ *p++ = *q++;
+ }
+ *p++ = '\0';
+ }
+ *p = '\0';
+ return 0;
+}
+
+/*
+ * For qsort. We sort (case-insensitive) the environment strings
+ * before generating the environment block.
+ */
+static int compare(const void *arg1, const void *arg2)
+{
+ return _stricmp(* (char**)arg1, * (char**)arg2);
+}
+
+PRProcess * _PR_CreateWindowsProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+#ifdef WINCE
+ STARTUPINFOW startupInfo;
+ PRUnichar *wideCmdLine;
+ PRUnichar *wideCwd;
+ int len = 0;
+#else
+ STARTUPINFO startupInfo;
+#endif
+ DWORD creationFlags = 0;
+ PROCESS_INFORMATION procInfo;
+ BOOL retVal;
+ char *cmdLine = NULL;
+ char *envBlock = NULL;
+ char **newEnvp = NULL;
+ const char *cwd = NULL; /* current working directory */
+ PRProcess *proc = NULL;
+ PRBool hasFdInheritBuffer;
+
+ proc = PR_NEW(PRProcess);
+ if (!proc) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+ if (assembleCmdLine(argv, &cmdLine) == -1) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+#ifndef WINCE
+ /*
+ * If attr->fdInheritBuffer is not NULL, we need to insert
+ * it into the envp array, so envp cannot be NULL.
+ */
+ hasFdInheritBuffer = (attr && attr->fdInheritBuffer);
+ if ((envp == NULL) && hasFdInheritBuffer) {
+ envp = environ;
+ }
+
+ if (envp != NULL) {
+ int idx;
+ int numEnv;
+ PRBool found = PR_FALSE;
+
+ numEnv = 0;
+ while (envp[numEnv]) {
+ numEnv++;
+ }
+ newEnvp = (char **) PR_MALLOC((numEnv + 2) * sizeof(char *));
+ for (idx = 0; idx < numEnv; idx++) {
+ newEnvp[idx] = envp[idx];
+ if (hasFdInheritBuffer && !found
+ && !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) {
+ newEnvp[idx] = attr->fdInheritBuffer;
+ found = PR_TRUE;
+ }
+ }
+ if (hasFdInheritBuffer && !found) {
+ newEnvp[idx++] = attr->fdInheritBuffer;
+ }
+ newEnvp[idx] = NULL;
+ qsort((void *) newEnvp, (size_t) idx, sizeof(char *), compare);
+ }
+ if (assembleEnvBlock(newEnvp, &envBlock) == -1) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+ ZeroMemory(&startupInfo, sizeof(startupInfo));
+ startupInfo.cb = sizeof(startupInfo);
+
+ if (attr) {
+ PRBool redirected = PR_FALSE;
+
+ /*
+ * XXX the default value for stdin, stdout, and stderr
+ * should probably be the console input and output, not
+ * those of the parent process.
+ */
+ startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ if (attr->stdinFd) {
+ startupInfo.hStdInput = (HANDLE) attr->stdinFd->secret->md.osfd;
+ redirected = PR_TRUE;
+ }
+ if (attr->stdoutFd) {
+ startupInfo.hStdOutput = (HANDLE) attr->stdoutFd->secret->md.osfd;
+ redirected = PR_TRUE;
+ /*
+ * If stdout is redirected, we can assume that the process will
+ * not write anything useful to the console windows, and therefore
+ * automatically set the CREATE_NO_WINDOW flag.
+ */
+ creationFlags |= CREATE_NO_WINDOW;
+ }
+ if (attr->stderrFd) {
+ startupInfo.hStdError = (HANDLE) attr->stderrFd->secret->md.osfd;
+ redirected = PR_TRUE;
+ }
+ if (redirected) {
+ startupInfo.dwFlags |= STARTF_USESTDHANDLES;
+ }
+ cwd = attr->currentDirectory;
+ }
+#endif
+
+#ifdef WINCE
+ len = MultiByteToWideChar(CP_ACP, 0, cmdLine, -1, NULL, 0);
+ wideCmdLine = (PRUnichar *)PR_MALLOC(len * sizeof(PRUnichar));
+ MultiByteToWideChar(CP_ACP, 0, cmdLine, -1, wideCmdLine, len);
+ len = MultiByteToWideChar(CP_ACP, 0, cwd, -1, NULL, 0);
+ wideCwd = PR_MALLOC(len * sizeof(PRUnichar));
+ MultiByteToWideChar(CP_ACP, 0, cwd, -1, wideCwd, len);
+ retVal = CreateProcessW(NULL,
+ wideCmdLine,
+ NULL, /* security attributes for the new
+ * process */
+ NULL, /* security attributes for the primary
+ * thread in the new process */
+ TRUE, /* inherit handles */
+ creationFlags,
+ envBlock, /* an environment block, consisting
+ * of a null-terminated block of
+ * null-terminated strings. Each
+ * string is in the form:
+ * name=value
+ * XXX: usually NULL */
+ wideCwd, /* current drive and directory */
+ &startupInfo,
+ &procInfo
+ );
+ PR_Free(wideCmdLine);
+ PR_Free(wideCwd);
+#else
+ retVal = CreateProcess(NULL,
+ cmdLine,
+ NULL, /* security attributes for the new
+ * process */
+ NULL, /* security attributes for the primary
+ * thread in the new process */
+ TRUE, /* inherit handles */
+ creationFlags,
+ envBlock, /* an environment block, consisting
+ * of a null-terminated block of
+ * null-terminated strings. Each
+ * string is in the form:
+ * name=value
+ * XXX: usually NULL */
+ cwd, /* current drive and directory */
+ &startupInfo,
+ &procInfo
+ );
+#endif
+
+ if (retVal == FALSE) {
+ /* XXX what error code? */
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ goto errorExit;
+ }
+
+ CloseHandle(procInfo.hThread);
+ proc->md.handle = procInfo.hProcess;
+ proc->md.id = procInfo.dwProcessId;
+
+ PR_DELETE(cmdLine);
+ if (newEnvp) {
+ PR_DELETE(newEnvp);
+ }
+ if (envBlock) {
+ PR_DELETE(envBlock);
+ }
+ return proc;
+
+errorExit:
+ if (cmdLine) {
+ PR_DELETE(cmdLine);
+ }
+ if (newEnvp) {
+ PR_DELETE(newEnvp);
+ }
+ if (envBlock) {
+ PR_DELETE(envBlock);
+ }
+ if (proc) {
+ PR_DELETE(proc);
+ }
+ return NULL;
+} /* _PR_CreateWindowsProcess */
+
+PRStatus _PR_DetachWindowsProcess(PRProcess *process)
+{
+ CloseHandle(process->md.handle);
+ PR_DELETE(process);
+ return PR_SUCCESS;
+}
+
+/*
+ * XXX: This implementation is a temporary quick solution.
+ * It can be called by native threads only (not by fibers).
+ */
+PRStatus _PR_WaitWindowsProcess(PRProcess *process,
+ PRInt32 *exitCode)
+{
+ DWORD dwRetVal;
+
+ dwRetVal = WaitForSingleObject(process->md.handle, INFINITE);
+ if (dwRetVal == WAIT_FAILED) {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ PR_ASSERT(dwRetVal == WAIT_OBJECT_0);
+ if (exitCode != NULL &&
+ GetExitCodeProcess(process->md.handle, exitCode) == FALSE) {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ CloseHandle(process->md.handle);
+ PR_DELETE(process);
+ return PR_SUCCESS;
+}
+
+PRStatus _PR_KillWindowsProcess(PRProcess *process)
+{
+ /*
+ * On Unix, if a process terminates normally, its exit code is
+ * between 0 and 255. So here on Windows, we use the exit code
+ * 256 to indicate that the process is killed.
+ */
+ if (TerminateProcess(process->md.handle, 256)) {
+ return PR_SUCCESS;
+ }
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+}
+
+PRStatus _MD_WindowsGetHostName(char *name, PRUint32 namelen)
+{
+ PRIntn rv;
+ PRInt32 syserror;
+
+ rv = gethostname(name, (PRInt32) namelen);
+ if (0 == rv) {
+ return PR_SUCCESS;
+ }
+ syserror = WSAGetLastError();
+ PR_ASSERT(WSANOTINITIALISED != syserror);
+ _PR_MD_MAP_GETHOSTNAME_ERROR(syserror);
+ return PR_FAILURE;
+}
+
+PRStatus _MD_WindowsGetSysInfo(PRSysInfo cmd, char *name, PRUint32 namelen)
+{
+ OSVERSIONINFO osvi;
+
+ PR_ASSERT((cmd == PR_SI_SYSNAME) || (cmd == PR_SI_RELEASE) ||
+ (cmd == PR_SI_RELEASE_BUILD));
+
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+ if (! GetVersionEx (&osvi) ) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+
+ switch (osvi.dwPlatformId) {
+ case VER_PLATFORM_WIN32_NT:
+ if (PR_SI_SYSNAME == cmd) {
+ (void)PR_snprintf(name, namelen, "Windows_NT");
+ }
+ else if (PR_SI_RELEASE == cmd) {
+ (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion,
+ osvi.dwMinorVersion);
+ }
+ else if (PR_SI_RELEASE_BUILD == cmd) {
+ (void)PR_snprintf(name, namelen, "%d", osvi.dwBuildNumber);
+ }
+ break;
+ case VER_PLATFORM_WIN32_WINDOWS:
+ if (PR_SI_SYSNAME == cmd) {
+ if ((osvi.dwMajorVersion > 4) ||
+ ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion > 0))) {
+ (void)PR_snprintf(name, namelen, "Windows_98");
+ }
+ else {
+ (void)PR_snprintf(name, namelen, "Windows_95");
+ }
+ } else if (PR_SI_RELEASE == cmd) {
+ (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion,
+ osvi.dwMinorVersion);
+ } else if (PR_SI_RELEASE_BUILD == cmd) {
+ (void)PR_snprintf(name, namelen, "%d", osvi.dwBuildNumber);
+ }
+ break;
+#ifdef VER_PLATFORM_WIN32_CE
+ case VER_PLATFORM_WIN32_CE:
+ if (PR_SI_SYSNAME == cmd) {
+ (void)PR_snprintf(name, namelen, "Windows_CE");
+ }
+ else if (PR_SI_RELEASE == cmd) {
+ (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion,
+ osvi.dwMinorVersion);
+ }
+ else if (PR_SI_RELEASE_BUILD == cmd) {
+ if (namelen) {
+ *name = 0;
+ }
+ }
+ break;
+#endif
+ default:
+ if (PR_SI_SYSNAME == cmd) {
+ (void)PR_snprintf(name, namelen, "Windows_Unknown");
+ }
+ else if (PR_SI_RELEASE == cmd) {
+ (void)PR_snprintf(name, namelen, "%d.%d",0,0);
+ }
+ else if (PR_SI_RELEASE_BUILD == cmd) {
+ if (namelen) {
+ *name = 0;
+ }
+ }
+ break;
+ }
+ return PR_SUCCESS;
+}
+
+PRStatus _MD_WindowsGetReleaseName(char *name, PRUint32 namelen)
+{
+ OSVERSIONINFO osvi;
+
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+ if (! GetVersionEx (&osvi) ) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+
+ switch (osvi.dwPlatformId) {
+ case VER_PLATFORM_WIN32_NT:
+ case VER_PLATFORM_WIN32_WINDOWS:
+ (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion,
+ osvi.dwMinorVersion);
+ break;
+ default:
+ (void)PR_snprintf(name, namelen, "%d.%d",0,0);
+ break;
+ }
+ return PR_SUCCESS;
+}
+
+/*
+ **********************************************************************
+ *
+ * Memory-mapped files
+ *
+ **********************************************************************
+ */
+
+PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
+{
+ DWORD dwHi, dwLo;
+ DWORD flProtect;
+ PROsfd osfd;
+
+ osfd = ( fmap->fd == (PRFileDesc*)-1 )? -1 : fmap->fd->secret->md.osfd;
+
+ dwLo = (DWORD) (size & 0xffffffff);
+ dwHi = (DWORD) (((PRUint64) size >> 32) & 0xffffffff);
+
+ if (fmap->prot == PR_PROT_READONLY) {
+ flProtect = PAGE_READONLY;
+ fmap->md.dwAccess = FILE_MAP_READ;
+ } else if (fmap->prot == PR_PROT_READWRITE) {
+ flProtect = PAGE_READWRITE;
+ fmap->md.dwAccess = FILE_MAP_WRITE;
+ } else {
+ PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY);
+#ifdef WINCE
+ /* WINCE does not have FILE_MAP_COPY. */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+#else
+ flProtect = PAGE_WRITECOPY;
+ fmap->md.dwAccess = FILE_MAP_COPY;
+#endif
+ }
+
+ fmap->md.hFileMap = CreateFileMapping(
+ (HANDLE) osfd,
+ NULL,
+ flProtect,
+ dwHi,
+ dwLo,
+ NULL);
+
+ if (fmap->md.hFileMap == NULL) {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PRInt32 _MD_GetMemMapAlignment(void)
+{
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+ return info.dwAllocationGranularity;
+}
+
+extern PRLogModuleInfo *_pr_shma_lm;
+
+void * _MD_MemMap(
+ PRFileMap *fmap,
+ PROffset64 offset,
+ PRUint32 len)
+{
+ DWORD dwHi, dwLo;
+ void *addr;
+
+ dwLo = (DWORD) (offset & 0xffffffff);
+ dwHi = (DWORD) (((PRUint64) offset >> 32) & 0xffffffff);
+ if ((addr = MapViewOfFile(fmap->md.hFileMap, fmap->md.dwAccess,
+ dwHi, dwLo, len)) == NULL) {
+ {
+ LPVOID lpMsgBuf;
+
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, ("md_memmap(): %s", lpMsgBuf ));
+ }
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ }
+ return addr;
+}
+
+PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
+{
+ if (UnmapViewOfFile(addr)) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ return PR_FAILURE;
+}
+
+PRStatus _MD_CloseFileMap(PRFileMap *fmap)
+{
+ CloseHandle(fmap->md.hFileMap);
+ PR_DELETE(fmap);
+ return PR_SUCCESS;
+}
+
+PRStatus _MD_SyncMemMap(
+ PRFileDesc *fd,
+ void *addr,
+ PRUint32 len)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+
+ /* The FlushViewOfFile page on MSDN says:
+ * To flush all the dirty pages plus the metadata for the file and
+ * ensure that they are physically written to disk, call
+ * FlushViewOfFile and then call the FlushFileBuffers function.
+ */
+ if (FlushViewOfFile(addr, len) && FlushFileBuffers((HANDLE) osfd)) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ return PR_FAILURE;
+}
+
+/*
+ ***********************************************************************
+ *
+ * Atomic increment and decrement operations for x86 processors
+ *
+ * We don't use InterlockedIncrement and InterlockedDecrement
+ * because on NT 3.51 and Win95, they return a number with
+ * the same sign as the incremented/decremented result, rather
+ * than the result itself. On NT 4.0 these functions do return
+ * the incremented/decremented result.
+ *
+ * The result is returned in the eax register by the inline
+ * assembly code. We disable the harmless "no return value"
+ * warning (4035) for these two functions.
+ *
+ ***********************************************************************
+ */
+
+#if defined(_M_IX86) || defined(_X86_)
+
+#pragma warning(disable: 4035)
+PRInt32 _PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+#if defined(__GNUC__)
+ PRInt32 result;
+ asm volatile ("lock ; xadd %0, %1"
+ : "=r"(result), "=m"(*val)
+ : "0"(1), "m"(*val));
+ return result + 1;
+#else
+ __asm
+ {
+ mov ecx, val
+ mov eax, 1
+ lock xadd dword ptr [ecx], eax
+ inc eax
+ }
+#endif /* __GNUC__ */
+}
+#pragma warning(default: 4035)
+
+#pragma warning(disable: 4035)
+PRInt32 _PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+#if defined(__GNUC__)
+ PRInt32 result;
+ asm volatile ("lock ; xadd %0, %1"
+ : "=r"(result), "=m"(*val)
+ : "0"(-1), "m"(*val));
+ //asm volatile("lock ; xadd %0, %1" : "=m" (val), "=a" (result) : "-1" (1));
+ return result - 1;
+#else
+ __asm
+ {
+ mov ecx, val
+ mov eax, 0ffffffffh
+ lock xadd dword ptr [ecx], eax
+ dec eax
+ }
+#endif /* __GNUC__ */
+}
+#pragma warning(default: 4035)
+
+#pragma warning(disable: 4035)
+PRInt32 _PR_MD_ATOMIC_ADD(PRInt32 *intp, PRInt32 val)
+{
+#if defined(__GNUC__)
+ PRInt32 result;
+ //asm volatile("lock ; xadd %1, %0" : "=m" (intp), "=a" (result) : "1" (val));
+ asm volatile ("lock ; xadd %0, %1"
+ : "=r"(result), "=m"(*intp)
+ : "0"(val), "m"(*intp));
+ return result + val;
+#else
+ __asm
+ {
+ mov ecx, intp
+ mov eax, val
+ mov edx, eax
+ lock xadd dword ptr [ecx], eax
+ add eax, edx
+ }
+#endif /* __GNUC__ */
+}
+#pragma warning(default: 4035)
+
+#ifdef _PR_HAVE_ATOMIC_CAS
+
+#pragma warning(disable: 4035)
+void
+PR_StackPush(PRStack *stack, PRStackElem *stack_elem)
+{
+#if defined(__GNUC__)
+ void **tos = (void **) stack;
+ void *tmp;
+
+retry:
+ if (*tos == (void *) -1) {
+ goto retry;
+ }
+
+ __asm__("xchg %0,%1"
+ : "=r" (tmp), "=m"(*tos)
+ : "0" (-1), "m"(*tos));
+
+ if (tmp == (void *) -1) {
+ goto retry;
+ }
+
+ *(void **)stack_elem = tmp;
+ __asm__("" : : : "memory");
+ *tos = stack_elem;
+#else
+ __asm
+ {
+ mov ebx, stack
+ mov ecx, stack_elem
+ retry: mov eax,[ebx]
+ cmp eax,-1
+ je retry
+ mov eax,-1
+ xchg dword ptr [ebx], eax
+ cmp eax,-1
+ je retry
+ mov [ecx],eax
+ mov [ebx],ecx
+ }
+#endif /* __GNUC__ */
+}
+#pragma warning(default: 4035)
+
+#pragma warning(disable: 4035)
+PRStackElem *
+PR_StackPop(PRStack *stack)
+{
+#if defined(__GNUC__)
+ void **tos = (void **) stack;
+ void *tmp;
+
+retry:
+ if (*tos == (void *) -1) {
+ goto retry;
+ }
+
+ __asm__("xchg %0,%1"
+ : "=r" (tmp), "=m"(*tos)
+ : "0" (-1), "m"(*tos));
+
+ if (tmp == (void *) -1) {
+ goto retry;
+ }
+
+ if (tmp != (void *) 0)
+ {
+ void *next = *(void **)tmp;
+ *tos = next;
+ *(void **)tmp = 0;
+ }
+ else {
+ *tos = tmp;
+ }
+
+ return tmp;
+#else
+ __asm
+ {
+ mov ebx, stack
+ retry: mov eax,[ebx]
+ cmp eax,-1
+ je retry
+ mov eax,-1
+ xchg dword ptr [ebx], eax
+ cmp eax,-1
+ je retry
+ cmp eax,0
+ je empty
+ mov ecx,[eax]
+ mov [ebx],ecx
+ mov [eax],0
+ jmp done
+ empty:
+ mov [ebx],eax
+ done:
+ }
+#endif /* __GNUC__ */
+}
+#pragma warning(default: 4035)
+
+#endif /* _PR_HAVE_ATOMIC_CAS */
+
+#endif /* x86 processors */
diff --git a/nsprpub/pr/src/md/windows/ntsec.c b/nsprpub/pr/src/md/windows/ntsec.c
new file mode 100644
index 0000000000..0006e8399e
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/ntsec.c
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+/*
+ * ntsec.c
+ *
+ * Implement the POSIX-style mode bits (access permissions) for
+ * files and other securable objects in Windows NT using Windows
+ * NT's security descriptors with appropriate discretionary
+ * access-control lists.
+ */
+
+/*
+ * The security identifiers (SIDs) for owner, primary group,
+ * and the Everyone (World) group.
+ *
+ * These SIDs are looked up during NSPR initialization and
+ * saved in this global structure (see _PR_NT_InitSids) so
+ * that _PR_NT_MakeSecurityDescriptorACL doesn't need to
+ * look them up every time.
+ */
+static struct {
+ PSID owner;
+ PSID group;
+ PSID everyone;
+} _pr_nt_sids;
+
+/*
+ * Initialize the SIDs for owner, primary group, and the Everyone
+ * group in the _pr_nt_sids structure.
+ *
+ * This function needs to be called by NSPR initialization.
+ */
+void _PR_NT_InitSids(void)
+{
+#ifdef WINCE /* not supported */
+ return;
+#else
+ SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
+ HANDLE hToken = NULL; /* initialized to an arbitrary value to
+ * silence a Purify UMR warning */
+ PSID infoBuffer[1024/sizeof(PSID)]; /* defined as an array of PSIDs
+ * to force proper alignment */
+ PTOKEN_OWNER pTokenOwner = (PTOKEN_OWNER) infoBuffer;
+ PTOKEN_PRIMARY_GROUP pTokenPrimaryGroup
+ = (PTOKEN_PRIMARY_GROUP) infoBuffer;
+ DWORD dwLength;
+ BOOL rv;
+
+ /*
+ * Look up and make a copy of the owner and primary group
+ * SIDs in the access token of the calling process.
+ */
+ rv = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
+ if (rv == 0) {
+ /*
+ * On non-NT systems, this function is not implemented
+ * (error code ERROR_CALL_NOT_IMPLEMENTED), and neither are
+ * the other security functions. There is no point in
+ * going further.
+ *
+ * A process with insufficient access permissions may fail
+ * with the error code ERROR_ACCESS_DENIED.
+ */
+ PR_LOG(_pr_io_lm, PR_LOG_DEBUG,
+ ("_PR_NT_InitSids: OpenProcessToken() failed. Error: %d",
+ GetLastError()));
+ return;
+ }
+
+ rv = GetTokenInformation(hToken, TokenOwner, infoBuffer,
+ sizeof(infoBuffer), &dwLength);
+ PR_ASSERT(rv != 0);
+ dwLength = GetLengthSid(pTokenOwner->Owner);
+ _pr_nt_sids.owner = (PSID) PR_Malloc(dwLength);
+ PR_ASSERT(_pr_nt_sids.owner != NULL);
+ rv = CopySid(dwLength, _pr_nt_sids.owner, pTokenOwner->Owner);
+ PR_ASSERT(rv != 0);
+
+ rv = GetTokenInformation(hToken, TokenPrimaryGroup, infoBuffer,
+ sizeof(infoBuffer), &dwLength);
+ PR_ASSERT(rv != 0);
+ dwLength = GetLengthSid(pTokenPrimaryGroup->PrimaryGroup);
+ _pr_nt_sids.group = (PSID) PR_Malloc(dwLength);
+ PR_ASSERT(_pr_nt_sids.group != NULL);
+ rv = CopySid(dwLength, _pr_nt_sids.group,
+ pTokenPrimaryGroup->PrimaryGroup);
+ PR_ASSERT(rv != 0);
+
+ rv = CloseHandle(hToken);
+ PR_ASSERT(rv != 0);
+
+ /* Create a well-known SID for the Everyone group. */
+ rv = AllocateAndInitializeSid(&SIDAuthWorld, 1,
+ SECURITY_WORLD_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &_pr_nt_sids.everyone);
+ PR_ASSERT(rv != 0);
+#endif
+}
+
+/*
+ * Free the SIDs for owner, primary group, and the Everyone group
+ * in the _pr_nt_sids structure.
+ *
+ * This function needs to be called by NSPR cleanup.
+ */
+void
+_PR_NT_FreeSids(void)
+{
+#ifdef WINCE
+ return;
+#else
+ if (_pr_nt_sids.owner) {
+ PR_Free(_pr_nt_sids.owner);
+ }
+ if (_pr_nt_sids.group) {
+ PR_Free(_pr_nt_sids.group);
+ }
+ if (_pr_nt_sids.everyone) {
+ FreeSid(_pr_nt_sids.everyone);
+ }
+#endif
+}
+
+/*
+ * Construct a security descriptor whose discretionary access-control
+ * list implements the specified mode bits. The SIDs for owner, group,
+ * and everyone are obtained from the global _pr_nt_sids structure.
+ * Both the security descriptor and access-control list are returned
+ * and should be freed by a _PR_NT_FreeSecurityDescriptorACL call.
+ *
+ * The accessTable array maps NSPR's read, write, and execute access
+ * rights to the corresponding NT access rights for the securable
+ * object.
+ */
+PRStatus
+_PR_NT_MakeSecurityDescriptorACL(
+ PRIntn mode,
+ DWORD accessTable[],
+ PSECURITY_DESCRIPTOR *resultSD,
+ PACL *resultACL)
+{
+#ifdef WINCE
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+#else
+ PSECURITY_DESCRIPTOR pSD = NULL;
+ PACL pACL = NULL;
+ DWORD cbACL; /* size of ACL */
+ DWORD accessMask;
+
+ if (_pr_nt_sids.owner == NULL) {
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ pSD = (PSECURITY_DESCRIPTOR) PR_Malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
+ if (pSD == NULL) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ goto failed;
+ }
+ if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ goto failed;
+ }
+ if (!SetSecurityDescriptorOwner(pSD, _pr_nt_sids.owner, FALSE)) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ goto failed;
+ }
+ if (!SetSecurityDescriptorGroup(pSD, _pr_nt_sids.group, FALSE)) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ goto failed;
+ }
+
+ /*
+ * Construct a discretionary access-control list with three
+ * access-control entries, one each for owner, primary group,
+ * and Everyone.
+ */
+
+ cbACL = sizeof(ACL)
+ + 3 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD))
+ + GetLengthSid(_pr_nt_sids.owner)
+ + GetLengthSid(_pr_nt_sids.group)
+ + GetLengthSid(_pr_nt_sids.everyone);
+ pACL = (PACL) PR_Malloc(cbACL);
+ if (pACL == NULL) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ goto failed;
+ }
+ if (!InitializeAcl(pACL, cbACL, ACL_REVISION)) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ goto failed;
+ }
+ accessMask = 0;
+ if (mode & 00400) {
+ accessMask |= accessTable[0];
+ }
+ if (mode & 00200) {
+ accessMask |= accessTable[1];
+ }
+ if (mode & 00100) {
+ accessMask |= accessTable[2];
+ }
+ if (accessMask && !AddAccessAllowedAce(pACL, ACL_REVISION, accessMask,
+ _pr_nt_sids.owner)) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ goto failed;
+ }
+ accessMask = 0;
+ if (mode & 00040) {
+ accessMask |= accessTable[0];
+ }
+ if (mode & 00020) {
+ accessMask |= accessTable[1];
+ }
+ if (mode & 00010) {
+ accessMask |= accessTable[2];
+ }
+ if (accessMask && !AddAccessAllowedAce(pACL, ACL_REVISION, accessMask,
+ _pr_nt_sids.group)) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ goto failed;
+ }
+ accessMask = 0;
+ if (mode & 00004) {
+ accessMask |= accessTable[0];
+ }
+ if (mode & 00002) {
+ accessMask |= accessTable[1];
+ }
+ if (mode & 00001) {
+ accessMask |= accessTable[2];
+ }
+ if (accessMask && !AddAccessAllowedAce(pACL, ACL_REVISION, accessMask,
+ _pr_nt_sids.everyone)) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ goto failed;
+ }
+
+ if (!SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE)) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ goto failed;
+ }
+
+ *resultSD = pSD;
+ *resultACL = pACL;
+ return PR_SUCCESS;
+
+failed:
+ if (pSD) {
+ PR_Free(pSD);
+ }
+ if (pACL) {
+ PR_Free(pACL);
+ }
+ return PR_FAILURE;
+#endif
+}
+
+/*
+ * Free the specified security descriptor and access-control list
+ * previously created by _PR_NT_MakeSecurityDescriptorACL.
+ */
+void
+_PR_NT_FreeSecurityDescriptorACL(PSECURITY_DESCRIPTOR pSD, PACL pACL)
+{
+ if (pSD) {
+ PR_Free(pSD);
+ }
+ if (pACL) {
+ PR_Free(pACL);
+ }
+}
diff --git a/nsprpub/pr/src/md/windows/ntsem.c b/nsprpub/pr/src/md/windows/ntsem.c
new file mode 100644
index 0000000000..757bc839e2
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/ntsem.c
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * NT-specific semaphore handling code.
+ *
+ */
+
+
+#include "primpl.h"
+
+
+void
+_PR_MD_NEW_SEM(_MDSemaphore *md, PRUintn value)
+{
+ md->sem = CreateSemaphore(NULL, value, 0x7fffffff, NULL);
+}
+
+void
+_PR_MD_DESTROY_SEM(_MDSemaphore *md)
+{
+ CloseHandle(md->sem);
+}
+
+PRStatus
+_PR_MD_TIMED_WAIT_SEM(_MDSemaphore *md, PRIntervalTime ticks)
+{
+ int rv;
+
+ rv = WaitForSingleObject(md->sem, PR_IntervalToMilliseconds(ticks));
+
+ if (rv == WAIT_OBJECT_0) {
+ return PR_SUCCESS;
+ }
+ else {
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_WAIT_SEM(_MDSemaphore *md)
+{
+ return _PR_MD_TIMED_WAIT_SEM(md, PR_INTERVAL_NO_TIMEOUT);
+}
+
+void
+_PR_MD_POST_SEM(_MDSemaphore *md)
+{
+ ReleaseSemaphore(md->sem, 1, NULL);
+}
diff --git a/nsprpub/pr/src/md/windows/ntthread.c b/nsprpub/pr/src/md/windows/ntthread.c
new file mode 100644
index 0000000000..395a80dcc1
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/ntthread.c
@@ -0,0 +1,590 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#include <process.h> /* for _beginthreadex() */
+
+/* --- globals ------------------------------------------------ */
+PRLock *_pr_schedLock = NULL;
+_PRInterruptTable _pr_interruptTable[] = { { 0 } };
+
+BOOL _pr_use_static_tls = TRUE;
+__declspec(thread) PRThread *_pr_current_fiber;
+__declspec(thread) PRThread *_pr_fiber_last_run;
+__declspec(thread) _PRCPU *_pr_current_cpu;
+__declspec(thread) PRUintn _pr_ints_off;
+DWORD _pr_currentFiberIndex;
+DWORD _pr_lastFiberIndex;
+DWORD _pr_currentCPUIndex;
+DWORD _pr_intsOffIndex;
+
+_MDLock _nt_idleLock;
+PRCList _nt_idleList;
+PRUint32 _nt_idleCount;
+
+extern __declspec(thread) PRThread *_pr_io_restarted_io;
+extern DWORD _pr_io_restartedIOIndex;
+
+typedef HRESULT (WINAPI *SETTHREADDESCRIPTION)(HANDLE, PCWSTR);
+static SETTHREADDESCRIPTION sSetThreadDescription = NULL;
+
+/* Must check the restarted_io *before* decrementing no_sched to 0 */
+#define POST_SWITCH_WORK() \
+ PR_BEGIN_MACRO \
+ PRThread *restarted_io = \
+ (_pr_use_static_tls ? _pr_io_restarted_io \
+ : (PRThread *) TlsGetValue(_pr_io_restartedIOIndex)); \
+ if (restarted_io) { \
+ _nt_handle_restarted_io(restarted_io); \
+ } \
+ _PR_MD_LAST_THREAD()->no_sched = 0; \
+ PR_END_MACRO
+
+void
+_nt_handle_restarted_io(PRThread *restarted_io)
+{
+ /* After the switch we can resume an IO if needed.
+ * XXXMB - this needs to be done in create thread, since that could
+ * be the result for a context switch too..
+ */
+ PR_ASSERT(restarted_io->io_suspended == PR_TRUE);
+ PR_ASSERT(restarted_io->md.thr_bound_cpu == restarted_io->cpu);
+
+ _PR_THREAD_LOCK(restarted_io);
+ if (restarted_io->io_pending == PR_FALSE) {
+
+ /* The IO already completed, put us back on the runq. */
+ int pri = restarted_io->priority;
+
+ restarted_io->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(restarted_io->cpu);
+ _PR_ADD_RUNQ(restarted_io, restarted_io->cpu, pri);
+ _PR_RUNQ_UNLOCK(restarted_io->cpu);
+ } else {
+ _PR_SLEEPQ_LOCK(restarted_io->cpu);
+ _PR_ADD_SLEEPQ(restarted_io, restarted_io->sleep);
+ _PR_SLEEPQ_UNLOCK(restarted_io->cpu);
+ }
+ restarted_io->io_suspended = PR_FALSE;
+ restarted_io->md.thr_bound_cpu = NULL;
+
+ _PR_THREAD_UNLOCK(restarted_io);
+
+ if (_pr_use_static_tls) {
+ _pr_io_restarted_io = NULL;
+ } else {
+ TlsSetValue(_pr_io_restartedIOIndex, NULL);
+ }
+}
+
+void
+_PR_MD_EARLY_INIT()
+{
+ HMODULE hModule;
+
+ _MD_NEW_LOCK( &_nt_idleLock );
+ _nt_idleCount = 0;
+ PR_INIT_CLIST(&_nt_idleList);
+
+#if 0
+ /* Make the clock tick at least once per millisecond */
+ if ( timeBeginPeriod(1) == TIMERR_NOCANDO) {
+ /* deep yoghurt; clock doesn't tick fast enough! */
+ PR_ASSERT(0);
+ }
+#endif
+
+ if (!_pr_use_static_tls) {
+ _pr_currentFiberIndex = TlsAlloc();
+ _pr_lastFiberIndex = TlsAlloc();
+ _pr_currentCPUIndex = TlsAlloc();
+ _pr_intsOffIndex = TlsAlloc();
+ _pr_io_restartedIOIndex = TlsAlloc();
+ }
+
+ // SetThreadDescription is Windows 10 build 1607+
+ hModule = GetModuleHandleW(L"kernel32.dll");
+ if (hModule) {
+ sSetThreadDescription =
+ (SETTHREADDESCRIPTION) GetProcAddress(
+ hModule,
+ "SetThreadDescription");
+ }
+}
+
+void _PR_MD_CLEANUP_BEFORE_EXIT(void)
+{
+ _PR_NT_FreeSids();
+
+ WSACleanup();
+
+ if (!_pr_use_static_tls) {
+ TlsFree(_pr_currentFiberIndex);
+ TlsFree(_pr_lastFiberIndex);
+ TlsFree(_pr_currentCPUIndex);
+ TlsFree(_pr_intsOffIndex);
+ TlsFree(_pr_io_restartedIOIndex);
+ }
+}
+
+PRStatus
+_PR_MD_INIT_THREAD(PRThread *thread)
+{
+ thread->md.overlapped.ioModel = _MD_BlockingIO;
+ thread->md.overlapped.data.mdThread = &thread->md;
+
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) {
+ /*
+ ** Warning:
+ ** --------
+ ** NSPR requires a real handle to every thread.
+ ** GetCurrentThread() returns a pseudo-handle which
+ ** is not suitable for some thread operations (e.g.,
+ ** suspending). Therefore, get a real handle from
+ ** the pseudo handle via DuplicateHandle(...)
+ */
+ DuplicateHandle(
+ GetCurrentProcess(), /* Process of source handle */
+ GetCurrentThread(), /* Pseudo Handle to dup */
+ GetCurrentProcess(), /* Process of handle */
+ &(thread->md.handle), /* resulting handle */
+ 0L, /* access flags */
+ FALSE, /* Inheritable */
+ DUPLICATE_SAME_ACCESS); /* Options */
+ }
+
+ /* Create the blocking IO semaphore */
+ thread->md.blocked_sema = CreateSemaphore(NULL, 0, 1, NULL);
+ if (thread->md.blocked_sema == NULL) {
+ return PR_FAILURE;
+ }
+ if (_native_threads_only) {
+ /* Create the blocking IO semaphore */
+ thread->md.thr_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (thread->md.thr_event == NULL) {
+ return PR_FAILURE;
+ }
+ }
+ }
+
+ return PR_SUCCESS;
+}
+
+static unsigned __stdcall
+pr_root(void *arg)
+{
+ PRThread *thread = (PRThread *)arg;
+ thread->md.start(thread);
+ return 0;
+}
+
+PRStatus
+_PR_MD_CREATE_THREAD(PRThread *thread,
+ void (*start)(void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+
+ thread->md.start = start;
+ thread->md.handle = (HANDLE) _beginthreadex(
+ NULL,
+ thread->stack->stackSize,
+ pr_root,
+ (void *)thread,
+ CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION,
+ &(thread->id));
+ if(!thread->md.handle) {
+ PRErrorCode prerror;
+ thread->md.fiber_last_error = GetLastError();
+ switch (errno) {
+ case ENOMEM:
+ prerror = PR_OUT_OF_MEMORY_ERROR;
+ break;
+ case EAGAIN:
+ prerror = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ case EINVAL:
+ prerror = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ default:
+ prerror = PR_UNKNOWN_ERROR;
+ }
+ PR_SetError(prerror, errno);
+ return PR_FAILURE;
+ }
+
+ thread->md.id = thread->id;
+ /*
+ * On windows, a thread is created with a thread priority of
+ * THREAD_PRIORITY_NORMAL.
+ */
+ if (priority != PR_PRIORITY_NORMAL) {
+ _PR_MD_SET_PRIORITY(&(thread->md), priority);
+ }
+
+ /* Activate the thread */
+ if ( ResumeThread( thread->md.handle ) != -1) {
+ return PR_SUCCESS;
+ }
+
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+}
+
+void
+_PR_MD_JOIN_THREAD(_MDThread *md)
+{
+ DWORD rv;
+
+ rv = WaitForSingleObject(md->handle, INFINITE);
+ PR_ASSERT(WAIT_OBJECT_0 == rv);
+}
+
+void
+_PR_MD_END_THREAD(void)
+{
+ _endthreadex(0);
+}
+
+void
+_PR_MD_YIELD(void)
+{
+ /* Can NT really yield at all? */
+ Sleep(0);
+}
+
+void
+_PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri)
+{
+ int nativePri;
+ BOOL rv;
+
+ if (newPri < PR_PRIORITY_FIRST) {
+ newPri = PR_PRIORITY_FIRST;
+ } else if (newPri > PR_PRIORITY_LAST) {
+ newPri = PR_PRIORITY_LAST;
+ }
+ switch (newPri) {
+ case PR_PRIORITY_LOW:
+ nativePri = THREAD_PRIORITY_BELOW_NORMAL;
+ break;
+ case PR_PRIORITY_NORMAL:
+ nativePri = THREAD_PRIORITY_NORMAL;
+ break;
+ case PR_PRIORITY_HIGH:
+ nativePri = THREAD_PRIORITY_ABOVE_NORMAL;
+ break;
+ case PR_PRIORITY_URGENT:
+ nativePri = THREAD_PRIORITY_HIGHEST;
+ }
+ rv = SetThreadPriority(thread->handle, nativePri);
+ PR_ASSERT(rv);
+ if (!rv) {
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("PR_SetThreadPriority: can't set thread priority\n"));
+ }
+ return;
+}
+
+const DWORD MS_VC_EXCEPTION = 0x406D1388;
+
+#pragma pack(push,8)
+typedef struct tagTHREADNAME_INFO
+{
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+} THREADNAME_INFO;
+#pragma pack(pop)
+
+void
+_PR_MD_SET_CURRENT_THREAD_NAME(const char *name)
+{
+#ifdef _MSC_VER
+ THREADNAME_INFO info;
+#endif
+
+ if (sSetThreadDescription) {
+ WCHAR wideName[MAX_PATH];
+ if (MultiByteToWideChar(CP_ACP, 0, name, -1, wideName, MAX_PATH)) {
+ sSetThreadDescription(GetCurrentThread(), wideName);
+ }
+ }
+
+#ifdef _MSC_VER
+ if (!IsDebuggerPresent()) {
+ return;
+ }
+
+ info.dwType = 0x1000;
+ info.szName = (char*) name;
+ info.dwThreadID = -1;
+ info.dwFlags = 0;
+
+ __try {
+ RaiseException(MS_VC_EXCEPTION,
+ 0,
+ sizeof(info) / sizeof(ULONG_PTR),
+ (ULONG_PTR*)&info);
+ } __except(EXCEPTION_CONTINUE_EXECUTION) {
+ }
+#endif
+}
+
+void
+_PR_MD_CLEAN_THREAD(PRThread *thread)
+{
+ BOOL rv;
+
+ if (thread->md.acceptex_buf) {
+ PR_DELETE(thread->md.acceptex_buf);
+ }
+
+ if (thread->md.xmit_bufs) {
+ PR_DELETE(thread->md.xmit_bufs);
+ }
+
+ if (thread->md.blocked_sema) {
+ rv = CloseHandle(thread->md.blocked_sema);
+ PR_ASSERT(rv);
+ thread->md.blocked_sema = 0;
+ }
+ if (_native_threads_only) {
+ if (thread->md.thr_event) {
+ rv = CloseHandle(thread->md.thr_event);
+ PR_ASSERT(rv);
+ thread->md.thr_event = 0;
+ }
+ }
+
+ if (thread->md.handle) {
+ rv = CloseHandle(thread->md.handle);
+ PR_ASSERT(rv);
+ thread->md.handle = 0;
+ }
+
+ /* Don't call DeleteFiber on current fiber or we'll kill the whole thread.
+ * Don't call free(thread) until we've switched off the thread.
+ * So put this fiber (or thread) on a list to be deleted by the idle
+ * fiber next time we have a chance.
+ */
+ if (!(thread->flags & (_PR_ATTACHED|_PR_GLOBAL_SCOPE))) {
+ _MD_LOCK(&_nt_idleLock);
+ _nt_idleCount++;
+ PR_APPEND_LINK(&thread->links, &_nt_idleList);
+ _MD_UNLOCK(&_nt_idleLock);
+ }
+}
+
+void
+_PR_MD_EXIT_THREAD(PRThread *thread)
+{
+ BOOL rv;
+
+ if (thread->md.acceptex_buf) {
+ PR_DELETE(thread->md.acceptex_buf);
+ }
+
+ if (thread->md.xmit_bufs) {
+ PR_DELETE(thread->md.xmit_bufs);
+ }
+
+ if (thread->md.blocked_sema) {
+ rv = CloseHandle(thread->md.blocked_sema);
+ PR_ASSERT(rv);
+ thread->md.blocked_sema = 0;
+ }
+
+ if (_native_threads_only) {
+ if (thread->md.thr_event) {
+ rv = CloseHandle(thread->md.thr_event);
+ PR_ASSERT(rv);
+ thread->md.thr_event = 0;
+ }
+ }
+
+ if (thread->md.handle) {
+ rv = CloseHandle(thread->md.handle);
+ PR_ASSERT(rv);
+ thread->md.handle = 0;
+ }
+
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ _MD_SET_CURRENT_THREAD(NULL);
+ }
+}
+
+
+void
+_PR_MD_EXIT(PRIntn status)
+{
+ _exit(status);
+}
+
+#ifdef HAVE_FIBERS
+
+void
+_pr_fiber_mainline(void *unused)
+{
+ PRThread *fiber = _PR_MD_CURRENT_THREAD();
+
+ POST_SWITCH_WORK();
+
+ fiber->md.fiber_fn(fiber->md.fiber_arg);
+}
+
+PRThread *_PR_MD_CREATE_USER_THREAD(
+ PRUint32 stacksize, void (*start)(void *), void *arg)
+{
+ PRThread *thread;
+
+ if ( (thread = PR_NEW(PRThread)) == NULL ) {
+ return NULL;
+ }
+
+ memset(thread, 0, sizeof(PRThread));
+ thread->md.fiber_fn = start;
+ thread->md.fiber_arg = arg;
+ thread->md.fiber_stacksize = stacksize;
+ return thread;
+}
+
+void
+_PR_MD_CREATE_PRIMORDIAL_USER_THREAD(PRThread *thread)
+{
+ thread->md.fiber_id = ConvertThreadToFiber(NULL);
+ PR_ASSERT(thread->md.fiber_id);
+ _MD_SET_CURRENT_THREAD(thread);
+ _MD_SET_LAST_THREAD(thread);
+ thread->no_sched = 1;
+ return;
+}
+
+void
+_PR_MD_INIT_CONTEXT(PRThread *thread, char *top, void (*start) (void), PRBool *status)
+{
+ thread->md.fiber_fn = (void (*)(void *))start;
+ thread->md.fiber_id = CreateFiber(thread->md.fiber_stacksize,
+ (LPFIBER_START_ROUTINE)_pr_fiber_mainline, NULL);
+ if (thread->md.fiber_id != 0) {
+ *status = PR_TRUE;
+ }
+ else {
+ DWORD oserror = GetLastError();
+ PRErrorCode prerror;
+ if (oserror == ERROR_NOT_ENOUGH_MEMORY) {
+ prerror = PR_OUT_OF_MEMORY_ERROR;
+ } else {
+ prerror = PR_UNKNOWN_ERROR;
+ }
+ PR_SetError(prerror, oserror);
+ *status = PR_FALSE;
+ }
+}
+
+void
+_PR_MD_SWITCH_CONTEXT(PRThread *thread)
+{
+ PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) );
+
+ thread->md.fiber_last_error = GetLastError();
+ _PR_Schedule();
+}
+
+void
+_PR_MD_RESTORE_CONTEXT(PRThread *thread)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) );
+
+ /* The user-level code for yielding will happily add ourselves to the runq
+ * and then switch to ourselves; the NT fibers can't handle switching to
+ * ourselves.
+ */
+ if (thread != me) {
+ SetLastError(thread->md.fiber_last_error);
+ _MD_SET_CURRENT_THREAD(thread);
+ _PR_MD_SET_LAST_THREAD(me);
+ thread->no_sched = 1;
+ SwitchToFiber(thread->md.fiber_id);
+ POST_SWITCH_WORK();
+ }
+}
+
+
+#endif /* HAVE_FIBERS */
+
+PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask )
+{
+ int rv;
+
+ rv = SetThreadAffinityMask(thread->md.handle, mask);
+
+ return rv?0:-1;
+}
+
+PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask)
+{
+ PRInt32 rv, system_mask;
+
+ rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask);
+
+ return rv?0:-1;
+}
+
+void
+_PR_MD_SUSPEND_CPU(_PRCPU *cpu)
+{
+ _PR_MD_SUSPEND_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_RESUME_CPU(_PRCPU *cpu)
+{
+ _PR_MD_RESUME_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_SUSPEND_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ /*
+ ** There seems to be some doubt about whether or not SuspendThread
+ ** is a synchronous function. The test afterwards is to help veriry
+ ** that it is, which is what Microsoft says it is.
+ */
+ PRUintn rv = SuspendThread(thread->md.handle);
+ PR_ASSERT(0xffffffffUL != rv);
+ }
+}
+
+void
+_PR_MD_RESUME_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ ResumeThread(thread->md.handle);
+ }
+}
+
+PRThread*
+_MD_CURRENT_THREAD(void)
+{
+ PRThread *thread;
+
+ thread = _MD_GET_ATTACHED_THREAD();
+
+ if (NULL == thread) {
+ thread = _PRI_AttachThread(
+ PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0);
+ }
+ PR_ASSERT(thread != NULL);
+ return thread;
+}
+
diff --git a/nsprpub/pr/src/md/windows/objs.mk b/nsprpub/pr/src/md/windows/objs.mk
new file mode 100644
index 0000000000..89f022e8ad
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/objs.mk
@@ -0,0 +1,48 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ifeq ($(OS_TARGET),WINNT)
+CSRCS = ntmisc.c \
+ ntsec.c \
+ ntsem.c \
+ ntinrval.c \
+ ntgc.c \
+ ntio.c \
+ ntthread.c \
+ ntdllmn.c \
+ win32_errors.c \
+ w32ipcsem.c \
+ w32poll.c \
+ w32rng.c \
+ w32shm.c
+else
+ifeq (,$(filter-out WIN95 WINCE WINMO, $(OS_TARGET)))
+CSRCS = ntmisc.c \
+ ntsec.c \
+ ntsem.c \
+ ntinrval.c \
+ ntgc.c \
+ w95thred.c \
+ w95io.c \
+ w95cv.c \
+ w95sock.c \
+ win32_errors.c \
+ w32ipcsem.c \
+ w32poll.c \
+ w32rng.c \
+ w32shm.c \
+ w95dllmain.c
+else
+endif # win95
+endif # winnt
+
+CSRCS += $(PR_MD_CSRCS)
+ASFILES += $(PR_MD_ASFILES)
+
+OBJS += $(addprefix md/windows/$(OBJDIR)/,$(CSRCS:.c=.$(OBJ_SUFFIX))) \
+ $(addprefix md/windows/$(OBJDIR)/,$(ASFILES:.s=.$(OBJ_SUFFIX)))
+
+
diff --git a/nsprpub/pr/src/md/windows/w32ipcsem.c b/nsprpub/pr/src/md/windows/w32ipcsem.c
new file mode 100644
index 0000000000..500009352b
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/w32ipcsem.c
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: w32ipcsem.c
+ * Description: implements named semaphores for NT and WIN95.
+ */
+
+#include "primpl.h"
+
+#ifdef WINCE
+static HANDLE OpenSemaphore(DWORD inDesiredAccess,
+ BOOL inInheritHandle,
+ const char *inName)
+{
+ HANDLE retval = NULL;
+ HANDLE semaphore = NULL;
+ PRUnichar wideName[MAX_PATH]; /* name size is limited to MAX_PATH */
+
+ MultiByteToWideChar(CP_ACP, 0, inName, -1, wideName, MAX_PATH);
+ /* 0x7fffffff is the max count for our semaphore */
+ semaphore = CreateSemaphoreW(NULL, 0, 0x7fffffff, wideName);
+ if (NULL != semaphore) {
+ DWORD lastErr = GetLastError();
+
+ if (ERROR_ALREADY_EXISTS != lastErr) {
+ CloseHandle(semaphore);
+ }
+ else {
+ retval = semaphore;
+ }
+ }
+ return retval;
+}
+#endif
+
+/*
+ * NSPR-to-NT access right mapping table for semaphore objects.
+ *
+ * The SYNCHRONIZE access is required by WaitForSingleObject.
+ * The SEMAPHORE_MODIFY_STATE access is required by ReleaseSemaphore.
+ * The OR of these three access masks must equal SEMAPHORE_ALL_ACCESS.
+ * This is because if a semaphore object with the specified name
+ * exists, CreateSemaphore requests SEMAPHORE_ALL_ACCESS access to
+ * the existing object.
+ */
+static DWORD semAccessTable[] = {
+ STANDARD_RIGHTS_REQUIRED|0x1, /* read (0x1 is "query state") */
+ STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|SEMAPHORE_MODIFY_STATE, /* write */
+ 0 /* execute */
+};
+
+#ifndef _PR_GLOBAL_THREADS_ONLY
+
+/*
+ * A fiber cannot call WaitForSingleObject because that
+ * will block the other fibers running on the same thread.
+ * If a fiber needs to wait on a (semaphore) handle, we
+ * create a native thread to call WaitForSingleObject and
+ * have the fiber join the native thread.
+ */
+
+/*
+ * Arguments, return value, and error code for WaitForSingleObject
+ */
+struct WaitSingleArg {
+ HANDLE handle;
+ DWORD timeout;
+ DWORD rv;
+ DWORD error;
+};
+
+static void WaitSingleThread(void *arg)
+{
+ struct WaitSingleArg *warg = (struct WaitSingleArg *) arg;
+
+ warg->rv = WaitForSingleObject(warg->handle, warg->timeout);
+ if (warg->rv == WAIT_FAILED) {
+ warg->error = GetLastError();
+ }
+}
+
+static DWORD FiberSafeWaitForSingleObject(
+ HANDLE hHandle,
+ DWORD dwMilliseconds
+)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ return WaitForSingleObject(hHandle, dwMilliseconds);
+ } else {
+ PRThread *waitThread;
+ struct WaitSingleArg warg;
+ PRStatus rv;
+
+ warg.handle = hHandle;
+ warg.timeout = dwMilliseconds;
+ waitThread = PR_CreateThread(
+ PR_USER_THREAD, WaitSingleThread, &warg,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (waitThread == NULL) {
+ return WAIT_FAILED;
+ }
+
+ rv = PR_JoinThread(waitThread);
+ PR_ASSERT(rv == PR_SUCCESS);
+ if (rv == PR_FAILURE) {
+ return WAIT_FAILED;
+ }
+ if (warg.rv == WAIT_FAILED) {
+ SetLastError(warg.error);
+ }
+ return warg.rv;
+ }
+}
+
+#endif /* !_PR_GLOBAL_THREADS_ONLY */
+
+PRSem *_PR_MD_OPEN_SEMAPHORE(
+ const char *osname, PRIntn flags, PRIntn mode, PRUintn value)
+{
+ PRSem *sem;
+ SECURITY_ATTRIBUTES sa;
+ LPSECURITY_ATTRIBUTES lpSA = NULL;
+ PSECURITY_DESCRIPTOR pSD = NULL;
+ PACL pACL = NULL;
+
+ sem = PR_NEW(PRSem);
+ if (sem == NULL) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ if (flags & PR_SEM_CREATE) {
+ if (_PR_NT_MakeSecurityDescriptorACL(mode, semAccessTable,
+ &pSD, &pACL) == PR_SUCCESS) {
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = pSD;
+ sa.bInheritHandle = FALSE;
+ lpSA = &sa;
+ }
+#ifdef WINCE
+ {
+ /* The size of a sem's name is limited to MAX_PATH. */
+ PRUnichar wosname[MAX_PATH];
+ MultiByteToWideChar(CP_ACP, 0, osname, -1, wosname, MAX_PATH);
+ sem->sem = CreateSemaphoreW(lpSA, value, 0x7fffffff, wosname);
+ }
+#else
+ sem->sem = CreateSemaphoreA(lpSA, value, 0x7fffffff, osname);
+#endif
+ if (lpSA != NULL) {
+ _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
+ }
+ if (sem->sem == NULL) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ PR_DELETE(sem);
+ return NULL;
+ }
+ if ((flags & PR_SEM_EXCL) && (GetLastError() == ERROR_ALREADY_EXISTS)) {
+ PR_SetError(PR_FILE_EXISTS_ERROR, ERROR_ALREADY_EXISTS);
+ CloseHandle(sem->sem);
+ PR_DELETE(sem);
+ return NULL;
+ }
+ } else {
+ sem->sem = OpenSemaphore(
+ SEMAPHORE_MODIFY_STATE|SYNCHRONIZE, FALSE, osname);
+ if (sem->sem == NULL) {
+ DWORD err = GetLastError();
+
+ /*
+ * If we open a nonexistent named semaphore, NT
+ * returns ERROR_FILE_NOT_FOUND, while Win95
+ * returns ERROR_INVALID_NAME
+ */
+ if (err == ERROR_INVALID_NAME) {
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ } else {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ }
+ PR_DELETE(sem);
+ return NULL;
+ }
+ }
+ return sem;
+}
+
+PRStatus _PR_MD_WAIT_SEMAPHORE(PRSem *sem)
+{
+ DWORD rv;
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ rv = WaitForSingleObject(sem->sem, INFINITE);
+#else
+ rv = FiberSafeWaitForSingleObject(sem->sem, INFINITE);
+#endif
+ PR_ASSERT(rv == WAIT_FAILED || rv == WAIT_OBJECT_0);
+ if (rv == WAIT_FAILED) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ if (rv != WAIT_OBJECT_0) {
+ /* Should not happen */
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PRStatus _PR_MD_POST_SEMAPHORE(PRSem *sem)
+{
+ if (ReleaseSemaphore(sem->sem, 1, NULL) == FALSE) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PRStatus _PR_MD_CLOSE_SEMAPHORE(PRSem *sem)
+{
+ if (CloseHandle(sem->sem) == FALSE) {
+ _PR_MD_MAP_CLOSE_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ PR_DELETE(sem);
+ return PR_SUCCESS;
+}
diff --git a/nsprpub/pr/src/md/windows/w32poll.c b/nsprpub/pr/src/md/windows/w32poll.c
new file mode 100644
index 0000000000..241c7e3e47
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/w32poll.c
@@ -0,0 +1,342 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file implements _PR_MD_PR_POLL for Win32.
+ */
+
+/* The default value of FD_SETSIZE is 64. */
+#define FD_SETSIZE 1024
+
+#include "primpl.h"
+
+#if !defined(_PR_GLOBAL_THREADS_ONLY)
+
+struct select_data_s {
+ PRInt32 status;
+ PRInt32 error;
+ fd_set *rd, *wt, *ex;
+ const struct timeval *tv;
+};
+
+static void
+_PR_MD_select_thread(void *cdata)
+{
+ struct select_data_s *cd = (struct select_data_s *)cdata;
+
+ cd->status = select(0, cd->rd, cd->wt, cd->ex, cd->tv);
+
+ if (cd->status == SOCKET_ERROR) {
+ cd->error = WSAGetLastError();
+ }
+}
+
+int _PR_NTFiberSafeSelect(
+ int nfds,
+ fd_set *readfds,
+ fd_set *writefds,
+ fd_set *exceptfds,
+ const struct timeval *timeout)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ int ready;
+
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ ready = _MD_SELECT(nfds, readfds, writefds, exceptfds, timeout);
+ }
+ else
+ {
+ /*
+ ** Creating a new thread on each call!!
+ ** I guess web server doesn't use non-block I/O.
+ */
+ PRThread *selectThread;
+ struct select_data_s data;
+ data.status = 0;
+ data.error = 0;
+ data.rd = readfds;
+ data.wt = writefds;
+ data.ex = exceptfds;
+ data.tv = timeout;
+
+ selectThread = PR_CreateThread(
+ PR_USER_THREAD, _PR_MD_select_thread, &data,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (selectThread == NULL) {
+ return -1;
+ }
+
+ PR_JoinThread(selectThread);
+ ready = data.status;
+ if (ready == SOCKET_ERROR) {
+ WSASetLastError(data.error);
+ }
+ }
+ return ready;
+}
+
+#endif /* !defined(_PR_GLOBAL_THREADS_ONLY) */
+
+PRInt32 _PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ int ready, err;
+ fd_set rd, wt, ex;
+ fd_set *rdp, *wtp, *exp;
+ int nrd, nwt, nex;
+ PRFileDesc *bottom;
+ PRPollDesc *pd, *epd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ struct timeval tv, *tvp = NULL;
+
+ if (_PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+
+ /*
+ ** Is it an empty set? If so, just sleep for the timeout and return
+ */
+ if (0 == npds)
+ {
+ PR_Sleep(timeout);
+ return 0;
+ }
+
+ nrd = nwt = nex = 0;
+ FD_ZERO(&rd);
+ FD_ZERO(&wt);
+ FD_ZERO(&ex);
+
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ SOCKET osfd;
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ if (pd->in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pd->fd->methods->poll)(
+ pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_WRITE),
+ &out_flags_read);
+ }
+ if (pd->in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pd->fd->methods->poll)(
+ pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_READ),
+ &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read))
+ || (0 != (in_flags_write & out_flags_write)))
+ {
+ /* this one's ready right now (buffered input) */
+ if (0 == ready)
+ {
+ /*
+ * We will have to return without calling the
+ * system poll/select function. So zero the
+ * out_flags fields of all the poll descriptors
+ * before this one.
+ */
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1;
+ pd->out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ pd->out_flags = 0; /* pre-condition */
+ /* make sure this is an NSPR supported stack */
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ /* ignore a socket without PR_NSPR_IO_LAYER available */
+
+ if ((NULL != bottom)
+ && (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ if (0 == ready)
+ {
+ osfd = (SOCKET) bottom->secret->md.osfd;
+ if (in_flags_read & PR_POLL_READ)
+ {
+ pd->out_flags |= _PR_POLL_READ_SYS_READ;
+ FD_SET(osfd, &rd);
+ nrd++;
+ }
+ if (in_flags_read & PR_POLL_WRITE)
+ {
+ pd->out_flags |= _PR_POLL_READ_SYS_WRITE;
+ FD_SET(osfd, &wt);
+ nwt++;
+ }
+ if (in_flags_write & PR_POLL_READ)
+ {
+ pd->out_flags |= _PR_POLL_WRITE_SYS_READ;
+ FD_SET(osfd, &rd);
+ nrd++;
+ }
+ if (in_flags_write & PR_POLL_WRITE)
+ {
+ pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE;
+ FD_SET(osfd, &wt);
+ nwt++;
+ }
+ if (pd->in_flags & PR_POLL_EXCEPT) {
+ FD_SET(osfd, &ex);
+ nex++;
+ }
+ }
+ }
+ else
+ {
+ if (0 == ready)
+ {
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1; /* this will cause an abrupt return */
+ pd->out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+ else
+ {
+ pd->out_flags = 0;
+ }
+ }
+
+ if (0 != ready) {
+ return ready; /* no need to block */
+ }
+
+ /*
+ * FD_SET does nothing if the fd_set's internal fd_array is full. If
+ * nrd, nwt, or nex is greater than FD_SETSIZE, we know FD_SET must
+ * have failed to insert an osfd into the corresponding fd_set, and
+ * therefore we should fail.
+ */
+ if ((nrd > FD_SETSIZE) || (nwt > FD_SETSIZE) || (nex > FD_SETSIZE)) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+
+ rdp = (0 == nrd) ? NULL : &rd;
+ wtp = (0 == nwt) ? NULL : &wt;
+ exp = (0 == nex) ? NULL : &ex;
+
+ if ((NULL == rdp) && (NULL == wtp) && (NULL == exp)) {
+ PR_Sleep(timeout);
+ return 0;
+ }
+
+ if (timeout != PR_INTERVAL_NO_TIMEOUT)
+ {
+ PRInt32 ticksPerSecond = PR_TicksPerSecond();
+ tv.tv_sec = timeout / ticksPerSecond;
+ tv.tv_usec = PR_IntervalToMicroseconds( timeout % ticksPerSecond );
+ tvp = &tv;
+ }
+
+#if defined(_PR_GLOBAL_THREADS_ONLY)
+ ready = _MD_SELECT(0, rdp, wtp, exp, tvp);
+#else
+ ready = _PR_NTFiberSafeSelect(0, rdp, wtp, exp, tvp);
+#endif
+
+ /*
+ ** Now to unravel the select sets back into the client's poll
+ ** descriptor list. Is this possibly an area for pissing away
+ ** a few cycles or what?
+ */
+ if (ready > 0)
+ {
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRInt16 out_flags = 0;
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ SOCKET osfd;
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+
+ osfd = (SOCKET) bottom->secret->md.osfd;
+
+ if (FD_ISSET(osfd, &rd))
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_READ) {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_READ) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (FD_ISSET(osfd, &wt))
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_WRITE) {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (FD_ISSET(osfd, &ex)) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ }
+ pd->out_flags = out_flags;
+ if (out_flags) {
+ ready++;
+ }
+ }
+ PR_ASSERT(ready > 0);
+ }
+ else if (ready == SOCKET_ERROR)
+ {
+ err = WSAGetLastError();
+ if (err == WSAENOTSOCK)
+ {
+ /* Find the bad fds */
+ int optval;
+ int optlen = sizeof(optval);
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ pd->out_flags = 0;
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ if (getsockopt(bottom->secret->md.osfd, SOL_SOCKET,
+ SO_TYPE, (char *) &optval, &optlen) == -1)
+ {
+ PR_ASSERT(WSAGetLastError() == WSAENOTSOCK);
+ if (WSAGetLastError() == WSAENOTSOCK)
+ {
+ pd->out_flags = PR_POLL_NVAL;
+ ready++;
+ }
+ }
+ }
+ }
+ PR_ASSERT(ready > 0);
+ }
+ else {
+ _PR_MD_MAP_SELECT_ERROR(err);
+ }
+ }
+
+ return ready;
+}
diff --git a/nsprpub/pr/src/md/windows/w32rng.c b/nsprpub/pr/src/md/windows/w32rng.c
new file mode 100644
index 0000000000..c07625eccb
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/w32rng.c
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <windows.h>
+#include <time.h>
+#include <io.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <primpl.h>
+
+static BOOL
+CurrentClockTickTime(LPDWORD lpdwHigh, LPDWORD lpdwLow)
+{
+ LARGE_INTEGER liCount;
+
+ if (!QueryPerformanceCounter(&liCount)) {
+ return FALSE;
+ }
+
+ *lpdwHigh = liCount.u.HighPart;
+ *lpdwLow = liCount.u.LowPart;
+ return TRUE;
+}
+
+extern PRSize _PR_MD_GetRandomNoise( void *buf, PRSize size )
+{
+ DWORD dwHigh, dwLow, dwVal;
+ size_t n = 0;
+ size_t nBytes;
+ time_t sTime;
+
+ if (size <= 0) {
+ return 0;
+ }
+
+ CurrentClockTickTime(&dwHigh, &dwLow);
+
+ // get the maximally changing bits first
+ nBytes = sizeof(dwLow) > size ? size : sizeof(dwLow);
+ memcpy((char *)buf, &dwLow, nBytes);
+ n += nBytes;
+ size -= nBytes;
+
+ if (size <= 0) {
+ return n;
+ }
+
+ nBytes = sizeof(dwHigh) > size ? size : sizeof(dwHigh);
+ memcpy(((char *)buf) + n, &dwHigh, nBytes);
+ n += nBytes;
+ size -= nBytes;
+
+ if (size <= 0) {
+ return n;
+ }
+
+ // get the number of milliseconds that have elapsed since Windows started
+ dwVal = GetTickCount();
+
+ nBytes = sizeof(dwVal) > size ? size : sizeof(dwVal);
+ memcpy(((char *)buf) + n, &dwVal, nBytes);
+ n += nBytes;
+ size -= nBytes;
+
+ if (size <= 0) {
+ return n;
+ }
+
+ // get the time in seconds since midnight Jan 1, 1970
+ time(&sTime);
+ nBytes = sizeof(sTime) > size ? size : sizeof(sTime);
+ memcpy(((char *)buf) + n, &sTime, nBytes);
+ n += nBytes;
+
+ return n;
+}
+
diff --git a/nsprpub/pr/src/md/windows/w32shm.c b/nsprpub/pr/src/md/windows/w32shm.c
new file mode 100644
index 0000000000..b0d38b9d70
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/w32shm.c
@@ -0,0 +1,348 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <private/primpl.h>
+#include <string.h>
+#include <prshm.h>
+#include <prerr.h>
+#include <prmem.h>
+
+#if defined(PR_HAVE_WIN32_NAMED_SHARED_MEMORY)
+
+extern PRLogModuleInfo *_pr_shm_lm;
+
+/*
+ * NSPR-to-NT access right mapping table for file-mapping objects.
+ *
+ * The OR of these three access masks must equal FILE_MAP_ALL_ACCESS.
+ * This is because if a file-mapping object with the specified name
+ * exists, CreateFileMapping requests full access to the existing
+ * object.
+ */
+static DWORD filemapAccessTable[] = {
+ FILE_MAP_ALL_ACCESS & ~FILE_MAP_WRITE, /* read */
+ FILE_MAP_ALL_ACCESS & ~FILE_MAP_READ, /* write */
+ 0 /* execute */
+};
+
+extern PRSharedMemory * _MD_OpenSharedMemory(
+ const char *name,
+ PRSize size,
+ PRIntn flags,
+ PRIntn mode
+)
+{
+ char ipcname[PR_IPC_NAME_SIZE];
+ PRStatus rc = PR_SUCCESS;
+ DWORD dwHi, dwLo;
+ PRSharedMemory *shm;
+ DWORD flProtect = ( PAGE_READWRITE );
+ SECURITY_ATTRIBUTES sa;
+ LPSECURITY_ATTRIBUTES lpSA = NULL;
+ PSECURITY_DESCRIPTOR pSD = NULL;
+ PACL pACL = NULL;
+
+ rc = _PR_MakeNativeIPCName( name, ipcname, PR_IPC_NAME_SIZE, _PRIPCShm );
+ if ( PR_FAILURE == rc )
+ {
+ PR_SetError(PR_UNKNOWN_ERROR, 0 );
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: name is invalid"));
+ return(NULL);
+ }
+
+ shm = PR_NEWZAP( PRSharedMemory );
+ if ( NULL == shm )
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0 );
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: New PRSharedMemory out of memory"));
+ return(NULL);
+ }
+
+ shm->ipcname = PR_MALLOC( (PRUint32) (strlen( ipcname ) + 1) );
+ if ( NULL == shm->ipcname )
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0 );
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: New shm->ipcname out of memory"));
+ PR_DELETE(shm);
+ return(NULL);
+ }
+
+ /* copy args to struct */
+ strcpy( shm->ipcname, ipcname );
+ shm->size = size;
+ shm->mode = mode;
+ shm->flags = flags;
+ shm->ident = _PR_SHM_IDENT;
+
+ if (flags & PR_SHM_CREATE ) {
+ dwHi = (DWORD) (((PRUint64) shm->size >> 32) & 0xffffffff);
+ dwLo = (DWORD) (shm->size & 0xffffffff);
+
+ if (_PR_NT_MakeSecurityDescriptorACL(mode, filemapAccessTable,
+ &pSD, &pACL) == PR_SUCCESS) {
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = pSD;
+ sa.bInheritHandle = FALSE;
+ lpSA = &sa;
+ }
+#ifdef WINCE
+ {
+ /*
+ * This is assuming that the name will never be larger than
+ * MAX_PATH. Should we dynamically allocate?
+ */
+ PRUnichar wideIpcName[MAX_PATH];
+ MultiByteToWideChar(CP_ACP, 0, shm->ipcname, -1,
+ wideIpcName, MAX_PATH);
+ shm->handle = CreateFileMappingW(
+ (HANDLE)-1,
+ lpSA,
+ flProtect,
+ dwHi,
+ dwLo,
+ wideIpcName);
+ }
+#else
+ shm->handle = CreateFileMappingA(
+ (HANDLE)-1,
+ lpSA,
+ flProtect,
+ dwHi,
+ dwLo,
+ shm->ipcname);
+#endif
+ if (lpSA != NULL) {
+ _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
+ }
+
+ if ( NULL == shm->handle ) {
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG,
+ ( "PR_OpenSharedMemory: CreateFileMapping() failed: %s",
+ shm->ipcname ));
+ _PR_MD_MAP_DEFAULT_ERROR( GetLastError());
+ PR_FREEIF( shm->ipcname )
+ PR_DELETE( shm );
+ return(NULL);
+ } else {
+ if (( flags & PR_SHM_EXCL) && ( GetLastError() == ERROR_ALREADY_EXISTS )) {
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG,
+ ( "PR_OpenSharedMemory: Request exclusive & already exists",
+ shm->ipcname ));
+ PR_SetError( PR_FILE_EXISTS_ERROR, ERROR_ALREADY_EXISTS );
+ CloseHandle( shm->handle );
+ PR_FREEIF( shm->ipcname )
+ PR_DELETE( shm );
+ return(NULL);
+ } else {
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG,
+ ( "PR_OpenSharedMemory: CreateFileMapping() success: %s, handle: %d",
+ shm->ipcname, shm->handle ));
+ return(shm);
+ }
+ }
+ } else {
+#ifdef WINCE
+ PR_SetError( PR_NOT_IMPLEMENTED_ERROR, 0 );
+ shm->handle = NULL; /* OpenFileMapping not supported */
+#else
+ shm->handle = OpenFileMapping( FILE_MAP_WRITE, TRUE, shm->ipcname );
+#endif
+ if ( NULL == shm->handle ) {
+ _PR_MD_MAP_DEFAULT_ERROR( GetLastError());
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG,
+ ( "PR_OpenSharedMemory: OpenFileMapping() failed: %s, error: %d",
+ shm->ipcname, PR_GetOSError()));
+ PR_FREEIF( shm->ipcname );
+ PR_DELETE( shm );
+ return(NULL);
+ } else {
+ PR_LOG(_pr_shm_lm, PR_LOG_DEBUG,
+ ( "PR_OpenSharedMemory: OpenFileMapping() success: %s, handle: %d",
+ shm->ipcname, shm->handle ));
+ return(shm);
+ }
+ }
+ /* returns from separate paths */
+}
+
+extern void * _MD_AttachSharedMemory( PRSharedMemory *shm, PRIntn flags )
+{
+ PRUint32 access = FILE_MAP_WRITE;
+ void *addr;
+
+ PR_ASSERT( shm->ident == _PR_SHM_IDENT );
+
+ if ( PR_SHM_READONLY & flags ) {
+ access = FILE_MAP_READ;
+ }
+
+ addr = MapViewOfFile( shm->handle,
+ access,
+ 0, 0,
+ shm->size );
+
+ if ( NULL == addr ) {
+ _PR_MD_MAP_DEFAULT_ERROR( GetLastError());
+ PR_LOG( _pr_shm_lm, PR_LOG_ERROR,
+ ("_MD_AttachSharedMemory: MapViewOfFile() failed. OSerror: %d", PR_GetOSError()));
+ }
+
+ return( addr );
+} /* end _MD_ATTACH_SHARED_MEMORY() */
+
+
+extern PRStatus _MD_DetachSharedMemory( PRSharedMemory *shm, void *addr )
+{
+ PRStatus rc = PR_SUCCESS;
+ BOOL wrc;
+
+ PR_ASSERT( shm->ident == _PR_SHM_IDENT );
+
+ wrc = UnmapViewOfFile( addr );
+ if ( FALSE == wrc )
+ {
+ _PR_MD_MAP_DEFAULT_ERROR( GetLastError());
+ PR_LOG( _pr_shm_lm, PR_LOG_ERROR,
+ ("_MD_DetachSharedMemory: UnmapViewOfFile() failed. OSerror: %d", PR_GetOSError()));
+ rc = PR_FAILURE;
+ }
+
+ return( rc );
+}
+
+
+extern PRStatus _MD_CloseSharedMemory( PRSharedMemory *shm )
+{
+ PRStatus rc = PR_SUCCESS;
+ BOOL wrc;
+
+ PR_ASSERT( shm->ident == _PR_SHM_IDENT );
+
+ wrc = CloseHandle( shm->handle );
+ if ( FALSE == wrc )
+ {
+ _PR_MD_MAP_DEFAULT_ERROR( GetLastError());
+ PR_LOG( _pr_shm_lm, PR_LOG_ERROR,
+ ("_MD_CloseSharedMemory: CloseHandle() failed. OSerror: %d", PR_GetOSError()));
+ rc = PR_FAILURE;
+ }
+ PR_FREEIF( shm->ipcname );
+ PR_DELETE( shm );
+
+ return( rc );
+} /* end _MD_CLOSE_SHARED_MEMORY() */
+
+extern PRStatus _MD_DeleteSharedMemory( const char *name )
+{
+ return( PR_SUCCESS );
+}
+
+
+/*
+** Windows implementation of anonymous memory (file) map
+*/
+extern PRLogModuleInfo *_pr_shma_lm;
+
+extern PRFileMap* _md_OpenAnonFileMap(
+ const char *dirName,
+ PRSize size,
+ PRFileMapProtect prot
+)
+{
+ PRFileMap *fm;
+ HANDLE hFileMap;
+
+ fm = PR_CreateFileMap( (PRFileDesc*)-1, size, prot );
+ if ( NULL == fm ) {
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_OpenAnonFileMap(): PR_CreateFileMap(): failed"));
+ goto Finished;
+ }
+
+ /*
+ ** Make fm->md.hFileMap inheritable. We can't use
+ ** GetHandleInformation and SetHandleInformation
+ ** because these two functions fail with
+ ** ERROR_CALL_NOT_IMPLEMENTED on Win95.
+ */
+ if (DuplicateHandle(GetCurrentProcess(), fm->md.hFileMap,
+ GetCurrentProcess(), &hFileMap,
+ 0, TRUE /* inheritable */,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ PR_SetError( PR_UNKNOWN_ERROR, GetLastError() );
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_OpenAnonFileMap(): DuplicateHandle(): failed"));
+ PR_CloseFileMap( fm );
+ fm = NULL;
+ goto Finished;
+ }
+ CloseHandle(fm->md.hFileMap);
+ fm->md.hFileMap = hFileMap;
+
+Finished:
+ return(fm);
+} /* end md_OpenAnonFileMap() */
+
+/*
+** _md_ExportFileMapAsString()
+**
+*/
+extern PRStatus _md_ExportFileMapAsString(
+ PRFileMap *fm,
+ PRSize bufSize,
+ char *buf
+)
+{
+ PRIntn written;
+
+ written = PR_snprintf( buf, (PRUint32) bufSize, "%d:%" PR_PRIdOSFD ":%ld",
+ (PRIntn)fm->prot, (PROsfd)fm->md.hFileMap, (PRInt32)fm->md.dwAccess );
+
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_ExportFileMapAsString(): prot: %x, hFileMap: %x, dwAccess: %x",
+ fm->prot, fm->md.hFileMap, fm->md.dwAccess ));
+
+ return((written == -1)? PR_FAILURE : PR_SUCCESS);
+} /* end _md_ExportFileMapAsString() */
+
+
+/*
+** _md_ImportFileMapFromString()
+**
+*/
+extern PRFileMap * _md_ImportFileMapFromString(
+ const char *fmstring
+)
+{
+ PRIntn prot;
+ PROsfd hFileMap;
+ PRInt32 dwAccess;
+ PRFileMap *fm = NULL;
+
+ PR_sscanf( fmstring, "%d:%" PR_SCNdOSFD ":%ld",
+ &prot, &hFileMap, &dwAccess );
+
+ fm = PR_NEWZAP(PRFileMap);
+ if ( NULL == fm ) {
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_ImportFileMapFromString(): PR_NEWZAP(): Failed"));
+ return(fm);
+ }
+
+ fm->prot = (PRFileMapProtect)prot;
+ fm->md.hFileMap = (HANDLE)hFileMap;
+ fm->md.dwAccess = (DWORD)dwAccess;
+ fm->fd = (PRFileDesc*)-1;
+
+ PR_LOG( _pr_shma_lm, PR_LOG_DEBUG,
+ ("_md_ImportFileMapFromString(): fm: %p, prot: %d, hFileMap: %8.8x, dwAccess: %8.8x, fd: %x",
+ fm, prot, fm->md.hFileMap, fm->md.dwAccess, fm->fd));
+ return(fm);
+} /* end _md_ImportFileMapFromString() */
+
+#else
+Error! Why is PR_HAVE_WIN32_NAMED_SHARED_MEMORY not defined?
+#endif /* PR_HAVE_WIN32_NAMED_SHARED_MEMORY */
+/* --- end w32shm.c --- */
diff --git a/nsprpub/pr/src/md/windows/w95cv.c b/nsprpub/pr/src/md/windows/w95cv.c
new file mode 100644
index 0000000000..5cf2966ffd
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/w95cv.c
@@ -0,0 +1,371 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * w95cv.c -- Windows 95 Machine-Dependent Code for Condition Variables
+ *
+ * We implement our own condition variable wait queue. Each thread
+ * has a semaphore object (thread->md.blocked_sema) to block on while
+ * waiting on a condition variable.
+ *
+ * We use a deferred condition notify algorithm. When PR_NotifyCondVar
+ * or PR_NotifyAllCondVar is called, the condition notifies are simply
+ * recorded in the _MDLock structure. We defer the condition notifies
+ * until right after we unlock the lock. This way the awakened threads
+ * have a better chance to reaquire the lock.
+ */
+
+#include "primpl.h"
+
+/*
+ * AddThreadToCVWaitQueueInternal --
+ *
+ * Add the thread to the end of the condition variable's wait queue.
+ * The CV's lock must be locked when this function is called.
+ */
+
+static void
+AddThreadToCVWaitQueueInternal(PRThread *thred, struct _MDCVar *cv)
+{
+ PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
+ || (cv->waitTail == NULL && cv->waitHead == NULL));
+ cv->nwait += 1;
+ thred->md.inCVWaitQueue = PR_TRUE;
+ thred->md.next = NULL;
+ thred->md.prev = cv->waitTail;
+ if (cv->waitHead == NULL) {
+ cv->waitHead = thred;
+ } else {
+ cv->waitTail->md.next = thred;
+ }
+ cv->waitTail = thred;
+}
+
+/*
+ * md_UnlockAndPostNotifies --
+ *
+ * Unlock the lock, and then do the deferred condition notifies.
+ * If waitThred and waitCV are not NULL, waitThred is added to
+ * the wait queue of waitCV before the lock is unlocked.
+ *
+ * This function is called by _PR_MD_WAIT_CV and _PR_MD_UNLOCK,
+ * the two places where a lock is unlocked.
+ */
+static void
+md_UnlockAndPostNotifies(
+ _MDLock *lock,
+ PRThread *waitThred,
+ _MDCVar *waitCV)
+{
+ PRIntn index;
+ _MDNotified post;
+ _MDNotified *notified, *prev = NULL;
+
+ /*
+ * Time to actually notify any conditions that were affected
+ * while the lock was held. Get a copy of the list that's in
+ * the lock structure and then zero the original. If it's
+ * linked to other such structures, we own that storage.
+ */
+ post = lock->notified; /* a safe copy; we own the lock */
+
+#if defined(DEBUG)
+ ZeroMemory(&lock->notified, sizeof(_MDNotified)); /* reset */
+#else
+ lock->notified.length = 0; /* these are really sufficient */
+ lock->notified.link = NULL;
+#endif
+
+ /*
+ * Figure out how many threads we need to wake up.
+ */
+ notified = &post; /* this is where we start */
+ do {
+ for (index = 0; index < notified->length; ++index) {
+ _MDCVar *cv = notified->cv[index].cv;
+ PRThread *thred;
+ int i;
+
+ /* Fast special case: no waiting threads */
+ if (cv->waitHead == NULL) {
+ notified->cv[index].notifyHead = NULL;
+ continue;
+ }
+
+ /* General case */
+ if (-1 == notified->cv[index].times) {
+ /* broadcast */
+ thred = cv->waitHead;
+ while (thred != NULL) {
+ thred->md.inCVWaitQueue = PR_FALSE;
+ thred = thred->md.next;
+ }
+ notified->cv[index].notifyHead = cv->waitHead;
+ cv->waitHead = cv->waitTail = NULL;
+ cv->nwait = 0;
+ } else {
+ thred = cv->waitHead;
+ i = notified->cv[index].times;
+ while (thred != NULL && i > 0) {
+ thred->md.inCVWaitQueue = PR_FALSE;
+ thred = thred->md.next;
+ i--;
+ }
+ notified->cv[index].notifyHead = cv->waitHead;
+ cv->waitHead = thred;
+ if (cv->waitHead == NULL) {
+ cv->waitTail = NULL;
+ } else {
+ if (cv->waitHead->md.prev != NULL) {
+ cv->waitHead->md.prev->md.next = NULL;
+ cv->waitHead->md.prev = NULL;
+ }
+ }
+ cv->nwait -= notified->cv[index].times - i;
+ }
+ }
+ notified = notified->link;
+ } while (NULL != notified);
+
+ if (waitThred) {
+ AddThreadToCVWaitQueueInternal(waitThred, waitCV);
+ }
+
+ /* Release the lock before notifying */
+ LeaveCriticalSection(&lock->mutex);
+
+ notified = &post; /* this is where we start */
+ do {
+ for (index = 0; index < notified->length; ++index) {
+ PRThread *thred;
+ PRThread *next;
+
+ thred = notified->cv[index].notifyHead;
+ while (thred != NULL) {
+ BOOL rv;
+
+ next = thred->md.next;
+ thred->md.prev = thred->md.next = NULL;
+
+ rv = ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
+ PR_ASSERT(rv != 0);
+ thred = next;
+ }
+ }
+ prev = notified;
+ notified = notified->link;
+ if (&post != prev) {
+ PR_DELETE(prev);
+ }
+ } while (NULL != notified);
+}
+
+/*
+ * Notifies just get posted to the protecting mutex. The
+ * actual notification is done when the lock is released so that
+ * MP systems don't contend for a lock that they can't have.
+ */
+static void md_PostNotifyToCvar(_MDCVar *cvar, _MDLock *lock,
+ PRBool broadcast)
+{
+ PRIntn index = 0;
+ _MDNotified *notified = &lock->notified;
+
+ while (1) {
+ for (index = 0; index < notified->length; ++index) {
+ if (notified->cv[index].cv == cvar) {
+ if (broadcast) {
+ notified->cv[index].times = -1;
+ } else if (-1 != notified->cv[index].times) {
+ notified->cv[index].times += 1;
+ }
+ return;
+ }
+ }
+ /* if not full, enter new CV in this array */
+ if (notified->length < _MD_CV_NOTIFIED_LENGTH) {
+ break;
+ }
+
+ /* if there's no link, create an empty array and link it */
+ if (NULL == notified->link) {
+ notified->link = PR_NEWZAP(_MDNotified);
+ }
+
+ notified = notified->link;
+ }
+
+ /* A brand new entry in the array */
+ notified->cv[index].times = (broadcast) ? -1 : 1;
+ notified->cv[index].cv = cvar;
+ notified->length += 1;
+}
+
+/*
+ * _PR_MD_NEW_CV() -- Creating new condition variable
+ * ... Solaris uses cond_init() in similar function.
+ *
+ * returns: -1 on failure
+ * 0 when it succeeds.
+ *
+ */
+PRInt32
+_PR_MD_NEW_CV(_MDCVar *cv)
+{
+ cv->magic = _MD_MAGIC_CV;
+ /*
+ * The waitHead, waitTail, and nwait fields are zeroed
+ * when the PRCondVar structure is created.
+ */
+ return 0;
+}
+
+void _PR_MD_FREE_CV(_MDCVar *cv)
+{
+ cv->magic = (PRUint32)-1;
+ return;
+}
+
+/*
+ * _PR_MD_WAIT_CV() -- Wait on condition variable
+ */
+void _PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout )
+{
+ PRThread *thred = _PR_MD_CURRENT_THREAD();
+ DWORD rv;
+ DWORD msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ?
+ INFINITE : PR_IntervalToMilliseconds(timeout);
+
+ /*
+ * If we have pending notifies, post them now.
+ */
+ if (0 != lock->notified.length) {
+ md_UnlockAndPostNotifies(lock, thred, cv);
+ } else {
+ AddThreadToCVWaitQueueInternal(thred, cv);
+ LeaveCriticalSection(&lock->mutex);
+ }
+
+ /* Wait for notification or timeout; don't really care which */
+ rv = WaitForSingleObject(thred->md.blocked_sema, msecs);
+
+ EnterCriticalSection(&(lock->mutex));
+
+ PR_ASSERT(rv != WAIT_ABANDONED);
+ PR_ASSERT(rv != WAIT_FAILED);
+ PR_ASSERT(rv != WAIT_OBJECT_0 || thred->md.inCVWaitQueue == PR_FALSE);
+
+ if (rv == WAIT_TIMEOUT) {
+ if (thred->md.inCVWaitQueue) {
+ PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
+ || (cv->waitTail == NULL && cv->waitHead == NULL));
+ cv->nwait -= 1;
+ thred->md.inCVWaitQueue = PR_FALSE;
+ if (cv->waitHead == thred) {
+ cv->waitHead = thred->md.next;
+ if (cv->waitHead == NULL) {
+ cv->waitTail = NULL;
+ } else {
+ cv->waitHead->md.prev = NULL;
+ }
+ } else {
+ PR_ASSERT(thred->md.prev != NULL);
+ thred->md.prev->md.next = thred->md.next;
+ if (thred->md.next != NULL) {
+ thred->md.next->md.prev = thred->md.prev;
+ } else {
+ PR_ASSERT(cv->waitTail == thred);
+ cv->waitTail = thred->md.prev;
+ }
+ }
+ thred->md.next = thred->md.prev = NULL;
+ } else {
+ /*
+ * This thread must have been notified, but the
+ * ReleaseSemaphore call happens after WaitForSingleObject
+ * times out. Wait on the semaphore again to make it
+ * non-signaled. We assume this wait won't take long.
+ */
+ rv = WaitForSingleObject(thred->md.blocked_sema, INFINITE);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ }
+ PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE);
+ return;
+} /* --- end _PR_MD_WAIT_CV() --- */
+
+void _PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock)
+{
+ md_PostNotifyToCvar(cv, lock, PR_FALSE);
+ return;
+}
+
+void _PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock)
+{
+ md_PostNotifyToCvar(cv, lock, PR_TRUE);
+ return;
+}
+
+typedef BOOL (WINAPI *INITIALIZECRITICALSECTIONEX)(
+ CRITICAL_SECTION *lpCriticalSection,
+ DWORD dwSpinCount,
+ DWORD Flags);
+
+static INITIALIZECRITICALSECTIONEX sInitializeCriticalSectionEx;
+
+void _PR_MD_INIT_LOCKS(void)
+{
+ /*
+ * Starting with Windows Vista, every CRITICAL_SECTION allocates an extra
+ * RTL_CRITICAL_SECTION_DEBUG object. Unfortunately, this debug object is
+ * not reclaimed by DeleteCriticalSection(), causing an apparent memory
+ * leak. This is a debugging "feature", not a bug. If we are running on
+ * Vista or later, use InitializeCriticalSectionEx() to allocate
+ * CRITICAL_SECTIONs without debug objects.
+ */
+ HMODULE hKernel32 = GetModuleHandle("kernel32.dll");
+ PR_ASSERT(hKernel32);
+ PR_ASSERT(!sInitializeCriticalSectionEx);
+ sInitializeCriticalSectionEx = (INITIALIZECRITICALSECTIONEX)
+ GetProcAddress(hKernel32, "InitializeCriticalSectionEx");
+}
+
+/*
+ * By default, CRITICAL_SECTIONs are initialized with a spin count of 0.
+ * Joe Duffy's "Concurrent Programming on Windows" book suggests 1500 is
+ * a "reasonable starting point". On single-processor systems, the spin
+ * count is ignored and the critical section spin count is set to 0.
+ */
+#define LOCK_SPIN_COUNT 1500
+
+PRStatus _PR_MD_NEW_LOCK(_MDLock *lock)
+{
+ CRITICAL_SECTION *cs = &lock->mutex;
+ BOOL ok;
+
+ if (sInitializeCriticalSectionEx) {
+ ok = sInitializeCriticalSectionEx(cs, LOCK_SPIN_COUNT,
+ CRITICAL_SECTION_NO_DEBUG_INFO);
+ } else {
+ ok = InitializeCriticalSectionAndSpinCount(cs, LOCK_SPIN_COUNT);
+ }
+ if (!ok) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+
+ lock->notified.length = 0;
+ lock->notified.link = NULL;
+ return PR_SUCCESS;
+}
+
+void _PR_MD_UNLOCK(_MDLock *lock)
+{
+ if (0 != lock->notified.length) {
+ md_UnlockAndPostNotifies(lock, NULL, NULL);
+ } else {
+ LeaveCriticalSection(&lock->mutex);
+ }
+}
diff --git a/nsprpub/pr/src/md/windows/w95dllmain.c b/nsprpub/pr/src/md/windows/w95dllmain.c
new file mode 100644
index 0000000000..c9ab87aec5
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/w95dllmain.c
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * The DLL entry point (DllMain) for NSPR.
+ *
+ * This is used to detach threads that were automatically attached by
+ * nspr.
+ */
+
+#include <windows.h>
+#include <primpl.h>
+
+BOOL WINAPI DllMain(
+ HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved)
+{
+ PRThread *me;
+
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ break;
+ case DLL_THREAD_ATTACH:
+ break;
+ case DLL_THREAD_DETACH:
+ if (_pr_initialized) {
+ me = _MD_GET_ATTACHED_THREAD();
+ if ((me != NULL) && (me->flags & _PR_ATTACHED)) {
+ _PRI_DetachThread();
+ }
+ }
+ break;
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
diff --git a/nsprpub/pr/src/md/windows/w95io.c b/nsprpub/pr/src/md/windows/w95io.c
new file mode 100644
index 0000000000..2ad52e19ba
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/w95io.c
@@ -0,0 +1,1416 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Windows 95 IO module
+ *
+ * Assumes synchronous I/O.
+ *
+ */
+
+#include "primpl.h"
+#include <direct.h>
+#include <mbstring.h>
+#ifdef MOZ_UNICODE
+#include <wchar.h>
+#endif /* MOZ_UNICODE */
+
+struct _MDLock _pr_ioq_lock;
+
+/*
+ * NSPR-to-NT access right mapping table for files.
+ */
+static DWORD fileAccessTable[] = {
+ FILE_GENERIC_READ,
+ FILE_GENERIC_WRITE,
+ FILE_GENERIC_EXECUTE
+};
+
+/*
+ * NSPR-to-NT access right mapping table for directories.
+ */
+static DWORD dirAccessTable[] = {
+ FILE_GENERIC_READ,
+ FILE_GENERIC_WRITE|FILE_DELETE_CHILD,
+ FILE_GENERIC_EXECUTE
+};
+
+static PRBool IsPrevCharSlash(const char *str, const char *current);
+
+void
+_PR_MD_INIT_IO()
+{
+ WORD WSAVersion = 0x0101;
+ WSADATA WSAData;
+ int err;
+
+ err = WSAStartup( WSAVersion, &WSAData );
+ PR_ASSERT(0 == err);
+
+#ifdef DEBUG
+ /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */
+ {
+ SYSTEMTIME systime;
+ union {
+ PRTime prt;
+ FILETIME ft;
+ } filetime;
+ BOOL rv;
+
+ systime.wYear = 1970;
+ systime.wMonth = 1;
+ /* wDayOfWeek is ignored */
+ systime.wDay = 1;
+ systime.wHour = 0;
+ systime.wMinute = 0;
+ systime.wSecond = 0;
+ systime.wMilliseconds = 0;
+
+ rv = SystemTimeToFileTime(&systime, &filetime.ft);
+ PR_ASSERT(0 != rv);
+ PR_ASSERT(filetime.prt == _pr_filetime_offset);
+ }
+#endif /* DEBUG */
+
+ _PR_NT_InitSids();
+
+ _PR_MD_InitSockets();
+}
+
+PRStatus
+_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ DWORD rv;
+
+ PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
+ INFINITE : PR_IntervalToMilliseconds(ticks);
+ rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
+ switch(rv)
+ {
+ case WAIT_OBJECT_0:
+ return PR_SUCCESS;
+ case WAIT_TIMEOUT:
+ _PR_THREAD_LOCK(thread);
+ if (thread->state == _PR_IO_WAIT) {
+ ;
+ } else {
+ if (thread->wait.cvar != NULL) {
+ thread->wait.cvar = NULL;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The CVAR was notified just as the timeout
+ * occurred. This led to us being notified twice.
+ * call WaitForSingleObject() to clear the semaphore.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ rv = WaitForSingleObject(thread->md.blocked_sema, 0);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ }
+ return PR_SUCCESS;
+ default:
+ return PR_FAILURE;
+ }
+}
+PRStatus
+_PR_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if ( _PR_IS_NATIVE_THREAD(thread) )
+ {
+ if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE) {
+ return PR_FAILURE;
+ }
+ else {
+ return PR_SUCCESS;
+ }
+ }
+}
+
+
+/* --- FILE IO ----------------------------------------------------------- */
+/*
+ * _PR_MD_OPEN() -- Open a file
+ *
+ * returns: a fileHandle
+ *
+ * The NSPR open flags (osflags) are translated into flags for Win95
+ *
+ * Mode seems to be passed in as a unix style file permissions argument
+ * as in 0666, in the case of opening the logFile.
+ *
+ */
+PROsfd
+_PR_MD_OPEN(const char *name, PRIntn osflags, int mode)
+{
+ HANDLE file;
+ PRInt32 access = 0;
+ PRInt32 flags = 0;
+ PRInt32 flag6 = 0;
+
+ if (osflags & PR_SYNC) {
+ flag6 = FILE_FLAG_WRITE_THROUGH;
+ }
+
+ if (osflags & PR_RDONLY || osflags & PR_RDWR) {
+ access |= GENERIC_READ;
+ }
+ if (osflags & PR_WRONLY || osflags & PR_RDWR) {
+ access |= GENERIC_WRITE;
+ }
+
+ if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) {
+ flags = CREATE_NEW;
+ }
+ else if (osflags & PR_CREATE_FILE) {
+ if (osflags & PR_TRUNCATE) {
+ flags = CREATE_ALWAYS;
+ }
+ else {
+ flags = OPEN_ALWAYS;
+ }
+ } else {
+ if (osflags & PR_TRUNCATE) {
+ flags = TRUNCATE_EXISTING;
+ }
+ else {
+ flags = OPEN_EXISTING;
+ }
+ }
+
+ file = CreateFileA(name,
+ access,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ flags,
+ flag6,
+ NULL);
+ if (file == INVALID_HANDLE_VALUE) {
+ _PR_MD_MAP_OPEN_ERROR(GetLastError());
+ return -1;
+ }
+
+ return (PROsfd)file;
+}
+
+PROsfd
+_PR_MD_OPEN_FILE(const char *name, PRIntn osflags, int mode)
+{
+ HANDLE file;
+ PRInt32 access = 0;
+ PRInt32 flags = 0;
+ PRInt32 flag6 = 0;
+ SECURITY_ATTRIBUTES sa;
+ LPSECURITY_ATTRIBUTES lpSA = NULL;
+ PSECURITY_DESCRIPTOR pSD = NULL;
+ PACL pACL = NULL;
+
+ if (osflags & PR_CREATE_FILE) {
+ if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable,
+ &pSD, &pACL) == PR_SUCCESS) {
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = pSD;
+ sa.bInheritHandle = FALSE;
+ lpSA = &sa;
+ }
+ }
+
+ if (osflags & PR_SYNC) {
+ flag6 = FILE_FLAG_WRITE_THROUGH;
+ }
+
+ if (osflags & PR_RDONLY || osflags & PR_RDWR) {
+ access |= GENERIC_READ;
+ }
+ if (osflags & PR_WRONLY || osflags & PR_RDWR) {
+ access |= GENERIC_WRITE;
+ }
+
+ if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) {
+ flags = CREATE_NEW;
+ }
+ else if (osflags & PR_CREATE_FILE) {
+ if (osflags & PR_TRUNCATE) {
+ flags = CREATE_ALWAYS;
+ }
+ else {
+ flags = OPEN_ALWAYS;
+ }
+ } else {
+ if (osflags & PR_TRUNCATE) {
+ flags = TRUNCATE_EXISTING;
+ }
+ else {
+ flags = OPEN_EXISTING;
+ }
+ }
+
+ file = CreateFileA(name,
+ access,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ lpSA,
+ flags,
+ flag6,
+ NULL);
+ if (lpSA != NULL) {
+ _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
+ }
+ if (file == INVALID_HANDLE_VALUE) {
+ _PR_MD_MAP_OPEN_ERROR(GetLastError());
+ return -1;
+ }
+
+ return (PROsfd)file;
+}
+
+PRInt32
+_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ PRUint32 bytes;
+ int rv, err;
+
+ rv = ReadFile((HANDLE)fd->secret->md.osfd,
+ (LPVOID)buf,
+ len,
+ &bytes,
+ NULL);
+
+ if (rv == 0)
+ {
+ err = GetLastError();
+ /* ERROR_HANDLE_EOF can only be returned by async io */
+ PR_ASSERT(err != ERROR_HANDLE_EOF);
+ if (err == ERROR_BROKEN_PIPE) {
+ return 0;
+ }
+ else {
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ }
+ }
+ return bytes;
+}
+
+PRInt32
+_PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len)
+{
+ PROsfd f = fd->secret->md.osfd;
+ PRInt32 bytes;
+ int rv;
+
+ rv = WriteFile((HANDLE)f,
+ buf,
+ len,
+ &bytes,
+ NULL );
+
+ if (rv == 0)
+ {
+ _PR_MD_MAP_WRITE_ERROR(GetLastError());
+ return -1;
+ }
+ return bytes;
+} /* --- end _PR_MD_WRITE() --- */
+
+PROffset32
+_PR_MD_LSEEK(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence)
+{
+ DWORD moveMethod;
+ PROffset32 rv;
+
+ switch (whence) {
+ case PR_SEEK_SET:
+ moveMethod = FILE_BEGIN;
+ break;
+ case PR_SEEK_CUR:
+ moveMethod = FILE_CURRENT;
+ break;
+ case PR_SEEK_END:
+ moveMethod = FILE_END;
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+
+ rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, moveMethod);
+
+ /*
+ * If the lpDistanceToMoveHigh argument (third argument) is
+ * NULL, SetFilePointer returns 0xffffffff on failure.
+ */
+ if (-1 == rv) {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ }
+ return rv;
+}
+
+PROffset64
+_PR_MD_LSEEK64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence)
+{
+ DWORD moveMethod;
+ LARGE_INTEGER li;
+ DWORD err;
+
+ switch (whence) {
+ case PR_SEEK_SET:
+ moveMethod = FILE_BEGIN;
+ break;
+ case PR_SEEK_CUR:
+ moveMethod = FILE_CURRENT;
+ break;
+ case PR_SEEK_END:
+ moveMethod = FILE_END;
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+
+ li.QuadPart = offset;
+ li.LowPart = SetFilePointer((HANDLE)fd->secret->md.osfd,
+ li.LowPart, &li.HighPart, moveMethod);
+
+ if (0xffffffff == li.LowPart && (err = GetLastError()) != NO_ERROR) {
+ _PR_MD_MAP_LSEEK_ERROR(err);
+ li.QuadPart = -1;
+ }
+ return li.QuadPart;
+}
+
+/*
+ * This is documented to succeed on read-only files, but Win32's
+ * FlushFileBuffers functions fails with "access denied" in such a
+ * case. So we only signal an error if the error is *not* "access
+ * denied".
+ */
+PRInt32
+_PR_MD_FSYNC(PRFileDesc *fd)
+{
+ /*
+ * From the documentation:
+ *
+ * On Windows NT, the function FlushFileBuffers fails if hFile
+ * is a handle to console output. That is because console
+ * output is not buffered. The function returns FALSE, and
+ * GetLastError returns ERROR_INVALID_HANDLE.
+ *
+ * On the other hand, on Win95, it returns without error. I cannot
+ * assume that 0, 1, and 2 are console, because if someone closes
+ * System.out and then opens a file, they might get file descriptor
+ * 1. An error on *that* version of 1 should be reported, whereas
+ * an error on System.out (which was the original 1) should be
+ * ignored. So I use isatty() to ensure that such an error was due
+ * to this bogosity, and if it was, I ignore the error.
+ */
+
+ BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd);
+
+ if (!ok) {
+ DWORD err = GetLastError();
+ if (err != ERROR_ACCESS_DENIED) { // from winerror.h
+ _PR_MD_MAP_FSYNC_ERROR(err);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+PRInt32
+_MD_CloseFile(PROsfd osfd)
+{
+ PRInt32 rv;
+
+ rv = (CloseHandle((HANDLE)osfd))?0:-1;
+ if (rv == -1) {
+ _PR_MD_MAP_CLOSE_ERROR(GetLastError());
+ }
+ return rv;
+}
+
+
+/* --- DIR IO ------------------------------------------------------------ */
+#define GetFileFromDIR(d) (d)->d_entry.cFileName
+#define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
+
+static void FlipSlashes(char *cp, size_t len)
+{
+ while (len-- > 0) {
+ if (cp[0] == '/') {
+ cp[0] = PR_DIRECTORY_SEPARATOR;
+ }
+ cp = _mbsinc(cp);
+ }
+} /* end FlipSlashes() */
+
+
+/*
+**
+** Local implementations of standard Unix RTL functions which are not provided
+** by the VC RTL.
+**
+*/
+
+PRInt32
+_PR_MD_CLOSE_DIR(_MDDir *d)
+{
+ if ( d ) {
+ if (FindClose(d->d_hdl)) {
+ d->magic = (PRUint32)-1;
+ return 0;
+ } else {
+ _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
+ return -1;
+ }
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+}
+
+
+PRStatus
+_PR_MD_OPEN_DIR(_MDDir *d, const char *name)
+{
+ char filename[ MAX_PATH ];
+ size_t len;
+
+ len = strlen(name);
+ /* Need 5 bytes for \*.* and the trailing null byte. */
+ if (len + 5 > MAX_PATH) {
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
+ return PR_FAILURE;
+ }
+ strcpy(filename, name);
+
+ /*
+ * If 'name' ends in a slash or backslash, do not append
+ * another backslash.
+ */
+ if (IsPrevCharSlash(filename, filename + len)) {
+ len--;
+ }
+ strcpy(&filename[len], "\\*.*");
+ FlipSlashes( filename, strlen(filename) );
+
+ d->d_hdl = FindFirstFileA( filename, &(d->d_entry) );
+ if ( d->d_hdl == INVALID_HANDLE_VALUE ) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ d->firstEntry = PR_TRUE;
+ d->magic = _MD_MAGIC_DIR;
+ return PR_SUCCESS;
+}
+
+char *
+_PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
+{
+ PRInt32 err;
+ BOOL rv;
+ char *fileName;
+
+ if ( d ) {
+ while (1) {
+ if (d->firstEntry) {
+ d->firstEntry = PR_FALSE;
+ rv = 1;
+ } else {
+ rv = FindNextFileA(d->d_hdl, &(d->d_entry));
+ }
+ if (rv == 0) {
+ break;
+ }
+ fileName = GetFileFromDIR(d);
+ if ( (flags & PR_SKIP_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '\0')) {
+ continue;
+ }
+ if ( (flags & PR_SKIP_DOT_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '.') &&
+ (fileName[2] == '\0')) {
+ continue;
+ }
+ if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) {
+ continue;
+ }
+ return fileName;
+ }
+ err = GetLastError();
+ PR_ASSERT(NO_ERROR != err);
+ _PR_MD_MAP_READDIR_ERROR(err);
+ return NULL;
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+}
+
+PRInt32
+_PR_MD_DELETE(const char *name)
+{
+ if (DeleteFileA(name)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_DELETE_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+void
+_PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
+{
+ PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
+ CopyMemory(prtm, filetime, sizeof(PRTime));
+#if defined(__MINGW32__)
+ *prtm = (*prtm - _pr_filetime_offset) / 10LL;
+#else
+ *prtm = (*prtm - _pr_filetime_offset) / 10i64;
+#endif
+
+#ifdef DEBUG
+ /* Doublecheck our calculation. */
+ {
+ SYSTEMTIME systime;
+ PRExplodedTime etm;
+ PRTime cmp; /* for comparison */
+ BOOL rv;
+
+ rv = FileTimeToSystemTime(filetime, &systime);
+ PR_ASSERT(0 != rv);
+
+ /*
+ * PR_ImplodeTime ignores wday and yday.
+ */
+ etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC;
+ etm.tm_sec = systime.wSecond;
+ etm.tm_min = systime.wMinute;
+ etm.tm_hour = systime.wHour;
+ etm.tm_mday = systime.wDay;
+ etm.tm_month = systime.wMonth - 1;
+ etm.tm_year = systime.wYear;
+ /*
+ * It is not well-documented what time zone the FILETIME's
+ * are in. WIN32_FIND_DATA is documented to be in UTC (GMT).
+ * But BY_HANDLE_FILE_INFORMATION is unclear about this.
+ * By our best judgement, we assume that FILETIME is in UTC.
+ */
+ etm.tm_params.tp_gmt_offset = 0;
+ etm.tm_params.tp_dst_offset = 0;
+ cmp = PR_ImplodeTime(&etm);
+
+ /*
+ * SYSTEMTIME is in milliseconds precision, so we convert PRTime's
+ * microseconds to milliseconds before doing the comparison.
+ */
+ PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC));
+ }
+#endif /* DEBUG */
+}
+
+PRInt32
+_PR_MD_STAT(const char *fn, struct stat *info)
+{
+ PRInt32 rv;
+
+ rv = _stat(fn, (struct _stat *)info);
+ if (-1 == rv) {
+ /*
+ * Check for MSVC runtime library _stat() bug.
+ * (It's really a bug in FindFirstFile().)
+ * If a pathname ends in a backslash or slash,
+ * e.g., c:\temp\ or c:/temp/, _stat() will fail.
+ * Note: a pathname ending in a slash (e.g., c:/temp/)
+ * can be handled by _stat() on NT but not on Win95.
+ *
+ * We remove the backslash or slash at the end and
+ * try again.
+ */
+
+ size_t len = strlen(fn);
+ if (len > 0 && len <= _MAX_PATH
+ && IsPrevCharSlash(fn, fn + len)) {
+ char newfn[_MAX_PATH + 1];
+
+ strcpy(newfn, fn);
+ newfn[len - 1] = '\0';
+ rv = _stat(newfn, (struct _stat *)info);
+ }
+ }
+
+ if (-1 == rv) {
+ _PR_MD_MAP_STAT_ERROR(errno);
+ }
+ return rv;
+}
+
+#define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
+
+static PRBool
+IsPrevCharSlash(const char *str, const char *current)
+{
+ const char *prev;
+
+ if (str >= current) {
+ return PR_FALSE;
+ }
+ prev = _mbsdec(str, current);
+ return (prev == current - 1) && _PR_IS_SLASH(*prev);
+}
+
+/*
+ * IsRootDirectory --
+ *
+ * Return PR_TRUE if the pathname 'fn' is a valid root directory,
+ * else return PR_FALSE. The char buffer pointed to by 'fn' must
+ * be writable. During the execution of this function, the contents
+ * of the buffer pointed to by 'fn' may be modified, but on return
+ * the original contents will be restored. 'buflen' is the size of
+ * the buffer pointed to by 'fn'.
+ *
+ * Root directories come in three formats:
+ * 1. / or \, meaning the root directory of the current drive.
+ * 2. C:/ or C:\, where C is a drive letter.
+ * 3. \\<server name>\<share point name>\ or
+ * \\<server name>\<share point name>, meaning the root directory
+ * of a UNC (Universal Naming Convention) name.
+ */
+
+static PRBool
+IsRootDirectory(char *fn, size_t buflen)
+{
+ char *p;
+ PRBool slashAdded = PR_FALSE;
+ PRBool rv = PR_FALSE;
+
+ if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') {
+ return PR_TRUE;
+ }
+
+ if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2])
+ && fn[3] == '\0') {
+ rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
+ return rv;
+ }
+
+ /* The UNC root directory */
+
+ if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) {
+ /* The 'server' part should have at least one character. */
+ p = &fn[2];
+ if (*p == '\0' || _PR_IS_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the next slash */
+ do {
+ p = _mbsinc(p);
+ } while (*p != '\0' && !_PR_IS_SLASH(*p));
+ if (*p == '\0') {
+ return PR_FALSE;
+ }
+
+ /* The 'share' part should have at least one character. */
+ p++;
+ if (*p == '\0' || _PR_IS_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the final slash */
+ do {
+ p = _mbsinc(p);
+ } while (*p != '\0' && !_PR_IS_SLASH(*p));
+ if (_PR_IS_SLASH(*p) && p[1] != '\0') {
+ return PR_FALSE;
+ }
+ if (*p == '\0') {
+ /*
+ * GetDriveType() doesn't work correctly if the
+ * path is of the form \\server\share, so we add
+ * a final slash temporarily.
+ */
+ if ((p + 1) < (fn + buflen)) {
+ *p++ = '\\';
+ *p = '\0';
+ slashAdded = PR_TRUE;
+ } else {
+ return PR_FALSE; /* name too long */
+ }
+ }
+ rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
+ /* restore the 'fn' buffer */
+ if (slashAdded) {
+ *--p = '\0';
+ }
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
+{
+ WIN32_FILE_ATTRIBUTE_DATA findFileData;
+ BOOL rv;
+
+ if (NULL == fn || '\0' == *fn) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+
+ rv = GetFileAttributesEx(fn, GetFileExInfoStandard, &findFileData);
+ if (!rv) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ info->type = PR_FILE_DIRECTORY;
+ } else {
+ info->type = PR_FILE_FILE;
+ }
+
+ info->size = findFileData.nFileSizeHigh;
+ info->size = (info->size << 32) + findFileData.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
+
+ if (0 == findFileData.ftCreationTime.dwLowDateTime &&
+ 0 == findFileData.ftCreationTime.dwHighDateTime) {
+ info->creationTime = info->modifyTime;
+ } else {
+ _PR_FileTimeToPRTime(&findFileData.ftCreationTime,
+ &info->creationTime);
+ }
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
+{
+ PRFileInfo64 info64;
+ PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64);
+ if (0 == rv)
+ {
+ info->type = info64.type;
+ info->size = (PRUint32) info64.size;
+ info->modifyTime = info64.modifyTime;
+ info->creationTime = info64.creationTime;
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
+{
+ int rv;
+
+ BY_HANDLE_FILE_INFORMATION hinfo;
+
+ rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
+ if (rv == FALSE) {
+ _PR_MD_MAP_FSTAT_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ info->type = PR_FILE_DIRECTORY;
+ }
+ else {
+ info->type = PR_FILE_FILE;
+ }
+
+ info->size = hinfo.nFileSizeHigh;
+ info->size = (info->size << 32) + hinfo.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
+ _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
+{
+ PRFileInfo64 info64;
+ int rv = _PR_MD_GETOPENFILEINFO64(fd, &info64);
+ if (0 == rv)
+ {
+ info->type = info64.type;
+ info->modifyTime = info64.modifyTime;
+ info->creationTime = info64.creationTime;
+ LL_L2I(info->size, info64.size);
+ }
+ return rv;
+}
+
+PRStatus
+_PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable)
+{
+ BOOL rv;
+
+ /*
+ * The SetHandleInformation function fails with the
+ * ERROR_CALL_NOT_IMPLEMENTED error on Win95.
+ */
+ rv = SetHandleInformation(
+ (HANDLE)fd->secret->md.osfd,
+ HANDLE_FLAG_INHERIT,
+ inheritable ? HANDLE_FLAG_INHERIT : 0);
+ if (0 == rv) {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+void
+_PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported)
+{
+ if (imported) {
+ fd->secret->inheritable = _PR_TRI_UNKNOWN;
+ } else {
+ fd->secret->inheritable = _PR_TRI_FALSE;
+ }
+}
+
+void
+_PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd)
+{
+ DWORD flags;
+
+ PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable);
+ if (GetHandleInformation((HANDLE)fd->secret->md.osfd, &flags)) {
+ if (flags & HANDLE_FLAG_INHERIT) {
+ fd->secret->inheritable = _PR_TRI_TRUE;
+ } else {
+ fd->secret->inheritable = _PR_TRI_FALSE;
+ }
+ }
+}
+
+PRInt32
+_PR_MD_RENAME(const char *from, const char *to)
+{
+ /* Does this work with dot-relative pathnames? */
+ if (MoveFileA(from, to)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RENAME_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_ACCESS(const char *name, PRAccessHow how)
+{
+ PRInt32 rv;
+ switch (how) {
+ case PR_ACCESS_WRITE_OK:
+ rv = _access(name, 02);
+ break;
+ case PR_ACCESS_READ_OK:
+ rv = _access(name, 04);
+ break;
+ case PR_ACCESS_EXISTS:
+ return _access(name, 00);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_ACCESS_ERROR(errno);
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_MKDIR(const char *name, PRIntn mode)
+{
+ /* XXXMB - how to translate the "mode"??? */
+ if (CreateDirectoryA(name, NULL)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_MKDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_MAKE_DIR(const char *name, PRIntn mode)
+{
+ BOOL rv;
+ SECURITY_ATTRIBUTES sa;
+ LPSECURITY_ATTRIBUTES lpSA = NULL;
+ PSECURITY_DESCRIPTOR pSD = NULL;
+ PACL pACL = NULL;
+
+ if (_PR_NT_MakeSecurityDescriptorACL(mode, dirAccessTable,
+ &pSD, &pACL) == PR_SUCCESS) {
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = pSD;
+ sa.bInheritHandle = FALSE;
+ lpSA = &sa;
+ }
+ rv = CreateDirectoryA(name, lpSA);
+ if (lpSA != NULL) {
+ _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
+ }
+ if (rv) {
+ return 0;
+ } else {
+ _PR_MD_MAP_MKDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_RMDIR(const char *name)
+{
+ if (RemoveDirectoryA(name)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RMDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRStatus
+_PR_MD_LOCKFILE(PROsfd f)
+{
+ PRStatus rc = PR_SUCCESS;
+ DWORD rv;
+
+ rv = LockFile( (HANDLE)f,
+ 0l, 0l,
+ 0x0l, 0xffffffffl );
+ if ( rv == 0 ) {
+ DWORD err = GetLastError();
+ _PR_MD_MAP_DEFAULT_ERROR(err);
+ PR_LOG( _pr_io_lm, PR_LOG_ERROR,
+ ("_PR_MD_LOCKFILE() failed. Error: %d", err ));
+ rc = PR_FAILURE;
+ }
+
+ return rc;
+} /* end _PR_MD_LOCKFILE() */
+
+PRStatus
+_PR_MD_TLOCKFILE(PROsfd f)
+{
+ PR_SetError( PR_NOT_IMPLEMENTED_ERROR, 0 );
+ return PR_FAILURE;
+} /* end _PR_MD_TLOCKFILE() */
+
+
+PRStatus
+_PR_MD_UNLOCKFILE(PROsfd f)
+{
+ PRInt32 rv;
+
+ rv = UnlockFile( (HANDLE) f,
+ 0l, 0l,
+ 0x0l, 0xffffffffl );
+
+ if ( rv )
+ {
+ return PR_SUCCESS;
+ }
+ else
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+} /* end _PR_MD_UNLOCKFILE() */
+
+PRInt32
+_PR_MD_PIPEAVAILABLE(PRFileDesc *fd)
+{
+ if (NULL == fd) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ }
+ else {
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ }
+ return -1;
+}
+
+#ifdef MOZ_UNICODE
+
+typedef HANDLE (WINAPI *CreateFileWFn) (LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
+static CreateFileWFn createFileW = CreateFileW;
+typedef HANDLE (WINAPI *FindFirstFileWFn) (LPCWSTR, LPWIN32_FIND_DATAW);
+static FindFirstFileWFn findFirstFileW = FindFirstFileW;
+typedef BOOL (WINAPI *FindNextFileWFn) (HANDLE, LPWIN32_FIND_DATAW);
+static FindNextFileWFn findNextFileW = FindNextFileW;
+typedef DWORD (WINAPI *GetFullPathNameWFn) (LPCWSTR, DWORD, LPWSTR, LPWSTR *);
+static GetFullPathNameWFn getFullPathNameW = GetFullPathNameW;
+typedef UINT (WINAPI *GetDriveTypeWFn) (LPCWSTR);
+static GetDriveTypeWFn getDriveTypeW = GetDriveTypeW;
+
+#endif /* MOZ_UNICODE */
+
+#ifdef MOZ_UNICODE
+
+/* ================ UTF16 Interfaces ================================ */
+static void FlipSlashesW(PRUnichar *cp, size_t len)
+{
+ while (len-- > 0) {
+ if (cp[0] == L'/') {
+ cp[0] = L'\\';
+ }
+ cp++;
+ }
+} /* end FlipSlashesW() */
+
+PROsfd
+_PR_MD_OPEN_FILE_UTF16(const PRUnichar *name, PRIntn osflags, int mode)
+{
+ HANDLE file;
+ PRInt32 access = 0;
+ PRInt32 flags = 0;
+ PRInt32 flag6 = 0;
+ SECURITY_ATTRIBUTES sa;
+ LPSECURITY_ATTRIBUTES lpSA = NULL;
+ PSECURITY_DESCRIPTOR pSD = NULL;
+ PACL pACL = NULL;
+
+ if (osflags & PR_CREATE_FILE) {
+ if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable,
+ &pSD, &pACL) == PR_SUCCESS) {
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = pSD;
+ sa.bInheritHandle = FALSE;
+ lpSA = &sa;
+ }
+ }
+
+ if (osflags & PR_SYNC) {
+ flag6 = FILE_FLAG_WRITE_THROUGH;
+ }
+
+ if (osflags & PR_RDONLY || osflags & PR_RDWR) {
+ access |= GENERIC_READ;
+ }
+ if (osflags & PR_WRONLY || osflags & PR_RDWR) {
+ access |= GENERIC_WRITE;
+ }
+
+ if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) {
+ flags = CREATE_NEW;
+ }
+ else if (osflags & PR_CREATE_FILE) {
+ if (osflags & PR_TRUNCATE) {
+ flags = CREATE_ALWAYS;
+ }
+ else {
+ flags = OPEN_ALWAYS;
+ }
+ } else {
+ if (osflags & PR_TRUNCATE) {
+ flags = TRUNCATE_EXISTING;
+ }
+ else {
+ flags = OPEN_EXISTING;
+ }
+ }
+
+ file = createFileW(name,
+ access,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ lpSA,
+ flags,
+ flag6,
+ NULL);
+ if (lpSA != NULL) {
+ _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
+ }
+ if (file == INVALID_HANDLE_VALUE) {
+ _PR_MD_MAP_OPEN_ERROR(GetLastError());
+ return -1;
+ }
+
+ return (PROsfd)file;
+}
+
+PRStatus
+_PR_MD_OPEN_DIR_UTF16(_MDDirUTF16 *d, const PRUnichar *name)
+{
+ PRUnichar filename[ MAX_PATH ];
+ int len;
+
+ len = wcslen(name);
+ /* Need 5 bytes for \*.* and the trailing null byte. */
+ if (len + 5 > MAX_PATH) {
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
+ return PR_FAILURE;
+ }
+ wcscpy(filename, name);
+
+ /*
+ * If 'name' ends in a slash or backslash, do not append
+ * another backslash.
+ */
+ if (filename[len - 1] == L'/' || filename[len - 1] == L'\\') {
+ len--;
+ }
+ wcscpy(&filename[len], L"\\*.*");
+ FlipSlashesW( filename, wcslen(filename) );
+
+ d->d_hdl = findFirstFileW( filename, &(d->d_entry) );
+ if ( d->d_hdl == INVALID_HANDLE_VALUE ) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ d->firstEntry = PR_TRUE;
+ d->magic = _MD_MAGIC_DIR;
+ return PR_SUCCESS;
+}
+
+PRUnichar *
+_PR_MD_READ_DIR_UTF16(_MDDirUTF16 *d, PRIntn flags)
+{
+ PRInt32 err;
+ BOOL rv;
+ PRUnichar *fileName;
+
+ if ( d ) {
+ while (1) {
+ if (d->firstEntry) {
+ d->firstEntry = PR_FALSE;
+ rv = 1;
+ } else {
+ rv = findNextFileW(d->d_hdl, &(d->d_entry));
+ }
+ if (rv == 0) {
+ break;
+ }
+ fileName = GetFileFromDIR(d);
+ if ( (flags & PR_SKIP_DOT) &&
+ (fileName[0] == L'.') && (fileName[1] == L'\0')) {
+ continue;
+ }
+ if ( (flags & PR_SKIP_DOT_DOT) &&
+ (fileName[0] == L'.') && (fileName[1] == L'.') &&
+ (fileName[2] == L'\0')) {
+ continue;
+ }
+ if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) {
+ continue;
+ }
+ return fileName;
+ }
+ err = GetLastError();
+ PR_ASSERT(NO_ERROR != err);
+ _PR_MD_MAP_READDIR_ERROR(err);
+ return NULL;
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+}
+
+PRInt32
+_PR_MD_CLOSE_DIR_UTF16(_MDDirUTF16 *d)
+{
+ if ( d ) {
+ if (FindClose(d->d_hdl)) {
+ d->magic = (PRUint32)-1;
+ return 0;
+ } else {
+ _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
+ return -1;
+ }
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+}
+
+#define _PR_IS_W_SLASH(ch) ((ch) == L'/' || (ch) == L'\\')
+
+/*
+ * IsRootDirectoryW --
+ *
+ * Return PR_TRUE if the pathname 'fn' is a valid root directory,
+ * else return PR_FALSE. The PRUnichar buffer pointed to by 'fn' must
+ * be writable. During the execution of this function, the contents
+ * of the buffer pointed to by 'fn' may be modified, but on return
+ * the original contents will be restored. 'buflen' is the size of
+ * the buffer pointed to by 'fn', in PRUnichars.
+ *
+ * Root directories come in three formats:
+ * 1. / or \, meaning the root directory of the current drive.
+ * 2. C:/ or C:\, where C is a drive letter.
+ * 3. \\<server name>\<share point name>\ or
+ * \\<server name>\<share point name>, meaning the root directory
+ * of a UNC (Universal Naming Convention) name.
+ */
+
+static PRBool
+IsRootDirectoryW(PRUnichar *fn, size_t buflen)
+{
+ PRUnichar *p;
+ PRBool slashAdded = PR_FALSE;
+ PRBool rv = PR_FALSE;
+
+ if (_PR_IS_W_SLASH(fn[0]) && fn[1] == L'\0') {
+ return PR_TRUE;
+ }
+
+ if (iswalpha(fn[0]) && fn[1] == L':' && _PR_IS_W_SLASH(fn[2])
+ && fn[3] == L'\0') {
+ rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE;
+ return rv;
+ }
+
+ /* The UNC root directory */
+
+ if (_PR_IS_W_SLASH(fn[0]) && _PR_IS_W_SLASH(fn[1])) {
+ /* The 'server' part should have at least one character. */
+ p = &fn[2];
+ if (*p == L'\0' || _PR_IS_W_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the next slash */
+ do {
+ p++;
+ } while (*p != L'\0' && !_PR_IS_W_SLASH(*p));
+ if (*p == L'\0') {
+ return PR_FALSE;
+ }
+
+ /* The 'share' part should have at least one character. */
+ p++;
+ if (*p == L'\0' || _PR_IS_W_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the final slash */
+ do {
+ p++;
+ } while (*p != L'\0' && !_PR_IS_W_SLASH(*p));
+ if (_PR_IS_W_SLASH(*p) && p[1] != L'\0') {
+ return PR_FALSE;
+ }
+ if (*p == L'\0') {
+ /*
+ * GetDriveType() doesn't work correctly if the
+ * path is of the form \\server\share, so we add
+ * a final slash temporarily.
+ */
+ if ((p + 1) < (fn + buflen)) {
+ *p++ = L'\\';
+ *p = L'\0';
+ slashAdded = PR_TRUE;
+ } else {
+ return PR_FALSE; /* name too long */
+ }
+ }
+ rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE;
+ /* restore the 'fn' buffer */
+ if (slashAdded) {
+ *--p = L'\0';
+ }
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO64_UTF16(const PRUnichar *fn, PRFileInfo64 *info)
+{
+ HANDLE hFindFile;
+ WIN32_FIND_DATAW findFileData;
+ PRUnichar pathbuf[MAX_PATH + 1];
+
+ if (NULL == fn || L'\0' == *fn) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+
+ /*
+ * FindFirstFile() expands wildcard characters. So
+ * we make sure the pathname contains no wildcard.
+ */
+ if (NULL != wcspbrk(fn, L"?*")) {
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0);
+ return -1;
+ }
+
+ hFindFile = findFirstFileW(fn, &findFileData);
+ if (INVALID_HANDLE_VALUE == hFindFile) {
+ DWORD len;
+ PRUnichar *filePart;
+
+ /*
+ * FindFirstFile() does not work correctly on root directories.
+ * It also doesn't work correctly on a pathname that ends in a
+ * slash. So we first check to see if the pathname specifies a
+ * root directory. If not, and if the pathname ends in a slash,
+ * we remove the final slash and try again.
+ */
+
+ /*
+ * If the pathname does not contain ., \, and /, it cannot be
+ * a root directory or a pathname that ends in a slash.
+ */
+ if (NULL == wcspbrk(fn, L".\\/")) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ }
+ len = getFullPathNameW(fn, sizeof(pathbuf)/sizeof(pathbuf[0]), pathbuf,
+ &filePart);
+ if (0 == len) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ }
+ if (len > sizeof(pathbuf)/sizeof(pathbuf[0])) {
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
+ return -1;
+ }
+ if (IsRootDirectoryW(pathbuf, sizeof(pathbuf)/sizeof(pathbuf[0]))) {
+ info->type = PR_FILE_DIRECTORY;
+ info->size = 0;
+ /*
+ * These timestamps don't make sense for root directories.
+ */
+ info->modifyTime = 0;
+ info->creationTime = 0;
+ return 0;
+ }
+ if (!_PR_IS_W_SLASH(pathbuf[len - 1])) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ } else {
+ pathbuf[len - 1] = L'\0';
+ hFindFile = findFirstFileW(pathbuf, &findFileData);
+ if (INVALID_HANDLE_VALUE == hFindFile) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ }
+ }
+ }
+
+ FindClose(hFindFile);
+
+ if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ info->type = PR_FILE_DIRECTORY;
+ } else {
+ info->type = PR_FILE_FILE;
+ }
+
+ info->size = findFileData.nFileSizeHigh;
+ info->size = (info->size << 32) + findFileData.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
+
+ if (0 == findFileData.ftCreationTime.dwLowDateTime &&
+ 0 == findFileData.ftCreationTime.dwHighDateTime) {
+ info->creationTime = info->modifyTime;
+ } else {
+ _PR_FileTimeToPRTime(&findFileData.ftCreationTime,
+ &info->creationTime);
+ }
+
+ return 0;
+}
+/* ================ end of UTF16 Interfaces ================================ */
+#endif /* MOZ_UNICODE */
diff --git a/nsprpub/pr/src/md/windows/w95sock.c b/nsprpub/pr/src/md/windows/w95sock.c
new file mode 100644
index 0000000000..abe462fe02
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/w95sock.c
@@ -0,0 +1,851 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Win95 Sockets module
+ *
+ */
+
+#if defined(_WIN64)
+#include <winsock2.h>
+#endif
+#include "primpl.h"
+
+#define READ_FD 1
+#define WRITE_FD 2
+#define CONNECT_FD 3
+
+static PRInt32 socket_io_wait(
+ PROsfd osfd,
+ PRInt32 fd_type,
+ PRIntervalTime timeout);
+
+
+/* --- SOCKET IO --------------------------------------------------------- */
+
+static PRBool socketFixInet6RcvBuf = PR_FALSE;
+
+void _PR_MD_InitSockets(void)
+{
+ OSVERSIONINFO osvi;
+
+ memset(&osvi, 0, sizeof(osvi));
+ osvi.dwOSVersionInfoSize = sizeof(osvi);
+ GetVersionEx(&osvi);
+
+ if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
+ {
+ /* if Windows XP (32-bit) */
+ socketFixInet6RcvBuf = PR_TRUE;
+ }
+}
+
+void _PR_MD_CleanupSockets(void)
+{
+ socketFixInet6RcvBuf = PR_FALSE;
+}
+
+PROsfd
+_PR_MD_SOCKET(int af, int type, int flags)
+{
+ SOCKET sock;
+ u_long one = 1;
+
+ sock = socket(af, type, flags);
+
+ if (sock == INVALID_SOCKET )
+ {
+ _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
+ return (PROsfd)sock;
+ }
+
+ /*
+ ** Make the socket Non-Blocking
+ */
+ if (ioctlsocket( sock, FIONBIO, &one) != 0)
+ {
+ PR_SetError(PR_UNKNOWN_ERROR, WSAGetLastError());
+ closesocket(sock);
+ return -1;
+ }
+
+ if (af == AF_INET6 && socketFixInet6RcvBuf)
+ {
+ int bufsize;
+ int len = sizeof(bufsize);
+ int rv;
+
+ /* Windows XP 32-bit returns an error on getpeername() for AF_INET6
+ * sockets if the receive buffer size is greater than 65535 before
+ * the connection is initiated. The default receive buffer size may
+ * be 128000 so fix it here to always be <= 65535. See bug 513659
+ * and IBM DB2 support technote "Receive/Send IPv6 Socket Size
+ * Problem in Windows XP SP2 & SP3".
+ */
+ rv = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, &len);
+ if (rv == 0 && bufsize > 65535)
+ {
+ bufsize = 65535;
+ setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, len);
+ }
+ }
+
+ return (PROsfd)sock;
+}
+
+/*
+** _MD_CloseSocket() -- Close a socket
+**
+*/
+PRInt32
+_MD_CloseSocket(PROsfd osfd)
+{
+ PRInt32 rv;
+
+ rv = closesocket((SOCKET) osfd );
+ if (rv < 0) {
+ _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
+ }
+
+ return rv;
+}
+
+PRInt32
+_MD_SocketAvailable(PRFileDesc *fd)
+{
+ PRInt32 result;
+
+ if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
+ return -1;
+ }
+ return result;
+}
+
+PROsfd _MD_Accept(
+ PRFileDesc *fd,
+ PRNetAddr *raddr,
+ PRUint32 *rlen,
+ PRIntervalTime timeout )
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ SOCKET sock;
+ PRInt32 rv, err;
+
+ while ((sock = accept(osfd, (struct sockaddr *) raddr, rlen)) == -1)
+ {
+ err = WSAGetLastError();
+ if ((err == WSAEWOULDBLOCK) && (!fd->secret->nonblocking))
+ {
+ if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0)
+ {
+ break;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ break;
+ }
+ }
+ return(sock);
+} /* end _MD_accept() */
+
+PRInt32
+_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv;
+ int err;
+
+ if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1)
+ {
+ err = WSAGetLastError();
+ if ((!fd->secret->nonblocking) && (err == WSAEWOULDBLOCK))
+ {
+ rv = socket_io_wait(osfd, CONNECT_FD, timeout);
+ if ( rv < 0 )
+ {
+ return(-1);
+ }
+ else
+ {
+ PR_ASSERT(rv > 0);
+ /* it's connected */
+ return(0);
+ }
+ }
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 rv;
+
+ rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
+
+ if (rv == SOCKET_ERROR) {
+ _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
+{
+ PRInt32 rv;
+
+ rv = listen(fd->secret->md.osfd, backlog);
+
+ if (rv == SOCKET_ERROR) {
+ _PR_MD_MAP_DEFAULT_ERROR(WSAGetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ int osflags;
+
+ if (0 == flags) {
+ osflags = 0;
+ } else {
+ PR_ASSERT(PR_MSG_PEEK == flags);
+ osflags = MSG_PEEK;
+ }
+ while ((rv = recv( osfd, buf, amount, osflags)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ rv = socket_io_wait(osfd, READ_FD, timeout);
+ if ( rv < 0 )
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_RECV_ERROR(err);
+ break;
+ }
+ } /* end while() */
+ return(rv);
+}
+
+PRInt32
+_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < amount )
+ {
+ while ((rv = send( osfd, buf, amount, 0 )) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if ( rv < 0 )
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_SEND_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking)
+ {
+ break;
+ }
+ if (bytesSent < amount)
+ {
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if ( rv < 0 )
+ {
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+PRInt32
+_PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRInt32 bytesSent = 0;
+
+ do {
+ while ((rv = sendto( osfd, buf, amount, 0, (struct sockaddr *) addr,
+ addrlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if ( rv < 0 )
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking)
+ {
+ break;
+ }
+ if (bytesSent < amount)
+ {
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if (rv < 0)
+ {
+ return -1;
+ }
+ }
+ } while(bytesSent < amount);
+ return bytesSent;
+}
+
+#if defined(_WIN64)
+
+static PRCallOnceType _pr_has_connectex_once;
+typedef BOOL (PASCAL FAR * _pr_win_connectex_ptr)(_In_ SOCKET s, _In_reads_bytes_(namelen) const struct sockaddr FAR *name, _In_ int namelen, _In_reads_bytes_opt_(dwSendDataLength) PVOID lpSendBuffer, _In_ DWORD dwSendDataLength, _Out_ LPDWORD lpdwBytesSent, _Inout_ LPOVERLAPPED lpOverlapped);
+
+#ifndef WSAID_CONNECTEX
+#define WSAID_CONNECTEX \
+ {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}}
+#endif
+#ifndef SIO_GET_EXTENSION_FUNCTION_POINTER
+#define SIO_GET_EXTENSION_FUNCTION_POINTER 0xC8000006
+#endif
+#ifndef TCP_FASTOPEN
+#define TCP_FASTOPEN 15
+#endif
+
+#ifndef SO_UPDATE_CONNECT_CONTEXT
+#define SO_UPDATE_CONNECT_CONTEXT 0x7010
+#endif
+
+static _pr_win_connectex_ptr _pr_win_connectex = NULL;
+
+static PRStatus PR_CALLBACK _pr_set_connectex(void)
+{
+ _pr_win_connectex = NULL;
+ SOCKET sock;
+ PRInt32 dwBytes;
+ int rc;
+
+ /* Dummy socket needed for WSAIoctl */
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock == INVALID_SOCKET) {
+ return PR_SUCCESS;
+ }
+
+ GUID guid = WSAID_CONNECTEX;
+ rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
+ &guid, sizeof(guid),
+ &_pr_win_connectex, sizeof(_pr_win_connectex),
+ &dwBytes, NULL, NULL);
+ if (rc != 0) {
+ _pr_win_connectex = NULL;
+ return PR_SUCCESS;
+ }
+
+ rc = closesocket(sock);
+ return PR_SUCCESS;
+}
+
+PRInt32
+_PR_MD_TCPSENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ if (!_fd_waiting_for_overlapped_done_lock) {
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (PR_CallOnce(&_pr_has_connectex_once, _pr_set_connectex) != PR_SUCCESS) {
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (_pr_win_connectex == NULL) {
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRInt32 bytesSent = 0;
+ DWORD rvSent;
+
+ BOOL option = 1;
+ rv = setsockopt((SOCKET)osfd, IPPROTO_TCP, TCP_FASTOPEN, (char*)&option, sizeof(option));
+ if (rv != 0) {
+ err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("_PR_MD_TCPSENDTO error set opt TCP_FASTOPEN failed %d\n", err));
+ if (err == WSAENOPROTOOPT) {
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ } else {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(err);
+ }
+ return -1;
+ }
+
+ /* ConnectEx requires the socket to be initially bound. We will use INADDR_ANY. */
+ PRNetAddr bindAddr;
+ memset(&bindAddr, 0, sizeof(bindAddr));
+ bindAddr.raw.family = addr->raw.family;
+
+ rv = bind((SOCKET)osfd, (const struct sockaddr *)&(bindAddr.inet), PR_NETADDR_SIZE(&bindAddr));
+ if (rv != 0) {
+ err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("_PR_MD_TCPSENDTO error bind failed %d\n", err));
+ _PR_MD_MAP_SETSOCKOPT_ERROR(err);
+ return -1;
+ }
+
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("_PR_MD_TCPSENDTO calling _pr_win_connectex %d %p\n", amount, (char*)buf));
+
+ rvSent = 0;
+ memset(&fd->secret->ol, 0, sizeof(fd->secret->ol));
+ /* ConnectEx return TRUE on a success and FALSE on an error. */
+ if (_pr_win_connectex( (SOCKET)osfd, (struct sockaddr *) addr,
+ addrlen, buf, amount,
+ &rvSent, &fd->secret->ol) == TRUE) {
+ /* When ConnectEx is used, all previously set socket options and
+ * property are not enabled and to enable them
+ * SO_UPDATE_CONNECT_CONTEXT option need to be set. */
+ rv = setsockopt((SOCKET)osfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0);
+ if (rv != 0) {
+ err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("_PR_MD_TCPSENDTO setting SO_UPDATE_CONNECT_CONTEXT failed %d\n", err));
+ _PR_MD_MAP_SETSOCKOPT_ERROR(err);
+ return -1;
+ }
+ /* We imitate Linux here. SendTo will return number of bytes send but
+ * it can not return connection success at the same time, so we return
+ * number of bytes send and "connection success" will be return on the
+ * connectcontinue. */
+ fd->secret->alreadyConnected = PR_TRUE;
+ return rvSent;
+ } else {
+ err = WSAGetLastError();
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("_PR_MD_TCPSENDTO error _pr_win_connectex failed %d\n", err));
+ if (err != ERROR_IO_PENDING) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ return -1;
+ } else if (fd->secret->nonblocking) {
+ /* Remember that overlapped structure is set. We will need to get
+ * the final result of ConnectEx call. */
+ fd->secret->overlappedActive = PR_TRUE;
+
+ /* ConnectEx will copy supplied data to a internal buffer and send
+ * them during Fast Open or after connect. Therefore we can assumed
+ * this data already send. */
+ if (amount > 0) {
+ return amount;
+ }
+
+ _PR_MD_MAP_CONNECT_ERROR(WSAEWOULDBLOCK);
+ return -1;
+ }
+ // err is ERROR_IO_PENDING and socket is blocking, so query
+ // GetOverlappedResult.
+ err = ERROR_IO_INCOMPLETE;
+ while (err == ERROR_IO_INCOMPLETE) {
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if ( rv < 0 ) {
+ return -1;
+ }
+ rv = GetOverlappedResult((HANDLE)osfd, &fd->secret->ol, &rvSent, FALSE);
+ if ( rv == TRUE ) {
+ return rvSent;
+ } else {
+ err = WSAGetLastError();
+ if (err != ERROR_IO_INCOMPLETE) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ return -1;
+ }
+ }
+ }
+ }
+ return -1;
+}
+#endif
+
+PRInt32
+_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PROsfd osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+
+ while ((rv = recvfrom( osfd, buf, amount, 0, (struct sockaddr *) addr,
+ addrlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ rv = socket_io_wait(osfd, READ_FD, timeout);
+ if ( rv < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+}
+
+PRInt32
+_PR_MD_WRITEV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
+{
+ int index;
+ int sent = 0;
+ int rv;
+
+ for (index=0; index < iov_size; index++)
+ {
+ rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0, timeout);
+ if (rv > 0) {
+ sent += rv;
+ }
+ if ( rv != iov[index].iov_len )
+ {
+ if (rv < 0)
+ {
+ if (fd->secret->nonblocking
+ && (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ && (sent > 0))
+ {
+ return sent;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ /* Only a nonblocking socket can have partial sends */
+ PR_ASSERT(fd->secret->nonblocking);
+ return sent;
+ }
+ }
+ return sent;
+}
+
+PRInt32
+_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
+{
+ PRInt32 rv;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+ if (rv < 0) {
+ _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
+ }
+ return rv;
+}
+
+PRStatus
+_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
+ if (rv==0) {
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getpeername((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
+ if (rv==0) {
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ PRInt32 rv;
+
+ rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0) {
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ PRInt32 rv;
+
+ rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0) {
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+void
+_MD_MakeNonblock(PRFileDesc *f)
+{
+ return; /* do nothing */
+}
+
+
+
+/*
+ * socket_io_wait --
+ *
+ * Wait for socket i/o, periodically checking for interrupt.
+ *
+ * This function returns 1 on success. On failure, it returns
+ * -1 and sets the error codes. It never returns 0.
+ */
+#define _PR_INTERRUPT_CHECK_INTERVAL_SECS 5
+
+static PRInt32 socket_io_wait(
+ PROsfd osfd,
+ PRInt32 fd_type,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv = -1;
+ struct timeval tv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntervalTime elapsed, remaining;
+ PRBool wait_for_remaining;
+ fd_set rd_wr, ex;
+ int err, len;
+
+ switch (timeout) {
+ case PR_INTERVAL_NO_WAIT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ break;
+ case PR_INTERVAL_NO_TIMEOUT:
+ /*
+ * This is a special case of the 'default' case below.
+ * Please see the comments there.
+ */
+ tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
+ tv.tv_usec = 0;
+ FD_ZERO(&rd_wr);
+ FD_ZERO(&ex);
+ do {
+ FD_SET(osfd, &rd_wr);
+ FD_SET(osfd, &ex);
+ switch( fd_type )
+ {
+ case READ_FD:
+ rv = _MD_SELECT(0, &rd_wr, NULL, NULL, &tv);
+ break;
+ case WRITE_FD:
+ rv = _MD_SELECT(0, NULL, &rd_wr, NULL, &tv);
+ break;
+ case CONNECT_FD:
+ rv = _MD_SELECT(0, NULL, &rd_wr, &ex, &tv);
+ break;
+ default:
+ PR_ASSERT(0);
+ break;
+ } /* end switch() */
+ if (rv == -1 )
+ {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if ( rv > 0 && fd_type == CONNECT_FD )
+ {
+ /*
+ * Call Sleep(0) to work around a Winsock timing bug.
+ */
+ Sleep(0);
+ if (FD_ISSET((SOCKET)osfd, &ex))
+ {
+ len = sizeof(err);
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
+ (char *) &err, &len) == SOCKET_ERROR)
+ {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return -1;
+ }
+ if (err != 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+ else {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ }
+ return -1;
+ }
+ if (FD_ISSET((SOCKET)osfd, &rd_wr))
+ {
+ /* it's connected */
+ return 1;
+ }
+ PR_ASSERT(0);
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ } while (rv == 0);
+ break;
+ default:
+ remaining = timeout;
+ FD_ZERO(&rd_wr);
+ FD_ZERO(&ex);
+ do {
+ /*
+ * We block in _MD_SELECT for at most
+ * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds,
+ * so that there is an upper limit on the delay
+ * before the interrupt bit is checked.
+ */
+ wait_for_remaining = PR_TRUE;
+ tv.tv_sec = PR_IntervalToSeconds(remaining);
+ if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) {
+ wait_for_remaining = PR_FALSE;
+ tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
+ tv.tv_usec = 0;
+ } else {
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ remaining -
+ PR_SecondsToInterval(tv.tv_sec));
+ }
+ FD_SET(osfd, &rd_wr);
+ FD_SET(osfd, &ex);
+ switch( fd_type )
+ {
+ case READ_FD:
+ rv = _MD_SELECT(0, &rd_wr, NULL, NULL, &tv);
+ break;
+ case WRITE_FD:
+ rv = _MD_SELECT(0, NULL, &rd_wr, NULL, &tv);
+ break;
+ case CONNECT_FD:
+ rv = _MD_SELECT(0, NULL, &rd_wr, &ex, &tv);
+ break;
+ default:
+ PR_ASSERT(0);
+ break;
+ } /* end switch() */
+ if (rv == -1)
+ {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if ( rv > 0 && fd_type == CONNECT_FD )
+ {
+ /*
+ * Call Sleep(0) to work around a Winsock timing bug.
+ */
+ Sleep(0);
+ if (FD_ISSET((SOCKET)osfd, &ex))
+ {
+ len = sizeof(err);
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
+ (char *) &err, &len) == SOCKET_ERROR)
+ {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return -1;
+ }
+ if (err != 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+ else {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ }
+ return -1;
+ }
+ if (FD_ISSET((SOCKET)osfd, &rd_wr))
+ {
+ /* it's connected */
+ return 1;
+ }
+ PR_ASSERT(0);
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ /*
+ * We loop again if _MD_SELECT timed out and the
+ * timeout deadline has not passed yet.
+ */
+ if (rv == 0 )
+ {
+ if (wait_for_remaining) {
+ elapsed = remaining;
+ } else {
+ elapsed = PR_SecondsToInterval(tv.tv_sec)
+ + PR_MicrosecondsToInterval(tv.tv_usec);
+ }
+ if (elapsed >= remaining) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ } else {
+ remaining = remaining - elapsed;
+ }
+ }
+ } while (rv == 0 );
+ break;
+ }
+ return(rv);
+} /* end socket_io_wait() */
diff --git a/nsprpub/pr/src/md/windows/w95thred.c b/nsprpub/pr/src/md/windows/w95thred.c
new file mode 100644
index 0000000000..fb9c457d73
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/w95thred.c
@@ -0,0 +1,381 @@
+
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#include <process.h> /* for _beginthreadex() */
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+/*
+ * VC++ 6.0 doesn't have DWORD_PTR.
+ */
+
+typedef DWORD DWORD_PTR;
+#endif /* _MSC_VER <= 1200 */
+
+/* --- globals ------------------------------------------------ */
+#ifdef _PR_USE_STATIC_TLS
+__declspec(thread) struct PRThread *_pr_thread_last_run;
+__declspec(thread) struct PRThread *_pr_currentThread;
+__declspec(thread) struct _PRCPU *_pr_currentCPU;
+#else
+DWORD _pr_currentThreadIndex;
+DWORD _pr_lastThreadIndex;
+DWORD _pr_currentCPUIndex;
+#endif
+int _pr_intsOff = 0;
+_PRInterruptTable _pr_interruptTable[] = { { 0 } };
+
+typedef HRESULT (WINAPI *SETTHREADDESCRIPTION)(HANDLE, PCWSTR);
+static SETTHREADDESCRIPTION sSetThreadDescription = NULL;
+
+void
+_PR_MD_EARLY_INIT()
+{
+ HMODULE hModule;
+
+#ifndef _PR_USE_STATIC_TLS
+ _pr_currentThreadIndex = TlsAlloc();
+ _pr_lastThreadIndex = TlsAlloc();
+ _pr_currentCPUIndex = TlsAlloc();
+#endif
+
+#if defined(_WIN64) && defined(WIN95)
+ _fd_waiting_for_overlapped_done_lock = PR_NewLock();
+#endif
+
+ // SetThreadDescription is Windows 10 build 1607+
+ hModule = GetModuleHandleW(L"kernel32.dll");
+ if (hModule) {
+ sSetThreadDescription =
+ (SETTHREADDESCRIPTION) GetProcAddress(
+ hModule,
+ "SetThreadDescription");
+ }
+}
+
+void _PR_MD_CLEANUP_BEFORE_EXIT(void)
+{
+ _PR_NT_FreeSids();
+
+ _PR_MD_CleanupSockets();
+
+ WSACleanup();
+
+#ifndef _PR_USE_STATIC_TLS
+ TlsFree(_pr_currentThreadIndex);
+ TlsFree(_pr_lastThreadIndex);
+ TlsFree(_pr_currentCPUIndex);
+#endif
+
+#if defined(_WIN64) && defined(WIN95)
+ // For each iteration check if TFO overlapped IOs are down.
+ if (_fd_waiting_for_overlapped_done_lock) {
+ PRIntervalTime delay = PR_MillisecondsToInterval(1000);
+ PRFileDescList *cur;
+ do {
+ CheckOverlappedPendingSocketsAreDone();
+
+ PR_Lock(_fd_waiting_for_overlapped_done_lock);
+ cur = _fd_waiting_for_overlapped_done;
+ PR_Unlock(_fd_waiting_for_overlapped_done_lock);
+#if defined(DO_NOT_WAIT_FOR_CONNECT_OVERLAPPED_OPERATIONS)
+ cur = NULL;
+#endif
+ if (cur) {
+ PR_Sleep(delay); // wait another 1s.
+ }
+ } while (cur);
+
+ PR_DestroyLock(_fd_waiting_for_overlapped_done_lock);
+ }
+#endif
+}
+
+PRStatus
+_PR_MD_INIT_THREAD(PRThread *thread)
+{
+ if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) {
+ /*
+ ** Warning:
+ ** --------
+ ** NSPR requires a real handle to every thread.
+ ** GetCurrentThread() returns a pseudo-handle which
+ ** is not suitable for some thread operations (e.g.,
+ ** suspending). Therefore, get a real handle from
+ ** the pseudo handle via DuplicateHandle(...)
+ */
+ BOOL ok = DuplicateHandle(
+ GetCurrentProcess(), /* Process of source handle */
+ GetCurrentThread(), /* Pseudo Handle to dup */
+ GetCurrentProcess(), /* Process of handle */
+ &(thread->md.handle), /* resulting handle */
+ 0L, /* access flags */
+ FALSE, /* Inheritable */
+ DUPLICATE_SAME_ACCESS); /* Options */
+ if (!ok) {
+ return PR_FAILURE;
+ }
+ thread->id = GetCurrentThreadId();
+ thread->md.id = thread->id;
+ }
+
+ /* Create the blocking IO semaphore */
+ thread->md.blocked_sema = CreateSemaphore(NULL, 0, 1, NULL);
+ if (thread->md.blocked_sema == NULL) {
+ return PR_FAILURE;
+ }
+ else {
+ return PR_SUCCESS;
+ }
+}
+
+static unsigned __stdcall
+pr_root(void *arg)
+{
+ PRThread *thread = (PRThread *)arg;
+ thread->md.start(thread);
+ return 0;
+}
+
+PRStatus
+_PR_MD_CREATE_THREAD(PRThread *thread,
+ void (*start)(void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+
+ thread->md.start = start;
+ thread->md.handle = (HANDLE) _beginthreadex(
+ NULL,
+ thread->stack->stackSize,
+ pr_root,
+ (void *)thread,
+ CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION,
+ &(thread->id));
+ if(!thread->md.handle) {
+ return PR_FAILURE;
+ }
+
+ thread->md.id = thread->id;
+ /*
+ * On windows, a thread is created with a thread priority of
+ * THREAD_PRIORITY_NORMAL.
+ */
+ if (priority != PR_PRIORITY_NORMAL) {
+ _PR_MD_SET_PRIORITY(&(thread->md), priority);
+ }
+
+ /* Activate the thread */
+ if ( ResumeThread( thread->md.handle ) != -1) {
+ return PR_SUCCESS;
+ }
+
+ return PR_FAILURE;
+}
+
+void
+_PR_MD_YIELD(void)
+{
+ /* Can NT really yield at all? */
+ Sleep(0);
+}
+
+void
+_PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri)
+{
+ int nativePri;
+ BOOL rv;
+
+ if (newPri < PR_PRIORITY_FIRST) {
+ newPri = PR_PRIORITY_FIRST;
+ } else if (newPri > PR_PRIORITY_LAST) {
+ newPri = PR_PRIORITY_LAST;
+ }
+ switch (newPri) {
+ case PR_PRIORITY_LOW:
+ nativePri = THREAD_PRIORITY_BELOW_NORMAL;
+ break;
+ case PR_PRIORITY_NORMAL:
+ nativePri = THREAD_PRIORITY_NORMAL;
+ break;
+ case PR_PRIORITY_HIGH:
+ nativePri = THREAD_PRIORITY_ABOVE_NORMAL;
+ break;
+ case PR_PRIORITY_URGENT:
+ nativePri = THREAD_PRIORITY_HIGHEST;
+ }
+ rv = SetThreadPriority(thread->handle, nativePri);
+ PR_ASSERT(rv);
+ if (!rv) {
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("PR_SetThreadPriority: can't set thread priority\n"));
+ }
+ return;
+}
+
+const DWORD MS_VC_EXCEPTION = 0x406D1388;
+
+#pragma pack(push,8)
+typedef struct tagTHREADNAME_INFO
+{
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+} THREADNAME_INFO;
+#pragma pack(pop)
+
+void
+_PR_MD_SET_CURRENT_THREAD_NAME(const char *name)
+{
+#ifdef _MSC_VER
+ THREADNAME_INFO info;
+#endif
+
+ if (sSetThreadDescription) {
+ WCHAR wideName[MAX_PATH];
+ if (MultiByteToWideChar(CP_ACP, 0, name, -1, wideName, MAX_PATH)) {
+ sSetThreadDescription(GetCurrentThread(), wideName);
+ }
+ }
+
+#ifdef _MSC_VER
+ if (!IsDebuggerPresent()) {
+ return;
+ }
+
+ info.dwType = 0x1000;
+ info.szName = (char*) name;
+ info.dwThreadID = -1;
+ info.dwFlags = 0;
+
+ __try {
+ RaiseException(MS_VC_EXCEPTION,
+ 0,
+ sizeof(info) / sizeof(ULONG_PTR),
+ (ULONG_PTR*)&info);
+ } __except(EXCEPTION_CONTINUE_EXECUTION) {
+ }
+#endif
+}
+
+void
+_PR_MD_CLEAN_THREAD(PRThread *thread)
+{
+ BOOL rv;
+
+ if (thread->md.blocked_sema) {
+ rv = CloseHandle(thread->md.blocked_sema);
+ PR_ASSERT(rv);
+ thread->md.blocked_sema = 0;
+ }
+
+ if (thread->md.handle) {
+ rv = CloseHandle(thread->md.handle);
+ PR_ASSERT(rv);
+ thread->md.handle = 0;
+ }
+}
+
+void
+_PR_MD_EXIT_THREAD(PRThread *thread)
+{
+ _PR_MD_CLEAN_THREAD(thread);
+ _PR_MD_SET_CURRENT_THREAD(NULL);
+}
+
+
+void
+_PR_MD_EXIT(PRIntn status)
+{
+ _exit(status);
+}
+
+PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask )
+{
+#ifdef WINCE
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return -1;
+#else
+ DWORD_PTR rv;
+
+ rv = SetThreadAffinityMask(thread->md.handle, mask);
+
+ return rv?0:-1;
+#endif
+}
+
+PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask)
+{
+#ifdef WINCE
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return -1;
+#else
+ BOOL rv;
+ DWORD_PTR process_mask;
+ DWORD_PTR system_mask;
+
+ rv = GetProcessAffinityMask(GetCurrentProcess(),
+ &process_mask, &system_mask);
+ if (rv) {
+ *mask = (PRUint32)process_mask;
+ }
+
+ return rv?0:-1;
+#endif
+}
+
+void
+_PR_MD_SUSPEND_CPU(_PRCPU *cpu)
+{
+ _PR_MD_SUSPEND_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_RESUME_CPU(_PRCPU *cpu)
+{
+ _PR_MD_RESUME_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_SUSPEND_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ DWORD previousSuspendCount;
+ /* XXXMB - SuspendThread() is not a blocking call; how do we
+ * know when the thread is *REALLY* suspended?
+ */
+ previousSuspendCount = SuspendThread(thread->md.handle);
+ PR_ASSERT(previousSuspendCount == 0);
+ }
+}
+
+void
+_PR_MD_RESUME_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ DWORD previousSuspendCount;
+ previousSuspendCount = ResumeThread(thread->md.handle);
+ PR_ASSERT(previousSuspendCount == 1);
+ }
+}
+
+PRThread*
+_MD_CURRENT_THREAD(void)
+{
+ PRThread *thread;
+
+ thread = _MD_GET_ATTACHED_THREAD();
+
+ if (NULL == thread) {
+ thread = _PRI_AttachThread(
+ PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0);
+ }
+ PR_ASSERT(thread != NULL);
+ return thread;
+}
diff --git a/nsprpub/pr/src/md/windows/win32_errors.c b/nsprpub/pr/src/md/windows/win32_errors.c
new file mode 100644
index 0000000000..b0118146b6
--- /dev/null
+++ b/nsprpub/pr/src/md/windows/win32_errors.c
@@ -0,0 +1,541 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prerror.h"
+#include "prlog.h"
+#include <errno.h>
+#include <windows.h>
+
+/*
+ * On Win32, we map three kinds of error codes:
+ * - GetLastError(): for Win32 functions
+ * - WSAGetLastError(): for Winsock functions
+ * - errno: for standard C library functions
+ *
+ * GetLastError() and WSAGetLastError() return error codes in
+ * non-overlapping ranges, so their error codes (ERROR_* and
+ * WSAE*) can be mapped by the same function. On the other hand,
+ * errno and GetLastError() have overlapping ranges, so we need
+ * to use a separate function to map errno.
+ *
+ * We do not check for WSAEINPROGRESS and WSAEINTR because we do not
+ * use blocking Winsock 1.1 calls.
+ *
+ * Except for the 'socket' call, we do not check for WSAEINITIALISED.
+ * It is assumed that if Winsock is not initialized, that fact will
+ * be detected at the time we create new sockets.
+ */
+
+static void _MD_win32_map_default_errno(PRInt32 err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case EACCES:
+ prError = PR_NO_ACCESS_RIGHTS_ERROR;
+ break;
+ case ENOENT:
+ prError = PR_FILE_NOT_FOUND_ERROR;
+ break;
+ default:
+ prError = PR_UNKNOWN_ERROR;
+ break;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_win32_map_default_error(PRInt32 err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ prError = PR_NO_ACCESS_RIGHTS_ERROR;
+ break;
+ case ERROR_ALREADY_EXISTS:
+ prError = PR_FILE_EXISTS_ERROR;
+ break;
+ case ERROR_CALL_NOT_IMPLEMENTED:
+ prError = PR_NOT_IMPLEMENTED_ERROR;
+ break;
+ case ERROR_DISK_CORRUPT:
+ prError = PR_IO_ERROR;
+ break;
+ case ERROR_DISK_FULL:
+ prError = PR_NO_DEVICE_SPACE_ERROR;
+ break;
+ case ERROR_DISK_OPERATION_FAILED:
+ prError = PR_IO_ERROR;
+ break;
+ case ERROR_DRIVE_LOCKED:
+ prError = PR_FILE_IS_LOCKED_ERROR;
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ prError = PR_NAME_TOO_LONG_ERROR;
+ break;
+ case ERROR_FILE_CORRUPT:
+ prError = PR_IO_ERROR;
+ break;
+ case ERROR_FILE_EXISTS:
+ prError = PR_FILE_EXISTS_ERROR;
+ break;
+ case ERROR_FILE_INVALID:
+ prError = PR_BAD_DESCRIPTOR_ERROR;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ prError = PR_FILE_NOT_FOUND_ERROR;
+ break;
+ case ERROR_HANDLE_DISK_FULL:
+ prError = PR_NO_DEVICE_SPACE_ERROR;
+ break;
+ case ERROR_INVALID_ADDRESS:
+ prError = PR_ACCESS_FAULT_ERROR;
+ break;
+ case ERROR_INVALID_HANDLE:
+ prError = PR_BAD_DESCRIPTOR_ERROR;
+ break;
+ case ERROR_INVALID_NAME:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case ERROR_INVALID_PARAMETER:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case ERROR_INVALID_USER_BUFFER:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ case ERROR_LOCKED:
+ prError = PR_FILE_IS_LOCKED_ERROR;
+ break;
+ case ERROR_NETNAME_DELETED:
+ prError = PR_CONNECT_RESET_ERROR;
+ break;
+ case ERROR_NOACCESS:
+ prError = PR_ACCESS_FAULT_ERROR;
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ case ERROR_NOT_ENOUGH_QUOTA:
+ prError = PR_OUT_OF_MEMORY_ERROR;
+ break;
+ case ERROR_NOT_READY:
+ prError = PR_IO_ERROR;
+ break;
+ case ERROR_NO_MORE_FILES:
+ prError = PR_NO_MORE_FILES_ERROR;
+ break;
+ case ERROR_OPEN_FAILED:
+ prError = PR_IO_ERROR;
+ break;
+ case ERROR_OPEN_FILES:
+ prError = PR_IO_ERROR;
+ break;
+ case ERROR_OPERATION_ABORTED:
+ prError = PR_OPERATION_ABORTED_ERROR;
+ break;
+ case ERROR_OUTOFMEMORY:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ case ERROR_PATH_BUSY:
+ prError = PR_IO_ERROR;
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ prError = PR_FILE_NOT_FOUND_ERROR;
+ break;
+ case ERROR_SEEK_ON_DEVICE:
+ prError = PR_IO_ERROR;
+ break;
+ case ERROR_SHARING_VIOLATION:
+ prError = PR_FILE_IS_BUSY_ERROR;
+ break;
+ case ERROR_STACK_OVERFLOW:
+ prError = PR_ACCESS_FAULT_ERROR;
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ prError = PR_SYS_DESC_TABLE_FULL_ERROR;
+ break;
+ case ERROR_WRITE_PROTECT:
+ prError = PR_NO_ACCESS_RIGHTS_ERROR;
+ break;
+ case WSAEACCES:
+ prError = PR_NO_ACCESS_RIGHTS_ERROR;
+ break;
+ case WSAEADDRINUSE:
+ prError = PR_ADDRESS_IN_USE_ERROR;
+ break;
+ case WSAEADDRNOTAVAIL:
+ case ERROR_INVALID_NETNAME:
+ prError = PR_ADDRESS_NOT_AVAILABLE_ERROR;
+ break;
+ case WSAEAFNOSUPPORT:
+ case ERROR_INCORRECT_ADDRESS:
+ prError = PR_ADDRESS_NOT_SUPPORTED_ERROR;
+ break;
+ case WSAEALREADY:
+ case ERROR_ALREADY_INITIALIZED:
+ prError = PR_ALREADY_INITIATED_ERROR;
+ break;
+ case WSAEBADF:
+ prError = PR_BAD_DESCRIPTOR_ERROR;
+ break;
+ case WSAECONNABORTED:
+ case ERROR_CONNECTION_ABORTED:
+ prError = PR_CONNECT_ABORTED_ERROR;
+ break;
+ case WSAECONNREFUSED:
+ case ERROR_CONNECTION_REFUSED:
+ prError = PR_CONNECT_REFUSED_ERROR;
+ break;
+ case WSAECONNRESET:
+ prError = PR_CONNECT_RESET_ERROR;
+ break;
+ case WSAEDESTADDRREQ:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case WSAEFAULT:
+ prError = PR_ACCESS_FAULT_ERROR;
+ break;
+ case WSAEHOSTUNREACH:
+ case ERROR_HOST_UNREACHABLE:
+ prError = PR_HOST_UNREACHABLE_ERROR;
+ break;
+ case WSAEINVAL:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case WSAEISCONN:
+ prError = PR_IS_CONNECTED_ERROR;
+ break;
+ case WSAEMFILE:
+ prError = PR_PROC_DESC_TABLE_FULL_ERROR;
+ break;
+ case WSAEMSGSIZE:
+ prError = PR_BUFFER_OVERFLOW_ERROR;
+ break;
+ case WSAENETDOWN:
+ case ERROR_NO_NETWORK:
+ prError = PR_NETWORK_DOWN_ERROR;
+ break;
+ case WSAENETRESET:
+ prError = PR_CONNECT_ABORTED_ERROR;
+ break;
+ case WSAENETUNREACH:
+ case ERROR_NETWORK_UNREACHABLE:
+ prError = PR_NETWORK_UNREACHABLE_ERROR;
+ break;
+ case WSAENOBUFS:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ case WSAENOPROTOOPT:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case WSAENOTCONN:
+ prError = PR_NOT_CONNECTED_ERROR;
+ break;
+ case WSAENOTSOCK:
+ prError = PR_NOT_SOCKET_ERROR;
+ break;
+ case WSAEOPNOTSUPP:
+ prError = PR_OPERATION_NOT_SUPPORTED_ERROR;
+ break;
+ case WSAEPROTONOSUPPORT:
+ prError = PR_PROTOCOL_NOT_SUPPORTED_ERROR;
+ break;
+ case WSAEPROTOTYPE:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case WSAESHUTDOWN:
+ prError = PR_SOCKET_SHUTDOWN_ERROR;
+ break;
+ case WSAESOCKTNOSUPPORT:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case WSAETIMEDOUT:
+ prError = PR_CONNECT_ABORTED_ERROR;
+ break;
+ case WSAEWOULDBLOCK:
+ prError = PR_WOULD_BLOCK_ERROR;
+ break;
+ default:
+ prError = PR_UNKNOWN_ERROR;
+ break;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_win32_map_opendir_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_closedir_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_unix_readdir_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_delete_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+/* The error code for stat() is in errno. */
+void _MD_win32_map_stat_error(PRInt32 err)
+{
+ _MD_win32_map_default_errno(err);
+}
+
+void _MD_win32_map_fstat_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_rename_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+/* The error code for access() is in errno. */
+void _MD_win32_map_access_error(PRInt32 err)
+{
+ _MD_win32_map_default_errno(err);
+}
+
+void _MD_win32_map_mkdir_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_rmdir_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_read_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_transmitfile_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_write_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_lseek_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_fsync_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+/*
+ * For both CloseHandle() and closesocket().
+ */
+void _MD_win32_map_close_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_socket_error(PRInt32 err)
+{
+ PR_ASSERT(err != WSANOTINITIALISED);
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_recv_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_recvfrom_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_send_error(PRInt32 err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case WSAEMSGSIZE:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ default:
+ _MD_win32_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_win32_map_sendto_error(PRInt32 err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case WSAEMSGSIZE:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ default:
+ _MD_win32_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_win32_map_accept_error(PRInt32 err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case WSAEOPNOTSUPP:
+ prError = PR_NOT_TCP_SOCKET_ERROR;
+ break;
+ case WSAEINVAL:
+ prError = PR_INVALID_STATE_ERROR;
+ break;
+ default:
+ _MD_win32_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_win32_map_acceptex_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_connect_error(PRInt32 err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ prError = PR_IN_PROGRESS_ERROR;
+ break;
+ case WSAEINVAL:
+ prError = PR_ALREADY_INITIATED_ERROR;
+ break;
+ case WSAETIMEDOUT:
+ prError = PR_IO_TIMEOUT_ERROR;
+ break;
+ default:
+ _MD_win32_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_win32_map_bind_error(PRInt32 err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case WSAEINVAL:
+ prError = PR_SOCKET_ADDRESS_IS_BOUND_ERROR;
+ break;
+ default:
+ _MD_win32_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_win32_map_listen_error(PRInt32 err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case WSAEOPNOTSUPP:
+ prError = PR_NOT_TCP_SOCKET_ERROR;
+ break;
+ case WSAEINVAL:
+ prError = PR_INVALID_STATE_ERROR;
+ break;
+ default:
+ _MD_win32_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_win32_map_shutdown_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_getsockname_error(PRInt32 err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case WSAEINVAL:
+ prError = PR_INVALID_STATE_ERROR;
+ break;
+ default:
+ _MD_win32_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_win32_map_getpeername_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_getsockopt_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_setsockopt_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_open_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+void _MD_win32_map_gethostname_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
+
+/* Win32 select() only works on sockets. So in this
+** context, WSAENOTSOCK is equivalent to EBADF on Unix.
+*/
+void _MD_win32_map_select_error(PRInt32 err)
+{
+ PRErrorCode prError;
+
+ switch (err) {
+ case WSAENOTSOCK:
+ prError = PR_BAD_DESCRIPTOR_ERROR;
+ break;
+ default:
+ _MD_win32_map_default_error(err);
+ return;
+ }
+ PR_SetError(prError, err);
+}
+
+void _MD_win32_map_lockf_error(PRInt32 err)
+{
+ _MD_win32_map_default_error(err);
+}
diff --git a/nsprpub/pr/src/memory/Makefile.in b/nsprpub/pr/src/memory/Makefile.in
new file mode 100644
index 0000000000..3272cadb4d
--- /dev/null
+++ b/nsprpub/pr/src/memory/Makefile.in
@@ -0,0 +1,29 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+CSRCS = prseg.c prshm.c prshma.c
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
+
diff --git a/nsprpub/pr/src/memory/prseg.c b/nsprpub/pr/src/memory/prseg.c
new file mode 100644
index 0000000000..0b70f2d4ea
--- /dev/null
+++ b/nsprpub/pr/src/memory/prseg.c
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#if defined(_PR_PTHREADS)
+
+/*
+** The pthreads version doesn't use these functions.
+*/
+void _PR_InitSegs(void)
+{
+}
+
+#else /* _PR_PTHREADS */
+
+void _PR_InitSegs(void)
+{
+ _PR_MD_INIT_SEGS();
+}
+
+/*
+** Allocate a memory segment. The size value is rounded up to the native
+** system page size and a page aligned portion of memory is returned.
+** This memory is not part of the malloc heap. If "vaddr" is not NULL
+** then PR tries to allocate the segment at the desired virtual address.
+*/
+PRSegment* _PR_NewSegment(PRUint32 size, void *vaddr)
+{
+ PRSegment *seg;
+
+ /* calloc the data structure for the segment */
+ seg = PR_NEWZAP(PRSegment);
+
+ if (seg) {
+ size = ((size + _pr_pageSize - 1) >> _pr_pageShift) << _pr_pageShift;
+ /*
+ ** Now, allocate the actual segment memory (or map under some OS)
+ ** The OS specific code decides from where or how to allocate memory.
+ */
+ if (_PR_MD_ALLOC_SEGMENT(seg, size, vaddr) != PR_SUCCESS) {
+ PR_DELETE(seg);
+ return NULL;
+ }
+ }
+
+ return seg;
+}
+
+/*
+** Free a memory segment.
+*/
+void _PR_DestroySegment(PRSegment *seg)
+{
+ _PR_MD_FREE_SEGMENT(seg);
+ PR_DELETE(seg);
+}
+
+#endif /* _PR_PTHREADS */
diff --git a/nsprpub/pr/src/memory/prshm.c b/nsprpub/pr/src/memory/prshm.c
new file mode 100644
index 0000000000..7a587cd5d1
--- /dev/null
+++ b/nsprpub/pr/src/memory/prshm.c
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** prshm.c -- NSPR Named Shared Memory
+**
+** lth. Jul-1999.
+*/
+#include <string.h>
+#include "primpl.h"
+
+extern PRLogModuleInfo *_pr_shm_lm;
+
+
+#if defined PR_HAVE_SYSV_NAMED_SHARED_MEMORY
+/* SysV implementation is in pr/src/md/unix/uxshm.c */
+#elif defined PR_HAVE_POSIX_NAMED_SHARED_MEMORY
+/* Posix implementation is in pr/src/md/unix/uxshm.c */
+#elif defined PR_HAVE_WIN32_NAMED_SHARED_MEMORY
+/* Win32 implementation is in pr/src/md/windows/w32shm.c */
+#else
+/*
+** there is no named_shared_memory
+*/
+extern PRSharedMemory* _MD_OpenSharedMemory( const char *name, PRSize size, PRIntn flags, PRIntn mode )
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+extern void * _MD_AttachSharedMemory( PRSharedMemory *shm, PRIntn flags )
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+extern PRStatus _MD_DetachSharedMemory( PRSharedMemory *shm, void *addr )
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+extern PRStatus _MD_CloseSharedMemory( PRSharedMemory *shm )
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+extern PRStatus _MD_DeleteSharedMemory( const char *name )
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+#endif /* HAVE_SYSV_NAMED_SHARED_MEMORY */
+
+/*
+** FUNCTION: PR_OpenSharedMemory()
+**
+*/
+PR_IMPLEMENT( PRSharedMemory * )
+PR_OpenSharedMemory(
+ const char *name,
+ PRSize size,
+ PRIntn flags,
+ PRIntn mode
+)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ return( _PR_MD_OPEN_SHARED_MEMORY( name, size, flags, mode ));
+} /* end PR_OpenSharedMemory() */
+
+/*
+** FUNCTION: PR_AttachSharedMemory()
+**
+*/
+PR_IMPLEMENT( void * )
+PR_AttachSharedMemory(
+ PRSharedMemory *shm,
+ PRIntn flags
+)
+{
+ return( _PR_MD_ATTACH_SHARED_MEMORY( shm, flags ));
+} /* end PR_AttachSharedMemory() */
+
+/*
+** FUNCTION: PR_DetachSharedMemory()
+**
+*/
+PR_IMPLEMENT( PRStatus )
+PR_DetachSharedMemory(
+ PRSharedMemory *shm,
+ void *addr
+)
+{
+ return( _PR_MD_DETACH_SHARED_MEMORY( shm, addr ));
+} /* end PR_DetachSharedMemory() */
+
+/*
+** FUNCTION: PR_CloseSharedMemory()
+**
+*/
+PR_IMPLEMENT( PRStatus )
+PR_CloseSharedMemory(
+ PRSharedMemory *shm
+)
+{
+ return( _PR_MD_CLOSE_SHARED_MEMORY( shm ));
+} /* end PR_CloseSharedMemory() */
+
+/*
+** FUNCTION: PR_DeleteSharedMemory()
+**
+*/
+PR_EXTERN( PRStatus )
+PR_DeleteSharedMemory(
+ const char *name
+)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ return(_PR_MD_DELETE_SHARED_MEMORY( name ));
+} /* end PR_DestroySharedMemory() */
+/* end prshm.c */
diff --git a/nsprpub/pr/src/memory/prshma.c b/nsprpub/pr/src/memory/prshma.c
new file mode 100644
index 0000000000..830b6df51d
--- /dev/null
+++ b/nsprpub/pr/src/memory/prshma.c
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** prshma.h -- NSPR Anonymous Shared Memory
+**
+**
+*/
+
+#include "primpl.h"
+
+extern PRLogModuleInfo *_pr_shma_lm;
+
+#if defined(XP_UNIX)
+/* defined in pr/src/md/unix/uxshm.c */
+#elif defined(WIN32)
+/* defined in pr/src/md/windows/w32shm.c */
+#else
+extern PRFileMap * _PR_MD_OPEN_ANON_FILE_MAP( const char *dirName, PRSize size, PRFileMapProtect prot )
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+extern PRStatus _PR_MD_EXPORT_FILE_MAP_AS_STRING(PRFileMap *fm, PRSize bufSize, char *buf)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+extern PRFileMap * _PR_MD_IMPORT_FILE_MAP_FROM_STRING(const char *fmstring)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+#endif
+
+/*
+** PR_OpenAnonFileMap() -- Creates an anonymous file-mapped shared memory
+**
+*/
+PR_IMPLEMENT(PRFileMap*)
+PR_OpenAnonFileMap(
+ const char *dirName,
+ PRSize size,
+ PRFileMapProtect prot
+)
+{
+ return(_PR_MD_OPEN_ANON_FILE_MAP( dirName, size, prot ));
+} /* end PR_OpenAnonFileMap() */
+
+/*
+** PR_ProcessAttrSetInheritableFileMap() -- Prepare FileMap for export
+** to my children processes via PR_CreateProcess()
+**
+**
+*/
+PR_IMPLEMENT( PRStatus)
+PR_ProcessAttrSetInheritableFileMap(
+ PRProcessAttr *attr,
+ PRFileMap *fm,
+ const char *shmname
+)
+{
+ PR_SetError( PR_NOT_IMPLEMENTED_ERROR, 0 );
+ return( PR_FAILURE);
+} /* end PR_ProcessAttrSetInheritableFileMap() */
+
+/*
+** PR_GetInheritedFileMap() -- Import a PRFileMap previously exported
+** by my parent process via PR_CreateProcess()
+**
+*/
+PR_IMPLEMENT( PRFileMap *)
+PR_GetInheritedFileMap(
+ const char *shmname
+)
+{
+ PRFileMap *fm = NULL;
+ PR_SetError( PR_NOT_IMPLEMENTED_ERROR, 0 );
+ return( fm );
+} /* end PR_GetInhteritedFileMap() */
+
+/*
+** PR_ExportFileMapAsString() -- Creates a string identifying a PRFileMap
+**
+*/
+PR_IMPLEMENT( PRStatus )
+PR_ExportFileMapAsString(
+ PRFileMap *fm,
+ PRSize bufSize,
+ char *buf
+)
+{
+ return( _PR_MD_EXPORT_FILE_MAP_AS_STRING( fm, bufSize, buf ));
+} /* end PR_ExportFileMapAsString() */
+
+/*
+** PR_ImportFileMapFromString() -- Creates a PRFileMap from the identifying string
+**
+**
+*/
+PR_IMPLEMENT( PRFileMap * )
+PR_ImportFileMapFromString(
+ const char *fmstring
+)
+{
+ return( _PR_MD_IMPORT_FILE_MAP_FROM_STRING(fmstring));
+} /* end PR_ImportFileMapFromString() */
+/* end prshma.c */
diff --git a/nsprpub/pr/src/misc/Makefile.in b/nsprpub/pr/src/misc/Makefile.in
new file mode 100644
index 0000000000..3d87da2091
--- /dev/null
+++ b/nsprpub/pr/src/misc/Makefile.in
@@ -0,0 +1,80 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+CSRCS = \
+ pralarm.c \
+ pratom.c \
+ prcountr.c \
+ prdtoa.c \
+ prenv.c \
+ prerr.c \
+ prerror.c \
+ prerrortable.c \
+ prinit.c \
+ prinrval.c \
+ pripc.c \
+ prlog2.c \
+ prlong.c \
+ prnetdb.c \
+ praton.c \
+ prolock.c \
+ prrng.c \
+ prsystem.c \
+ prtime.c \
+ prthinfo.c \
+ prtpool.c \
+ prtrace.c \
+ $(NULL)
+
+ifndef USE_PTHREADS
+CSRCS += \
+ pripcsem.c \
+ $(NULL)
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+RELEASE_BINS = $(srcdir)/compile-et.pl $(srcdir)/prerr.properties
+
+include $(topsrcdir)/config/rules.mk
+
+# Prevent floating point errors caused by MSVC 6.0 Processor Pack
+# optimizations (bug 207421). This disables optimizations that
+# could change the precision of floating-point calculations for
+# this single compilation unit.
+ifeq ($(NS_USE_GCC)_$(OS_ARCH),_WINNT)
+$(OBJDIR)/prdtoa.$(OBJ_SUFFIX): prdtoa.c
+ @$(MAKE_OBJDIR)
+ifeq (,$(filter-out 1100 1200 1300 1310,$(MSC_VER)))
+ $(CC) -Fo$@ -c $(CFLAGS) -Op $(call pr_abspath,$<)
+else
+ $(CC) -Fo$@ -c $(CFLAGS) -fp:precise $(call pr_abspath,$<)
+endif
+endif
+
+#
+# Generate prerr.h, prerr.c, and prerr.properties from prerr.et.
+#
+build_prerr:
+ cd $(srcdir); $(PERL) compile-et.pl prerr.et
+
+export:: $(TARGETS)
+
+
diff --git a/nsprpub/pr/src/misc/compile-et.pl b/nsprpub/pr/src/misc/compile-et.pl
new file mode 100644
index 0000000000..50855298a4
--- /dev/null
+++ b/nsprpub/pr/src/misc/compile-et.pl
@@ -0,0 +1,108 @@
+#!/usr/bin/perl
+
+# usage: compile-et input.et
+
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+sub header
+{
+ local($filename, $comment) = @_;
+
+<<EOF
+$comment
+$comment $filename
+$comment This file is automatically generated; please do not edit it.
+EOF
+}
+
+sub table_base
+{
+ local($name) = @_;
+ local($base) = 0;
+
+ for ($i = 0; $i < length($name); $i++) {
+ $base *= 64;
+ $base += index("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_", substr($name, $i, 1)) + 1;
+ }
+ $base -= 0x1000000 if ($base > 0x7fffff);
+ $base*256;
+}
+
+sub code {
+ local($macro, $text) = @_;
+ $code = $table_base + $table_item_count;
+
+ print H "\n";
+ print H "/* ", $text, " */\n";
+ printf H "#define %-40s (%dL)\n", $macro, $code;
+
+ print C "\t{\"", $macro, "\", \"", $text, "\"},\n";
+
+ print PROPERTIES $macro, "=", $text, "\n";
+
+ $table_item_count++;
+}
+
+
+$filename = $ARGV[0];
+open(INPUT, "< $filename") || die "Can't read $filename: $!\n";
+
+$base = "$filename";
+$base =~ s/\.et$//;
+$base =~ s#.*/##;
+
+open(H, "> ${base}.h") || die "Can't write ${base}.h\n";
+open(C, "> ${base}.c") || die "Can't write ${base}.c\n";
+open(PROPERTIES, "> ${base}.properties") || die "Can't write ${base}.properties\n";
+
+print H "/*\n", &header("${base}.h", " *"), " */\n";
+print C "/*\n", &header("${base}.c", " *"), " */\n";
+print PROPERTIES &header("${base}.properties", "#");
+
+$skipone = 0;
+
+while ($_ = <INPUT>) {
+ next if /^#/;
+
+ if (/^[ \t]*(error_table|et)[ \t]+([a-zA-Z][a-zA-Z0-9_]+) *(-?[0-9]*)/) {
+ $table_name = $2;
+ if ($3) {
+ $table_base = $3;
+ }
+ else {
+ $table_base = &table_base($table_name);
+ }
+ $table_item_count = 0;
+
+ print C "#include \"prerror.h\"\n";
+ print C "static const struct PRErrorMessage text[] = {\n";
+ }
+ elsif (/^[ \t]*(error_code|ec)[ \t]+([A-Z_0-9]+),[ \t]*$/) {
+ $skipone = 1;
+ $macro = $2;
+ }
+ elsif (/^[ \t]*(error_code|ec)[ \t]+([A-Z_0-9]+),[ \t]*"(.*)"[ \t]*$/) {
+ &code($2, $3);
+ }
+ elsif ($skipone && /^[ \t]*"(.*)"[ \t]*$/) {
+ &code($macro, $1);
+ }
+}
+
+print H "\n";
+print H "extern void ", $table_name, "_InitializePRErrorTable","(void);\n";
+printf H "#define ERROR_TABLE_BASE_%s (%dL)\n", $table_name, $table_base;
+
+print C "\t{0, 0}\n";
+print C "};\n\n";
+printf C "static const struct PRErrorTable et = { text, \"%s\", %dL, %d };\n",
+ $base, $table_base, $table_item_count;
+print C "\n";
+print C "void ", $table_name, "_InitializePRErrorTable", "(void) {\n";
+print C " PR_ErrorInstallTable(&et);\n";
+print C "}\n";
+
+0;
diff --git a/nsprpub/pr/src/misc/dtoa.c b/nsprpub/pr/src/misc/dtoa.c
new file mode 100644
index 0000000000..43883aba26
--- /dev/null
+++ b/nsprpub/pr/src/misc/dtoa.c
@@ -0,0 +1,4610 @@
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ ***************************************************************/
+
+/* Please send bug reports to David M. Gay (dmg at acm dot org,
+ * with " at " changed at "@" and " dot " changed to "."). */
+
+/* On a machine with IEEE extended-precision registers, it is
+ * necessary to specify double-precision (53-bit) rounding precision
+ * before invoking strtod or dtoa. If the machine uses (the equivalent
+ * of) Intel 80x87 arithmetic, the call
+ * _control87(PC_53, MCW_PC);
+ * does this with many compilers. Whether this or another call is
+ * appropriate depends on the compiler; for this to work, it may be
+ * necessary to #include "float.h" or another system-dependent header
+ * file.
+ */
+
+/* strtod for IEEE-, VAX-, and IBM-arithmetic machines.
+ *
+ * This strtod returns a nearest machine number to the input decimal
+ * string (or sets errno to ERANGE). With IEEE arithmetic, ties are
+ * broken by the IEEE round-even rule. Otherwise ties are broken by
+ * biased rounding (add half and chop).
+ *
+ * Inspired loosely by William D. Clinger's paper "How to Read Floating
+ * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ *
+ * 1. We only require IEEE, IBM, or VAX double-precision
+ * arithmetic (not IEEE double-extended).
+ * 2. We get by with floating-point arithmetic in a case that
+ * Clinger missed -- when we're computing d * 10^n
+ * for a small integer d and the integer n is not too
+ * much larger than 22 (the maximum integer k for which
+ * we can represent 10^k exactly), we may be able to
+ * compute (d*10^k) * 10^(e-k) with just one roundoff.
+ * 3. Rather than a bit-at-a-time adjustment of the binary
+ * result in the hard case, we use floating-point
+ * arithmetic to determine the adjustment to within
+ * one bit; only in really hard cases do we need to
+ * compute a second residual.
+ * 4. Because of 3., we don't need a large table of powers of 10
+ * for ten-to-e (just some small tables, e.g. of 10^k
+ * for 0 <= k <= 22).
+ */
+
+/*
+ * #define IEEE_8087 for IEEE-arithmetic machines where the least
+ * significant byte has the lowest address.
+ * #define IEEE_MC68k for IEEE-arithmetic machines where the most
+ * significant byte has the lowest address.
+ * #define Long int on machines with 32-bit ints and 64-bit longs.
+ * #define IBM for IBM mainframe-style floating-point arithmetic.
+ * #define VAX for VAX-style floating-point arithmetic (D_floating).
+ * #define No_leftright to omit left-right logic in fast floating-point
+ * computation of dtoa. This will cause dtoa modes 4 and 5 to be
+ * treated the same as modes 2 and 3 for some inputs.
+ * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ * and strtod and dtoa should round accordingly. Unless Trust_FLT_ROUNDS
+ * is also #defined, fegetround() will be queried for the rounding mode.
+ * Note that both FLT_ROUNDS and fegetround() are specified by the C99
+ * standard (and are specified to be consistent, with fesetround()
+ * affecting the value of FLT_ROUNDS), but that some (Linux) systems
+ * do not work correctly in this regard, so using fegetround() is more
+ * portable than using FLT_ROUNDS directly.
+ * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ * and Honor_FLT_ROUNDS is not #defined.
+ * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines
+ * that use extended-precision instructions to compute rounded
+ * products and quotients) with IBM.
+ * #define ROUND_BIASED for IEEE-format with biased rounding and arithmetic
+ * that rounds toward +Infinity.
+ * #define ROUND_BIASED_without_Round_Up for IEEE-format with biased
+ * rounding when the underlying floating-point arithmetic uses
+ * unbiased rounding. This prevent using ordinary floating-point
+ * arithmetic when the result could be computed with one rounding error.
+ * #define Inaccurate_Divide for IEEE-format with correctly rounded
+ * products but inaccurate quotients, e.g., for Intel i860.
+ * #define NO_LONG_LONG on machines that do not have a "long long"
+ * integer type (of >= 64 bits). On such machines, you can
+ * #define Just_16 to store 16 bits per 32-bit Long when doing
+ * high-precision integer arithmetic. Whether this speeds things
+ * up or slows things down depends on the machine and the number
+ * being converted. If long long is available and the name is
+ * something other than "long long", #define Llong to be the name,
+ * and if "unsigned Llong" does not work as an unsigned version of
+ * Llong, #define #ULLong to be the corresponding unsigned type.
+ * #define KR_headers for old-style C function headers.
+ * #define Bad_float_h if your system lacks a float.h or if it does not
+ * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP,
+ * FLT_RADIX, FLT_ROUNDS, and DBL_MAX.
+ * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n)
+ * if memory is available and otherwise does something you deem
+ * appropriate. If MALLOC is undefined, malloc will be invoked
+ * directly -- and assumed always to succeed. Similarly, if you
+ * want something other than the system's free() to be called to
+ * recycle memory acquired from MALLOC, #define FREE to be the
+ * name of the alternate routine. (FREE or free is only called in
+ * pathological cases, e.g., in a dtoa call after a dtoa return in
+ * mode 3 with thousands of digits requested.)
+ * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making
+ * memory allocations from a private pool of memory when possible.
+ * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes,
+ * unless #defined to be a different length. This default length
+ * suffices to get rid of MALLOC calls except for unusual cases,
+ * such as decimal-to-binary conversion of a very long string of
+ * digits. The longest string dtoa can return is about 751 bytes
+ * long. For conversions by strtod of strings of 800 digits and
+ * all dtoa conversions in single-threaded executions with 8-byte
+ * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte
+ * pointers, PRIVATE_MEM >= 7112 appears adequate.
+ * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK
+ * #defined automatically on IEEE systems. On such systems,
+ * when INFNAN_CHECK is #defined, strtod checks
+ * for Infinity and NaN (case insensitively). On some systems
+ * (e.g., some HP systems), it may be necessary to #define NAN_WORD0
+ * appropriately -- to the most significant word of a quiet NaN.
+ * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.)
+ * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined,
+ * strtod also accepts (case insensitively) strings of the form
+ * NaN(x), where x is a string of hexadecimal digits and spaces;
+ * if there is only one string of hexadecimal digits, it is taken
+ * for the 52 fraction bits of the resulting NaN; if there are two
+ * or more strings of hex digits, the first is for the high 20 bits,
+ * the second and subsequent for the low 32 bits, with intervening
+ * white space ignored; but if this results in none of the 52
+ * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0
+ * and NAN_WORD1 are used instead.
+ * #define MULTIPLE_THREADS if the system offers preemptively scheduled
+ * multiple threads. In this case, you must provide (or suitably
+ * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed
+ * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed
+ * in pow5mult, ensures lazy evaluation of only one copy of high
+ * powers of 5; omitting this lock would introduce a small
+ * probability of wasting memory, but would otherwise be harmless.)
+ * You must also invoke freedtoa(s) to free the value s returned by
+ * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined.
+ * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that
+ * avoids underflows on inputs whose result does not underflow.
+ * If you #define NO_IEEE_Scale on a machine that uses IEEE-format
+ * floating-point numbers and flushes underflows to zero rather
+ * than implementing gradual underflow, then you must also #define
+ * Sudden_Underflow.
+ * #define USE_LOCALE to use the current locale's decimal_point value.
+ * #define SET_INEXACT if IEEE arithmetic is being used and extra
+ * computation should be done to set the inexact flag when the
+ * result is inexact and avoid setting inexact when the result
+ * is exact. In this case, dtoa.c must be compiled in
+ * an environment, perhaps provided by #include "dtoa.c" in a
+ * suitable wrapper, that defines two functions,
+ * int get_inexact(void);
+ * void clear_inexact(void);
+ * such that get_inexact() returns a nonzero value if the
+ * inexact bit is already set, and clear_inexact() sets the
+ * inexact bit to 0. When SET_INEXACT is #defined, strtod
+ * also does extra computations to set the underflow and overflow
+ * flags when appropriate (i.e., when the result is tiny and
+ * inexact or when it is a numeric value rounded to +-infinity).
+ * #define NO_ERRNO if strtod should not assign errno = ERANGE when
+ * the result overflows to +-Infinity or underflows to 0.
+ * #define NO_HEX_FP to omit recognition of hexadecimal floating-point
+ * values by strtod.
+ * #define NO_STRTOD_BIGCOMP (on IEEE-arithmetic systems only for now)
+ * to disable logic for "fast" testing of very long input strings
+ * to strtod. This testing proceeds by initially truncating the
+ * input string, then if necessary comparing the whole string with
+ * a decimal expansion to decide close cases. This logic is only
+ * used for input more than STRTOD_DIGLIM digits long (default 40).
+ */
+
+#ifndef Long
+#define Long long
+#endif
+#ifndef ULong
+typedef unsigned Long ULong;
+#endif
+
+#ifdef DEBUG
+#include "stdio.h"
+#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);}
+#endif
+
+#include "stdlib.h"
+#include "string.h"
+
+#ifdef USE_LOCALE
+#include "locale.h"
+#endif
+
+#ifdef Honor_FLT_ROUNDS
+#ifndef Trust_FLT_ROUNDS
+#include <fenv.h>
+#endif
+#endif
+
+#ifdef MALLOC
+#ifdef KR_headers
+extern char *MALLOC();
+#else
+extern void *MALLOC(size_t);
+#endif
+#else
+#define MALLOC malloc
+#endif
+
+#ifndef Omit_Private_Memory
+#ifndef PRIVATE_MEM
+#define PRIVATE_MEM 2304
+#endif
+#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double))
+static double private_mem[PRIVATE_mem], *pmem_next = private_mem;
+#endif
+
+#undef IEEE_Arith
+#undef Avoid_Underflow
+#ifdef IEEE_MC68k
+#define IEEE_Arith
+#endif
+#ifdef IEEE_8087
+#define IEEE_Arith
+#endif
+
+#ifdef IEEE_Arith
+#ifndef NO_INFNAN_CHECK
+#undef INFNAN_CHECK
+#define INFNAN_CHECK
+#endif
+#else
+#undef INFNAN_CHECK
+#define NO_STRTOD_BIGCOMP
+#endif
+
+#include "errno.h"
+
+#ifdef Bad_float_h
+
+#ifdef IEEE_Arith
+#define DBL_DIG 15
+#define DBL_MAX_10_EXP 308
+#define DBL_MAX_EXP 1024
+#define FLT_RADIX 2
+#endif /*IEEE_Arith*/
+
+#ifdef IBM
+#define DBL_DIG 16
+#define DBL_MAX_10_EXP 75
+#define DBL_MAX_EXP 63
+#define FLT_RADIX 16
+#define DBL_MAX 7.2370055773322621e+75
+#endif
+
+#ifdef VAX
+#define DBL_DIG 16
+#define DBL_MAX_10_EXP 38
+#define DBL_MAX_EXP 127
+#define FLT_RADIX 2
+#define DBL_MAX 1.7014118346046923e+38
+#endif
+
+#ifndef LONG_MAX
+#define LONG_MAX 2147483647
+#endif
+
+#else /* ifndef Bad_float_h */
+#include "float.h"
+#endif /* Bad_float_h */
+
+#ifndef __MATH_H__
+#include "math.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef CONST
+#ifdef KR_headers
+#define CONST /* blank */
+#else
+#define CONST const
+#endif
+#endif
+
+#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1
+Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined.
+#endif
+
+typedef union {
+ double d;
+ ULong L[2];
+} U;
+
+#ifdef IEEE_8087
+#define word0(x) (x)->L[1]
+#define word1(x) (x)->L[0]
+#else
+#define word0(x) (x)->L[0]
+#define word1(x) (x)->L[1]
+#endif
+#define dval(x) (x)->d
+
+#ifndef STRTOD_DIGLIM
+#define STRTOD_DIGLIM 40
+#endif
+
+#ifdef DIGLIM_DEBUG
+extern int strtod_diglim;
+#else
+#define strtod_diglim STRTOD_DIGLIM
+#endif
+
+/* The following definition of Storeinc is appropriate for MIPS processors.
+ * An alternative that might be better on some machines is
+ * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff)
+ */
+#if defined(IEEE_8087) + defined(VAX)
+#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \
+((unsigned short *)a)[0] = (unsigned short)c, a++)
+#else
+#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \
+((unsigned short *)a)[1] = (unsigned short)c, a++)
+#endif
+
+/* #define P DBL_MANT_DIG */
+/* Ten_pmax = floor(P*log(2)/log(5)) */
+/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */
+/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */
+/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */
+
+#ifdef IEEE_Arith
+#define Exp_shift 20
+#define Exp_shift1 20
+#define Exp_msk1 0x100000
+#define Exp_msk11 0x100000
+#define Exp_mask 0x7ff00000
+#define P 53
+#define Nbits 53
+#define Bias 1023
+#define Emax 1023
+#define Emin (-1022)
+#define Exp_1 0x3ff00000
+#define Exp_11 0x3ff00000
+#define Ebits 11
+#define Frac_mask 0xfffff
+#define Frac_mask1 0xfffff
+#define Ten_pmax 22
+#define Bletch 0x10
+#define Bndry_mask 0xfffff
+#define Bndry_mask1 0xfffff
+#define LSB 1
+#define Sign_bit 0x80000000
+#define Log2P 1
+#define Tiny0 0
+#define Tiny1 1
+#define Quick_max 14
+#define Int_max 14
+#ifndef NO_IEEE_Scale
+#define Avoid_Underflow
+#ifdef Flush_Denorm /* debugging option */
+#undef Sudden_Underflow
+#endif
+#endif
+
+#ifndef Flt_Rounds
+#ifdef FLT_ROUNDS
+#define Flt_Rounds FLT_ROUNDS
+#else
+#define Flt_Rounds 1
+#endif
+#endif /*Flt_Rounds*/
+
+#ifdef Honor_FLT_ROUNDS
+#undef Check_FLT_ROUNDS
+#define Check_FLT_ROUNDS
+#else
+#define Rounding Flt_Rounds
+#endif
+
+#else /* ifndef IEEE_Arith */
+#undef Check_FLT_ROUNDS
+#undef Honor_FLT_ROUNDS
+#undef SET_INEXACT
+#undef Sudden_Underflow
+#define Sudden_Underflow
+#ifdef IBM
+#undef Flt_Rounds
+#define Flt_Rounds 0
+#define Exp_shift 24
+#define Exp_shift1 24
+#define Exp_msk1 0x1000000
+#define Exp_msk11 0x1000000
+#define Exp_mask 0x7f000000
+#define P 14
+#define Nbits 56
+#define Bias 65
+#define Emax 248
+#define Emin (-260)
+#define Exp_1 0x41000000
+#define Exp_11 0x41000000
+#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */
+#define Frac_mask 0xffffff
+#define Frac_mask1 0xffffff
+#define Bletch 4
+#define Ten_pmax 22
+#define Bndry_mask 0xefffff
+#define Bndry_mask1 0xffffff
+#define LSB 1
+#define Sign_bit 0x80000000
+#define Log2P 4
+#define Tiny0 0x100000
+#define Tiny1 0
+#define Quick_max 14
+#define Int_max 15
+#else /* VAX */
+#undef Flt_Rounds
+#define Flt_Rounds 1
+#define Exp_shift 23
+#define Exp_shift1 7
+#define Exp_msk1 0x80
+#define Exp_msk11 0x800000
+#define Exp_mask 0x7f80
+#define P 56
+#define Nbits 56
+#define Bias 129
+#define Emax 126
+#define Emin (-129)
+#define Exp_1 0x40800000
+#define Exp_11 0x4080
+#define Ebits 8
+#define Frac_mask 0x7fffff
+#define Frac_mask1 0xffff007f
+#define Ten_pmax 24
+#define Bletch 2
+#define Bndry_mask 0xffff007f
+#define Bndry_mask1 0xffff007f
+#define LSB 0x10000
+#define Sign_bit 0x8000
+#define Log2P 1
+#define Tiny0 0x80
+#define Tiny1 0
+#define Quick_max 15
+#define Int_max 15
+#endif /* IBM, VAX */
+#endif /* IEEE_Arith */
+
+#ifndef IEEE_Arith
+#define ROUND_BIASED
+#else
+#ifdef ROUND_BIASED_without_Round_Up
+#undef ROUND_BIASED
+#define ROUND_BIASED
+#endif
+#endif
+
+#ifdef RND_PRODQUOT
+#define rounded_product(a,b) a = rnd_prod(a, b)
+#define rounded_quotient(a,b) a = rnd_quot(a, b)
+#ifdef KR_headers
+extern double rnd_prod(), rnd_quot();
+#else
+extern double rnd_prod(double, double), rnd_quot(double, double);
+#endif
+#else
+#define rounded_product(a,b) a *= b
+#define rounded_quotient(a,b) a /= b
+#endif
+
+#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))
+#define Big1 0xffffffff
+
+#ifndef Pack_32
+#define Pack_32
+#endif
+
+typedef struct BCinfo BCinfo;
+struct
+ BCinfo {
+ int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflchk;
+};
+
+#ifdef KR_headers
+#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff)
+#else
+#define FFFFFFFF 0xffffffffUL
+#endif
+
+#ifdef NO_LONG_LONG
+#undef ULLong
+#ifdef Just_16
+#undef Pack_32
+/* When Pack_32 is not defined, we store 16 bits per 32-bit Long.
+ * This makes some inner loops simpler and sometimes saves work
+ * during multiplications, but it often seems to make things slightly
+ * slower. Hence the default is now to store 32 bits per Long.
+ */
+#endif
+#else /* long long available */
+#ifndef Llong
+#define Llong long long
+#endif
+#ifndef ULLong
+#define ULLong unsigned Llong
+#endif
+#endif /* NO_LONG_LONG */
+
+#ifndef MULTIPLE_THREADS
+#define ACQUIRE_DTOA_LOCK(n) /*nothing*/
+#define FREE_DTOA_LOCK(n) /*nothing*/
+#endif
+
+#define Kmax 7
+
+#ifdef __cplusplus
+extern "C" double strtod(const char *s00, char **se);
+extern "C" char *dtoa(double d, int mode, int ndigits,
+ int *decpt, int *sign, char **rve);
+#endif
+
+struct
+ Bigint {
+ struct Bigint *next;
+ int k, maxwds, sign, wds;
+ ULong x[1];
+};
+
+typedef struct Bigint Bigint;
+
+static Bigint *freelist[Kmax+1];
+
+static Bigint *
+Balloc
+#ifdef KR_headers
+(k) int k;
+#else
+(int k)
+#endif
+{
+ int x;
+ Bigint *rv;
+#ifndef Omit_Private_Memory
+ unsigned int len;
+#endif
+
+ ACQUIRE_DTOA_LOCK(0);
+ /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */
+ /* but this case seems very unlikely. */
+ if (k <= Kmax && (rv = freelist[k])) {
+ freelist[k] = rv->next;
+ }
+ else {
+ x = 1 << k;
+#ifdef Omit_Private_Memory
+ rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong));
+#else
+ len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1)
+ /sizeof(double);
+ if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) {
+ rv = (Bigint*)pmem_next;
+ pmem_next += len;
+ }
+ else {
+ rv = (Bigint*)MALLOC(len*sizeof(double));
+ }
+#endif
+ rv->k = k;
+ rv->maxwds = x;
+ }
+ FREE_DTOA_LOCK(0);
+ rv->sign = rv->wds = 0;
+ return rv;
+}
+
+static void
+Bfree
+#ifdef KR_headers
+(v) Bigint *v;
+#else
+(Bigint *v)
+#endif
+{
+ if (v) {
+ if (v->k > Kmax)
+#ifdef FREE
+ FREE((void*)v);
+#else
+ free((void*)v);
+#endif
+ else {
+ ACQUIRE_DTOA_LOCK(0);
+ v->next = freelist[v->k];
+ freelist[v->k] = v;
+ FREE_DTOA_LOCK(0);
+ }
+ }
+}
+
+#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \
+y->wds*sizeof(Long) + 2*sizeof(int))
+
+static Bigint *
+multadd
+#ifdef KR_headers
+(b, m, a) Bigint *b; int m, a;
+#else
+(Bigint *b, int m, int a) /* multiply by m and add a */
+#endif
+{
+ int i, wds;
+#ifdef ULLong
+ ULong *x;
+ ULLong carry, y;
+#else
+ ULong carry, *x, y;
+#ifdef Pack_32
+ ULong xi, z;
+#endif
+#endif
+ Bigint *b1;
+
+ wds = b->wds;
+ x = b->x;
+ i = 0;
+ carry = a;
+ do {
+#ifdef ULLong
+ y = *x * (ULLong)m + carry;
+ carry = y >> 32;
+ *x++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ xi = *x;
+ y = (xi & 0xffff) * m + carry;
+ z = (xi >> 16) * m + (y >> 16);
+ carry = z >> 16;
+ *x++ = (z << 16) + (y & 0xffff);
+#else
+ y = *x * m + carry;
+ carry = y >> 16;
+ *x++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(++i < wds);
+ if (carry) {
+ if (wds >= b->maxwds) {
+ b1 = Balloc(b->k+1);
+ Bcopy(b1, b);
+ Bfree(b);
+ b = b1;
+ }
+ b->x[wds++] = carry;
+ b->wds = wds;
+ }
+ return b;
+}
+
+static Bigint *
+s2b
+#ifdef KR_headers
+(s, nd0, nd, y9, dplen) CONST char *s; int nd0, nd, dplen; ULong y9;
+#else
+(const char *s, int nd0, int nd, ULong y9, int dplen)
+#endif
+{
+ Bigint *b;
+ int i, k;
+ Long x, y;
+
+ x = (nd + 8) / 9;
+ for(k = 0, y = 1; x > y; y <<= 1, k++) ;
+#ifdef Pack_32
+ b = Balloc(k);
+ b->x[0] = y9;
+ b->wds = 1;
+#else
+ b = Balloc(k+1);
+ b->x[0] = y9 & 0xffff;
+ b->wds = (b->x[1] = y9 >> 16) ? 2 : 1;
+#endif
+
+ i = 9;
+ if (9 < nd0) {
+ s += 9;
+ do {
+ b = multadd(b, 10, *s++ - '0');
+ }
+ while(++i < nd0);
+ s += dplen;
+ }
+ else {
+ s += dplen + 9;
+ }
+ for(; i < nd; i++) {
+ b = multadd(b, 10, *s++ - '0');
+ }
+ return b;
+}
+
+static int
+hi0bits
+#ifdef KR_headers
+(x) ULong x;
+#else
+(ULong x)
+#endif
+{
+ int k = 0;
+
+ if (!(x & 0xffff0000)) {
+ k = 16;
+ x <<= 16;
+ }
+ if (!(x & 0xff000000)) {
+ k += 8;
+ x <<= 8;
+ }
+ if (!(x & 0xf0000000)) {
+ k += 4;
+ x <<= 4;
+ }
+ if (!(x & 0xc0000000)) {
+ k += 2;
+ x <<= 2;
+ }
+ if (!(x & 0x80000000)) {
+ k++;
+ if (!(x & 0x40000000)) {
+ return 32;
+ }
+ }
+ return k;
+}
+
+static int
+lo0bits
+#ifdef KR_headers
+(y) ULong *y;
+#else
+(ULong *y)
+#endif
+{
+ int k;
+ ULong x = *y;
+
+ if (x & 7) {
+ if (x & 1) {
+ return 0;
+ }
+ if (x & 2) {
+ *y = x >> 1;
+ return 1;
+ }
+ *y = x >> 2;
+ return 2;
+ }
+ k = 0;
+ if (!(x & 0xffff)) {
+ k = 16;
+ x >>= 16;
+ }
+ if (!(x & 0xff)) {
+ k += 8;
+ x >>= 8;
+ }
+ if (!(x & 0xf)) {
+ k += 4;
+ x >>= 4;
+ }
+ if (!(x & 0x3)) {
+ k += 2;
+ x >>= 2;
+ }
+ if (!(x & 1)) {
+ k++;
+ x >>= 1;
+ if (!x) {
+ return 32;
+ }
+ }
+ *y = x;
+ return k;
+}
+
+static Bigint *
+i2b
+#ifdef KR_headers
+(i) int i;
+#else
+(int i)
+#endif
+{
+ Bigint *b;
+
+ b = Balloc(1);
+ b->x[0] = i;
+ b->wds = 1;
+ return b;
+}
+
+static Bigint *
+mult
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ Bigint *c;
+ int k, wa, wb, wc;
+ ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0;
+ ULong y;
+#ifdef ULLong
+ ULLong carry, z;
+#else
+ ULong carry, z;
+#ifdef Pack_32
+ ULong z2;
+#endif
+#endif
+
+ if (a->wds < b->wds) {
+ c = a;
+ a = b;
+ b = c;
+ }
+ k = a->k;
+ wa = a->wds;
+ wb = b->wds;
+ wc = wa + wb;
+ if (wc > a->maxwds) {
+ k++;
+ }
+ c = Balloc(k);
+ for(x = c->x, xa = x + wc; x < xa; x++) {
+ *x = 0;
+ }
+ xa = a->x;
+ xae = xa + wa;
+ xb = b->x;
+ xbe = xb + wb;
+ xc0 = c->x;
+#ifdef ULLong
+ for(; xb < xbe; xc0++) {
+ if ((y = *xb++)) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = *x++ * (ULLong)y + *xc + carry;
+ carry = z >> 32;
+ *xc++ = z & FFFFFFFF;
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ }
+#else
+#ifdef Pack_32
+ for(; xb < xbe; xb++, xc0++) {
+ if (y = *xb & 0xffff) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = (*x & 0xffff) * y + (*xc & 0xffff) + carry;
+ carry = z >> 16;
+ z2 = (*x++ >> 16) * y + (*xc >> 16) + carry;
+ carry = z2 >> 16;
+ Storeinc(xc, z2, z);
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ if (y = *xb >> 16) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ z2 = *xc;
+ do {
+ z = (*x & 0xffff) * y + (*xc >> 16) + carry;
+ carry = z >> 16;
+ Storeinc(xc, z, z2);
+ z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry;
+ carry = z2 >> 16;
+ }
+ while(x < xae);
+ *xc = z2;
+ }
+ }
+#else
+ for(; xb < xbe; xc0++) {
+ if (y = *xb++) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = *x++ * y + *xc + carry;
+ carry = z >> 16;
+ *xc++ = z & 0xffff;
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ }
+#endif
+#endif
+ for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ;
+ c->wds = wc;
+ return c;
+}
+
+static Bigint *p5s;
+
+static Bigint *
+pow5mult
+#ifdef KR_headers
+(b, k) Bigint *b; int k;
+#else
+(Bigint *b, int k)
+#endif
+{
+ Bigint *b1, *p5, *p51;
+ int i;
+ static int p05[3] = { 5, 25, 125 };
+
+ if ((i = k & 3)) {
+ b = multadd(b, p05[i-1], 0);
+ }
+
+ if (!(k >>= 2)) {
+ return b;
+ }
+ if (!(p5 = p5s)) {
+ /* first time */
+#ifdef MULTIPLE_THREADS
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p5 = p5s)) {
+ p5 = p5s = i2b(625);
+ p5->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+#else
+ p5 = p5s = i2b(625);
+ p5->next = 0;
+#endif
+ }
+ for(;;) {
+ if (k & 1) {
+ b1 = mult(b, p5);
+ Bfree(b);
+ b = b1;
+ }
+ if (!(k >>= 1)) {
+ break;
+ }
+ if (!(p51 = p5->next)) {
+#ifdef MULTIPLE_THREADS
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p51 = p5->next)) {
+ p51 = p5->next = mult(p5,p5);
+ p51->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+#else
+ p51 = p5->next = mult(p5,p5);
+ p51->next = 0;
+#endif
+ }
+ p5 = p51;
+ }
+ return b;
+}
+
+static Bigint *
+lshift
+#ifdef KR_headers
+(b, k) Bigint *b; int k;
+#else
+(Bigint *b, int k)
+#endif
+{
+ int i, k1, n, n1;
+ Bigint *b1;
+ ULong *x, *x1, *xe, z;
+
+#ifdef Pack_32
+ n = k >> 5;
+#else
+ n = k >> 4;
+#endif
+ k1 = b->k;
+ n1 = n + b->wds + 1;
+ for(i = b->maxwds; n1 > i; i <<= 1) {
+ k1++;
+ }
+ b1 = Balloc(k1);
+ x1 = b1->x;
+ for(i = 0; i < n; i++) {
+ *x1++ = 0;
+ }
+ x = b->x;
+ xe = x + b->wds;
+#ifdef Pack_32
+ if (k &= 0x1f) {
+ k1 = 32 - k;
+ z = 0;
+ do {
+ *x1++ = *x << k | z;
+ z = *x++ >> k1;
+ }
+ while(x < xe);
+ if ((*x1 = z)) {
+ ++n1;
+ }
+ }
+#else
+ if (k &= 0xf) {
+ k1 = 16 - k;
+ z = 0;
+ do {
+ *x1++ = *x << k & 0xffff | z;
+ z = *x++ >> k1;
+ }
+ while(x < xe);
+ if (*x1 = z) {
+ ++n1;
+ }
+ }
+#endif
+ else do {
+ *x1++ = *x++;
+ }
+ while(x < xe);
+ b1->wds = n1 - 1;
+ Bfree(b);
+ return b1;
+}
+
+static int
+cmp
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ ULong *xa, *xa0, *xb, *xb0;
+ int i, j;
+
+ i = a->wds;
+ j = b->wds;
+#ifdef DEBUG
+ if (i > 1 && !a->x[i-1]) {
+ Bug("cmp called with a->x[a->wds-1] == 0");
+ }
+ if (j > 1 && !b->x[j-1]) {
+ Bug("cmp called with b->x[b->wds-1] == 0");
+ }
+#endif
+ if (i -= j) {
+ return i;
+ }
+ xa0 = a->x;
+ xa = xa0 + j;
+ xb0 = b->x;
+ xb = xb0 + j;
+ for(;;) {
+ if (*--xa != *--xb) {
+ return *xa < *xb ? -1 : 1;
+ }
+ if (xa <= xa0) {
+ break;
+ }
+ }
+ return 0;
+}
+
+static Bigint *
+diff
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ Bigint *c;
+ int i, wa, wb;
+ ULong *xa, *xae, *xb, *xbe, *xc;
+#ifdef ULLong
+ ULLong borrow, y;
+#else
+ ULong borrow, y;
+#ifdef Pack_32
+ ULong z;
+#endif
+#endif
+
+ i = cmp(a,b);
+ if (!i) {
+ c = Balloc(0);
+ c->wds = 1;
+ c->x[0] = 0;
+ return c;
+ }
+ if (i < 0) {
+ c = a;
+ a = b;
+ b = c;
+ i = 1;
+ }
+ else {
+ i = 0;
+ }
+ c = Balloc(a->k);
+ c->sign = i;
+ wa = a->wds;
+ xa = a->x;
+ xae = xa + wa;
+ wb = b->wds;
+ xb = b->x;
+ xbe = xb + wb;
+ xc = c->x;
+ borrow = 0;
+#ifdef ULLong
+ do {
+ y = (ULLong)*xa++ - *xb++ - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *xc++ = y & FFFFFFFF;
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = *xa++ - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *xc++ = y & FFFFFFFF;
+ }
+#else
+#ifdef Pack_32
+ do {
+ y = (*xa & 0xffff) - (*xb & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - (*xb++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = (*xa & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ }
+#else
+ do {
+ y = *xa++ - *xb++ - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *xc++ = y & 0xffff;
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = *xa++ - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *xc++ = y & 0xffff;
+ }
+#endif
+#endif
+ while(!*--xc) {
+ wa--;
+ }
+ c->wds = wa;
+ return c;
+}
+
+static double
+ulp
+#ifdef KR_headers
+(x) U *x;
+#else
+(U *x)
+#endif
+{
+ Long L;
+ U u;
+
+ L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1;
+#ifndef Avoid_Underflow
+#ifndef Sudden_Underflow
+ if (L > 0) {
+#endif
+#endif
+#ifdef IBM
+ L |= Exp_msk1 >> 4;
+#endif
+ word0(&u) = L;
+ word1(&u) = 0;
+#ifndef Avoid_Underflow
+#ifndef Sudden_Underflow
+ }
+ else {
+ L = -L >> Exp_shift;
+ if (L < Exp_shift) {
+ word0(&u) = 0x80000 >> L;
+ word1(&u) = 0;
+ }
+ else {
+ word0(&u) = 0;
+ L -= Exp_shift;
+ word1(&u) = L >= 31 ? 1 : 1 << 31 - L;
+ }
+ }
+#endif
+#endif
+ return dval(&u);
+}
+
+static double
+b2d
+#ifdef KR_headers
+(a, e) Bigint *a; int *e;
+#else
+(Bigint *a, int *e)
+#endif
+{
+ ULong *xa, *xa0, w, y, z;
+ int k;
+ U d;
+#ifdef VAX
+ ULong d0, d1;
+#else
+#define d0 word0(&d)
+#define d1 word1(&d)
+#endif
+
+ xa0 = a->x;
+ xa = xa0 + a->wds;
+ y = *--xa;
+#ifdef DEBUG
+ if (!y) {
+ Bug("zero y in b2d");
+ }
+#endif
+ k = hi0bits(y);
+ *e = 32 - k;
+#ifdef Pack_32
+ if (k < Ebits) {
+ d0 = Exp_1 | y >> (Ebits - k);
+ w = xa > xa0 ? *--xa : 0;
+ d1 = y << ((32-Ebits) + k) | w >> (Ebits - k);
+ goto ret_d;
+ }
+ z = xa > xa0 ? *--xa : 0;
+ if (k -= Ebits) {
+ d0 = Exp_1 | y << k | z >> (32 - k);
+ y = xa > xa0 ? *--xa : 0;
+ d1 = z << k | y >> (32 - k);
+ }
+ else {
+ d0 = Exp_1 | y;
+ d1 = z;
+ }
+#else
+ if (k < Ebits + 16) {
+ z = xa > xa0 ? *--xa : 0;
+ d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k;
+ w = xa > xa0 ? *--xa : 0;
+ y = xa > xa0 ? *--xa : 0;
+ d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k;
+ goto ret_d;
+ }
+ z = xa > xa0 ? *--xa : 0;
+ w = xa > xa0 ? *--xa : 0;
+ k -= Ebits + 16;
+ d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k;
+ y = xa > xa0 ? *--xa : 0;
+ d1 = w << k + 16 | y << k;
+#endif
+ret_d:
+#ifdef VAX
+ word0(&d) = d0 >> 16 | d0 << 16;
+ word1(&d) = d1 >> 16 | d1 << 16;
+#else
+#undef d0
+#undef d1
+#endif
+ return dval(&d);
+}
+
+static Bigint *
+d2b
+#ifdef KR_headers
+(d, e, bits) U *d; int *e, *bits;
+#else
+(U *d, int *e, int *bits)
+#endif
+{
+ Bigint *b;
+ int de, k;
+ ULong *x, y, z;
+#ifndef Sudden_Underflow
+ int i;
+#endif
+#ifdef VAX
+ ULong d0, d1;
+ d0 = word0(d) >> 16 | word0(d) << 16;
+ d1 = word1(d) >> 16 | word1(d) << 16;
+#else
+#define d0 word0(d)
+#define d1 word1(d)
+#endif
+
+#ifdef Pack_32
+ b = Balloc(1);
+#else
+ b = Balloc(2);
+#endif
+ x = b->x;
+
+ z = d0 & Frac_mask;
+ d0 &= 0x7fffffff; /* clear sign bit, which we ignore */
+#ifdef Sudden_Underflow
+ de = (int)(d0 >> Exp_shift);
+#ifndef IBM
+ z |= Exp_msk11;
+#endif
+#else
+ if ((de = (int)(d0 >> Exp_shift))) {
+ z |= Exp_msk1;
+ }
+#endif
+#ifdef Pack_32
+ if ((y = d1)) {
+ if ((k = lo0bits(&y))) {
+ x[0] = y | z << (32 - k);
+ z >>= k;
+ }
+ else {
+ x[0] = y;
+ }
+#ifndef Sudden_Underflow
+ i =
+#endif
+ b->wds = (x[1] = z) ? 2 : 1;
+ }
+ else {
+ k = lo0bits(&z);
+ x[0] = z;
+#ifndef Sudden_Underflow
+ i =
+#endif
+ b->wds = 1;
+ k += 32;
+ }
+#else
+ if (y = d1) {
+ if (k = lo0bits(&y))
+ if (k >= 16) {
+ x[0] = y | z << 32 - k & 0xffff;
+ x[1] = z >> k - 16 & 0xffff;
+ x[2] = z >> k;
+ i = 2;
+ }
+ else {
+ x[0] = y & 0xffff;
+ x[1] = y >> 16 | z << 16 - k & 0xffff;
+ x[2] = z >> k & 0xffff;
+ x[3] = z >> k+16;
+ i = 3;
+ }
+ else {
+ x[0] = y & 0xffff;
+ x[1] = y >> 16;
+ x[2] = z & 0xffff;
+ x[3] = z >> 16;
+ i = 3;
+ }
+ }
+ else {
+#ifdef DEBUG
+ if (!z) {
+ Bug("Zero passed to d2b");
+ }
+#endif
+ k = lo0bits(&z);
+ if (k >= 16) {
+ x[0] = z;
+ i = 0;
+ }
+ else {
+ x[0] = z & 0xffff;
+ x[1] = z >> 16;
+ i = 1;
+ }
+ k += 32;
+ }
+ while(!x[i]) {
+ --i;
+ }
+ b->wds = i + 1;
+#endif
+#ifndef Sudden_Underflow
+ if (de) {
+#endif
+#ifdef IBM
+ *e = (de - Bias - (P-1) << 2) + k;
+ *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask);
+#else
+ *e = de - Bias - (P-1) + k;
+ *bits = P - k;
+#endif
+#ifndef Sudden_Underflow
+ }
+ else {
+ *e = de - Bias - (P-1) + 1 + k;
+#ifdef Pack_32
+ *bits = 32*i - hi0bits(x[i-1]);
+#else
+ *bits = (i+2)*16 - hi0bits(x[i]);
+#endif
+ }
+#endif
+ return b;
+}
+#undef d0
+#undef d1
+
+static double
+ratio
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ U da, db;
+ int k, ka, kb;
+
+ dval(&da) = b2d(a, &ka);
+ dval(&db) = b2d(b, &kb);
+#ifdef Pack_32
+ k = ka - kb + 32*(a->wds - b->wds);
+#else
+ k = ka - kb + 16*(a->wds - b->wds);
+#endif
+#ifdef IBM
+ if (k > 0) {
+ word0(&da) += (k >> 2)*Exp_msk1;
+ if (k &= 3) {
+ dval(&da) *= 1 << k;
+ }
+ }
+ else {
+ k = -k;
+ word0(&db) += (k >> 2)*Exp_msk1;
+ if (k &= 3) {
+ dval(&db) *= 1 << k;
+ }
+ }
+#else
+ if (k > 0) {
+ word0(&da) += k*Exp_msk1;
+ }
+ else {
+ k = -k;
+ word0(&db) += k*Exp_msk1;
+ }
+#endif
+ return dval(&da) / dval(&db);
+}
+
+static CONST double
+tens[] = {
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22
+#ifdef VAX
+ , 1e23, 1e24
+#endif
+};
+
+static CONST double
+#ifdef IEEE_Arith
+bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };
+static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128,
+#ifdef Avoid_Underflow
+ 9007199254740992.*9007199254740992.e-256
+ /* = 2^106 * 1e-256 */
+#else
+ 1e-256
+#endif
+ };
+/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */
+/* flag unnecessarily. It leads to a song and dance at the end of strtod. */
+#define Scale_Bit 0x10
+#define n_bigtens 5
+#else
+#ifdef IBM
+bigtens[] = { 1e16, 1e32, 1e64 };
+static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 };
+#define n_bigtens 3
+#else
+bigtens[] = { 1e16, 1e32 };
+static CONST double tinytens[] = { 1e-16, 1e-32 };
+#define n_bigtens 2
+#endif
+#endif
+
+#undef Need_Hexdig
+#ifdef INFNAN_CHECK
+#ifndef No_Hex_NaN
+#define Need_Hexdig
+#endif
+#endif
+
+#ifndef Need_Hexdig
+#ifndef NO_HEX_FP
+#define Need_Hexdig
+#endif
+#endif
+
+#ifdef Need_Hexdig /*{*/
+static unsigned char hexdig[256];
+
+static void
+#ifdef KR_headers
+htinit(h, s, inc) unsigned char *h; unsigned char *s; int inc;
+#else
+htinit(unsigned char *h, unsigned char *s, int inc)
+#endif
+{
+ int i, j;
+ for(i = 0; (j = s[i]) !=0; i++) {
+ h[j] = i + inc;
+ }
+}
+
+static void
+#ifdef KR_headers
+hexdig_init()
+#else
+hexdig_init(void)
+#endif
+{
+#define USC (unsigned char *)
+ htinit(hexdig, USC "0123456789", 0x10);
+ htinit(hexdig, USC "abcdef", 0x10 + 10);
+ htinit(hexdig, USC "ABCDEF", 0x10 + 10);
+}
+#endif /* } Need_Hexdig */
+
+#ifdef INFNAN_CHECK
+
+#ifndef NAN_WORD0
+#define NAN_WORD0 0x7ff80000
+#endif
+
+#ifndef NAN_WORD1
+#define NAN_WORD1 0
+#endif
+
+static int
+match
+#ifdef KR_headers
+(sp, t) char **sp, *t;
+#else
+(const char **sp, const char *t)
+#endif
+{
+ int c, d;
+ CONST char *s = *sp;
+
+ while((d = *t++)) {
+ if ((c = *++s) >= 'A' && c <= 'Z') {
+ c += 'a' - 'A';
+ }
+ if (c != d) {
+ return 0;
+ }
+ }
+ *sp = s + 1;
+ return 1;
+}
+
+#ifndef No_Hex_NaN
+static void
+hexnan
+#ifdef KR_headers
+(rvp, sp) U *rvp; CONST char **sp;
+#else
+(U *rvp, const char **sp)
+#endif
+{
+ ULong c, x[2];
+ CONST char *s;
+ int c1, havedig, udx0, xshift;
+
+ if (!hexdig['0']) {
+ hexdig_init();
+ }
+ x[0] = x[1] = 0;
+ havedig = xshift = 0;
+ udx0 = 1;
+ s = *sp;
+ /* allow optional initial 0x or 0X */
+ while((c = *(CONST unsigned char*)(s+1)) && c <= ' ') {
+ ++s;
+ }
+ if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X')) {
+ s += 2;
+ }
+ while((c = *(CONST unsigned char*)++s)) {
+ if ((c1 = hexdig[c])) {
+ c = c1 & 0xf;
+ }
+ else if (c <= ' ') {
+ if (udx0 && havedig) {
+ udx0 = 0;
+ xshift = 1;
+ }
+ continue;
+ }
+#ifdef GDTOA_NON_PEDANTIC_NANCHECK
+ else if (/*(*/ c == ')' && havedig) {
+ *sp = s + 1;
+ break;
+ }
+ else {
+ return; /* invalid form: don't change *sp */
+ }
+#else
+ else {
+ do {
+ if (/*(*/ c == ')') {
+ *sp = s + 1;
+ break;
+ }
+ } while((c = *++s));
+ break;
+ }
+#endif
+ havedig = 1;
+ if (xshift) {
+ xshift = 0;
+ x[0] = x[1];
+ x[1] = 0;
+ }
+ if (udx0) {
+ x[0] = (x[0] << 4) | (x[1] >> 28);
+ }
+ x[1] = (x[1] << 4) | c;
+ }
+ if ((x[0] &= 0xfffff) || x[1]) {
+ word0(rvp) = Exp_mask | x[0];
+ word1(rvp) = x[1];
+ }
+}
+#endif /*No_Hex_NaN*/
+#endif /* INFNAN_CHECK */
+
+#ifdef Pack_32
+#define ULbits 32
+#define kshift 5
+#define kmask 31
+#else
+#define ULbits 16
+#define kshift 4
+#define kmask 15
+#endif
+
+#if !defined(NO_HEX_FP) || defined(Honor_FLT_ROUNDS) /*{*/
+static Bigint *
+#ifdef KR_headers
+increment(b) Bigint *b;
+#else
+increment(Bigint *b)
+#endif
+{
+ ULong *x, *xe;
+ Bigint *b1;
+
+ x = b->x;
+ xe = x + b->wds;
+ do {
+ if (*x < (ULong)0xffffffffL) {
+ ++*x;
+ return b;
+ }
+ *x++ = 0;
+ } while(x < xe);
+ {
+ if (b->wds >= b->maxwds) {
+ b1 = Balloc(b->k+1);
+ Bcopy(b1,b);
+ Bfree(b);
+ b = b1;
+ }
+ b->x[b->wds++] = 1;
+ }
+ return b;
+}
+
+#endif /*}*/
+
+#ifndef NO_HEX_FP /*{*/
+
+static void
+#ifdef KR_headers
+rshift(b, k) Bigint *b; int k;
+#else
+rshift(Bigint *b, int k)
+#endif
+{
+ ULong *x, *x1, *xe, y;
+ int n;
+
+ x = x1 = b->x;
+ n = k >> kshift;
+ if (n < b->wds) {
+ xe = x + b->wds;
+ x += n;
+ if (k &= kmask) {
+ n = 32 - k;
+ y = *x++ >> k;
+ while(x < xe) {
+ *x1++ = (y | (*x << n)) & 0xffffffff;
+ y = *x++ >> k;
+ }
+ if ((*x1 = y) !=0) {
+ x1++;
+ }
+ }
+ else
+ while(x < xe) {
+ *x1++ = *x++;
+ }
+ }
+ if ((b->wds = x1 - b->x) == 0) {
+ b->x[0] = 0;
+ }
+}
+
+static ULong
+#ifdef KR_headers
+any_on(b, k) Bigint *b; int k;
+#else
+any_on(Bigint *b, int k)
+#endif
+{
+ int n, nwds;
+ ULong *x, *x0, x1, x2;
+
+ x = b->x;
+ nwds = b->wds;
+ n = k >> kshift;
+ if (n > nwds) {
+ n = nwds;
+ }
+ else if (n < nwds && (k &= kmask)) {
+ x1 = x2 = x[n];
+ x1 >>= k;
+ x1 <<= k;
+ if (x1 != x2) {
+ return 1;
+ }
+ }
+ x0 = x;
+ x += n;
+ while(x > x0)
+ if (*--x) {
+ return 1;
+ }
+ return 0;
+}
+
+enum { /* rounding values: same as FLT_ROUNDS */
+ Round_zero = 0,
+ Round_near = 1,
+ Round_up = 2,
+ Round_down = 3
+};
+
+void
+#ifdef KR_headers
+gethex(sp, rvp, rounding, sign)
+CONST char **sp; U *rvp; int rounding, sign;
+#else
+gethex( CONST char **sp, U *rvp, int rounding, int sign)
+#endif
+{
+ Bigint *b;
+ CONST unsigned char *decpt, *s0, *s, *s1;
+ Long e, e1;
+ ULong L, lostbits, *x;
+ int big, denorm, esign, havedig, k, n, nbits, up, zret;
+#ifdef IBM
+ int j;
+#endif
+ enum {
+#ifdef IEEE_Arith /*{{*/
+ emax = 0x7fe - Bias - P + 1,
+ emin = Emin - P + 1
+#else /*}{*/
+ emin = Emin - P,
+#ifdef VAX
+ emax = 0x7ff - Bias - P + 1
+#endif
+#ifdef IBM
+ emax = 0x7f - Bias - P
+#endif
+#endif /*}}*/
+ };
+#ifdef USE_LOCALE
+ int i;
+#ifdef NO_LOCALE_CACHE
+ const unsigned char *decimalpoint = (unsigned char*)
+ localeconv()->decimal_point;
+#else
+ const unsigned char *decimalpoint;
+ static unsigned char *decimalpoint_cache;
+ if (!(s0 = decimalpoint_cache)) {
+ s0 = (unsigned char*)localeconv()->decimal_point;
+ if ((decimalpoint_cache = (unsigned char*)
+ MALLOC(strlen((CONST char*)s0) + 1))) {
+ strcpy((char*)decimalpoint_cache, (CONST char*)s0);
+ s0 = decimalpoint_cache;
+ }
+ }
+ decimalpoint = s0;
+#endif
+#endif
+
+ if (!hexdig['0']) {
+ hexdig_init();
+ }
+ havedig = 0;
+ s0 = *(CONST unsigned char **)sp + 2;
+ while(s0[havedig] == '0') {
+ havedig++;
+ }
+ s0 += havedig;
+ s = s0;
+ decpt = 0;
+ zret = 0;
+ e = 0;
+ if (hexdig[*s]) {
+ havedig++;
+ }
+ else {
+ zret = 1;
+#ifdef USE_LOCALE
+ for(i = 0; decimalpoint[i]; ++i) {
+ if (s[i] != decimalpoint[i]) {
+ goto pcheck;
+ }
+ }
+ decpt = s += i;
+#else
+ if (*s != '.') {
+ goto pcheck;
+ }
+ decpt = ++s;
+#endif
+ if (!hexdig[*s]) {
+ goto pcheck;
+ }
+ while(*s == '0') {
+ s++;
+ }
+ if (hexdig[*s]) {
+ zret = 0;
+ }
+ havedig = 1;
+ s0 = s;
+ }
+ while(hexdig[*s]) {
+ s++;
+ }
+#ifdef USE_LOCALE
+ if (*s == *decimalpoint && !decpt) {
+ for(i = 1; decimalpoint[i]; ++i) {
+ if (s[i] != decimalpoint[i]) {
+ goto pcheck;
+ }
+ }
+ decpt = s += i;
+#else
+ if (*s == '.' && !decpt) {
+ decpt = ++s;
+#endif
+ while(hexdig[*s]) {
+ s++;
+ }
+ }/*}*/
+ if (decpt) {
+ e = -(((Long)(s-decpt)) << 2);
+ }
+pcheck:
+ s1 = s;
+ big = esign = 0;
+ switch(*s) {
+ case 'p':
+ case 'P':
+ switch(*++s) {
+ case '-':
+ esign = 1;
+ /* no break */
+ case '+':
+ s++;
+ }
+ if ((n = hexdig[*s]) == 0 || n > 0x19) {
+ s = s1;
+ break;
+ }
+ e1 = n - 0x10;
+ while((n = hexdig[*++s]) !=0 && n <= 0x19) {
+ if (e1 & 0xf8000000) {
+ big = 1;
+ }
+ e1 = 10*e1 + n - 0x10;
+ }
+ if (esign) {
+ e1 = -e1;
+ }
+ e += e1;
+ }
+ *sp = (char*)s;
+ if (!havedig) {
+ *sp = (char*)s0 - 1;
+ }
+ if (zret) {
+ goto retz1;
+ }
+ if (big) {
+ if (esign) {
+#ifdef IEEE_Arith
+ switch(rounding) {
+ case Round_up:
+ if (sign) {
+ break;
+ }
+ goto ret_tiny;
+ case Round_down:
+ if (!sign) {
+ break;
+ }
+ goto ret_tiny;
+ }
+#endif
+ goto retz;
+#ifdef IEEE_Arith
+ret_tiny:
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+ word0(rvp) = 0;
+ word1(rvp) = 1;
+ return;
+#endif /* IEEE_Arith */
+ }
+ switch(rounding) {
+ case Round_near:
+ goto ovfl1;
+ case Round_up:
+ if (!sign) {
+ goto ovfl1;
+ }
+ goto ret_big;
+ case Round_down:
+ if (sign) {
+ goto ovfl1;
+ }
+ goto ret_big;
+ }
+ret_big:
+ word0(rvp) = Big0;
+ word1(rvp) = Big1;
+ return;
+ }
+ n = s1 - s0 - 1;
+ for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1) {
+ k++;
+ }
+ b = Balloc(k);
+ x = b->x;
+ n = 0;
+ L = 0;
+#ifdef USE_LOCALE
+ for(i = 0; decimalpoint[i+1]; ++i);
+#endif
+ while(s1 > s0) {
+#ifdef USE_LOCALE
+ if (*--s1 == decimalpoint[i]) {
+ s1 -= i;
+ continue;
+ }
+#else
+ if (*--s1 == '.') {
+ continue;
+ }
+#endif
+ if (n == ULbits) {
+ *x++ = L;
+ L = 0;
+ n = 0;
+ }
+ L |= (hexdig[*s1] & 0x0f) << n;
+ n += 4;
+ }
+ *x++ = L;
+ b->wds = n = x - b->x;
+ n = ULbits*n - hi0bits(L);
+ nbits = Nbits;
+ lostbits = 0;
+ x = b->x;
+ if (n > nbits) {
+ n -= nbits;
+ if (any_on(b,n)) {
+ lostbits = 1;
+ k = n - 1;
+ if (x[k>>kshift] & 1 << (k & kmask)) {
+ lostbits = 2;
+ if (k > 0 && any_on(b,k)) {
+ lostbits = 3;
+ }
+ }
+ }
+ rshift(b, n);
+ e += n;
+ }
+ else if (n < nbits) {
+ n = nbits - n;
+ b = lshift(b, n);
+ e -= n;
+ x = b->x;
+ }
+ if (e > Emax) {
+ovfl:
+ Bfree(b);
+ovfl1:
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+ word0(rvp) = Exp_mask;
+ word1(rvp) = 0;
+ return;
+ }
+ denorm = 0;
+ if (e < emin) {
+ denorm = 1;
+ n = emin - e;
+ if (n >= nbits) {
+#ifdef IEEE_Arith /*{*/
+ switch (rounding) {
+ case Round_near:
+ if (n == nbits && (n < 2 || any_on(b,n-1))) {
+ goto ret_tiny;
+ }
+ break;
+ case Round_up:
+ if (!sign) {
+ goto ret_tiny;
+ }
+ break;
+ case Round_down:
+ if (sign) {
+ goto ret_tiny;
+ }
+ }
+#endif /* } IEEE_Arith */
+ Bfree(b);
+retz:
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+retz1:
+ rvp->d = 0.;
+ return;
+ }
+ k = n - 1;
+ if (lostbits) {
+ lostbits = 1;
+ }
+ else if (k > 0) {
+ lostbits = any_on(b,k);
+ }
+ if (x[k>>kshift] & 1 << (k & kmask)) {
+ lostbits |= 2;
+ }
+ nbits -= n;
+ rshift(b,n);
+ e = emin;
+ }
+ if (lostbits) {
+ up = 0;
+ switch(rounding) {
+ case Round_zero:
+ break;
+ case Round_near:
+ if (lostbits & 2
+ && (lostbits & 1) | (x[0] & 1)) {
+ up = 1;
+ }
+ break;
+ case Round_up:
+ up = 1 - sign;
+ break;
+ case Round_down:
+ up = sign;
+ }
+ if (up) {
+ k = b->wds;
+ b = increment(b);
+ x = b->x;
+ if (denorm) {
+#if 0
+ if (nbits == Nbits - 1
+ && x[nbits >> kshift] & 1 << (nbits & kmask)) {
+ denorm = 0; /* not currently used */
+ }
+#endif
+ }
+ else if (b->wds > k
+ || ((n = nbits & kmask) !=0
+ && hi0bits(x[k-1]) < 32-n)) {
+ rshift(b,1);
+ if (++e > Emax) {
+ goto ovfl;
+ }
+ }
+ }
+ }
+#ifdef IEEE_Arith
+ if (denorm) {
+ word0(rvp) = b->wds > 1 ? b->x[1] & ~0x100000 : 0;
+ }
+ else {
+ word0(rvp) = (b->x[1] & ~0x100000) | ((e + 0x3ff + 52) << 20);
+ }
+ word1(rvp) = b->x[0];
+#endif
+#ifdef IBM
+ if ((j = e & 3)) {
+ k = b->x[0] & ((1 << j) - 1);
+ rshift(b,j);
+ if (k) {
+ switch(rounding) {
+ case Round_up:
+ if (!sign) {
+ increment(b);
+ }
+ break;
+ case Round_down:
+ if (sign) {
+ increment(b);
+ }
+ break;
+ case Round_near:
+ j = 1 << (j-1);
+ if (k & j && ((k & (j-1)) | lostbits)) {
+ increment(b);
+ }
+ }
+ }
+ }
+ e >>= 2;
+ word0(rvp) = b->x[1] | ((e + 65 + 13) << 24);
+ word1(rvp) = b->x[0];
+#endif
+#ifdef VAX
+ /* The next two lines ignore swap of low- and high-order 2 bytes. */
+ /* word0(rvp) = (b->x[1] & ~0x800000) | ((e + 129 + 55) << 23); */
+ /* word1(rvp) = b->x[0]; */
+ word0(rvp) = ((b->x[1] & ~0x800000) >> 16) | ((e + 129 + 55) << 7) | (b->x[1] << 16);
+ word1(rvp) = (b->x[0] >> 16) | (b->x[0] << 16);
+#endif
+ Bfree(b);
+}
+#endif /*!NO_HEX_FP}*/
+
+static int
+#ifdef KR_headers
+dshift(b, p2) Bigint *b; int p2;
+#else
+dshift(Bigint *b, int p2)
+#endif
+{
+ int rv = hi0bits(b->x[b->wds-1]) - 4;
+ if (p2 > 0) {
+ rv -= p2;
+ }
+ return rv & kmask;
+}
+
+static int
+quorem
+#ifdef KR_headers
+(b, S) Bigint *b, *S;
+#else
+(Bigint *b, Bigint *S)
+#endif
+{
+ int n;
+ ULong *bx, *bxe, q, *sx, *sxe;
+#ifdef ULLong
+ ULLong borrow, carry, y, ys;
+#else
+ ULong borrow, carry, y, ys;
+#ifdef Pack_32
+ ULong si, z, zs;
+#endif
+#endif
+
+ n = S->wds;
+#ifdef DEBUG
+ /*debug*/ if (b->wds > n)
+ /*debug*/{
+ Bug("oversize b in quorem");
+ }
+#endif
+ if (b->wds < n) {
+ return 0;
+ }
+ sx = S->x;
+ sxe = sx + --n;
+ bx = b->x;
+ bxe = bx + n;
+ q = *bxe / (*sxe + 1); /* ensure q <= true quotient */
+#ifdef DEBUG
+#ifdef NO_STRTOD_BIGCOMP
+ /*debug*/ if (q > 9)
+#else
+ /* An oversized q is possible when quorem is called from bigcomp and */
+ /* the input is near, e.g., twice the smallest denormalized number. */
+ /*debug*/ if (q > 15)
+#endif
+ /*debug*/ Bug("oversized quotient in quorem");
+#endif
+ if (q) {
+ borrow = 0;
+ carry = 0;
+ do {
+#ifdef ULLong
+ ys = *sx++ * (ULLong)q + carry;
+ carry = ys >> 32;
+ y = *bx - (ys & FFFFFFFF) - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *bx++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) * q + carry;
+ zs = (si >> 16) * q + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ * q + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *bx++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(sx <= sxe);
+ if (!*bxe) {
+ bx = b->x;
+ while(--bxe > bx && !*bxe) {
+ --n;
+ }
+ b->wds = n;
+ }
+ }
+ if (cmp(b, S) >= 0) {
+ q++;
+ borrow = 0;
+ carry = 0;
+ bx = b->x;
+ sx = S->x;
+ do {
+#ifdef ULLong
+ ys = *sx++ + carry;
+ carry = ys >> 32;
+ y = *bx - (ys & FFFFFFFF) - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *bx++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) + carry;
+ zs = (si >> 16) + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *bx++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(sx <= sxe);
+ bx = b->x;
+ bxe = bx + n;
+ if (!*bxe) {
+ while(--bxe > bx && !*bxe) {
+ --n;
+ }
+ b->wds = n;
+ }
+ }
+ return q;
+}
+
+#if defined(Avoid_Underflow) || !defined(NO_STRTOD_BIGCOMP) /*{*/
+static double
+sulp
+#ifdef KR_headers
+(x, bc) U *x; BCinfo *bc;
+#else
+(U *x, BCinfo *bc)
+#endif
+{
+ U u;
+ double rv;
+ int i;
+
+ rv = ulp(x);
+ if (!bc->scale || (i = 2*P + 1 - ((word0(x) & Exp_mask) >> Exp_shift)) <= 0) {
+ return rv; /* Is there an example where i <= 0 ? */
+ }
+ word0(&u) = Exp_1 + (i << Exp_shift);
+ word1(&u) = 0;
+ return rv * u.d;
+}
+#endif /*}*/
+
+#ifndef NO_STRTOD_BIGCOMP
+static void
+bigcomp
+#ifdef KR_headers
+(rv, s0, bc)
+U *rv; CONST char *s0; BCinfo *bc;
+#else
+(U *rv, const char *s0, BCinfo *bc)
+#endif
+{
+ Bigint *b, *d;
+ int b2, bbits, d2, dd, dig, dsign, i, j, nd, nd0, p2, p5, speccase;
+
+ dsign = bc->dsign;
+ nd = bc->nd;
+ nd0 = bc->nd0;
+ p5 = nd + bc->e0 - 1;
+ speccase = 0;
+#ifndef Sudden_Underflow
+ if (rv->d == 0.) { /* special case: value near underflow-to-zero */
+ /* threshold was rounded to zero */
+ b = i2b(1);
+ p2 = Emin - P + 1;
+ bbits = 1;
+#ifdef Avoid_Underflow
+ word0(rv) = (P+2) << Exp_shift;
+#else
+ word1(rv) = 1;
+#endif
+ i = 0;
+#ifdef Honor_FLT_ROUNDS
+ if (bc->rounding == 1)
+#endif
+ {
+ speccase = 1;
+ --p2;
+ dsign = 0;
+ goto have_i;
+ }
+ }
+ else
+#endif
+ b = d2b(rv, &p2, &bbits);
+#ifdef Avoid_Underflow
+ p2 -= bc->scale;
+#endif
+ /* floor(log2(rv)) == bbits - 1 + p2 */
+ /* Check for denormal case. */
+ i = P - bbits;
+ if (i > (j = P - Emin - 1 + p2)) {
+#ifdef Sudden_Underflow
+ Bfree(b);
+ b = i2b(1);
+ p2 = Emin;
+ i = P - 1;
+#ifdef Avoid_Underflow
+ word0(rv) = (1 + bc->scale) << Exp_shift;
+#else
+ word0(rv) = Exp_msk1;
+#endif
+ word1(rv) = 0;
+#else
+ i = j;
+#endif
+ }
+#ifdef Honor_FLT_ROUNDS
+ if (bc->rounding != 1) {
+ if (i > 0) {
+ b = lshift(b, i);
+ }
+ if (dsign) {
+ b = increment(b);
+ }
+ }
+ else
+#endif
+ {
+ b = lshift(b, ++i);
+ b->x[0] |= 1;
+ }
+#ifndef Sudden_Underflow
+have_i:
+#endif
+ p2 -= p5 + i;
+ d = i2b(1);
+ /* Arrange for convenient computation of quotients:
+ * shift left if necessary so divisor has 4 leading 0 bits.
+ */
+ if (p5 > 0) {
+ d = pow5mult(d, p5);
+ }
+ else if (p5 < 0) {
+ b = pow5mult(b, -p5);
+ }
+ if (p2 > 0) {
+ b2 = p2;
+ d2 = 0;
+ }
+ else {
+ b2 = 0;
+ d2 = -p2;
+ }
+ i = dshift(d, d2);
+ if ((b2 += i) > 0) {
+ b = lshift(b, b2);
+ }
+ if ((d2 += i) > 0) {
+ d = lshift(d, d2);
+ }
+
+ /* Now b/d = exactly half-way between the two floating-point values */
+ /* on either side of the input string. Compute first digit of b/d. */
+
+ if (!(dig = quorem(b,d))) {
+ b = multadd(b, 10, 0); /* very unlikely */
+ dig = quorem(b,d);
+ }
+
+ /* Compare b/d with s0 */
+
+ for(i = 0; i < nd0; ) {
+ if ((dd = s0[i++] - '0' - dig)) {
+ goto ret;
+ }
+ if (!b->x[0] && b->wds == 1) {
+ if (i < nd) {
+ dd = 1;
+ }
+ goto ret;
+ }
+ b = multadd(b, 10, 0);
+ dig = quorem(b,d);
+ }
+ for(j = bc->dp1; i++ < nd;) {
+ if ((dd = s0[j++] - '0' - dig)) {
+ goto ret;
+ }
+ if (!b->x[0] && b->wds == 1) {
+ if (i < nd) {
+ dd = 1;
+ }
+ goto ret;
+ }
+ b = multadd(b, 10, 0);
+ dig = quorem(b,d);
+ }
+ if (b->x[0] || b->wds > 1 || dig > 0) {
+ dd = -1;
+ }
+ret:
+ Bfree(b);
+ Bfree(d);
+#ifdef Honor_FLT_ROUNDS
+ if (bc->rounding != 1) {
+ if (dd < 0) {
+ if (bc->rounding == 0) {
+ if (!dsign) {
+ goto retlow1;
+ }
+ }
+ else if (dsign) {
+ goto rethi1;
+ }
+ }
+ else if (dd > 0) {
+ if (bc->rounding == 0) {
+ if (dsign) {
+ goto rethi1;
+ }
+ goto ret1;
+ }
+ if (!dsign) {
+ goto rethi1;
+ }
+ dval(rv) += 2.*sulp(rv,bc);
+ }
+ else {
+ bc->inexact = 0;
+ if (dsign) {
+ goto rethi1;
+ }
+ }
+ }
+ else
+#endif
+ if (speccase) {
+ if (dd <= 0) {
+ rv->d = 0.;
+ }
+ }
+ else if (dd < 0) {
+ if (!dsign) /* does not happen for round-near */
+retlow1:
+ dval(rv) -= sulp(rv,bc);
+ }
+ else if (dd > 0) {
+ if (dsign) {
+rethi1:
+ dval(rv) += sulp(rv,bc);
+ }
+ }
+ else {
+ /* Exact half-way case: apply round-even rule. */
+ if ((j = ((word0(rv) & Exp_mask) >> Exp_shift) - bc->scale) <= 0) {
+ i = 1 - j;
+ if (i <= 31) {
+ if (word1(rv) & (0x1 << i)) {
+ goto odd;
+ }
+ }
+ else if (word0(rv) & (0x1 << (i-32))) {
+ goto odd;
+ }
+ }
+ else if (word1(rv) & 1) {
+odd:
+ if (dsign) {
+ goto rethi1;
+ }
+ goto retlow1;
+ }
+ }
+
+#ifdef Honor_FLT_ROUNDS
+ret1:
+#endif
+ return;
+}
+#endif /* NO_STRTOD_BIGCOMP */
+
+double
+strtod
+#ifdef KR_headers
+(s00, se) CONST char *s00; char **se;
+#else
+(const char *s00, char **se)
+#endif
+{
+ int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, e, e1;
+ int esign, i, j, k, nd, nd0, nf, nz, nz0, nz1, sign;
+ CONST char *s, *s0, *s1;
+ double aadj, aadj1;
+ Long L;
+ U aadj2, adj, rv, rv0;
+ ULong y, z;
+ BCinfo bc;
+ Bigint *bb, *bb1, *bd, *bd0, *bs, *delta;
+#ifdef Avoid_Underflow
+ ULong Lsb, Lsb1;
+#endif
+#ifdef SET_INEXACT
+ int oldinexact;
+#endif
+#ifndef NO_STRTOD_BIGCOMP
+ int req_bigcomp = 0;
+#endif
+#ifdef Honor_FLT_ROUNDS /*{*/
+#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */
+ bc.rounding = Flt_Rounds;
+#else /*}{*/
+ bc.rounding = 1;
+ switch(fegetround()) {
+ case FE_TOWARDZERO: bc.rounding = 0; break;
+ case FE_UPWARD: bc.rounding = 2; break;
+ case FE_DOWNWARD: bc.rounding = 3;
+ }
+#endif /*}}*/
+#endif /*}*/
+#ifdef USE_LOCALE
+ CONST char *s2;
+#endif
+
+ sign = nz0 = nz1 = nz = bc.dplen = bc.uflchk = 0;
+ dval(&rv) = 0.;
+ for(s = s00;; s++) switch(*s) {
+ case '-':
+ sign = 1;
+ /* no break */
+ case '+':
+ if (*++s) {
+ goto break2;
+ }
+ /* no break */
+ case 0:
+ goto ret0;
+ case '\t':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\r':
+ case ' ':
+ continue;
+ default:
+ goto break2;
+ }
+break2:
+ if (*s == '0') {
+#ifndef NO_HEX_FP /*{*/
+ switch(s[1]) {
+ case 'x':
+ case 'X':
+#ifdef Honor_FLT_ROUNDS
+ gethex(&s, &rv, bc.rounding, sign);
+#else
+ gethex(&s, &rv, 1, sign);
+#endif
+ goto ret;
+ }
+#endif /*}*/
+ nz0 = 1;
+ while(*++s == '0') ;
+ if (!*s) {
+ goto ret;
+ }
+ }
+ s0 = s;
+ y = z = 0;
+ for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++)
+ if (nd < 9) {
+ y = 10*y + c - '0';
+ }
+ else if (nd < 16) {
+ z = 10*z + c - '0';
+ }
+ nd0 = nd;
+ bc.dp0 = bc.dp1 = s - s0;
+ for(s1 = s; s1 > s0 && *--s1 == '0'; ) {
+ ++nz1;
+ }
+#ifdef USE_LOCALE
+ s1 = localeconv()->decimal_point;
+ if (c == *s1) {
+ c = '.';
+ if (*++s1) {
+ s2 = s;
+ for(;;) {
+ if (*++s2 != *s1) {
+ c = 0;
+ break;
+ }
+ if (!*++s1) {
+ s = s2;
+ break;
+ }
+ }
+ }
+ }
+#endif
+ if (c == '.') {
+ c = *++s;
+ bc.dp1 = s - s0;
+ bc.dplen = bc.dp1 - bc.dp0;
+ if (!nd) {
+ for(; c == '0'; c = *++s) {
+ nz++;
+ }
+ if (c > '0' && c <= '9') {
+ bc.dp0 = s0 - s;
+ bc.dp1 = bc.dp0 + bc.dplen;
+ s0 = s;
+ nf += nz;
+ nz = 0;
+ goto have_dig;
+ }
+ goto dig_done;
+ }
+ for(; c >= '0' && c <= '9'; c = *++s) {
+have_dig:
+ nz++;
+ if (c -= '0') {
+ nf += nz;
+ for(i = 1; i < nz; i++)
+ if (nd++ < 9) {
+ y *= 10;
+ }
+ else if (nd <= DBL_DIG + 1) {
+ z *= 10;
+ }
+ if (nd++ < 9) {
+ y = 10*y + c;
+ }
+ else if (nd <= DBL_DIG + 1) {
+ z = 10*z + c;
+ }
+ nz = nz1 = 0;
+ }
+ }
+ }
+dig_done:
+ e = 0;
+ if (c == 'e' || c == 'E') {
+ if (!nd && !nz && !nz0) {
+ goto ret0;
+ }
+ s00 = s;
+ esign = 0;
+ switch(c = *++s) {
+ case '-':
+ esign = 1;
+ case '+':
+ c = *++s;
+ }
+ if (c >= '0' && c <= '9') {
+ while(c == '0') {
+ c = *++s;
+ }
+ if (c > '0' && c <= '9') {
+ L = c - '0';
+ s1 = s;
+ while((c = *++s) >= '0' && c <= '9') {
+ L = 10*L + c - '0';
+ }
+ if (s - s1 > 8 || L > 19999)
+ /* Avoid confusion from exponents
+ * so large that e might overflow.
+ */
+ {
+ e = 19999; /* safe for 16 bit ints */
+ }
+ else {
+ e = (int)L;
+ }
+ if (esign) {
+ e = -e;
+ }
+ }
+ else {
+ e = 0;
+ }
+ }
+ else {
+ s = s00;
+ }
+ }
+ if (!nd) {
+ if (!nz && !nz0) {
+#ifdef INFNAN_CHECK
+ /* Check for Nan and Infinity */
+ if (!bc.dplen)
+ switch(c) {
+ case 'i':
+ case 'I':
+ if (match(&s,"nf")) {
+ --s;
+ if (!match(&s,"inity")) {
+ ++s;
+ }
+ word0(&rv) = 0x7ff00000;
+ word1(&rv) = 0;
+ goto ret;
+ }
+ break;
+ case 'n':
+ case 'N':
+ if (match(&s, "an")) {
+ word0(&rv) = NAN_WORD0;
+ word1(&rv) = NAN_WORD1;
+#ifndef No_Hex_NaN
+ if (*s == '(') { /*)*/
+ hexnan(&rv, &s);
+ }
+#endif
+ goto ret;
+ }
+ }
+#endif /* INFNAN_CHECK */
+ret0:
+ s = s00;
+ sign = 0;
+ }
+ goto ret;
+ }
+ bc.e0 = e1 = e -= nf;
+
+ /* Now we have nd0 digits, starting at s0, followed by a
+ * decimal point, followed by nd-nd0 digits. The number we're
+ * after is the integer represented by those digits times
+ * 10**e */
+
+ if (!nd0) {
+ nd0 = nd;
+ }
+ k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1;
+ dval(&rv) = y;
+ if (k > 9) {
+#ifdef SET_INEXACT
+ if (k > DBL_DIG) {
+ oldinexact = get_inexact();
+ }
+#endif
+ dval(&rv) = tens[k - 9] * dval(&rv) + z;
+ }
+ bd0 = 0;
+ if (nd <= DBL_DIG
+#ifndef RND_PRODQUOT
+#ifndef Honor_FLT_ROUNDS
+ && Flt_Rounds == 1
+#endif
+#endif
+ ) {
+ if (!e) {
+ goto ret;
+ }
+#ifndef ROUND_BIASED_without_Round_Up
+ if (e > 0) {
+ if (e <= Ten_pmax) {
+#ifdef VAX
+ goto vax_ovfl_check;
+#else
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv.d = -rv.d;
+ sign = 0;
+ }
+#endif
+ /* rv = */ rounded_product(dval(&rv), tens[e]);
+ goto ret;
+#endif
+ }
+ i = DBL_DIG - nd;
+ if (e <= Ten_pmax + i) {
+ /* A fancier test would sometimes let us do
+ * this for larger i values.
+ */
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv.d = -rv.d;
+ sign = 0;
+ }
+#endif
+ e -= i;
+ dval(&rv) *= tens[i];
+#ifdef VAX
+ /* VAX exponent range is so narrow we must
+ * worry about overflow here...
+ */
+vax_ovfl_check:
+ word0(&rv) -= P*Exp_msk1;
+ /* rv = */ rounded_product(dval(&rv), tens[e]);
+ if ((word0(&rv) & Exp_mask)
+ > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) {
+ goto ovfl;
+ }
+ word0(&rv) += P*Exp_msk1;
+#else
+ /* rv = */ rounded_product(dval(&rv), tens[e]);
+#endif
+ goto ret;
+ }
+ }
+#ifndef Inaccurate_Divide
+ else if (e >= -Ten_pmax) {
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv.d = -rv.d;
+ sign = 0;
+ }
+#endif
+ /* rv = */ rounded_quotient(dval(&rv), tens[-e]);
+ goto ret;
+ }
+#endif
+#endif /* ROUND_BIASED_without_Round_Up */
+ }
+ e1 += nd - k;
+
+#ifdef IEEE_Arith
+#ifdef SET_INEXACT
+ bc.inexact = 1;
+ if (k <= DBL_DIG) {
+ oldinexact = get_inexact();
+ }
+#endif
+#ifdef Avoid_Underflow
+ bc.scale = 0;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ if (bc.rounding >= 2) {
+ if (sign) {
+ bc.rounding = bc.rounding == 2 ? 0 : 2;
+ }
+ else if (bc.rounding != 2) {
+ bc.rounding = 0;
+ }
+ }
+#endif
+#endif /*IEEE_Arith*/
+
+ /* Get starting approximation = rv * 10**e1 */
+
+ if (e1 > 0) {
+ if ((i = e1 & 15)) {
+ dval(&rv) *= tens[i];
+ }
+ if (e1 &= ~15) {
+ if (e1 > DBL_MAX_10_EXP) {
+ovfl:
+ /* Can't trust HUGE_VAL */
+#ifdef IEEE_Arith
+#ifdef Honor_FLT_ROUNDS
+ switch(bc.rounding) {
+ case 0: /* toward 0 */
+ case 3: /* toward -infinity */
+ word0(&rv) = Big0;
+ word1(&rv) = Big1;
+ break;
+ default:
+ word0(&rv) = Exp_mask;
+ word1(&rv) = 0;
+ }
+#else /*Honor_FLT_ROUNDS*/
+ word0(&rv) = Exp_mask;
+ word1(&rv) = 0;
+#endif /*Honor_FLT_ROUNDS*/
+#ifdef SET_INEXACT
+ /* set overflow bit */
+ dval(&rv0) = 1e300;
+ dval(&rv0) *= dval(&rv0);
+#endif
+#else /*IEEE_Arith*/
+ word0(&rv) = Big0;
+ word1(&rv) = Big1;
+#endif /*IEEE_Arith*/
+range_err:
+ if (bd0) {
+ Bfree(bb);
+ Bfree(bd);
+ Bfree(bs);
+ Bfree(bd0);
+ Bfree(delta);
+ }
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+ goto ret;
+ }
+ e1 >>= 4;
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1) {
+ dval(&rv) *= bigtens[j];
+ }
+ /* The last multiplication could overflow. */
+ word0(&rv) -= P*Exp_msk1;
+ dval(&rv) *= bigtens[j];
+ if ((z = word0(&rv) & Exp_mask)
+ > Exp_msk1*(DBL_MAX_EXP+Bias-P)) {
+ goto ovfl;
+ }
+ if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) {
+ /* set to largest number */
+ /* (Can't trust DBL_MAX) */
+ word0(&rv) = Big0;
+ word1(&rv) = Big1;
+ }
+ else {
+ word0(&rv) += P*Exp_msk1;
+ }
+ }
+ }
+ else if (e1 < 0) {
+ e1 = -e1;
+ if ((i = e1 & 15)) {
+ dval(&rv) /= tens[i];
+ }
+ if (e1 >>= 4) {
+ if (e1 >= 1 << n_bigtens) {
+ goto undfl;
+ }
+#ifdef Avoid_Underflow
+ if (e1 & Scale_Bit) {
+ bc.scale = 2*P;
+ }
+ for(j = 0; e1 > 0; j++, e1 >>= 1)
+ if (e1 & 1) {
+ dval(&rv) *= tinytens[j];
+ }
+ if (bc.scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask)
+ >> Exp_shift)) > 0) {
+ /* scaled rv is denormal; clear j low bits */
+ if (j >= 32) {
+ if (j > 54) {
+ goto undfl;
+ }
+ word1(&rv) = 0;
+ if (j >= 53) {
+ word0(&rv) = (P+2)*Exp_msk1;
+ }
+ else {
+ word0(&rv) &= 0xffffffff << (j-32);
+ }
+ }
+ else {
+ word1(&rv) &= 0xffffffff << j;
+ }
+ }
+#else
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1) {
+ dval(&rv) *= tinytens[j];
+ }
+ /* The last multiplication could underflow. */
+ dval(&rv0) = dval(&rv);
+ dval(&rv) *= tinytens[j];
+ if (!dval(&rv)) {
+ dval(&rv) = 2.*dval(&rv0);
+ dval(&rv) *= tinytens[j];
+#endif
+ if (!dval(&rv)) {
+undfl:
+ dval(&rv) = 0.;
+ goto range_err;
+ }
+#ifndef Avoid_Underflow
+ word0(&rv) = Tiny0;
+ word1(&rv) = Tiny1;
+ /* The refinement below will clean
+ * this approximation up.
+ */
+ }
+#endif
+ }
+}
+
+/* Now the hard part -- adjusting rv to the correct value.*/
+
+/* Put digits into bd: true value = bd * 10^e */
+
+bc.nd = nd - nz1;
+#ifndef NO_STRTOD_BIGCOMP
+bc.nd0 = nd0; /* Only needed if nd > strtod_diglim, but done here */
+/* to silence an erroneous warning about bc.nd0 */
+/* possibly not being initialized. */
+if (nd > strtod_diglim) {
+ /* ASSERT(strtod_diglim >= 18); 18 == one more than the */
+ /* minimum number of decimal digits to distinguish double values */
+ /* in IEEE arithmetic. */
+ i = j = 18;
+ if (i > nd0) {
+ j += bc.dplen;
+ }
+ for(;;) {
+ if (--j < bc.dp1 && j >= bc.dp0) {
+ j = bc.dp0 - 1;
+ }
+ if (s0[j] != '0') {
+ break;
+ }
+ --i;
+ }
+ e += nd - i;
+ nd = i;
+ if (nd0 > nd) {
+ nd0 = nd;
+ }
+ if (nd < 9) { /* must recompute y */
+ y = 0;
+ for(i = 0; i < nd0; ++i) {
+ y = 10*y + s0[i] - '0';
+ }
+ for(j = bc.dp1; i < nd; ++i) {
+ y = 10*y + s0[j++] - '0';
+ }
+ }
+}
+#endif
+bd0 = s2b(s0, nd0, nd, y, bc.dplen);
+
+for(;;) {
+ bd = Balloc(bd0->k);
+ Bcopy(bd, bd0);
+ bb = d2b(&rv, &bbe, &bbbits); /* rv = bb * 2^bbe */
+ bs = i2b(1);
+
+ if (e >= 0) {
+ bb2 = bb5 = 0;
+ bd2 = bd5 = e;
+ }
+ else {
+ bb2 = bb5 = -e;
+ bd2 = bd5 = 0;
+ }
+ if (bbe >= 0) {
+ bb2 += bbe;
+ }
+ else {
+ bd2 -= bbe;
+ }
+ bs2 = bb2;
+#ifdef Honor_FLT_ROUNDS
+ if (bc.rounding != 1) {
+ bs2++;
+ }
+#endif
+#ifdef Avoid_Underflow
+ Lsb = LSB;
+ Lsb1 = 0;
+ j = bbe - bc.scale;
+ i = j + bbbits - 1; /* logb(rv) */
+ j = P + 1 - bbbits;
+ if (i < Emin) { /* denormal */
+ i = Emin - i;
+ j -= i;
+ if (i < 32) {
+ Lsb <<= i;
+ }
+ else if (i < 52) {
+ Lsb1 = Lsb << (i-32);
+ }
+ else {
+ Lsb1 = Exp_mask;
+ }
+ }
+#else /*Avoid_Underflow*/
+#ifdef Sudden_Underflow
+#ifdef IBM
+ j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3);
+#else
+ j = P + 1 - bbbits;
+#endif
+#else /*Sudden_Underflow*/
+ j = bbe;
+ i = j + bbbits - 1; /* logb(rv) */
+ if (i < Emin) { /* denormal */
+ j += P - Emin;
+ }
+ else {
+ j = P + 1 - bbbits;
+ }
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ bb2 += j;
+ bd2 += j;
+#ifdef Avoid_Underflow
+ bd2 += bc.scale;
+#endif
+ i = bb2 < bd2 ? bb2 : bd2;
+ if (i > bs2) {
+ i = bs2;
+ }
+ if (i > 0) {
+ bb2 -= i;
+ bd2 -= i;
+ bs2 -= i;
+ }
+ if (bb5 > 0) {
+ bs = pow5mult(bs, bb5);
+ bb1 = mult(bs, bb);
+ Bfree(bb);
+ bb = bb1;
+ }
+ if (bb2 > 0) {
+ bb = lshift(bb, bb2);
+ }
+ if (bd5 > 0) {
+ bd = pow5mult(bd, bd5);
+ }
+ if (bd2 > 0) {
+ bd = lshift(bd, bd2);
+ }
+ if (bs2 > 0) {
+ bs = lshift(bs, bs2);
+ }
+ delta = diff(bb, bd);
+ bc.dsign = delta->sign;
+ delta->sign = 0;
+ i = cmp(delta, bs);
+#ifndef NO_STRTOD_BIGCOMP /*{*/
+ if (bc.nd > nd && i <= 0) {
+ if (bc.dsign) {
+ /* Must use bigcomp(). */
+ req_bigcomp = 1;
+ break;
+ }
+#ifdef Honor_FLT_ROUNDS
+ if (bc.rounding != 1) {
+ if (i < 0) {
+ req_bigcomp = 1;
+ break;
+ }
+ }
+ else
+#endif
+ i = -1; /* Discarded digits make delta smaller. */
+ }
+#endif /*}*/
+#ifdef Honor_FLT_ROUNDS /*{*/
+ if (bc.rounding != 1) {
+ if (i < 0) {
+ /* Error is less than an ulp */
+ if (!delta->x[0] && delta->wds <= 1) {
+ /* exact */
+#ifdef SET_INEXACT
+ bc.inexact = 0;
+#endif
+ break;
+ }
+ if (bc.rounding) {
+ if (bc.dsign) {
+ adj.d = 1.;
+ goto apply_adj;
+ }
+ }
+ else if (!bc.dsign) {
+ adj.d = -1.;
+ if (!word1(&rv)
+ && !(word0(&rv) & Frac_mask)) {
+ y = word0(&rv) & Exp_mask;
+#ifdef Avoid_Underflow
+ if (!bc.scale || y > 2*P*Exp_msk1)
+#else
+ if (y)
+#endif
+ {
+ delta = lshift(delta,Log2P);
+ if (cmp(delta, bs) <= 0) {
+ adj.d = -0.5;
+ }
+ }
+ }
+apply_adj:
+#ifdef Avoid_Underflow /*{*/
+ if (bc.scale && (y = word0(&rv) & Exp_mask)
+ <= 2*P*Exp_msk1) {
+ word0(&adj) += (2*P+1)*Exp_msk1 - y;
+ }
+#else
+#ifdef Sudden_Underflow
+ if ((word0(&rv) & Exp_mask) <=
+ P*Exp_msk1) {
+ word0(&rv) += P*Exp_msk1;
+ dval(&rv) += adj.d*ulp(dval(&rv));
+ word0(&rv) -= P*Exp_msk1;
+ }
+ else
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow}*/
+ dval(&rv) += adj.d*ulp(&rv);
+ }
+ break;
+ }
+ adj.d = ratio(delta, bs);
+ if (adj.d < 1.) {
+ adj.d = 1.;
+ }
+ if (adj.d <= 0x7ffffffe) {
+ /* adj = rounding ? ceil(adj) : floor(adj); */
+ y = adj.d;
+ if (y != adj.d) {
+ if (!((bc.rounding>>1) ^ bc.dsign)) {
+ y++;
+ }
+ adj.d = y;
+ }
+ }
+#ifdef Avoid_Underflow /*{*/
+ if (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) {
+ word0(&adj) += (2*P+1)*Exp_msk1 - y;
+ }
+#else
+#ifdef Sudden_Underflow
+ if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) {
+ word0(&rv) += P*Exp_msk1;
+ adj.d *= ulp(dval(&rv));
+ if (bc.dsign) {
+ dval(&rv) += adj.d;
+ }
+ else {
+ dval(&rv) -= adj.d;
+ }
+ word0(&rv) -= P*Exp_msk1;
+ goto cont;
+ }
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow}*/
+ adj.d *= ulp(&rv);
+ if (bc.dsign) {
+ if (word0(&rv) == Big0 && word1(&rv) == Big1) {
+ goto ovfl;
+ }
+ dval(&rv) += adj.d;
+ }
+ else {
+ dval(&rv) -= adj.d;
+ }
+ goto cont;
+ }
+#endif /*}Honor_FLT_ROUNDS*/
+
+ if (i < 0) {
+ /* Error is less than half an ulp -- check for
+ * special case of mantissa a power of two.
+ */
+ if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask
+#ifdef IEEE_Arith /*{*/
+#ifdef Avoid_Underflow
+ || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1
+#else
+ || (word0(&rv) & Exp_mask) <= Exp_msk1
+#endif
+#endif /*}*/
+ ) {
+#ifdef SET_INEXACT
+ if (!delta->x[0] && delta->wds <= 1) {
+ bc.inexact = 0;
+ }
+#endif
+ break;
+ }
+ if (!delta->x[0] && delta->wds <= 1) {
+ /* exact result */
+#ifdef SET_INEXACT
+ bc.inexact = 0;
+#endif
+ break;
+ }
+ delta = lshift(delta,Log2P);
+ if (cmp(delta, bs) > 0) {
+ goto drop_down;
+ }
+ break;
+ }
+ if (i == 0) {
+ /* exactly half-way between */
+ if (bc.dsign) {
+ if ((word0(&rv) & Bndry_mask1) == Bndry_mask1
+ && word1(&rv) == (
+#ifdef Avoid_Underflow
+ (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1)
+ ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) :
+#endif
+ 0xffffffff)) {
+ /*boundary case -- increment exponent*/
+ if (word0(&rv) == Big0 && word1(&rv) == Big1) {
+ goto ovfl;
+ }
+ word0(&rv) = (word0(&rv) & Exp_mask)
+ + Exp_msk1
+#ifdef IBM
+ | Exp_msk1 >> 4
+#endif
+ ;
+ word1(&rv) = 0;
+#ifdef Avoid_Underflow
+ bc.dsign = 0;
+#endif
+ break;
+ }
+ }
+ else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) {
+drop_down:
+ /* boundary case -- decrement exponent */
+#ifdef Sudden_Underflow /*{{*/
+ L = word0(&rv) & Exp_mask;
+#ifdef IBM
+ if (L < Exp_msk1)
+#else
+#ifdef Avoid_Underflow
+ if (L <= (bc.scale ? (2*P+1)*Exp_msk1 : Exp_msk1))
+#else
+ if (L <= Exp_msk1)
+#endif /*Avoid_Underflow*/
+#endif /*IBM*/
+ {
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+ L -= Exp_msk1;
+#else /*Sudden_Underflow}{*/
+#ifdef Avoid_Underflow
+ if (bc.scale) {
+ L = word0(&rv) & Exp_mask;
+ if (L <= (2*P+1)*Exp_msk1) {
+ if (L > (P+2)*Exp_msk1)
+ /* round even ==> */
+ /* accept rv */
+ {
+ break;
+ }
+ /* rv = smallest denormal */
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+ }
+#endif /*Avoid_Underflow*/
+ L = (word0(&rv) & Exp_mask) - Exp_msk1;
+#endif /*Sudden_Underflow}}*/
+ word0(&rv) = L | Bndry_mask1;
+ word1(&rv) = 0xffffffff;
+#ifdef IBM
+ goto cont;
+#else
+#ifndef NO_STRTOD_BIGCOMP
+ if (bc.nd > nd) {
+ goto cont;
+ }
+#endif
+ break;
+#endif
+ }
+#ifndef ROUND_BIASED
+#ifdef Avoid_Underflow
+ if (Lsb1) {
+ if (!(word0(&rv) & Lsb1)) {
+ break;
+ }
+ }
+ else if (!(word1(&rv) & Lsb)) {
+ break;
+ }
+#else
+ if (!(word1(&rv) & LSB)) {
+ break;
+ }
+#endif
+#endif
+ if (bc.dsign)
+#ifdef Avoid_Underflow
+ dval(&rv) += sulp(&rv, &bc);
+#else
+ dval(&rv) += ulp(&rv);
+#endif
+#ifndef ROUND_BIASED
+ else {
+#ifdef Avoid_Underflow
+ dval(&rv) -= sulp(&rv, &bc);
+#else
+ dval(&rv) -= ulp(&rv);
+#endif
+#ifndef Sudden_Underflow
+ if (!dval(&rv)) {
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+#endif
+ }
+#ifdef Avoid_Underflow
+ bc.dsign = 1 - bc.dsign;
+#endif
+#endif
+ break;
+ }
+ if ((aadj = ratio(delta, bs)) <= 2.) {
+ if (bc.dsign) {
+ aadj = aadj1 = 1.;
+ }
+ else if (word1(&rv) || word0(&rv) & Bndry_mask) {
+#ifndef Sudden_Underflow
+ if (word1(&rv) == Tiny1 && !word0(&rv)) {
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+#endif
+ aadj = 1.;
+ aadj1 = -1.;
+ }
+ else {
+ /* special case -- power of FLT_RADIX to be */
+ /* rounded down... */
+
+ if (aadj < 2./FLT_RADIX) {
+ aadj = 1./FLT_RADIX;
+ }
+ else {
+ aadj *= 0.5;
+ }
+ aadj1 = -aadj;
+ }
+ }
+ else {
+ aadj *= 0.5;
+ aadj1 = bc.dsign ? aadj : -aadj;
+#ifdef Check_FLT_ROUNDS
+ switch(bc.rounding) {
+ case 2: /* towards +infinity */
+ aadj1 -= 0.5;
+ break;
+ case 0: /* towards 0 */
+ case 3: /* towards -infinity */
+ aadj1 += 0.5;
+ }
+#else
+ if (Flt_Rounds == 0) {
+ aadj1 += 0.5;
+ }
+#endif /*Check_FLT_ROUNDS*/
+ }
+ y = word0(&rv) & Exp_mask;
+
+ /* Check for overflow */
+
+ if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) {
+ dval(&rv0) = dval(&rv);
+ word0(&rv) -= P*Exp_msk1;
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+ if ((word0(&rv) & Exp_mask) >=
+ Exp_msk1*(DBL_MAX_EXP+Bias-P)) {
+ if (word0(&rv0) == Big0 && word1(&rv0) == Big1) {
+ goto ovfl;
+ }
+ word0(&rv) = Big0;
+ word1(&rv) = Big1;
+ goto cont;
+ }
+ else {
+ word0(&rv) += P*Exp_msk1;
+ }
+ }
+ else {
+#ifdef Avoid_Underflow
+ if (bc.scale && y <= 2*P*Exp_msk1) {
+ if (aadj <= 0x7fffffff) {
+ if ((z = aadj) <= 0) {
+ z = 1;
+ }
+ aadj = z;
+ aadj1 = bc.dsign ? aadj : -aadj;
+ }
+ dval(&aadj2) = aadj1;
+ word0(&aadj2) += (2*P+1)*Exp_msk1 - y;
+ aadj1 = dval(&aadj2);
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+ if (rv.d == 0.)
+#ifdef NO_STRTOD_BIGCOMP
+ goto undfl;
+#else
+ {
+ if (bc.nd > nd) {
+ bc.dsign = 1;
+ }
+ break;
+ }
+#endif
+ }
+ else {
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+ }
+#else
+#ifdef Sudden_Underflow
+ if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) {
+ dval(&rv0) = dval(&rv);
+ word0(&rv) += P*Exp_msk1;
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+#ifdef IBM
+ if ((word0(&rv) & Exp_mask) < P*Exp_msk1)
+#else
+ if ((word0(&rv) & Exp_mask) <= P*Exp_msk1)
+#endif
+ {
+ if (word0(&rv0) == Tiny0
+ && word1(&rv0) == Tiny1) {
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+ word0(&rv) = Tiny0;
+ word1(&rv) = Tiny1;
+ goto cont;
+ }
+ else {
+ word0(&rv) -= P*Exp_msk1;
+ }
+ }
+ else {
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+ }
+#else /*Sudden_Underflow*/
+ /* Compute adj so that the IEEE rounding rules will
+ * correctly round rv + adj in some half-way cases.
+ * If rv * ulp(rv) is denormalized (i.e.,
+ * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid
+ * trouble from bits lost to denormalization;
+ * example: 1.2e-307 .
+ */
+ if (y <= (P-1)*Exp_msk1 && aadj > 1.) {
+ aadj1 = (double)(int)(aadj + 0.5);
+ if (!bc.dsign) {
+ aadj1 = -aadj1;
+ }
+ }
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ }
+ z = word0(&rv) & Exp_mask;
+#ifndef SET_INEXACT
+ if (bc.nd == nd) {
+#ifdef Avoid_Underflow
+ if (!bc.scale)
+#endif
+ if (y == z) {
+ /* Can we stop now? */
+ L = (Long)aadj;
+ aadj -= L;
+ /* The tolerances below are conservative. */
+ if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask) {
+ if (aadj < .4999999 || aadj > .5000001) {
+ break;
+ }
+ }
+ else if (aadj < .4999999/FLT_RADIX) {
+ break;
+ }
+ }
+ }
+#endif
+cont:
+ Bfree(bb);
+ Bfree(bd);
+ Bfree(bs);
+ Bfree(delta);
+}
+Bfree(bb);
+Bfree(bd);
+Bfree(bs);
+Bfree(bd0);
+Bfree(delta);
+#ifndef NO_STRTOD_BIGCOMP
+if (req_bigcomp) {
+ bd0 = 0;
+ bc.e0 += nz1;
+ bigcomp(&rv, s0, &bc);
+ y = word0(&rv) & Exp_mask;
+ if (y == Exp_mask) {
+ goto ovfl;
+ }
+ if (y == 0 && rv.d == 0.) {
+ goto undfl;
+ }
+}
+#endif
+#ifdef SET_INEXACT
+if (bc.inexact) {
+ if (!oldinexact) {
+ word0(&rv0) = Exp_1 + (70 << Exp_shift);
+ word1(&rv0) = 0;
+ dval(&rv0) += 1.;
+ }
+}
+else if (!oldinexact) {
+ clear_inexact();
+}
+#endif
+#ifdef Avoid_Underflow
+if (bc.scale) {
+ word0(&rv0) = Exp_1 - 2*P*Exp_msk1;
+ word1(&rv0) = 0;
+ dval(&rv) *= dval(&rv0);
+#ifndef NO_ERRNO
+ /* try to avoid the bug of testing an 8087 register value */
+#ifdef IEEE_Arith
+ if (!(word0(&rv) & Exp_mask))
+#else
+ if (word0(&rv) == 0 && word1(&rv) == 0)
+#endif
+ errno = ERANGE;
+#endif
+}
+#endif /* Avoid_Underflow */
+#ifdef SET_INEXACT
+if (bc.inexact && !(word0(&rv) & Exp_mask)) {
+ /* set underflow bit */
+ dval(&rv0) = 1e-300;
+ dval(&rv0) *= dval(&rv0);
+}
+#endif
+ret:
+if (se) {
+ *se = (char *)s;
+}
+return sign ? -dval(&rv) : dval(&rv);
+}
+
+#ifndef MULTIPLE_THREADS
+static char *dtoa_result;
+#endif
+
+static char *
+#ifdef KR_headers
+rv_alloc(i) int i;
+#else
+rv_alloc(int i)
+#endif
+{
+ int j, k, *r;
+
+ j = sizeof(ULong);
+ for(k = 0;
+ sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i;
+ j <<= 1) {
+ k++;
+ }
+ r = (int*)Balloc(k);
+ *r = k;
+ return
+#ifndef MULTIPLE_THREADS
+ dtoa_result =
+#endif
+ (char *)(r+1);
+}
+
+static char *
+#ifdef KR_headers
+nrv_alloc(s, rve, n) char *s, **rve; int n;
+#else
+nrv_alloc(const char *s, char **rve, int n)
+#endif
+{
+ char *rv, *t;
+
+ t = rv = rv_alloc(n);
+ while((*t = *s++)) {
+ t++;
+ }
+ if (rve) {
+ *rve = t;
+ }
+ return rv;
+}
+
+/* freedtoa(s) must be used to free values s returned by dtoa
+ * when MULTIPLE_THREADS is #defined. It should be used in all cases,
+ * but for consistency with earlier versions of dtoa, it is optional
+ * when MULTIPLE_THREADS is not defined.
+ */
+
+void
+#ifdef KR_headers
+freedtoa(s) char *s;
+#else
+freedtoa(char *s)
+#endif
+{
+ Bigint *b = (Bigint *)((int *)s - 1);
+ b->maxwds = 1 << (b->k = *(int*)b);
+ Bfree(b);
+#ifndef MULTIPLE_THREADS
+ if (s == dtoa_result) {
+ dtoa_result = 0;
+ }
+#endif
+}
+
+/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
+ *
+ * Inspired by "How to Print Floating-Point Numbers Accurately" by
+ * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126].
+ *
+ * Modifications:
+ * 1. Rather than iterating, we use a simple numeric overestimate
+ * to determine k = floor(log10(d)). We scale relevant
+ * quantities using O(log2(k)) rather than O(k) multiplications.
+ * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
+ * try to generate digits strictly left to right. Instead, we
+ * compute with fewer bits and propagate the carry if necessary
+ * when rounding the final digit up. This is often faster.
+ * 3. Under the assumption that input will be rounded nearest,
+ * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
+ * That is, we allow equality in stopping tests when the
+ * round-nearest rule will give the same floating-point value
+ * as would satisfaction of the stopping test with strict
+ * inequality.
+ * 4. We remove common factors of powers of 2 from relevant
+ * quantities.
+ * 5. When converting floating-point integers less than 1e16,
+ * we use floating-point arithmetic rather than resorting
+ * to multiple-precision integers.
+ * 6. When asked to produce fewer than 15 digits, we first try
+ * to get by with floating-point arithmetic; we resort to
+ * multiple-precision integer arithmetic only if we cannot
+ * guarantee that the floating-point calculation has given
+ * the correctly rounded result. For k requested digits and
+ * "uniformly" distributed input, the probability is
+ * something like 10^(k-15) that we must resort to the Long
+ * calculation.
+ */
+
+char *
+dtoa
+#ifdef KR_headers
+(dd, mode, ndigits, decpt, sign, rve)
+double dd; int mode, ndigits, *decpt, *sign; char **rve;
+#else
+(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve)
+#endif
+{
+ /* Arguments ndigits, decpt, sign are similar to those
+ of ecvt and fcvt; trailing zeros are suppressed from
+ the returned string. If not null, *rve is set to point
+ to the end of the return value. If d is +-Infinity or NaN,
+ then *decpt is set to 9999.
+
+ mode:
+ 0 ==> shortest string that yields d when read in
+ and rounded to nearest.
+ 1 ==> like 0, but with Steele & White stopping rule;
+ e.g. with IEEE P754 arithmetic , mode 0 gives
+ 1e23 whereas mode 1 gives 9.999999999999999e22.
+ 2 ==> max(1,ndigits) significant digits. This gives a
+ return value similar to that of ecvt, except
+ that trailing zeros are suppressed.
+ 3 ==> through ndigits past the decimal point. This
+ gives a return value similar to that from fcvt,
+ except that trailing zeros are suppressed, and
+ ndigits can be negative.
+ 4,5 ==> similar to 2 and 3, respectively, but (in
+ round-nearest mode) with the tests of mode 0 to
+ possibly return a shorter string that rounds to d.
+ With IEEE arithmetic and compilation with
+ -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same
+ as modes 2 and 3 when FLT_ROUNDS != 1.
+ 6-9 ==> Debugging modes similar to mode - 4: don't try
+ fast floating-point estimate (if applicable).
+
+ Values of mode other than 0-9 are treated as mode 0.
+
+ Sufficient space is allocated to the return value
+ to hold the suppressed trailing zeros.
+ */
+
+ int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1,
+ j, j1, k, k0, k_check, leftright, m2, m5, s2, s5,
+ spec_case, try_quick;
+ Long L;
+#ifndef Sudden_Underflow
+ int denorm;
+ ULong x;
+#endif
+ Bigint *b, *b1, *delta, *mlo, *mhi, *S;
+ U d2, eps, u;
+ double ds;
+ char *s, *s0;
+#ifndef No_leftright
+#ifdef IEEE_Arith
+ U eps1;
+#endif
+#endif
+#ifdef SET_INEXACT
+ int inexact, oldinexact;
+#endif
+#ifdef Honor_FLT_ROUNDS /*{*/
+ int Rounding;
+#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */
+ Rounding = Flt_Rounds;
+#else /*}{*/
+ Rounding = 1;
+ switch(fegetround()) {
+ case FE_TOWARDZERO: Rounding = 0; break;
+ case FE_UPWARD: Rounding = 2; break;
+ case FE_DOWNWARD: Rounding = 3;
+ }
+#endif /*}}*/
+#endif /*}*/
+
+#ifndef MULTIPLE_THREADS
+ if (dtoa_result) {
+ freedtoa(dtoa_result);
+ dtoa_result = 0;
+ }
+#endif
+
+ u.d = dd;
+ if (word0(&u) & Sign_bit) {
+ /* set sign for everything, including 0's and NaNs */
+ *sign = 1;
+ word0(&u) &= ~Sign_bit; /* clear sign bit */
+ }
+ else {
+ *sign = 0;
+ }
+
+#if defined(IEEE_Arith) + defined(VAX)
+#ifdef IEEE_Arith
+ if ((word0(&u) & Exp_mask) == Exp_mask)
+#else
+ if (word0(&u) == 0x8000)
+#endif
+ {
+ /* Infinity or NaN */
+ *decpt = 9999;
+#ifdef IEEE_Arith
+ if (!word1(&u) && !(word0(&u) & 0xfffff)) {
+ return nrv_alloc("Infinity", rve, 8);
+ }
+#endif
+ return nrv_alloc("NaN", rve, 3);
+ }
+#endif
+#ifdef IBM
+ dval(&u) += 0; /* normalize */
+#endif
+ if (!dval(&u)) {
+ *decpt = 1;
+ return nrv_alloc("0", rve, 1);
+ }
+
+#ifdef SET_INEXACT
+ try_quick = oldinexact = get_inexact();
+ inexact = 1;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ if (Rounding >= 2) {
+ if (*sign) {
+ Rounding = Rounding == 2 ? 0 : 2;
+ }
+ else if (Rounding != 2) {
+ Rounding = 0;
+ }
+ }
+#endif
+
+ b = d2b(&u, &be, &bbits);
+#ifdef Sudden_Underflow
+ i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1));
+#else
+ if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) {
+#endif
+ dval(&d2) = dval(&u);
+ word0(&d2) &= Frac_mask1;
+ word0(&d2) |= Exp_11;
+#ifdef IBM
+ if (j = 11 - hi0bits(word0(&d2) & Frac_mask)) {
+ dval(&d2) /= 1 << j;
+ }
+#endif
+
+ /* log(x) ~=~ log(1.5) + (x-1.5)/1.5
+ * log10(x) = log(x) / log(10)
+ * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
+ * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)
+ *
+ * This suggests computing an approximation k to log10(d) by
+ *
+ * k = (i - Bias)*0.301029995663981
+ * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
+ *
+ * We want k to be too large rather than too small.
+ * The error in the first-order Taylor series approximation
+ * is in our favor, so we just round up the constant enough
+ * to compensate for any error in the multiplication of
+ * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,
+ * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
+ * adding 1e-13 to the constant term more than suffices.
+ * Hence we adjust the constant term to 0.1760912590558.
+ * (We could get a more accurate k by invoking log10,
+ * but this is probably not worthwhile.)
+ */
+
+ i -= Bias;
+#ifdef IBM
+ i <<= 2;
+ i += j;
+#endif
+#ifndef Sudden_Underflow
+ denorm = 0;
+}
+else {
+ /* d is denormalized */
+
+ i = bbits + be + (Bias + (P-1) - 1);
+ x = i > 32 ? word0(&u) << (64 - i) | word1(&u) >> (i - 32)
+ : word1(&u) << (32 - i);
+ dval(&d2) = x;
+ word0(&d2) -= 31*Exp_msk1; /* adjust exponent */
+ i -= (Bias + (P-1) - 1) + 1;
+ denorm = 1;
+}
+#endif
+ds = (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
+k = (int)ds;
+if (ds < 0. && ds != k) {
+ k--; /* want k = floor(ds) */
+}
+k_check = 1;
+if (k >= 0 && k <= Ten_pmax) {
+ if (dval(&u) < tens[k]) {
+ k--;
+ }
+ k_check = 0;
+}
+j = bbits - i - 1;
+if (j >= 0) {
+ b2 = 0;
+ s2 = j;
+}
+else {
+ b2 = -j;
+ s2 = 0;
+}
+if (k >= 0) {
+ b5 = 0;
+ s5 = k;
+ s2 += k;
+}
+else {
+ b2 -= k;
+ b5 = -k;
+ s5 = 0;
+}
+if (mode < 0 || mode > 9) {
+ mode = 0;
+}
+
+#ifndef SET_INEXACT
+#ifdef Check_FLT_ROUNDS
+try_quick = Rounding == 1;
+#else
+try_quick = 1;
+#endif
+#endif /*SET_INEXACT*/
+
+if (mode > 5) {
+ mode -= 4;
+ try_quick = 0;
+}
+leftright = 1;
+ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */
+/* silence erroneous "gcc -Wall" warning. */
+switch(mode) {
+case 0:
+case 1:
+ i = 18;
+ ndigits = 0;
+ break;
+case 2:
+ leftright = 0;
+/* no break */
+case 4:
+ if (ndigits <= 0) {
+ ndigits = 1;
+ }
+ ilim = ilim1 = i = ndigits;
+ break;
+case 3:
+ leftright = 0;
+/* no break */
+case 5:
+ i = ndigits + k + 1;
+ ilim = i;
+ ilim1 = i - 1;
+ if (i <= 0) {
+ i = 1;
+ }
+}
+s = s0 = rv_alloc(i);
+
+#ifdef Honor_FLT_ROUNDS
+if (mode > 1 && Rounding != 1) {
+ leftright = 0;
+}
+#endif
+
+if (ilim >= 0 && ilim <= Quick_max && try_quick) {
+
+ /* Try to get by with floating-point arithmetic. */
+
+ i = 0;
+ dval(&d2) = dval(&u);
+ k0 = k;
+ ilim0 = ilim;
+ ieps = 2; /* conservative */
+ if (k > 0) {
+ ds = tens[k&0xf];
+ j = k >> 4;
+ if (j & Bletch) {
+ /* prevent overflows */
+ j &= Bletch - 1;
+ dval(&u) /= bigtens[n_bigtens-1];
+ ieps++;
+ }
+ for(; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ ds *= bigtens[i];
+ }
+ dval(&u) /= ds;
+ }
+ else if ((j1 = -k)) {
+ dval(&u) *= tens[j1 & 0xf];
+ for(j = j1 >> 4; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ dval(&u) *= bigtens[i];
+ }
+ }
+ if (k_check && dval(&u) < 1. && ilim > 0) {
+ if (ilim1 <= 0) {
+ goto fast_failed;
+ }
+ ilim = ilim1;
+ k--;
+ dval(&u) *= 10.;
+ ieps++;
+ }
+ dval(&eps) = ieps*dval(&u) + 7.;
+ word0(&eps) -= (P-1)*Exp_msk1;
+ if (ilim == 0) {
+ S = mhi = 0;
+ dval(&u) -= 5.;
+ if (dval(&u) > dval(&eps)) {
+ goto one_digit;
+ }
+ if (dval(&u) < -dval(&eps)) {
+ goto no_digits;
+ }
+ goto fast_failed;
+ }
+#ifndef No_leftright
+ if (leftright) {
+ /* Use Steele & White method of only
+ * generating digits needed.
+ */
+ dval(&eps) = 0.5/tens[ilim-1] - dval(&eps);
+#ifdef IEEE_Arith
+ if (k0 < 0 && j1 >= 307) {
+ eps1.d = 1.01e256; /* 1.01 allows roundoff in the next few lines */
+ word0(&eps1) -= Exp_msk1 * (Bias+P-1);
+ dval(&eps1) *= tens[j1 & 0xf];
+ for(i = 0, j = (j1-256) >> 4; j; j >>= 1, i++)
+ if (j & 1) {
+ dval(&eps1) *= bigtens[i];
+ }
+ if (eps.d < eps1.d) {
+ eps.d = eps1.d;
+ }
+ }
+#endif
+ for(i = 0;;) {
+ L = dval(&u);
+ dval(&u) -= L;
+ *s++ = '0' + (int)L;
+ if (1. - dval(&u) < dval(&eps)) {
+ goto bump_up;
+ }
+ if (dval(&u) < dval(&eps)) {
+ goto ret1;
+ }
+ if (++i >= ilim) {
+ break;
+ }
+ dval(&eps) *= 10.;
+ dval(&u) *= 10.;
+ }
+ }
+ else {
+#endif
+ /* Generate ilim digits, then fix them up. */
+ dval(&eps) *= tens[ilim-1];
+ for(i = 1;; i++, dval(&u) *= 10.) {
+ L = (Long)(dval(&u));
+ if (!(dval(&u) -= L)) {
+ ilim = i;
+ }
+ *s++ = '0' + (int)L;
+ if (i == ilim) {
+ if (dval(&u) > 0.5 + dval(&eps)) {
+ goto bump_up;
+ }
+ else if (dval(&u) < 0.5 - dval(&eps)) {
+ while(*--s == '0');
+ s++;
+ goto ret1;
+ }
+ break;
+ }
+ }
+#ifndef No_leftright
+ }
+#endif
+fast_failed:
+ s = s0;
+ dval(&u) = dval(&d2);
+ k = k0;
+ ilim = ilim0;
+}
+
+/* Do we have a "small" integer? */
+
+if (be >= 0 && k <= Int_max) {
+ /* Yes. */
+ ds = tens[k];
+ if (ndigits < 0 && ilim <= 0) {
+ S = mhi = 0;
+ if (ilim < 0 || dval(&u) <= 5*ds) {
+ goto no_digits;
+ }
+ goto one_digit;
+ }
+ for(i = 1;; i++, dval(&u) *= 10.) {
+ L = (Long)(dval(&u) / ds);
+ dval(&u) -= L*ds;
+#ifdef Check_FLT_ROUNDS
+ /* If FLT_ROUNDS == 2, L will usually be high by 1 */
+ if (dval(&u) < 0) {
+ L--;
+ dval(&u) += ds;
+ }
+#endif
+ *s++ = '0' + (int)L;
+ if (!dval(&u)) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ break;
+ }
+ if (i == ilim) {
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch(Rounding) {
+ case 0: goto ret1;
+ case 2: goto bump_up;
+ }
+#endif
+ dval(&u) += dval(&u);
+#ifdef ROUND_BIASED
+ if (dval(&u) >= ds)
+#else
+ if (dval(&u) > ds || (dval(&u) == ds && L & 1))
+#endif
+ {
+bump_up:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s = '0';
+ break;
+ }
+ ++*s++;
+ }
+ break;
+ }
+ }
+ goto ret1;
+}
+
+m2 = b2;
+m5 = b5;
+mhi = mlo = 0;
+if (leftright) {
+ i =
+#ifndef Sudden_Underflow
+ denorm ? be + (Bias + (P-1) - 1 + 1) :
+#endif
+#ifdef IBM
+ 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3);
+#else
+ 1 + P - bbits;
+#endif
+ b2 += i;
+ s2 += i;
+ mhi = i2b(1);
+}
+if (m2 > 0 && s2 > 0) {
+ i = m2 < s2 ? m2 : s2;
+ b2 -= i;
+ m2 -= i;
+ s2 -= i;
+}
+if (b5 > 0) {
+ if (leftright) {
+ if (m5 > 0) {
+ mhi = pow5mult(mhi, m5);
+ b1 = mult(mhi, b);
+ Bfree(b);
+ b = b1;
+ }
+ if ((j = b5 - m5)) {
+ b = pow5mult(b, j);
+ }
+ }
+ else {
+ b = pow5mult(b, b5);
+ }
+}
+S = i2b(1);
+if (s5 > 0) {
+ S = pow5mult(S, s5);
+}
+
+/* Check for special case that d is a normalized power of 2. */
+
+spec_case = 0;
+if ((mode < 2 || leftright)
+#ifdef Honor_FLT_ROUNDS
+ && Rounding == 1
+#endif
+ ) {
+ if (!word1(&u) && !(word0(&u) & Bndry_mask)
+#ifndef Sudden_Underflow
+ && word0(&u) & (Exp_mask & ~Exp_msk1)
+#endif
+ ) {
+ /* The special case */
+ b2 += Log2P;
+ s2 += Log2P;
+ spec_case = 1;
+ }
+}
+
+/* Arrange for convenient computation of quotients:
+ * shift left if necessary so divisor has 4 leading 0 bits.
+ *
+ * Perhaps we should just compute leading 28 bits of S once
+ * and for all and pass them and a shift to quorem, so it
+ * can do shifts and ors to compute the numerator for q.
+ */
+i = dshift(S, s2);
+b2 += i;
+m2 += i;
+s2 += i;
+if (b2 > 0) {
+ b = lshift(b, b2);
+}
+if (s2 > 0) {
+ S = lshift(S, s2);
+}
+if (k_check) {
+ if (cmp(b,S) < 0) {
+ k--;
+ b = multadd(b, 10, 0); /* we botched the k estimate */
+ if (leftright) {
+ mhi = multadd(mhi, 10, 0);
+ }
+ ilim = ilim1;
+ }
+}
+if (ilim <= 0 && (mode == 3 || mode == 5)) {
+ if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) {
+ /* no digits, fcvt style */
+no_digits:
+ k = -1 - ndigits;
+ goto ret;
+ }
+one_digit:
+ *s++ = '1';
+ k++;
+ goto ret;
+}
+if (leftright) {
+ if (m2 > 0) {
+ mhi = lshift(mhi, m2);
+ }
+
+ /* Compute mlo -- check for special case
+ * that d is a normalized power of 2.
+ */
+
+ mlo = mhi;
+ if (spec_case) {
+ mhi = Balloc(mhi->k);
+ Bcopy(mhi, mlo);
+ mhi = lshift(mhi, Log2P);
+ }
+
+ for(i = 1;; i++) {
+ dig = quorem(b,S) + '0';
+ /* Do we yet have the shortest decimal string
+ * that will round to d?
+ */
+ j = cmp(b, mlo);
+ delta = diff(S, mhi);
+ j1 = delta->sign ? 1 : cmp(b, delta);
+ Bfree(delta);
+#ifndef ROUND_BIASED
+ if (j1 == 0 && mode != 1 && !(word1(&u) & 1)
+#ifdef Honor_FLT_ROUNDS
+ && Rounding >= 1
+#endif
+ ) {
+ if (dig == '9') {
+ goto round_9_up;
+ }
+ if (j > 0) {
+ dig++;
+ }
+#ifdef SET_INEXACT
+ else if (!b->x[0] && b->wds <= 1) {
+ inexact = 0;
+ }
+#endif
+ *s++ = dig;
+ goto ret;
+ }
+#endif
+ if (j < 0 || (j == 0 && mode != 1
+#ifndef ROUND_BIASED
+ && !(word1(&u) & 1)
+#endif
+ )) {
+ if (!b->x[0] && b->wds <= 1) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ goto accept_dig;
+ }
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch(Rounding) {
+ case 0: goto accept_dig;
+ case 2: goto keep_dig;
+ }
+#endif /*Honor_FLT_ROUNDS*/
+ if (j1 > 0) {
+ b = lshift(b, 1);
+ j1 = cmp(b, S);
+#ifdef ROUND_BIASED
+ if (j1 >= 0 /*)*/
+#else
+ if ((j1 > 0 || (j1 == 0 && dig & 1))
+#endif
+ && dig++ == '9')
+ goto round_9_up;
+ }
+accept_dig:
+ *s++ = dig;
+ goto ret;
+ }
+ if (j1 > 0) {
+#ifdef Honor_FLT_ROUNDS
+ if (!Rounding) {
+ goto accept_dig;
+ }
+#endif
+ if (dig == '9') { /* possible if i == 1 */
+round_9_up:
+ *s++ = '9';
+ goto roundoff;
+ }
+ *s++ = dig + 1;
+ goto ret;
+ }
+#ifdef Honor_FLT_ROUNDS
+keep_dig:
+#endif
+ *s++ = dig;
+ if (i == ilim) {
+ break;
+ }
+ b = multadd(b, 10, 0);
+ if (mlo == mhi) {
+ mlo = mhi = multadd(mhi, 10, 0);
+ }
+ else {
+ mlo = multadd(mlo, 10, 0);
+ mhi = multadd(mhi, 10, 0);
+ }
+ }
+}
+else
+ for(i = 1;; i++) {
+ *s++ = dig = quorem(b,S) + '0';
+ if (!b->x[0] && b->wds <= 1) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ goto ret;
+ }
+ if (i >= ilim) {
+ break;
+ }
+ b = multadd(b, 10, 0);
+ }
+
+/* Round off last digit */
+
+#ifdef Honor_FLT_ROUNDS
+switch(Rounding) {
+case 0: goto trimzeros;
+case 2: goto roundoff;
+}
+#endif
+b = lshift(b, 1);
+j = cmp(b, S);
+#ifdef ROUND_BIASED
+if (j >= 0)
+#else
+if (j > 0 || (j == 0 && dig & 1))
+#endif
+{
+roundoff:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s++ = '1';
+ goto ret;
+ }
+ ++*s++;
+}
+else {
+#ifdef Honor_FLT_ROUNDS
+trimzeros:
+#endif
+ while(*--s == '0');
+ s++;
+}
+ret:
+Bfree(S);
+if (mhi) {
+ if (mlo && mlo != mhi) {
+ Bfree(mlo);
+ }
+ Bfree(mhi);
+}
+ret1:
+#ifdef SET_INEXACT
+if (inexact) {
+ if (!oldinexact) {
+ word0(&u) = Exp_1 + (70 << Exp_shift);
+ word1(&u) = 0;
+ dval(&u) += 1.;
+ }
+}
+else if (!oldinexact) {
+ clear_inexact();
+}
+#endif
+Bfree(b);
+*s = 0;
+*decpt = k + 1;
+if (rve) {
+ *rve = s;
+}
+return s0;
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/nsprpub/pr/src/misc/pralarm.c b/nsprpub/pr/src/misc/pralarm.c
new file mode 100644
index 0000000000..8130215c24
--- /dev/null
+++ b/nsprpub/pr/src/misc/pralarm.c
@@ -0,0 +1,263 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+/**********************************************************************/
+/******************************* PRALARM ******************************/
+/**********************************************************************/
+
+#include "obsolete/pralarm.h"
+
+struct PRAlarmID { /* typedef'd in pralarm.h */
+ PRCList list; /* circular list linkage */
+ PRAlarm *alarm; /* back pointer to owning alarm */
+ PRPeriodicAlarmFn function; /* function to call for notify */
+ void *clientData; /* opaque client context */
+ PRIntervalTime period; /* the client defined period */
+ PRUint32 rate; /* rate of notification */
+
+ PRUint32 accumulator; /* keeps track of # notifies */
+ PRIntervalTime epoch; /* when timer was started */
+ PRIntervalTime nextNotify; /* when we'll next do our thing */
+ PRIntervalTime lastNotify; /* when we last did our thing */
+};
+
+typedef enum {alarm_active, alarm_inactive} _AlarmState;
+
+struct PRAlarm { /* typedef'd in pralarm.h */
+ PRCList timers; /* base of alarm ids list */
+ PRLock *lock; /* lock used to protect data */
+ PRCondVar *cond; /* condition that used to wait */
+ PRThread *notifier; /* thread to deliver notifies */
+ PRAlarmID *current; /* current alarm being served */
+ _AlarmState state; /* used to delete the alarm */
+};
+
+static PRAlarmID *pr_getNextAlarm(PRAlarm *alarm, PRAlarmID *id)
+{
+ /*
+ * Puts 'id' back into the sorted list iff it's not NULL.
+ * Removes the first element from the list and returns it (or NULL).
+ * List is "assumed" to be short.
+ *
+ * NB: Caller is providing locking
+ */
+ PRCList *timer;
+ PRAlarmID *result = id;
+ PRIntervalTime now = PR_IntervalNow();
+
+ if (!PR_CLIST_IS_EMPTY(&alarm->timers))
+ {
+ if (id != NULL) /* have to put this id back in */
+ {
+ PRIntervalTime idDelta = now - id->nextNotify;
+ timer = alarm->timers.next;
+ do
+ {
+ result = (PRAlarmID*)timer;
+ if ((PRIntervalTime)(now - result->nextNotify) > idDelta)
+ {
+ PR_INSERT_BEFORE(&id->list, &alarm->timers);
+ break;
+ }
+ timer = timer->next;
+ } while (timer != &alarm->timers);
+ }
+ result = (PRAlarmID*)(timer = PR_LIST_HEAD(&alarm->timers));
+ PR_REMOVE_LINK(timer); /* remove it from the list */
+ }
+
+ return result;
+} /* pr_getNextAlarm */
+
+static PRIntervalTime pr_PredictNextNotifyTime(PRAlarmID *id)
+{
+ PRIntervalTime delta;
+ PRFloat64 baseRate = (PRFloat64)id->period / (PRFloat64)id->rate;
+ PRFloat64 offsetFromEpoch = (PRFloat64)id->accumulator * baseRate;
+
+ id->accumulator += 1; /* every call advances to next period */
+ id->lastNotify = id->nextNotify; /* just keeping track of things */
+ id->nextNotify = (PRIntervalTime)(offsetFromEpoch + 0.5);
+
+ delta = id->nextNotify - id->lastNotify;
+ return delta;
+} /* pr_PredictNextNotifyTime */
+
+static void PR_CALLBACK pr_alarmNotifier(void *arg)
+{
+ /*
+ * This is the root of the notifier thread. There is one such thread
+ * for each PRAlarm. It may service an arbitrary (though assumed to be
+ * small) number of alarms using the same thread and structure. It
+ * continues to run until the alarm is destroyed.
+ */
+ PRAlarmID *id = NULL;
+ PRAlarm *alarm = (PRAlarm*)arg;
+ enum {notify, abort, scan} why = scan;
+
+ while (why != abort)
+ {
+ PRIntervalTime pause;
+
+ PR_Lock(alarm->lock);
+ while (why == scan)
+ {
+ alarm->current = NULL; /* reset current id */
+ if (alarm->state == alarm_inactive) {
+ why = abort; /* we're toast */
+ }
+ else if (why == scan) /* the dominant case */
+ {
+ id = pr_getNextAlarm(alarm, id); /* even if it's the same */
+ if (id == NULL) { /* there are no alarms set */
+ (void)PR_WaitCondVar(alarm->cond, PR_INTERVAL_NO_TIMEOUT);
+ }
+ else
+ {
+ pause = id->nextNotify - (PR_IntervalNow() - id->epoch);
+ if ((PRInt32)pause <= 0) /* is this one's time up? */
+ {
+ why = notify; /* set up to do our thing */
+ alarm->current = id; /* id we're about to schedule */
+ }
+ else {
+ (void)PR_WaitCondVar(alarm->cond, pause); /* dally */
+ }
+ }
+ }
+ }
+ PR_Unlock(alarm->lock);
+
+ if (why == notify)
+ {
+ (void)pr_PredictNextNotifyTime(id);
+ if (!id->function(id, id->clientData, ~pause))
+ {
+ /*
+ * Notified function decided not to continue. Free
+ * the alarm id to make sure it doesn't get back on
+ * the list.
+ */
+ PR_DELETE(id); /* free notifier object */
+ id = NULL; /* so it doesn't get back into the list */
+ }
+ why = scan; /* so we can cycle through the loop again */
+ }
+ }
+
+} /* pr_alarm_notifier */
+
+PR_IMPLEMENT(PRAlarm*) PR_CreateAlarm(void)
+{
+ PRAlarm *alarm = PR_NEWZAP(PRAlarm);
+ if (alarm != NULL)
+ {
+ if ((alarm->lock = PR_NewLock()) == NULL) {
+ goto done;
+ }
+ if ((alarm->cond = PR_NewCondVar(alarm->lock)) == NULL) {
+ goto done;
+ }
+ alarm->state = alarm_active;
+ PR_INIT_CLIST(&alarm->timers);
+ alarm->notifier = PR_CreateThread(
+ PR_USER_THREAD, pr_alarmNotifier, alarm,
+ PR_GetThreadPriority(PR_GetCurrentThread()),
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (alarm->notifier == NULL) {
+ goto done;
+ }
+ }
+ return alarm;
+
+done:
+ if (alarm->cond != NULL) {
+ PR_DestroyCondVar(alarm->cond);
+ }
+ if (alarm->lock != NULL) {
+ PR_DestroyLock(alarm->lock);
+ }
+ PR_DELETE(alarm);
+ return NULL;
+} /* CreateAlarm */
+
+PR_IMPLEMENT(PRStatus) PR_DestroyAlarm(PRAlarm *alarm)
+{
+ PRStatus rv;
+
+ PR_Lock(alarm->lock);
+ alarm->state = alarm_inactive;
+ rv = PR_NotifyCondVar(alarm->cond);
+ PR_Unlock(alarm->lock);
+
+ if (rv == PR_SUCCESS) {
+ rv = PR_JoinThread(alarm->notifier);
+ }
+ if (rv == PR_SUCCESS)
+ {
+ PR_DestroyCondVar(alarm->cond);
+ PR_DestroyLock(alarm->lock);
+ PR_DELETE(alarm);
+ }
+ return rv;
+} /* PR_DestroyAlarm */
+
+PR_IMPLEMENT(PRAlarmID*) PR_SetAlarm(
+ PRAlarm *alarm, PRIntervalTime period, PRUint32 rate,
+ PRPeriodicAlarmFn function, void *clientData)
+{
+ /*
+ * Create a new periodic alarm an existing current structure.
+ * Set up the context and compute the first notify time (immediate).
+ * Link the new ID into the head of the list (since it's notifying
+ * immediately).
+ */
+
+ PRAlarmID *id = PR_NEWZAP(PRAlarmID);
+
+ if (!id) {
+ return NULL;
+ }
+
+ id->alarm = alarm;
+ PR_INIT_CLIST(&id->list);
+ id->function = function;
+ id->clientData = clientData;
+ id->period = period;
+ id->rate = rate;
+ id->epoch = id->nextNotify = PR_IntervalNow();
+ (void)pr_PredictNextNotifyTime(id);
+
+ PR_Lock(alarm->lock);
+ PR_INSERT_BEFORE(&id->list, &alarm->timers);
+ PR_NotifyCondVar(alarm->cond);
+ PR_Unlock(alarm->lock);
+
+ return id;
+} /* PR_SetAlarm */
+
+PR_IMPLEMENT(PRStatus) PR_ResetAlarm(
+ PRAlarmID *id, PRIntervalTime period, PRUint32 rate)
+{
+ /*
+ * Can only be called from within the notify routine. Doesn't
+ * need locking because it can only be called from within the
+ * notify routine.
+ */
+ if (id != id->alarm->current) {
+ return PR_FAILURE;
+ }
+ id->period = period;
+ id->rate = rate;
+ id->accumulator = 1;
+ id->epoch = PR_IntervalNow();
+ (void)pr_PredictNextNotifyTime(id);
+ return PR_SUCCESS;
+} /* PR_ResetAlarm */
+
+
+
diff --git a/nsprpub/pr/src/misc/pratom.c b/nsprpub/pr/src/misc/pratom.c
new file mode 100644
index 0000000000..4f0e3da303
--- /dev/null
+++ b/nsprpub/pr/src/misc/pratom.c
@@ -0,0 +1,384 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** PR Atomic operations
+*/
+
+
+#include "pratom.h"
+#include "primpl.h"
+
+#include <string.h>
+
+/*
+ * The following is a fallback implementation that emulates
+ * atomic operations for platforms without atomic operations.
+ * If a platform has atomic operations, it should define the
+ * macro _PR_HAVE_ATOMIC_OPS, and the following will not be
+ * compiled in.
+ */
+
+#if !defined(_PR_HAVE_ATOMIC_OPS)
+
+#if defined(_PR_PTHREADS)
+/*
+ * PR_AtomicDecrement() is used in NSPR's thread-specific data
+ * destructor. Because thread-specific data destructors may be
+ * invoked after a PR_Cleanup() call, we need an implementation
+ * of the atomic routines that doesn't need NSPR to be initialized.
+ */
+
+/*
+ * We use a set of locks for all the emulated atomic operations.
+ * By hashing on the address of the integer to be locked the
+ * contention between multiple threads should be lessened.
+ *
+ * The number of atomic locks can be set by the environment variable
+ * NSPR_ATOMIC_HASH_LOCKS
+ */
+
+/*
+ * lock counts should be a power of 2
+ */
+#define DEFAULT_ATOMIC_LOCKS 16 /* should be in sync with the number of initializers
+ below */
+#define MAX_ATOMIC_LOCKS (4 * 1024)
+
+static pthread_mutex_t static_atomic_locks[DEFAULT_ATOMIC_LOCKS] = {
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER
+};
+
+#ifdef DEBUG
+static PRInt32 static_hash_lock_counts[DEFAULT_ATOMIC_LOCKS];
+static PRInt32 *hash_lock_counts = static_hash_lock_counts;
+#endif
+
+static PRUint32 num_atomic_locks = DEFAULT_ATOMIC_LOCKS;
+static pthread_mutex_t *atomic_locks = static_atomic_locks;
+static PRUint32 atomic_hash_mask = DEFAULT_ATOMIC_LOCKS - 1;
+
+#define _PR_HASH_FOR_LOCK(ptr) \
+ ((PRUint32) (((PRUptrdiff) (ptr) >> 2) ^ \
+ ((PRUptrdiff) (ptr) >> 8)) & \
+ atomic_hash_mask)
+
+void _PR_MD_INIT_ATOMIC()
+{
+ char *eval;
+ int index;
+
+
+ PR_ASSERT(PR_FloorLog2(MAX_ATOMIC_LOCKS) ==
+ PR_CeilingLog2(MAX_ATOMIC_LOCKS));
+
+ PR_ASSERT(PR_FloorLog2(DEFAULT_ATOMIC_LOCKS) ==
+ PR_CeilingLog2(DEFAULT_ATOMIC_LOCKS));
+
+ if (((eval = getenv("NSPR_ATOMIC_HASH_LOCKS")) != NULL) &&
+ ((num_atomic_locks = atoi(eval)) != DEFAULT_ATOMIC_LOCKS)) {
+
+ if (num_atomic_locks > MAX_ATOMIC_LOCKS) {
+ num_atomic_locks = MAX_ATOMIC_LOCKS;
+ }
+ else if (num_atomic_locks < 1) {
+ num_atomic_locks = 1;
+ }
+ else {
+ num_atomic_locks = PR_FloorLog2(num_atomic_locks);
+ num_atomic_locks = 1L << num_atomic_locks;
+ }
+ atomic_locks = (pthread_mutex_t *) PR_Malloc(sizeof(pthread_mutex_t) *
+ num_atomic_locks);
+ if (atomic_locks) {
+ for (index = 0; index < num_atomic_locks; index++) {
+ if (pthread_mutex_init(&atomic_locks[index], NULL)) {
+ PR_DELETE(atomic_locks);
+ atomic_locks = NULL;
+ break;
+ }
+ }
+ }
+#ifdef DEBUG
+ if (atomic_locks) {
+ hash_lock_counts = PR_CALLOC(num_atomic_locks * sizeof(PRInt32));
+ if (hash_lock_counts == NULL) {
+ PR_DELETE(atomic_locks);
+ atomic_locks = NULL;
+ }
+ }
+#endif
+ if (atomic_locks == NULL) {
+ /*
+ * Use statically allocated locks
+ */
+ atomic_locks = static_atomic_locks;
+ num_atomic_locks = DEFAULT_ATOMIC_LOCKS;
+#ifdef DEBUG
+ hash_lock_counts = static_hash_lock_counts;
+#endif
+ }
+ atomic_hash_mask = num_atomic_locks - 1;
+ }
+ PR_ASSERT(PR_FloorLog2(num_atomic_locks) ==
+ PR_CeilingLog2(num_atomic_locks));
+}
+
+PRInt32
+_PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ PRInt32 rv;
+ PRInt32 idx = _PR_HASH_FOR_LOCK(val);
+
+ pthread_mutex_lock(&atomic_locks[idx]);
+ rv = ++(*val);
+#ifdef DEBUG
+ hash_lock_counts[idx]++;
+#endif
+ pthread_mutex_unlock(&atomic_locks[idx]);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
+{
+ PRInt32 rv;
+ PRInt32 idx = _PR_HASH_FOR_LOCK(ptr);
+
+ pthread_mutex_lock(&atomic_locks[idx]);
+ rv = ((*ptr) += val);
+#ifdef DEBUG
+ hash_lock_counts[idx]++;
+#endif
+ pthread_mutex_unlock(&atomic_locks[idx]);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ PRInt32 rv;
+ PRInt32 idx = _PR_HASH_FOR_LOCK(val);
+
+ pthread_mutex_lock(&atomic_locks[idx]);
+ rv = --(*val);
+#ifdef DEBUG
+ hash_lock_counts[idx]++;
+#endif
+ pthread_mutex_unlock(&atomic_locks[idx]);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ PRInt32 rv;
+ PRInt32 idx = _PR_HASH_FOR_LOCK(val);
+
+ pthread_mutex_lock(&atomic_locks[idx]);
+ rv = *val;
+ *val = newval;
+#ifdef DEBUG
+ hash_lock_counts[idx]++;
+#endif
+ pthread_mutex_unlock(&atomic_locks[idx]);
+ return rv;
+}
+#else /* _PR_PTHREADS */
+/*
+ * We use a single lock for all the emulated atomic operations.
+ * The lock contention should be acceptable.
+ */
+static PRLock *atomic_lock = NULL;
+void _PR_MD_INIT_ATOMIC(void)
+{
+ if (atomic_lock == NULL) {
+ atomic_lock = PR_NewLock();
+ }
+}
+
+PRInt32
+_PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_Lock(atomic_lock);
+ rv = ++(*val);
+ PR_Unlock(atomic_lock);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_Lock(atomic_lock);
+ rv = ((*ptr) += val);
+ PR_Unlock(atomic_lock);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_Lock(atomic_lock);
+ rv = --(*val);
+ PR_Unlock(atomic_lock);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_Lock(atomic_lock);
+ rv = *val;
+ *val = newval;
+ PR_Unlock(atomic_lock);
+ return rv;
+}
+#endif /* _PR_PTHREADS */
+
+#endif /* !_PR_HAVE_ATOMIC_OPS */
+
+void _PR_InitAtomic(void)
+{
+ _PR_MD_INIT_ATOMIC();
+}
+
+PR_IMPLEMENT(PRInt32)
+PR_AtomicIncrement(PRInt32 *val)
+{
+ return _PR_MD_ATOMIC_INCREMENT(val);
+}
+
+PR_IMPLEMENT(PRInt32)
+PR_AtomicDecrement(PRInt32 *val)
+{
+ return _PR_MD_ATOMIC_DECREMENT(val);
+}
+
+PR_IMPLEMENT(PRInt32)
+PR_AtomicSet(PRInt32 *val, PRInt32 newval)
+{
+ return _PR_MD_ATOMIC_SET(val, newval);
+}
+
+PR_IMPLEMENT(PRInt32)
+PR_AtomicAdd(PRInt32 *ptr, PRInt32 val)
+{
+ return _PR_MD_ATOMIC_ADD(ptr, val);
+}
+/*
+ * For platforms, which don't support the CAS (compare-and-swap) instruction
+ * (or an equivalent), the stack operations are implemented by use of PRLock
+ */
+
+PR_IMPLEMENT(PRStack *)
+PR_CreateStack(const char *stack_name)
+{
+ PRStack *stack;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if ((stack = PR_NEW(PRStack)) == NULL) {
+ return NULL;
+ }
+ if (stack_name) {
+ stack->prstk_name = (char *) PR_Malloc(strlen(stack_name) + 1);
+ if (stack->prstk_name == NULL) {
+ PR_DELETE(stack);
+ return NULL;
+ }
+ strcpy(stack->prstk_name, stack_name);
+ } else {
+ stack->prstk_name = NULL;
+ }
+
+#ifndef _PR_HAVE_ATOMIC_CAS
+ stack->prstk_lock = PR_NewLock();
+ if (stack->prstk_lock == NULL) {
+ PR_Free(stack->prstk_name);
+ PR_DELETE(stack);
+ return NULL;
+ }
+#endif /* !_PR_HAVE_ATOMIC_CAS */
+
+ stack->prstk_head.prstk_elem_next = NULL;
+
+ return stack;
+}
+
+PR_IMPLEMENT(PRStatus)
+PR_DestroyStack(PRStack *stack)
+{
+ if (stack->prstk_head.prstk_elem_next != NULL) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (stack->prstk_name) {
+ PR_Free(stack->prstk_name);
+ }
+#ifndef _PR_HAVE_ATOMIC_CAS
+ PR_DestroyLock(stack->prstk_lock);
+#endif /* !_PR_HAVE_ATOMIC_CAS */
+ PR_DELETE(stack);
+
+ return PR_SUCCESS;
+}
+
+#ifndef _PR_HAVE_ATOMIC_CAS
+
+PR_IMPLEMENT(void)
+PR_StackPush(PRStack *stack, PRStackElem *stack_elem)
+{
+ PR_Lock(stack->prstk_lock);
+ stack_elem->prstk_elem_next = stack->prstk_head.prstk_elem_next;
+ stack->prstk_head.prstk_elem_next = stack_elem;
+ PR_Unlock(stack->prstk_lock);
+ return;
+}
+
+PR_IMPLEMENT(PRStackElem *)
+PR_StackPop(PRStack *stack)
+{
+ PRStackElem *element;
+
+ PR_Lock(stack->prstk_lock);
+ element = stack->prstk_head.prstk_elem_next;
+ if (element != NULL) {
+ stack->prstk_head.prstk_elem_next = element->prstk_elem_next;
+ element->prstk_elem_next = NULL; /* debugging aid */
+ }
+ PR_Unlock(stack->prstk_lock);
+ return element;
+}
+#endif /* !_PR_HAVE_ATOMIC_CAS */
diff --git a/nsprpub/pr/src/misc/praton.c b/nsprpub/pr/src/misc/praton.c
new file mode 100644
index 0000000000..3e729f5879
--- /dev/null
+++ b/nsprpub/pr/src/misc/praton.c
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*******************************************************************************
+ * The following function pr_inet_aton is based on the BSD function inet_aton
+ * with some modifications. The license and copyright notices applying to this
+ * function appear below. Modifications are also according to the license below.
+ ******************************************************************************/
+
+#include "prnetdb.h"
+
+/*
+ * Copyright (c) 1983, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define XX 127
+static const unsigned char index_hex[256] = {
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX,
+ XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+};
+
+static PRBool _isdigit(char c) {
+ return c >= '0' && c <= '9';
+}
+static PRBool _isxdigit(char c) {
+ return index_hex[(unsigned char) c] != XX;
+}
+static PRBool _isspace(char c) {
+ return c == ' ' || (c >= '\t' && c <= '\r');
+}
+#undef XX
+
+int
+pr_inet_aton(const char *cp, PRUint32 *addr)
+{
+ PRUint32 val;
+ int base, n;
+ char c;
+ PRUint8 parts[4];
+ PRUint8 *pp = parts;
+ int digit;
+
+ c = *cp;
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, isdigit=decimal.
+ */
+ if (!_isdigit(c)) {
+ return (0);
+ }
+ val = 0; base = 10; digit = 0;
+ if (c == '0') {
+ c = *++cp;
+ if (c == 'x' || c == 'X') {
+ base = 16, c = *++cp;
+ }
+ else {
+ base = 8;
+ digit = 1;
+ }
+ }
+ for (;;) {
+ if (_isdigit(c)) {
+ if (base == 8 && (c == '8' || c == '9')) {
+ return (0);
+ }
+ val = (val * base) + (c - '0');
+ c = *++cp;
+ digit = 1;
+ } else if (base == 16 && _isxdigit(c)) {
+ val = (val << 4) + index_hex[(unsigned char) c];
+ c = *++cp;
+ digit = 1;
+ } else {
+ break;
+ }
+ }
+ if (c == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16 bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3 || val > 0xffU) {
+ return (0);
+ }
+ *pp++ = val;
+ c = *++cp;
+ } else {
+ break;
+ }
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (c != '\0' && !_isspace(c)) {
+ return (0);
+ }
+ /*
+ * Did we get a valid digit?
+ */
+ if (!digit) {
+ return (0);
+ }
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ n = pp - parts + 1;
+ switch (n) {
+ case 1: /*%< a -- 32 bits */
+ break;
+
+ case 2: /*%< a.b -- 8.24 bits */
+ if (val > 0xffffffU) {
+ return (0);
+ }
+ val |= (unsigned int)parts[0] << 24;
+ break;
+
+ case 3: /*%< a.b.c -- 8.8.16 bits */
+ if (val > 0xffffU) {
+ return (0);
+ }
+ val |= ((unsigned int)parts[0] << 24) | ((unsigned int)parts[1] << 16);
+ break;
+
+ case 4: /*%< a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xffU) {
+ return (0);
+ }
+ val |= ((unsigned int)parts[0] << 24) |
+ ((unsigned int)parts[1] << 16) |
+ ((unsigned int)parts[2] << 8);
+ break;
+ }
+ *addr = PR_htonl(val);
+ return (1);
+}
+
diff --git a/nsprpub/pr/src/misc/prcountr.c b/nsprpub/pr/src/misc/prcountr.c
new file mode 100644
index 0000000000..29d7d4ccbe
--- /dev/null
+++ b/nsprpub/pr/src/misc/prcountr.c
@@ -0,0 +1,483 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** prcountr.c -- NSPR Instrumentation Counters
+**
+** Implement the interface defined in prcountr.h
+**
+** Design Notes:
+**
+** The Counter Facility (CF) has a single anchor: qNameList.
+** The anchor is a PRCList. qNameList is a list of links in QName
+** structures. From qNameList any QName structure and its
+** associated RName structure can be located.
+**
+** For each QName, a list of RName structures is anchored at
+** rnLink in the QName structure.
+**
+** The counter itself is embedded in the RName structure.
+**
+** For manipulating the counter database, single lock is used to
+** protect the entire list: counterLock.
+**
+** A PRCounterHandle, defined in prcountr.h, is really a pointer
+** to a RName structure. References by PRCounterHandle are
+** dead-reconed to the RName structure. The PRCounterHandle is
+** "overloaded" for traversing the QName structures; only the
+** function PR_FindNextQnameHandle() uses this overloading.
+**
+**
+** ToDo (lth): decide on how to lock or atomically update
+** individual counters. Candidates are: the global lock; a lock
+** per RName structure; Atomic operations (Note that there are
+** not adaquate atomic operations (yet) to achieve this goal). At
+** this writing (6/19/98) , the update of the counter variable in
+** a QName structure is unprotected.
+**
+*/
+
+#include "prcountr.h"
+#include "prclist.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prmem.h"
+#include <string.h>
+
+/*
+**
+*/
+typedef struct QName
+{
+ PRCList link;
+ PRCList rNameList;
+ char name[PRCOUNTER_NAME_MAX+1];
+} QName;
+
+/*
+**
+*/
+typedef struct RName
+{
+ PRCList link;
+ QName *qName;
+ PRLock *lock;
+ volatile PRUint32 counter;
+ char name[PRCOUNTER_NAME_MAX+1];
+ char desc[PRCOUNTER_DESC_MAX+1];
+} RName;
+
+
+/*
+** Define the Counter Facility database
+*/
+static PRLock *counterLock;
+static PRCList qNameList;
+static PRLogModuleInfo *lm;
+
+/*
+** _PR_CounterInitialize() -- Initialize the Counter Facility
+**
+*/
+static void _PR_CounterInitialize( void )
+{
+ /*
+ ** This function should be called only once
+ */
+ PR_ASSERT( counterLock == NULL );
+
+ counterLock = PR_NewLock();
+ PR_INIT_CLIST( &qNameList );
+ lm = PR_NewLogModule("counters");
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Initialization complete"));
+
+ return;
+} /* end _PR_CounterInitialize() */
+
+/*
+** PR_CreateCounter() -- Create a counter
+**
+** ValidateArguments
+** Lock
+** if (qName not already in database)
+** NewQname
+** if (rName already in database )
+** Assert
+** else NewRname
+** NewCounter
+** link 'em up
+** Unlock
+**
+*/
+PR_IMPLEMENT(PRCounterHandle)
+PR_CreateCounter(
+ const char *qName,
+ const char *rName,
+ const char *description
+)
+{
+ QName *qnp;
+ RName *rnp;
+ PRBool matchQname = PR_FALSE;
+
+ /* Self initialize, if necessary */
+ if ( counterLock == NULL ) {
+ _PR_CounterInitialize();
+ }
+
+ /* Validate input arguments */
+ PR_ASSERT( strlen(qName) <= PRCOUNTER_NAME_MAX );
+ PR_ASSERT( strlen(rName) <= PRCOUNTER_NAME_MAX );
+ PR_ASSERT( strlen(description) <= PRCOUNTER_DESC_MAX );
+
+ /* Lock the Facility */
+ PR_Lock( counterLock );
+
+ /* Do we already have a matching QName? */
+ if (!PR_CLIST_IS_EMPTY( &qNameList ))
+ {
+ qnp = (QName *) PR_LIST_HEAD( &qNameList );
+ do {
+ if ( strcmp(qnp->name, qName) == 0)
+ {
+ matchQname = PR_TRUE;
+ break;
+ }
+ qnp = (QName *)PR_NEXT_LINK( &qnp->link );
+ } while( qnp != (QName *)&qNameList );
+ }
+ /*
+ ** If we did not find a matching QName,
+ ** allocate one and initialize it.
+ ** link it onto the qNameList.
+ **
+ */
+ if ( matchQname != PR_TRUE )
+ {
+ qnp = PR_NEWZAP( QName );
+ PR_ASSERT( qnp != NULL );
+ PR_INIT_CLIST( &qnp->link );
+ PR_INIT_CLIST( &qnp->rNameList );
+ strcpy( qnp->name, qName );
+ PR_APPEND_LINK( &qnp->link, &qNameList );
+ }
+
+ /* Do we already have a matching RName? */
+ if (!PR_CLIST_IS_EMPTY( &qnp->rNameList ))
+ {
+ rnp = (RName *) PR_LIST_HEAD( &qnp->rNameList );
+ do {
+ /*
+ ** No duplicate RNames are allowed within a QName
+ **
+ */
+ PR_ASSERT( strcmp(rnp->name, rName));
+ rnp = (RName *)PR_NEXT_LINK( &rnp->link );
+ } while( rnp != (RName *)&qnp->rNameList );
+ }
+
+ /* Get a new RName structure; initialize its members */
+ rnp = PR_NEWZAP( RName );
+ PR_ASSERT( rnp != NULL );
+ PR_INIT_CLIST( &rnp->link );
+ strcpy( rnp->name, rName );
+ strcpy( rnp->desc, description );
+ rnp->lock = PR_NewLock();
+ if ( rnp->lock == NULL )
+ {
+ PR_ASSERT(0);
+ }
+
+ PR_APPEND_LINK( &rnp->link, &qnp->rNameList ); /* add RName to QName's rnList */
+ rnp->qName = qnp; /* point the RName to the QName */
+
+ /* Unlock the Facility */
+ PR_Unlock( counterLock );
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Create: QName: %s %p, RName: %s %p\n\t",
+ qName, qnp, rName, rnp ));
+
+ return((PRCounterHandle)rnp);
+} /* end PR_CreateCounter() */
+
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+PR_DestroyCounter(
+ PRCounterHandle handle
+)
+{
+ RName *rnp = (RName *)handle;
+ QName *qnp = rnp->qName;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Deleting: QName: %s, RName: %s",
+ qnp->name, rnp->name));
+
+ /* Lock the Facility */
+ PR_Lock( counterLock );
+
+ /*
+ ** Remove RName from the list of RNames in QName
+ ** and free RName
+ */
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Deleting RName: %s, %p",
+ rnp->name, rnp));
+ PR_REMOVE_LINK( &rnp->link );
+ PR_Free( rnp->lock );
+ PR_DELETE( rnp );
+
+ /*
+ ** If this is the last RName within QName
+ ** remove QName from the qNameList and free it
+ */
+ if ( PR_CLIST_IS_EMPTY( &qnp->rNameList ) )
+ {
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Deleting unused QName: %s, %p",
+ qnp->name, qnp));
+ PR_REMOVE_LINK( &qnp->link );
+ PR_DELETE( qnp );
+ }
+
+ /* Unlock the Facility */
+ PR_Unlock( counterLock );
+ return;
+} /* end PR_DestroyCounter() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRCounterHandle)
+PR_GetCounterHandleFromName(
+ const char *qName,
+ const char *rName
+)
+{
+ const char *qn, *rn, *desc;
+ PRCounterHandle qh, rh = NULL;
+ RName *rnp = NULL;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetCounterHandleFromName:\n\t"
+ "QName: %s, RName: %s", qName, rName ));
+
+ qh = PR_FindNextCounterQname( NULL );
+ while (qh != NULL)
+ {
+ rh = PR_FindNextCounterRname( NULL, qh );
+ while ( rh != NULL )
+ {
+ PR_GetCounterNameFromHandle( rh, &qn, &rn, &desc );
+ if ( (strcmp( qName, qn ) == 0)
+ && (strcmp( rName, rn ) == 0 ))
+ {
+ rnp = (RName *)rh;
+ goto foundIt;
+ }
+ rh = PR_FindNextCounterRname( rh, qh );
+ }
+ qh = PR_FindNextCounterQname( NULL );
+ }
+
+foundIt:
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetConterHandleFromName: %p", rnp ));
+ return(rh);
+} /* end PR_GetCounterHandleFromName() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+PR_GetCounterNameFromHandle(
+ PRCounterHandle handle,
+ const char **qName,
+ const char **rName,
+ const char **description
+)
+{
+ RName *rnp = (RName *)handle;
+ QName *qnp = rnp->qName;
+
+ *qName = qnp->name;
+ *rName = rnp->name;
+ *description = rnp->desc;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetConterNameFromHandle: "
+ "QNp: %p, RNp: %p,\n\tQName: %s, RName: %s, Desc: %s",
+ qnp, rnp, qnp->name, rnp->name, rnp->desc ));
+
+ return;
+} /* end PR_GetCounterNameFromHandle() */
+
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+PR_IncrementCounter(
+ PRCounterHandle handle
+)
+{
+ PR_Lock(((RName *)handle)->lock);
+ ((RName *)handle)->counter++;
+ PR_Unlock(((RName *)handle)->lock);
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Increment: %p, %ld",
+ handle, ((RName *)handle)->counter ));
+
+ return;
+} /* end PR_IncrementCounter() */
+
+
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+PR_DecrementCounter(
+ PRCounterHandle handle
+)
+{
+ PR_Lock(((RName *)handle)->lock);
+ ((RName *)handle)->counter--;
+ PR_Unlock(((RName *)handle)->lock);
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Decrement: %p, %ld",
+ handle, ((RName *)handle)->counter ));
+
+ return;
+} /* end PR_DecrementCounter() */
+
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+PR_AddToCounter(
+ PRCounterHandle handle,
+ PRUint32 value
+)
+{
+ PR_Lock(((RName *)handle)->lock);
+ ((RName *)handle)->counter += value;
+ PR_Unlock(((RName *)handle)->lock);
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: AddToCounter: %p, %ld",
+ handle, ((RName *)handle)->counter ));
+
+ return;
+} /* end PR_AddToCounter() */
+
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+PR_SubtractFromCounter(
+ PRCounterHandle handle,
+ PRUint32 value
+)
+{
+ PR_Lock(((RName *)handle)->lock);
+ ((RName *)handle)->counter -= value;
+ PR_Unlock(((RName *)handle)->lock);
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: SubtractFromCounter: %p, %ld",
+ handle, ((RName *)handle)->counter ));
+
+ return;
+} /* end PR_SubtractFromCounter() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRUint32)
+PR_GetCounter(
+ PRCounterHandle handle
+)
+{
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetCounter: %p, %ld",
+ handle, ((RName *)handle)->counter ));
+
+ return(((RName *)handle)->counter);
+} /* end PR_GetCounter() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+PR_SetCounter(
+ PRCounterHandle handle,
+ PRUint32 value
+)
+{
+ ((RName *)handle)->counter = value;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: SetCounter: %p, %ld",
+ handle, ((RName *)handle)->counter ));
+
+ return;
+} /* end PR_SetCounter() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRCounterHandle)
+PR_FindNextCounterQname(
+ PRCounterHandle handle
+)
+{
+ QName *qnp = (QName *)handle;
+
+ if ( PR_CLIST_IS_EMPTY( &qNameList )) {
+ qnp = NULL;
+ }
+ else if ( qnp == NULL ) {
+ qnp = (QName *)PR_LIST_HEAD( &qNameList );
+ }
+ else if ( PR_NEXT_LINK( &qnp->link ) == &qNameList ) {
+ qnp = NULL;
+ }
+ else {
+ qnp = (QName *)PR_NEXT_LINK( &qnp->link );
+ }
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: FindNextQname: Handle: %p, Returns: %p",
+ handle, qnp ));
+
+ return((PRCounterHandle)qnp);
+} /* end PR_FindNextCounterQname() */
+
+
+/*
+**
+*/
+PR_IMPLEMENT(PRCounterHandle)
+PR_FindNextCounterRname(
+ PRCounterHandle rhandle,
+ PRCounterHandle qhandle
+)
+{
+ RName *rnp = (RName *)rhandle;
+ QName *qnp = (QName *)qhandle;
+
+
+ if ( PR_CLIST_IS_EMPTY( &qnp->rNameList )) {
+ rnp = NULL;
+ }
+ else if ( rnp == NULL ) {
+ rnp = (RName *)PR_LIST_HEAD( &qnp->rNameList );
+ }
+ else if ( PR_NEXT_LINK( &rnp->link ) == &qnp->rNameList ) {
+ rnp = NULL;
+ }
+ else {
+ rnp = (RName *)PR_NEXT_LINK( &rnp->link );
+ }
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: FindNextRname: Rhandle: %p, QHandle: %p, Returns: %p",
+ rhandle, qhandle, rnp ));
+
+ return((PRCounterHandle)rnp);
+} /* end PR_FindNextCounterRname() */
diff --git a/nsprpub/pr/src/misc/prdtoa.c b/nsprpub/pr/src/misc/prdtoa.c
new file mode 100644
index 0000000000..51b331bac8
--- /dev/null
+++ b/nsprpub/pr/src/misc/prdtoa.c
@@ -0,0 +1,3697 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file is based on the third-party code dtoa.c. We minimize our
+ * modifications to third-party code to make it easy to merge new versions.
+ * The author of dtoa.c was not willing to add the parentheses suggested by
+ * GCC, so we suppress these warnings.
+ */
+#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)
+#pragma GCC diagnostic ignored "-Wparentheses"
+#endif
+
+#include "primpl.h"
+#include "prbit.h"
+
+#define MULTIPLE_THREADS
+#define ACQUIRE_DTOA_LOCK(n) PR_Lock(dtoa_lock[n])
+#define FREE_DTOA_LOCK(n) PR_Unlock(dtoa_lock[n])
+
+static PRLock *dtoa_lock[2];
+
+void _PR_InitDtoa(void)
+{
+ dtoa_lock[0] = PR_NewLock();
+ dtoa_lock[1] = PR_NewLock();
+}
+
+void _PR_CleanupDtoa(void)
+{
+ PR_DestroyLock(dtoa_lock[0]);
+ dtoa_lock[0] = NULL;
+ PR_DestroyLock(dtoa_lock[1]);
+ dtoa_lock[1] = NULL;
+
+ /* FIXME: deal with freelist and p5s. */
+}
+
+#if !defined(__ARM_EABI__) \
+ && (defined(__arm) || defined(__arm__) || defined(__arm26__) \
+ || defined(__arm32__))
+#define IEEE_ARM
+#elif defined(IS_LITTLE_ENDIAN)
+#define IEEE_8087
+#else
+#define IEEE_MC68k
+#endif
+
+#define Long PRInt32
+#define ULong PRUint32
+#define NO_LONG_LONG
+
+#define No_Hex_NaN
+
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ ***************************************************************/
+
+/* Please send bug reports to David M. Gay (dmg at acm dot org,
+ * with " at " changed at "@" and " dot " changed to "."). */
+
+/* On a machine with IEEE extended-precision registers, it is
+ * necessary to specify double-precision (53-bit) rounding precision
+ * before invoking strtod or dtoa. If the machine uses (the equivalent
+ * of) Intel 80x87 arithmetic, the call
+ * _control87(PC_53, MCW_PC);
+ * does this with many compilers. Whether this or another call is
+ * appropriate depends on the compiler; for this to work, it may be
+ * necessary to #include "float.h" or another system-dependent header
+ * file.
+ */
+
+/* strtod for IEEE-, VAX-, and IBM-arithmetic machines.
+ *
+ * This strtod returns a nearest machine number to the input decimal
+ * string (or sets errno to ERANGE). With IEEE arithmetic, ties are
+ * broken by the IEEE round-even rule. Otherwise ties are broken by
+ * biased rounding (add half and chop).
+ *
+ * Inspired loosely by William D. Clinger's paper "How to Read Floating
+ * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ *
+ * 1. We only require IEEE, IBM, or VAX double-precision
+ * arithmetic (not IEEE double-extended).
+ * 2. We get by with floating-point arithmetic in a case that
+ * Clinger missed -- when we're computing d * 10^n
+ * for a small integer d and the integer n is not too
+ * much larger than 22 (the maximum integer k for which
+ * we can represent 10^k exactly), we may be able to
+ * compute (d*10^k) * 10^(e-k) with just one roundoff.
+ * 3. Rather than a bit-at-a-time adjustment of the binary
+ * result in the hard case, we use floating-point
+ * arithmetic to determine the adjustment to within
+ * one bit; only in really hard cases do we need to
+ * compute a second residual.
+ * 4. Because of 3., we don't need a large table of powers of 10
+ * for ten-to-e (just some small tables, e.g. of 10^k
+ * for 0 <= k <= 22).
+ */
+
+/*
+ * #define IEEE_8087 for IEEE-arithmetic machines where the least
+ * significant byte has the lowest address.
+ * #define IEEE_MC68k for IEEE-arithmetic machines where the most
+ * significant byte has the lowest address.
+ * #define IEEE_ARM for IEEE-arithmetic machines where the two words
+ * in a double are stored in big endian order but the two shorts
+ * in a word are still stored in little endian order.
+ * #define Long int on machines with 32-bit ints and 64-bit longs.
+ * #define IBM for IBM mainframe-style floating-point arithmetic.
+ * #define VAX for VAX-style floating-point arithmetic (D_floating).
+ * #define No_leftright to omit left-right logic in fast floating-point
+ * computation of dtoa.
+ * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ * and strtod and dtoa should round accordingly.
+ * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ * and Honor_FLT_ROUNDS is not #defined.
+ * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines
+ * that use extended-precision instructions to compute rounded
+ * products and quotients) with IBM.
+ * #define ROUND_BIASED for IEEE-format with biased rounding.
+ * #define Inaccurate_Divide for IEEE-format with correctly rounded
+ * products but inaccurate quotients, e.g., for Intel i860.
+ * #define NO_LONG_LONG on machines that do not have a "long long"
+ * integer type (of >= 64 bits). On such machines, you can
+ * #define Just_16 to store 16 bits per 32-bit Long when doing
+ * high-precision integer arithmetic. Whether this speeds things
+ * up or slows things down depends on the machine and the number
+ * being converted. If long long is available and the name is
+ * something other than "long long", #define Llong to be the name,
+ * and if "unsigned Llong" does not work as an unsigned version of
+ * Llong, #define #ULLong to be the corresponding unsigned type.
+ * #define KR_headers for old-style C function headers.
+ * #define Bad_float_h if your system lacks a float.h or if it does not
+ * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP,
+ * FLT_RADIX, FLT_ROUNDS, and DBL_MAX.
+ * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n)
+ * if memory is available and otherwise does something you deem
+ * appropriate. If MALLOC is undefined, malloc will be invoked
+ * directly -- and assumed always to succeed. Similarly, if you
+ * want something other than the system's free() to be called to
+ * recycle memory acquired from MALLOC, #define FREE to be the
+ * name of the alternate routine. (FREE or free is only called in
+ * pathological cases, e.g., in a dtoa call after a dtoa return in
+ * mode 3 with thousands of digits requested.)
+ * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making
+ * memory allocations from a private pool of memory when possible.
+ * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes,
+ * unless #defined to be a different length. This default length
+ * suffices to get rid of MALLOC calls except for unusual cases,
+ * such as decimal-to-binary conversion of a very long string of
+ * digits. The longest string dtoa can return is about 751 bytes
+ * long. For conversions by strtod of strings of 800 digits and
+ * all dtoa conversions in single-threaded executions with 8-byte
+ * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte
+ * pointers, PRIVATE_MEM >= 7112 appears adequate.
+ * #define INFNAN_CHECK on IEEE systems to cause strtod to check for
+ * Infinity and NaN (case insensitively). On some systems (e.g.,
+ * some HP systems), it may be necessary to #define NAN_WORD0
+ * appropriately -- to the most significant word of a quiet NaN.
+ * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.)
+ * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined,
+ * strtod also accepts (case insensitively) strings of the form
+ * NaN(x), where x is a string of hexadecimal digits and spaces;
+ * if there is only one string of hexadecimal digits, it is taken
+ * for the 52 fraction bits of the resulting NaN; if there are two
+ * or more strings of hex digits, the first is for the high 20 bits,
+ * the second and subsequent for the low 32 bits, with intervening
+ * white space ignored; but if this results in none of the 52
+ * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0
+ * and NAN_WORD1 are used instead.
+ * #define MULTIPLE_THREADS if the system offers preemptively scheduled
+ * multiple threads. In this case, you must provide (or suitably
+ * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed
+ * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed
+ * in pow5mult, ensures lazy evaluation of only one copy of high
+ * powers of 5; omitting this lock would introduce a small
+ * probability of wasting memory, but would otherwise be harmless.)
+ * You must also invoke freedtoa(s) to free the value s returned by
+ * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined.
+ * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that
+ * avoids underflows on inputs whose result does not underflow.
+ * If you #define NO_IEEE_Scale on a machine that uses IEEE-format
+ * floating-point numbers and flushes underflows to zero rather
+ * than implementing gradual underflow, then you must also #define
+ * Sudden_Underflow.
+ * #define USE_LOCALE to use the current locale's decimal_point value.
+ * #define SET_INEXACT if IEEE arithmetic is being used and extra
+ * computation should be done to set the inexact flag when the
+ * result is inexact and avoid setting inexact when the result
+ * is exact. In this case, dtoa.c must be compiled in
+ * an environment, perhaps provided by #include "dtoa.c" in a
+ * suitable wrapper, that defines two functions,
+ * int get_inexact(void);
+ * void clear_inexact(void);
+ * such that get_inexact() returns a nonzero value if the
+ * inexact bit is already set, and clear_inexact() sets the
+ * inexact bit to 0. When SET_INEXACT is #defined, strtod
+ * also does extra computations to set the underflow and overflow
+ * flags when appropriate (i.e., when the result is tiny and
+ * inexact or when it is a numeric value rounded to +-infinity).
+ * #define NO_ERRNO if strtod should not assign errno = ERANGE when
+ * the result overflows to +-Infinity or underflows to 0.
+ */
+
+#ifndef Long
+#define Long long
+#endif
+#ifndef ULong
+typedef unsigned Long ULong;
+#endif
+
+#ifdef DEBUG
+#include "stdio.h"
+#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);}
+#endif
+
+#include "stdlib.h"
+#include "string.h"
+
+#ifdef USE_LOCALE
+#include "locale.h"
+#endif
+
+#ifdef MALLOC
+#ifdef KR_headers
+extern char *MALLOC();
+#else
+extern void *MALLOC(size_t);
+#endif
+#else
+#define MALLOC malloc
+#endif
+
+#ifndef Omit_Private_Memory
+#ifndef PRIVATE_MEM
+#define PRIVATE_MEM 2304
+#endif
+#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double))
+static double private_mem[PRIVATE_mem], *pmem_next = private_mem;
+#endif
+
+#undef IEEE_Arith
+#undef Avoid_Underflow
+#ifdef IEEE_MC68k
+#define IEEE_Arith
+#endif
+#ifdef IEEE_8087
+#define IEEE_Arith
+#endif
+#ifdef IEEE_ARM
+#define IEEE_Arith
+#endif
+
+#include "errno.h"
+
+#ifdef Bad_float_h
+
+#ifdef IEEE_Arith
+#define DBL_DIG 15
+#define DBL_MAX_10_EXP 308
+#define DBL_MAX_EXP 1024
+#define FLT_RADIX 2
+#endif /*IEEE_Arith*/
+
+#ifdef IBM
+#define DBL_DIG 16
+#define DBL_MAX_10_EXP 75
+#define DBL_MAX_EXP 63
+#define FLT_RADIX 16
+#define DBL_MAX 7.2370055773322621e+75
+#endif
+
+#ifdef VAX
+#define DBL_DIG 16
+#define DBL_MAX_10_EXP 38
+#define DBL_MAX_EXP 127
+#define FLT_RADIX 2
+#define DBL_MAX 1.7014118346046923e+38
+#endif
+
+#ifndef LONG_MAX
+#define LONG_MAX 2147483647
+#endif
+
+#else /* ifndef Bad_float_h */
+#include "float.h"
+#endif /* Bad_float_h */
+
+#ifndef __MATH_H__
+#include "math.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef CONST
+#ifdef KR_headers
+#define CONST /* blank */
+#else
+#define CONST const
+#endif
+#endif
+
+#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(IEEE_ARM) + defined(VAX) + defined(IBM) != 1
+Exactly one of IEEE_8087, IEEE_MC68k, IEEE_ARM, VAX, or IBM should be defined.
+#endif
+
+typedef union {
+ double d;
+ ULong L[2];
+} U;
+
+#define dval(x) (x).d
+#ifdef IEEE_8087
+#define word0(x) (x).L[1]
+#define word1(x) (x).L[0]
+#else
+#define word0(x) (x).L[0]
+#define word1(x) (x).L[1]
+#endif
+
+/* The following definition of Storeinc is appropriate for MIPS processors.
+ * An alternative that might be better on some machines is
+ * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff)
+ */
+#if defined(IEEE_8087) + defined(IEEE_ARM) + defined(VAX)
+#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \
+((unsigned short *)a)[0] = (unsigned short)c, a++)
+#else
+#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \
+((unsigned short *)a)[1] = (unsigned short)c, a++)
+#endif
+
+/* #define P DBL_MANT_DIG */
+/* Ten_pmax = floor(P*log(2)/log(5)) */
+/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */
+/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */
+/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */
+
+#ifdef IEEE_Arith
+#define Exp_shift 20
+#define Exp_shift1 20
+#define Exp_msk1 0x100000
+#define Exp_msk11 0x100000
+#define Exp_mask 0x7ff00000
+#define P 53
+#define Bias 1023
+#define Emin (-1022)
+#define Exp_1 0x3ff00000
+#define Exp_11 0x3ff00000
+#define Ebits 11
+#define Frac_mask 0xfffff
+#define Frac_mask1 0xfffff
+#define Ten_pmax 22
+#define Bletch 0x10
+#define Bndry_mask 0xfffff
+#define Bndry_mask1 0xfffff
+#define LSB 1
+#define Sign_bit 0x80000000
+#define Log2P 1
+#define Tiny0 0
+#define Tiny1 1
+#define Quick_max 14
+#define Int_max 14
+#ifndef NO_IEEE_Scale
+#define Avoid_Underflow
+#ifdef Flush_Denorm /* debugging option */
+#undef Sudden_Underflow
+#endif
+#endif
+
+#ifndef Flt_Rounds
+#ifdef FLT_ROUNDS
+#define Flt_Rounds FLT_ROUNDS
+#else
+#define Flt_Rounds 1
+#endif
+#endif /*Flt_Rounds*/
+
+#ifdef Honor_FLT_ROUNDS
+#define Rounding rounding
+#undef Check_FLT_ROUNDS
+#define Check_FLT_ROUNDS
+#else
+#define Rounding Flt_Rounds
+#endif
+
+#else /* ifndef IEEE_Arith */
+#undef Check_FLT_ROUNDS
+#undef Honor_FLT_ROUNDS
+#undef SET_INEXACT
+#undef Sudden_Underflow
+#define Sudden_Underflow
+#ifdef IBM
+#undef Flt_Rounds
+#define Flt_Rounds 0
+#define Exp_shift 24
+#define Exp_shift1 24
+#define Exp_msk1 0x1000000
+#define Exp_msk11 0x1000000
+#define Exp_mask 0x7f000000
+#define P 14
+#define Bias 65
+#define Exp_1 0x41000000
+#define Exp_11 0x41000000
+#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */
+#define Frac_mask 0xffffff
+#define Frac_mask1 0xffffff
+#define Bletch 4
+#define Ten_pmax 22
+#define Bndry_mask 0xefffff
+#define Bndry_mask1 0xffffff
+#define LSB 1
+#define Sign_bit 0x80000000
+#define Log2P 4
+#define Tiny0 0x100000
+#define Tiny1 0
+#define Quick_max 14
+#define Int_max 15
+#else /* VAX */
+#undef Flt_Rounds
+#define Flt_Rounds 1
+#define Exp_shift 23
+#define Exp_shift1 7
+#define Exp_msk1 0x80
+#define Exp_msk11 0x800000
+#define Exp_mask 0x7f80
+#define P 56
+#define Bias 129
+#define Exp_1 0x40800000
+#define Exp_11 0x4080
+#define Ebits 8
+#define Frac_mask 0x7fffff
+#define Frac_mask1 0xffff007f
+#define Ten_pmax 24
+#define Bletch 2
+#define Bndry_mask 0xffff007f
+#define Bndry_mask1 0xffff007f
+#define LSB 0x10000
+#define Sign_bit 0x8000
+#define Log2P 1
+#define Tiny0 0x80
+#define Tiny1 0
+#define Quick_max 15
+#define Int_max 15
+#endif /* IBM, VAX */
+#endif /* IEEE_Arith */
+
+#ifndef IEEE_Arith
+#define ROUND_BIASED
+#endif
+
+#ifdef RND_PRODQUOT
+#define rounded_product(a,b) a = rnd_prod(a, b)
+#define rounded_quotient(a,b) a = rnd_quot(a, b)
+#ifdef KR_headers
+extern double rnd_prod(), rnd_quot();
+#else
+extern double rnd_prod(double, double), rnd_quot(double, double);
+#endif
+#else
+#define rounded_product(a,b) a *= b
+#define rounded_quotient(a,b) a /= b
+#endif
+
+#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))
+#define Big1 0xffffffff
+
+#ifndef Pack_32
+#define Pack_32
+#endif
+
+#ifdef KR_headers
+#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff)
+#else
+#define FFFFFFFF 0xffffffffUL
+#endif
+
+#ifdef NO_LONG_LONG
+#undef ULLong
+#ifdef Just_16
+#undef Pack_32
+/* When Pack_32 is not defined, we store 16 bits per 32-bit Long.
+ * This makes some inner loops simpler and sometimes saves work
+ * during multiplications, but it often seems to make things slightly
+ * slower. Hence the default is now to store 32 bits per Long.
+ */
+#endif
+#else /* long long available */
+#ifndef Llong
+#define Llong long long
+#endif
+#ifndef ULLong
+#define ULLong unsigned Llong
+#endif
+#endif /* NO_LONG_LONG */
+
+#ifndef MULTIPLE_THREADS
+#define ACQUIRE_DTOA_LOCK(n) /*nothing*/
+#define FREE_DTOA_LOCK(n) /*nothing*/
+#endif
+
+#define Kmax 7
+
+struct
+ Bigint {
+ struct Bigint *next;
+ int k, maxwds, sign, wds;
+ ULong x[1];
+};
+
+typedef struct Bigint Bigint;
+
+static Bigint *freelist[Kmax+1];
+
+static Bigint *
+Balloc
+#ifdef KR_headers
+(k) int k;
+#else
+(int k)
+#endif
+{
+ int x;
+ Bigint *rv;
+#ifndef Omit_Private_Memory
+ unsigned int len;
+#endif
+
+ ACQUIRE_DTOA_LOCK(0);
+ /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */
+ /* but this case seems very unlikely. */
+ if (k <= Kmax && (rv = freelist[k])) {
+ freelist[k] = rv->next;
+ }
+ else {
+ x = 1 << k;
+#ifdef Omit_Private_Memory
+ rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong));
+#else
+ len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1)
+ /sizeof(double);
+ if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) {
+ rv = (Bigint*)pmem_next;
+ pmem_next += len;
+ }
+ else {
+ rv = (Bigint*)MALLOC(len*sizeof(double));
+ }
+#endif
+ rv->k = k;
+ rv->maxwds = x;
+ }
+ FREE_DTOA_LOCK(0);
+ rv->sign = rv->wds = 0;
+ return rv;
+}
+
+static void
+Bfree
+#ifdef KR_headers
+(v) Bigint *v;
+#else
+(Bigint *v)
+#endif
+{
+ if (v) {
+ if (v->k > Kmax)
+#ifdef FREE
+ FREE((void*)v);
+#else
+ free((void*)v);
+#endif
+ else {
+ ACQUIRE_DTOA_LOCK(0);
+ v->next = freelist[v->k];
+ freelist[v->k] = v;
+ FREE_DTOA_LOCK(0);
+ }
+ }
+}
+
+#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \
+y->wds*sizeof(Long) + 2*sizeof(int))
+
+static Bigint *
+multadd
+#ifdef KR_headers
+(b, m, a) Bigint *b; int m, a;
+#else
+(Bigint *b, int m, int a) /* multiply by m and add a */
+#endif
+{
+ int i, wds;
+#ifdef ULLong
+ ULong *x;
+ ULLong carry, y;
+#else
+ ULong carry, *x, y;
+#ifdef Pack_32
+ ULong xi, z;
+#endif
+#endif
+ Bigint *b1;
+
+ wds = b->wds;
+ x = b->x;
+ i = 0;
+ carry = a;
+ do {
+#ifdef ULLong
+ y = *x * (ULLong)m + carry;
+ carry = y >> 32;
+ *x++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ xi = *x;
+ y = (xi & 0xffff) * m + carry;
+ z = (xi >> 16) * m + (y >> 16);
+ carry = z >> 16;
+ *x++ = (z << 16) + (y & 0xffff);
+#else
+ y = *x * m + carry;
+ carry = y >> 16;
+ *x++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(++i < wds);
+ if (carry) {
+ if (wds >= b->maxwds) {
+ b1 = Balloc(b->k+1);
+ Bcopy(b1, b);
+ Bfree(b);
+ b = b1;
+ }
+ b->x[wds++] = carry;
+ b->wds = wds;
+ }
+ return b;
+}
+
+static Bigint *
+s2b
+#ifdef KR_headers
+(s, nd0, nd, y9) CONST char *s; int nd0, nd; ULong y9;
+#else
+(CONST char *s, int nd0, int nd, ULong y9)
+#endif
+{
+ Bigint *b;
+ int i, k;
+ Long x, y;
+
+ x = (nd + 8) / 9;
+ for(k = 0, y = 1; x > y; y <<= 1, k++) ;
+#ifdef Pack_32
+ b = Balloc(k);
+ b->x[0] = y9;
+ b->wds = 1;
+#else
+ b = Balloc(k+1);
+ b->x[0] = y9 & 0xffff;
+ b->wds = (b->x[1] = y9 >> 16) ? 2 : 1;
+#endif
+
+ i = 9;
+ if (9 < nd0) {
+ s += 9;
+ do {
+ b = multadd(b, 10, *s++ - '0');
+ }
+ while(++i < nd0);
+ s++;
+ }
+ else {
+ s += 10;
+ }
+ for(; i < nd; i++) {
+ b = multadd(b, 10, *s++ - '0');
+ }
+ return b;
+}
+
+static int
+hi0bits
+#ifdef KR_headers
+(x) register ULong x;
+#else
+(register ULong x)
+#endif
+{
+#ifdef PR_HAVE_BUILTIN_BITSCAN32
+ return( (!x) ? 32 : pr_bitscan_clz32(x) );
+#else
+ register int k = 0;
+
+ if (!(x & 0xffff0000)) {
+ k = 16;
+ x <<= 16;
+ }
+ if (!(x & 0xff000000)) {
+ k += 8;
+ x <<= 8;
+ }
+ if (!(x & 0xf0000000)) {
+ k += 4;
+ x <<= 4;
+ }
+ if (!(x & 0xc0000000)) {
+ k += 2;
+ x <<= 2;
+ }
+ if (!(x & 0x80000000)) {
+ k++;
+ if (!(x & 0x40000000)) {
+ return 32;
+ }
+ }
+ return k;
+#endif /* PR_HAVE_BUILTIN_BITSCAN32 */
+}
+
+static int
+lo0bits
+#ifdef KR_headers
+(y) ULong *y;
+#else
+(ULong *y)
+#endif
+{
+#ifdef PR_HAVE_BUILTIN_BITSCAN32
+ int k;
+ ULong x = *y;
+
+ if (x>1) {
+ *y = ( x >> (k = pr_bitscan_ctz32(x)) );
+ }
+ else {
+ k = ((x ^ 1) << 5);
+ }
+#else
+ register int k;
+ register ULong x = *y;
+
+ if (x & 7) {
+ if (x & 1) {
+ return 0;
+ }
+ if (x & 2) {
+ *y = x >> 1;
+ return 1;
+ }
+ *y = x >> 2;
+ return 2;
+ }
+ k = 0;
+ if (!(x & 0xffff)) {
+ k = 16;
+ x >>= 16;
+ }
+ if (!(x & 0xff)) {
+ k += 8;
+ x >>= 8;
+ }
+ if (!(x & 0xf)) {
+ k += 4;
+ x >>= 4;
+ }
+ if (!(x & 0x3)) {
+ k += 2;
+ x >>= 2;
+ }
+ if (!(x & 1)) {
+ k++;
+ x >>= 1;
+ if (!x) {
+ return 32;
+ }
+ }
+ *y = x;
+#endif /* PR_HAVE_BUILTIN_BITSCAN32 */
+ return k;
+}
+
+static Bigint *
+i2b
+#ifdef KR_headers
+(i) int i;
+#else
+(int i)
+#endif
+{
+ Bigint *b;
+
+ b = Balloc(1);
+ b->x[0] = i;
+ b->wds = 1;
+ return b;
+}
+
+static Bigint *
+mult
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ Bigint *c;
+ int k, wa, wb, wc;
+ ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0;
+ ULong y;
+#ifdef ULLong
+ ULLong carry, z;
+#else
+ ULong carry, z;
+#ifdef Pack_32
+ ULong z2;
+#endif
+#endif
+
+ if (a->wds < b->wds) {
+ c = a;
+ a = b;
+ b = c;
+ }
+ k = a->k;
+ wa = a->wds;
+ wb = b->wds;
+ wc = wa + wb;
+ if (wc > a->maxwds) {
+ k++;
+ }
+ c = Balloc(k);
+ for(x = c->x, xa = x + wc; x < xa; x++) {
+ *x = 0;
+ }
+ xa = a->x;
+ xae = xa + wa;
+ xb = b->x;
+ xbe = xb + wb;
+ xc0 = c->x;
+#ifdef ULLong
+ for(; xb < xbe; xc0++) {
+ if (y = *xb++) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = *x++ * (ULLong)y + *xc + carry;
+ carry = z >> 32;
+ *xc++ = z & FFFFFFFF;
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ }
+#else
+#ifdef Pack_32
+ for(; xb < xbe; xb++, xc0++) {
+ if (y = *xb & 0xffff) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = (*x & 0xffff) * y + (*xc & 0xffff) + carry;
+ carry = z >> 16;
+ z2 = (*x++ >> 16) * y + (*xc >> 16) + carry;
+ carry = z2 >> 16;
+ Storeinc(xc, z2, z);
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ if (y = *xb >> 16) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ z2 = *xc;
+ do {
+ z = (*x & 0xffff) * y + (*xc >> 16) + carry;
+ carry = z >> 16;
+ Storeinc(xc, z, z2);
+ z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry;
+ carry = z2 >> 16;
+ }
+ while(x < xae);
+ *xc = z2;
+ }
+ }
+#else
+ for(; xb < xbe; xc0++) {
+ if (y = *xb++) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = *x++ * y + *xc + carry;
+ carry = z >> 16;
+ *xc++ = z & 0xffff;
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ }
+#endif
+#endif
+ for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ;
+ c->wds = wc;
+ return c;
+}
+
+static Bigint *p5s;
+
+static Bigint *
+pow5mult
+#ifdef KR_headers
+(b, k) Bigint *b; int k;
+#else
+(Bigint *b, int k)
+#endif
+{
+ Bigint *b1, *p5, *p51;
+ int i;
+ static int p05[3] = { 5, 25, 125 };
+
+ if (i = k & 3) {
+ b = multadd(b, p05[i-1], 0);
+ }
+
+ if (!(k >>= 2)) {
+ return b;
+ }
+ if (!(p5 = p5s)) {
+ /* first time */
+#ifdef MULTIPLE_THREADS
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p5 = p5s)) {
+ p5 = p5s = i2b(625);
+ p5->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+#else
+ p5 = p5s = i2b(625);
+ p5->next = 0;
+#endif
+ }
+ for(;;) {
+ if (k & 1) {
+ b1 = mult(b, p5);
+ Bfree(b);
+ b = b1;
+ }
+ if (!(k >>= 1)) {
+ break;
+ }
+ if (!(p51 = p5->next)) {
+#ifdef MULTIPLE_THREADS
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p51 = p5->next)) {
+ p51 = p5->next = mult(p5,p5);
+ p51->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+#else
+ p51 = p5->next = mult(p5,p5);
+ p51->next = 0;
+#endif
+ }
+ p5 = p51;
+ }
+ return b;
+}
+
+static Bigint *
+lshift
+#ifdef KR_headers
+(b, k) Bigint *b; int k;
+#else
+(Bigint *b, int k)
+#endif
+{
+ int i, k1, n, n1;
+ Bigint *b1;
+ ULong *x, *x1, *xe, z;
+
+#ifdef Pack_32
+ n = k >> 5;
+#else
+ n = k >> 4;
+#endif
+ k1 = b->k;
+ n1 = n + b->wds + 1;
+ for(i = b->maxwds; n1 > i; i <<= 1) {
+ k1++;
+ }
+ b1 = Balloc(k1);
+ x1 = b1->x;
+ for(i = 0; i < n; i++) {
+ *x1++ = 0;
+ }
+ x = b->x;
+ xe = x + b->wds;
+#ifdef Pack_32
+ if (k &= 0x1f) {
+ k1 = 32 - k;
+ z = 0;
+ do {
+ *x1++ = *x << k | z;
+ z = *x++ >> k1;
+ }
+ while(x < xe);
+ if (*x1 = z) {
+ ++n1;
+ }
+ }
+#else
+ if (k &= 0xf) {
+ k1 = 16 - k;
+ z = 0;
+ do {
+ *x1++ = *x << k & 0xffff | z;
+ z = *x++ >> k1;
+ }
+ while(x < xe);
+ if (*x1 = z) {
+ ++n1;
+ }
+ }
+#endif
+ else do {
+ *x1++ = *x++;
+ }
+ while(x < xe);
+ b1->wds = n1 - 1;
+ Bfree(b);
+ return b1;
+}
+
+static int
+cmp
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ ULong *xa, *xa0, *xb, *xb0;
+ int i, j;
+
+ i = a->wds;
+ j = b->wds;
+#ifdef DEBUG
+ if (i > 1 && !a->x[i-1]) {
+ Bug("cmp called with a->x[a->wds-1] == 0");
+ }
+ if (j > 1 && !b->x[j-1]) {
+ Bug("cmp called with b->x[b->wds-1] == 0");
+ }
+#endif
+ if (i -= j) {
+ return i;
+ }
+ xa0 = a->x;
+ xa = xa0 + j;
+ xb0 = b->x;
+ xb = xb0 + j;
+ for(;;) {
+ if (*--xa != *--xb) {
+ return *xa < *xb ? -1 : 1;
+ }
+ if (xa <= xa0) {
+ break;
+ }
+ }
+ return 0;
+}
+
+static Bigint *
+diff
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ Bigint *c;
+ int i, wa, wb;
+ ULong *xa, *xae, *xb, *xbe, *xc;
+#ifdef ULLong
+ ULLong borrow, y;
+#else
+ ULong borrow, y;
+#ifdef Pack_32
+ ULong z;
+#endif
+#endif
+
+ i = cmp(a,b);
+ if (!i) {
+ c = Balloc(0);
+ c->wds = 1;
+ c->x[0] = 0;
+ return c;
+ }
+ if (i < 0) {
+ c = a;
+ a = b;
+ b = c;
+ i = 1;
+ }
+ else {
+ i = 0;
+ }
+ c = Balloc(a->k);
+ c->sign = i;
+ wa = a->wds;
+ xa = a->x;
+ xae = xa + wa;
+ wb = b->wds;
+ xb = b->x;
+ xbe = xb + wb;
+ xc = c->x;
+ borrow = 0;
+#ifdef ULLong
+ do {
+ y = (ULLong)*xa++ - *xb++ - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *xc++ = y & FFFFFFFF;
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = *xa++ - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *xc++ = y & FFFFFFFF;
+ }
+#else
+#ifdef Pack_32
+ do {
+ y = (*xa & 0xffff) - (*xb & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - (*xb++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = (*xa & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ }
+#else
+ do {
+ y = *xa++ - *xb++ - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *xc++ = y & 0xffff;
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = *xa++ - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *xc++ = y & 0xffff;
+ }
+#endif
+#endif
+ while(!*--xc) {
+ wa--;
+ }
+ c->wds = wa;
+ return c;
+}
+
+static double
+ulp
+#ifdef KR_headers
+(dx) double dx;
+#else
+(double dx)
+#endif
+{
+ register Long L;
+ U x, a;
+
+ dval(x) = dx;
+ L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1;
+#ifndef Avoid_Underflow
+#ifndef Sudden_Underflow
+ if (L > 0) {
+#endif
+#endif
+#ifdef IBM
+ L |= Exp_msk1 >> 4;
+#endif
+ word0(a) = L;
+ word1(a) = 0;
+#ifndef Avoid_Underflow
+#ifndef Sudden_Underflow
+ }
+ else {
+ L = -L >> Exp_shift;
+ if (L < Exp_shift) {
+ word0(a) = 0x80000 >> L;
+ word1(a) = 0;
+ }
+ else {
+ word0(a) = 0;
+ L -= Exp_shift;
+ word1(a) = L >= 31 ? 1 : 1 << 31 - L;
+ }
+ }
+#endif
+#endif
+ return dval(a);
+}
+
+static double
+b2d
+#ifdef KR_headers
+(a, e) Bigint *a; int *e;
+#else
+(Bigint *a, int *e)
+#endif
+{
+ ULong *xa, *xa0, w, y, z;
+ int k;
+ U d;
+#ifdef VAX
+ ULong d0, d1;
+#else
+#define d0 word0(d)
+#define d1 word1(d)
+#endif
+
+ xa0 = a->x;
+ xa = xa0 + a->wds;
+ y = *--xa;
+#ifdef DEBUG
+ if (!y) {
+ Bug("zero y in b2d");
+ }
+#endif
+ k = hi0bits(y);
+ *e = 32 - k;
+#ifdef Pack_32
+ if (k < Ebits) {
+ d0 = Exp_1 | y >> Ebits - k;
+ w = xa > xa0 ? *--xa : 0;
+ d1 = y << (32-Ebits) + k | w >> Ebits - k;
+ goto ret_d;
+ }
+ z = xa > xa0 ? *--xa : 0;
+ if (k -= Ebits) {
+ d0 = Exp_1 | y << k | z >> 32 - k;
+ y = xa > xa0 ? *--xa : 0;
+ d1 = z << k | y >> 32 - k;
+ }
+ else {
+ d0 = Exp_1 | y;
+ d1 = z;
+ }
+#else
+ if (k < Ebits + 16) {
+ z = xa > xa0 ? *--xa : 0;
+ d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k;
+ w = xa > xa0 ? *--xa : 0;
+ y = xa > xa0 ? *--xa : 0;
+ d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k;
+ goto ret_d;
+ }
+ z = xa > xa0 ? *--xa : 0;
+ w = xa > xa0 ? *--xa : 0;
+ k -= Ebits + 16;
+ d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k;
+ y = xa > xa0 ? *--xa : 0;
+ d1 = w << k + 16 | y << k;
+#endif
+ret_d:
+#ifdef VAX
+ word0(d) = d0 >> 16 | d0 << 16;
+ word1(d) = d1 >> 16 | d1 << 16;
+#else
+#undef d0
+#undef d1
+#endif
+ return dval(d);
+}
+
+static Bigint *
+d2b
+#ifdef KR_headers
+(dd, e, bits) double dd; int *e, *bits;
+#else
+(double dd, int *e, int *bits)
+#endif
+{
+ U d;
+ Bigint *b;
+ int de, k;
+ ULong *x, y, z;
+#ifndef Sudden_Underflow
+ int i;
+#endif
+#ifdef VAX
+ ULong d0, d1;
+#endif
+
+ dval(d) = dd;
+#ifdef VAX
+ d0 = word0(d) >> 16 | word0(d) << 16;
+ d1 = word1(d) >> 16 | word1(d) << 16;
+#else
+#define d0 word0(d)
+#define d1 word1(d)
+#endif
+
+#ifdef Pack_32
+ b = Balloc(1);
+#else
+ b = Balloc(2);
+#endif
+ x = b->x;
+
+ z = d0 & Frac_mask;
+ d0 &= 0x7fffffff; /* clear sign bit, which we ignore */
+#ifdef Sudden_Underflow
+ de = (int)(d0 >> Exp_shift);
+#ifndef IBM
+ z |= Exp_msk11;
+#endif
+#else
+ if (de = (int)(d0 >> Exp_shift)) {
+ z |= Exp_msk1;
+ }
+#endif
+#ifdef Pack_32
+ if (y = d1) {
+ if (k = lo0bits(&y)) {
+ x[0] = y | z << 32 - k;
+ z >>= k;
+ }
+ else {
+ x[0] = y;
+ }
+#ifndef Sudden_Underflow
+ i =
+#endif
+ b->wds = (x[1] = z) ? 2 : 1;
+ }
+ else {
+ k = lo0bits(&z);
+ x[0] = z;
+#ifndef Sudden_Underflow
+ i =
+#endif
+ b->wds = 1;
+ k += 32;
+ }
+#else
+ if (y = d1) {
+ if (k = lo0bits(&y))
+ if (k >= 16) {
+ x[0] = y | z << 32 - k & 0xffff;
+ x[1] = z >> k - 16 & 0xffff;
+ x[2] = z >> k;
+ i = 2;
+ }
+ else {
+ x[0] = y & 0xffff;
+ x[1] = y >> 16 | z << 16 - k & 0xffff;
+ x[2] = z >> k & 0xffff;
+ x[3] = z >> k+16;
+ i = 3;
+ }
+ else {
+ x[0] = y & 0xffff;
+ x[1] = y >> 16;
+ x[2] = z & 0xffff;
+ x[3] = z >> 16;
+ i = 3;
+ }
+ }
+ else {
+#ifdef DEBUG
+ if (!z) {
+ Bug("Zero passed to d2b");
+ }
+#endif
+ k = lo0bits(&z);
+ if (k >= 16) {
+ x[0] = z;
+ i = 0;
+ }
+ else {
+ x[0] = z & 0xffff;
+ x[1] = z >> 16;
+ i = 1;
+ }
+ k += 32;
+ }
+ while(!x[i]) {
+ --i;
+ }
+ b->wds = i + 1;
+#endif
+#ifndef Sudden_Underflow
+ if (de) {
+#endif
+#ifdef IBM
+ *e = (de - Bias - (P-1) << 2) + k;
+ *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask);
+#else
+ *e = de - Bias - (P-1) + k;
+ *bits = P - k;
+#endif
+#ifndef Sudden_Underflow
+ }
+ else {
+ *e = de - Bias - (P-1) + 1 + k;
+#ifdef Pack_32
+ *bits = 32*i - hi0bits(x[i-1]);
+#else
+ *bits = (i+2)*16 - hi0bits(x[i]);
+#endif
+ }
+#endif
+ return b;
+}
+#undef d0
+#undef d1
+
+static double
+ratio
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ U da, db;
+ int k, ka, kb;
+
+ dval(da) = b2d(a, &ka);
+ dval(db) = b2d(b, &kb);
+#ifdef Pack_32
+ k = ka - kb + 32*(a->wds - b->wds);
+#else
+ k = ka - kb + 16*(a->wds - b->wds);
+#endif
+#ifdef IBM
+ if (k > 0) {
+ word0(da) += (k >> 2)*Exp_msk1;
+ if (k &= 3) {
+ dval(da) *= 1 << k;
+ }
+ }
+ else {
+ k = -k;
+ word0(db) += (k >> 2)*Exp_msk1;
+ if (k &= 3) {
+ dval(db) *= 1 << k;
+ }
+ }
+#else
+ if (k > 0) {
+ word0(da) += k*Exp_msk1;
+ }
+ else {
+ k = -k;
+ word0(db) += k*Exp_msk1;
+ }
+#endif
+ return dval(da) / dval(db);
+}
+
+static CONST double
+tens[] = {
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22
+#ifdef VAX
+ , 1e23, 1e24
+#endif
+};
+
+static CONST double
+#ifdef IEEE_Arith
+bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };
+static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128,
+#ifdef Avoid_Underflow
+ 9007199254740992.*9007199254740992.e-256
+ /* = 2^106 * 1e-53 */
+#else
+ 1e-256
+#endif
+ };
+/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */
+/* flag unnecessarily. It leads to a song and dance at the end of strtod. */
+#define Scale_Bit 0x10
+#define n_bigtens 5
+#else
+#ifdef IBM
+bigtens[] = { 1e16, 1e32, 1e64 };
+static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 };
+#define n_bigtens 3
+#else
+bigtens[] = { 1e16, 1e32 };
+static CONST double tinytens[] = { 1e-16, 1e-32 };
+#define n_bigtens 2
+#endif
+#endif
+
+#ifndef IEEE_Arith
+#undef INFNAN_CHECK
+#endif
+
+#ifdef INFNAN_CHECK
+
+#ifndef NAN_WORD0
+#define NAN_WORD0 0x7ff80000
+#endif
+
+#ifndef NAN_WORD1
+#define NAN_WORD1 0
+#endif
+
+static int
+match
+#ifdef KR_headers
+(sp, t) char **sp, *t;
+#else
+(CONST char **sp, char *t)
+#endif
+{
+ int c, d;
+ CONST char *s = *sp;
+
+ while(d = *t++) {
+ if ((c = *++s) >= 'A' && c <= 'Z') {
+ c += 'a' - 'A';
+ }
+ if (c != d) {
+ return 0;
+ }
+ }
+ *sp = s + 1;
+ return 1;
+}
+
+#ifndef No_Hex_NaN
+static void
+hexnan
+#ifdef KR_headers
+(rvp, sp) double *rvp; CONST char **sp;
+#else
+(double *rvp, CONST char **sp)
+#endif
+{
+ ULong c, x[2];
+ CONST char *s;
+ int havedig, udx0, xshift;
+
+ x[0] = x[1] = 0;
+ havedig = xshift = 0;
+ udx0 = 1;
+ s = *sp;
+ while(c = *(CONST unsigned char*)++s) {
+ if (c >= '0' && c <= '9') {
+ c -= '0';
+ }
+ else if (c >= 'a' && c <= 'f') {
+ c += 10 - 'a';
+ }
+ else if (c >= 'A' && c <= 'F') {
+ c += 10 - 'A';
+ }
+ else if (c <= ' ') {
+ if (udx0 && havedig) {
+ udx0 = 0;
+ xshift = 1;
+ }
+ continue;
+ }
+ else if (/*(*/ c == ')' && havedig) {
+ *sp = s + 1;
+ break;
+ }
+ else {
+ return; /* invalid form: don't change *sp */
+ }
+ havedig = 1;
+ if (xshift) {
+ xshift = 0;
+ x[0] = x[1];
+ x[1] = 0;
+ }
+ if (udx0) {
+ x[0] = (x[0] << 4) | (x[1] >> 28);
+ }
+ x[1] = (x[1] << 4) | c;
+ }
+ if ((x[0] &= 0xfffff) || x[1]) {
+ word0(*rvp) = Exp_mask | x[0];
+ word1(*rvp) = x[1];
+ }
+}
+#endif /*No_Hex_NaN*/
+#endif /* INFNAN_CHECK */
+
+PR_IMPLEMENT(double)
+PR_strtod
+#ifdef KR_headers
+(s00, se) CONST char *s00; char **se;
+#else
+(CONST char *s00, char **se)
+#endif
+{
+#ifdef Avoid_Underflow
+ int scale;
+#endif
+ int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign,
+ e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign;
+ CONST char *s, *s0, *s1;
+ double aadj, aadj1, adj;
+ U aadj2, rv, rv0;
+ Long L;
+ ULong y, z;
+ Bigint *bb, *bb1, *bd, *bd0, *bs, *delta;
+#ifdef SET_INEXACT
+ int inexact, oldinexact;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ int rounding;
+#endif
+#ifdef USE_LOCALE
+ CONST char *s2;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ sign = nz0 = nz = 0;
+ dval(rv) = 0.;
+ for(s = s00;; s++) switch(*s) {
+ case '-':
+ sign = 1;
+ /* no break */
+ case '+':
+ if (*++s) {
+ goto break2;
+ }
+ /* no break */
+ case 0:
+ goto ret0;
+ case '\t':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\r':
+ case ' ':
+ continue;
+ default:
+ goto break2;
+ }
+break2:
+ if (*s == '0') {
+ nz0 = 1;
+ while(*++s == '0') ;
+ if (!*s) {
+ goto ret;
+ }
+ }
+ s0 = s;
+ y = z = 0;
+ for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++)
+ if (nd < 9) {
+ y = 10*y + c - '0';
+ }
+ else if (nd < 16) {
+ z = 10*z + c - '0';
+ }
+ nd0 = nd;
+#ifdef USE_LOCALE
+ s1 = localeconv()->decimal_point;
+ if (c == *s1) {
+ c = '.';
+ if (*++s1) {
+ s2 = s;
+ for(;;) {
+ if (*++s2 != *s1) {
+ c = 0;
+ break;
+ }
+ if (!*++s1) {
+ s = s2;
+ break;
+ }
+ }
+ }
+ }
+#endif
+ if (c == '.') {
+ c = *++s;
+ if (!nd) {
+ for(; c == '0'; c = *++s) {
+ nz++;
+ }
+ if (c > '0' && c <= '9') {
+ s0 = s;
+ nf += nz;
+ nz = 0;
+ goto have_dig;
+ }
+ goto dig_done;
+ }
+ for(; c >= '0' && c <= '9'; c = *++s) {
+have_dig:
+ nz++;
+ if (c -= '0') {
+ nf += nz;
+ for(i = 1; i < nz; i++)
+ if (nd++ < 9) {
+ y *= 10;
+ }
+ else if (nd <= DBL_DIG + 1) {
+ z *= 10;
+ }
+ if (nd++ < 9) {
+ y = 10*y + c;
+ }
+ else if (nd <= DBL_DIG + 1) {
+ z = 10*z + c;
+ }
+ nz = 0;
+ }
+ }
+ }
+dig_done:
+ if (nd > 64 * 1024) {
+ goto ret0;
+ }
+ e = 0;
+ if (c == 'e' || c == 'E') {
+ if (!nd && !nz && !nz0) {
+ goto ret0;
+ }
+ s00 = s;
+ esign = 0;
+ switch(c = *++s) {
+ case '-':
+ esign = 1;
+ case '+':
+ c = *++s;
+ }
+ if (c >= '0' && c <= '9') {
+ while(c == '0') {
+ c = *++s;
+ }
+ if (c > '0' && c <= '9') {
+ L = c - '0';
+ s1 = s;
+ while((c = *++s) >= '0' && c <= '9') {
+ L = 10*L + c - '0';
+ }
+ if (s - s1 > 8 || L > 19999)
+ /* Avoid confusion from exponents
+ * so large that e might overflow.
+ */
+ {
+ e = 19999; /* safe for 16 bit ints */
+ }
+ else {
+ e = (int)L;
+ }
+ if (esign) {
+ e = -e;
+ }
+ }
+ else {
+ e = 0;
+ }
+ }
+ else {
+ s = s00;
+ }
+ }
+ if (!nd) {
+ if (!nz && !nz0) {
+#ifdef INFNAN_CHECK
+ /* Check for Nan and Infinity */
+ switch(c) {
+ case 'i':
+ case 'I':
+ if (match(&s,"nf")) {
+ --s;
+ if (!match(&s,"inity")) {
+ ++s;
+ }
+ word0(rv) = 0x7ff00000;
+ word1(rv) = 0;
+ goto ret;
+ }
+ break;
+ case 'n':
+ case 'N':
+ if (match(&s, "an")) {
+ word0(rv) = NAN_WORD0;
+ word1(rv) = NAN_WORD1;
+#ifndef No_Hex_NaN
+ if (*s == '(') { /*)*/
+ hexnan(&rv, &s);
+ }
+#endif
+ goto ret;
+ }
+ }
+#endif /* INFNAN_CHECK */
+ret0:
+ s = s00;
+ sign = 0;
+ }
+ goto ret;
+ }
+ e1 = e -= nf;
+
+ /* Now we have nd0 digits, starting at s0, followed by a
+ * decimal point, followed by nd-nd0 digits. The number we're
+ * after is the integer represented by those digits times
+ * 10**e */
+
+ if (!nd0) {
+ nd0 = nd;
+ }
+ k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1;
+ dval(rv) = y;
+ if (k > 9) {
+#ifdef SET_INEXACT
+ if (k > DBL_DIG) {
+ oldinexact = get_inexact();
+ }
+#endif
+ dval(rv) = tens[k - 9] * dval(rv) + z;
+ }
+ bd0 = 0;
+ if (nd <= DBL_DIG
+#ifndef RND_PRODQUOT
+#ifndef Honor_FLT_ROUNDS
+ && Flt_Rounds == 1
+#endif
+#endif
+ ) {
+ if (!e) {
+ goto ret;
+ }
+ if (e > 0) {
+ if (e <= Ten_pmax) {
+#ifdef VAX
+ goto vax_ovfl_check;
+#else
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv = -rv;
+ sign = 0;
+ }
+#endif
+ /* rv = */ rounded_product(dval(rv), tens[e]);
+ goto ret;
+#endif
+ }
+ i = DBL_DIG - nd;
+ if (e <= Ten_pmax + i) {
+ /* A fancier test would sometimes let us do
+ * this for larger i values.
+ */
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv = -rv;
+ sign = 0;
+ }
+#endif
+ e -= i;
+ dval(rv) *= tens[i];
+#ifdef VAX
+ /* VAX exponent range is so narrow we must
+ * worry about overflow here...
+ */
+vax_ovfl_check:
+ word0(rv) -= P*Exp_msk1;
+ /* rv = */ rounded_product(dval(rv), tens[e]);
+ if ((word0(rv) & Exp_mask)
+ > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) {
+ goto ovfl;
+ }
+ word0(rv) += P*Exp_msk1;
+#else
+ /* rv = */ rounded_product(dval(rv), tens[e]);
+#endif
+ goto ret;
+ }
+ }
+#ifndef Inaccurate_Divide
+ else if (e >= -Ten_pmax) {
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv = -rv;
+ sign = 0;
+ }
+#endif
+ /* rv = */ rounded_quotient(dval(rv), tens[-e]);
+ goto ret;
+ }
+#endif
+ }
+ e1 += nd - k;
+
+#ifdef IEEE_Arith
+#ifdef SET_INEXACT
+ inexact = 1;
+ if (k <= DBL_DIG) {
+ oldinexact = get_inexact();
+ }
+#endif
+#ifdef Avoid_Underflow
+ scale = 0;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ if ((rounding = Flt_Rounds) >= 2) {
+ if (sign) {
+ rounding = rounding == 2 ? 0 : 2;
+ }
+ else if (rounding != 2) {
+ rounding = 0;
+ }
+ }
+#endif
+#endif /*IEEE_Arith*/
+
+ /* Get starting approximation = rv * 10**e1 */
+
+ if (e1 > 0) {
+ if (i = e1 & 15) {
+ dval(rv) *= tens[i];
+ }
+ if (e1 &= ~15) {
+ if (e1 > DBL_MAX_10_EXP) {
+ovfl:
+#ifndef NO_ERRNO
+ PR_SetError(PR_RANGE_ERROR, 0);
+#endif
+ /* Can't trust HUGE_VAL */
+#ifdef IEEE_Arith
+#ifdef Honor_FLT_ROUNDS
+ switch(rounding) {
+ case 0: /* toward 0 */
+ case 3: /* toward -infinity */
+ word0(rv) = Big0;
+ word1(rv) = Big1;
+ break;
+ default:
+ word0(rv) = Exp_mask;
+ word1(rv) = 0;
+ }
+#else /*Honor_FLT_ROUNDS*/
+ word0(rv) = Exp_mask;
+ word1(rv) = 0;
+#endif /*Honor_FLT_ROUNDS*/
+#ifdef SET_INEXACT
+ /* set overflow bit */
+ dval(rv0) = 1e300;
+ dval(rv0) *= dval(rv0);
+#endif
+#else /*IEEE_Arith*/
+ word0(rv) = Big0;
+ word1(rv) = Big1;
+#endif /*IEEE_Arith*/
+ if (bd0) {
+ goto retfree;
+ }
+ goto ret;
+ }
+ e1 >>= 4;
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1) {
+ dval(rv) *= bigtens[j];
+ }
+ /* The last multiplication could overflow. */
+ word0(rv) -= P*Exp_msk1;
+ dval(rv) *= bigtens[j];
+ if ((z = word0(rv) & Exp_mask)
+ > Exp_msk1*(DBL_MAX_EXP+Bias-P)) {
+ goto ovfl;
+ }
+ if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) {
+ /* set to largest number */
+ /* (Can't trust DBL_MAX) */
+ word0(rv) = Big0;
+ word1(rv) = Big1;
+ }
+ else {
+ word0(rv) += P*Exp_msk1;
+ }
+ }
+ }
+ else if (e1 < 0) {
+ e1 = -e1;
+ if (i = e1 & 15) {
+ dval(rv) /= tens[i];
+ }
+ if (e1 >>= 4) {
+ if (e1 >= 1 << n_bigtens) {
+ goto undfl;
+ }
+#ifdef Avoid_Underflow
+ if (e1 & Scale_Bit) {
+ scale = 2*P;
+ }
+ for(j = 0; e1 > 0; j++, e1 >>= 1)
+ if (e1 & 1) {
+ dval(rv) *= tinytens[j];
+ }
+ if (scale && (j = 2*P + 1 - ((word0(rv) & Exp_mask)
+ >> Exp_shift)) > 0) {
+ /* scaled rv is denormal; zap j low bits */
+ if (j >= 32) {
+ word1(rv) = 0;
+ if (j >= 53) {
+ word0(rv) = (P+2)*Exp_msk1;
+ }
+ else {
+ word0(rv) &= 0xffffffff << j-32;
+ }
+ }
+ else {
+ word1(rv) &= 0xffffffff << j;
+ }
+ }
+#else
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1) {
+ dval(rv) *= tinytens[j];
+ }
+ /* The last multiplication could underflow. */
+ dval(rv0) = dval(rv);
+ dval(rv) *= tinytens[j];
+ if (!dval(rv)) {
+ dval(rv) = 2.*dval(rv0);
+ dval(rv) *= tinytens[j];
+#endif
+ if (!dval(rv)) {
+undfl:
+ dval(rv) = 0.;
+#ifndef NO_ERRNO
+ PR_SetError(PR_RANGE_ERROR, 0);
+#endif
+ if (bd0) {
+ goto retfree;
+ }
+ goto ret;
+ }
+#ifndef Avoid_Underflow
+ word0(rv) = Tiny0;
+ word1(rv) = Tiny1;
+ /* The refinement below will clean
+ * this approximation up.
+ */
+ }
+#endif
+ }
+}
+
+/* Now the hard part -- adjusting rv to the correct value.*/
+
+/* Put digits into bd: true value = bd * 10^e */
+
+bd0 = s2b(s0, nd0, nd, y);
+
+for(;;) {
+ bd = Balloc(bd0->k);
+ Bcopy(bd, bd0);
+ bb = d2b(dval(rv), &bbe, &bbbits); /* rv = bb * 2^bbe */
+ bs = i2b(1);
+
+ if (e >= 0) {
+ bb2 = bb5 = 0;
+ bd2 = bd5 = e;
+ }
+ else {
+ bb2 = bb5 = -e;
+ bd2 = bd5 = 0;
+ }
+ if (bbe >= 0) {
+ bb2 += bbe;
+ }
+ else {
+ bd2 -= bbe;
+ }
+ bs2 = bb2;
+#ifdef Honor_FLT_ROUNDS
+ if (rounding != 1) {
+ bs2++;
+ }
+#endif
+#ifdef Avoid_Underflow
+ j = bbe - scale;
+ i = j + bbbits - 1; /* logb(rv) */
+ if (i < Emin) { /* denormal */
+ j += P - Emin;
+ }
+ else {
+ j = P + 1 - bbbits;
+ }
+#else /*Avoid_Underflow*/
+#ifdef Sudden_Underflow
+#ifdef IBM
+ j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3);
+#else
+ j = P + 1 - bbbits;
+#endif
+#else /*Sudden_Underflow*/
+ j = bbe;
+ i = j + bbbits - 1; /* logb(rv) */
+ if (i < Emin) { /* denormal */
+ j += P - Emin;
+ }
+ else {
+ j = P + 1 - bbbits;
+ }
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ bb2 += j;
+ bd2 += j;
+#ifdef Avoid_Underflow
+ bd2 += scale;
+#endif
+ i = bb2 < bd2 ? bb2 : bd2;
+ if (i > bs2) {
+ i = bs2;
+ }
+ if (i > 0) {
+ bb2 -= i;
+ bd2 -= i;
+ bs2 -= i;
+ }
+ if (bb5 > 0) {
+ bs = pow5mult(bs, bb5);
+ bb1 = mult(bs, bb);
+ Bfree(bb);
+ bb = bb1;
+ }
+ if (bb2 > 0) {
+ bb = lshift(bb, bb2);
+ }
+ if (bd5 > 0) {
+ bd = pow5mult(bd, bd5);
+ }
+ if (bd2 > 0) {
+ bd = lshift(bd, bd2);
+ }
+ if (bs2 > 0) {
+ bs = lshift(bs, bs2);
+ }
+ delta = diff(bb, bd);
+ dsign = delta->sign;
+ delta->sign = 0;
+ i = cmp(delta, bs);
+#ifdef Honor_FLT_ROUNDS
+ if (rounding != 1) {
+ if (i < 0) {
+ /* Error is less than an ulp */
+ if (!delta->x[0] && delta->wds <= 1) {
+ /* exact */
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ break;
+ }
+ if (rounding) {
+ if (dsign) {
+ adj = 1.;
+ goto apply_adj;
+ }
+ }
+ else if (!dsign) {
+ adj = -1.;
+ if (!word1(rv)
+ && !(word0(rv) & Frac_mask)) {
+ y = word0(rv) & Exp_mask;
+#ifdef Avoid_Underflow
+ if (!scale || y > 2*P*Exp_msk1)
+#else
+ if (y)
+#endif
+ {
+ delta = lshift(delta,Log2P);
+ if (cmp(delta, bs) <= 0) {
+ adj = -0.5;
+ }
+ }
+ }
+apply_adj:
+#ifdef Avoid_Underflow
+ if (scale && (y = word0(rv) & Exp_mask)
+ <= 2*P*Exp_msk1) {
+ word0(adj) += (2*P+1)*Exp_msk1 - y;
+ }
+#else
+#ifdef Sudden_Underflow
+ if ((word0(rv) & Exp_mask) <=
+ P*Exp_msk1) {
+ word0(rv) += P*Exp_msk1;
+ dval(rv) += adj*ulp(dval(rv));
+ word0(rv) -= P*Exp_msk1;
+ }
+ else
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ dval(rv) += adj*ulp(dval(rv));
+ }
+ break;
+ }
+ adj = ratio(delta, bs);
+ if (adj < 1.) {
+ adj = 1.;
+ }
+ if (adj <= 0x7ffffffe) {
+ /* adj = rounding ? ceil(adj) : floor(adj); */
+ y = adj;
+ if (y != adj) {
+ if (!((rounding>>1) ^ dsign)) {
+ y++;
+ }
+ adj = y;
+ }
+ }
+#ifdef Avoid_Underflow
+ if (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) {
+ word0(adj) += (2*P+1)*Exp_msk1 - y;
+ }
+#else
+#ifdef Sudden_Underflow
+ if ((word0(rv) & Exp_mask) <= P*Exp_msk1) {
+ word0(rv) += P*Exp_msk1;
+ adj *= ulp(dval(rv));
+ if (dsign) {
+ dval(rv) += adj;
+ }
+ else {
+ dval(rv) -= adj;
+ }
+ word0(rv) -= P*Exp_msk1;
+ goto cont;
+ }
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ adj *= ulp(dval(rv));
+ if (dsign) {
+ dval(rv) += adj;
+ }
+ else {
+ dval(rv) -= adj;
+ }
+ goto cont;
+ }
+#endif /*Honor_FLT_ROUNDS*/
+
+ if (i < 0) {
+ /* Error is less than half an ulp -- check for
+ * special case of mantissa a power of two.
+ */
+ if (dsign || word1(rv) || word0(rv) & Bndry_mask
+#ifdef IEEE_Arith
+#ifdef Avoid_Underflow
+ || (word0(rv) & Exp_mask) <= (2*P+1)*Exp_msk1
+#else
+ || (word0(rv) & Exp_mask) <= Exp_msk1
+#endif
+#endif
+ ) {
+#ifdef SET_INEXACT
+ if (!delta->x[0] && delta->wds <= 1) {
+ inexact = 0;
+ }
+#endif
+ break;
+ }
+ if (!delta->x[0] && delta->wds <= 1) {
+ /* exact result */
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ break;
+ }
+ delta = lshift(delta,Log2P);
+ if (cmp(delta, bs) > 0) {
+ goto drop_down;
+ }
+ break;
+ }
+ if (i == 0) {
+ /* exactly half-way between */
+ if (dsign) {
+ if ((word0(rv) & Bndry_mask1) == Bndry_mask1
+ && word1(rv) == (
+#ifdef Avoid_Underflow
+ (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1)
+ ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) :
+#endif
+ 0xffffffff)) {
+ /*boundary case -- increment exponent*/
+ word0(rv) = (word0(rv) & Exp_mask)
+ + Exp_msk1
+#ifdef IBM
+ | Exp_msk1 >> 4
+#endif
+ ;
+ word1(rv) = 0;
+#ifdef Avoid_Underflow
+ dsign = 0;
+#endif
+ break;
+ }
+ }
+ else if (!(word0(rv) & Bndry_mask) && !word1(rv)) {
+drop_down:
+ /* boundary case -- decrement exponent */
+#ifdef Sudden_Underflow /*{{*/
+ L = word0(rv) & Exp_mask;
+#ifdef IBM
+ if (L < Exp_msk1)
+#else
+#ifdef Avoid_Underflow
+ if (L <= (scale ? (2*P+1)*Exp_msk1 : Exp_msk1))
+#else
+ if (L <= Exp_msk1)
+#endif /*Avoid_Underflow*/
+#endif /*IBM*/
+ goto undfl;
+ L -= Exp_msk1;
+#else /*Sudden_Underflow}{*/
+#ifdef Avoid_Underflow
+ if (scale) {
+ L = word0(rv) & Exp_mask;
+ if (L <= (2*P+1)*Exp_msk1) {
+ if (L > (P+2)*Exp_msk1)
+ /* round even ==> */
+ /* accept rv */
+ {
+ break;
+ }
+ /* rv = smallest denormal */
+ goto undfl;
+ }
+ }
+#endif /*Avoid_Underflow*/
+ L = (word0(rv) & Exp_mask) - Exp_msk1;
+#endif /*Sudden_Underflow}}*/
+ word0(rv) = L | Bndry_mask1;
+ word1(rv) = 0xffffffff;
+#ifdef IBM
+ goto cont;
+#else
+ break;
+#endif
+ }
+#ifndef ROUND_BIASED
+ if (!(word1(rv) & LSB)) {
+ break;
+ }
+#endif
+ if (dsign) {
+ dval(rv) += ulp(dval(rv));
+ }
+#ifndef ROUND_BIASED
+ else {
+ dval(rv) -= ulp(dval(rv));
+#ifndef Sudden_Underflow
+ if (!dval(rv)) {
+ goto undfl;
+ }
+#endif
+ }
+#ifdef Avoid_Underflow
+ dsign = 1 - dsign;
+#endif
+#endif
+ break;
+ }
+ if ((aadj = ratio(delta, bs)) <= 2.) {
+ if (dsign) {
+ aadj = aadj1 = 1.;
+ }
+ else if (word1(rv) || word0(rv) & Bndry_mask) {
+#ifndef Sudden_Underflow
+ if (word1(rv) == Tiny1 && !word0(rv)) {
+ goto undfl;
+ }
+#endif
+ aadj = 1.;
+ aadj1 = -1.;
+ }
+ else {
+ /* special case -- power of FLT_RADIX to be */
+ /* rounded down... */
+
+ if (aadj < 2./FLT_RADIX) {
+ aadj = 1./FLT_RADIX;
+ }
+ else {
+ aadj *= 0.5;
+ }
+ aadj1 = -aadj;
+ }
+ }
+ else {
+ aadj *= 0.5;
+ aadj1 = dsign ? aadj : -aadj;
+#ifdef Check_FLT_ROUNDS
+ switch(Rounding) {
+ case 2: /* towards +infinity */
+ aadj1 -= 0.5;
+ break;
+ case 0: /* towards 0 */
+ case 3: /* towards -infinity */
+ aadj1 += 0.5;
+ }
+#else
+ if (Flt_Rounds == 0) {
+ aadj1 += 0.5;
+ }
+#endif /*Check_FLT_ROUNDS*/
+ }
+ y = word0(rv) & Exp_mask;
+
+ /* Check for overflow */
+
+ if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) {
+ dval(rv0) = dval(rv);
+ word0(rv) -= P*Exp_msk1;
+ adj = aadj1 * ulp(dval(rv));
+ dval(rv) += adj;
+ if ((word0(rv) & Exp_mask) >=
+ Exp_msk1*(DBL_MAX_EXP+Bias-P)) {
+ if (word0(rv0) == Big0 && word1(rv0) == Big1) {
+ goto ovfl;
+ }
+ word0(rv) = Big0;
+ word1(rv) = Big1;
+ goto cont;
+ }
+ else {
+ word0(rv) += P*Exp_msk1;
+ }
+ }
+ else {
+#ifdef Avoid_Underflow
+ if (scale && y <= 2*P*Exp_msk1) {
+ if (aadj <= 0x7fffffff) {
+ if ((z = aadj) <= 0) {
+ z = 1;
+ }
+ aadj = z;
+ aadj1 = dsign ? aadj : -aadj;
+ }
+ dval(aadj2) = aadj1;
+ word0(aadj2) += (2*P+1)*Exp_msk1 - y;
+ aadj1 = dval(aadj2);
+ }
+ adj = aadj1 * ulp(dval(rv));
+ dval(rv) += adj;
+#else
+#ifdef Sudden_Underflow
+ if ((word0(rv) & Exp_mask) <= P*Exp_msk1) {
+ dval(rv0) = dval(rv);
+ word0(rv) += P*Exp_msk1;
+ adj = aadj1 * ulp(dval(rv));
+ dval(rv) += adj;
+#ifdef IBM
+ if ((word0(rv) & Exp_mask) < P*Exp_msk1)
+#else
+ if ((word0(rv) & Exp_mask) <= P*Exp_msk1)
+#endif
+ {
+ if (word0(rv0) == Tiny0
+ && word1(rv0) == Tiny1) {
+ goto undfl;
+ }
+ word0(rv) = Tiny0;
+ word1(rv) = Tiny1;
+ goto cont;
+ }
+ else {
+ word0(rv) -= P*Exp_msk1;
+ }
+ }
+ else {
+ adj = aadj1 * ulp(dval(rv));
+ dval(rv) += adj;
+ }
+#else /*Sudden_Underflow*/
+ /* Compute adj so that the IEEE rounding rules will
+ * correctly round rv + adj in some half-way cases.
+ * If rv * ulp(rv) is denormalized (i.e.,
+ * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid
+ * trouble from bits lost to denormalization;
+ * example: 1.2e-307 .
+ */
+ if (y <= (P-1)*Exp_msk1 && aadj > 1.) {
+ aadj1 = (double)(int)(aadj + 0.5);
+ if (!dsign) {
+ aadj1 = -aadj1;
+ }
+ }
+ adj = aadj1 * ulp(dval(rv));
+ dval(rv) += adj;
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ }
+ z = word0(rv) & Exp_mask;
+#ifndef SET_INEXACT
+#ifdef Avoid_Underflow
+ if (!scale)
+#endif
+ if (y == z) {
+ /* Can we stop now? */
+ L = (Long)aadj;
+ aadj -= L;
+ /* The tolerances below are conservative. */
+ if (dsign || word1(rv) || word0(rv) & Bndry_mask) {
+ if (aadj < .4999999 || aadj > .5000001) {
+ break;
+ }
+ }
+ else if (aadj < .4999999/FLT_RADIX) {
+ break;
+ }
+ }
+#endif
+cont:
+ Bfree(bb);
+ Bfree(bd);
+ Bfree(bs);
+ Bfree(delta);
+}
+#ifdef SET_INEXACT
+if (inexact) {
+ if (!oldinexact) {
+ word0(rv0) = Exp_1 + (70 << Exp_shift);
+ word1(rv0) = 0;
+ dval(rv0) += 1.;
+ }
+}
+else if (!oldinexact) {
+ clear_inexact();
+}
+#endif
+#ifdef Avoid_Underflow
+if (scale) {
+ word0(rv0) = Exp_1 - 2*P*Exp_msk1;
+ word1(rv0) = 0;
+ dval(rv) *= dval(rv0);
+#ifndef NO_ERRNO
+ /* try to avoid the bug of testing an 8087 register value */
+ if (word0(rv) == 0 && word1(rv) == 0) {
+ PR_SetError(PR_RANGE_ERROR, 0);
+ }
+#endif
+}
+#endif /* Avoid_Underflow */
+#ifdef SET_INEXACT
+if (inexact && !(word0(rv) & Exp_mask)) {
+ /* set underflow bit */
+ dval(rv0) = 1e-300;
+ dval(rv0) *= dval(rv0);
+}
+#endif
+retfree:
+Bfree(bb);
+Bfree(bd);
+Bfree(bs);
+Bfree(bd0);
+Bfree(delta);
+ret:
+if (se) {
+ *se = (char *)s;
+}
+return sign ? -dval(rv) : dval(rv);
+}
+
+static int
+quorem
+#ifdef KR_headers
+(b, S) Bigint *b, *S;
+#else
+(Bigint *b, Bigint *S)
+#endif
+{
+ int n;
+ ULong *bx, *bxe, q, *sx, *sxe;
+#ifdef ULLong
+ ULLong borrow, carry, y, ys;
+#else
+ ULong borrow, carry, y, ys;
+#ifdef Pack_32
+ ULong si, z, zs;
+#endif
+#endif
+
+ n = S->wds;
+#ifdef DEBUG
+ /*debug*/ if (b->wds > n)
+ /*debug*/{
+ Bug("oversize b in quorem");
+ }
+#endif
+ if (b->wds < n) {
+ return 0;
+ }
+ sx = S->x;
+ sxe = sx + --n;
+ bx = b->x;
+ bxe = bx + n;
+ q = *bxe / (*sxe + 1); /* ensure q <= true quotient */
+#ifdef DEBUG
+ /*debug*/ if (q > 9)
+ /*debug*/{
+ Bug("oversized quotient in quorem");
+ }
+#endif
+ if (q) {
+ borrow = 0;
+ carry = 0;
+ do {
+#ifdef ULLong
+ ys = *sx++ * (ULLong)q + carry;
+ carry = ys >> 32;
+ y = *bx - (ys & FFFFFFFF) - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *bx++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) * q + carry;
+ zs = (si >> 16) * q + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ * q + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *bx++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(sx <= sxe);
+ if (!*bxe) {
+ bx = b->x;
+ while(--bxe > bx && !*bxe) {
+ --n;
+ }
+ b->wds = n;
+ }
+ }
+ if (cmp(b, S) >= 0) {
+ q++;
+ borrow = 0;
+ carry = 0;
+ bx = b->x;
+ sx = S->x;
+ do {
+#ifdef ULLong
+ ys = *sx++ + carry;
+ carry = ys >> 32;
+ y = *bx - (ys & FFFFFFFF) - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *bx++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) + carry;
+ zs = (si >> 16) + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *bx++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(sx <= sxe);
+ bx = b->x;
+ bxe = bx + n;
+ if (!*bxe) {
+ while(--bxe > bx && !*bxe) {
+ --n;
+ }
+ b->wds = n;
+ }
+ }
+ return q;
+}
+
+#ifndef MULTIPLE_THREADS
+static char *dtoa_result;
+#endif
+
+static char *
+#ifdef KR_headers
+rv_alloc(i) int i;
+#else
+rv_alloc(int i)
+#endif
+{
+ int j, k, *r;
+
+ j = sizeof(ULong);
+ for(k = 0;
+ sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i;
+ j <<= 1) {
+ k++;
+ }
+ r = (int*)Balloc(k);
+ *r = k;
+ return
+#ifndef MULTIPLE_THREADS
+ dtoa_result =
+#endif
+ (char *)(r+1);
+}
+
+static char *
+#ifdef KR_headers
+nrv_alloc(s, rve, n) char *s, **rve; int n;
+#else
+nrv_alloc(char *s, char **rve, int n)
+#endif
+{
+ char *rv, *t;
+
+ t = rv = rv_alloc(n);
+ while(*t = *s++) {
+ t++;
+ }
+ if (rve) {
+ *rve = t;
+ }
+ return rv;
+}
+
+/* freedtoa(s) must be used to free values s returned by dtoa
+ * when MULTIPLE_THREADS is #defined. It should be used in all cases,
+ * but for consistency with earlier versions of dtoa, it is optional
+ * when MULTIPLE_THREADS is not defined.
+ */
+
+static void
+#ifdef KR_headers
+freedtoa(s) char *s;
+#else
+freedtoa(char *s)
+#endif
+{
+ Bigint *b = (Bigint *)((int *)s - 1);
+ b->maxwds = 1 << (b->k = *(int*)b);
+ Bfree(b);
+#ifndef MULTIPLE_THREADS
+ if (s == dtoa_result) {
+ dtoa_result = 0;
+ }
+#endif
+}
+
+/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
+ *
+ * Inspired by "How to Print Floating-Point Numbers Accurately" by
+ * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126].
+ *
+ * Modifications:
+ * 1. Rather than iterating, we use a simple numeric overestimate
+ * to determine k = floor(log10(d)). We scale relevant
+ * quantities using O(log2(k)) rather than O(k) multiplications.
+ * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
+ * try to generate digits strictly left to right. Instead, we
+ * compute with fewer bits and propagate the carry if necessary
+ * when rounding the final digit up. This is often faster.
+ * 3. Under the assumption that input will be rounded nearest,
+ * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
+ * That is, we allow equality in stopping tests when the
+ * round-nearest rule will give the same floating-point value
+ * as would satisfaction of the stopping test with strict
+ * inequality.
+ * 4. We remove common factors of powers of 2 from relevant
+ * quantities.
+ * 5. When converting floating-point integers less than 1e16,
+ * we use floating-point arithmetic rather than resorting
+ * to multiple-precision integers.
+ * 6. When asked to produce fewer than 15 digits, we first try
+ * to get by with floating-point arithmetic; we resort to
+ * multiple-precision integer arithmetic only if we cannot
+ * guarantee that the floating-point calculation has given
+ * the correctly rounded result. For k requested digits and
+ * "uniformly" distributed input, the probability is
+ * something like 10^(k-15) that we must resort to the Long
+ * calculation.
+ */
+
+static char *
+dtoa
+#ifdef KR_headers
+(dd, mode, ndigits, decpt, sign, rve)
+double dd; int mode, ndigits, *decpt, *sign; char **rve;
+#else
+(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve)
+#endif
+{
+ /* Arguments ndigits, decpt, sign are similar to those
+ of ecvt and fcvt; trailing zeros are suppressed from
+ the returned string. If not null, *rve is set to point
+ to the end of the return value. If d is +-Infinity or NaN,
+ then *decpt is set to 9999.
+
+ mode:
+ 0 ==> shortest string that yields d when read in
+ and rounded to nearest.
+ 1 ==> like 0, but with Steele & White stopping rule;
+ e.g. with IEEE P754 arithmetic , mode 0 gives
+ 1e23 whereas mode 1 gives 9.999999999999999e22.
+ 2 ==> max(1,ndigits) significant digits. This gives a
+ return value similar to that of ecvt, except
+ that trailing zeros are suppressed.
+ 3 ==> through ndigits past the decimal point. This
+ gives a return value similar to that from fcvt,
+ except that trailing zeros are suppressed, and
+ ndigits can be negative.
+ 4,5 ==> similar to 2 and 3, respectively, but (in
+ round-nearest mode) with the tests of mode 0 to
+ possibly return a shorter string that rounds to d.
+ With IEEE arithmetic and compilation with
+ -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same
+ as modes 2 and 3 when FLT_ROUNDS != 1.
+ 6-9 ==> Debugging modes similar to mode - 4: don't try
+ fast floating-point estimate (if applicable).
+
+ Values of mode other than 0-9 are treated as mode 0.
+
+ Sufficient space is allocated to the return value
+ to hold the suppressed trailing zeros.
+ */
+
+ int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1,
+ j, j1, k, k0, k_check, leftright, m2, m5, s2, s5,
+ spec_case, try_quick;
+ Long L;
+#ifndef Sudden_Underflow
+ int denorm;
+ ULong x;
+#endif
+ Bigint *b, *b1, *delta, *mlo, *mhi, *S;
+ U d, d2, eps;
+ double ds;
+ char *s, *s0;
+#ifdef Honor_FLT_ROUNDS
+ int rounding;
+#endif
+#ifdef SET_INEXACT
+ int inexact, oldinexact;
+#endif
+
+#ifndef MULTIPLE_THREADS
+ if (dtoa_result) {
+ freedtoa(dtoa_result);
+ dtoa_result = 0;
+ }
+#endif
+
+ dval(d) = dd;
+ if (word0(d) & Sign_bit) {
+ /* set sign for everything, including 0's and NaNs */
+ *sign = 1;
+ word0(d) &= ~Sign_bit; /* clear sign bit */
+ }
+ else {
+ *sign = 0;
+ }
+
+#if defined(IEEE_Arith) + defined(VAX)
+#ifdef IEEE_Arith
+ if ((word0(d) & Exp_mask) == Exp_mask)
+#else
+ if (word0(d) == 0x8000)
+#endif
+ {
+ /* Infinity or NaN */
+ *decpt = 9999;
+#ifdef IEEE_Arith
+ if (!word1(d) && !(word0(d) & 0xfffff)) {
+ return nrv_alloc("Infinity", rve, 8);
+ }
+#endif
+ return nrv_alloc("NaN", rve, 3);
+ }
+#endif
+#ifdef IBM
+ dval(d) += 0; /* normalize */
+#endif
+ if (!dval(d)) {
+ *decpt = 1;
+ return nrv_alloc("0", rve, 1);
+ }
+
+#ifdef SET_INEXACT
+ try_quick = oldinexact = get_inexact();
+ inexact = 1;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ if ((rounding = Flt_Rounds) >= 2) {
+ if (*sign) {
+ rounding = rounding == 2 ? 0 : 2;
+ }
+ else if (rounding != 2) {
+ rounding = 0;
+ }
+ }
+#endif
+
+ b = d2b(dval(d), &be, &bbits);
+#ifdef Sudden_Underflow
+ i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1));
+#else
+ if (i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) {
+#endif
+ dval(d2) = dval(d);
+ word0(d2) &= Frac_mask1;
+ word0(d2) |= Exp_11;
+#ifdef IBM
+ if (j = 11 - hi0bits(word0(d2) & Frac_mask)) {
+ dval(d2) /= 1 << j;
+ }
+#endif
+
+ /* log(x) ~=~ log(1.5) + (x-1.5)/1.5
+ * log10(x) = log(x) / log(10)
+ * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
+ * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)
+ *
+ * This suggests computing an approximation k to log10(d) by
+ *
+ * k = (i - Bias)*0.301029995663981
+ * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
+ *
+ * We want k to be too large rather than too small.
+ * The error in the first-order Taylor series approximation
+ * is in our favor, so we just round up the constant enough
+ * to compensate for any error in the multiplication of
+ * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,
+ * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
+ * adding 1e-13 to the constant term more than suffices.
+ * Hence we adjust the constant term to 0.1760912590558.
+ * (We could get a more accurate k by invoking log10,
+ * but this is probably not worthwhile.)
+ */
+
+ i -= Bias;
+#ifdef IBM
+ i <<= 2;
+ i += j;
+#endif
+#ifndef Sudden_Underflow
+ denorm = 0;
+}
+else {
+ /* d is denormalized */
+
+ i = bbits + be + (Bias + (P-1) - 1);
+ x = i > 32 ? word0(d) << 64 - i | word1(d) >> i - 32
+ : word1(d) << 32 - i;
+ dval(d2) = x;
+ word0(d2) -= 31*Exp_msk1; /* adjust exponent */
+ i -= (Bias + (P-1) - 1) + 1;
+ denorm = 1;
+}
+#endif
+ds = (dval(d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
+k = (int)ds;
+if (ds < 0. && ds != k) {
+ k--; /* want k = floor(ds) */
+}
+k_check = 1;
+if (k >= 0 && k <= Ten_pmax) {
+ if (dval(d) < tens[k]) {
+ k--;
+ }
+ k_check = 0;
+}
+j = bbits - i - 1;
+if (j >= 0) {
+ b2 = 0;
+ s2 = j;
+}
+else {
+ b2 = -j;
+ s2 = 0;
+}
+if (k >= 0) {
+ b5 = 0;
+ s5 = k;
+ s2 += k;
+}
+else {
+ b2 -= k;
+ b5 = -k;
+ s5 = 0;
+}
+if (mode < 0 || mode > 9) {
+ mode = 0;
+}
+
+#ifndef SET_INEXACT
+#ifdef Check_FLT_ROUNDS
+try_quick = Rounding == 1;
+#else
+try_quick = 1;
+#endif
+#endif /*SET_INEXACT*/
+
+if (mode > 5) {
+ mode -= 4;
+ try_quick = 0;
+}
+leftright = 1;
+switch(mode) {
+case 0:
+case 1:
+ ilim = ilim1 = -1;
+ i = 18;
+ ndigits = 0;
+ break;
+case 2:
+ leftright = 0;
+/* no break */
+case 4:
+ if (ndigits <= 0) {
+ ndigits = 1;
+ }
+ ilim = ilim1 = i = ndigits;
+ break;
+case 3:
+ leftright = 0;
+/* no break */
+case 5:
+ i = ndigits + k + 1;
+ ilim = i;
+ ilim1 = i - 1;
+ if (i <= 0) {
+ i = 1;
+ }
+}
+s = s0 = rv_alloc(i);
+
+#ifdef Honor_FLT_ROUNDS
+if (mode > 1 && rounding != 1) {
+ leftright = 0;
+}
+#endif
+
+if (ilim >= 0 && ilim <= Quick_max && try_quick) {
+
+ /* Try to get by with floating-point arithmetic. */
+
+ i = 0;
+ dval(d2) = dval(d);
+ k0 = k;
+ ilim0 = ilim;
+ ieps = 2; /* conservative */
+ if (k > 0) {
+ ds = tens[k&0xf];
+ j = k >> 4;
+ if (j & Bletch) {
+ /* prevent overflows */
+ j &= Bletch - 1;
+ dval(d) /= bigtens[n_bigtens-1];
+ ieps++;
+ }
+ for(; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ ds *= bigtens[i];
+ }
+ dval(d) /= ds;
+ }
+ else if (j1 = -k) {
+ dval(d) *= tens[j1 & 0xf];
+ for(j = j1 >> 4; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ dval(d) *= bigtens[i];
+ }
+ }
+ if (k_check && dval(d) < 1. && ilim > 0) {
+ if (ilim1 <= 0) {
+ goto fast_failed;
+ }
+ ilim = ilim1;
+ k--;
+ dval(d) *= 10.;
+ ieps++;
+ }
+ dval(eps) = ieps*dval(d) + 7.;
+ word0(eps) -= (P-1)*Exp_msk1;
+ if (ilim == 0) {
+ S = mhi = 0;
+ dval(d) -= 5.;
+ if (dval(d) > dval(eps)) {
+ goto one_digit;
+ }
+ if (dval(d) < -dval(eps)) {
+ goto no_digits;
+ }
+ goto fast_failed;
+ }
+#ifndef No_leftright
+ if (leftright) {
+ /* Use Steele & White method of only
+ * generating digits needed.
+ */
+ dval(eps) = 0.5/tens[ilim-1] - dval(eps);
+ for(i = 0;;) {
+ L = dval(d);
+ dval(d) -= L;
+ *s++ = '0' + (int)L;
+ if (dval(d) < dval(eps)) {
+ goto ret1;
+ }
+ if (1. - dval(d) < dval(eps)) {
+ goto bump_up;
+ }
+ if (++i >= ilim) {
+ break;
+ }
+ dval(eps) *= 10.;
+ dval(d) *= 10.;
+ }
+ }
+ else {
+#endif
+ /* Generate ilim digits, then fix them up. */
+ dval(eps) *= tens[ilim-1];
+ for(i = 1;; i++, dval(d) *= 10.) {
+ L = (Long)(dval(d));
+ if (!(dval(d) -= L)) {
+ ilim = i;
+ }
+ *s++ = '0' + (int)L;
+ if (i == ilim) {
+ if (dval(d) > 0.5 + dval(eps)) {
+ goto bump_up;
+ }
+ else if (dval(d) < 0.5 - dval(eps)) {
+ while(*--s == '0');
+ s++;
+ goto ret1;
+ }
+ break;
+ }
+ }
+#ifndef No_leftright
+ }
+#endif
+fast_failed:
+ s = s0;
+ dval(d) = dval(d2);
+ k = k0;
+ ilim = ilim0;
+}
+
+/* Do we have a "small" integer? */
+
+if (be >= 0 && k <= Int_max) {
+ /* Yes. */
+ ds = tens[k];
+ if (ndigits < 0 && ilim <= 0) {
+ S = mhi = 0;
+ if (ilim < 0 || dval(d) <= 5*ds) {
+ goto no_digits;
+ }
+ goto one_digit;
+ }
+ for(i = 1; i <= k+1; i++, dval(d) *= 10.) {
+ L = (Long)(dval(d) / ds);
+ dval(d) -= L*ds;
+#ifdef Check_FLT_ROUNDS
+ /* If FLT_ROUNDS == 2, L will usually be high by 1 */
+ if (dval(d) < 0) {
+ L--;
+ dval(d) += ds;
+ }
+#endif
+ *s++ = '0' + (int)L;
+ if (!dval(d)) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ break;
+ }
+ if (i == ilim) {
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch(rounding) {
+ case 0: goto ret1;
+ case 2: goto bump_up;
+ }
+#endif
+ dval(d) += dval(d);
+ if (dval(d) > ds || dval(d) == ds && L & 1) {
+bump_up:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s = '0';
+ break;
+ }
+ ++*s++;
+ }
+ break;
+ }
+ }
+ goto ret1;
+}
+
+m2 = b2;
+m5 = b5;
+mhi = mlo = 0;
+if (leftright) {
+ i =
+#ifndef Sudden_Underflow
+ denorm ? be + (Bias + (P-1) - 1 + 1) :
+#endif
+#ifdef IBM
+ 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3);
+#else
+ 1 + P - bbits;
+#endif
+ b2 += i;
+ s2 += i;
+ mhi = i2b(1);
+}
+if (m2 > 0 && s2 > 0) {
+ i = m2 < s2 ? m2 : s2;
+ b2 -= i;
+ m2 -= i;
+ s2 -= i;
+}
+if (b5 > 0) {
+ if (leftright) {
+ if (m5 > 0) {
+ mhi = pow5mult(mhi, m5);
+ b1 = mult(mhi, b);
+ Bfree(b);
+ b = b1;
+ }
+ if (j = b5 - m5) {
+ b = pow5mult(b, j);
+ }
+ }
+ else {
+ b = pow5mult(b, b5);
+ }
+}
+S = i2b(1);
+if (s5 > 0) {
+ S = pow5mult(S, s5);
+}
+
+/* Check for special case that d is a normalized power of 2. */
+
+spec_case = 0;
+if ((mode < 2 || leftright)
+#ifdef Honor_FLT_ROUNDS
+ && rounding == 1
+#endif
+ ) {
+ if (!word1(d) && !(word0(d) & Bndry_mask)
+#ifndef Sudden_Underflow
+ && word0(d) & (Exp_mask & ~Exp_msk1)
+#endif
+ ) {
+ /* The special case */
+ b2 += Log2P;
+ s2 += Log2P;
+ spec_case = 1;
+ }
+}
+
+/* Arrange for convenient computation of quotients:
+ * shift left if necessary so divisor has 4 leading 0 bits.
+ *
+ * Perhaps we should just compute leading 28 bits of S once
+ * and for all and pass them and a shift to quorem, so it
+ * can do shifts and ors to compute the numerator for q.
+ */
+#ifdef Pack_32
+if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) {
+ i = 32 - i;
+}
+#else
+if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) {
+ i = 16 - i;
+}
+#endif
+if (i > 4) {
+ i -= 4;
+ b2 += i;
+ m2 += i;
+ s2 += i;
+}
+else if (i < 4) {
+ i += 28;
+ b2 += i;
+ m2 += i;
+ s2 += i;
+}
+if (b2 > 0) {
+ b = lshift(b, b2);
+}
+if (s2 > 0) {
+ S = lshift(S, s2);
+}
+if (k_check) {
+ if (cmp(b,S) < 0) {
+ k--;
+ b = multadd(b, 10, 0); /* we botched the k estimate */
+ if (leftright) {
+ mhi = multadd(mhi, 10, 0);
+ }
+ ilim = ilim1;
+ }
+}
+if (ilim <= 0 && (mode == 3 || mode == 5)) {
+ if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) {
+ /* no digits, fcvt style */
+no_digits:
+ k = -1 - ndigits;
+ goto ret;
+ }
+one_digit:
+ *s++ = '1';
+ k++;
+ goto ret;
+}
+if (leftright) {
+ if (m2 > 0) {
+ mhi = lshift(mhi, m2);
+ }
+
+ /* Compute mlo -- check for special case
+ * that d is a normalized power of 2.
+ */
+
+ mlo = mhi;
+ if (spec_case) {
+ mhi = Balloc(mhi->k);
+ Bcopy(mhi, mlo);
+ mhi = lshift(mhi, Log2P);
+ }
+
+ for(i = 1;; i++) {
+ dig = quorem(b,S) + '0';
+ /* Do we yet have the shortest decimal string
+ * that will round to d?
+ */
+ j = cmp(b, mlo);
+ delta = diff(S, mhi);
+ j1 = delta->sign ? 1 : cmp(b, delta);
+ Bfree(delta);
+#ifndef ROUND_BIASED
+ if (j1 == 0 && mode != 1 && !(word1(d) & 1)
+#ifdef Honor_FLT_ROUNDS
+ && rounding >= 1
+#endif
+ ) {
+ if (dig == '9') {
+ goto round_9_up;
+ }
+ if (j > 0) {
+ dig++;
+ }
+#ifdef SET_INEXACT
+ else if (!b->x[0] && b->wds <= 1) {
+ inexact = 0;
+ }
+#endif
+ *s++ = dig;
+ goto ret;
+ }
+#endif
+ if (j < 0 || j == 0 && mode != 1
+#ifndef ROUND_BIASED
+ && !(word1(d) & 1)
+#endif
+ ) {
+ if (!b->x[0] && b->wds <= 1) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ goto accept_dig;
+ }
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch(rounding) {
+ case 0: goto accept_dig;
+ case 2: goto keep_dig;
+ }
+#endif /*Honor_FLT_ROUNDS*/
+ if (j1 > 0) {
+ b = lshift(b, 1);
+ j1 = cmp(b, S);
+ if ((j1 > 0 || j1 == 0 && dig & 1)
+ && dig++ == '9') {
+ goto round_9_up;
+ }
+ }
+accept_dig:
+ *s++ = dig;
+ goto ret;
+ }
+ if (j1 > 0) {
+#ifdef Honor_FLT_ROUNDS
+ if (!rounding) {
+ goto accept_dig;
+ }
+#endif
+ if (dig == '9') { /* possible if i == 1 */
+round_9_up:
+ *s++ = '9';
+ goto roundoff;
+ }
+ *s++ = dig + 1;
+ goto ret;
+ }
+#ifdef Honor_FLT_ROUNDS
+keep_dig:
+#endif
+ *s++ = dig;
+ if (i == ilim) {
+ break;
+ }
+ b = multadd(b, 10, 0);
+ if (mlo == mhi) {
+ mlo = mhi = multadd(mhi, 10, 0);
+ }
+ else {
+ mlo = multadd(mlo, 10, 0);
+ mhi = multadd(mhi, 10, 0);
+ }
+ }
+}
+else
+ for(i = 1;; i++) {
+ *s++ = dig = quorem(b,S) + '0';
+ if (!b->x[0] && b->wds <= 1) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ goto ret;
+ }
+ if (i >= ilim) {
+ break;
+ }
+ b = multadd(b, 10, 0);
+ }
+
+/* Round off last digit */
+
+#ifdef Honor_FLT_ROUNDS
+switch(rounding) {
+case 0: goto trimzeros;
+case 2: goto roundoff;
+}
+#endif
+b = lshift(b, 1);
+j = cmp(b, S);
+if (j > 0 || j == 0 && dig & 1) {
+roundoff:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s++ = '1';
+ goto ret;
+ }
+ ++*s++;
+}
+else {
+#ifdef Honor_FLT_ROUNDS
+trimzeros:
+#endif
+ while(*--s == '0');
+ s++;
+}
+ret:
+Bfree(S);
+if (mhi) {
+ if (mlo && mlo != mhi) {
+ Bfree(mlo);
+ }
+ Bfree(mhi);
+}
+ret1:
+#ifdef SET_INEXACT
+if (inexact) {
+ if (!oldinexact) {
+ word0(d) = Exp_1 + (70 << Exp_shift);
+ word1(d) = 0;
+ dval(d) += 1.;
+ }
+}
+else if (!oldinexact) {
+ clear_inexact();
+}
+#endif
+Bfree(b);
+*s = 0;
+*decpt = k + 1;
+if (rve) {
+ *rve = s;
+}
+return s0;
+}
+#ifdef __cplusplus
+}
+#endif
+
+PR_IMPLEMENT(PRStatus)
+PR_dtoa(PRFloat64 d, PRIntn mode, PRIntn ndigits,
+ PRIntn *decpt, PRIntn *sign, char **rve, char *buf, PRSize bufsize)
+{
+ char *result;
+ PRSize resultlen;
+ PRStatus rv = PR_FAILURE;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (mode < 0 || mode > 3) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return rv;
+ }
+ result = dtoa(d, mode, ndigits, decpt, sign, rve);
+ if (!result) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return rv;
+ }
+ resultlen = strlen(result)+1;
+ if (bufsize < resultlen) {
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ } else {
+ memcpy(buf, result, resultlen);
+ if (rve) {
+ *rve = buf + (*rve - result);
+ }
+ rv = PR_SUCCESS;
+ }
+ freedtoa(result);
+ return rv;
+}
+
+/*
+** conversion routines for floating point
+** prcsn - number of digits of precision to generate floating
+** point value.
+** This should be reparameterized so that you can send in a
+** prcn for the positive and negative ranges. For now,
+** conform to the ECMA JavaScript spec which says numbers
+** less than 1e-6 are in scientific notation.
+** Also, the ECMA spec says that there should always be a
+** '+' or '-' after the 'e' in scientific notation
+*/
+PR_IMPLEMENT(void)
+PR_cnvtf(char *buf, int bufsz, int prcsn, double dfval)
+{
+ PRIntn decpt, sign, numdigits;
+ char *num, *nump;
+ char *bufp = buf;
+ char *endnum;
+ U fval;
+
+ dval(fval) = dfval;
+ /* If anything fails, we store an empty string in 'buf' */
+ num = (char*)PR_MALLOC(bufsz);
+ if (num == NULL) {
+ buf[0] = '\0';
+ return;
+ }
+ /* XXX Why use mode 1? */
+ if (PR_dtoa(dval(fval),1,prcsn,&decpt,&sign,&endnum,num,bufsz)
+ == PR_FAILURE) {
+ buf[0] = '\0';
+ goto done;
+ }
+ numdigits = endnum - num;
+ nump = num;
+
+ if (sign &&
+ !(word0(fval) == Sign_bit && word1(fval) == 0) &&
+ !((word0(fval) & Exp_mask) == Exp_mask &&
+ (word1(fval) || (word0(fval) & 0xfffff)))) {
+ *bufp++ = '-';
+ }
+
+ if (decpt == 9999) {
+ while ((*bufp++ = *nump++) != 0) {} /* nothing to execute */
+ goto done;
+ }
+
+ if (decpt > (prcsn+1) || decpt < -(prcsn-1) || decpt < -5) {
+ *bufp++ = *nump++;
+ if (numdigits != 1) {
+ *bufp++ = '.';
+ }
+
+ while (*nump != '\0') {
+ *bufp++ = *nump++;
+ }
+ *bufp++ = 'e';
+ PR_snprintf(bufp, bufsz - (bufp - buf), "%+d", decpt-1);
+ } else if (decpt >= 0) {
+ if (decpt == 0) {
+ *bufp++ = '0';
+ } else {
+ while (decpt--) {
+ if (*nump != '\0') {
+ *bufp++ = *nump++;
+ } else {
+ *bufp++ = '0';
+ }
+ }
+ }
+ if (*nump != '\0') {
+ *bufp++ = '.';
+ while (*nump != '\0') {
+ *bufp++ = *nump++;
+ }
+ }
+ *bufp++ = '\0';
+ } else if (decpt < 0) {
+ *bufp++ = '0';
+ *bufp++ = '.';
+ while (decpt++) {
+ *bufp++ = '0';
+ }
+
+ while (*nump != '\0') {
+ *bufp++ = *nump++;
+ }
+ *bufp++ = '\0';
+ }
+done:
+ PR_DELETE(num);
+}
diff --git a/nsprpub/pr/src/misc/prenv.c b/nsprpub/pr/src/misc/prenv.c
new file mode 100644
index 0000000000..b057a1c89a
--- /dev/null
+++ b/nsprpub/pr/src/misc/prenv.c
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string.h>
+#include <stdlib.h>
+#include "primpl.h"
+#include "prmem.h"
+
+#if defined(XP_UNIX)
+#include <unistd.h>
+#if defined(DARWIN)
+#if defined(HAVE_CRT_EXTERNS_H)
+#include <crt_externs.h>
+#endif /* HAVE_CRT_EXTERNS_H */
+#else /* DARWIN */
+PR_IMPORT_DATA(char **) environ;
+#endif /* DARWIN */
+#endif /* XP_UNIX */
+
+#if !defined(HAVE_SECURE_GETENV) && defined(HAVE___SECURE_GETENV)
+#define secure_getenv __secure_getenv
+#define HAVE_SECURE_GETENV 1
+#endif
+
+/* Lock used to lock the environment */
+#if defined(_PR_NO_PREEMPT)
+#define _PR_NEW_LOCK_ENV()
+#define _PR_DELETE_LOCK_ENV()
+#define _PR_LOCK_ENV()
+#define _PR_UNLOCK_ENV()
+#elif defined(_PR_LOCAL_THREADS_ONLY)
+extern _PRCPU * _pr_primordialCPU;
+static PRIntn _is;
+#define _PR_NEW_LOCK_ENV()
+#define _PR_DELETE_LOCK_ENV()
+#define _PR_LOCK_ENV() if (_pr_primordialCPU) _PR_INTSOFF(_is);
+#define _PR_UNLOCK_ENV() if (_pr_primordialCPU) _PR_INTSON(_is);
+#else
+static PRLock *_pr_envLock = NULL;
+#define _PR_NEW_LOCK_ENV() {_pr_envLock = PR_NewLock();}
+#define _PR_DELETE_LOCK_ENV() \
+ { if (_pr_envLock) { PR_DestroyLock(_pr_envLock); _pr_envLock = NULL; } }
+#define _PR_LOCK_ENV() { if (_pr_envLock) PR_Lock(_pr_envLock); }
+#define _PR_UNLOCK_ENV() { if (_pr_envLock) PR_Unlock(_pr_envLock); }
+#endif
+
+/************************************************************************/
+
+void _PR_InitEnv(void)
+{
+ _PR_NEW_LOCK_ENV();
+}
+
+void _PR_CleanupEnv(void)
+{
+ _PR_DELETE_LOCK_ENV();
+}
+
+PR_IMPLEMENT(char*) PR_GetEnv(const char *var)
+{
+ char *ev;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ _PR_LOCK_ENV();
+ ev = _PR_MD_GET_ENV(var);
+ _PR_UNLOCK_ENV();
+ return ev;
+}
+
+PR_IMPLEMENT(char*) PR_GetEnvSecure(const char *var)
+{
+#ifdef HAVE_SECURE_GETENV
+ char *ev;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ _PR_LOCK_ENV();
+ ev = secure_getenv(var);
+ _PR_UNLOCK_ENV();
+
+ return ev;
+#else
+#ifdef XP_UNIX
+ /*
+ ** Fall back to checking uids and gids. This won't detect any other
+ ** privilege-granting mechanisms the platform may have. This also
+ ** can't detect the case where the process already called
+ ** setuid(geteuid()) and/or setgid(getegid()).
+ */
+ if (getuid() != geteuid() || getgid() != getegid()) {
+ return NULL;
+ }
+#endif /* XP_UNIX */
+ return PR_GetEnv(var);
+#endif /* HAVE_SECURE_GETENV */
+}
+
+PR_IMPLEMENT(PRStatus) PR_SetEnv(const char *string)
+{
+ PRIntn result;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (!strchr(string, '=')) {
+ return(PR_FAILURE);
+ }
+
+ _PR_LOCK_ENV();
+ result = _PR_MD_PUT_ENV((char*)string);
+ _PR_UNLOCK_ENV();
+ return result ? PR_FAILURE : PR_SUCCESS;
+}
+
+#if defined(XP_UNIX) && (!defined(DARWIN) || defined(HAVE_CRT_EXTERNS_H))
+PR_IMPLEMENT(char **) PR_DuplicateEnvironment(void)
+{
+ char **the_environ, **result, **end, **src, **dst;
+
+ _PR_LOCK_ENV();
+#ifdef DARWIN
+ the_environ = *(_NSGetEnviron());
+#else
+ the_environ = environ;
+#endif
+
+ for (end = the_environ; *end != NULL; end++)
+ /* empty loop body */;
+
+ result = (char **)PR_Malloc(sizeof(char *) * (end - the_environ + 1));
+ if (result != NULL) {
+ for (src = the_environ, dst = result; src != end; src++, dst++) {
+ size_t len;
+
+ len = strlen(*src) + 1;
+ *dst = PR_Malloc(len);
+ if (*dst == NULL) {
+ /* Allocation failed. Must clean up the half-copied env. */
+ char **to_delete;
+
+ for (to_delete = result; to_delete != dst; to_delete++) {
+ PR_Free(*to_delete);
+ }
+ PR_Free(result);
+ result = NULL;
+ goto out;
+ }
+ memcpy(*dst, *src, len);
+ }
+ *dst = NULL;
+ }
+out:
+ _PR_UNLOCK_ENV();
+ return result;
+}
+#else
+/* This platform doesn't support raw access to the environ block. */
+PR_IMPLEMENT(char **) PR_DuplicateEnvironment(void)
+{
+ return NULL;
+}
+#endif
diff --git a/nsprpub/pr/src/misc/prerr.c b/nsprpub/pr/src/misc/prerr.c
new file mode 100644
index 0000000000..99cf39f8c2
--- /dev/null
+++ b/nsprpub/pr/src/misc/prerr.c
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *
+ * prerr.c
+ * This file is automatically generated; please do not edit it.
+ */
+#include "prerror.h"
+static const struct PRErrorMessage text[] = {
+ {"PR_OUT_OF_MEMORY_ERROR", "Memory allocation attempt failed"},
+ {"PR_BAD_DESCRIPTOR_ERROR", "Invalid file descriptor"},
+ {"PR_WOULD_BLOCK_ERROR", "The operation would have blocked"},
+ {"PR_ACCESS_FAULT_ERROR", "Invalid memory address argument"},
+ {"PR_INVALID_METHOD_ERROR", "Invalid function for file type"},
+ {"PR_ILLEGAL_ACCESS_ERROR", "Invalid memory address argument"},
+ {"PR_UNKNOWN_ERROR", "Some unknown error has occurred"},
+ {"PR_PENDING_INTERRUPT_ERROR", "Operation interrupted by another thread"},
+ {"PR_NOT_IMPLEMENTED_ERROR", "function not implemented"},
+ {"PR_IO_ERROR", "I/O function error"},
+ {"PR_IO_TIMEOUT_ERROR", "I/O operation timed out"},
+ {"PR_IO_PENDING_ERROR", "I/O operation on busy file descriptor"},
+ {"PR_DIRECTORY_OPEN_ERROR", "The directory could not be opened"},
+ {"PR_INVALID_ARGUMENT_ERROR", "Invalid function argument"},
+ {"PR_ADDRESS_NOT_AVAILABLE_ERROR", "Network address not available (in use?)"},
+ {"PR_ADDRESS_NOT_SUPPORTED_ERROR", "Network address type not supported"},
+ {"PR_IS_CONNECTED_ERROR", "Already connected"},
+ {"PR_BAD_ADDRESS_ERROR", "Network address is invalid"},
+ {"PR_ADDRESS_IN_USE_ERROR", "Local Network address is in use"},
+ {"PR_CONNECT_REFUSED_ERROR", "Connection refused by peer"},
+ {"PR_NETWORK_UNREACHABLE_ERROR", "Network address is presently unreachable"},
+ {"PR_CONNECT_TIMEOUT_ERROR", "Connection attempt timed out"},
+ {"PR_NOT_CONNECTED_ERROR", "Network file descriptor is not connected"},
+ {"PR_LOAD_LIBRARY_ERROR", "Failure to load dynamic library"},
+ {"PR_UNLOAD_LIBRARY_ERROR", "Failure to unload dynamic library"},
+ {"PR_FIND_SYMBOL_ERROR", "Symbol not found in any of the loaded dynamic libraries"},
+ {"PR_INSUFFICIENT_RESOURCES_ERROR", "Insufficient system resources"},
+ {"PR_DIRECTORY_LOOKUP_ERROR", "A directory lookup on a network address has failed"},
+ {"PR_TPD_RANGE_ERROR", "Attempt to access a TPD key that is out of range"},
+ {"PR_PROC_DESC_TABLE_FULL_ERROR", "Process open FD table is full"},
+ {"PR_SYS_DESC_TABLE_FULL_ERROR", "System open FD table is full"},
+ {"PR_NOT_SOCKET_ERROR", "Network operation attempted on non-network file descriptor"},
+ {"PR_NOT_TCP_SOCKET_ERROR", "TCP-specific function attempted on a non-TCP file descriptor"},
+ {"PR_SOCKET_ADDRESS_IS_BOUND_ERROR", "TCP file descriptor is already bound"},
+ {"PR_NO_ACCESS_RIGHTS_ERROR", "Access Denied"},
+ {"PR_OPERATION_NOT_SUPPORTED_ERROR", "The requested operation is not supported by the platform"},
+ {"PR_PROTOCOL_NOT_SUPPORTED_ERROR", "The host operating system does not support the protocol requested"},
+ {"PR_REMOTE_FILE_ERROR", "Access to the remote file has been severed"},
+ {"PR_BUFFER_OVERFLOW_ERROR", "The value requested is too large to be stored in the data buffer provided"},
+ {"PR_CONNECT_RESET_ERROR", "TCP connection reset by peer"},
+ {"PR_RANGE_ERROR", "Unused"},
+ {"PR_DEADLOCK_ERROR", "The operation would have deadlocked"},
+ {"PR_FILE_IS_LOCKED_ERROR", "The file is already locked"},
+ {"PR_FILE_TOO_BIG_ERROR", "Write would result in file larger than the system allows"},
+ {"PR_NO_DEVICE_SPACE_ERROR", "The device for storing the file is full"},
+ {"PR_PIPE_ERROR", "Unused"},
+ {"PR_NO_SEEK_DEVICE_ERROR", "Unused"},
+ {"PR_IS_DIRECTORY_ERROR", "Cannot perform a normal file operation on a directory"},
+ {"PR_LOOP_ERROR", "Symbolic link loop"},
+ {"PR_NAME_TOO_LONG_ERROR", "File name is too long"},
+ {"PR_FILE_NOT_FOUND_ERROR", "File not found"},
+ {"PR_NOT_DIRECTORY_ERROR", "Cannot perform directory operation on a normal file"},
+ {"PR_READ_ONLY_FILESYSTEM_ERROR", "Cannot write to a read-only file system"},
+ {"PR_DIRECTORY_NOT_EMPTY_ERROR", "Cannot delete a directory that is not empty"},
+ {"PR_FILESYSTEM_MOUNTED_ERROR", "Cannot delete or rename a file object while the file system is busy"},
+ {"PR_NOT_SAME_DEVICE_ERROR", "Cannot rename a file to a file system on another device"},
+ {"PR_DIRECTORY_CORRUPTED_ERROR", "The directory object in the file system is corrupted"},
+ {"PR_FILE_EXISTS_ERROR", "Cannot create or rename a filename that already exists"},
+ {"PR_MAX_DIRECTORY_ENTRIES_ERROR", "Directory is full. No additional filenames may be added"},
+ {"PR_INVALID_DEVICE_STATE_ERROR", "The required device was in an invalid state"},
+ {"PR_DEVICE_IS_LOCKED_ERROR", "The device is locked"},
+ {"PR_NO_MORE_FILES_ERROR", "No more entries in the directory"},
+ {"PR_END_OF_FILE_ERROR", "Encountered end of file"},
+ {"PR_FILE_SEEK_ERROR", "Seek error"},
+ {"PR_FILE_IS_BUSY_ERROR", "The file is busy"},
+ {"PR_OPERATION_ABORTED_ERROR", "The I/O operation was aborted"},
+ {"PR_IN_PROGRESS_ERROR", "Operation is still in progress (probably a non-blocking connect)"},
+ {"PR_ALREADY_INITIATED_ERROR", "Operation has already been initiated (probably a non-blocking connect)"},
+ {"PR_GROUP_EMPTY_ERROR", "The wait group is empty"},
+ {"PR_INVALID_STATE_ERROR", "Object state improper for request"},
+ {"PR_NETWORK_DOWN_ERROR", "Network is down"},
+ {"PR_SOCKET_SHUTDOWN_ERROR", "Socket shutdown"},
+ {"PR_CONNECT_ABORTED_ERROR", "Connection aborted"},
+ {"PR_HOST_UNREACHABLE_ERROR", "Host is unreachable"},
+ {"PR_LIBRARY_NOT_LOADED_ERROR", "The library is not loaded"},
+ {"PR_CALL_ONCE_ERROR", "The one-time function was previously called and failed. Its error code is no longer available"},
+ {"PR_MAX_ERROR", "Placeholder for the end of the list"},
+ {0, 0}
+};
+
+static const struct PRErrorTable et = { text, "prerr", -6000L, 77 };
+
+void nspr_InitializePRErrorTable(void) {
+ PR_ErrorInstallTable(&et);
+}
diff --git a/nsprpub/pr/src/misc/prerr.et b/nsprpub/pr/src/misc/prerr.et
new file mode 100644
index 0000000000..fa81aafcf6
--- /dev/null
+++ b/nsprpub/pr/src/misc/prerr.et
@@ -0,0 +1,108 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+et nspr -6000
+
+ec PR_OUT_OF_MEMORY_ERROR, "Memory allocation attempt failed"
+ec PR_BAD_DESCRIPTOR_ERROR, "Invalid file descriptor"
+ec PR_WOULD_BLOCK_ERROR, "The operation would have blocked"
+ec PR_ACCESS_FAULT_ERROR, "Invalid memory address argument"
+ec PR_INVALID_METHOD_ERROR, "Invalid function for file type"
+ec PR_ILLEGAL_ACCESS_ERROR, "Invalid memory address argument"
+ec PR_UNKNOWN_ERROR, "Some unknown error has occurred"
+ec PR_PENDING_INTERRUPT_ERROR,"Operation interrupted by another thread"
+ec PR_NOT_IMPLEMENTED_ERROR, "function not implemented"
+ec PR_IO_ERROR, "I/O function error"
+ec PR_IO_TIMEOUT_ERROR, "I/O operation timed out"
+ec PR_IO_PENDING_ERROR, "I/O operation on busy file descriptor"
+ec PR_DIRECTORY_OPEN_ERROR, "The directory could not be opened"
+ec PR_INVALID_ARGUMENT_ERROR, "Invalid function argument"
+ec PR_ADDRESS_NOT_AVAILABLE_ERROR, "Network address not available (in use?)"
+ec PR_ADDRESS_NOT_SUPPORTED_ERROR, "Network address type not supported"
+ec PR_IS_CONNECTED_ERROR, "Already connected"
+ec PR_BAD_ADDRESS_ERROR, "Network address is invalid"
+ec PR_ADDRESS_IN_USE_ERROR, "Local Network address is in use"
+ec PR_CONNECT_REFUSED_ERROR, "Connection refused by peer"
+ec PR_NETWORK_UNREACHABLE_ERROR, "Network address is presently unreachable"
+ec PR_CONNECT_TIMEOUT_ERROR, "Connection attempt timed out"
+ec PR_NOT_CONNECTED_ERROR, "Network file descriptor is not connected"
+ec PR_LOAD_LIBRARY_ERROR, "Failure to load dynamic library"
+ec PR_UNLOAD_LIBRARY_ERROR, "Failure to unload dynamic library"
+ec PR_FIND_SYMBOL_ERROR,
+"Symbol not found in any of the loaded dynamic libraries"
+ec PR_INSUFFICIENT_RESOURCES_ERROR, "Insufficient system resources"
+ec PR_DIRECTORY_LOOKUP_ERROR,
+"A directory lookup on a network address has failed"
+ec PR_TPD_RANGE_ERROR,
+"Attempt to access a TPD key that is out of range"
+ec PR_PROC_DESC_TABLE_FULL_ERROR, "Process open FD table is full"
+ec PR_SYS_DESC_TABLE_FULL_ERROR, "System open FD table is full"
+ec PR_NOT_SOCKET_ERROR,
+"Network operation attempted on non-network file descriptor"
+ec PR_NOT_TCP_SOCKET_ERROR,
+"TCP-specific function attempted on a non-TCP file descriptor"
+ec PR_SOCKET_ADDRESS_IS_BOUND_ERROR, "TCP file descriptor is already bound"
+ec PR_NO_ACCESS_RIGHTS_ERROR, "Access Denied"
+ec PR_OPERATION_NOT_SUPPORTED_ERROR,
+"The requested operation is not supported by the platform"
+ec PR_PROTOCOL_NOT_SUPPORTED_ERROR,
+"The host operating system does not support the protocol requested"
+ec PR_REMOTE_FILE_ERROR, "Access to the remote file has been severed"
+ec PR_BUFFER_OVERFLOW_ERROR,
+"The value requested is too large to be stored in the data buffer provided"
+ec PR_CONNECT_RESET_ERROR, "TCP connection reset by peer"
+ec PR_RANGE_ERROR, "Unused"
+ec PR_DEADLOCK_ERROR, "The operation would have deadlocked"
+ec PR_FILE_IS_LOCKED_ERROR, "The file is already locked"
+ec PR_FILE_TOO_BIG_ERROR,
+"Write would result in file larger than the system allows"
+ec PR_NO_DEVICE_SPACE_ERROR, "The device for storing the file is full"
+ec PR_PIPE_ERROR, "Unused"
+ec PR_NO_SEEK_DEVICE_ERROR, "Unused"
+ec PR_IS_DIRECTORY_ERROR,
+"Cannot perform a normal file operation on a directory"
+ec PR_LOOP_ERROR, "Symbolic link loop"
+ec PR_NAME_TOO_LONG_ERROR, "File name is too long"
+ec PR_FILE_NOT_FOUND_ERROR, "File not found"
+ec PR_NOT_DIRECTORY_ERROR,
+"Cannot perform directory operation on a normal file"
+ec PR_READ_ONLY_FILESYSTEM_ERROR,
+"Cannot write to a read-only file system"
+ec PR_DIRECTORY_NOT_EMPTY_ERROR,
+"Cannot delete a directory that is not empty"
+ec PR_FILESYSTEM_MOUNTED_ERROR,
+"Cannot delete or rename a file object while the file system is busy"
+ec PR_NOT_SAME_DEVICE_ERROR,
+"Cannot rename a file to a file system on another device"
+ec PR_DIRECTORY_CORRUPTED_ERROR,
+"The directory object in the file system is corrupted"
+ec PR_FILE_EXISTS_ERROR,
+"Cannot create or rename a filename that already exists"
+ec PR_MAX_DIRECTORY_ENTRIES_ERROR,
+"Directory is full. No additional filenames may be added"
+ec PR_INVALID_DEVICE_STATE_ERROR,
+"The required device was in an invalid state"
+ec PR_DEVICE_IS_LOCKED_ERROR, "The device is locked"
+ec PR_NO_MORE_FILES_ERROR, "No more entries in the directory"
+ec PR_END_OF_FILE_ERROR, "Encountered end of file"
+ec PR_FILE_SEEK_ERROR, "Seek error"
+ec PR_FILE_IS_BUSY_ERROR, "The file is busy"
+ec PR_OPERATION_ABORTED_ERROR, "The I/O operation was aborted"
+ec PR_IN_PROGRESS_ERROR,
+"Operation is still in progress (probably a non-blocking connect)"
+ec PR_ALREADY_INITIATED_ERROR,
+"Operation has already been initiated (probably a non-blocking connect)"
+ec PR_GROUP_EMPTY_ERROR, "The wait group is empty"
+ec PR_INVALID_STATE_ERROR, "Object state improper for request"
+ec PR_NETWORK_DOWN_ERROR, "Network is down"
+ec PR_SOCKET_SHUTDOWN_ERROR, "Socket shutdown"
+ec PR_CONNECT_ABORTED_ERROR, "Connection aborted"
+ec PR_HOST_UNREACHABLE_ERROR, "Host is unreachable"
+ec PR_LIBRARY_NOT_LOADED_ERROR, "The library is not loaded"
+ec PR_CALL_ONCE_ERROR, "The one-time function was previously called and failed. Its error code is no longer available"
+
+ec PR_MAX_ERROR, "Placeholder for the end of the list"
+
+end
diff --git a/nsprpub/pr/src/misc/prerr.properties b/nsprpub/pr/src/misc/prerr.properties
new file mode 100644
index 0000000000..ef236b138a
--- /dev/null
+++ b/nsprpub/pr/src/misc/prerr.properties
@@ -0,0 +1,85 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#
+# prerr.properties
+# This file is automatically generated; please do not edit it.
+PR_OUT_OF_MEMORY_ERROR=Memory allocation attempt failed
+PR_BAD_DESCRIPTOR_ERROR=Invalid file descriptor
+PR_WOULD_BLOCK_ERROR=The operation would have blocked
+PR_ACCESS_FAULT_ERROR=Invalid memory address argument
+PR_INVALID_METHOD_ERROR=Invalid function for file type
+PR_ILLEGAL_ACCESS_ERROR=Invalid memory address argument
+PR_UNKNOWN_ERROR=Some unknown error has occurred
+PR_PENDING_INTERRUPT_ERROR=Operation interrupted by another thread
+PR_NOT_IMPLEMENTED_ERROR=function not implemented
+PR_IO_ERROR=I/O function error
+PR_IO_TIMEOUT_ERROR=I/O operation timed out
+PR_IO_PENDING_ERROR=I/O operation on busy file descriptor
+PR_DIRECTORY_OPEN_ERROR=The directory could not be opened
+PR_INVALID_ARGUMENT_ERROR=Invalid function argument
+PR_ADDRESS_NOT_AVAILABLE_ERROR=Network address not available (in use?)
+PR_ADDRESS_NOT_SUPPORTED_ERROR=Network address type not supported
+PR_IS_CONNECTED_ERROR=Already connected
+PR_BAD_ADDRESS_ERROR=Network address is invalid
+PR_ADDRESS_IN_USE_ERROR=Local Network address is in use
+PR_CONNECT_REFUSED_ERROR=Connection refused by peer
+PR_NETWORK_UNREACHABLE_ERROR=Network address is presently unreachable
+PR_CONNECT_TIMEOUT_ERROR=Connection attempt timed out
+PR_NOT_CONNECTED_ERROR=Network file descriptor is not connected
+PR_LOAD_LIBRARY_ERROR=Failure to load dynamic library
+PR_UNLOAD_LIBRARY_ERROR=Failure to unload dynamic library
+PR_FIND_SYMBOL_ERROR=Symbol not found in any of the loaded dynamic libraries
+PR_INSUFFICIENT_RESOURCES_ERROR=Insufficient system resources
+PR_DIRECTORY_LOOKUP_ERROR=A directory lookup on a network address has failed
+PR_TPD_RANGE_ERROR=Attempt to access a TPD key that is out of range
+PR_PROC_DESC_TABLE_FULL_ERROR=Process open FD table is full
+PR_SYS_DESC_TABLE_FULL_ERROR=System open FD table is full
+PR_NOT_SOCKET_ERROR=Network operation attempted on non-network file descriptor
+PR_NOT_TCP_SOCKET_ERROR=TCP-specific function attempted on a non-TCP file descriptor
+PR_SOCKET_ADDRESS_IS_BOUND_ERROR=TCP file descriptor is already bound
+PR_NO_ACCESS_RIGHTS_ERROR=Access Denied
+PR_OPERATION_NOT_SUPPORTED_ERROR=The requested operation is not supported by the platform
+PR_PROTOCOL_NOT_SUPPORTED_ERROR=The host operating system does not support the protocol requested
+PR_REMOTE_FILE_ERROR=Access to the remote file has been severed
+PR_BUFFER_OVERFLOW_ERROR=The value requested is too large to be stored in the data buffer provided
+PR_CONNECT_RESET_ERROR=TCP connection reset by peer
+PR_RANGE_ERROR=Unused
+PR_DEADLOCK_ERROR=The operation would have deadlocked
+PR_FILE_IS_LOCKED_ERROR=The file is already locked
+PR_FILE_TOO_BIG_ERROR=Write would result in file larger than the system allows
+PR_NO_DEVICE_SPACE_ERROR=The device for storing the file is full
+PR_PIPE_ERROR=Unused
+PR_NO_SEEK_DEVICE_ERROR=Unused
+PR_IS_DIRECTORY_ERROR=Cannot perform a normal file operation on a directory
+PR_LOOP_ERROR=Symbolic link loop
+PR_NAME_TOO_LONG_ERROR=File name is too long
+PR_FILE_NOT_FOUND_ERROR=File not found
+PR_NOT_DIRECTORY_ERROR=Cannot perform directory operation on a normal file
+PR_READ_ONLY_FILESYSTEM_ERROR=Cannot write to a read-only file system
+PR_DIRECTORY_NOT_EMPTY_ERROR=Cannot delete a directory that is not empty
+PR_FILESYSTEM_MOUNTED_ERROR=Cannot delete or rename a file object while the file system is busy
+PR_NOT_SAME_DEVICE_ERROR=Cannot rename a file to a file system on another device
+PR_DIRECTORY_CORRUPTED_ERROR=The directory object in the file system is corrupted
+PR_FILE_EXISTS_ERROR=Cannot create or rename a filename that already exists
+PR_MAX_DIRECTORY_ENTRIES_ERROR=Directory is full. No additional filenames may be added
+PR_INVALID_DEVICE_STATE_ERROR=The required device was in an invalid state
+PR_DEVICE_IS_LOCKED_ERROR=The device is locked
+PR_NO_MORE_FILES_ERROR=No more entries in the directory
+PR_END_OF_FILE_ERROR=Encountered end of file
+PR_FILE_SEEK_ERROR=Seek error
+PR_FILE_IS_BUSY_ERROR=The file is busy
+PR_OPERATION_ABORTED_ERROR=The I/O operation was aborted
+PR_IN_PROGRESS_ERROR=Operation is still in progress (probably a non-blocking connect)
+PR_ALREADY_INITIATED_ERROR=Operation has already been initiated (probably a non-blocking connect)
+PR_GROUP_EMPTY_ERROR=The wait group is empty
+PR_INVALID_STATE_ERROR=Object state improper for request
+PR_NETWORK_DOWN_ERROR=Network is down
+PR_SOCKET_SHUTDOWN_ERROR=Socket shutdown
+PR_CONNECT_ABORTED_ERROR=Connection aborted
+PR_HOST_UNREACHABLE_ERROR=Host is unreachable
+PR_LIBRARY_NOT_LOADED_ERROR=The library is not loaded
+PR_CALL_ONCE_ERROR=The one-time function was previously called and failed. Its error code is no longer available
+PR_MAX_ERROR=Placeholder for the end of the list
diff --git a/nsprpub/pr/src/misc/prerror.c b/nsprpub/pr/src/misc/prerror.c
new file mode 100644
index 0000000000..a06b78ddbc
--- /dev/null
+++ b/nsprpub/pr/src/misc/prerror.c
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+PR_IMPLEMENT(PRErrorCode) PR_GetError(void)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ return thread->errorCode;
+}
+
+PR_IMPLEMENT(PRInt32) PR_GetOSError(void)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ return thread->osErrorCode;
+}
+
+PR_IMPLEMENT(void) PR_SetError(PRErrorCode code, PRInt32 osErr)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ thread->errorCode = code;
+ thread->osErrorCode = osErr;
+ thread->errorStringLength = 0;
+}
+
+PR_IMPLEMENT(void) PR_SetErrorText(PRIntn textLength, const char *text)
+{
+ PRThread *thread = PR_GetCurrentThread();
+
+ if (0 == textLength)
+ {
+ if (NULL != thread->errorString) {
+ PR_DELETE(thread->errorString);
+ }
+ thread->errorStringSize = 0;
+ }
+ else
+ {
+ PRIntn size = textLength + 31; /* actual length to allocate. Plus a little extra */
+ if (thread->errorStringSize < textLength+1) /* do we have room? */
+ {
+ if (NULL != thread->errorString) {
+ PR_DELETE(thread->errorString);
+ }
+ thread->errorString = (char*)PR_MALLOC(size);
+ if ( NULL == thread->errorString ) {
+ thread->errorStringSize = 0;
+ thread->errorStringLength = 0;
+ return;
+ }
+ thread->errorStringSize = size;
+ }
+ memcpy(thread->errorString, text, textLength+1 );
+ }
+ thread->errorStringLength = textLength;
+}
+
+PR_IMPLEMENT(PRInt32) PR_GetErrorTextLength(void)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ return thread->errorStringLength;
+} /* PR_GetErrorTextLength */
+
+PR_IMPLEMENT(PRInt32) PR_GetErrorText(char *text)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ if (0 != thread->errorStringLength) {
+ memcpy(text, thread->errorString, thread->errorStringLength+1);
+ }
+ return thread->errorStringLength;
+} /* PR_GetErrorText */
+
+
diff --git a/nsprpub/pr/src/misc/prerrortable.c b/nsprpub/pr/src/misc/prerrortable.c
new file mode 100644
index 0000000000..a7ff4f0eb5
--- /dev/null
+++ b/nsprpub/pr/src/misc/prerrortable.c
@@ -0,0 +1,209 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+
+
+/*
+
+Copyright 1987, 1988 by the Student Information Processing Board
+ of the Massachusetts Institute of Technology
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is
+hereby granted, provided that the above copyright notice
+appear in all copies and that both that copyright notice and
+this permission notice appear in supporting documentation,
+and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+M.I.T. and the M.I.T. S.I.P.B. make no representations about
+the suitability of this software for any purpose. It is
+provided "as is" without express or implied warranty.
+
+*/
+
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include "prmem.h"
+#include "prerror.h"
+
+#define ERRCODE_RANGE 8 /* # of bits to shift table number */
+#define BITS_PER_CHAR 6 /* # bits to shift per character in name */
+
+#ifdef NEED_SYS_ERRLIST
+extern char const * const sys_errlist[];
+extern const int sys_nerr;
+#endif
+
+/* List of error tables */
+struct PRErrorTableList {
+ struct PRErrorTableList *next;
+ const struct PRErrorTable *table;
+ struct PRErrorCallbackTablePrivate *table_private;
+};
+static struct PRErrorTableList * Table_List = (struct PRErrorTableList *) NULL;
+
+/* Supported languages */
+static const char * default_languages[] = { "i-default", "en", 0 };
+static const char * const * callback_languages = default_languages;
+
+/* Callback info */
+static struct PRErrorCallbackPrivate *callback_private = 0;
+static PRErrorCallbackLookupFn *callback_lookup = 0;
+static PRErrorCallbackNewTableFn *callback_newtable = 0;
+
+
+static const char char_set[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
+
+static const char *
+error_table_name (PRErrorCode num)
+{
+ static char buf[6]; /* only used if internal code problems exist */
+
+ long ch;
+ int i;
+ char *p;
+
+ /* num = aa aaa abb bbb bcc ccc cdd ddd d?? ??? ??? */
+ p = buf;
+ num >>= ERRCODE_RANGE;
+ /* num = ?? ??? ??? aaa aaa bbb bbb ccc ccc ddd ddd */
+ num &= 077777777;
+ /* num = 00 000 000 aaa aaa bbb bbb ccc ccc ddd ddd */
+ for (i = 4; i >= 0; i--) {
+ ch = (num >> BITS_PER_CHAR * i) & ((1 << BITS_PER_CHAR) - 1);
+ if (ch != 0) {
+ *p++ = char_set[ch-1];
+ }
+ }
+ *p = '\0';
+ return(buf);
+}
+
+PR_IMPLEMENT(const char *)
+PR_ErrorToString(PRErrorCode code, PRLanguageCode language)
+{
+ /* static buffer only used if code is using inconsistent error message
+ * numbers, so just ignore the possible thread contention
+ */
+ static char buffer[25];
+
+ const char *msg;
+ int offset;
+ PRErrorCode table_num;
+ struct PRErrorTableList *et;
+ int started = 0;
+ char *cp;
+
+ for (et = Table_List; et; et = et->next) {
+ if (et->table->base <= code &&
+ et->table->base + et->table->n_msgs > code) {
+ /* This is the right table */
+ if (callback_lookup) {
+ msg = callback_lookup(code, language, et->table,
+ callback_private, et->table_private);
+ if (msg) {
+ return msg;
+ }
+ }
+
+ return(et->table->msgs[code - et->table->base].en_text);
+ }
+ }
+
+ if (code >= 0 && code < 256) {
+ return strerror(code);
+ }
+
+ offset = (int) (code & ((1<<ERRCODE_RANGE)-1));
+ table_num = code - offset;
+ strcpy (buffer, "Unknown code ");
+ if (table_num) {
+ strcat(buffer, error_table_name (table_num));
+ strcat(buffer, " ");
+ }
+ for (cp = buffer; *cp; cp++)
+ ;
+ if (offset >= 100) {
+ *cp++ = (char)('0' + offset / 100);
+ offset %= 100;
+ started++;
+ }
+ if (started || offset >= 10) {
+ *cp++ = (char)('0' + offset / 10);
+ offset %= 10;
+ }
+ *cp++ = (char)('0' + offset);
+ *cp = '\0';
+ return(buffer);
+}
+
+PR_IMPLEMENT(const char *)
+PR_ErrorToName(PRErrorCode code)
+{
+ struct PRErrorTableList *et;
+
+ for (et = Table_List; et; et = et->next) {
+ if (et->table->base <= code &&
+ et->table->base + et->table->n_msgs > code) {
+ /* This is the right table */
+ return(et->table->msgs[code - et->table->base].name);
+ }
+ }
+
+ return 0;
+}
+
+PR_IMPLEMENT(const char * const *)
+PR_ErrorLanguages(void)
+{
+ return callback_languages;
+}
+
+PR_IMPLEMENT(PRErrorCode)
+PR_ErrorInstallTable(const struct PRErrorTable *table)
+{
+ struct PRErrorTableList * new_et;
+
+ new_et = (struct PRErrorTableList *)
+ PR_Malloc(sizeof(struct PRErrorTableList));
+ if (!new_et) {
+ return errno; /* oops */
+ }
+ new_et->table = table;
+ if (callback_newtable) {
+ new_et->table_private = callback_newtable(table, callback_private);
+ } else {
+ new_et->table_private = 0;
+ }
+ new_et->next = Table_List;
+ Table_List = new_et;
+ return 0;
+}
+
+PR_IMPLEMENT(void)
+PR_ErrorInstallCallback(const char * const * languages,
+ PRErrorCallbackLookupFn *lookup,
+ PRErrorCallbackNewTableFn *newtable,
+ struct PRErrorCallbackPrivate *cb_private)
+{
+ struct PRErrorTableList *et;
+
+ assert(strcmp(languages[0], "i-default") == 0);
+ assert(strcmp(languages[1], "en") == 0);
+
+ callback_languages = languages;
+ callback_lookup = lookup;
+ callback_newtable = newtable;
+ callback_private = cb_private;
+
+ if (callback_newtable) {
+ for (et = Table_List; et; et = et->next) {
+ et->table_private = callback_newtable(et->table, callback_private);
+ }
+ }
+}
diff --git a/nsprpub/pr/src/misc/prinit.c b/nsprpub/pr/src/misc/prinit.c
new file mode 100644
index 0000000000..a952ad6554
--- /dev/null
+++ b/nsprpub/pr/src/misc/prinit.c
@@ -0,0 +1,857 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#include <ctype.h>
+#include <string.h>
+
+PRLogModuleInfo *_pr_clock_lm;
+PRLogModuleInfo *_pr_cmon_lm;
+PRLogModuleInfo *_pr_io_lm;
+PRLogModuleInfo *_pr_cvar_lm;
+PRLogModuleInfo *_pr_mon_lm;
+PRLogModuleInfo *_pr_linker_lm;
+PRLogModuleInfo *_pr_sched_lm;
+PRLogModuleInfo *_pr_thread_lm;
+PRLogModuleInfo *_pr_gc_lm;
+PRLogModuleInfo *_pr_shm_lm;
+PRLogModuleInfo *_pr_shma_lm;
+
+PRFileDesc *_pr_stdin;
+PRFileDesc *_pr_stdout;
+PRFileDesc *_pr_stderr;
+
+#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS)
+
+PRCList _pr_active_local_threadQ =
+ PR_INIT_STATIC_CLIST(&_pr_active_local_threadQ);
+PRCList _pr_active_global_threadQ =
+ PR_INIT_STATIC_CLIST(&_pr_active_global_threadQ);
+
+_MDLock _pr_cpuLock; /* lock for the CPU Q */
+PRCList _pr_cpuQ = PR_INIT_STATIC_CLIST(&_pr_cpuQ);
+
+PRUint32 _pr_utid;
+
+PRInt32 _pr_userActive;
+PRInt32 _pr_systemActive;
+PRUintn _pr_maxPTDs;
+
+#ifdef _PR_LOCAL_THREADS_ONLY
+
+struct _PRCPU *_pr_currentCPU;
+PRThread *_pr_currentThread;
+PRThread *_pr_lastThread;
+PRInt32 _pr_intsOff;
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+/* Lock protecting all "termination" condition variables of all threads */
+PRLock *_pr_terminationCVLock;
+
+#endif /* !defined(_PR_PTHREADS) */
+
+PRLock *_pr_sleeplock; /* used in PR_Sleep(), classic and pthreads */
+
+static void _PR_InitCallOnce(void);
+
+PRBool _pr_initialized = PR_FALSE;
+
+
+PR_IMPLEMENT(PRBool) PR_VersionCheck(const char *importedVersion)
+{
+ /*
+ ** This is the secret handshake algorithm.
+ **
+ ** This release has a simple version compatibility
+ ** check algorithm. This release is not backward
+ ** compatible with previous major releases. It is
+ ** not compatible with future major, minor, or
+ ** patch releases.
+ */
+ int vmajor = 0, vminor = 0, vpatch = 0;
+ const char *ptr = importedVersion;
+
+ while (isdigit(*ptr)) {
+ vmajor = 10 * vmajor + *ptr - '0';
+ ptr++;
+ }
+ if (*ptr == '.') {
+ ptr++;
+ while (isdigit(*ptr)) {
+ vminor = 10 * vminor + *ptr - '0';
+ ptr++;
+ }
+ if (*ptr == '.') {
+ ptr++;
+ while (isdigit(*ptr)) {
+ vpatch = 10 * vpatch + *ptr - '0';
+ ptr++;
+ }
+ }
+ }
+
+ if (vmajor != PR_VMAJOR) {
+ return PR_FALSE;
+ }
+ if (vmajor == PR_VMAJOR && vminor > PR_VMINOR) {
+ return PR_FALSE;
+ }
+ if (vmajor == PR_VMAJOR && vminor == PR_VMINOR && vpatch > PR_VPATCH) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+} /* PR_VersionCheck */
+
+PR_IMPLEMENT(const char*) PR_GetVersion(void)
+{
+ return PR_VERSION;
+}
+
+PR_IMPLEMENT(PRBool) PR_Initialized(void)
+{
+ return _pr_initialized;
+}
+
+PRInt32 _native_threads_only = 0;
+
+#ifdef WINNT
+static void _pr_SetNativeThreadsOnlyMode(void)
+{
+ HMODULE mainExe;
+ PRBool *globalp;
+ char *envp;
+
+ mainExe = GetModuleHandle(NULL);
+ PR_ASSERT(NULL != mainExe);
+ globalp = (PRBool *) GetProcAddress(mainExe, "nspr_native_threads_only");
+ if (globalp) {
+ _native_threads_only = (*globalp != PR_FALSE);
+ } else if (envp = getenv("NSPR_NATIVE_THREADS_ONLY")) {
+ _native_threads_only = (atoi(envp) == 1);
+ }
+}
+#endif
+
+static void _PR_InitStuff(void)
+{
+
+ if (_pr_initialized) {
+ return;
+ }
+ _pr_initialized = PR_TRUE;
+#ifdef _PR_ZONE_ALLOCATOR
+ _PR_InitZones();
+#endif
+#ifdef WINNT
+ _pr_SetNativeThreadsOnlyMode();
+#endif
+
+
+ (void) PR_GetPageSize();
+
+ _pr_clock_lm = PR_NewLogModule("clock");
+ _pr_cmon_lm = PR_NewLogModule("cmon");
+ _pr_io_lm = PR_NewLogModule("io");
+ _pr_mon_lm = PR_NewLogModule("mon");
+ _pr_linker_lm = PR_NewLogModule("linker");
+ _pr_cvar_lm = PR_NewLogModule("cvar");
+ _pr_sched_lm = PR_NewLogModule("sched");
+ _pr_thread_lm = PR_NewLogModule("thread");
+ _pr_gc_lm = PR_NewLogModule("gc");
+ _pr_shm_lm = PR_NewLogModule("shm");
+ _pr_shma_lm = PR_NewLogModule("shma");
+
+ /* NOTE: These init's cannot depend on _PR_MD_CURRENT_THREAD() */
+ _PR_MD_EARLY_INIT();
+
+ _PR_InitLocks();
+ _PR_InitAtomic();
+ _PR_InitSegs();
+ _PR_InitStacks();
+ _PR_InitTPD();
+ _PR_InitEnv();
+ _PR_InitLayerCache();
+ _PR_InitClock();
+
+ _pr_sleeplock = PR_NewLock();
+ PR_ASSERT(NULL != _pr_sleeplock);
+
+ _PR_InitThreads(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+
+#ifdef WIN16
+ {
+ PRInt32 top; /* artificial top of stack, win16 */
+ _pr_top_of_task_stack = (char *) &top;
+ }
+#endif
+
+#ifndef _PR_GLOBAL_THREADS_ONLY
+ _PR_InitCPUs();
+#endif
+
+ /*
+ * XXX: call _PR_InitMem only on those platforms for which nspr implements
+ * malloc, for now.
+ */
+#ifdef _PR_OVERRIDE_MALLOC
+ _PR_InitMem();
+#endif
+
+ _PR_InitCMon();
+ _PR_InitIO();
+ _PR_InitNet();
+ _PR_InitTime();
+ _PR_InitLog();
+ _PR_InitLinker();
+ _PR_InitCallOnce();
+ _PR_InitDtoa();
+ _PR_InitMW();
+ _PR_InitRWLocks();
+
+ nspr_InitializePRErrorTable();
+
+ _PR_MD_FINAL_INIT();
+}
+
+void _PR_ImplicitInitialization(void)
+{
+ _PR_InitStuff();
+
+ /* Enable interrupts */
+#if !defined(_PR_PTHREADS) && !defined(_PR_GLOBAL_THREADS_ONLY)
+ _PR_MD_START_INTERRUPTS();
+#endif
+
+}
+
+PR_IMPLEMENT(void) PR_DisableClockInterrupts(void)
+{
+#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS)
+ if (!_pr_initialized) {
+ _PR_InitStuff();
+ } else {
+ _PR_MD_DISABLE_CLOCK_INTERRUPTS();
+ }
+#endif
+}
+
+PR_IMPLEMENT(void) PR_EnableClockInterrupts(void)
+{
+#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS)
+ if (!_pr_initialized) {
+ _PR_InitStuff();
+ }
+ _PR_MD_ENABLE_CLOCK_INTERRUPTS();
+#endif
+}
+
+PR_IMPLEMENT(void) PR_BlockClockInterrupts(void)
+{
+#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS)
+ _PR_MD_BLOCK_CLOCK_INTERRUPTS();
+#endif
+}
+
+PR_IMPLEMENT(void) PR_UnblockClockInterrupts(void)
+{
+#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS)
+ _PR_MD_UNBLOCK_CLOCK_INTERRUPTS();
+#endif
+}
+
+PR_IMPLEMENT(void) PR_Init(
+ PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs)
+{
+ _PR_ImplicitInitialization();
+}
+
+PR_IMPLEMENT(PRIntn) PR_Initialize(
+ PRPrimordialFn prmain, PRIntn argc, char **argv, PRUintn maxPTDs)
+{
+ PRIntn rv;
+ _PR_ImplicitInitialization();
+ rv = prmain(argc, argv);
+ PR_Cleanup();
+ return rv;
+} /* PR_Initialize */
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * _PR_CleanupBeforeExit --
+ *
+ * Perform the cleanup work before exiting the process.
+ * We first do the cleanup generic to all platforms. Then
+ * we call _PR_MD_CLEANUP_BEFORE_EXIT(), where platform-dependent
+ * cleanup is done. This function is used by PR_Cleanup().
+ *
+ * See also: PR_Cleanup().
+ *
+ *-----------------------------------------------------------------------
+ */
+#if defined(_PR_PTHREADS) || defined(_PR_BTHREADS)
+/* see ptthread.c */
+#else
+static void
+_PR_CleanupBeforeExit(void)
+{
+ /*
+ Do not make any calls here other than to destroy resources. For example,
+ do not make any calls that eventually may end up in PR_Lock. Because the
+ thread is destroyed, can not access current thread any more.
+ */
+ _PR_CleanupTPD();
+ if (_pr_terminationCVLock)
+ /*
+ * In light of the comment above, this looks real suspicious.
+ * I'd go so far as to say it's just a problem waiting to happen.
+ */
+ {
+ PR_DestroyLock(_pr_terminationCVLock);
+ }
+
+ _PR_MD_CLEANUP_BEFORE_EXIT();
+}
+#endif /* defined(_PR_PTHREADS) */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PR_Cleanup --
+ *
+ * Perform a graceful shutdown of the NSPR runtime. PR_Cleanup() may
+ * only be called from the primordial thread, typically at the
+ * end of the main() function. It returns when it has completed
+ * its platform-dependent duty and the process must not make any other
+ * NSPR library calls prior to exiting from main().
+ *
+ * PR_Cleanup() first blocks the primordial thread until all the
+ * other user (non-system) threads, if any, have terminated.
+ * Then it performs cleanup in preparation for exiting the process.
+ * PR_Cleanup() does not exit the primordial thread (which would
+ * in turn exit the process).
+ *
+ * PR_Cleanup() only responds when it is called by the primordial
+ * thread. Calls by any other thread are silently ignored.
+ *
+ * See also: PR_ExitProcess()
+ *
+ *----------------------------------------------------------------------
+ */
+#if defined(_PR_PTHREADS) || defined(_PR_BTHREADS)
+/* see ptthread.c */
+#else
+
+PR_IMPLEMENT(PRStatus) PR_Cleanup()
+{
+ PRThread *me = PR_GetCurrentThread();
+ PR_ASSERT((NULL != me) && (me->flags & _PR_PRIMORDIAL));
+ if ((NULL != me) && (me->flags & _PR_PRIMORDIAL))
+ {
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR"));
+
+ /*
+ * No more recycling of threads
+ */
+ _pr_recycleThreads = 0;
+
+ /*
+ * Wait for all other user (non-system/daemon) threads
+ * to terminate.
+ */
+ PR_Lock(_pr_activeLock);
+ while (_pr_userActive > _pr_primordialExitCount) {
+ PR_WaitCondVar(_pr_primordialExitCVar, PR_INTERVAL_NO_TIMEOUT);
+ }
+ if (me->flags & _PR_SYSTEM) {
+ _pr_systemActive--;
+ } else {
+ _pr_userActive--;
+ }
+ PR_Unlock(_pr_activeLock);
+
+ _PR_MD_EARLY_CLEANUP();
+
+ _PR_CleanupMW();
+ _PR_CleanupTime();
+ _PR_CleanupDtoa();
+ _PR_CleanupCallOnce();
+ _PR_ShutdownLinker();
+ _PR_CleanupNet();
+ _PR_CleanupIO();
+ /* Release the primordial thread's private data, etc. */
+ _PR_CleanupThread(me);
+
+ _PR_MD_STOP_INTERRUPTS();
+
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("PR_Cleanup: clean up before destroying thread"));
+ _PR_LogCleanup();
+
+ /*
+ * This part should look like the end of _PR_NativeRunThread
+ * and _PR_UserRunThread.
+ */
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ _PR_MD_EXIT_THREAD(me);
+ _PR_NativeDestroyThread(me);
+ } else {
+ _PR_UserDestroyThread(me);
+ PR_DELETE(me->stack);
+ PR_DELETE(me);
+ }
+
+ /*
+ * XXX: We are freeing the heap memory here so that Purify won't
+ * complain, but we should also free other kinds of resources
+ * that are allocated by the _PR_InitXXX() functions.
+ * Ideally, for each _PR_InitXXX(), there should be a corresponding
+ * _PR_XXXCleanup() that we can call here.
+ */
+#ifdef WINNT
+ _PR_CleanupCPUs();
+#endif
+ _PR_CleanupThreads();
+ _PR_CleanupCMon();
+ PR_DestroyLock(_pr_sleeplock);
+ _pr_sleeplock = NULL;
+ _PR_CleanupLayerCache();
+ _PR_CleanupEnv();
+ _PR_CleanupStacks();
+ _PR_CleanupBeforeExit();
+ _pr_initialized = PR_FALSE;
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+}
+#endif /* defined(_PR_PTHREADS) */
+
+/*
+ *------------------------------------------------------------------------
+ * PR_ProcessExit --
+ *
+ * Cause an immediate, nongraceful, forced termination of the process.
+ * It takes a PRIntn argument, which is the exit status code of the
+ * process.
+ *
+ * See also: PR_Cleanup()
+ *
+ *------------------------------------------------------------------------
+ */
+
+#if defined(_PR_PTHREADS) || defined(_PR_BTHREADS)
+/* see ptthread.c */
+#else
+PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status)
+{
+ _PR_MD_EXIT(status);
+}
+
+#endif /* defined(_PR_PTHREADS) */
+
+PR_IMPLEMENT(PRProcessAttr *)
+PR_NewProcessAttr(void)
+{
+ PRProcessAttr *attr;
+
+ attr = PR_NEWZAP(PRProcessAttr);
+ if (!attr) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ return attr;
+}
+
+PR_IMPLEMENT(void)
+PR_ResetProcessAttr(PRProcessAttr *attr)
+{
+ PR_FREEIF(attr->currentDirectory);
+ PR_FREEIF(attr->fdInheritBuffer);
+ memset(attr, 0, sizeof(*attr));
+}
+
+PR_IMPLEMENT(void)
+PR_DestroyProcessAttr(PRProcessAttr *attr)
+{
+ PR_FREEIF(attr->currentDirectory);
+ PR_FREEIF(attr->fdInheritBuffer);
+ PR_DELETE(attr);
+}
+
+PR_IMPLEMENT(void)
+PR_ProcessAttrSetStdioRedirect(
+ PRProcessAttr *attr,
+ PRSpecialFD stdioFd,
+ PRFileDesc *redirectFd)
+{
+ switch (stdioFd) {
+ case PR_StandardInput:
+ attr->stdinFd = redirectFd;
+ break;
+ case PR_StandardOutput:
+ attr->stdoutFd = redirectFd;
+ break;
+ case PR_StandardError:
+ attr->stderrFd = redirectFd;
+ break;
+ default:
+ PR_ASSERT(0);
+ }
+}
+
+/*
+ * OBSOLETE
+ */
+PR_IMPLEMENT(void)
+PR_SetStdioRedirect(
+ PRProcessAttr *attr,
+ PRSpecialFD stdioFd,
+ PRFileDesc *redirectFd)
+{
+#if defined(DEBUG)
+ static PRBool warn = PR_TRUE;
+ if (warn) {
+ warn = _PR_Obsolete("PR_SetStdioRedirect()",
+ "PR_ProcessAttrSetStdioRedirect()");
+ }
+#endif
+ PR_ProcessAttrSetStdioRedirect(attr, stdioFd, redirectFd);
+}
+
+PR_IMPLEMENT(PRStatus)
+PR_ProcessAttrSetCurrentDirectory(
+ PRProcessAttr *attr,
+ const char *dir)
+{
+ PR_FREEIF(attr->currentDirectory);
+ attr->currentDirectory = (char *) PR_MALLOC(strlen(dir) + 1);
+ if (!attr->currentDirectory) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return PR_FAILURE;
+ }
+ strcpy(attr->currentDirectory, dir);
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus)
+PR_ProcessAttrSetInheritableFD(
+ PRProcessAttr *attr,
+ PRFileDesc *fd,
+ const char *name)
+{
+ /* We malloc the fd inherit buffer in multiples of this number. */
+#define FD_INHERIT_BUFFER_INCR 128
+ /* The length of "NSPR_INHERIT_FDS=" */
+#define NSPR_INHERIT_FDS_STRLEN 17
+ /* The length of osfd (PROsfd) printed in hexadecimal with 0x prefix */
+#ifdef _WIN64
+#define OSFD_STRLEN 18
+#else
+#define OSFD_STRLEN 10
+#endif
+ /* The length of fd type (PRDescType) printed in decimal */
+#define FD_TYPE_STRLEN 1
+ PRSize newSize;
+ int remainder;
+ char *newBuffer;
+ int nwritten;
+ char *cur;
+ int freeSize;
+
+ if (fd->identity != PR_NSPR_IO_LAYER) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (fd->secret->inheritable == _PR_TRI_UNKNOWN) {
+ _PR_MD_QUERY_FD_INHERITABLE(fd);
+ }
+ if (fd->secret->inheritable != _PR_TRI_TRUE) {
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ /*
+ * We also need to account for the : separators and the
+ * terminating null byte.
+ */
+ if (NULL == attr->fdInheritBuffer) {
+ /* The first time, we print "NSPR_INHERIT_FDS=<name>:<type>:<val>" */
+ newSize = NSPR_INHERIT_FDS_STRLEN + strlen(name)
+ + FD_TYPE_STRLEN + OSFD_STRLEN + 2 + 1;
+ } else {
+ /* At other times, we print ":<name>:<type>:<val>" */
+ newSize = attr->fdInheritBufferUsed + strlen(name)
+ + FD_TYPE_STRLEN + OSFD_STRLEN + 3 + 1;
+ }
+ if (newSize > attr->fdInheritBufferSize) {
+ /* Make newSize a multiple of FD_INHERIT_BUFFER_INCR */
+ remainder = newSize % FD_INHERIT_BUFFER_INCR;
+ if (remainder != 0) {
+ newSize += (FD_INHERIT_BUFFER_INCR - remainder);
+ }
+ if (NULL == attr->fdInheritBuffer) {
+ newBuffer = (char *) PR_MALLOC(newSize);
+ } else {
+ newBuffer = (char *) PR_REALLOC(attr->fdInheritBuffer, newSize);
+ }
+ if (NULL == newBuffer) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return PR_FAILURE;
+ }
+ attr->fdInheritBuffer = newBuffer;
+ attr->fdInheritBufferSize = newSize;
+ }
+ cur = attr->fdInheritBuffer + attr->fdInheritBufferUsed;
+ freeSize = attr->fdInheritBufferSize - attr->fdInheritBufferUsed;
+ if (0 == attr->fdInheritBufferUsed) {
+ nwritten = PR_snprintf(cur, freeSize,
+ "NSPR_INHERIT_FDS=%s:%d:0x%" PR_PRIxOSFD,
+ name, (PRIntn)fd->methods->file_type, fd->secret->md.osfd);
+ } else {
+ nwritten = PR_snprintf(cur, freeSize, ":%s:%d:0x%" PR_PRIxOSFD,
+ name, (PRIntn)fd->methods->file_type, fd->secret->md.osfd);
+ }
+ attr->fdInheritBufferUsed += nwritten;
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRFileDesc *) PR_GetInheritedFD(
+ const char *name)
+{
+ PRFileDesc *fd;
+ const char *envVar;
+ const char *ptr;
+ int len = strlen(name);
+ PROsfd osfd;
+ int nColons;
+ PRIntn fileType;
+
+ envVar = PR_GetEnv("NSPR_INHERIT_FDS");
+ if (NULL == envVar || '\0' == envVar[0]) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return NULL;
+ }
+
+ ptr = envVar;
+ while (1) {
+ if ((ptr[len] == ':') && (strncmp(ptr, name, len) == 0)) {
+ ptr += len + 1;
+ if (PR_sscanf(ptr, "%d:0x%" PR_SCNxOSFD, &fileType, &osfd) != 2) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return NULL;
+ }
+ switch ((PRDescType)fileType) {
+ case PR_DESC_FILE:
+ fd = PR_ImportFile(osfd);
+ break;
+ case PR_DESC_PIPE:
+ fd = PR_ImportPipe(osfd);
+ break;
+ case PR_DESC_SOCKET_TCP:
+ fd = PR_ImportTCPSocket(osfd);
+ break;
+ case PR_DESC_SOCKET_UDP:
+ fd = PR_ImportUDPSocket(osfd);
+ break;
+ default:
+ PR_ASSERT(0);
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ fd = NULL;
+ break;
+ }
+ if (fd) {
+ /*
+ * An inherited FD is inheritable by default.
+ * The child process needs to call PR_SetFDInheritable
+ * to make it non-inheritable if so desired.
+ */
+ fd->secret->inheritable = _PR_TRI_TRUE;
+ }
+ return fd;
+ }
+ /* Skip three colons */
+ nColons = 0;
+ while (*ptr) {
+ if (*ptr == ':') {
+ if (++nColons == 3) {
+ break;
+ }
+ }
+ ptr++;
+ }
+ if (*ptr == '\0') {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return NULL;
+ }
+ ptr++;
+ }
+}
+
+PR_IMPLEMENT(PRProcess*) PR_CreateProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ return _PR_MD_CREATE_PROCESS(path, argv, envp, attr);
+} /* PR_CreateProcess */
+
+PR_IMPLEMENT(PRStatus) PR_CreateProcessDetached(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ PRProcess *process;
+ PRStatus rv;
+
+ process = PR_CreateProcess(path, argv, envp, attr);
+ if (NULL == process) {
+ return PR_FAILURE;
+ }
+ rv = PR_DetachProcess(process);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if (rv == PR_FAILURE) {
+ PR_DELETE(process);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_DetachProcess(PRProcess *process)
+{
+ return _PR_MD_DETACH_PROCESS(process);
+}
+
+PR_IMPLEMENT(PRStatus) PR_WaitProcess(PRProcess *process, PRInt32 *exitCode)
+{
+ return _PR_MD_WAIT_PROCESS(process, exitCode);
+} /* PR_WaitProcess */
+
+PR_IMPLEMENT(PRStatus) PR_KillProcess(PRProcess *process)
+{
+ return _PR_MD_KILL_PROCESS(process);
+}
+
+/*
+ ********************************************************************
+ *
+ * Module initialization
+ *
+ ********************************************************************
+ */
+
+static struct {
+ PRLock *ml;
+ PRCondVar *cv;
+} mod_init;
+
+static void _PR_InitCallOnce(void) {
+ mod_init.ml = PR_NewLock();
+ PR_ASSERT(NULL != mod_init.ml);
+ mod_init.cv = PR_NewCondVar(mod_init.ml);
+ PR_ASSERT(NULL != mod_init.cv);
+}
+
+void _PR_CleanupCallOnce()
+{
+ PR_DestroyLock(mod_init.ml);
+ mod_init.ml = NULL;
+ PR_DestroyCondVar(mod_init.cv);
+ mod_init.cv = NULL;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CallOnce(
+ PRCallOnceType *once,
+ PRCallOnceFN func)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ PR_Lock(mod_init.ml);
+ PRIntn initialized = once->initialized;
+ PRStatus status = once->status;
+ PR_Unlock(mod_init.ml);
+ if (!initialized) {
+ if (PR_ATOMIC_SET(&once->inProgress, 1) == 0) {
+ status = (*func)();
+ PR_Lock(mod_init.ml);
+ once->status = status;
+ once->initialized = 1;
+ PR_NotifyAllCondVar(mod_init.cv);
+ PR_Unlock(mod_init.ml);
+ } else {
+ PR_Lock(mod_init.ml);
+ while (!once->initialized) {
+ PR_WaitCondVar(mod_init.cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ status = once->status;
+ PR_Unlock(mod_init.ml);
+ if (PR_SUCCESS != status) {
+ PR_SetError(PR_CALL_ONCE_ERROR, 0);
+ }
+ }
+ return status;
+ }
+ if (PR_SUCCESS != status) {
+ PR_SetError(PR_CALL_ONCE_ERROR, 0);
+ }
+ return status;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CallOnceWithArg(
+ PRCallOnceType *once,
+ PRCallOnceWithArgFN func,
+ void *arg)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ PR_Lock(mod_init.ml);
+ PRIntn initialized = once->initialized;
+ PRStatus status = once->status;
+ PR_Unlock(mod_init.ml);
+ if (!initialized) {
+ if (PR_ATOMIC_SET(&once->inProgress, 1) == 0) {
+ status = (*func)(arg);
+ PR_Lock(mod_init.ml);
+ once->status = status;
+ once->initialized = 1;
+ PR_NotifyAllCondVar(mod_init.cv);
+ PR_Unlock(mod_init.ml);
+ } else {
+ PR_Lock(mod_init.ml);
+ while (!once->initialized) {
+ PR_WaitCondVar(mod_init.cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ status = once->status;
+ PR_Unlock(mod_init.ml);
+ if (PR_SUCCESS != status) {
+ PR_SetError(PR_CALL_ONCE_ERROR, 0);
+ }
+ }
+ return status;
+ }
+ if (PR_SUCCESS != status) {
+ PR_SetError(PR_CALL_ONCE_ERROR, 0);
+ }
+ return status;
+}
+
+PRBool _PR_Obsolete(const char *obsolete, const char *preferred)
+{
+#if defined(DEBUG)
+ PR_fprintf(
+ PR_STDERR, "'%s' is obsolete. Use '%s' instead.\n",
+ obsolete, (NULL == preferred) ? "something else" : preferred);
+#endif
+ return PR_FALSE;
+} /* _PR_Obsolete */
+
+/* prinit.c */
+
+
diff --git a/nsprpub/pr/src/misc/prinrval.c b/nsprpub/pr/src/misc/prinrval.c
new file mode 100644
index 0000000000..79ff2cbca4
--- /dev/null
+++ b/nsprpub/pr/src/misc/prinrval.c
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * file: prinrval.c
+ * description: implementation for the kernel interval timing functions
+ */
+
+#include "primpl.h"
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * _PR_InitClock --
+ *
+ *
+ *-----------------------------------------------------------------------
+ */
+
+void _PR_InitClock(void)
+{
+ _PR_MD_INTERVAL_INIT();
+#ifdef DEBUG
+ {
+ PRIntervalTime ticksPerSec = PR_TicksPerSecond();
+
+ PR_ASSERT(ticksPerSec >= PR_INTERVAL_MIN);
+ PR_ASSERT(ticksPerSec <= PR_INTERVAL_MAX);
+ }
+#endif /* DEBUG */
+}
+
+PR_IMPLEMENT(PRIntervalTime) PR_IntervalNow(void)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ return _PR_MD_GET_INTERVAL();
+} /* PR_IntervalNow */
+
+PR_EXTERN(PRUint32) PR_TicksPerSecond(void)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ return _PR_MD_INTERVAL_PER_SEC();
+} /* PR_TicksPerSecond */
+
+PR_IMPLEMENT(PRIntervalTime) PR_SecondsToInterval(PRUint32 seconds)
+{
+ return seconds * PR_TicksPerSecond();
+} /* PR_SecondsToInterval */
+
+PR_IMPLEMENT(PRIntervalTime) PR_MillisecondsToInterval(PRUint32 milli)
+{
+ PRIntervalTime ticks;
+ PRUint64 tock, tps, msecPerSec, rounding;
+ LL_UI2L(tock, milli);
+ LL_I2L(msecPerSec, PR_MSEC_PER_SEC);
+ LL_I2L(rounding, (PR_MSEC_PER_SEC >> 1));
+ LL_I2L(tps, PR_TicksPerSecond());
+ LL_MUL(tock, tock, tps);
+ LL_ADD(tock, tock, rounding);
+ LL_DIV(tock, tock, msecPerSec);
+ LL_L2UI(ticks, tock);
+ return ticks;
+} /* PR_MillisecondsToInterval */
+
+PR_IMPLEMENT(PRIntervalTime) PR_MicrosecondsToInterval(PRUint32 micro)
+{
+ PRIntervalTime ticks;
+ PRUint64 tock, tps, usecPerSec, rounding;
+ LL_UI2L(tock, micro);
+ LL_I2L(usecPerSec, PR_USEC_PER_SEC);
+ LL_I2L(rounding, (PR_USEC_PER_SEC >> 1));
+ LL_I2L(tps, PR_TicksPerSecond());
+ LL_MUL(tock, tock, tps);
+ LL_ADD(tock, tock, rounding);
+ LL_DIV(tock, tock, usecPerSec);
+ LL_L2UI(ticks, tock);
+ return ticks;
+} /* PR_MicrosecondsToInterval */
+
+PR_IMPLEMENT(PRUint32) PR_IntervalToSeconds(PRIntervalTime ticks)
+{
+ return ticks / PR_TicksPerSecond();
+} /* PR_IntervalToSeconds */
+
+PR_IMPLEMENT(PRUint32) PR_IntervalToMilliseconds(PRIntervalTime ticks)
+{
+ PRUint32 milli;
+ PRUint64 tock, tps, msecPerSec, rounding;
+ LL_UI2L(tock, ticks);
+ LL_I2L(msecPerSec, PR_MSEC_PER_SEC);
+ LL_I2L(tps, PR_TicksPerSecond());
+ LL_USHR(rounding, tps, 1);
+ LL_MUL(tock, tock, msecPerSec);
+ LL_ADD(tock, tock, rounding);
+ LL_DIV(tock, tock, tps);
+ LL_L2UI(milli, tock);
+ return milli;
+} /* PR_IntervalToMilliseconds */
+
+PR_IMPLEMENT(PRUint32) PR_IntervalToMicroseconds(PRIntervalTime ticks)
+{
+ PRUint32 micro;
+ PRUint64 tock, tps, usecPerSec, rounding;
+ LL_UI2L(tock, ticks);
+ LL_I2L(usecPerSec, PR_USEC_PER_SEC);
+ LL_I2L(tps, PR_TicksPerSecond());
+ LL_USHR(rounding, tps, 1);
+ LL_MUL(tock, tock, usecPerSec);
+ LL_ADD(tock, tock, rounding);
+ LL_DIV(tock, tock, tps);
+ LL_L2UI(micro, tock);
+ return micro;
+} /* PR_IntervalToMicroseconds */
+
+/* prinrval.c */
+
diff --git a/nsprpub/pr/src/misc/pripc.c b/nsprpub/pr/src/misc/pripc.c
new file mode 100644
index 0000000000..64415dd631
--- /dev/null
+++ b/nsprpub/pr/src/misc/pripc.c
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: pripc.c
+ *
+ * Description: functions for IPC support
+ */
+
+#include "primpl.h"
+
+#include <string.h>
+
+/*
+ * A POSIX IPC name must begin with a '/'.
+ * A POSIX IPC name on Solaris cannot contain any '/' except
+ * the required leading '/'.
+ * A POSIX IPC name on HP-UX must be a valid pathname
+ * in the file system.
+ *
+ * The ftok() function for System V IPC requires a valid pathname
+ * in the file system.
+ *
+ * A Win32 IPC name cannot contain '\'.
+ */
+
+static void _pr_ConvertSemName(char *result)
+{
+#ifdef _PR_HAVE_POSIX_SEMAPHORES
+#if defined(SOLARIS)
+ char *p;
+
+ /* Convert '/' to '_' except for the leading '/' */
+ for (p = result+1; *p; p++) {
+ if (*p == '/') {
+ *p = '_';
+ }
+ }
+ return;
+#else
+ return;
+#endif
+#elif defined(_PR_HAVE_SYSV_SEMAPHORES)
+ return;
+#elif defined(WIN32)
+ return;
+#endif
+}
+
+static void _pr_ConvertShmName(char *result)
+{
+#if defined(PR_HAVE_POSIX_NAMED_SHARED_MEMORY)
+#if defined(SOLARIS)
+ char *p;
+
+ /* Convert '/' to '_' except for the leading '/' */
+ for (p = result+1; *p; p++) {
+ if (*p == '/') {
+ *p = '_';
+ }
+ }
+ return;
+#else
+ return;
+#endif
+#elif defined(PR_HAVE_SYSV_NAMED_SHARED_MEMORY)
+ return;
+#elif defined(WIN32)
+ return;
+#else
+ return;
+#endif
+}
+
+PRStatus _PR_MakeNativeIPCName(
+ const char *name,
+ char *result,
+ PRIntn size,
+ _PRIPCType type)
+{
+ if (strlen(name) >= (PRSize)size) {
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ return PR_FAILURE;
+ }
+ strcpy(result, name);
+ switch (type) {
+ case _PRIPCSem:
+ _pr_ConvertSemName(result);
+ break;
+ case _PRIPCShm:
+ _pr_ConvertShmName(result);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
diff --git a/nsprpub/pr/src/misc/pripcsem.c b/nsprpub/pr/src/misc/pripcsem.c
new file mode 100644
index 0000000000..49b051b74d
--- /dev/null
+++ b/nsprpub/pr/src/misc/pripcsem.c
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: pripcsem.c
+ *
+ * Description: implements the named semaphores API in prsemipc.h
+ * for classic NSPR. If _PR_HAVE_NAMED_SEMAPHORES is not defined,
+ * the named semaphore functions all fail with the error code
+ * PR_NOT_IMPLEMENTED_ERROR.
+ */
+
+#include "primpl.h"
+
+#ifdef _PR_PTHREADS
+
+#error "This file should not be compiled for the pthreads version"
+
+#else
+
+#ifndef _PR_HAVE_NAMED_SEMAPHORES
+
+PRSem * _PR_MD_OPEN_SEMAPHORE(
+ const char *osname, PRIntn flags, PRIntn mode, PRUintn value)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+PRStatus _PR_MD_WAIT_SEMAPHORE(PRSem *sem)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _PR_MD_POST_SEMAPHORE(PRSem *sem)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _PR_MD_CLOSE_SEMAPHORE(PRSem *sem)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _PR_MD_DELETE_SEMAPHORE(const char *osname)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+#endif /* !_PR_HAVE_NAMED_SEMAPHORES */
+
+PR_IMPLEMENT(PRSem *) PR_OpenSemaphore(
+ const char *name, PRIntn flags, PRIntn mode, PRUintn value)
+{
+ char osname[PR_IPC_NAME_SIZE];
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
+ == PR_FAILURE) {
+ return NULL;
+ }
+ return _PR_MD_OPEN_SEMAPHORE(osname, flags, mode, value);
+}
+
+PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem)
+{
+ return _PR_MD_WAIT_SEMAPHORE(sem);
+}
+
+PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem)
+{
+ return _PR_MD_POST_SEMAPHORE(sem);
+}
+
+PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem)
+{
+ return _PR_MD_CLOSE_SEMAPHORE(sem);
+}
+
+PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name)
+{
+ char osname[PR_IPC_NAME_SIZE];
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
+ == PR_FAILURE) {
+ return PR_FAILURE;
+ }
+ return _PR_MD_DELETE_SEMAPHORE(osname);
+}
+
+#endif /* _PR_PTHREADS */
diff --git a/nsprpub/pr/src/misc/prlog2.c b/nsprpub/pr/src/misc/prlog2.c
new file mode 100644
index 0000000000..2d476cd711
--- /dev/null
+++ b/nsprpub/pr/src/misc/prlog2.c
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prbit.h"
+
+/*
+** Compute the log of the least power of 2 greater than or equal to n
+*/
+PR_IMPLEMENT(PRIntn) PR_CeilingLog2(PRUint32 n)
+{
+ PRIntn log2;
+ PR_CEILING_LOG2(log2, n);
+ return log2;
+}
+
+/*
+** Compute the log of the greatest power of 2 less than or equal to n.
+** This really just finds the highest set bit in the word.
+*/
+PR_IMPLEMENT(PRIntn) PR_FloorLog2(PRUint32 n)
+{
+ PRIntn log2;
+ PR_FLOOR_LOG2(log2, n);
+ return log2;
+}
diff --git a/nsprpub/pr/src/misc/prlong.c b/nsprpub/pr/src/misc/prlong.c
new file mode 100644
index 0000000000..9c5abf06bb
--- /dev/null
+++ b/nsprpub/pr/src/misc/prlong.c
@@ -0,0 +1,254 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prlong.h"
+
+static PRInt64 ll_zero = PR_INT64(0x0000000000000000);
+static PRInt64 ll_maxint = PR_INT64(0x7fffffffffffffff);
+static PRInt64 ll_minint = PR_INT64(0x8000000000000000);
+static PRUint64 ll_maxuint = PR_UINT64(0xffffffffffffffff);
+
+PR_IMPLEMENT(PRInt64) LL_Zero(void) {
+ return ll_zero;
+}
+PR_IMPLEMENT(PRInt64) LL_MaxInt(void) {
+ return ll_maxint;
+}
+PR_IMPLEMENT(PRInt64) LL_MinInt(void) {
+ return ll_minint;
+}
+PR_IMPLEMENT(PRUint64) LL_MaxUint(void) {
+ return ll_maxuint;
+}
+
+#ifndef HAVE_LONG_LONG
+/*
+** Divide 64-bit a by 32-bit b, which must be normalized so its high bit is 1.
+*/
+static void norm_udivmod32(PRUint32 *qp, PRUint32 *rp, PRUint64 a, PRUint32 b)
+{
+ PRUint32 d1, d0, q1, q0;
+ PRUint32 r1, r0, m;
+
+ d1 = _hi16(b);
+ d0 = _lo16(b);
+ r1 = a.hi % d1;
+ q1 = a.hi / d1;
+ m = q1 * d0;
+ r1 = (r1 << 16) | _hi16(a.lo);
+ if (r1 < m) {
+ q1--, r1 += b;
+ if (r1 >= b /* i.e., we didn't get a carry when adding to r1 */
+ && r1 < m) {
+ q1--, r1 += b;
+ }
+ }
+ r1 -= m;
+ r0 = r1 % d1;
+ q0 = r1 / d1;
+ m = q0 * d0;
+ r0 = (r0 << 16) | _lo16(a.lo);
+ if (r0 < m) {
+ q0--, r0 += b;
+ if (r0 >= b
+ && r0 < m) {
+ q0--, r0 += b;
+ }
+ }
+ *qp = (q1 << 16) | q0;
+ *rp = r0 - m;
+}
+
+static PRUint32 CountLeadingZeros(PRUint32 a)
+{
+ PRUint32 t;
+ PRUint32 r = 32;
+
+ if ((t = a >> 16) != 0) {
+ r -= 16, a = t;
+ }
+ if ((t = a >> 8) != 0) {
+ r -= 8, a = t;
+ }
+ if ((t = a >> 4) != 0) {
+ r -= 4, a = t;
+ }
+ if ((t = a >> 2) != 0) {
+ r -= 2, a = t;
+ }
+ if ((t = a >> 1) != 0) {
+ r -= 1, a = t;
+ }
+ if (a & 1) {
+ r--;
+ }
+ return r;
+}
+
+PR_IMPLEMENT(void) ll_udivmod(PRUint64 *qp, PRUint64 *rp, PRUint64 a, PRUint64 b)
+{
+ PRUint32 n0, n1, n2;
+ PRUint32 q0, q1;
+ PRUint32 rsh, lsh;
+
+ n0 = a.lo;
+ n1 = a.hi;
+
+ if (b.hi == 0) {
+ if (b.lo > n1) {
+ /* (0 q0) = (n1 n0) / (0 D0) */
+
+ lsh = CountLeadingZeros(b.lo);
+
+ if (lsh) {
+ /*
+ * Normalize, i.e. make the most significant bit of the
+ * denominator be set.
+ */
+ b.lo = b.lo << lsh;
+ n1 = (n1 << lsh) | (n0 >> (32 - lsh));
+ n0 = n0 << lsh;
+ }
+
+ a.lo = n0, a.hi = n1;
+ norm_udivmod32(&q0, &n0, a, b.lo);
+ q1 = 0;
+
+ /* remainder is in n0 >> lsh */
+ } else {
+ /* (q1 q0) = (n1 n0) / (0 d0) */
+
+ if (b.lo == 0) { /* user wants to divide by zero! */
+ b.lo = 1 / b.lo; /* so go ahead and crash */
+ }
+
+ lsh = CountLeadingZeros(b.lo);
+
+ if (lsh == 0) {
+ /*
+ * From (n1 >= b.lo)
+ * && (the most significant bit of b.lo is set),
+ * conclude that
+ * (the most significant bit of n1 is set)
+ * && (the leading quotient digit q1 = 1).
+ *
+ * This special case is necessary, not an optimization
+ * (Shifts counts of 32 are undefined).
+ */
+ n1 -= b.lo;
+ q1 = 1;
+ } else {
+ /*
+ * Normalize.
+ */
+ rsh = 32 - lsh;
+
+ b.lo = b.lo << lsh;
+ n2 = n1 >> rsh;
+ n1 = (n1 << lsh) | (n0 >> rsh);
+ n0 = n0 << lsh;
+
+ a.lo = n1, a.hi = n2;
+ norm_udivmod32(&q1, &n1, a, b.lo);
+ }
+
+ /* n1 != b.lo... */
+
+ a.lo = n0, a.hi = n1;
+ norm_udivmod32(&q0, &n0, a, b.lo);
+
+ /* remainder in n0 >> lsh */
+ }
+
+ if (rp) {
+ rp->lo = n0 >> lsh;
+ rp->hi = 0;
+ }
+ } else {
+ if (b.hi > n1) {
+ /* (0 0) = (n1 n0) / (D1 d0) */
+
+ q0 = 0;
+ q1 = 0;
+
+ /* remainder in (n1 n0) */
+ if (rp) {
+ rp->lo = n0;
+ rp->hi = n1;
+ }
+ } else {
+ /* (0 q0) = (n1 n0) / (d1 d0) */
+
+ lsh = CountLeadingZeros(b.hi);
+ if (lsh == 0) {
+ /*
+ * From (n1 >= b.hi)
+ * && (the most significant bit of b.hi is set),
+ * conclude that
+ * (the most significant bit of n1 is set)
+ * && (the quotient digit q0 = 0 or 1).
+ *
+ * This special case is necessary, not an optimization.
+ */
+
+ /*
+ * The condition on the next line takes advantage of that
+ * n1 >= b.hi (true due to control flow).
+ */
+ if (n1 > b.hi || n0 >= b.lo) {
+ q0 = 1;
+ a.lo = n0, a.hi = n1;
+ LL_SUB(a, a, b);
+ } else {
+ q0 = 0;
+ }
+ q1 = 0;
+
+ if (rp) {
+ rp->lo = n0;
+ rp->hi = n1;
+ }
+ } else {
+ PRInt64 m;
+
+ /*
+ * Normalize.
+ */
+ rsh = 32 - lsh;
+
+ b.hi = (b.hi << lsh) | (b.lo >> rsh);
+ b.lo = b.lo << lsh;
+ n2 = n1 >> rsh;
+ n1 = (n1 << lsh) | (n0 >> rsh);
+ n0 = n0 << lsh;
+
+ a.lo = n1, a.hi = n2;
+ norm_udivmod32(&q0, &n1, a, b.hi);
+ LL_MUL32(m, q0, b.lo);
+
+ if ((m.hi > n1) || ((m.hi == n1) && (m.lo > n0))) {
+ q0--;
+ LL_SUB(m, m, b);
+ }
+
+ q1 = 0;
+
+ /* Remainder is ((n1 n0) - (m1 m0)) >> lsh */
+ if (rp) {
+ a.lo = n0, a.hi = n1;
+ LL_SUB(a, a, m);
+ rp->lo = (a.hi << rsh) | (a.lo >> lsh);
+ rp->hi = a.hi >> lsh;
+ }
+ }
+ }
+ }
+
+ if (qp) {
+ qp->lo = q0;
+ qp->hi = q1;
+ }
+}
+#endif /* !HAVE_LONG_LONG */
diff --git a/nsprpub/pr/src/misc/prnetdb.c b/nsprpub/pr/src/misc/prnetdb.c
new file mode 100644
index 0000000000..7decd96751
--- /dev/null
+++ b/nsprpub/pr/src/misc/prnetdb.c
@@ -0,0 +1,2549 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <string.h>
+
+#if defined(LINUX)
+#include <sys/un.h>
+#endif
+
+/*
+ * On Unix, the error code for gethostbyname() and gethostbyaddr()
+ * is returned in the global variable h_errno, instead of the usual
+ * errno.
+ */
+#if defined(XP_UNIX)
+#if defined(_PR_NEED_H_ERRNO)
+extern int h_errno;
+#endif
+#define _MD_GETHOST_ERRNO() h_errno
+#else
+#define _MD_GETHOST_ERRNO() _MD_ERRNO()
+#endif
+
+/*
+ * The meaning of the macros related to gethostbyname, gethostbyaddr,
+ * and gethostbyname2 is defined below.
+ * - _PR_HAVE_THREADSAFE_GETHOST: the gethostbyXXX functions return
+ * the result in thread specific storage. For example, AIX, HP-UX.
+ * - _PR_HAVE_GETHOST_R: have the gethostbyXXX_r functions. See next
+ * two macros.
+ * - _PR_HAVE_GETHOST_R_INT: the gethostbyXXX_r functions return an
+ * int. For example, Linux glibc.
+ * - _PR_HAVE_GETHOST_R_POINTER: the gethostbyXXX_r functions return
+ * a struct hostent* pointer. For example, Solaris.
+ */
+#if defined(_PR_NO_PREEMPT) || defined(_PR_HAVE_GETHOST_R) \
+ || defined(_PR_HAVE_THREADSAFE_GETHOST)
+#define _PR_NO_DNS_LOCK
+#endif
+
+#if defined(_PR_NO_DNS_LOCK)
+#define LOCK_DNS()
+#define UNLOCK_DNS()
+#else
+PRLock *_pr_dnsLock = NULL;
+#define LOCK_DNS() PR_Lock(_pr_dnsLock)
+#define UNLOCK_DNS() PR_Unlock(_pr_dnsLock)
+#endif /* defined(_PR_NO_DNS_LOCK) */
+
+/*
+ * Some platforms have the reentrant getprotobyname_r() and
+ * getprotobynumber_r(). However, they come in three flavors.
+ * Some return a pointer to struct protoent, others return
+ * an int, and glibc's flavor takes five arguments.
+ */
+
+#if defined(SOLARIS) || (defined(BSDI) && defined(_REENTRANT)) \
+ || (defined(LINUX) && defined(_REENTRANT) \
+ && defined(__GLIBC__) && __GLIBC__ < 2)
+#define _PR_HAVE_GETPROTO_R
+#define _PR_HAVE_GETPROTO_R_POINTER
+#endif
+
+#if defined(AIX4_3_PLUS) || (defined(AIX) && defined(_THREAD_SAFE)) \
+ || (defined(HPUX10_10) && defined(_REENTRANT)) \
+ || (defined(HPUX10_20) && defined(_REENTRANT)) \
+ || defined(OPENBSD)
+#define _PR_HAVE_GETPROTO_R
+#define _PR_HAVE_GETPROTO_R_INT
+#endif
+
+#if __FreeBSD_version >= 602000
+#define _PR_HAVE_GETPROTO_R
+#define _PR_HAVE_5_ARG_GETPROTO_R
+#endif
+
+/* BeOS has glibc but not the glibc-style getprotobyxxx_r functions. */
+#if (defined(__GLIBC__) && __GLIBC__ >= 2)
+#define _PR_HAVE_GETPROTO_R
+#define _PR_HAVE_5_ARG_GETPROTO_R
+#endif
+
+#if !defined(_PR_HAVE_GETPROTO_R)
+PRLock* _getproto_lock = NULL;
+#endif
+
+#if defined(_PR_INET6_PROBE)
+extern PRBool _pr_ipv6_is_present(void);
+#endif
+
+#define _PR_IN6_IS_ADDR_UNSPECIFIED(a) \
+ (((a)->pr_s6_addr32[0] == 0) && \
+ ((a)->pr_s6_addr32[1] == 0) && \
+ ((a)->pr_s6_addr32[2] == 0) && \
+ ((a)->pr_s6_addr32[3] == 0))
+
+#define _PR_IN6_IS_ADDR_LOOPBACK(a) \
+ (((a)->pr_s6_addr32[0] == 0) && \
+ ((a)->pr_s6_addr32[1] == 0) && \
+ ((a)->pr_s6_addr32[2] == 0) && \
+ ((a)->pr_s6_addr[12] == 0) && \
+ ((a)->pr_s6_addr[13] == 0) && \
+ ((a)->pr_s6_addr[14] == 0) && \
+ ((a)->pr_s6_addr[15] == 0x1U))
+
+const PRIPv6Addr _pr_in6addr_any = {{{
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+ }
+ }
+};
+
+const PRIPv6Addr _pr_in6addr_loopback = {{{
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0x1U
+ }
+ }
+};
+/*
+ * The values at bytes 10 and 11 are compared using pointers to
+ * 8-bit fields, and not 32-bit fields, to make the comparison work on
+ * both big-endian and little-endian systems
+ */
+
+#define _PR_IN6_IS_ADDR_V4MAPPED(a) \
+ (((a)->pr_s6_addr32[0] == 0) && \
+ ((a)->pr_s6_addr32[1] == 0) && \
+ ((a)->pr_s6_addr[8] == 0) && \
+ ((a)->pr_s6_addr[9] == 0) && \
+ ((a)->pr_s6_addr[10] == 0xff) && \
+ ((a)->pr_s6_addr[11] == 0xff))
+
+#define _PR_IN6_IS_ADDR_V4COMPAT(a) \
+ (((a)->pr_s6_addr32[0] == 0) && \
+ ((a)->pr_s6_addr32[1] == 0) && \
+ ((a)->pr_s6_addr32[2] == 0))
+
+#define _PR_IN6_V4MAPPED_TO_IPADDR(a) ((a)->pr_s6_addr32[3])
+
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2)
+
+/*
+ * The _pr_QueryNetIfs() function finds out if the system has
+ * IPv4 or IPv6 source addresses configured and sets _pr_have_inet_if
+ * and _pr_have_inet6_if accordingly.
+ *
+ * We have an implementation using SIOCGIFCONF ioctl and a
+ * default implementation that simply sets _pr_have_inet_if
+ * and _pr_have_inet6_if to true. A better implementation
+ * would be to use the routing sockets (see Chapter 17 of
+ * W. Richard Stevens' Unix Network Programming, Vol. 1, 2nd. Ed.)
+ */
+
+static PRLock *_pr_query_ifs_lock = NULL;
+static PRBool _pr_have_inet_if = PR_FALSE;
+static PRBool _pr_have_inet6_if = PR_FALSE;
+
+#undef DEBUG_QUERY_IFS
+
+#if defined(AIX) \
+ || (defined(DARWIN) && !defined(HAVE_GETIFADDRS))
+
+/*
+ * Use SIOCGIFCONF ioctl on platforms that don't have routing
+ * sockets. Warning: whether SIOCGIFCONF ioctl returns AF_INET6
+ * network interfaces is not portable.
+ *
+ * The _pr_QueryNetIfs() function is derived from the code in
+ * src/lib/libc/net/getifaddrs.c in BSD Unix and the code in
+ * Section 16.6 of W. Richard Stevens' Unix Network Programming,
+ * Vol. 1, 2nd. Ed.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#ifdef DEBUG_QUERY_IFS
+static void
+_pr_PrintIfreq(struct ifreq *ifr)
+{
+ PRNetAddr addr;
+ struct sockaddr *sa;
+ const char* family;
+ char addrstr[64];
+
+ sa = &ifr->ifr_addr;
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ family = "inet";
+ memcpy(&addr.inet.ip, &sin->sin_addr, sizeof(sin->sin_addr));
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ family = "inet6";
+ memcpy(&addr.ipv6.ip, &sin6->sin6_addr, sizeof(sin6->sin6_addr));
+ } else {
+ return; /* skip if not AF_INET or AF_INET6 */
+ }
+ addr.raw.family = sa->sa_family;
+ PR_NetAddrToString(&addr, addrstr, sizeof(addrstr));
+ printf("%s: %s %s\n", ifr->ifr_name, family, addrstr);
+}
+#endif
+
+static void
+_pr_QueryNetIfs(void)
+{
+ int sock;
+ int rv;
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ struct ifreq *lifr;
+ PRUint32 len, lastlen;
+ char *buf;
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ return;
+ }
+
+ /* Issue SIOCGIFCONF request in a loop. */
+ lastlen = 0;
+ len = 100 * sizeof(struct ifreq); /* initial buffer size guess */
+ for (;;) {
+ buf = (char *)PR_Malloc(len);
+ if (NULL == buf) {
+ close(sock);
+ return;
+ }
+ ifc.ifc_buf = buf;
+ ifc.ifc_len = len;
+ rv = ioctl(sock, SIOCGIFCONF, &ifc);
+ if (rv < 0) {
+ if (errno != EINVAL || lastlen != 0) {
+ close(sock);
+ PR_Free(buf);
+ return;
+ }
+ } else {
+ if (ifc.ifc_len == lastlen) {
+ break; /* success, len has not changed */
+ }
+ lastlen = ifc.ifc_len;
+ }
+ len += 10 * sizeof(struct ifreq); /* increment */
+ PR_Free(buf);
+ }
+ close(sock);
+
+ ifr = ifc.ifc_req;
+ lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
+
+ while (ifr < lifr) {
+ struct sockaddr *sa;
+ int sa_len;
+
+#ifdef DEBUG_QUERY_IFS
+ _pr_PrintIfreq(ifr);
+#endif
+ sa = &ifr->ifr_addr;
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *) sa;
+ if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+ _pr_have_inet_if = PR_TRUE;
+ }
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
+ if (!IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)
+ && !IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+ _pr_have_inet6_if = PR_TRUE;
+ }
+ }
+
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ sa_len = PR_MAX(sa->sa_len, sizeof(struct sockaddr));
+#else
+ switch (sa->sa_family) {
+#ifdef AF_LINK
+ case AF_LINK:
+ sa_len = sizeof(struct sockaddr_dl);
+ break;
+#endif
+ case AF_INET6:
+ sa_len = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ sa_len = sizeof(struct sockaddr);
+ break;
+ }
+#endif
+ ifr = (struct ifreq *)(((char *)sa) + sa_len);
+ }
+ PR_Free(buf);
+}
+
+#elif (defined(DARWIN) && defined(HAVE_GETIFADDRS)) || defined(FREEBSD) \
+ || defined(NETBSD) || defined(OPENBSD)
+
+/*
+ * Use the BSD getifaddrs function.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ifaddrs.h>
+#include <netinet/in.h>
+
+#ifdef DEBUG_QUERY_IFS
+static void
+_pr_PrintIfaddrs(struct ifaddrs *ifa)
+{
+ struct sockaddr *sa;
+ const char* family;
+ void *addrp;
+ char addrstr[64];
+
+ sa = ifa->ifa_addr;
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ family = "inet";
+ addrp = &sin->sin_addr;
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ family = "inet6";
+ addrp = &sin6->sin6_addr;
+ } else {
+ return; /* skip if not AF_INET or AF_INET6 */
+ }
+ inet_ntop(sa->sa_family, addrp, addrstr, sizeof(addrstr));
+ printf("%s: %s %s\n", ifa->ifa_name, family, addrstr);
+}
+#endif
+
+static void
+_pr_QueryNetIfs(void)
+{
+ struct ifaddrs *ifp;
+ struct ifaddrs *ifa;
+
+ if (getifaddrs(&ifp) == -1) {
+ return;
+ }
+ for (ifa = ifp; ifa; ifa = ifa->ifa_next) {
+ struct sockaddr *sa;
+
+#ifdef DEBUG_QUERY_IFS
+ _pr_PrintIfaddrs(ifa);
+#endif
+ sa = ifa->ifa_addr;
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *) sa;
+ if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+ _pr_have_inet_if = 1;
+ }
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
+ if (!IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)
+ && !IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+ _pr_have_inet6_if = 1;
+ }
+ }
+ }
+ freeifaddrs(ifp);
+}
+
+#else /* default */
+
+/*
+ * Emulate the code in NSPR 4.2 or older. PR_GetIPNodeByName behaves
+ * as if the system had both IPv4 and IPv6 source addresses configured.
+ */
+static void
+_pr_QueryNetIfs(void)
+{
+ _pr_have_inet_if = PR_TRUE;
+ _pr_have_inet6_if = PR_TRUE;
+}
+
+#endif
+
+#endif /* _PR_INET6 && _PR_HAVE_GETHOSTBYNAME2 */
+
+void _PR_InitNet(void)
+{
+#if defined(XP_UNIX)
+#ifdef HAVE_NETCONFIG
+ /*
+ * This one-liner prevents the endless re-open's and re-read's of
+ * /etc/netconfig on EACH and EVERY call to accept(), connect(), etc.
+ */
+ (void)setnetconfig();
+#endif
+#endif
+#if !defined(_PR_NO_DNS_LOCK)
+ _pr_dnsLock = PR_NewLock();
+#endif
+#if !defined(_PR_HAVE_GETPROTO_R)
+ _getproto_lock = PR_NewLock();
+#endif
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2)
+ _pr_query_ifs_lock = PR_NewLock();
+#endif
+}
+
+void _PR_CleanupNet(void)
+{
+#if !defined(_PR_NO_DNS_LOCK)
+ if (_pr_dnsLock) {
+ PR_DestroyLock(_pr_dnsLock);
+ _pr_dnsLock = NULL;
+ }
+#endif
+#if !defined(_PR_HAVE_GETPROTO_R)
+ if (_getproto_lock) {
+ PR_DestroyLock(_getproto_lock);
+ _getproto_lock = NULL;
+ }
+#endif
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2)
+ if (_pr_query_ifs_lock) {
+ PR_DestroyLock(_pr_query_ifs_lock);
+ _pr_query_ifs_lock = NULL;
+ }
+#endif
+}
+
+/*
+** Allocate space from the buffer, aligning it to "align" before doing
+** the allocation. "align" must be a power of 2.
+*/
+static char *Alloc(PRIntn amount, char **bufp, PRIntn *buflenp, PRIntn align)
+{
+ char *buf = *bufp;
+ PRIntn buflen = *buflenp;
+
+ if (align && ((long)buf & (align - 1))) {
+ PRIntn skip = align - ((ptrdiff_t)buf & (align - 1));
+ if (buflen < skip) {
+ return 0;
+ }
+ buf += skip;
+ buflen -= skip;
+ }
+ if (buflen < amount) {
+ return 0;
+ }
+ *bufp = buf + amount;
+ *buflenp = buflen - amount;
+ return buf;
+}
+
+typedef enum _PRIPAddrConversion {
+ _PRIPAddrNoConversion,
+ _PRIPAddrIPv4Mapped,
+ _PRIPAddrIPv4Compat
+} _PRIPAddrConversion;
+
+/*
+** Convert an IPv4 address (v4) to an IPv4-mapped IPv6 address (v6).
+*/
+static void MakeIPv4MappedAddr(const char *v4, char *v6)
+{
+ memset(v6, 0, 10);
+ memset(v6 + 10, 0xff, 2);
+ memcpy(v6 + 12, v4, 4);
+}
+
+/*
+** Convert an IPv4 address (v4) to an IPv4-compatible IPv6 address (v6).
+*/
+static void MakeIPv4CompatAddr(const char *v4, char *v6)
+{
+ memset(v6, 0, 12);
+ memcpy(v6 + 12, v4, 4);
+}
+
+/*
+** Copy a hostent, and all of the memory that it refers to into
+** (hopefully) stacked buffers.
+*/
+static PRStatus CopyHostent(
+ struct hostent *from,
+ char **buf,
+ PRIntn *bufsize,
+ _PRIPAddrConversion conversion,
+ PRHostEnt *to)
+{
+ PRIntn len, na;
+ char **ap;
+
+ if (conversion != _PRIPAddrNoConversion
+ && from->h_addrtype == AF_INET) {
+ PR_ASSERT(from->h_length == 4);
+ to->h_addrtype = PR_AF_INET6;
+ to->h_length = 16;
+ } else {
+#if defined(_PR_INET6) || defined(_PR_INET6_PROBE)
+ if (AF_INET6 == from->h_addrtype) {
+ to->h_addrtype = PR_AF_INET6;
+ }
+ else
+#endif
+ to->h_addrtype = from->h_addrtype;
+ to->h_length = from->h_length;
+ }
+
+ /* Copy the official name */
+ if (!from->h_name) {
+ return PR_FAILURE;
+ }
+ len = strlen(from->h_name) + 1;
+ to->h_name = Alloc(len, buf, bufsize, 0);
+ if (!to->h_name) {
+ return PR_FAILURE;
+ }
+ memcpy(to->h_name, from->h_name, len);
+
+ /* Count the aliases, then allocate storage for the pointers */
+ if (!from->h_aliases) {
+ na = 1;
+ } else {
+ for (na = 1, ap = from->h_aliases; *ap != 0; na++, ap++) {;} /* nothing to execute */
+ }
+ to->h_aliases = (char**)Alloc(
+ na * sizeof(char*), buf, bufsize, sizeof(char**));
+ if (!to->h_aliases) {
+ return PR_FAILURE;
+ }
+
+ /* Copy the aliases, one at a time */
+ if (!from->h_aliases) {
+ to->h_aliases[0] = 0;
+ } else {
+ for (na = 0, ap = from->h_aliases; *ap != 0; na++, ap++) {
+ len = strlen(*ap) + 1;
+ to->h_aliases[na] = Alloc(len, buf, bufsize, 0);
+ if (!to->h_aliases[na]) {
+ return PR_FAILURE;
+ }
+ memcpy(to->h_aliases[na], *ap, len);
+ }
+ to->h_aliases[na] = 0;
+ }
+
+ /* Count the addresses, then allocate storage for the pointers */
+ for (na = 1, ap = from->h_addr_list; *ap != 0; na++, ap++) {;} /* nothing to execute */
+ to->h_addr_list = (char**)Alloc(
+ na * sizeof(char*), buf, bufsize, sizeof(char**));
+ if (!to->h_addr_list) {
+ return PR_FAILURE;
+ }
+
+ /* Copy the addresses, one at a time */
+ for (na = 0, ap = from->h_addr_list; *ap != 0; na++, ap++) {
+ to->h_addr_list[na] = Alloc(to->h_length, buf, bufsize, 0);
+ if (!to->h_addr_list[na]) {
+ return PR_FAILURE;
+ }
+ if (conversion != _PRIPAddrNoConversion
+ && from->h_addrtype == AF_INET) {
+ if (conversion == _PRIPAddrIPv4Mapped) {
+ MakeIPv4MappedAddr(*ap, to->h_addr_list[na]);
+ } else {
+ PR_ASSERT(conversion == _PRIPAddrIPv4Compat);
+ MakeIPv4CompatAddr(*ap, to->h_addr_list[na]);
+ }
+ } else {
+ memcpy(to->h_addr_list[na], *ap, to->h_length);
+ }
+ }
+ to->h_addr_list[na] = 0;
+ return PR_SUCCESS;
+}
+
+#if !defined(_PR_HAVE_GETPROTO_R)
+/*
+** Copy a protoent, and all of the memory that it refers to into
+** (hopefully) stacked buffers.
+*/
+static PRStatus CopyProtoent(
+ struct protoent *from, char *buf, PRIntn bufsize, PRProtoEnt *to)
+{
+ PRIntn len, na;
+ char **ap;
+
+ /* Do the easy stuff */
+ to->p_num = from->p_proto;
+
+ /* Copy the official name */
+ if (!from->p_name) {
+ return PR_FAILURE;
+ }
+ len = strlen(from->p_name) + 1;
+ to->p_name = Alloc(len, &buf, &bufsize, 0);
+ if (!to->p_name) {
+ return PR_FAILURE;
+ }
+ memcpy(to->p_name, from->p_name, len);
+
+ /* Count the aliases, then allocate storage for the pointers */
+ for (na = 1, ap = from->p_aliases; *ap != 0; na++, ap++) {;} /* nothing to execute */
+ to->p_aliases = (char**)Alloc(
+ na * sizeof(char*), &buf, &bufsize, sizeof(char**));
+ if (!to->p_aliases) {
+ return PR_FAILURE;
+ }
+
+ /* Copy the aliases, one at a time */
+ for (na = 0, ap = from->p_aliases; *ap != 0; na++, ap++) {
+ len = strlen(*ap) + 1;
+ to->p_aliases[na] = Alloc(len, &buf, &bufsize, 0);
+ if (!to->p_aliases[na]) {
+ return PR_FAILURE;
+ }
+ memcpy(to->p_aliases[na], *ap, len);
+ }
+ to->p_aliases[na] = 0;
+
+ return PR_SUCCESS;
+}
+#endif /* !defined(_PR_HAVE_GETPROTO_R) */
+
+/*
+ * #################################################################
+ * NOTE: tmphe, tmpbuf, bufsize, h, and h_err are local variables
+ * or arguments of PR_GetHostByName, PR_GetIPNodeByName, and
+ * PR_GetHostByAddr. DO NOT CHANGE THE NAMES OF THESE LOCAL
+ * VARIABLES OR ARGUMENTS.
+ * #################################################################
+ */
+#if defined(_PR_HAVE_GETHOST_R_INT)
+
+#define GETHOSTBYNAME(name) \
+ (gethostbyname_r(name, &tmphe, tmpbuf, bufsize, &h, &h_err), h)
+#define GETHOSTBYNAME2(name, af) \
+ (gethostbyname2_r(name, af, &tmphe, tmpbuf, bufsize, &h, &h_err), h)
+#define GETHOSTBYADDR(addr, addrlen, af) \
+ (gethostbyaddr_r(addr, addrlen, af, \
+ &tmphe, tmpbuf, bufsize, &h, &h_err), h)
+
+#elif defined(_PR_HAVE_GETHOST_R_POINTER)
+
+#define GETHOSTBYNAME(name) \
+ gethostbyname_r(name, &tmphe, tmpbuf, bufsize, &h_err)
+#define GETHOSTBYNAME2(name, af) \
+ gethostbyname2_r(name, af, &tmphe, tmpbuf, bufsize, &h_err)
+#define GETHOSTBYADDR(addr, addrlen, af) \
+ gethostbyaddr_r(addr, addrlen, af, &tmphe, tmpbuf, bufsize, &h_err)
+
+#else
+
+#define GETHOSTBYNAME(name) gethostbyname(name)
+#define GETHOSTBYNAME2(name, af) gethostbyname2(name, af)
+#define GETHOSTBYADDR(addr, addrlen, af) gethostbyaddr(addr, addrlen, af)
+
+#endif /* definition of GETHOSTBYXXX */
+
+PR_IMPLEMENT(PRStatus) PR_GetHostByName(
+ const char *name, char *buf, PRIntn bufsize, PRHostEnt *hp)
+{
+ struct hostent *h;
+ PRStatus rv = PR_FAILURE;
+#if defined(_PR_HAVE_GETHOST_R)
+ char localbuf[PR_NETDB_BUF_SIZE];
+ char *tmpbuf;
+ struct hostent tmphe;
+ int h_err;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+#if defined(_PR_HAVE_GETHOST_R)
+ tmpbuf = localbuf;
+ if (bufsize > sizeof(localbuf))
+ {
+ tmpbuf = (char *)PR_Malloc(bufsize);
+ if (NULL == tmpbuf)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return rv;
+ }
+ }
+#endif
+
+ LOCK_DNS();
+
+ h = GETHOSTBYNAME(name);
+
+ if (NULL == h)
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO());
+ }
+ else
+ {
+ _PRIPAddrConversion conversion = _PRIPAddrNoConversion;
+ rv = CopyHostent(h, &buf, &bufsize, conversion, hp);
+ if (PR_SUCCESS != rv) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+ }
+ UNLOCK_DNS();
+#if defined(_PR_HAVE_GETHOST_R)
+ if (tmpbuf != localbuf) {
+ PR_Free(tmpbuf);
+ }
+#endif
+ return rv;
+}
+
+#if !defined(_PR_INET6) && \
+ defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME)
+typedef struct hostent * (*_pr_getipnodebyname_t)(const char *, int,
+ int, int *);
+typedef struct hostent * (*_pr_getipnodebyaddr_t)(const void *, size_t,
+ int, int *);
+typedef void (*_pr_freehostent_t)(struct hostent *);
+static void * _pr_getipnodebyname_fp;
+static void * _pr_getipnodebyaddr_fp;
+static void * _pr_freehostent_fp;
+
+/*
+ * Look up the addresses of getipnodebyname, getipnodebyaddr,
+ * and freehostent.
+ */
+PRStatus
+_pr_find_getipnodebyname(void)
+{
+ PRLibrary *lib;
+ PRStatus rv;
+#define GETIPNODEBYNAME "getipnodebyname"
+#define GETIPNODEBYADDR "getipnodebyaddr"
+#define FREEHOSTENT "freehostent"
+
+ _pr_getipnodebyname_fp = PR_FindSymbolAndLibrary(GETIPNODEBYNAME, &lib);
+ if (NULL != _pr_getipnodebyname_fp) {
+ _pr_freehostent_fp = PR_FindSymbol(lib, FREEHOSTENT);
+ if (NULL != _pr_freehostent_fp) {
+ _pr_getipnodebyaddr_fp = PR_FindSymbol(lib, GETIPNODEBYADDR);
+ if (NULL != _pr_getipnodebyaddr_fp) {
+ rv = PR_SUCCESS;
+ }
+ else {
+ rv = PR_FAILURE;
+ }
+ } else {
+ rv = PR_FAILURE;
+ }
+ (void)PR_UnloadLibrary(lib);
+ } else {
+ rv = PR_FAILURE;
+ }
+ return rv;
+}
+#endif
+
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2)
+/*
+** Append the V4 addresses to the end of the list
+*/
+static PRStatus AppendV4AddrsToHostent(
+ struct hostent *from,
+ char **buf,
+ PRIntn *bufsize,
+ PRHostEnt *to)
+{
+ PRIntn na, na_old;
+ char **ap;
+ char **new_addr_list;
+
+ /* Count the addresses, then grow storage for the pointers */
+ for (na_old = 0, ap = to->h_addr_list; *ap != 0; na_old++, ap++)
+ {;} /* nothing to execute */
+ for (na = na_old + 1, ap = from->h_addr_list; *ap != 0; na++, ap++)
+ {;} /* nothing to execute */
+ new_addr_list = (char**)Alloc(
+ na * sizeof(char*), buf, bufsize, sizeof(char**));
+ if (!new_addr_list) {
+ return PR_FAILURE;
+ }
+
+ /* Copy the V6 addresses, one at a time */
+ for (na = 0, ap = to->h_addr_list; *ap != 0; na++, ap++) {
+ new_addr_list[na] = to->h_addr_list[na];
+ }
+ to->h_addr_list = new_addr_list;
+
+ /* Copy the V4 addresses, one at a time */
+ for (ap = from->h_addr_list; *ap != 0; na++, ap++) {
+ to->h_addr_list[na] = Alloc(to->h_length, buf, bufsize, 0);
+ if (!to->h_addr_list[na]) {
+ return PR_FAILURE;
+ }
+ MakeIPv4MappedAddr(*ap, to->h_addr_list[na]);
+ }
+ to->h_addr_list[na] = 0;
+ return PR_SUCCESS;
+}
+#endif
+
+PR_IMPLEMENT(PRStatus) PR_GetIPNodeByName(
+ const char *name, PRUint16 af, PRIntn flags,
+ char *buf, PRIntn bufsize, PRHostEnt *hp)
+{
+ struct hostent *h = 0;
+ PRStatus rv = PR_FAILURE;
+#if defined(_PR_HAVE_GETHOST_R)
+ char localbuf[PR_NETDB_BUF_SIZE];
+ char *tmpbuf;
+ struct hostent tmphe;
+ int h_err;
+#endif
+#if defined(_PR_HAVE_GETIPNODEBYNAME)
+ PRUint16 md_af = af;
+ int error_num;
+ int tmp_flags = 0;
+#endif
+#if defined(_PR_HAVE_GETHOSTBYNAME2)
+ PRBool did_af_inet = PR_FALSE;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (af != PR_AF_INET && af != PR_AF_INET6) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2)
+ PR_Lock(_pr_query_ifs_lock);
+ /*
+ * Keep querying the presence of IPv4 and IPv6 interfaces until
+ * at least one is up. This allows us to detect the local
+ * machine going from offline to online.
+ */
+ if (!_pr_have_inet_if && !_pr_have_inet6_if) {
+ _pr_QueryNetIfs();
+#ifdef DEBUG_QUERY_IFS
+ if (_pr_have_inet_if) {
+ printf("Have IPv4 source address\n");
+ }
+ if (_pr_have_inet6_if) {
+ printf("Have IPv6 source address\n");
+ }
+#endif
+ }
+ PR_Unlock(_pr_query_ifs_lock);
+#endif
+
+#if defined(_PR_HAVE_GETIPNODEBYNAME)
+ if (flags & PR_AI_V4MAPPED) {
+ tmp_flags |= AI_V4MAPPED;
+ }
+ if (flags & PR_AI_ADDRCONFIG) {
+ tmp_flags |= AI_ADDRCONFIG;
+ }
+ if (flags & PR_AI_ALL) {
+ tmp_flags |= AI_ALL;
+ }
+ if (af == PR_AF_INET6) {
+ md_af = AF_INET6;
+ }
+ else {
+ md_af = af;
+ }
+#endif
+
+#if defined(_PR_HAVE_GETHOST_R)
+ tmpbuf = localbuf;
+ if (bufsize > sizeof(localbuf))
+ {
+ tmpbuf = (char *)PR_Malloc(bufsize);
+ if (NULL == tmpbuf)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return rv;
+ }
+ }
+#endif
+
+ /* Do not need to lock the DNS lock if getipnodebyname() is called */
+#ifdef _PR_INET6
+#ifdef _PR_HAVE_GETHOSTBYNAME2
+ LOCK_DNS();
+ if (af == PR_AF_INET6)
+ {
+ if ((flags & PR_AI_ADDRCONFIG) == 0 || _pr_have_inet6_if)
+ {
+#ifdef _PR_INET6_PROBE
+ if (_pr_ipv6_is_present())
+#endif
+ h = GETHOSTBYNAME2(name, AF_INET6);
+ }
+ if ((NULL == h) && (flags & PR_AI_V4MAPPED)
+ && ((flags & PR_AI_ADDRCONFIG) == 0 || _pr_have_inet_if))
+ {
+ did_af_inet = PR_TRUE;
+ h = GETHOSTBYNAME2(name, AF_INET);
+ }
+ }
+ else
+ {
+ if ((flags & PR_AI_ADDRCONFIG) == 0 || _pr_have_inet_if)
+ {
+ did_af_inet = PR_TRUE;
+ h = GETHOSTBYNAME2(name, af);
+ }
+ }
+#elif defined(_PR_HAVE_GETIPNODEBYNAME)
+ h = getipnodebyname(name, md_af, tmp_flags, &error_num);
+#else
+#error "Unknown name-to-address translation function"
+#endif /* _PR_HAVE_GETHOSTBYNAME2 */
+#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME)
+ if (_pr_ipv6_is_present())
+ {
+#ifdef PR_GETIPNODE_NOT_THREADSAFE
+ LOCK_DNS();
+#endif
+ h = (*((_pr_getipnodebyname_t)_pr_getipnodebyname_fp))(name, md_af, tmp_flags, &error_num);
+ }
+ else
+ {
+ LOCK_DNS();
+ h = GETHOSTBYNAME(name);
+ }
+#else /* _PR_INET6 */
+ LOCK_DNS();
+ h = GETHOSTBYNAME(name);
+#endif /* _PR_INET6 */
+
+ if (NULL == h)
+ {
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME)
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num);
+#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME)
+ if (_pr_ipv6_is_present()) {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num);
+ }
+ else {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO());
+ }
+#else
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO());
+#endif
+ }
+ else
+ {
+ _PRIPAddrConversion conversion = _PRIPAddrNoConversion;
+
+ if (af == PR_AF_INET6) {
+ conversion = _PRIPAddrIPv4Mapped;
+ }
+ rv = CopyHostent(h, &buf, &bufsize, conversion, hp);
+ if (PR_SUCCESS != rv) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME)
+ freehostent(h);
+#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME)
+ if (_pr_ipv6_is_present()) {
+ (*((_pr_freehostent_t)_pr_freehostent_fp))(h);
+ }
+#endif
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2)
+ if ((PR_SUCCESS == rv) && (flags & PR_AI_V4MAPPED)
+ && ((flags & PR_AI_ALL)
+ || ((flags & PR_AI_ADDRCONFIG) && _pr_have_inet_if))
+ && !did_af_inet && (h = GETHOSTBYNAME2(name, AF_INET)) != 0) {
+ rv = AppendV4AddrsToHostent(h, &buf, &bufsize, hp);
+ if (PR_SUCCESS != rv) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+ }
+#endif
+ }
+
+ /* Must match the convoluted logic above for LOCK_DNS() */
+#ifdef _PR_INET6
+#ifdef _PR_HAVE_GETHOSTBYNAME2
+ UNLOCK_DNS();
+#endif /* _PR_HAVE_GETHOSTBYNAME2 */
+#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME)
+#ifdef PR_GETIPNODE_NOT_THREADSAFE
+ UNLOCK_DNS();
+#else
+ if (!_pr_ipv6_is_present()) {
+ UNLOCK_DNS();
+ }
+#endif
+#else /* _PR_INET6 */
+ UNLOCK_DNS();
+#endif /* _PR_INET6 */
+
+#if defined(_PR_HAVE_GETHOST_R)
+ if (tmpbuf != localbuf) {
+ PR_Free(tmpbuf);
+ }
+#endif
+
+ return rv;
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetHostByAddr(
+ const PRNetAddr *hostaddr, char *buf, PRIntn bufsize, PRHostEnt *hostentry)
+{
+ struct hostent *h;
+ PRStatus rv = PR_FAILURE;
+ const void *addr;
+ PRUint32 tmp_ip;
+ int addrlen;
+ PRInt32 af;
+#if defined(_PR_HAVE_GETHOST_R)
+ char localbuf[PR_NETDB_BUF_SIZE];
+ char *tmpbuf;
+ struct hostent tmphe;
+ int h_err;
+#endif
+#if defined(_PR_HAVE_GETIPNODEBYADDR)
+ int error_num;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (hostaddr->raw.family == PR_AF_INET6)
+ {
+#if defined(_PR_INET6_PROBE)
+ af = _pr_ipv6_is_present() ? AF_INET6 : AF_INET;
+#elif defined(_PR_INET6)
+ af = AF_INET6;
+#else
+ af = AF_INET;
+#endif
+#if defined(_PR_GHBA_DISALLOW_V4MAPPED)
+ if (_PR_IN6_IS_ADDR_V4MAPPED(&hostaddr->ipv6.ip)) {
+ af = AF_INET;
+ }
+#endif
+ }
+ else
+ {
+ PR_ASSERT(hostaddr->raw.family == AF_INET);
+ af = AF_INET;
+ }
+ if (hostaddr->raw.family == PR_AF_INET6) {
+#if defined(_PR_INET6) || defined(_PR_INET6_PROBE)
+ if (af == AF_INET6) {
+ addr = &hostaddr->ipv6.ip;
+ addrlen = sizeof(hostaddr->ipv6.ip);
+ }
+ else
+#endif
+ {
+ PR_ASSERT(af == AF_INET);
+ if (!_PR_IN6_IS_ADDR_V4MAPPED(&hostaddr->ipv6.ip)) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return rv;
+ }
+ tmp_ip = _PR_IN6_V4MAPPED_TO_IPADDR((PRIPv6Addr *)
+ &hostaddr->ipv6.ip);
+ addr = &tmp_ip;
+ addrlen = sizeof(tmp_ip);
+ }
+ } else {
+ PR_ASSERT(hostaddr->raw.family == AF_INET);
+ PR_ASSERT(af == AF_INET);
+ addr = &hostaddr->inet.ip;
+ addrlen = sizeof(hostaddr->inet.ip);
+ }
+
+#if defined(_PR_HAVE_GETHOST_R)
+ tmpbuf = localbuf;
+ if (bufsize > sizeof(localbuf))
+ {
+ tmpbuf = (char *)PR_Malloc(bufsize);
+ if (NULL == tmpbuf)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return rv;
+ }
+ }
+#endif
+
+ /* Do not need to lock the DNS lock if getipnodebyaddr() is called */
+#if defined(_PR_HAVE_GETIPNODEBYADDR) && defined(_PR_INET6)
+ h = getipnodebyaddr(addr, addrlen, af, &error_num);
+#elif defined(_PR_HAVE_GETIPNODEBYADDR) && defined(_PR_INET6_PROBE)
+ if (_pr_ipv6_is_present())
+ {
+#ifdef PR_GETIPNODE_NOT_THREADSAFE
+ LOCK_DNS();
+#endif
+ h = (*((_pr_getipnodebyaddr_t)_pr_getipnodebyaddr_fp))(addr, addrlen,
+ af, &error_num);
+ }
+ else
+ {
+ LOCK_DNS();
+ h = GETHOSTBYADDR(addr, addrlen, af);
+ }
+#else /* _PR_HAVE_GETIPNODEBYADDR */
+ LOCK_DNS();
+ h = GETHOSTBYADDR(addr, addrlen, af);
+#endif /* _PR_HAVE_GETIPNODEBYADDR */
+ if (NULL == h)
+ {
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYADDR)
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num);
+#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYADDR)
+ if (_pr_ipv6_is_present()) {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num);
+ }
+ else {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO());
+ }
+#else
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO());
+#endif
+ }
+ else
+ {
+ _PRIPAddrConversion conversion = _PRIPAddrNoConversion;
+ if (hostaddr->raw.family == PR_AF_INET6) {
+ if (af == AF_INET) {
+ if (_PR_IN6_IS_ADDR_V4MAPPED((PRIPv6Addr*)
+ &hostaddr->ipv6.ip)) {
+ conversion = _PRIPAddrIPv4Mapped;
+ } else if (_PR_IN6_IS_ADDR_V4COMPAT((PRIPv6Addr *)
+ &hostaddr->ipv6.ip)) {
+ conversion = _PRIPAddrIPv4Compat;
+ }
+ }
+ }
+ rv = CopyHostent(h, &buf, &bufsize, conversion, hostentry);
+ if (PR_SUCCESS != rv) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYADDR)
+ freehostent(h);
+#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYADDR)
+ if (_pr_ipv6_is_present()) {
+ (*((_pr_freehostent_t)_pr_freehostent_fp))(h);
+ }
+#endif
+ }
+
+ /* Must match the convoluted logic above for LOCK_DNS() */
+#if defined(_PR_HAVE_GETIPNODEBYADDR) && defined(_PR_INET6)
+#elif defined(_PR_HAVE_GETIPNODEBYADDR) && defined(_PR_INET6_PROBE)
+#ifdef PR_GETIPNODE_NOT_THREADSAFE
+ UNLOCK_DNS();
+#else
+ if (!_pr_ipv6_is_present()) {
+ UNLOCK_DNS();
+ }
+#endif
+#else /* _PR_HAVE_GETIPNODEBYADDR */
+ UNLOCK_DNS();
+#endif /* _PR_HAVE_GETIPNODEBYADDR */
+
+#if defined(_PR_HAVE_GETHOST_R)
+ if (tmpbuf != localbuf) {
+ PR_Free(tmpbuf);
+ }
+#endif
+
+ return rv;
+}
+
+/******************************************************************************/
+/*
+ * Some systems define a reentrant version of getprotobyname(). Too bad
+ * the signature isn't always the same. But hey, they tried. If there
+ * is such a definition, use it. Otherwise, grab a lock and do it here.
+ */
+/******************************************************************************/
+
+#if !defined(_PR_HAVE_GETPROTO_R)
+/*
+ * This may seem like a silly thing to do, but the compiler SHOULD
+ * complain if getprotobyname_r() is implemented on some system and
+ * we're not using it. For sure these signatures are different than
+ * any usable implementation.
+ */
+
+#if defined(ANDROID)
+/* Android's Bionic libc system includes prototypes for these in netdb.h,
+ * but doesn't actually include implementations. It uses the 5-arg form,
+ * so these functions end up not matching the prototype. So just rename
+ * them if not found.
+ */
+#define getprotobyname_r _pr_getprotobyname_r
+#define getprotobynumber_r _pr_getprotobynumber_r
+#endif
+
+static struct protoent *getprotobyname_r(const char* name)
+{
+ return getprotobyname(name);
+} /* getprotobyname_r */
+
+static struct protoent *getprotobynumber_r(PRInt32 number)
+{
+ return getprotobynumber(number);
+} /* getprotobynumber_r */
+
+#endif /* !defined(_PR_HAVE_GETPROTO_R) */
+
+PR_IMPLEMENT(PRStatus) PR_GetProtoByName(
+ const char* name, char* buffer, PRInt32 buflen, PRProtoEnt* result)
+{
+ PRStatus rv = PR_SUCCESS;
+#if defined(_PR_HAVE_GETPROTO_R)
+ struct protoent* res = (struct protoent*)result;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+#if defined(_PR_HAVE_GETPROTO_R_INT)
+ {
+ /*
+ ** The protoent_data has a pointer as the first field.
+ ** That implies the buffer better be aligned, and char*
+ ** doesn't promise much.
+ */
+ PRUptrdiff aligned = (PRUptrdiff)buffer;
+ if (0 != (aligned & (sizeof(struct protoent_data*) - 1)))
+ {
+ aligned += sizeof(struct protoent_data*) - 1;
+ aligned &= ~(sizeof(struct protoent_data*) - 1);
+ buflen -= (aligned - (PRUptrdiff)buffer);
+ buffer = (char*)aligned;
+ }
+ }
+#endif /* defined(_PR_HAVE_GETPROTO_R_INT) */
+
+ if (PR_MIN_NETDB_BUF_SIZE > buflen)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+#if defined(_PR_HAVE_GETPROTO_R_POINTER)
+ if (NULL == getprotobyname_r(name, res, buffer, buflen))
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+#elif defined(_PR_HAVE_GETPROTO_R_INT)
+ /*
+ ** The buffer needs to be zero'd, and it should be
+ ** at least the size of a struct protoent_data.
+ */
+ memset(buffer, 0, buflen);
+ if (-1 == getprotobyname_r(name, res, (struct protoent_data*)buffer))
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+#elif defined(_PR_HAVE_5_ARG_GETPROTO_R)
+ /* The 5th argument for getprotobyname_r() cannot be NULL */
+ if (-1 == getprotobyname_r(name, res, buffer, buflen, &res))
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+#else /* do it the hard way */
+ {
+ struct protoent *staticBuf;
+ PR_Lock(_getproto_lock);
+ staticBuf = getprotobyname_r(name);
+ if (NULL == staticBuf)
+ {
+ rv = PR_FAILURE;
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ }
+ else
+ {
+ rv = CopyProtoent(staticBuf, buffer, buflen, result);
+ if (PR_FAILURE == rv) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+ }
+ PR_Unlock(_getproto_lock);
+ }
+#endif /* all that */
+ return rv;
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetProtoByNumber(
+ PRInt32 number, char* buffer, PRInt32 buflen, PRProtoEnt* result)
+{
+ PRStatus rv = PR_SUCCESS;
+#if defined(_PR_HAVE_GETPROTO_R)
+ struct protoent* res = (struct protoent*)result;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+#if defined(_PR_HAVE_GETPROTO_R_INT)
+ {
+ /*
+ ** The protoent_data has a pointer as the first field.
+ ** That implies the buffer better be aligned, and char*
+ ** doesn't promise much.
+ */
+ PRUptrdiff aligned = (PRUptrdiff)buffer;
+ if (0 != (aligned & (sizeof(struct protoent_data*) - 1)))
+ {
+ aligned += sizeof(struct protoent_data*) - 1;
+ aligned &= ~(sizeof(struct protoent_data*) - 1);
+ buflen -= (aligned - (PRUptrdiff)buffer);
+ buffer = (char*)aligned;
+ }
+ }
+#endif /* defined(_PR_HAVE_GETPROTO_R_INT) */
+
+ if (PR_MIN_NETDB_BUF_SIZE > buflen)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+#if defined(_PR_HAVE_GETPROTO_R_POINTER)
+ if (NULL == getprotobynumber_r(number, res, buffer, buflen))
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+
+#elif defined(_PR_HAVE_GETPROTO_R_INT)
+ /*
+ ** The buffer needs to be zero'd for these OS's.
+ */
+ memset(buffer, 0, buflen);
+ if (-1 == getprotobynumber_r(number, res, (struct protoent_data*)buffer))
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+#elif defined(_PR_HAVE_5_ARG_GETPROTO_R)
+ /* The 5th argument for getprotobynumber_r() cannot be NULL */
+ if (-1 == getprotobynumber_r(number, res, buffer, buflen, &res))
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+#else /* do it the hard way */
+ {
+ struct protoent *staticBuf;
+ PR_Lock(_getproto_lock);
+ staticBuf = getprotobynumber_r(number);
+ if (NULL == staticBuf)
+ {
+ rv = PR_FAILURE;
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ }
+ else
+ {
+ rv = CopyProtoent(staticBuf, buffer, buflen, result);
+ if (PR_FAILURE == rv) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+ }
+ PR_Unlock(_getproto_lock);
+ }
+#endif /* all that crap */
+ return rv;
+
+}
+
+PRUintn _PR_NetAddrSize(const PRNetAddr* addr)
+{
+ PRUintn addrsize;
+
+ /*
+ * RFC 2553 added a new field (sin6_scope_id) to
+ * struct sockaddr_in6. PRNetAddr's ipv6 member has a
+ * scope_id field to match the new field. In order to
+ * work with older implementations supporting RFC 2133,
+ * we take the size of struct sockaddr_in6 instead of
+ * addr->ipv6.
+ */
+ if (AF_INET == addr->raw.family) {
+ addrsize = sizeof(addr->inet);
+ }
+ else if (PR_AF_INET6 == addr->raw.family)
+#if defined(_PR_INET6)
+ addrsize = sizeof(struct sockaddr_in6);
+#else
+ addrsize = sizeof(addr->ipv6);
+#endif
+#if defined(XP_UNIX) || defined(XP_OS2)
+ else if (AF_UNIX == addr->raw.family)
+ {
+#if defined(LINUX)
+ if (addr->local.path[0] == 0)
+ /* abstract socket address is supported on Linux only */
+ addrsize = strnlen(addr->local.path + 1,
+ sizeof(addr->local.path)) +
+ offsetof(struct sockaddr_un, sun_path) + 1;
+ else
+#endif
+ addrsize = sizeof(addr->local);
+ }
+#endif
+ else {
+ addrsize = 0;
+ }
+
+ return addrsize;
+} /* _PR_NetAddrSize */
+
+PR_IMPLEMENT(PRIntn) PR_EnumerateHostEnt(
+ PRIntn enumIndex, const PRHostEnt *hostEnt, PRUint16 port, PRNetAddr *address)
+{
+ void *addr = hostEnt->h_addr_list[enumIndex++];
+ memset(address, 0, sizeof(PRNetAddr));
+ if (NULL == addr) {
+ enumIndex = 0;
+ }
+ else
+ {
+ address->raw.family = hostEnt->h_addrtype;
+ if (PR_AF_INET6 == hostEnt->h_addrtype)
+ {
+ address->ipv6.port = htons(port);
+ address->ipv6.flowinfo = 0;
+ address->ipv6.scope_id = 0;
+ memcpy(&address->ipv6.ip, addr, hostEnt->h_length);
+ }
+ else
+ {
+ PR_ASSERT(AF_INET == hostEnt->h_addrtype);
+ address->inet.port = htons(port);
+ memcpy(&address->inet.ip, addr, hostEnt->h_length);
+ }
+ }
+ return enumIndex;
+} /* PR_EnumerateHostEnt */
+
+PR_IMPLEMENT(PRStatus) PR_InitializeNetAddr(
+ PRNetAddrValue val, PRUint16 port, PRNetAddr *addr)
+{
+ PRStatus rv = PR_SUCCESS;
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (val != PR_IpAddrNull) {
+ memset(addr, 0, sizeof(*addr));
+ }
+ addr->inet.family = AF_INET;
+ addr->inet.port = htons(port);
+ switch (val)
+ {
+ case PR_IpAddrNull:
+ break; /* don't overwrite the address */
+ case PR_IpAddrAny:
+ addr->inet.ip = htonl(INADDR_ANY);
+ break;
+ case PR_IpAddrLoopback:
+ addr->inet.ip = htonl(INADDR_LOOPBACK);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ return rv;
+} /* PR_InitializeNetAddr */
+
+PR_IMPLEMENT(PRStatus) PR_SetNetAddr(
+ PRNetAddrValue val, PRUint16 af, PRUint16 port, PRNetAddr *addr)
+{
+ PRStatus rv = PR_SUCCESS;
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (af == PR_AF_INET6)
+ {
+ if (val != PR_IpAddrNull) {
+ memset(addr, 0, sizeof(addr->ipv6));
+ }
+ addr->ipv6.family = af;
+ addr->ipv6.port = htons(port);
+ addr->ipv6.flowinfo = 0;
+ addr->ipv6.scope_id = 0;
+ switch (val)
+ {
+ case PR_IpAddrNull:
+ break; /* don't overwrite the address */
+ case PR_IpAddrAny:
+ addr->ipv6.ip = _pr_in6addr_any;
+ break;
+ case PR_IpAddrLoopback:
+ addr->ipv6.ip = _pr_in6addr_loopback;
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ }
+ else
+ {
+ if (val != PR_IpAddrNull) {
+ memset(addr, 0, sizeof(addr->inet));
+ }
+ addr->inet.family = af;
+ addr->inet.port = htons(port);
+ switch (val)
+ {
+ case PR_IpAddrNull:
+ break; /* don't overwrite the address */
+ case PR_IpAddrAny:
+ addr->inet.ip = htonl(INADDR_ANY);
+ break;
+ case PR_IpAddrLoopback:
+ addr->inet.ip = htonl(INADDR_LOOPBACK);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ }
+ return rv;
+} /* PR_SetNetAddr */
+
+PR_IMPLEMENT(PRBool)
+PR_IsNetAddrType(const PRNetAddr *addr, PRNetAddrValue val)
+{
+ if (addr->raw.family == PR_AF_INET6) {
+ if (val == PR_IpAddrAny) {
+ if (_PR_IN6_IS_ADDR_UNSPECIFIED((PRIPv6Addr *)&addr->ipv6.ip)) {
+ return PR_TRUE;
+ }
+ if (_PR_IN6_IS_ADDR_V4MAPPED((PRIPv6Addr *)&addr->ipv6.ip)
+ && _PR_IN6_V4MAPPED_TO_IPADDR((PRIPv6Addr *)&addr->ipv6.ip)
+ == htonl(INADDR_ANY)) {
+ return PR_TRUE;
+ }
+ } else if (val == PR_IpAddrLoopback) {
+ if (_PR_IN6_IS_ADDR_LOOPBACK((PRIPv6Addr *)&addr->ipv6.ip)) {
+ return PR_TRUE;
+ }
+ if (_PR_IN6_IS_ADDR_V4MAPPED((PRIPv6Addr *)&addr->ipv6.ip)
+ && _PR_IN6_V4MAPPED_TO_IPADDR((PRIPv6Addr *)&addr->ipv6.ip)
+ == htonl(INADDR_LOOPBACK)) {
+ return PR_TRUE;
+ }
+ } else if (val == PR_IpAddrV4Mapped
+ && _PR_IN6_IS_ADDR_V4MAPPED((PRIPv6Addr *)&addr->ipv6.ip)) {
+ return PR_TRUE;
+ }
+ } else {
+ if (addr->raw.family == AF_INET) {
+ if (val == PR_IpAddrAny && addr->inet.ip == htonl(INADDR_ANY)) {
+ return PR_TRUE;
+ }
+ if (val == PR_IpAddrLoopback
+ && addr->inet.ip == htonl(INADDR_LOOPBACK)) {
+ return PR_TRUE;
+ }
+ }
+ }
+ return PR_FALSE;
+}
+
+extern int pr_inet_aton(const char *cp, PRUint32 *addr);
+
+#define XX 127
+static const unsigned char index_hex[256] = {
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX,
+ XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+};
+
+/*
+ * StringToV6Addr() returns 1 if the conversion succeeds,
+ * or 0 if the input is not a valid IPv6 address string.
+ * (Same as inet_pton(AF_INET6, string, addr).)
+ */
+static int StringToV6Addr(const char *string, PRIPv6Addr *addr)
+{
+ const unsigned char *s = (const unsigned char *)string;
+ int section = 0; /* index of the current section (a 16-bit
+ * piece of the address */
+ int double_colon = -1; /* index of the section after the first
+ * 16-bit group of zeros represented by
+ * the double colon */
+ unsigned int val;
+ int len;
+
+ /* Handle initial (double) colon */
+ if (*s == ':') {
+ if (s[1] != ':') {
+ return 0;
+ }
+ s += 2;
+ addr->pr_s6_addr16[0] = 0;
+ section = double_colon = 1;
+ }
+
+ while (*s) {
+ if (section == 8) {
+ return 0; /* too long */
+ }
+ if (*s == ':') {
+ if (double_colon != -1) {
+ return 0; /* two double colons */
+ }
+ addr->pr_s6_addr16[section++] = 0;
+ double_colon = section;
+ s++;
+ continue;
+ }
+ for (len = val = 0; len < 4 && index_hex[*s] != XX; len++) {
+ val = (val << 4) + index_hex[*s++];
+ }
+ if (*s == '.') {
+ if (len == 0) {
+ return 0; /* nothing between : and . */
+ }
+ break;
+ }
+ if (*s == ':') {
+ s++;
+ if (!*s) {
+ return 0; /* cannot end with single colon */
+ }
+ } else if (*s) {
+ return 0; /* bad character */
+ }
+ addr->pr_s6_addr16[section++] = htons((unsigned short)val);
+ }
+
+ if (*s == '.') {
+ /* Have a trailing v4 format address */
+ if (section > 6) {
+ return 0; /* not enough room */
+ }
+
+ /*
+ * The number before the '.' is decimal, but we parsed it
+ * as hex. That means it is in BCD. Check it for validity
+ * and convert it to binary.
+ */
+ if (val > 0x0255 || (val & 0xf0) > 0x90 || (val & 0xf) > 9) {
+ return 0;
+ }
+ val = (val >> 8) * 100 + ((val >> 4) & 0xf) * 10 + (val & 0xf);
+ addr->pr_s6_addr[2 * section] = val;
+
+ s++;
+ val = index_hex[*s++];
+ if (val > 9) {
+ return 0;
+ }
+ while (*s >= '0' && *s <= '9') {
+ val = val * 10 + *s++ - '0';
+ if (val > 255) {
+ return 0;
+ }
+ }
+ if (*s != '.') {
+ return 0; /* must have exactly 4 decimal numbers */
+ }
+ addr->pr_s6_addr[2 * section + 1] = val;
+ section++;
+
+ s++;
+ val = index_hex[*s++];
+ if (val > 9) {
+ return 0;
+ }
+ while (*s >= '0' && *s <= '9') {
+ val = val * 10 + *s++ - '0';
+ if (val > 255) {
+ return 0;
+ }
+ }
+ if (*s != '.') {
+ return 0; /* must have exactly 4 decimal numbers */
+ }
+ addr->pr_s6_addr[2 * section] = val;
+
+ s++;
+ val = index_hex[*s++];
+ if (val > 9) {
+ return 0;
+ }
+ while (*s >= '0' && *s <= '9') {
+ val = val * 10 + *s++ - '0';
+ if (val > 255) {
+ return 0;
+ }
+ }
+ if (*s) {
+ return 0; /* must have exactly 4 decimal numbers */
+ }
+ addr->pr_s6_addr[2 * section + 1] = val;
+ section++;
+ }
+
+ if (double_colon != -1) {
+ /* Stretch the double colon */
+ int tosection;
+ int ncopy = section - double_colon;
+ for (tosection = 7; ncopy--; tosection--) {
+ addr->pr_s6_addr16[tosection] =
+ addr->pr_s6_addr16[double_colon + ncopy];
+ }
+ while (tosection >= double_colon) {
+ addr->pr_s6_addr16[tosection--] = 0;
+ }
+ } else if (section != 8) {
+ return 0; /* too short */
+ }
+ return 1;
+}
+#undef XX
+
+#ifndef _PR_HAVE_INET_NTOP
+static const char *basis_hex = "0123456789abcdef";
+
+/*
+ * V6AddrToString() returns a pointer to the buffer containing
+ * the text string if the conversion succeeds, and NULL otherwise.
+ * (Same as inet_ntop(AF_INET6, addr, buf, size), except that errno
+ * is not set on failure.)
+ */
+static const char *V6AddrToString(
+ const PRIPv6Addr *addr, char *buf, PRUint32 size)
+{
+#define STUFF(c) do { \
+ if (!size--) return NULL; \
+ *buf++ = (c); \
+} while (0)
+
+ int double_colon = -1; /* index of the first 16-bit
+ * group of zeros represented
+ * by the double colon */
+ int double_colon_length = 1; /* use double colon only if
+ * there are two or more 16-bit
+ * groups of zeros */
+ int zero_length;
+ int section;
+ unsigned int val;
+ const char *bufcopy = buf;
+
+ /* Scan to find the placement of the double colon */
+ for (section = 0; section < 8; section++) {
+ if (addr->pr_s6_addr16[section] == 0) {
+ zero_length = 1;
+ section++;
+ while (section < 8 && addr->pr_s6_addr16[section] == 0) {
+ zero_length++;
+ section++;
+ }
+ /* Select the longest sequence of zeros */
+ if (zero_length > double_colon_length) {
+ double_colon = section - zero_length;
+ double_colon_length = zero_length;
+ }
+ }
+ }
+
+ /* Now start converting to a string */
+ section = 0;
+
+ if (double_colon == 0) {
+ if (double_colon_length == 6 ||
+ (double_colon_length == 5 && addr->pr_s6_addr16[5] == 0xffff)) {
+ /* ipv4 format address */
+ STUFF(':');
+ STUFF(':');
+ if (double_colon_length == 5) {
+ STUFF('f');
+ STUFF('f');
+ STUFF('f');
+ STUFF('f');
+ STUFF(':');
+ }
+ if (addr->pr_s6_addr[12] > 99) {
+ STUFF(addr->pr_s6_addr[12]/100 + '0');
+ }
+ if (addr->pr_s6_addr[12] > 9) {
+ STUFF((addr->pr_s6_addr[12]%100)/10 + '0');
+ }
+ STUFF(addr->pr_s6_addr[12]%10 + '0');
+ STUFF('.');
+ if (addr->pr_s6_addr[13] > 99) {
+ STUFF(addr->pr_s6_addr[13]/100 + '0');
+ }
+ if (addr->pr_s6_addr[13] > 9) {
+ STUFF((addr->pr_s6_addr[13]%100)/10 + '0');
+ }
+ STUFF(addr->pr_s6_addr[13]%10 + '0');
+ STUFF('.');
+ if (addr->pr_s6_addr[14] > 99) {
+ STUFF(addr->pr_s6_addr[14]/100 + '0');
+ }
+ if (addr->pr_s6_addr[14] > 9) {
+ STUFF((addr->pr_s6_addr[14]%100)/10 + '0');
+ }
+ STUFF(addr->pr_s6_addr[14]%10 + '0');
+ STUFF('.');
+ if (addr->pr_s6_addr[15] > 99) {
+ STUFF(addr->pr_s6_addr[15]/100 + '0');
+ }
+ if (addr->pr_s6_addr[15] > 9) {
+ STUFF((addr->pr_s6_addr[15]%100)/10 + '0');
+ }
+ STUFF(addr->pr_s6_addr[15]%10 + '0');
+ STUFF('\0');
+ return bufcopy;
+ }
+ }
+
+ while (section < 8) {
+ if (section == double_colon) {
+ STUFF(':');
+ STUFF(':');
+ section += double_colon_length;
+ continue;
+ }
+ val = ntohs(addr->pr_s6_addr16[section]);
+ if (val > 0xfff) {
+ STUFF(basis_hex[val >> 12]);
+ }
+ if (val > 0xff) {
+ STUFF(basis_hex[(val >> 8) & 0xf]);
+ }
+ if (val > 0xf) {
+ STUFF(basis_hex[(val >> 4) & 0xf]);
+ }
+ STUFF(basis_hex[val & 0xf]);
+ section++;
+ if (section < 8 && section != double_colon) {
+ STUFF(':');
+ }
+ }
+ STUFF('\0');
+ return bufcopy;
+#undef STUFF
+}
+#endif /* !_PR_HAVE_INET_NTOP */
+
+/*
+ * Convert an IPv4 addr to an (IPv4-mapped) IPv6 addr
+ */
+PR_IMPLEMENT(void) PR_ConvertIPv4AddrToIPv6(PRUint32 v4addr, PRIPv6Addr *v6addr)
+{
+ PRUint8 *dstp;
+ dstp = v6addr->pr_s6_addr;
+ memset(dstp, 0, 10);
+ memset(dstp + 10, 0xff, 2);
+ memcpy(dstp + 12,(char *) &v4addr, 4);
+}
+
+PR_IMPLEMENT(PRUint16) PR_ntohs(PRUint16 n) {
+ return ntohs(n);
+}
+PR_IMPLEMENT(PRUint32) PR_ntohl(PRUint32 n) {
+ return ntohl(n);
+}
+PR_IMPLEMENT(PRUint16) PR_htons(PRUint16 n) {
+ return htons(n);
+}
+PR_IMPLEMENT(PRUint32) PR_htonl(PRUint32 n) {
+ return htonl(n);
+}
+PR_IMPLEMENT(PRUint64) PR_ntohll(PRUint64 n)
+{
+#ifdef IS_BIG_ENDIAN
+ return n;
+#else
+ PRUint32 hi, lo;
+ lo = (PRUint32)n;
+ hi = (PRUint32)(n >> 32);
+ hi = PR_ntohl(hi);
+ lo = PR_ntohl(lo);
+ return ((PRUint64)lo << 32) + (PRUint64)hi;
+#endif
+} /* ntohll */
+
+PR_IMPLEMENT(PRUint64) PR_htonll(PRUint64 n)
+{
+#ifdef IS_BIG_ENDIAN
+ return n;
+#else
+ PRUint32 hi, lo;
+ lo = (PRUint32)n;
+ hi = (PRUint32)(n >> 32);
+ hi = htonl(hi);
+ lo = htonl(lo);
+ return ((PRUint64)lo << 32) + (PRUint64)hi;
+#endif
+} /* htonll */
+
+
+/*
+ * Implementation of PR_GetAddrInfoByName and friends
+ *
+ * Compile-time options:
+ *
+ * _PR_HAVE_GETADDRINFO Define this macro if the target system provides
+ * getaddrinfo. With this defined, NSPR will require
+ * getaddrinfo at run time. If this if not defined,
+ * then NSPR will attempt to dynamically resolve
+ * getaddrinfo, falling back to PR_GetHostByName if
+ * getaddrinfo does not exist on the target system.
+ *
+ * Since getaddrinfo is a relatively new system call on many systems,
+ * we are forced to dynamically resolve it at run time in most cases.
+ * The exception includes any system (such as Mac OS X) that is known to
+ * provide getaddrinfo in all versions that NSPR cares to support.
+ */
+
+#if defined(_PR_HAVE_GETADDRINFO)
+
+#if defined(_PR_INET6)
+
+typedef struct addrinfo PRADDRINFO;
+#define GETADDRINFO getaddrinfo
+#define FREEADDRINFO freeaddrinfo
+#define GETNAMEINFO getnameinfo
+
+#elif defined(_PR_INET6_PROBE)
+
+typedef struct addrinfo PRADDRINFO;
+
+/* getaddrinfo/freeaddrinfo/getnameinfo prototypes */
+#if defined(WIN32)
+#define FUNC_MODIFIER __stdcall
+#else
+#define FUNC_MODIFIER
+#endif
+typedef int (FUNC_MODIFIER * FN_GETADDRINFO)
+(const char *nodename,
+ const char *servname,
+ const PRADDRINFO *hints,
+ PRADDRINFO **res);
+typedef int (FUNC_MODIFIER * FN_FREEADDRINFO)
+(PRADDRINFO *ai);
+typedef int (FUNC_MODIFIER * FN_GETNAMEINFO)
+(const struct sockaddr *addr, int addrlen,
+ char *host, int hostlen,
+ char *serv, int servlen, int flags);
+
+/* global state */
+static FN_GETADDRINFO _pr_getaddrinfo = NULL;
+static FN_FREEADDRINFO _pr_freeaddrinfo = NULL;
+static FN_GETNAMEINFO _pr_getnameinfo = NULL;
+
+#define GETADDRINFO_SYMBOL "getaddrinfo"
+#define FREEADDRINFO_SYMBOL "freeaddrinfo"
+#define GETNAMEINFO_SYMBOL "getnameinfo"
+
+PRStatus
+_pr_find_getaddrinfo(void)
+{
+ PRLibrary *lib;
+#ifdef WIN32
+ /*
+ * On windows, we need to search ws2_32.dll or wship6.dll
+ * (Microsoft IPv6 Technology Preview for Windows 2000) for
+ * getaddrinfo and freeaddrinfo. These libraries might not
+ * be loaded yet.
+ */
+ const char *libname[] = { "ws2_32.dll", "wship6.dll" };
+ int i;
+
+ for (i = 0; i < sizeof(libname)/sizeof(libname[0]); i++) {
+ lib = PR_LoadLibrary(libname[i]);
+ if (!lib) {
+ continue;
+ }
+ _pr_getaddrinfo = (FN_GETADDRINFO)
+ PR_FindFunctionSymbol(lib, GETADDRINFO_SYMBOL);
+ if (!_pr_getaddrinfo) {
+ PR_UnloadLibrary(lib);
+ continue;
+ }
+ _pr_freeaddrinfo = (FN_FREEADDRINFO)
+ PR_FindFunctionSymbol(lib, FREEADDRINFO_SYMBOL);
+ _pr_getnameinfo = (FN_GETNAMEINFO)
+ PR_FindFunctionSymbol(lib, GETNAMEINFO_SYMBOL);
+ if (!_pr_freeaddrinfo || !_pr_getnameinfo) {
+ PR_UnloadLibrary(lib);
+ continue;
+ }
+ /* Keep the library loaded. */
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+#else
+ /*
+ * Resolve getaddrinfo by searching all loaded libraries. Then
+ * search library containing getaddrinfo for freeaddrinfo.
+ */
+ _pr_getaddrinfo = (FN_GETADDRINFO)
+ PR_FindFunctionSymbolAndLibrary(GETADDRINFO_SYMBOL, &lib);
+ if (!_pr_getaddrinfo) {
+ return PR_FAILURE;
+ }
+ _pr_freeaddrinfo = (FN_FREEADDRINFO)
+ PR_FindFunctionSymbol(lib, FREEADDRINFO_SYMBOL);
+ _pr_getnameinfo = (FN_GETNAMEINFO)
+ PR_FindFunctionSymbol(lib, GETNAMEINFO_SYMBOL);
+ PR_UnloadLibrary(lib);
+ if (!_pr_freeaddrinfo || !_pr_getnameinfo) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+#endif
+}
+
+#define GETADDRINFO (*_pr_getaddrinfo)
+#define FREEADDRINFO (*_pr_freeaddrinfo)
+#define GETNAMEINFO (*_pr_getnameinfo)
+
+#endif /* _PR_INET6 */
+
+#endif /* _PR_HAVE_GETADDRINFO */
+
+#if !defined(_PR_HAVE_GETADDRINFO) || defined(_PR_INET6_PROBE)
+/*
+ * If getaddrinfo does not exist, then we will fall back on
+ * PR_GetHostByName, which requires that we allocate a buffer for the
+ * PRHostEnt data structure and its members.
+ */
+typedef struct PRAddrInfoFB {
+ char buf[PR_NETDB_BUF_SIZE];
+ PRHostEnt hostent;
+ PRBool has_cname;
+} PRAddrInfoFB;
+
+static PRAddrInfo *
+pr_GetAddrInfoByNameFB(const char *hostname,
+ PRUint16 af,
+ PRIntn flags)
+{
+ PRStatus rv;
+ PRAddrInfoFB *ai;
+ /* fallback on PR_GetHostByName */
+ ai = PR_NEW(PRAddrInfoFB);
+ if (!ai) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ rv = PR_GetHostByName(hostname, ai->buf, sizeof ai->buf, &ai->hostent);
+ if (rv == PR_FAILURE) {
+ PR_Free(ai);
+ return NULL;
+ }
+ ai->has_cname = !(flags & PR_AI_NOCANONNAME);
+
+ return (PRAddrInfo *) ai;
+}
+#endif /* !_PR_HAVE_GETADDRINFO || _PR_INET6_PROBE */
+
+PR_IMPLEMENT(PRAddrInfo *) PR_GetAddrInfoByName(const char *hostname,
+ PRUint16 af,
+ PRIntn flags)
+{
+ /* restrict input to supported values */
+ if ((af != PR_AF_INET && af != PR_AF_UNSPEC) ||
+ (flags & ~ PR_AI_NOCANONNAME) != PR_AI_ADDRCONFIG) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+#if !defined(_PR_HAVE_GETADDRINFO)
+ return pr_GetAddrInfoByNameFB(hostname, af, flags);
+#else
+#if defined(_PR_INET6_PROBE)
+ if (!_pr_ipv6_is_present()) {
+ return pr_GetAddrInfoByNameFB(hostname, af, flags);
+ }
+#endif
+ {
+ PRADDRINFO *res, hints;
+ int rv;
+
+ /*
+ * we assume a RFC 2553 compliant getaddrinfo. this may at some
+ * point need to be customized as platforms begin to adopt the
+ * RFC 3493.
+ */
+
+ memset(&hints, 0, sizeof(hints));
+ if (!(flags & PR_AI_NOCANONNAME)) {
+ hints.ai_flags |= AI_CANONNAME;
+ }
+#ifdef AI_ADDRCONFIG
+ /*
+ * Propagate AI_ADDRCONFIG to the GETADDRINFO call if PR_AI_ADDRCONFIG
+ * is set.
+ *
+ * Need a workaround for loopback host addresses:
+ * The problem is that in glibc and Windows, AI_ADDRCONFIG applies the
+ * existence of an outgoing network interface to IP addresses of the
+ * loopback interface, due to a strict interpretation of the
+ * specification. For example, if a computer does not have any
+ * outgoing IPv6 network interface, but its loopback network interface
+ * supports IPv6, a getaddrinfo call on "localhost" with AI_ADDRCONFIG
+ * won't return the IPv6 loopback address "::1", because getaddrinfo
+ * thinks the computer cannot connect to any IPv6 destination,
+ * ignoring the remote vs. local/loopback distinction.
+ */
+ if ((flags & PR_AI_ADDRCONFIG) &&
+ strcmp(hostname, "localhost") != 0 &&
+ strcmp(hostname, "localhost.localdomain") != 0 &&
+ strcmp(hostname, "localhost6") != 0 &&
+ strcmp(hostname, "localhost6.localdomain6") != 0) {
+ hints.ai_flags |= AI_ADDRCONFIG;
+ }
+#endif
+ hints.ai_family = (af == PR_AF_INET) ? AF_INET : AF_UNSPEC;
+
+ /*
+ * it is important to select a socket type in the hints, otherwise we
+ * will get back repetitive entries: one for each socket type. since
+ * we do not expose ai_socktype through our API, it is okay to do this
+ * here. the application may still choose to create a socket of some
+ * other type.
+ */
+ hints.ai_socktype = SOCK_STREAM;
+
+ rv = GETADDRINFO(hostname, NULL, &hints, &res);
+#ifdef AI_ADDRCONFIG
+ if (rv == EAI_BADFLAGS && (hints.ai_flags & AI_ADDRCONFIG)) {
+ hints.ai_flags &= ~AI_ADDRCONFIG;
+ rv = GETADDRINFO(hostname, NULL, &hints, &res);
+ }
+#endif
+ if (rv == 0) {
+ return (PRAddrInfo *) res;
+ }
+
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, rv);
+ }
+ return NULL;
+#endif
+}
+
+PR_IMPLEMENT(PRStatus)
+PR_GetPrefLoopbackAddrInfo(PRNetAddr *result,
+ PRUint16 port)
+{
+ char tmpBuf[ 40 ];
+ const int tmpBufSize = sizeof( tmpBuf );
+
+ if (!result) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ PR_snprintf(tmpBuf, tmpBufSize, "%u", port );
+
+#if !defined(_PR_HAVE_GETADDRINFO) || !defined(AI_PASSIVE)
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+#else
+
+ PRADDRINFO *res, hints;
+ PRStatus rv;
+
+ memset(&hints, 0, sizeof(hints));
+
+ rv = GETADDRINFO(NULL, tmpBuf, &hints, &res);
+ if (rv == 0) {
+ PRBool result_still_empty = PR_TRUE;
+ PRADDRINFO *ai = res;
+ do {
+ PRNetAddr aNetAddr;
+
+ while (ai && ai->ai_addrlen > sizeof(PRNetAddr))
+ ai = ai->ai_next;
+
+ if (ai) {
+ /* copy sockaddr to PRNetAddr */
+ memcpy(&aNetAddr, ai->ai_addr, ai->ai_addrlen);
+ aNetAddr.raw.family = ai->ai_addr->sa_family;
+#ifdef _PR_INET6
+ if (AF_INET6 == aNetAddr.raw.family)
+ aNetAddr.raw.family = PR_AF_INET6;
+#endif
+ if (ai->ai_addrlen < sizeof(PRNetAddr))
+ memset(((char*)result)+ai->ai_addrlen, 0,
+ sizeof(PRNetAddr) - ai->ai_addrlen);
+ }
+
+ /* If we obtain more than one result, prefer IPv6. */
+ if (result_still_empty || aNetAddr.raw.family == PR_AF_INET6) {
+ memcpy(result, &aNetAddr, sizeof(PRNetAddr));
+ }
+ result_still_empty = PR_FALSE;
+ ai = ai->ai_next;
+ }
+ while (ai);
+
+ FREEADDRINFO(res);
+ return PR_SUCCESS;
+ }
+
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, rv);
+ return PR_FAILURE;
+#endif
+}
+
+PR_IMPLEMENT(void) PR_FreeAddrInfo(PRAddrInfo *ai)
+{
+#if defined(_PR_HAVE_GETADDRINFO)
+#if defined(_PR_INET6_PROBE)
+ if (!_pr_ipv6_is_present()) {
+ PR_Free((PRAddrInfoFB *) ai);
+ }
+ else
+#endif
+ FREEADDRINFO((PRADDRINFO *) ai);
+#else
+ PR_Free((PRAddrInfoFB *) ai);
+#endif
+}
+
+PR_IMPLEMENT(void *) PR_EnumerateAddrInfo(void *iterPtr,
+ const PRAddrInfo *base,
+ PRUint16 port,
+ PRNetAddr *result)
+{
+#if defined(_PR_HAVE_GETADDRINFO)
+ PRADDRINFO *ai;
+#if defined(_PR_INET6_PROBE)
+ if (!_pr_ipv6_is_present()) {
+ /* using PRAddrInfoFB */
+ PRIntn iter = (PRIntn)(PRPtrdiff) iterPtr;
+ iter = PR_EnumerateHostEnt(iter, &((PRAddrInfoFB *) base)->hostent, port, result);
+ if (iter < 0) {
+ iter = 0;
+ }
+ return (void *)(PRPtrdiff) iter;
+ }
+#endif
+
+ if (iterPtr) {
+ ai = ((PRADDRINFO *) iterPtr)->ai_next;
+ }
+ else {
+ ai = (PRADDRINFO *) base;
+ }
+
+ while (ai && ai->ai_addrlen > sizeof(PRNetAddr)) {
+ ai = ai->ai_next;
+ }
+
+ if (ai) {
+ /* copy sockaddr to PRNetAddr */
+ memcpy(result, ai->ai_addr, ai->ai_addrlen);
+ result->raw.family = ai->ai_addr->sa_family;
+#ifdef _PR_INET6
+ if (AF_INET6 == result->raw.family) {
+ result->raw.family = PR_AF_INET6;
+ }
+#endif
+ if (ai->ai_addrlen < sizeof(PRNetAddr)) {
+ memset(((char*)result)+ai->ai_addrlen, 0, sizeof(PRNetAddr) - ai->ai_addrlen);
+ }
+
+ if (result->raw.family == PR_AF_INET) {
+ result->inet.port = htons(port);
+ }
+ else {
+ result->ipv6.port = htons(port);
+ }
+ }
+
+ return ai;
+#else
+ /* using PRAddrInfoFB */
+ PRIntn iter = (PRIntn) iterPtr;
+ iter = PR_EnumerateHostEnt(iter, &((PRAddrInfoFB *) base)->hostent, port, result);
+ if (iter < 0) {
+ iter = 0;
+ }
+ return (void *) iter;
+#endif
+}
+
+PR_IMPLEMENT(const char *) PR_GetCanonNameFromAddrInfo(const PRAddrInfo *ai)
+{
+#if defined(_PR_HAVE_GETADDRINFO)
+#if defined(_PR_INET6_PROBE)
+ if (!_pr_ipv6_is_present()) {
+ const PRAddrInfoFB *fb = (const PRAddrInfoFB *) ai;
+ return fb->has_cname ? fb->hostent.h_name : NULL;
+ }
+#endif
+ return ((const PRADDRINFO *) ai)->ai_canonname;
+#else
+ const PRAddrInfoFB *fb = (const PRAddrInfoFB *) ai;
+ return fb->has_cname ? fb->hostent.h_name : NULL;
+#endif
+}
+
+#if defined(_PR_HAVE_GETADDRINFO)
+static PRStatus pr_StringToNetAddrGAI(const char *string, PRNetAddr *addr)
+{
+ PRADDRINFO *res, hints;
+ int rv; /* 0 for success, or the error code EAI_xxx */
+ PRNetAddr laddr;
+ PRStatus status = PR_SUCCESS;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ rv = GETADDRINFO(string, NULL, &hints, &res);
+ if (rv != 0)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, rv);
+ return PR_FAILURE;
+ }
+
+ /* pick up the first addr */
+ memcpy(&laddr, res->ai_addr, res->ai_addrlen);
+ if (AF_INET6 == res->ai_addr->sa_family)
+ {
+ addr->ipv6.family = PR_AF_INET6;
+ addr->ipv6.ip = laddr.ipv6.ip;
+ addr->ipv6.scope_id = laddr.ipv6.scope_id;
+ }
+ else if (AF_INET == res->ai_addr->sa_family)
+ {
+ addr->inet.family = PR_AF_INET;
+ addr->inet.ip = laddr.inet.ip;
+ }
+ else
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ status = PR_FAILURE;
+ }
+
+ FREEADDRINFO(res);
+ return status;
+}
+#endif /* _PR_HAVE_GETADDRINFO */
+
+static PRStatus pr_StringToNetAddrFB(const char *string, PRNetAddr *addr)
+{
+ PRIntn rv;
+
+ rv = pr_inet_aton(string, &addr->inet.ip);
+ if (1 == rv)
+ {
+ addr->raw.family = AF_INET;
+ return PR_SUCCESS;
+ }
+
+ PR_ASSERT(0 == rv);
+ /* clean up after the failed call */
+ memset(&addr->inet.ip, 0, sizeof(addr->inet.ip));
+
+ rv = StringToV6Addr(string, &addr->ipv6.ip);
+ if (1 == rv)
+ {
+ addr->raw.family = PR_AF_INET6;
+ return PR_SUCCESS;
+ }
+
+ PR_ASSERT(0 == rv);
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus) PR_StringToNetAddr(const char *string, PRNetAddr *addr)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (!addr || !string || !*string)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+#if !defined(_PR_HAVE_GETADDRINFO)
+ return pr_StringToNetAddrFB(string, addr);
+#else
+ /*
+ * getaddrinfo with AI_NUMERICHOST is much slower than pr_inet_aton on some
+ * platforms, such as Mac OS X (bug 404399), Linux glibc 2.10 (bug 344809),
+ * and most likely others. So we only use it to convert literal IP addresses
+ * that contain IPv6 scope IDs, which pr_inet_aton cannot convert.
+ */
+ if (!strchr(string, '%')) {
+ return pr_StringToNetAddrFB(string, addr);
+ }
+
+#if defined(_PR_INET6_PROBE)
+ if (!_pr_ipv6_is_present()) {
+ return pr_StringToNetAddrFB(string, addr);
+ }
+#endif
+
+ return pr_StringToNetAddrGAI(string, addr);
+#endif
+}
+
+#if defined(_PR_HAVE_GETADDRINFO)
+static PRStatus pr_NetAddrToStringGNI(
+ const PRNetAddr *addr, char *string, PRUint32 size)
+{
+ int addrlen;
+ const PRNetAddr *addrp = addr;
+#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6)
+ PRUint16 md_af = addr->raw.family;
+ PRNetAddr addrcopy;
+#endif
+ int rv; /* 0 for success, or the error code EAI_xxx */
+
+#ifdef _PR_INET6
+ if (addr->raw.family == PR_AF_INET6)
+ {
+ md_af = AF_INET6;
+#ifndef _PR_HAVE_SOCKADDR_LEN
+ addrcopy = *addr;
+ addrcopy.raw.family = md_af;
+ addrp = &addrcopy;
+#endif
+ }
+#endif
+
+ addrlen = PR_NETADDR_SIZE(addr);
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ addrcopy = *addr;
+ ((struct sockaddr*)&addrcopy)->sa_len = addrlen;
+ ((struct sockaddr*)&addrcopy)->sa_family = md_af;
+ addrp = &addrcopy;
+#endif
+ rv = GETNAMEINFO((const struct sockaddr *)addrp, addrlen,
+ string, size, NULL, 0, NI_NUMERICHOST);
+ if (rv != 0)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, rv);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+#endif /* _PR_HAVE_GETADDRINFO */
+
+#if !defined(_PR_HAVE_GETADDRINFO) || defined(_PR_INET6_PROBE)
+static PRStatus pr_NetAddrToStringFB(
+ const PRNetAddr *addr, char *string, PRUint32 size)
+{
+ if (PR_AF_INET6 == addr->raw.family)
+ {
+#if defined(_PR_HAVE_INET_NTOP)
+ if (NULL == inet_ntop(AF_INET6, &addr->ipv6.ip, string, size))
+#else
+ if (NULL == V6AddrToString(&addr->ipv6.ip, string, size))
+#endif
+ {
+ /* the size of the result buffer is inadequate */
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ return PR_FAILURE;
+ }
+ }
+ else
+ {
+ if (size < 16) {
+ goto failed;
+ }
+ if (AF_INET != addr->raw.family) {
+ goto failed;
+ }
+ else
+ {
+ unsigned char *byte = (unsigned char*)&addr->inet.ip;
+ PR_snprintf(string, size, "%u.%u.%u.%u",
+ byte[0], byte[1], byte[2], byte[3]);
+ }
+ }
+
+ return PR_SUCCESS;
+
+failed:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+
+} /* pr_NetAddrToStringFB */
+#endif /* !_PR_HAVE_GETADDRINFO || _PR_INET6_PROBE */
+
+PR_IMPLEMENT(PRStatus) PR_NetAddrToString(
+ const PRNetAddr *addr, char *string, PRUint32 size)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+#if !defined(_PR_HAVE_GETADDRINFO)
+ return pr_NetAddrToStringFB(addr, string, size);
+#else
+#if defined(_PR_INET6_PROBE)
+ if (!_pr_ipv6_is_present()) {
+ return pr_NetAddrToStringFB(addr, string, size);
+ }
+#endif
+ return pr_NetAddrToStringGNI(addr, string, size);
+#endif
+} /* PR_NetAddrToString */
diff --git a/nsprpub/pr/src/misc/prolock.c b/nsprpub/pr/src/misc/prolock.c
new file mode 100644
index 0000000000..77a26bb2f7
--- /dev/null
+++ b/nsprpub/pr/src/misc/prolock.c
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** prolock.c -- NSPR Ordered Lock
+**
+** Implement the API defined in prolock.h
+**
+*/
+#include "prolock.h"
+#include "prlog.h"
+#include "prerror.h"
+
+PR_IMPLEMENT(PROrderedLock *)
+PR_CreateOrderedLock(
+ PRInt32 order,
+ const char *name
+)
+{
+ PR_NOT_REACHED("Not implemented"); /* Not implemented yet */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+} /* end PR_CreateOrderedLock() */
+
+
+PR_IMPLEMENT(void)
+PR_DestroyOrderedLock(
+ PROrderedLock *lock
+)
+{
+ PR_NOT_REACHED("Not implemented"); /* Not implemented yet */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+} /* end PR_DestroyOrderedLock() */
+
+
+PR_IMPLEMENT(void)
+PR_LockOrderedLock(
+ PROrderedLock *lock
+)
+{
+ PR_NOT_REACHED("Not implemented"); /* Not implemented yet */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+} /* end PR_LockOrderedLock() */
+
+
+PR_IMPLEMENT(PRStatus)
+PR_UnlockOrderedLock(
+ PROrderedLock *lock
+)
+{
+ PR_NOT_REACHED("Not implemented"); /* Not implemented yet */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+} /* end PR_UnlockOrderedLock() */
diff --git a/nsprpub/pr/src/misc/prrng.c b/nsprpub/pr/src/misc/prrng.c
new file mode 100644
index 0000000000..b3e3d4878b
--- /dev/null
+++ b/nsprpub/pr/src/misc/prrng.c
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+/*
+ * We were not including <string.h> in optimized builds. On AIX this
+ * caused libnspr4.so to export memcpy and some binaries linked with
+ * libnspr4.so resolved their memcpy references with libnspr4.so. To
+ * be backward compatible with old libnspr4.so binaries, we do not
+ * include <string.h> in optimized builds for AIX. (bug 200561)
+ */
+#if !(defined(AIX) && !defined(DEBUG))
+#include <string.h>
+#endif
+
+PRSize _pr_CopyLowBits(
+ void *dst,
+ PRSize dstlen,
+ void *src,
+ PRSize srclen )
+{
+ if (srclen <= dstlen) {
+ memcpy(dst, src, srclen);
+ return srclen;
+ }
+#if defined IS_BIG_ENDIAN
+ memcpy(dst, (char*)src + (srclen - dstlen), dstlen);
+#else
+ memcpy(dst, src, dstlen);
+#endif
+ return dstlen;
+}
+
+PR_IMPLEMENT(PRSize) PR_GetRandomNoise(
+ void *buf,
+ PRSize size
+)
+{
+ return( _PR_MD_GET_RANDOM_NOISE( buf, size ));
+} /* end PR_GetRandomNoise() */
+/* end prrng.c */
diff --git a/nsprpub/pr/src/misc/prsystem.c b/nsprpub/pr/src/misc/prsystem.c
new file mode 100644
index 0000000000..dba093e9b1
--- /dev/null
+++ b/nsprpub/pr/src/misc/prsystem.c
@@ -0,0 +1,373 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#include "prsystem.h"
+#include "prprf.h"
+#include "prlong.h"
+
+#if defined(OS2)
+#define INCL_DOS
+#define INCL_DOSMISC
+#include <os2.h>
+/* define the required constant if it is not already defined in the headers */
+#ifndef QSV_NUMPROCESSORS
+#define QSV_NUMPROCESSORS 26
+#endif
+#endif
+
+/* BSD-derived systems use sysctl() to get the number of processors */
+#if defined(BSDI) || defined(FREEBSD) || defined(NETBSD) \
+ || defined(OPENBSD) || defined(DRAGONFLY) || defined(DARWIN)
+#define _PR_HAVE_SYSCTL
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#endif
+
+#if defined(DARWIN)
+#include <mach/mach_init.h>
+#include <mach/mach_host.h>
+#include <mach/mach_port.h>
+#endif
+
+#if defined(HPUX)
+#include <sys/mpctl.h>
+#include <sys/pstat.h>
+#endif
+
+#if defined(XP_UNIX)
+#include <unistd.h>
+#include <sys/utsname.h>
+#endif
+
+#if defined(LINUX)
+#include <string.h>
+#include <ctype.h>
+#define MAX_LINE 512
+#endif
+
+#if defined(AIX)
+#include <cf.h>
+#include <sys/cfgodm.h>
+#endif
+
+PR_IMPLEMENT(char) PR_GetDirectorySeparator(void)
+{
+ return PR_DIRECTORY_SEPARATOR;
+} /* PR_GetDirectorySeparator */
+
+/*
+** OBSOLETE -- the function name is misspelled.
+*/
+PR_IMPLEMENT(char) PR_GetDirectorySepartor(void)
+{
+#if defined(DEBUG)
+ static PRBool warn = PR_TRUE;
+ if (warn) {
+ warn = _PR_Obsolete("PR_GetDirectorySepartor()",
+ "PR_GetDirectorySeparator()");
+ }
+#endif
+ return PR_GetDirectorySeparator();
+} /* PR_GetDirectorySepartor */
+
+PR_IMPLEMENT(char) PR_GetPathSeparator(void)
+{
+ return PR_PATH_SEPARATOR;
+} /* PR_GetPathSeparator */
+
+PR_IMPLEMENT(PRStatus) PR_GetSystemInfo(PRSysInfo cmd, char *buf, PRUint32 buflen)
+{
+ PRUintn len = 0;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ switch(cmd)
+ {
+ case PR_SI_HOSTNAME:
+ case PR_SI_HOSTNAME_UNTRUNCATED:
+ if (PR_FAILURE == _PR_MD_GETHOSTNAME(buf, (PRUintn)buflen)) {
+ return PR_FAILURE;
+ }
+
+ if (cmd == PR_SI_HOSTNAME_UNTRUNCATED) {
+ break;
+ }
+ /*
+ * On some platforms a system does not have a hostname and
+ * its IP address is returned instead. The following code
+ * should be skipped on those platforms.
+ */
+#ifndef _PR_GET_HOST_ADDR_AS_NAME
+ /* Return the unqualified hostname */
+ while (buf[len] && (len < buflen)) {
+ if (buf[len] == '.') {
+ buf[len] = '\0';
+ break;
+ }
+ len += 1;
+ }
+#endif
+ break;
+
+ case PR_SI_SYSNAME:
+ /* Return the operating system name */
+#if defined(XP_UNIX) || defined(WIN32)
+ if (PR_FAILURE == _PR_MD_GETSYSINFO(cmd, buf, (PRUintn)buflen)) {
+ return PR_FAILURE;
+ }
+#else
+ (void)PR_snprintf(buf, buflen, _PR_SI_SYSNAME);
+#endif
+ break;
+
+ case PR_SI_RELEASE:
+ /* Return the version of the operating system */
+#if defined(XP_UNIX) || defined(WIN32)
+ if (PR_FAILURE == _PR_MD_GETSYSINFO(cmd, buf, (PRUintn)buflen)) {
+ return PR_FAILURE;
+ }
+#endif
+#if defined(XP_OS2)
+ {
+ ULONG os2ver[2] = {0};
+ DosQuerySysInfo(QSV_VERSION_MINOR, QSV_VERSION_REVISION,
+ &os2ver, sizeof(os2ver));
+ /* Formatting for normal usage (2.11, 3.0, 4.0, 4.5); officially,
+ Warp 4 is version 2.40.00, WSeB 2.45.00 */
+ if (os2ver[0] < 30)
+ (void)PR_snprintf(buf, buflen, "%s%lu",
+ "2.", os2ver[0]);
+ else if (os2ver[0] < 45)
+ (void)PR_snprintf(buf, buflen, "%lu%s%lu",
+ os2ver[0]/10, ".", os2ver[1]);
+ else
+ (void)PR_snprintf(buf, buflen, "%.1f",
+ os2ver[0]/10.0);
+ }
+#endif /* OS2 */
+ break;
+
+ case PR_SI_RELEASE_BUILD:
+ /* Return the version of the operating system */
+#if defined(XP_UNIX) || defined(WIN32)
+ if (PR_FAILURE == _PR_MD_GETSYSINFO(cmd, buf, (PRUintn)buflen)) {
+ return PR_FAILURE;
+ }
+#else
+ if (buflen) {
+ *buf = 0;
+ }
+#endif /* XP_UNIX || WIN32 */
+ break;
+
+ case PR_SI_ARCHITECTURE:
+ /* Return the architecture of the machine (ie. x86, mips, alpha, ...)*/
+ (void)PR_snprintf(buf, buflen, _PR_SI_ARCHITECTURE);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+/*
+** PR_GetNumberOfProcessors()
+**
+** Implementation notes:
+** Every platform does it a bit different.
+** numCpus is the returned value.
+** for each platform's "if defined" section
+** declare your local variable
+** do your thing, assign to numCpus
+** order of the if defined()s may be important,
+** especially for unix variants. Do platform
+** specific implementations before XP_UNIX.
+**
+*/
+PR_IMPLEMENT(PRInt32) PR_GetNumberOfProcessors( void )
+{
+ PRInt32 numCpus;
+#if defined(WIN32)
+ SYSTEM_INFO info;
+
+ GetSystemInfo( &info );
+ numCpus = info.dwNumberOfProcessors;
+#elif defined(OS2)
+ DosQuerySysInfo( QSV_NUMPROCESSORS, QSV_NUMPROCESSORS, &numCpus, sizeof(numCpus));
+#elif defined(_PR_HAVE_SYSCTL)
+ int mib[2];
+ int rc;
+ size_t len = sizeof(numCpus);
+
+ mib[0] = CTL_HW;
+#ifdef HW_NCPUONLINE
+ mib[1] = HW_NCPUONLINE;
+#else
+ mib[1] = HW_NCPU;
+#endif
+ rc = sysctl( mib, 2, &numCpus, &len, NULL, 0 );
+ if ( -1 == rc ) {
+ numCpus = -1; /* set to -1 for return value on error */
+ _PR_MD_MAP_DEFAULT_ERROR( _MD_ERRNO() );
+ }
+#elif defined(HPUX)
+ numCpus = mpctl( MPC_GETNUMSPUS, 0, 0 );
+ if ( numCpus < 1 ) {
+ numCpus = -1; /* set to -1 for return value on error */
+ _PR_MD_MAP_DEFAULT_ERROR( _MD_ERRNO() );
+ }
+#elif defined(RISCOS)
+ numCpus = 1;
+#elif defined(LINUX)
+ /* for the benefit of devices with advanced power-saving, that
+ actually hotplug their cpus in heavy load, try to figure out
+ the real number of CPUs */
+ char buf[MAX_LINE];
+ FILE *fin;
+ const char *cpu_present = "/sys/devices/system/cpu/present";
+ size_t strsize;
+ numCpus = 0;
+ fin = fopen(cpu_present, "r");
+ if (fin != NULL) {
+ if (fgets(buf, MAX_LINE, fin) != NULL) {
+ /* check that the format is what we expect */
+ if (buf[0] == '0') {
+ strsize = strlen(buf);
+ if (strsize == 1) {
+ /* single core */
+ numCpus = 1;
+ } else if (strsize >= 3 && strsize <= 5) {
+ /* should be of the form 0-999 */
+ /* parse the part after the 0-, note count is 0-based */
+ if (buf[1] == '-' && isdigit(buf[2])) {
+ numCpus = 1 + atoi(buf + 2);
+ }
+ }
+ }
+ }
+ fclose(fin);
+ }
+ /* if that fails, fall back to more standard methods */
+ if (!numCpus) {
+ numCpus = sysconf( _SC_NPROCESSORS_CONF );
+ }
+#elif defined(XP_UNIX)
+ numCpus = sysconf( _SC_NPROCESSORS_CONF );
+#else
+#error "An implementation is required"
+#endif
+ return(numCpus);
+} /* end PR_GetNumberOfProcessors() */
+
+/*
+** PR_GetPhysicalMemorySize()
+**
+** Implementation notes:
+** Every platform does it a bit different.
+** bytes is the returned value.
+** for each platform's "if defined" section
+** declare your local variable
+** do your thing, assign to bytes.
+**
+*/
+PR_IMPLEMENT(PRUint64) PR_GetPhysicalMemorySize(void)
+{
+ PRUint64 bytes = 0;
+
+#if defined(LINUX) || defined(SOLARIS)
+
+ long pageSize = sysconf(_SC_PAGESIZE);
+ long pageCount = sysconf(_SC_PHYS_PAGES);
+ if (pageSize >= 0 && pageCount >= 0) {
+ bytes = (PRUint64) pageSize * pageCount;
+ }
+
+#elif defined(NETBSD) || defined(OPENBSD) \
+ || defined(FREEBSD) || defined(DRAGONFLY)
+
+ int mib[2];
+ int rc;
+#ifdef HW_PHYSMEM64
+ uint64_t memSize;
+#else
+ unsigned long memSize;
+#endif
+ size_t len = sizeof(memSize);
+
+ mib[0] = CTL_HW;
+#ifdef HW_PHYSMEM64
+ mib[1] = HW_PHYSMEM64;
+#else
+ mib[1] = HW_PHYSMEM;
+#endif
+ rc = sysctl(mib, 2, &memSize, &len, NULL, 0);
+ if (-1 != rc) {
+ bytes = memSize;
+ }
+
+#elif defined(HPUX)
+
+ struct pst_static info;
+ int result = pstat_getstatic(&info, sizeof(info), 1, 0);
+ if (result == 1) {
+ bytes = (PRUint64) info.physical_memory * info.page_size;
+ }
+
+#elif defined(DARWIN)
+
+ mach_port_t mach_host = mach_host_self();
+ struct host_basic_info hInfo;
+ mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
+
+ int result = host_info(mach_host,
+ HOST_BASIC_INFO,
+ (host_info_t) &hInfo,
+ &count);
+ mach_port_deallocate(mach_task_self(), mach_host);
+ if (result == KERN_SUCCESS) {
+ bytes = hInfo.max_mem;
+ }
+
+#elif defined(WIN32)
+
+ MEMORYSTATUSEX memStat;
+ memStat.dwLength = sizeof(memStat);
+ if (GlobalMemoryStatusEx(&memStat)) {
+ bytes = memStat.ullTotalPhys;
+ }
+
+#elif defined(OS2)
+
+ ULONG ulPhysMem;
+ DosQuerySysInfo(QSV_TOTPHYSMEM,
+ QSV_TOTPHYSMEM,
+ &ulPhysMem,
+ sizeof(ulPhysMem));
+ bytes = ulPhysMem;
+
+#elif defined(AIX)
+
+ if (odm_initialize() == 0) {
+ int how_many;
+ struct CuAt *obj = getattr("sys0", "realmem", 0, &how_many);
+ if (obj != NULL) {
+ bytes = (PRUint64) atoi(obj->value) * 1024;
+ free(obj);
+ }
+ odm_terminate();
+ }
+
+#else
+
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+
+#endif
+
+ return bytes;
+} /* end PR_GetPhysicalMemorySize() */
diff --git a/nsprpub/pr/src/misc/prthinfo.c b/nsprpub/pr/src/misc/prthinfo.c
new file mode 100644
index 0000000000..14602df0be
--- /dev/null
+++ b/nsprpub/pr/src/misc/prthinfo.c
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prlog.h"
+#include "prthread.h"
+#include "private/pprthred.h"
+#include "primpl.h"
+
+PR_IMPLEMENT(PRWord *)
+PR_GetGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ return _MD_HomeGCRegisters(t, isCurrent, np);
+}
+
+PR_IMPLEMENT(PRStatus)
+PR_ThreadScanStackPointers(PRThread* t,
+ PRScanStackFun scanFun, void* scanClosure)
+{
+ PRThread* current = PR_GetCurrentThread();
+ PRWord *sp, *esp, *p0;
+ int n;
+ void **ptd;
+ PRStatus status;
+ PRUint32 index;
+ int stack_end;
+
+ /*
+ ** Store the thread's registers in the thread structure so the GC
+ ** can scan them. Then scan them.
+ */
+ p0 = _MD_HomeGCRegisters(t, t == current, &n);
+ status = scanFun(t, (void**)p0, n, scanClosure);
+ if (status != PR_SUCCESS) {
+ return status;
+ }
+
+ /* Scan the C stack for pointers into the GC heap */
+#if defined(XP_PC) && defined(WIN16)
+ /*
+ ** Under WIN16, the stack of the current thread is always mapped into
+ ** the "task stack" (at SS:xxxx). So, if t is the current thread, scan
+ ** the "task stack". Otherwise, scan the "cached stack" of the inactive
+ ** thread...
+ */
+ if (t == current) {
+ sp = (PRWord*) &stack_end;
+ esp = (PRWord*) _pr_top_of_task_stack;
+
+ PR_ASSERT(sp <= esp);
+ } else {
+ sp = (PRWord*) PR_GetSP(t);
+ esp = (PRWord*) t->stack->stackTop;
+
+ PR_ASSERT((t->stack->stackSize == 0) ||
+ ((sp > (PRWord*)t->stack->stackBottom) &&
+ (sp <= (PRWord*)t->stack->stackTop)));
+ }
+#else /* ! WIN16 */
+#ifdef HAVE_STACK_GROWING_UP
+ if (t == current) {
+ esp = (PRWord*) &stack_end;
+ } else {
+ esp = (PRWord*) PR_GetSP(t);
+ }
+ sp = (PRWord*) t->stack->stackTop;
+ if (t->stack->stackSize) {
+ PR_ASSERT((esp > (PRWord*)t->stack->stackTop) &&
+ (esp < (PRWord*)t->stack->stackBottom));
+ }
+#else /* ! HAVE_STACK_GROWING_UP */
+ if (t == current) {
+ sp = (PRWord*) &stack_end;
+ } else {
+ sp = (PRWord*) PR_GetSP(t);
+ }
+ esp = (PRWord*) t->stack->stackTop;
+ if (t->stack->stackSize) {
+ PR_ASSERT((sp > (PRWord*)t->stack->stackBottom) &&
+ (sp < (PRWord*)t->stack->stackTop));
+ }
+#endif /* ! HAVE_STACK_GROWING_UP */
+#endif /* ! WIN16 */
+
+#if defined(WIN16)
+ {
+ prword_t scan;
+ prword_t limit;
+
+ scan = (prword_t) sp;
+ limit = (prword_t) esp;
+ while (scan < limit) {
+ prword_t *test;
+
+ test = *((prword_t **)scan);
+ status = scanFun(t, (void**)&test, 1, scanClosure);
+ if (status != PR_SUCCESS) {
+ return status;
+ }
+ scan += sizeof(char);
+ }
+ }
+#else
+ if (sp < esp) {
+ status = scanFun(t, (void**)sp, esp - sp, scanClosure);
+ if (status != PR_SUCCESS) {
+ return status;
+ }
+ }
+#endif
+
+ /*
+ ** Mark all of the per-thread-data items attached to this thread
+ **
+ ** The execution environment better be accounted for otherwise it
+ ** will be collected
+ */
+ status = scanFun(t, (void**)&t->environment, 1, scanClosure);
+ if (status != PR_SUCCESS) {
+ return status;
+ }
+
+ /* if thread is not allocated on stack, this is redundant. */
+ ptd = t->privateData;
+ for (index = 0; index < t->tpdLength; index++, ptd++) {
+ status = scanFun(t, (void**)ptd, 1, scanClosure);
+ if (status != PR_SUCCESS) {
+ return status;
+ }
+ }
+
+ return PR_SUCCESS;
+}
+
+/* transducer for PR_EnumerateThreads */
+typedef struct PRScanStackData {
+ PRScanStackFun scanFun;
+ void* scanClosure;
+} PRScanStackData;
+
+static PRStatus PR_CALLBACK
+pr_ScanStack(PRThread* t, int i, void* arg)
+{
+ PRScanStackData* data = (PRScanStackData*)arg;
+ return PR_ThreadScanStackPointers(t, data->scanFun, data->scanClosure);
+}
+
+PR_IMPLEMENT(PRStatus)
+PR_ScanStackPointers(PRScanStackFun scanFun, void* scanClosure)
+{
+ PRScanStackData data;
+ data.scanFun = scanFun;
+ data.scanClosure = scanClosure;
+ return PR_EnumerateThreads(pr_ScanStack, &data);
+}
+
+PR_IMPLEMENT(PRUword)
+PR_GetStackSpaceLeft(PRThread* t)
+{
+ PRThread *current = PR_GetCurrentThread();
+ PRWord *sp, *esp;
+ int stack_end;
+
+#if defined(WIN16)
+ /*
+ ** Under WIN16, the stack of the current thread is always mapped into
+ ** the "task stack" (at SS:xxxx). So, if t is the current thread, scan
+ ** the "task stack". Otherwise, scan the "cached stack" of the inactive
+ ** thread...
+ */
+ if (t == current) {
+ sp = (PRWord*) &stack_end;
+ esp = (PRWord*) _pr_top_of_task_stack;
+
+ PR_ASSERT(sp <= esp);
+ } else {
+ sp = (PRWord*) PR_GetSP(t);
+ esp = (PRWord*) t->stack->stackTop;
+
+ PR_ASSERT((t->stack->stackSize == 0) ||
+ ((sp > (PRWord*)t->stack->stackBottom) &&
+ (sp <= (PRWord*)t->stack->stackTop)));
+ }
+#else /* ! WIN16 */
+#ifdef HAVE_STACK_GROWING_UP
+ if (t == current) {
+ esp = (PRWord*) &stack_end;
+ } else {
+ esp = (PRWord*) PR_GetSP(t);
+ }
+ sp = (PRWord*) t->stack->stackTop;
+ if (t->stack->stackSize) {
+ PR_ASSERT((esp > (PRWord*)t->stack->stackTop) &&
+ (esp < (PRWord*)t->stack->stackBottom));
+ }
+#else /* ! HAVE_STACK_GROWING_UP */
+ if (t == current) {
+ sp = (PRWord*) &stack_end;
+ } else {
+ sp = (PRWord*) PR_GetSP(t);
+ }
+ esp = (PRWord*) t->stack->stackTop;
+ if (t->stack->stackSize) {
+ PR_ASSERT((sp > (PRWord*)t->stack->stackBottom) &&
+ (sp < (PRWord*)t->stack->stackTop));
+ }
+#endif /* ! HAVE_STACK_GROWING_UP */
+#endif /* ! WIN16 */
+ return (PRUword)t->stack->stackSize - ((PRWord)esp - (PRWord)sp);
+}
diff --git a/nsprpub/pr/src/misc/prtime.c b/nsprpub/pr/src/misc/prtime.c
new file mode 100644
index 0000000000..6d711a6b8d
--- /dev/null
+++ b/nsprpub/pr/src/misc/prtime.c
@@ -0,0 +1,2150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * prtime.c --
+ *
+ * NSPR date and time functions
+ *
+ */
+
+#include "prinit.h"
+#include "prtime.h"
+#include "prlock.h"
+#include "prprf.h"
+#include "prlog.h"
+
+#include <string.h>
+#include <ctype.h>
+#include <errno.h> /* for EINVAL */
+#include <time.h>
+
+/*
+ * The COUNT_LEAPS macro counts the number of leap years passed by
+ * till the start of the given year Y. At the start of the year 4
+ * A.D. the number of leap years passed by is 0, while at the start of
+ * the year 5 A.D. this count is 1. The number of years divisible by
+ * 100 but not divisible by 400 (the non-leap years) is deducted from
+ * the count to get the correct number of leap years.
+ *
+ * The COUNT_DAYS macro counts the number of days since 01/01/01 till the
+ * start of the given year Y. The number of days at the start of the year
+ * 1 is 0 while the number of days at the start of the year 2 is 365
+ * (which is ((2)-1) * 365) and so on. The reference point is 01/01/01
+ * midnight 00:00:00.
+ */
+
+#define COUNT_LEAPS(Y) ( ((Y)-1)/4 - ((Y)-1)/100 + ((Y)-1)/400 )
+#define COUNT_DAYS(Y) ( ((Y)-1)*365 + COUNT_LEAPS(Y) )
+#define DAYS_BETWEEN_YEARS(A, B) (COUNT_DAYS(B) - COUNT_DAYS(A))
+
+/*
+ * Static variables used by functions in this file
+ */
+
+/*
+ * The following array contains the day of year for the last day of
+ * each month, where index 1 is January, and day 0 is January 1.
+ */
+
+static const int lastDayOfMonth[2][13] = {
+ {-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364},
+ {-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}
+};
+
+/*
+ * The number of days in a month
+ */
+
+static const PRInt8 nDays[2][12] = {
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+/*
+ * Declarations for internal functions defined later in this file.
+ */
+
+static void ComputeGMT(PRTime time, PRExplodedTime *gmt);
+static int IsLeapYear(PRInt16 year);
+static void ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset);
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * ComputeGMT --
+ *
+ * Caveats:
+ * - we ignore leap seconds
+ *
+ *------------------------------------------------------------------------
+ */
+
+static void
+ComputeGMT(PRTime time, PRExplodedTime *gmt)
+{
+ PRInt32 tmp, rem;
+ PRInt32 numDays;
+ PRInt64 numDays64, rem64;
+ int isLeap;
+ PRInt64 sec;
+ PRInt64 usec;
+ PRInt64 usecPerSec;
+ PRInt64 secPerDay;
+
+ /*
+ * We first do the usec, sec, min, hour thing so that we do not
+ * have to do LL arithmetic.
+ */
+
+ LL_I2L(usecPerSec, 1000000L);
+ LL_DIV(sec, time, usecPerSec);
+ LL_MOD(usec, time, usecPerSec);
+ LL_L2I(gmt->tm_usec, usec);
+ /* Correct for weird mod semantics so the remainder is always positive */
+ if (gmt->tm_usec < 0) {
+ PRInt64 one;
+
+ LL_I2L(one, 1L);
+ LL_SUB(sec, sec, one);
+ gmt->tm_usec += 1000000L;
+ }
+
+ LL_I2L(secPerDay, 86400L);
+ LL_DIV(numDays64, sec, secPerDay);
+ LL_MOD(rem64, sec, secPerDay);
+ /* We are sure both of these numbers can fit into PRInt32 */
+ LL_L2I(numDays, numDays64);
+ LL_L2I(rem, rem64);
+ if (rem < 0) {
+ numDays--;
+ rem += 86400L;
+ }
+
+ /* Compute day of week. Epoch started on a Thursday. */
+
+ gmt->tm_wday = (numDays + 4) % 7;
+ if (gmt->tm_wday < 0) {
+ gmt->tm_wday += 7;
+ }
+
+ /* Compute the time of day. */
+
+ gmt->tm_hour = rem / 3600;
+ rem %= 3600;
+ gmt->tm_min = rem / 60;
+ gmt->tm_sec = rem % 60;
+
+ /*
+ * Compute the year by finding the 400 year period, then working
+ * down from there.
+ *
+ * Since numDays is originally the number of days since January 1, 1970,
+ * we must change it to be the number of days from January 1, 0001.
+ */
+
+ numDays += 719162; /* 719162 = days from year 1 up to 1970 */
+ tmp = numDays / 146097; /* 146097 = days in 400 years */
+ rem = numDays % 146097;
+ gmt->tm_year = tmp * 400 + 1;
+
+ /* Compute the 100 year period. */
+
+ tmp = rem / 36524; /* 36524 = days in 100 years */
+ rem %= 36524;
+ if (tmp == 4) { /* the 400th year is a leap year */
+ tmp = 3;
+ rem = 36524;
+ }
+ gmt->tm_year += tmp * 100;
+
+ /* Compute the 4 year period. */
+
+ tmp = rem / 1461; /* 1461 = days in 4 years */
+ rem %= 1461;
+ gmt->tm_year += tmp * 4;
+
+ /* Compute which year in the 4. */
+
+ tmp = rem / 365;
+ rem %= 365;
+ if (tmp == 4) { /* the 4th year is a leap year */
+ tmp = 3;
+ rem = 365;
+ }
+
+ gmt->tm_year += tmp;
+ gmt->tm_yday = rem;
+ isLeap = IsLeapYear(gmt->tm_year);
+
+ /* Compute the month and day of month. */
+
+ for (tmp = 1; lastDayOfMonth[isLeap][tmp] < gmt->tm_yday; tmp++) {
+ }
+ gmt->tm_month = --tmp;
+ gmt->tm_mday = gmt->tm_yday - lastDayOfMonth[isLeap][tmp];
+
+ gmt->tm_params.tp_gmt_offset = 0;
+ gmt->tm_params.tp_dst_offset = 0;
+}
+
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PR_ExplodeTime --
+ *
+ * Cf. struct tm *gmtime(const time_t *tp) and
+ * struct tm *localtime(const time_t *tp)
+ *
+ *------------------------------------------------------------------------
+ */
+
+PR_IMPLEMENT(void)
+PR_ExplodeTime(
+ PRTime usecs,
+ PRTimeParamFn params,
+ PRExplodedTime *exploded)
+{
+ ComputeGMT(usecs, exploded);
+ exploded->tm_params = params(exploded);
+ ApplySecOffset(exploded, exploded->tm_params.tp_gmt_offset
+ + exploded->tm_params.tp_dst_offset);
+}
+
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PR_ImplodeTime --
+ *
+ * Cf. time_t mktime(struct tm *tp)
+ * Note that 1 year has < 2^25 seconds. So an PRInt32 is large enough.
+ *
+ *------------------------------------------------------------------------
+ */
+PR_IMPLEMENT(PRTime)
+PR_ImplodeTime(const PRExplodedTime *exploded)
+{
+ PRExplodedTime copy;
+ PRTime retVal;
+ PRInt64 secPerDay, usecPerSec;
+ PRInt64 temp;
+ PRInt64 numSecs64;
+ PRInt32 numDays;
+ PRInt32 numSecs;
+
+ /* Normalize first. Do this on our copy */
+ copy = *exploded;
+ PR_NormalizeTime(&copy, PR_GMTParameters);
+
+ numDays = DAYS_BETWEEN_YEARS(1970, copy.tm_year);
+
+ numSecs = copy.tm_yday * 86400 + copy.tm_hour * 3600
+ + copy.tm_min * 60 + copy.tm_sec;
+
+ LL_I2L(temp, numDays);
+ LL_I2L(secPerDay, 86400);
+ LL_MUL(temp, temp, secPerDay);
+ LL_I2L(numSecs64, numSecs);
+ LL_ADD(numSecs64, numSecs64, temp);
+
+ /* apply the GMT and DST offsets */
+ LL_I2L(temp, copy.tm_params.tp_gmt_offset);
+ LL_SUB(numSecs64, numSecs64, temp);
+ LL_I2L(temp, copy.tm_params.tp_dst_offset);
+ LL_SUB(numSecs64, numSecs64, temp);
+
+ LL_I2L(usecPerSec, 1000000L);
+ LL_MUL(temp, numSecs64, usecPerSec);
+ LL_I2L(retVal, copy.tm_usec);
+ LL_ADD(retVal, retVal, temp);
+
+ return retVal;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * IsLeapYear --
+ *
+ * Returns 1 if the year is a leap year, 0 otherwise.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static int IsLeapYear(PRInt16 year)
+{
+ if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * 'secOffset' should be less than 86400 (i.e., a day).
+ * 'time' should point to a normalized PRExplodedTime.
+ */
+
+static void
+ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset)
+{
+ time->tm_sec += secOffset;
+
+ /* Note that in this implementation we do not count leap seconds */
+ if (time->tm_sec < 0 || time->tm_sec >= 60) {
+ time->tm_min += time->tm_sec / 60;
+ time->tm_sec %= 60;
+ if (time->tm_sec < 0) {
+ time->tm_sec += 60;
+ time->tm_min--;
+ }
+ }
+
+ if (time->tm_min < 0 || time->tm_min >= 60) {
+ time->tm_hour += time->tm_min / 60;
+ time->tm_min %= 60;
+ if (time->tm_min < 0) {
+ time->tm_min += 60;
+ time->tm_hour--;
+ }
+ }
+
+ if (time->tm_hour < 0) {
+ /* Decrement mday, yday, and wday */
+ time->tm_hour += 24;
+ time->tm_mday--;
+ time->tm_yday--;
+ if (time->tm_mday < 1) {
+ time->tm_month--;
+ if (time->tm_month < 0) {
+ time->tm_month = 11;
+ time->tm_year--;
+ if (IsLeapYear(time->tm_year)) {
+ time->tm_yday = 365;
+ }
+ else {
+ time->tm_yday = 364;
+ }
+ }
+ time->tm_mday = nDays[IsLeapYear(time->tm_year)][time->tm_month];
+ }
+ time->tm_wday--;
+ if (time->tm_wday < 0) {
+ time->tm_wday = 6;
+ }
+ } else if (time->tm_hour > 23) {
+ /* Increment mday, yday, and wday */
+ time->tm_hour -= 24;
+ time->tm_mday++;
+ time->tm_yday++;
+ if (time->tm_mday >
+ nDays[IsLeapYear(time->tm_year)][time->tm_month]) {
+ time->tm_mday = 1;
+ time->tm_month++;
+ if (time->tm_month > 11) {
+ time->tm_month = 0;
+ time->tm_year++;
+ time->tm_yday = 0;
+ }
+ }
+ time->tm_wday++;
+ if (time->tm_wday > 6) {
+ time->tm_wday = 0;
+ }
+ }
+}
+
+PR_IMPLEMENT(void)
+PR_NormalizeTime(PRExplodedTime *time, PRTimeParamFn params)
+{
+ int daysInMonth;
+ PRInt32 numDays;
+
+ /* Get back to GMT */
+ time->tm_sec -= time->tm_params.tp_gmt_offset
+ + time->tm_params.tp_dst_offset;
+ time->tm_params.tp_gmt_offset = 0;
+ time->tm_params.tp_dst_offset = 0;
+
+ /* Now normalize GMT */
+
+ if (time->tm_usec < 0 || time->tm_usec >= 1000000) {
+ time->tm_sec += time->tm_usec / 1000000;
+ time->tm_usec %= 1000000;
+ if (time->tm_usec < 0) {
+ time->tm_usec += 1000000;
+ time->tm_sec--;
+ }
+ }
+
+ /* Note that we do not count leap seconds in this implementation */
+ if (time->tm_sec < 0 || time->tm_sec >= 60) {
+ time->tm_min += time->tm_sec / 60;
+ time->tm_sec %= 60;
+ if (time->tm_sec < 0) {
+ time->tm_sec += 60;
+ time->tm_min--;
+ }
+ }
+
+ if (time->tm_min < 0 || time->tm_min >= 60) {
+ time->tm_hour += time->tm_min / 60;
+ time->tm_min %= 60;
+ if (time->tm_min < 0) {
+ time->tm_min += 60;
+ time->tm_hour--;
+ }
+ }
+
+ if (time->tm_hour < 0 || time->tm_hour >= 24) {
+ time->tm_mday += time->tm_hour / 24;
+ time->tm_hour %= 24;
+ if (time->tm_hour < 0) {
+ time->tm_hour += 24;
+ time->tm_mday--;
+ }
+ }
+
+ /* Normalize month and year before mday */
+ if (time->tm_month < 0 || time->tm_month >= 12) {
+ time->tm_year += time->tm_month / 12;
+ time->tm_month %= 12;
+ if (time->tm_month < 0) {
+ time->tm_month += 12;
+ time->tm_year--;
+ }
+ }
+
+ /* Now that month and year are in proper range, normalize mday */
+
+ if (time->tm_mday < 1) {
+ /* mday too small */
+ do {
+ /* the previous month */
+ time->tm_month--;
+ if (time->tm_month < 0) {
+ time->tm_month = 11;
+ time->tm_year--;
+ }
+ time->tm_mday += nDays[IsLeapYear(time->tm_year)][time->tm_month];
+ } while (time->tm_mday < 1);
+ } else {
+ daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
+ while (time->tm_mday > daysInMonth) {
+ /* mday too large */
+ time->tm_mday -= daysInMonth;
+ time->tm_month++;
+ if (time->tm_month > 11) {
+ time->tm_month = 0;
+ time->tm_year++;
+ }
+ daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
+ }
+ }
+
+ /* Recompute yday and wday */
+ time->tm_yday = time->tm_mday +
+ lastDayOfMonth[IsLeapYear(time->tm_year)][time->tm_month];
+
+ numDays = DAYS_BETWEEN_YEARS(1970, time->tm_year) + time->tm_yday;
+ time->tm_wday = (numDays + 4) % 7;
+ if (time->tm_wday < 0) {
+ time->tm_wday += 7;
+ }
+
+ /* Recompute time parameters */
+
+ time->tm_params = params(time);
+
+ ApplySecOffset(time, time->tm_params.tp_gmt_offset
+ + time->tm_params.tp_dst_offset);
+}
+
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * PR_LocalTimeParameters --
+ *
+ * returns the time parameters for the local time zone
+ *
+ * The following uses localtime() from the standard C library.
+ * (time.h) This is our fallback implementation. Unix, PC, and BeOS
+ * use this version. A platform may have its own machine-dependent
+ * implementation of this function.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if defined(HAVE_INT_LOCALTIME_R)
+
+/*
+ * In this case we could define the macro as
+ * #define MT_safe_localtime(timer, result) \
+ * (localtime_r(timer, result) == 0 ? result : NULL)
+ * I chose to compare the return value of localtime_r with -1 so
+ * that I can catch the cases where localtime_r returns a pointer
+ * to struct tm. The macro definition above would not be able to
+ * detect such mistakes because it is legal to compare a pointer
+ * with 0.
+ */
+
+#define MT_safe_localtime(timer, result) \
+ (localtime_r(timer, result) == -1 ? NULL: result)
+
+#elif defined(HAVE_POINTER_LOCALTIME_R)
+
+#define MT_safe_localtime localtime_r
+
+#elif defined(_MSC_VER)
+
+/* Visual C++ has had localtime_s() since Visual C++ 2005. */
+
+static struct tm *MT_safe_localtime(const time_t *clock, struct tm *result)
+{
+ errno_t err = localtime_s(result, clock);
+ if (err != 0) {
+ errno = err;
+ return NULL;
+ }
+ return result;
+}
+
+#else
+
+#define HAVE_LOCALTIME_MONITOR 1 /* We use 'monitor' to serialize our calls
+ * to localtime(). */
+static PRLock *monitor = NULL;
+
+static struct tm *MT_safe_localtime(const time_t *clock, struct tm *result)
+{
+ struct tm *tmPtr;
+ int needLock = PR_Initialized(); /* We need to use a lock to protect
+ * against NSPR threads only when the
+ * NSPR thread system is activated. */
+
+ if (needLock) {
+ PR_Lock(monitor);
+ }
+
+ /*
+ * Microsoft (all flavors) localtime() returns a NULL pointer if 'clock'
+ * represents a time before midnight January 1, 1970. In
+ * that case, we also return a NULL pointer and the struct tm
+ * object pointed to by 'result' is not modified.
+ *
+ * Watcom C/C++ 11.0 localtime() treats time_t as unsigned long
+ * hence, does not recognize negative values of clock as pre-1/1/70.
+ * We have to manually check (WIN16 only) for negative value of
+ * clock and return NULL.
+ *
+ * With negative values of clock, OS/2 returns the struct tm for
+ * clock plus ULONG_MAX. So we also have to check for the invalid
+ * structs returned for timezones west of Greenwich when clock == 0.
+ */
+
+ tmPtr = localtime(clock);
+
+#if defined(WIN16) || defined(XP_OS2)
+ if ( (PRInt32) *clock < 0 ||
+ ( (PRInt32) *clock == 0 && tmPtr->tm_year != 70)) {
+ result = NULL;
+ }
+ else {
+ *result = *tmPtr;
+ }
+#else
+ if (tmPtr) {
+ *result = *tmPtr;
+ } else {
+ result = NULL;
+ }
+#endif /* WIN16 */
+
+ if (needLock) {
+ PR_Unlock(monitor);
+ }
+
+ return result;
+}
+
+#endif /* definition of MT_safe_localtime() */
+
+void _PR_InitTime(void)
+{
+#ifdef HAVE_LOCALTIME_MONITOR
+ monitor = PR_NewLock();
+#endif
+#ifdef WINCE
+ _MD_InitTime();
+#endif
+}
+
+void _PR_CleanupTime(void)
+{
+#ifdef HAVE_LOCALTIME_MONITOR
+ if (monitor) {
+ PR_DestroyLock(monitor);
+ monitor = NULL;
+ }
+#endif
+#ifdef WINCE
+ _MD_CleanupTime();
+#endif
+}
+
+#if defined(XP_UNIX) || defined(XP_PC)
+
+PR_IMPLEMENT(PRTimeParameters)
+PR_LocalTimeParameters(const PRExplodedTime *gmt)
+{
+
+ PRTimeParameters retVal;
+ struct tm localTime;
+ struct tm *localTimeResult;
+ time_t secs;
+ PRTime secs64;
+ PRInt64 usecPerSec;
+ PRInt64 usecPerSec_1;
+ PRInt64 maxInt32;
+ PRInt64 minInt32;
+ PRInt32 dayOffset;
+ PRInt32 offset2Jan1970;
+ PRInt32 offsetNew;
+ int isdst2Jan1970;
+
+ /*
+ * Calculate the GMT offset. First, figure out what is
+ * 00:00:00 Jan. 2, 1970 GMT (which is exactly a day, or 86400
+ * seconds, since the epoch) in local time. Then we calculate
+ * the difference between local time and GMT in seconds:
+ * gmt_offset = local_time - GMT
+ *
+ * Caveat: the validity of this calculation depends on two
+ * assumptions:
+ * 1. Daylight saving time was not in effect on Jan. 2, 1970.
+ * 2. The time zone of the geographic location has not changed
+ * since Jan. 2, 1970.
+ */
+
+ secs = 86400L;
+ localTimeResult = MT_safe_localtime(&secs, &localTime);
+ PR_ASSERT(localTimeResult != NULL);
+ if (localTimeResult == NULL) {
+ /* Shouldn't happen. Use safe fallback for optimized builds. */
+ return PR_GMTParameters(gmt);
+ }
+
+ /* GMT is 00:00:00, 2nd of Jan. */
+
+ offset2Jan1970 = (PRInt32)localTime.tm_sec
+ + 60L * (PRInt32)localTime.tm_min
+ + 3600L * (PRInt32)localTime.tm_hour
+ + 86400L * (PRInt32)((PRInt32)localTime.tm_mday - 2L);
+
+ isdst2Jan1970 = localTime.tm_isdst;
+
+ /*
+ * Now compute DST offset. We calculate the overall offset
+ * of local time from GMT, similar to above. The overall
+ * offset has two components: gmt offset and dst offset.
+ * We subtract gmt offset from the overall offset to get
+ * the dst offset.
+ * overall_offset = local_time - GMT
+ * overall_offset = gmt_offset + dst_offset
+ * ==> dst_offset = local_time - GMT - gmt_offset
+ */
+
+ secs64 = PR_ImplodeTime(gmt); /* This is still in microseconds */
+ LL_I2L(usecPerSec, PR_USEC_PER_SEC);
+ LL_I2L(usecPerSec_1, PR_USEC_PER_SEC - 1);
+ /* Convert to seconds, truncating down (3.1 -> 3 and -3.1 -> -4) */
+ if (LL_GE_ZERO(secs64)) {
+ LL_DIV(secs64, secs64, usecPerSec);
+ } else {
+ LL_NEG(secs64, secs64);
+ LL_ADD(secs64, secs64, usecPerSec_1);
+ LL_DIV(secs64, secs64, usecPerSec);
+ LL_NEG(secs64, secs64);
+ }
+ LL_I2L(maxInt32, PR_INT32_MAX);
+ LL_I2L(minInt32, PR_INT32_MIN);
+ if (LL_CMP(secs64, >, maxInt32) || LL_CMP(secs64, <, minInt32)) {
+ /* secs64 is too large or too small for time_t (32-bit integer) */
+ retVal.tp_gmt_offset = offset2Jan1970;
+ retVal.tp_dst_offset = 0;
+ return retVal;
+ }
+ LL_L2I(secs, secs64);
+
+ /*
+ * On Windows, localtime() (and our MT_safe_localtime() too)
+ * returns a NULL pointer for time before midnight January 1,
+ * 1970 GMT. In that case, we just use the GMT offset for
+ * Jan 2, 1970 and assume that DST was not in effect.
+ */
+
+ if (MT_safe_localtime(&secs, &localTime) == NULL) {
+ retVal.tp_gmt_offset = offset2Jan1970;
+ retVal.tp_dst_offset = 0;
+ return retVal;
+ }
+
+ /*
+ * dayOffset is the offset between local time and GMT in
+ * the day component, which can only be -1, 0, or 1. We
+ * use the day of the week to compute dayOffset.
+ */
+
+ dayOffset = (PRInt32) localTime.tm_wday - gmt->tm_wday;
+
+ /*
+ * Need to adjust for wrapping around of day of the week from
+ * 6 back to 0.
+ */
+
+ if (dayOffset == -6) {
+ /* Local time is Sunday (0) and GMT is Saturday (6) */
+ dayOffset = 1;
+ } else if (dayOffset == 6) {
+ /* Local time is Saturday (6) and GMT is Sunday (0) */
+ dayOffset = -1;
+ }
+
+ offsetNew = (PRInt32)localTime.tm_sec - gmt->tm_sec
+ + 60L * ((PRInt32)localTime.tm_min - gmt->tm_min)
+ + 3600L * ((PRInt32)localTime.tm_hour - gmt->tm_hour)
+ + 86400L * (PRInt32)dayOffset;
+
+ if (localTime.tm_isdst <= 0) {
+ /* DST is not in effect */
+ retVal.tp_gmt_offset = offsetNew;
+ retVal.tp_dst_offset = 0;
+ } else {
+ /* DST is in effect */
+ if (isdst2Jan1970 <=0) {
+ /*
+ * DST was not in effect back in 2 Jan. 1970.
+ * Use the offset back then as the GMT offset,
+ * assuming the time zone has not changed since then.
+ */
+ retVal.tp_gmt_offset = offset2Jan1970;
+ retVal.tp_dst_offset = offsetNew - offset2Jan1970;
+ } else {
+ /*
+ * DST was also in effect back in 2 Jan. 1970.
+ * Then our clever trick (or rather, ugly hack) fails.
+ * We will just assume DST offset is an hour.
+ */
+ retVal.tp_gmt_offset = offsetNew - 3600;
+ retVal.tp_dst_offset = 3600;
+ }
+ }
+
+ return retVal;
+}
+
+#endif /* defined(XP_UNIX) || defined(XP_PC) */
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PR_USPacificTimeParameters --
+ *
+ * The time parameters function for the US Pacific Time Zone.
+ *
+ *------------------------------------------------------------------------
+ */
+
+/*
+ * Returns the mday of the first sunday of the month, where
+ * mday and wday are for a given day in the month.
+ * mdays start with 1 (e.g. 1..31).
+ * wdays start with 0 and are in the range 0..6. 0 = Sunday.
+ */
+#define firstSunday(mday, wday) (((mday - wday + 7 - 1) % 7) + 1)
+
+/*
+ * Returns the mday for the N'th Sunday of the month, where
+ * mday and wday are for a given day in the month.
+ * mdays start with 1 (e.g. 1..31).
+ * wdays start with 0 and are in the range 0..6. 0 = Sunday.
+ * N has the following values: 0 = first, 1 = second (etc), -1 = last.
+ * ndays is the number of days in that month, the same value as the
+ * mday of the last day of the month.
+ */
+static PRInt32
+NthSunday(PRInt32 mday, PRInt32 wday, PRInt32 N, PRInt32 ndays)
+{
+ PRInt32 firstSun = firstSunday(mday, wday);
+
+ if (N < 0) {
+ N = (ndays - firstSun) / 7;
+ }
+ return firstSun + (7 * N);
+}
+
+typedef struct DSTParams {
+ PRInt8 dst_start_month; /* 0 = January */
+ PRInt8 dst_start_Nth_Sunday; /* N as defined above */
+ PRInt8 dst_start_month_ndays; /* ndays as defined above */
+ PRInt8 dst_end_month; /* 0 = January */
+ PRInt8 dst_end_Nth_Sunday; /* N as defined above */
+ PRInt8 dst_end_month_ndays; /* ndays as defined above */
+} DSTParams;
+
+static const DSTParams dstParams[2] = {
+ /* year < 2007: First April Sunday - Last October Sunday */
+ { 3, 0, 30, 9, -1, 31 },
+ /* year >= 2007: Second March Sunday - First November Sunday */
+ { 2, 1, 31, 10, 0, 30 }
+};
+
+PR_IMPLEMENT(PRTimeParameters)
+PR_USPacificTimeParameters(const PRExplodedTime *gmt)
+{
+ const DSTParams *dst;
+ PRTimeParameters retVal;
+ PRExplodedTime st;
+
+ /*
+ * Based on geographic location and GMT, figure out offset of
+ * standard time from GMT. In this example implementation, we
+ * assume the local time zone is US Pacific Time.
+ */
+
+ retVal.tp_gmt_offset = -8L * 3600L;
+
+ /*
+ * Make a copy of GMT. Note that the tm_params field of this copy
+ * is ignored.
+ */
+
+ st.tm_usec = gmt->tm_usec;
+ st.tm_sec = gmt->tm_sec;
+ st.tm_min = gmt->tm_min;
+ st.tm_hour = gmt->tm_hour;
+ st.tm_mday = gmt->tm_mday;
+ st.tm_month = gmt->tm_month;
+ st.tm_year = gmt->tm_year;
+ st.tm_wday = gmt->tm_wday;
+ st.tm_yday = gmt->tm_yday;
+
+ /* Apply the offset to GMT to obtain the local standard time */
+ ApplySecOffset(&st, retVal.tp_gmt_offset);
+
+ if (st.tm_year < 2007) { /* first April Sunday - Last October Sunday */
+ dst = &dstParams[0];
+ } else { /* Second March Sunday - First November Sunday */
+ dst = &dstParams[1];
+ }
+
+ /*
+ * Apply the rules on standard time or GMT to obtain daylight saving
+ * time offset. In this implementation, we use the US DST rule.
+ */
+ if (st.tm_month < dst->dst_start_month) {
+ retVal.tp_dst_offset = 0L;
+ } else if (st.tm_month == dst->dst_start_month) {
+ int NthSun = NthSunday(st.tm_mday, st.tm_wday,
+ dst->dst_start_Nth_Sunday,
+ dst->dst_start_month_ndays);
+ if (st.tm_mday < NthSun) { /* Before starting Sunday */
+ retVal.tp_dst_offset = 0L;
+ } else if (st.tm_mday == NthSun) { /* Starting Sunday */
+ /* 01:59:59 PST -> 03:00:00 PDT */
+ if (st.tm_hour < 2) {
+ retVal.tp_dst_offset = 0L;
+ } else {
+ retVal.tp_dst_offset = 3600L;
+ }
+ } else { /* After starting Sunday */
+ retVal.tp_dst_offset = 3600L;
+ }
+ } else if (st.tm_month < dst->dst_end_month) {
+ retVal.tp_dst_offset = 3600L;
+ } else if (st.tm_month == dst->dst_end_month) {
+ int NthSun = NthSunday(st.tm_mday, st.tm_wday,
+ dst->dst_end_Nth_Sunday,
+ dst->dst_end_month_ndays);
+ if (st.tm_mday < NthSun) { /* Before ending Sunday */
+ retVal.tp_dst_offset = 3600L;
+ } else if (st.tm_mday == NthSun) { /* Ending Sunday */
+ /* 01:59:59 PDT -> 01:00:00 PST */
+ if (st.tm_hour < 1) {
+ retVal.tp_dst_offset = 3600L;
+ } else {
+ retVal.tp_dst_offset = 0L;
+ }
+ } else { /* After ending Sunday */
+ retVal.tp_dst_offset = 0L;
+ }
+ } else {
+ retVal.tp_dst_offset = 0L;
+ }
+ return retVal;
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PR_GMTParameters --
+ *
+ * Returns the PRTimeParameters for Greenwich Mean Time.
+ * Trivially, both the tp_gmt_offset and tp_dst_offset fields are 0.
+ *
+ *------------------------------------------------------------------------
+ */
+
+PR_IMPLEMENT(PRTimeParameters)
+PR_GMTParameters(const PRExplodedTime *gmt)
+{
+ PRTimeParameters retVal = { 0, 0 };
+ return retVal;
+}
+
+/*
+ * The following code implements PR_ParseTimeString(). It is based on
+ * ns/lib/xp/xp_time.c, revision 1.25, by Jamie Zawinski <jwz@netscape.com>.
+ */
+
+/*
+ * We only recognize the abbreviations of a small subset of time zones
+ * in North America, Europe, and Japan.
+ *
+ * PST/PDT: Pacific Standard/Daylight Time
+ * MST/MDT: Mountain Standard/Daylight Time
+ * CST/CDT: Central Standard/Daylight Time
+ * EST/EDT: Eastern Standard/Daylight Time
+ * AST: Atlantic Standard Time
+ * NST: Newfoundland Standard Time
+ * GMT: Greenwich Mean Time
+ * BST: British Summer Time
+ * MET: Middle Europe Time
+ * EET: Eastern Europe Time
+ * JST: Japan Standard Time
+ */
+
+typedef enum
+{
+ TT_UNKNOWN,
+
+ TT_SUN, TT_MON, TT_TUE, TT_WED, TT_THU, TT_FRI, TT_SAT,
+
+ TT_JAN, TT_FEB, TT_MAR, TT_APR, TT_MAY, TT_JUN,
+ TT_JUL, TT_AUG, TT_SEP, TT_OCT, TT_NOV, TT_DEC,
+
+ TT_PST, TT_PDT, TT_MST, TT_MDT, TT_CST, TT_CDT, TT_EST, TT_EDT,
+ TT_AST, TT_NST, TT_GMT, TT_BST, TT_MET, TT_EET, TT_JST
+} TIME_TOKEN;
+
+/*
+ * This parses a time/date string into a PRTime
+ * (microseconds after "1-Jan-1970 00:00:00 GMT").
+ * It returns PR_SUCCESS on success, and PR_FAILURE
+ * if the time/date string can't be parsed.
+ *
+ * Many formats are handled, including:
+ *
+ * 14 Apr 89 03:20:12
+ * 14 Apr 89 03:20 GMT
+ * Fri, 17 Mar 89 4:01:33
+ * Fri, 17 Mar 89 4:01 GMT
+ * Mon Jan 16 16:12 PDT 1989
+ * Mon Jan 16 16:12 +0130 1989
+ * 6 May 1992 16:41-JST (Wednesday)
+ * 22-AUG-1993 10:59:12.82
+ * 22-AUG-1993 10:59pm
+ * 22-AUG-1993 12:59am
+ * 22-AUG-1993 12:59 PM
+ * Friday, August 04, 1995 3:54 PM
+ * 06/21/95 04:24:34 PM
+ * 20/06/95 21:07
+ * 95-06-08 19:32:48 EDT
+ *
+ * If the input string doesn't contain a description of the timezone,
+ * we consult the `default_to_gmt' to decide whether the string should
+ * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
+ * The correct value for this argument depends on what standard specified
+ * the time string which you are parsing.
+ */
+
+PR_IMPLEMENT(PRStatus)
+PR_ParseTimeStringToExplodedTime(
+ const char *string,
+ PRBool default_to_gmt,
+ PRExplodedTime *result)
+{
+ TIME_TOKEN dotw = TT_UNKNOWN;
+ TIME_TOKEN month = TT_UNKNOWN;
+ TIME_TOKEN zone = TT_UNKNOWN;
+ int zone_offset = -1;
+ int dst_offset = 0;
+ int date = -1;
+ PRInt32 year = -1;
+ int hour = -1;
+ int min = -1;
+ int sec = -1;
+ struct tm *localTimeResult;
+
+ const char *rest = string;
+
+ int iterations = 0;
+
+ PR_ASSERT(string && result);
+ if (!string || !result) {
+ return PR_FAILURE;
+ }
+
+ while (*rest)
+ {
+
+ if (iterations++ > 1000)
+ {
+ return PR_FAILURE;
+ }
+
+ switch (*rest)
+ {
+ case 'a': case 'A':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'p' || rest[1] == 'P') &&
+ (rest[2] == 'r' || rest[2] == 'R')) {
+ month = TT_APR;
+ }
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_AST;
+ }
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'g' || rest[2] == 'G')) {
+ month = TT_AUG;
+ }
+ break;
+ case 'b': case 'B':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_BST;
+ }
+ break;
+ case 'c': case 'C':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_CDT;
+ }
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_CST;
+ }
+ break;
+ case 'd': case 'D':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'c' || rest[2] == 'C')) {
+ month = TT_DEC;
+ }
+ break;
+ case 'e': case 'E':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_EDT;
+ }
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_EET;
+ }
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_EST;
+ }
+ break;
+ case 'f': case 'F':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'b' || rest[2] == 'B')) {
+ month = TT_FEB;
+ }
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'r' || rest[1] == 'R') &&
+ (rest[2] == 'i' || rest[2] == 'I')) {
+ dotw = TT_FRI;
+ }
+ break;
+ case 'g': case 'G':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'm' || rest[1] == 'M') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_GMT;
+ }
+ break;
+ case 'j': case 'J':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 'n' || rest[2] == 'N')) {
+ month = TT_JAN;
+ }
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_JST;
+ }
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'l' || rest[2] == 'L')) {
+ month = TT_JUL;
+ }
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'n' || rest[2] == 'N')) {
+ month = TT_JUN;
+ }
+ break;
+ case 'm': case 'M':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 'r' || rest[2] == 'R')) {
+ month = TT_MAR;
+ }
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 'y' || rest[2] == 'Y')) {
+ month = TT_MAY;
+ }
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_MDT;
+ }
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_MET;
+ }
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'o' || rest[1] == 'O') &&
+ (rest[2] == 'n' || rest[2] == 'N')) {
+ dotw = TT_MON;
+ }
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_MST;
+ }
+ break;
+ case 'n': case 'N':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'o' || rest[1] == 'O') &&
+ (rest[2] == 'v' || rest[2] == 'V')) {
+ month = TT_NOV;
+ }
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_NST;
+ }
+ break;
+ case 'o': case 'O':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'c' || rest[1] == 'C') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ month = TT_OCT;
+ }
+ break;
+ case 'p': case 'P':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_PDT;
+ }
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ zone = TT_PST;
+ }
+ break;
+ case 's': case 'S':
+ if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 't' || rest[2] == 'T')) {
+ dotw = TT_SAT;
+ }
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'p' || rest[2] == 'P')) {
+ month = TT_SEP;
+ }
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'n' || rest[2] == 'N')) {
+ dotw = TT_SUN;
+ }
+ break;
+ case 't': case 'T':
+ if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'h' || rest[1] == 'H') &&
+ (rest[2] == 'u' || rest[2] == 'U')) {
+ dotw = TT_THU;
+ }
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'e' || rest[2] == 'E')) {
+ dotw = TT_TUE;
+ }
+ break;
+ case 'u': case 'U':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 't' || rest[1] == 'T') &&
+ !(rest[2] >= 'A' && rest[2] <= 'Z') &&
+ !(rest[2] >= 'a' && rest[2] <= 'z'))
+ /* UT is the same as GMT but UTx is not. */
+ {
+ zone = TT_GMT;
+ }
+ break;
+ case 'w': case 'W':
+ if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'd' || rest[2] == 'D')) {
+ dotw = TT_WED;
+ }
+ break;
+
+ case '+': case '-':
+ {
+ const char *end;
+ int sign;
+ if (zone_offset != -1)
+ {
+ /* already got one... */
+ rest++;
+ break;
+ }
+ if (zone != TT_UNKNOWN && zone != TT_GMT)
+ {
+ /* GMT+0300 is legal, but PST+0300 is not. */
+ rest++;
+ break;
+ }
+
+ sign = ((*rest == '+') ? 1 : -1);
+ rest++; /* move over sign */
+ end = rest;
+ while (*end >= '0' && *end <= '9') {
+ end++;
+ }
+ if (rest == end) { /* no digits here */
+ break;
+ }
+
+ if ((end - rest) == 4)
+ /* offset in HHMM */
+ zone_offset = (((((rest[0]-'0')*10) + (rest[1]-'0')) * 60) +
+ (((rest[2]-'0')*10) + (rest[3]-'0')));
+ else if ((end - rest) == 2)
+ /* offset in hours */
+ {
+ zone_offset = (((rest[0]-'0')*10) + (rest[1]-'0')) * 60;
+ }
+ else if ((end - rest) == 1)
+ /* offset in hours */
+ {
+ zone_offset = (rest[0]-'0') * 60;
+ }
+ else
+ /* 3 or >4 */
+ {
+ break;
+ }
+
+ zone_offset *= sign;
+ zone = TT_GMT;
+ break;
+ }
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int tmp_hour = -1;
+ int tmp_min = -1;
+ int tmp_sec = -1;
+ const char *end = rest + 1;
+ while (*end >= '0' && *end <= '9') {
+ end++;
+ }
+
+ /* end is now the first character after a range of digits. */
+
+ if (*end == ':')
+ {
+ if (hour >= 0 && min >= 0) { /* already got it */
+ break;
+ }
+
+ /* We have seen "[0-9]+:", so this is probably HH:MM[:SS] */
+ if ((end - rest) > 2)
+ /* it is [0-9][0-9][0-9]+: */
+ {
+ break;
+ }
+ if ((end - rest) == 2)
+ tmp_hour = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ else {
+ tmp_hour = (rest[0]-'0');
+ }
+
+ /* move over the colon, and parse minutes */
+
+ rest = ++end;
+ while (*end >= '0' && *end <= '9') {
+ end++;
+ }
+
+ if (end == rest)
+ /* no digits after first colon? */
+ {
+ break;
+ }
+ if ((end - rest) > 2)
+ /* it is [0-9][0-9][0-9]+: */
+ {
+ break;
+ }
+ if ((end - rest) == 2)
+ tmp_min = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ else {
+ tmp_min = (rest[0]-'0');
+ }
+
+ /* now go for seconds */
+ rest = end;
+ if (*rest == ':') {
+ rest++;
+ }
+ end = rest;
+ while (*end >= '0' && *end <= '9') {
+ end++;
+ }
+
+ if (end == rest)
+ /* no digits after second colon - that's ok. */
+ ;
+ else if ((end - rest) > 2)
+ /* it is [0-9][0-9][0-9]+: */
+ {
+ break;
+ }
+ if ((end - rest) == 2)
+ tmp_sec = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ else {
+ tmp_sec = (rest[0]-'0');
+ }
+
+ /* If we made it here, we've parsed hour and min,
+ and possibly sec, so it worked as a unit. */
+
+ /* skip over whitespace and see if there's an AM or PM
+ directly following the time.
+ */
+ if (tmp_hour <= 12)
+ {
+ const char *s = end;
+ while (*s && (*s == ' ' || *s == '\t')) {
+ s++;
+ }
+ if ((s[0] == 'p' || s[0] == 'P') &&
+ (s[1] == 'm' || s[1] == 'M'))
+ /* 10:05pm == 22:05, and 12:05pm == 12:05 */
+ {
+ tmp_hour = (tmp_hour == 12 ? 12 : tmp_hour + 12);
+ }
+ else if (tmp_hour == 12 &&
+ (s[0] == 'a' || s[0] == 'A') &&
+ (s[1] == 'm' || s[1] == 'M'))
+ /* 12:05am == 00:05 */
+ {
+ tmp_hour = 0;
+ }
+ }
+
+ hour = tmp_hour;
+ min = tmp_min;
+ sec = tmp_sec;
+ rest = end;
+ break;
+ }
+ if ((*end == '/' || *end == '-') &&
+ end[1] >= '0' && end[1] <= '9')
+ {
+ /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95
+ or even 95-06-05...
+ #### But it doesn't handle 1995-06-22.
+ */
+ int n1, n2, n3;
+ const char *s;
+
+ if (month != TT_UNKNOWN)
+ /* if we saw a month name, this can't be. */
+ {
+ break;
+ }
+
+ s = rest;
+
+ n1 = (*s++ - '0'); /* first 1 or 2 digits */
+ if (*s >= '0' && *s <= '9') {
+ n1 = n1*10 + (*s++ - '0');
+ }
+
+ if (*s != '/' && *s != '-') { /* slash */
+ break;
+ }
+ s++;
+
+ if (*s < '0' || *s > '9') { /* second 1 or 2 digits */
+ break;
+ }
+ n2 = (*s++ - '0');
+ if (*s >= '0' && *s <= '9') {
+ n2 = n2*10 + (*s++ - '0');
+ }
+
+ if (*s != '/' && *s != '-') { /* slash */
+ break;
+ }
+ s++;
+
+ if (*s < '0' || *s > '9') { /* third 1, 2, 4, or 5 digits */
+ break;
+ }
+ n3 = (*s++ - '0');
+ if (*s >= '0' && *s <= '9') {
+ n3 = n3*10 + (*s++ - '0');
+ }
+
+ if (*s >= '0' && *s <= '9') /* optional digits 3, 4, and 5 */
+ {
+ n3 = n3*10 + (*s++ - '0');
+ if (*s < '0' || *s > '9') {
+ break;
+ }
+ n3 = n3*10 + (*s++ - '0');
+ if (*s >= '0' && *s <= '9') {
+ n3 = n3*10 + (*s++ - '0');
+ }
+ }
+
+ if ((*s >= '0' && *s <= '9') || /* followed by non-alphanum */
+ (*s >= 'A' && *s <= 'Z') ||
+ (*s >= 'a' && *s <= 'z')) {
+ break;
+ }
+
+ /* Ok, we parsed three 1-2 digit numbers, with / or -
+ between them. Now decide what the hell they are
+ (DD/MM/YY or MM/DD/YY or YY/MM/DD.)
+ */
+
+ if (n1 > 31 || n1 == 0) /* must be YY/MM/DD */
+ {
+ if (n2 > 12) {
+ break;
+ }
+ if (n3 > 31) {
+ break;
+ }
+ year = n1;
+ if (year < 70) {
+ year += 2000;
+ }
+ else if (year < 100) {
+ year += 1900;
+ }
+ month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
+ date = n3;
+ rest = s;
+ break;
+ }
+
+ if (n1 > 12 && n2 > 12) /* illegal */
+ {
+ rest = s;
+ break;
+ }
+
+ if (n3 < 70) {
+ n3 += 2000;
+ }
+ else if (n3 < 100) {
+ n3 += 1900;
+ }
+
+ if (n1 > 12) /* must be DD/MM/YY */
+ {
+ date = n1;
+ month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
+ year = n3;
+ }
+ else /* assume MM/DD/YY */
+ {
+ /* #### In the ambiguous case, should we consult the
+ locale to find out the local default? */
+ month = (TIME_TOKEN)(n1 + ((int)TT_JAN) - 1);
+ date = n2;
+ year = n3;
+ }
+ rest = s;
+ }
+ else if ((*end >= 'A' && *end <= 'Z') ||
+ (*end >= 'a' && *end <= 'z'))
+ /* Digits followed by non-punctuation - what's that? */
+ ;
+ else if ((end - rest) == 5) /* five digits is a year */
+ year = (year < 0
+ ? ((rest[0]-'0')*10000L +
+ (rest[1]-'0')*1000L +
+ (rest[2]-'0')*100L +
+ (rest[3]-'0')*10L +
+ (rest[4]-'0'))
+ : year);
+ else if ((end - rest) == 4) /* four digits is a year */
+ year = (year < 0
+ ? ((rest[0]-'0')*1000L +
+ (rest[1]-'0')*100L +
+ (rest[2]-'0')*10L +
+ (rest[3]-'0'))
+ : year);
+ else if ((end - rest) == 2) /* two digits - date or year */
+ {
+ int n = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ /* If we don't have a date (day of the month) and we see a number
+ less than 32, then assume that is the date.
+
+ Otherwise, if we have a date and not a year, assume this is the
+ year. If it is less than 70, then assume it refers to the 21st
+ century. If it is two digits (>= 70), assume it refers to this
+ century. Otherwise, assume it refers to an unambiguous year.
+
+ The world will surely end soon.
+ */
+ if (date < 0 && n < 32) {
+ date = n;
+ }
+ else if (year < 0)
+ {
+ if (n < 70) {
+ year = 2000 + n;
+ }
+ else if (n < 100) {
+ year = 1900 + n;
+ }
+ else {
+ year = n;
+ }
+ }
+ /* else what the hell is this. */
+ }
+ else if ((end - rest) == 1) { /* one digit - date */
+ date = (date < 0 ? (rest[0]-'0') : date);
+ }
+ /* else, three or more than five digits - what's that? */
+
+ break;
+ }
+ }
+
+ /* Skip to the end of this token, whether we parsed it or not.
+ Tokens are delimited by whitespace, or ,;-/
+ But explicitly not :+-.
+ */
+ while (*rest &&
+ *rest != ' ' && *rest != '\t' &&
+ *rest != ',' && *rest != ';' &&
+ *rest != '-' && *rest != '+' &&
+ *rest != '/' &&
+ *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']') {
+ rest++;
+ }
+ /* skip over uninteresting chars. */
+SKIP_MORE:
+ while (*rest &&
+ (*rest == ' ' || *rest == '\t' ||
+ *rest == ',' || *rest == ';' || *rest == '/' ||
+ *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']')) {
+ rest++;
+ }
+
+ /* "-" is ignored at the beginning of a token if we have not yet
+ parsed a year (e.g., the second "-" in "30-AUG-1966"), or if
+ the character after the dash is not a digit. */
+ if (*rest == '-' && ((rest > string &&
+ isalpha((unsigned char)rest[-1]) && year < 0) ||
+ rest[1] < '0' || rest[1] > '9'))
+ {
+ rest++;
+ goto SKIP_MORE;
+ }
+
+ }
+
+ if (zone != TT_UNKNOWN && zone_offset == -1)
+ {
+ switch (zone)
+ {
+ case TT_PST: zone_offset = -8 * 60; break;
+ case TT_PDT: zone_offset = -8 * 60; dst_offset = 1 * 60; break;
+ case TT_MST: zone_offset = -7 * 60; break;
+ case TT_MDT: zone_offset = -7 * 60; dst_offset = 1 * 60; break;
+ case TT_CST: zone_offset = -6 * 60; break;
+ case TT_CDT: zone_offset = -6 * 60; dst_offset = 1 * 60; break;
+ case TT_EST: zone_offset = -5 * 60; break;
+ case TT_EDT: zone_offset = -5 * 60; dst_offset = 1 * 60; break;
+ case TT_AST: zone_offset = -4 * 60; break;
+ case TT_NST: zone_offset = -3 * 60 - 30; break;
+ case TT_GMT: zone_offset = 0 * 60; break;
+ case TT_BST: zone_offset = 0 * 60; dst_offset = 1 * 60; break;
+ case TT_MET: zone_offset = 1 * 60; break;
+ case TT_EET: zone_offset = 2 * 60; break;
+ case TT_JST: zone_offset = 9 * 60; break;
+ default:
+ PR_ASSERT (0);
+ break;
+ }
+ }
+
+ /* If we didn't find a year, month, or day-of-the-month, we can't
+ possibly parse this, and in fact, mktime() will do something random
+ (I'm seeing it return "Tue Feb 5 06:28:16 2036", which is no doubt
+ a numerologically significant date... */
+ if (month == TT_UNKNOWN || date == -1 || year == -1 || year > PR_INT16_MAX) {
+ return PR_FAILURE;
+ }
+
+ memset(result, 0, sizeof(*result));
+ if (sec != -1) {
+ result->tm_sec = sec;
+ }
+ if (min != -1) {
+ result->tm_min = min;
+ }
+ if (hour != -1) {
+ result->tm_hour = hour;
+ }
+ if (date != -1) {
+ result->tm_mday = date;
+ }
+ if (month != TT_UNKNOWN) {
+ result->tm_month = (((int)month) - ((int)TT_JAN));
+ }
+ if (year != -1) {
+ result->tm_year = year;
+ }
+ if (dotw != TT_UNKNOWN) {
+ result->tm_wday = (((int)dotw) - ((int)TT_SUN));
+ }
+ /*
+ * Mainly to compute wday and yday, but normalized time is also required
+ * by the check below that works around a Visual C++ 2005 mktime problem.
+ */
+ PR_NormalizeTime(result, PR_GMTParameters);
+ /* The remaining work is to set the gmt and dst offsets in tm_params. */
+
+ if (zone == TT_UNKNOWN && default_to_gmt)
+ {
+ /* No zone was specified, so pretend the zone was GMT. */
+ zone = TT_GMT;
+ zone_offset = 0;
+ }
+
+ if (zone_offset == -1)
+ {
+ /* no zone was specified, and we're to assume that everything
+ is local. */
+ struct tm localTime;
+ time_t secs;
+
+ PR_ASSERT(result->tm_month > -1 &&
+ result->tm_mday > 0 &&
+ result->tm_hour > -1 &&
+ result->tm_min > -1 &&
+ result->tm_sec > -1);
+
+ /*
+ * To obtain time_t from a tm structure representing the local
+ * time, we call mktime(). However, we need to see if we are
+ * on 1-Jan-1970 or before. If we are, we can't call mktime()
+ * because mktime() will crash on win16. In that case, we
+ * calculate zone_offset based on the zone offset at
+ * 00:00:00, 2 Jan 1970 GMT, and subtract zone_offset from the
+ * date we are parsing to transform the date to GMT. We also
+ * do so if mktime() returns (time_t) -1 (time out of range).
+ */
+
+ /* month, day, hours, mins and secs are always non-negative
+ so we dont need to worry about them. */
+ if(result->tm_year >= 1970)
+ {
+ PRInt64 usec_per_sec;
+
+ localTime.tm_sec = result->tm_sec;
+ localTime.tm_min = result->tm_min;
+ localTime.tm_hour = result->tm_hour;
+ localTime.tm_mday = result->tm_mday;
+ localTime.tm_mon = result->tm_month;
+ localTime.tm_year = result->tm_year - 1900;
+ /* Set this to -1 to tell mktime "I don't care". If you set
+ it to 0 or 1, you are making assertions about whether the
+ date you are handing it is in daylight savings mode or not;
+ and if you're wrong, it will "fix" it for you. */
+ localTime.tm_isdst = -1;
+
+#if _MSC_VER == 1400 /* 1400 = Visual C++ 2005 (8.0) */
+ /*
+ * mktime will return (time_t) -1 if the input is a date
+ * after 23:59:59, December 31, 3000, US Pacific Time (not
+ * UTC as documented):
+ * http://msdn.microsoft.com/en-us/library/d1y53h2a(VS.80).aspx
+ * But if the year is 3001, mktime also invokes the invalid
+ * parameter handler, causing the application to crash. This
+ * problem has been reported in
+ * http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=266036.
+ * We avoid this crash by not calling mktime if the date is
+ * out of range. To use a simple test that works in any time
+ * zone, we consider year 3000 out of range as well. (See
+ * bug 480740.)
+ */
+ if (result->tm_year >= 3000) {
+ /* Emulate what mktime would have done. */
+ errno = EINVAL;
+ secs = (time_t) -1;
+ } else {
+ secs = mktime(&localTime);
+ }
+#else
+ secs = mktime(&localTime);
+#endif
+ if (secs != (time_t) -1)
+ {
+ PRTime usecs64;
+ LL_I2L(usecs64, secs);
+ LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
+ LL_MUL(usecs64, usecs64, usec_per_sec);
+ PR_ExplodeTime(usecs64, PR_LocalTimeParameters, result);
+ return PR_SUCCESS;
+ }
+ }
+
+ /* So mktime() can't handle this case. We assume the
+ zone_offset for the date we are parsing is the same as
+ the zone offset on 00:00:00 2 Jan 1970 GMT. */
+ secs = 86400;
+ localTimeResult = MT_safe_localtime(&secs, &localTime);
+ PR_ASSERT(localTimeResult != NULL);
+ if (localTimeResult == NULL) {
+ return PR_FAILURE;
+ }
+ zone_offset = localTime.tm_min
+ + 60 * localTime.tm_hour
+ + 1440 * (localTime.tm_mday - 2);
+ }
+
+ result->tm_params.tp_gmt_offset = zone_offset * 60;
+ result->tm_params.tp_dst_offset = dst_offset * 60;
+
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus)
+PR_ParseTimeString(
+ const char *string,
+ PRBool default_to_gmt,
+ PRTime *result)
+{
+ PRExplodedTime tm;
+ PRStatus rv;
+
+ rv = PR_ParseTimeStringToExplodedTime(string,
+ default_to_gmt,
+ &tm);
+ if (rv != PR_SUCCESS) {
+ return rv;
+ }
+
+ *result = PR_ImplodeTime(&tm);
+
+ return PR_SUCCESS;
+}
+
+/*
+ *******************************************************************
+ *******************************************************************
+ **
+ ** OLD COMPATIBILITY FUNCTIONS
+ **
+ *******************************************************************
+ *******************************************************************
+ */
+
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * PR_FormatTime --
+ *
+ * Format a time value into a buffer. Same semantics as strftime().
+ *
+ *-----------------------------------------------------------------------
+ */
+
+PR_IMPLEMENT(PRUint32)
+PR_FormatTime(char *buf, int buflen, const char *fmt,
+ const PRExplodedTime *time)
+{
+ size_t rv;
+ struct tm a;
+ struct tm *ap;
+
+ if (time) {
+ ap = &a;
+ a.tm_sec = time->tm_sec;
+ a.tm_min = time->tm_min;
+ a.tm_hour = time->tm_hour;
+ a.tm_mday = time->tm_mday;
+ a.tm_mon = time->tm_month;
+ a.tm_wday = time->tm_wday;
+ a.tm_year = time->tm_year - 1900;
+ a.tm_yday = time->tm_yday;
+ a.tm_isdst = time->tm_params.tp_dst_offset ? 1 : 0;
+
+ /*
+ * On some platforms, for example SunOS 4, struct tm has two
+ * additional fields: tm_zone and tm_gmtoff.
+ */
+
+#if (__GLIBC__ >= 2) || defined(NETBSD) \
+ || defined(OPENBSD) || defined(FREEBSD) \
+ || defined(DARWIN) || defined(ANDROID)
+ a.tm_zone = NULL;
+ a.tm_gmtoff = time->tm_params.tp_gmt_offset +
+ time->tm_params.tp_dst_offset;
+#endif
+ } else {
+ ap = NULL;
+ }
+
+ rv = strftime(buf, buflen, fmt, ap);
+ if (!rv && buf && buflen > 0) {
+ /*
+ * When strftime fails, the contents of buf are indeterminate.
+ * Some callers don't check the return value from this function,
+ * so store an empty string in buf in case they try to print it.
+ */
+ buf[0] = '\0';
+ }
+ return rv;
+}
+
+
+/*
+ * The following string arrays and macros are used by PR_FormatTimeUSEnglish().
+ */
+
+static const char* abbrevDays[] =
+{
+ "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
+};
+
+static const char* days[] =
+{
+ "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
+};
+
+static const char* abbrevMonths[] =
+{
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+static const char* months[] =
+{
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+};
+
+
+/*
+ * Add a single character to the given buffer, incrementing the buffer pointer
+ * and decrementing the buffer size. Return 0 on error.
+ */
+#define ADDCHAR( buf, bufSize, ch ) \
+do \
+{ \
+ if( bufSize < 1 ) \
+ { \
+ *(--buf) = '\0'; \
+ return 0; \
+ } \
+ *buf++ = ch; \
+ bufSize--; \
+} \
+while(0)
+
+
+/*
+ * Add a string to the given buffer, incrementing the buffer pointer
+ * and decrementing the buffer size appropriately. Return 0 on error.
+ */
+#define ADDSTR( buf, bufSize, str ) \
+do \
+{ \
+ PRUint32 strSize = strlen( str ); \
+ if( strSize > bufSize ) \
+ { \
+ if( bufSize==0 ) \
+ *(--buf) = '\0'; \
+ else \
+ *buf = '\0'; \
+ return 0; \
+ } \
+ memcpy(buf, str, strSize); \
+ buf += strSize; \
+ bufSize -= strSize; \
+} \
+while(0)
+
+/* Needed by PR_FormatTimeUSEnglish() */
+static unsigned int pr_WeekOfYear(const PRExplodedTime* time,
+ unsigned int firstDayOfWeek);
+
+
+/***********************************************************************************
+ *
+ * Description:
+ * This is a dumbed down version of strftime that will format the date in US
+ * English regardless of the setting of the global locale. This functionality is
+ * needed to write things like MIME headers which must always be in US English.
+ *
+ **********************************************************************************/
+
+PR_IMPLEMENT(PRUint32)
+PR_FormatTimeUSEnglish( char* buf, PRUint32 bufSize,
+ const char* format, const PRExplodedTime* time )
+{
+ char* bufPtr = buf;
+ const char* fmtPtr;
+ char tmpBuf[ 40 ];
+ const int tmpBufSize = sizeof( tmpBuf );
+
+
+ for( fmtPtr=format; *fmtPtr != '\0'; fmtPtr++ )
+ {
+ if( *fmtPtr != '%' )
+ {
+ ADDCHAR( bufPtr, bufSize, *fmtPtr );
+ }
+ else
+ {
+ switch( *(++fmtPtr) )
+ {
+ case '%':
+ /* escaped '%' character */
+ ADDCHAR( bufPtr, bufSize, '%' );
+ break;
+
+ case 'a':
+ /* abbreviated weekday name */
+ ADDSTR( bufPtr, bufSize, abbrevDays[ time->tm_wday ] );
+ break;
+
+ case 'A':
+ /* full weekday name */
+ ADDSTR( bufPtr, bufSize, days[ time->tm_wday ] );
+ break;
+
+ case 'b':
+ /* abbreviated month name */
+ ADDSTR( bufPtr, bufSize, abbrevMonths[ time->tm_month ] );
+ break;
+
+ case 'B':
+ /* full month name */
+ ADDSTR(bufPtr, bufSize, months[ time->tm_month ] );
+ break;
+
+ case 'c':
+ /* Date and time. */
+ PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%a %b %d %H:%M:%S %Y", time );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'd':
+ /* day of month ( 01 - 31 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_mday );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'H':
+ /* hour ( 00 - 23 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_hour );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'I':
+ /* hour ( 01 - 12 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",
+ (time->tm_hour%12) ? time->tm_hour%12 : (PRInt32) 12 );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'j':
+ /* day number of year ( 001 - 366 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.3d",time->tm_yday + 1);
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'm':
+ /* month number ( 01 - 12 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_month+1);
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'M':
+ /* minute ( 00 - 59 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_min );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'p':
+ /* locale's equivalent of either AM or PM */
+ ADDSTR( bufPtr, bufSize, (time->tm_hour<12)?"AM":"PM" );
+ break;
+
+ case 'S':
+ /* seconds ( 00 - 61 ), allows for leap seconds */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_sec );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'U':
+ /* week number of year ( 00 - 53 ), Sunday is the first day of week 1 */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 0 ) );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'w':
+ /* weekday number ( 0 - 6 ), Sunday = 0 */
+ PR_snprintf(tmpBuf,tmpBufSize,"%d",time->tm_wday );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'W':
+ /* Week number of year ( 00 - 53 ), Monday is the first day of week 1 */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 1 ) );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'x':
+ /* Date representation */
+ PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%m/%d/%y", time );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'X':
+ /* Time representation. */
+ PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%H:%M:%S", time );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'y':
+ /* year within century ( 00 - 99 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2d",time->tm_year % 100 );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'Y':
+ /* year as ccyy ( for example 1986 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.4d",time->tm_year );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'Z':
+ /* Time zone name or no characters if no time zone exists.
+ * Since time zone name is supposed to be independant of locale, we
+ * defer to PR_FormatTime() for this option.
+ */
+ PR_FormatTime( tmpBuf, tmpBufSize, "%Z", time );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ default:
+ /* Unknown format. Simply copy format into output buffer. */
+ ADDCHAR( bufPtr, bufSize, '%' );
+ ADDCHAR( bufPtr, bufSize, *fmtPtr );
+ break;
+
+ }
+ }
+ }
+
+ ADDCHAR( bufPtr, bufSize, '\0' );
+ return (PRUint32)(bufPtr - buf - 1);
+}
+
+
+
+/***********************************************************************************
+ *
+ * Description:
+ * Returns the week number of the year (0-53) for the given time. firstDayOfWeek
+ * is the day on which the week is considered to start (0=Sun, 1=Mon, ...).
+ * Week 1 starts the first time firstDayOfWeek occurs in the year. In other words,
+ * a partial week at the start of the year is considered week 0.
+ *
+ **********************************************************************************/
+
+static unsigned int
+pr_WeekOfYear(const PRExplodedTime* time, unsigned int firstDayOfWeek)
+{
+ int dayOfWeek;
+ int dayOfYear;
+
+ /* Get the day of the year for the given time then adjust it to represent the
+ * first day of the week containing the given time.
+ */
+ dayOfWeek = time->tm_wday - firstDayOfWeek;
+ if (dayOfWeek < 0) {
+ dayOfWeek += 7;
+ }
+
+ dayOfYear = time->tm_yday - dayOfWeek;
+
+ if( dayOfYear <= 0 )
+ {
+ /* If dayOfYear is <= 0, it is in the first partial week of the year. */
+ return 0;
+ }
+
+ /* Count the number of full weeks ( dayOfYear / 7 ) then add a week if there
+ * are any days left over ( dayOfYear % 7 ). Because we are only counting to
+ * the first day of the week containing the given time, rather than to the
+ * actual day representing the given time, any days in week 0 will be "absorbed"
+ * as extra days in the given week.
+ */
+ return (dayOfYear / 7) + ( (dayOfYear % 7) == 0 ? 0 : 1 );
+
+}
+
diff --git a/nsprpub/pr/src/misc/prtpool.c b/nsprpub/pr/src/misc/prtpool.c
new file mode 100644
index 0000000000..69f588ef6e
--- /dev/null
+++ b/nsprpub/pr/src/misc/prtpool.c
@@ -0,0 +1,1236 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+
+/*
+ * Thread pools
+ * Thread pools create and manage threads to provide support for
+ * scheduling jobs onto one or more threads.
+ *
+ */
+#ifdef OPT_WINNT
+#include <windows.h>
+#endif
+
+/*
+ * worker thread
+ */
+typedef struct wthread {
+ PRCList links;
+ PRThread *thread;
+} wthread;
+
+/*
+ * queue of timer jobs
+ */
+typedef struct timer_jobq {
+ PRCList list;
+ PRLock *lock;
+ PRCondVar *cv;
+ PRInt32 cnt;
+ PRCList wthreads;
+} timer_jobq;
+
+/*
+ * queue of jobs
+ */
+typedef struct tp_jobq {
+ PRCList list;
+ PRInt32 cnt;
+ PRLock *lock;
+ PRCondVar *cv;
+ PRCList wthreads;
+#ifdef OPT_WINNT
+ HANDLE nt_completion_port;
+#endif
+} tp_jobq;
+
+/*
+ * queue of IO jobs
+ */
+typedef struct io_jobq {
+ PRCList list;
+ PRPollDesc *pollfds;
+ PRInt32 npollfds;
+ PRJob **polljobs;
+ PRLock *lock;
+ PRInt32 cnt;
+ PRFileDesc *notify_fd;
+ PRCList wthreads;
+} io_jobq;
+
+/*
+ * Threadpool
+ */
+struct PRThreadPool {
+ PRInt32 init_threads;
+ PRInt32 max_threads;
+ PRInt32 current_threads;
+ PRInt32 idle_threads;
+ PRUint32 stacksize;
+ tp_jobq jobq;
+ io_jobq ioq;
+ timer_jobq timerq;
+ PRLock *join_lock; /* used with jobp->join_cv */
+ PRCondVar *shutdown_cv;
+ PRBool shutdown;
+};
+
+typedef enum io_op_type
+{ JOB_IO_READ, JOB_IO_WRITE, JOB_IO_CONNECT, JOB_IO_ACCEPT } io_op_type;
+
+#ifdef OPT_WINNT
+typedef struct NT_notifier {
+ OVERLAPPED overlapped; /* must be first */
+ PRJob *jobp;
+} NT_notifier;
+#endif
+
+struct PRJob {
+ PRCList links; /* for linking jobs */
+ PRBool on_ioq; /* job on ioq */
+ PRBool on_timerq; /* job on timerq */
+ PRJobFn job_func;
+ void *job_arg;
+ PRCondVar *join_cv;
+ PRBool join_wait; /* == PR_TRUE, when waiting to join */
+ PRCondVar *cancel_cv; /* for cancelling IO jobs */
+ PRBool cancel_io; /* for cancelling IO jobs */
+ PRThreadPool *tpool; /* back pointer to thread pool */
+ PRJobIoDesc *iod;
+ io_op_type io_op;
+ PRInt16 io_poll_flags;
+ PRNetAddr *netaddr;
+ PRIntervalTime timeout; /* relative value */
+ PRIntervalTime absolute;
+#ifdef OPT_WINNT
+ NT_notifier nt_notifier;
+#endif
+};
+
+#define JOB_LINKS_PTR(_qp) \
+ ((PRJob *) ((char *) (_qp) - offsetof(PRJob, links)))
+
+#define WTHREAD_LINKS_PTR(_qp) \
+ ((wthread *) ((char *) (_qp) - offsetof(wthread, links)))
+
+#define JOINABLE_JOB(_jobp) (NULL != (_jobp)->join_cv)
+
+#define JOIN_NOTIFY(_jobp) \
+ PR_BEGIN_MACRO \
+ PR_Lock(_jobp->tpool->join_lock); \
+ _jobp->join_wait = PR_FALSE; \
+ PR_NotifyCondVar(_jobp->join_cv); \
+ PR_Unlock(_jobp->tpool->join_lock); \
+ PR_END_MACRO
+
+#define CANCEL_IO_JOB(jobp) \
+ PR_BEGIN_MACRO \
+ jobp->cancel_io = PR_FALSE; \
+ jobp->on_ioq = PR_FALSE; \
+ PR_REMOVE_AND_INIT_LINK(&jobp->links); \
+ tp->ioq.cnt--; \
+ PR_NotifyCondVar(jobp->cancel_cv); \
+ PR_END_MACRO
+
+static void delete_job(PRJob *jobp);
+static PRThreadPool * alloc_threadpool(void);
+static PRJob * alloc_job(PRBool joinable, PRThreadPool *tp);
+static void notify_ioq(PRThreadPool *tp);
+static void notify_timerq(PRThreadPool *tp);
+
+/*
+ * locks are acquired in the following order
+ *
+ * tp->ioq.lock,tp->timerq.lock
+ * |
+ * V
+ * tp->jobq->lock
+ */
+
+/*
+ * worker thread function
+ */
+static void wstart(void *arg)
+{
+ PRThreadPool *tp = (PRThreadPool *) arg;
+ PRCList *head;
+
+ /*
+ * execute jobs until shutdown
+ */
+ while (!tp->shutdown) {
+ PRJob *jobp;
+#ifdef OPT_WINNT
+ BOOL rv;
+ DWORD unused, shutdown;
+ LPOVERLAPPED olp;
+
+ PR_Lock(tp->jobq.lock);
+ tp->idle_threads++;
+ PR_Unlock(tp->jobq.lock);
+ rv = GetQueuedCompletionStatus(tp->jobq.nt_completion_port,
+ &unused, &shutdown, &olp, INFINITE);
+
+ PR_ASSERT(rv);
+ if (shutdown) {
+ break;
+ }
+ jobp = ((NT_notifier *) olp)->jobp;
+ PR_Lock(tp->jobq.lock);
+ tp->idle_threads--;
+ tp->jobq.cnt--;
+ PR_Unlock(tp->jobq.lock);
+#else
+
+ PR_Lock(tp->jobq.lock);
+ while (PR_CLIST_IS_EMPTY(&tp->jobq.list) && (!tp->shutdown)) {
+ tp->idle_threads++;
+ PR_WaitCondVar(tp->jobq.cv, PR_INTERVAL_NO_TIMEOUT);
+ tp->idle_threads--;
+ }
+ if (tp->shutdown) {
+ PR_Unlock(tp->jobq.lock);
+ break;
+ }
+ head = PR_LIST_HEAD(&tp->jobq.list);
+ /*
+ * remove job from queue
+ */
+ PR_REMOVE_AND_INIT_LINK(head);
+ tp->jobq.cnt--;
+ jobp = JOB_LINKS_PTR(head);
+ PR_Unlock(tp->jobq.lock);
+#endif
+
+ jobp->job_func(jobp->job_arg);
+ if (!JOINABLE_JOB(jobp)) {
+ delete_job(jobp);
+ } else {
+ JOIN_NOTIFY(jobp);
+ }
+ }
+ PR_Lock(tp->jobq.lock);
+ tp->current_threads--;
+ PR_Unlock(tp->jobq.lock);
+}
+
+/*
+ * add a job to the work queue
+ */
+static void
+add_to_jobq(PRThreadPool *tp, PRJob *jobp)
+{
+ /*
+ * add to jobq
+ */
+#ifdef OPT_WINNT
+ PR_Lock(tp->jobq.lock);
+ tp->jobq.cnt++;
+ PR_Unlock(tp->jobq.lock);
+ /*
+ * notify worker thread(s)
+ */
+ PostQueuedCompletionStatus(tp->jobq.nt_completion_port, 0,
+ FALSE, &jobp->nt_notifier.overlapped);
+#else
+ PR_Lock(tp->jobq.lock);
+ PR_APPEND_LINK(&jobp->links,&tp->jobq.list);
+ tp->jobq.cnt++;
+ if ((tp->idle_threads < tp->jobq.cnt) &&
+ (tp->current_threads < tp->max_threads)) {
+ wthread *wthrp;
+ /*
+ * increment thread count and unlock the jobq lock
+ */
+ tp->current_threads++;
+ PR_Unlock(tp->jobq.lock);
+ /* create new worker thread */
+ wthrp = PR_NEWZAP(wthread);
+ if (wthrp) {
+ wthrp->thread = PR_CreateThread(PR_USER_THREAD, wstart,
+ tp, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,PR_JOINABLE_THREAD,tp->stacksize);
+ if (NULL == wthrp->thread) {
+ PR_DELETE(wthrp); /* this sets wthrp to NULL */
+ }
+ }
+ PR_Lock(tp->jobq.lock);
+ if (NULL == wthrp) {
+ tp->current_threads--;
+ } else {
+ PR_APPEND_LINK(&wthrp->links, &tp->jobq.wthreads);
+ }
+ }
+ /*
+ * wakeup a worker thread
+ */
+ PR_NotifyCondVar(tp->jobq.cv);
+ PR_Unlock(tp->jobq.lock);
+#endif
+}
+
+/*
+ * io worker thread function
+ */
+static void io_wstart(void *arg)
+{
+ PRThreadPool *tp = (PRThreadPool *) arg;
+ int pollfd_cnt, pollfds_used;
+ int rv;
+ PRCList *qp, *nextqp;
+ PRPollDesc *pollfds = NULL;
+ PRJob **polljobs = NULL;
+ int poll_timeout;
+ PRIntervalTime now;
+
+ /*
+ * scan io_jobq
+ * construct poll list
+ * call PR_Poll
+ * for all fds, for which poll returns true, move the job to
+ * jobq and wakeup worker thread.
+ */
+ while (!tp->shutdown) {
+ PRJob *jobp;
+
+ pollfd_cnt = tp->ioq.cnt + 10;
+ if (pollfd_cnt > tp->ioq.npollfds) {
+
+ /*
+ * re-allocate pollfd array if the current one is not large
+ * enough
+ */
+ if (NULL != tp->ioq.pollfds) {
+ PR_Free(tp->ioq.pollfds);
+ }
+ tp->ioq.pollfds = (PRPollDesc *) PR_Malloc(pollfd_cnt *
+ (sizeof(PRPollDesc) + sizeof(PRJob *)));
+ PR_ASSERT(NULL != tp->ioq.pollfds);
+ /*
+ * array of pollfds
+ */
+ pollfds = tp->ioq.pollfds;
+ tp->ioq.polljobs = (PRJob **) (&tp->ioq.pollfds[pollfd_cnt]);
+ /*
+ * parallel array of jobs
+ */
+ polljobs = tp->ioq.polljobs;
+ tp->ioq.npollfds = pollfd_cnt;
+ }
+
+ pollfds_used = 0;
+ /*
+ * add the notify fd; used for unblocking io thread(s)
+ */
+ pollfds[pollfds_used].fd = tp->ioq.notify_fd;
+ pollfds[pollfds_used].in_flags = PR_POLL_READ;
+ pollfds[pollfds_used].out_flags = 0;
+ polljobs[pollfds_used] = NULL;
+ pollfds_used++;
+ /*
+ * fill in the pollfd array
+ */
+ PR_Lock(tp->ioq.lock);
+ for (qp = tp->ioq.list.next; qp != &tp->ioq.list; qp = nextqp) {
+ nextqp = qp->next;
+ jobp = JOB_LINKS_PTR(qp);
+ if (jobp->cancel_io) {
+ CANCEL_IO_JOB(jobp);
+ continue;
+ }
+ if (pollfds_used == (pollfd_cnt)) {
+ break;
+ }
+ pollfds[pollfds_used].fd = jobp->iod->socket;
+ pollfds[pollfds_used].in_flags = jobp->io_poll_flags;
+ pollfds[pollfds_used].out_flags = 0;
+ polljobs[pollfds_used] = jobp;
+
+ pollfds_used++;
+ }
+ if (!PR_CLIST_IS_EMPTY(&tp->ioq.list)) {
+ qp = tp->ioq.list.next;
+ jobp = JOB_LINKS_PTR(qp);
+ if (PR_INTERVAL_NO_TIMEOUT == jobp->timeout) {
+ poll_timeout = PR_INTERVAL_NO_TIMEOUT;
+ }
+ else if (PR_INTERVAL_NO_WAIT == jobp->timeout) {
+ poll_timeout = PR_INTERVAL_NO_WAIT;
+ }
+ else {
+ poll_timeout = jobp->absolute - PR_IntervalNow();
+ if (poll_timeout <= 0) { /* already timed out */
+ poll_timeout = PR_INTERVAL_NO_WAIT;
+ }
+ }
+ } else {
+ poll_timeout = PR_INTERVAL_NO_TIMEOUT;
+ }
+ PR_Unlock(tp->ioq.lock);
+
+ /*
+ * XXXX
+ * should retry if more jobs have been added to the queue?
+ *
+ */
+ PR_ASSERT(pollfds_used <= pollfd_cnt);
+ rv = PR_Poll(tp->ioq.pollfds, pollfds_used, poll_timeout);
+
+ if (tp->shutdown) {
+ break;
+ }
+
+ if (rv > 0) {
+ /*
+ * at least one io event is set
+ */
+ PRStatus rval_status;
+ PRInt32 index;
+
+ PR_ASSERT(pollfds[0].fd == tp->ioq.notify_fd);
+ /*
+ * reset the pollable event, if notified
+ */
+ if (pollfds[0].out_flags & PR_POLL_READ) {
+ rval_status = PR_WaitForPollableEvent(tp->ioq.notify_fd);
+ PR_ASSERT(PR_SUCCESS == rval_status);
+ }
+
+ for(index = 1; index < (pollfds_used); index++) {
+ PRInt16 events = pollfds[index].in_flags;
+ PRInt16 revents = pollfds[index].out_flags;
+ jobp = polljobs[index];
+
+ if ((revents & PR_POLL_NVAL) || /* busted in all cases */
+ (revents & PR_POLL_ERR) ||
+ ((events & PR_POLL_WRITE) &&
+ (revents & PR_POLL_HUP))) { /* write op & hup */
+ PR_Lock(tp->ioq.lock);
+ if (jobp->cancel_io) {
+ CANCEL_IO_JOB(jobp);
+ PR_Unlock(tp->ioq.lock);
+ continue;
+ }
+ PR_REMOVE_AND_INIT_LINK(&jobp->links);
+ tp->ioq.cnt--;
+ jobp->on_ioq = PR_FALSE;
+ PR_Unlock(tp->ioq.lock);
+
+ /* set error */
+ if (PR_POLL_NVAL & revents) {
+ jobp->iod->error = PR_BAD_DESCRIPTOR_ERROR;
+ }
+ else if (PR_POLL_HUP & revents) {
+ jobp->iod->error = PR_CONNECT_RESET_ERROR;
+ }
+ else {
+ jobp->iod->error = PR_IO_ERROR;
+ }
+
+ /*
+ * add to jobq
+ */
+ add_to_jobq(tp, jobp);
+ } else if (revents) {
+ /*
+ * add to jobq
+ */
+ PR_Lock(tp->ioq.lock);
+ if (jobp->cancel_io) {
+ CANCEL_IO_JOB(jobp);
+ PR_Unlock(tp->ioq.lock);
+ continue;
+ }
+ PR_REMOVE_AND_INIT_LINK(&jobp->links);
+ tp->ioq.cnt--;
+ jobp->on_ioq = PR_FALSE;
+ PR_Unlock(tp->ioq.lock);
+
+ if (jobp->io_op == JOB_IO_CONNECT) {
+ if (PR_GetConnectStatus(&pollfds[index]) == PR_SUCCESS) {
+ jobp->iod->error = 0;
+ }
+ else {
+ jobp->iod->error = PR_GetError();
+ }
+ } else {
+ jobp->iod->error = 0;
+ }
+
+ add_to_jobq(tp, jobp);
+ }
+ }
+ }
+ /*
+ * timeout processing
+ */
+ now = PR_IntervalNow();
+ PR_Lock(tp->ioq.lock);
+ for (qp = tp->ioq.list.next; qp != &tp->ioq.list; qp = nextqp) {
+ nextqp = qp->next;
+ jobp = JOB_LINKS_PTR(qp);
+ if (jobp->cancel_io) {
+ CANCEL_IO_JOB(jobp);
+ continue;
+ }
+ if (PR_INTERVAL_NO_TIMEOUT == jobp->timeout) {
+ break;
+ }
+ if ((PR_INTERVAL_NO_WAIT != jobp->timeout) &&
+ ((PRInt32)(jobp->absolute - now) > 0)) {
+ break;
+ }
+ PR_REMOVE_AND_INIT_LINK(&jobp->links);
+ tp->ioq.cnt--;
+ jobp->on_ioq = PR_FALSE;
+ jobp->iod->error = PR_IO_TIMEOUT_ERROR;
+ add_to_jobq(tp, jobp);
+ }
+ PR_Unlock(tp->ioq.lock);
+ }
+}
+
+/*
+ * timer worker thread function
+ */
+static void timer_wstart(void *arg)
+{
+ PRThreadPool *tp = (PRThreadPool *) arg;
+ PRCList *qp;
+ PRIntervalTime timeout;
+ PRIntervalTime now;
+
+ /*
+ * call PR_WaitCondVar with minimum value of all timeouts
+ */
+ while (!tp->shutdown) {
+ PRJob *jobp;
+
+ PR_Lock(tp->timerq.lock);
+ if (PR_CLIST_IS_EMPTY(&tp->timerq.list)) {
+ timeout = PR_INTERVAL_NO_TIMEOUT;
+ } else {
+ PRCList *qp;
+
+ qp = tp->timerq.list.next;
+ jobp = JOB_LINKS_PTR(qp);
+
+ timeout = jobp->absolute - PR_IntervalNow();
+ if (timeout <= 0) {
+ timeout = PR_INTERVAL_NO_WAIT; /* already timed out */
+ }
+ }
+ if (PR_INTERVAL_NO_WAIT != timeout) {
+ PR_WaitCondVar(tp->timerq.cv, timeout);
+ }
+ if (tp->shutdown) {
+ PR_Unlock(tp->timerq.lock);
+ break;
+ }
+ /*
+ * move expired-timer jobs to jobq
+ */
+ now = PR_IntervalNow();
+ while (!PR_CLIST_IS_EMPTY(&tp->timerq.list)) {
+ qp = tp->timerq.list.next;
+ jobp = JOB_LINKS_PTR(qp);
+
+ if ((PRInt32)(jobp->absolute - now) > 0) {
+ break;
+ }
+ /*
+ * job timed out
+ */
+ PR_REMOVE_AND_INIT_LINK(&jobp->links);
+ tp->timerq.cnt--;
+ jobp->on_timerq = PR_FALSE;
+ add_to_jobq(tp, jobp);
+ }
+ PR_Unlock(tp->timerq.lock);
+ }
+}
+
+static void
+delete_threadpool(PRThreadPool *tp)
+{
+ if (NULL != tp) {
+ if (NULL != tp->shutdown_cv) {
+ PR_DestroyCondVar(tp->shutdown_cv);
+ }
+ if (NULL != tp->jobq.cv) {
+ PR_DestroyCondVar(tp->jobq.cv);
+ }
+ if (NULL != tp->jobq.lock) {
+ PR_DestroyLock(tp->jobq.lock);
+ }
+ if (NULL != tp->join_lock) {
+ PR_DestroyLock(tp->join_lock);
+ }
+#ifdef OPT_WINNT
+ if (NULL != tp->jobq.nt_completion_port) {
+ CloseHandle(tp->jobq.nt_completion_port);
+ }
+#endif
+ /* Timer queue */
+ if (NULL != tp->timerq.cv) {
+ PR_DestroyCondVar(tp->timerq.cv);
+ }
+ if (NULL != tp->timerq.lock) {
+ PR_DestroyLock(tp->timerq.lock);
+ }
+
+ if (NULL != tp->ioq.lock) {
+ PR_DestroyLock(tp->ioq.lock);
+ }
+ if (NULL != tp->ioq.pollfds) {
+ PR_Free(tp->ioq.pollfds);
+ }
+ if (NULL != tp->ioq.notify_fd) {
+ PR_DestroyPollableEvent(tp->ioq.notify_fd);
+ }
+ PR_Free(tp);
+ }
+ return;
+}
+
+static PRThreadPool *
+alloc_threadpool(void)
+{
+ PRThreadPool *tp;
+
+ tp = (PRThreadPool *) PR_CALLOC(sizeof(*tp));
+ if (NULL == tp) {
+ goto failed;
+ }
+ tp->jobq.lock = PR_NewLock();
+ if (NULL == tp->jobq.lock) {
+ goto failed;
+ }
+ tp->jobq.cv = PR_NewCondVar(tp->jobq.lock);
+ if (NULL == tp->jobq.cv) {
+ goto failed;
+ }
+ tp->join_lock = PR_NewLock();
+ if (NULL == tp->join_lock) {
+ goto failed;
+ }
+#ifdef OPT_WINNT
+ tp->jobq.nt_completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
+ NULL, 0, 0);
+ if (NULL == tp->jobq.nt_completion_port) {
+ goto failed;
+ }
+#endif
+
+ tp->ioq.lock = PR_NewLock();
+ if (NULL == tp->ioq.lock) {
+ goto failed;
+ }
+
+ /* Timer queue */
+
+ tp->timerq.lock = PR_NewLock();
+ if (NULL == tp->timerq.lock) {
+ goto failed;
+ }
+ tp->timerq.cv = PR_NewCondVar(tp->timerq.lock);
+ if (NULL == tp->timerq.cv) {
+ goto failed;
+ }
+
+ tp->shutdown_cv = PR_NewCondVar(tp->jobq.lock);
+ if (NULL == tp->shutdown_cv) {
+ goto failed;
+ }
+ tp->ioq.notify_fd = PR_NewPollableEvent();
+ if (NULL == tp->ioq.notify_fd) {
+ goto failed;
+ }
+ return tp;
+failed:
+ delete_threadpool(tp);
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+}
+
+/* Create thread pool */
+PR_IMPLEMENT(PRThreadPool *)
+PR_CreateThreadPool(PRInt32 initial_threads, PRInt32 max_threads,
+ PRUint32 stacksize)
+{
+ PRThreadPool *tp;
+ PRThread *thr;
+ int i;
+ wthread *wthrp;
+
+ tp = alloc_threadpool();
+ if (NULL == tp) {
+ return NULL;
+ }
+
+ tp->init_threads = initial_threads;
+ tp->max_threads = max_threads;
+ tp->stacksize = stacksize;
+ PR_INIT_CLIST(&tp->jobq.list);
+ PR_INIT_CLIST(&tp->ioq.list);
+ PR_INIT_CLIST(&tp->timerq.list);
+ PR_INIT_CLIST(&tp->jobq.wthreads);
+ PR_INIT_CLIST(&tp->ioq.wthreads);
+ PR_INIT_CLIST(&tp->timerq.wthreads);
+ tp->shutdown = PR_FALSE;
+
+ PR_Lock(tp->jobq.lock);
+ for(i=0; i < initial_threads; ++i) {
+
+ thr = PR_CreateThread(PR_USER_THREAD, wstart,
+ tp, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,stacksize);
+ PR_ASSERT(thr);
+ wthrp = PR_NEWZAP(wthread);
+ PR_ASSERT(wthrp);
+ wthrp->thread = thr;
+ PR_APPEND_LINK(&wthrp->links, &tp->jobq.wthreads);
+ }
+ tp->current_threads = initial_threads;
+
+ thr = PR_CreateThread(PR_USER_THREAD, io_wstart,
+ tp, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,PR_JOINABLE_THREAD,stacksize);
+ PR_ASSERT(thr);
+ wthrp = PR_NEWZAP(wthread);
+ PR_ASSERT(wthrp);
+ wthrp->thread = thr;
+ PR_APPEND_LINK(&wthrp->links, &tp->ioq.wthreads);
+
+ thr = PR_CreateThread(PR_USER_THREAD, timer_wstart,
+ tp, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,PR_JOINABLE_THREAD,stacksize);
+ PR_ASSERT(thr);
+ wthrp = PR_NEWZAP(wthread);
+ PR_ASSERT(wthrp);
+ wthrp->thread = thr;
+ PR_APPEND_LINK(&wthrp->links, &tp->timerq.wthreads);
+
+ PR_Unlock(tp->jobq.lock);
+ return tp;
+}
+
+static void
+delete_job(PRJob *jobp)
+{
+ if (NULL != jobp) {
+ if (NULL != jobp->join_cv) {
+ PR_DestroyCondVar(jobp->join_cv);
+ jobp->join_cv = NULL;
+ }
+ if (NULL != jobp->cancel_cv) {
+ PR_DestroyCondVar(jobp->cancel_cv);
+ jobp->cancel_cv = NULL;
+ }
+ PR_DELETE(jobp);
+ }
+}
+
+static PRJob *
+alloc_job(PRBool joinable, PRThreadPool *tp)
+{
+ PRJob *jobp;
+
+ jobp = PR_NEWZAP(PRJob);
+ if (NULL == jobp) {
+ goto failed;
+ }
+ if (joinable) {
+ jobp->join_cv = PR_NewCondVar(tp->join_lock);
+ jobp->join_wait = PR_TRUE;
+ if (NULL == jobp->join_cv) {
+ goto failed;
+ }
+ } else {
+ jobp->join_cv = NULL;
+ }
+#ifdef OPT_WINNT
+ jobp->nt_notifier.jobp = jobp;
+#endif
+ return jobp;
+failed:
+ delete_job(jobp);
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+}
+
+/* queue a job */
+PR_IMPLEMENT(PRJob *)
+PR_QueueJob(PRThreadPool *tpool, PRJobFn fn, void *arg, PRBool joinable)
+{
+ PRJob *jobp;
+
+ jobp = alloc_job(joinable, tpool);
+ if (NULL == jobp) {
+ return NULL;
+ }
+
+ jobp->job_func = fn;
+ jobp->job_arg = arg;
+ jobp->tpool = tpool;
+
+ add_to_jobq(tpool, jobp);
+ return jobp;
+}
+
+/* queue a job, when a socket is readable or writeable */
+static PRJob *
+queue_io_job(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn, void * arg,
+ PRBool joinable, io_op_type op)
+{
+ PRJob *jobp;
+ PRIntervalTime now;
+
+ jobp = alloc_job(joinable, tpool);
+ if (NULL == jobp) {
+ return NULL;
+ }
+
+ /*
+ * Add a new job to io_jobq
+ * wakeup io worker thread
+ */
+
+ jobp->job_func = fn;
+ jobp->job_arg = arg;
+ jobp->tpool = tpool;
+ jobp->iod = iod;
+ if (JOB_IO_READ == op) {
+ jobp->io_op = JOB_IO_READ;
+ jobp->io_poll_flags = PR_POLL_READ;
+ } else if (JOB_IO_WRITE == op) {
+ jobp->io_op = JOB_IO_WRITE;
+ jobp->io_poll_flags = PR_POLL_WRITE;
+ } else if (JOB_IO_ACCEPT == op) {
+ jobp->io_op = JOB_IO_ACCEPT;
+ jobp->io_poll_flags = PR_POLL_READ;
+ } else if (JOB_IO_CONNECT == op) {
+ jobp->io_op = JOB_IO_CONNECT;
+ jobp->io_poll_flags = PR_POLL_WRITE|PR_POLL_EXCEPT;
+ } else {
+ delete_job(jobp);
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+
+ jobp->timeout = iod->timeout;
+ if ((PR_INTERVAL_NO_TIMEOUT == iod->timeout) ||
+ (PR_INTERVAL_NO_WAIT == iod->timeout)) {
+ jobp->absolute = iod->timeout;
+ } else {
+ now = PR_IntervalNow();
+ jobp->absolute = now + iod->timeout;
+ }
+
+
+ PR_Lock(tpool->ioq.lock);
+
+ if (PR_CLIST_IS_EMPTY(&tpool->ioq.list) ||
+ (PR_INTERVAL_NO_TIMEOUT == iod->timeout)) {
+ PR_APPEND_LINK(&jobp->links,&tpool->ioq.list);
+ } else if (PR_INTERVAL_NO_WAIT == iod->timeout) {
+ PR_INSERT_LINK(&jobp->links,&tpool->ioq.list);
+ } else {
+ PRCList *qp;
+ PRJob *tmp_jobp;
+ /*
+ * insert into the timeout-sorted ioq
+ */
+ for (qp = tpool->ioq.list.prev; qp != &tpool->ioq.list;
+ qp = qp->prev) {
+ tmp_jobp = JOB_LINKS_PTR(qp);
+ if ((PRInt32)(jobp->absolute - tmp_jobp->absolute) >= 0) {
+ break;
+ }
+ }
+ PR_INSERT_AFTER(&jobp->links,qp);
+ }
+
+ jobp->on_ioq = PR_TRUE;
+ tpool->ioq.cnt++;
+ /*
+ * notify io worker thread(s)
+ */
+ PR_Unlock(tpool->ioq.lock);
+ notify_ioq(tpool);
+ return jobp;
+}
+
+/* queue a job, when a socket is readable */
+PR_IMPLEMENT(PRJob *)
+PR_QueueJob_Read(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn, void * arg,
+ PRBool joinable)
+{
+ return (queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_READ));
+}
+
+/* queue a job, when a socket is writeable */
+PR_IMPLEMENT(PRJob *)
+PR_QueueJob_Write(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn,void * arg,
+ PRBool joinable)
+{
+ return (queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_WRITE));
+}
+
+
+/* queue a job, when a socket has a pending connection */
+PR_IMPLEMENT(PRJob *)
+PR_QueueJob_Accept(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn,
+ void * arg, PRBool joinable)
+{
+ return (queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_ACCEPT));
+}
+
+/* queue a job, when a socket can be connected */
+PR_IMPLEMENT(PRJob *)
+PR_QueueJob_Connect(PRThreadPool *tpool, PRJobIoDesc *iod,
+ const PRNetAddr *addr, PRJobFn fn, void * arg, PRBool joinable)
+{
+ PRStatus rv;
+ PRErrorCode err;
+
+ rv = PR_Connect(iod->socket, addr, PR_INTERVAL_NO_WAIT);
+ if ((rv == PR_FAILURE) && ((err = PR_GetError()) == PR_IN_PROGRESS_ERROR)) {
+ /* connection pending */
+ return(queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_CONNECT));
+ }
+ /*
+ * connection succeeded or failed; add to jobq right away
+ */
+ if (rv == PR_FAILURE) {
+ iod->error = err;
+ }
+ else {
+ iod->error = 0;
+ }
+ return(PR_QueueJob(tpool, fn, arg, joinable));
+
+}
+
+/* queue a job, when a timer expires */
+PR_IMPLEMENT(PRJob *)
+PR_QueueJob_Timer(PRThreadPool *tpool, PRIntervalTime timeout,
+ PRJobFn fn, void * arg, PRBool joinable)
+{
+ PRIntervalTime now;
+ PRJob *jobp;
+
+ if (PR_INTERVAL_NO_TIMEOUT == timeout) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+ if (PR_INTERVAL_NO_WAIT == timeout) {
+ /*
+ * no waiting; add to jobq right away
+ */
+ return(PR_QueueJob(tpool, fn, arg, joinable));
+ }
+ jobp = alloc_job(joinable, tpool);
+ if (NULL == jobp) {
+ return NULL;
+ }
+
+ /*
+ * Add a new job to timer_jobq
+ * wakeup timer worker thread
+ */
+
+ jobp->job_func = fn;
+ jobp->job_arg = arg;
+ jobp->tpool = tpool;
+ jobp->timeout = timeout;
+
+ now = PR_IntervalNow();
+ jobp->absolute = now + timeout;
+
+
+ PR_Lock(tpool->timerq.lock);
+ jobp->on_timerq = PR_TRUE;
+ if (PR_CLIST_IS_EMPTY(&tpool->timerq.list)) {
+ PR_APPEND_LINK(&jobp->links,&tpool->timerq.list);
+ }
+ else {
+ PRCList *qp;
+ PRJob *tmp_jobp;
+ /*
+ * insert into the sorted timer jobq
+ */
+ for (qp = tpool->timerq.list.prev; qp != &tpool->timerq.list;
+ qp = qp->prev) {
+ tmp_jobp = JOB_LINKS_PTR(qp);
+ if ((PRInt32)(jobp->absolute - tmp_jobp->absolute) >= 0) {
+ break;
+ }
+ }
+ PR_INSERT_AFTER(&jobp->links,qp);
+ }
+ tpool->timerq.cnt++;
+ /*
+ * notify timer worker thread(s)
+ */
+ notify_timerq(tpool);
+ PR_Unlock(tpool->timerq.lock);
+ return jobp;
+}
+
+static void
+notify_timerq(PRThreadPool *tp)
+{
+ /*
+ * wakeup the timer thread(s)
+ */
+ PR_NotifyCondVar(tp->timerq.cv);
+}
+
+static void
+notify_ioq(PRThreadPool *tp)
+{
+ PRStatus rval_status;
+
+ /*
+ * wakeup the io thread(s)
+ */
+ rval_status = PR_SetPollableEvent(tp->ioq.notify_fd);
+ PR_ASSERT(PR_SUCCESS == rval_status);
+}
+
+/*
+ * cancel a job
+ *
+ * XXXX: is this needed? likely to be removed
+ */
+PR_IMPLEMENT(PRStatus)
+PR_CancelJob(PRJob *jobp) {
+
+ PRStatus rval = PR_FAILURE;
+ PRThreadPool *tp;
+
+ if (jobp->on_timerq) {
+ /*
+ * now, check again while holding the timerq lock
+ */
+ tp = jobp->tpool;
+ PR_Lock(tp->timerq.lock);
+ if (jobp->on_timerq) {
+ jobp->on_timerq = PR_FALSE;
+ PR_REMOVE_AND_INIT_LINK(&jobp->links);
+ tp->timerq.cnt--;
+ PR_Unlock(tp->timerq.lock);
+ if (!JOINABLE_JOB(jobp)) {
+ delete_job(jobp);
+ } else {
+ JOIN_NOTIFY(jobp);
+ }
+ rval = PR_SUCCESS;
+ } else {
+ PR_Unlock(tp->timerq.lock);
+ }
+ } else if (jobp->on_ioq) {
+ /*
+ * now, check again while holding the ioq lock
+ */
+ tp = jobp->tpool;
+ PR_Lock(tp->ioq.lock);
+ if (jobp->on_ioq) {
+ jobp->cancel_cv = PR_NewCondVar(tp->ioq.lock);
+ if (NULL == jobp->cancel_cv) {
+ PR_Unlock(tp->ioq.lock);
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ return PR_FAILURE;
+ }
+ /*
+ * mark job 'cancelled' and notify io thread(s)
+ * XXXX:
+ * this assumes there is only one io thread; when there
+ * are multiple threads, the io thread processing this job
+ * must be notified.
+ */
+ jobp->cancel_io = PR_TRUE;
+ PR_Unlock(tp->ioq.lock); /* release, reacquire ioq lock */
+ notify_ioq(tp);
+ PR_Lock(tp->ioq.lock);
+ while (jobp->cancel_io) {
+ PR_WaitCondVar(jobp->cancel_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(tp->ioq.lock);
+ PR_ASSERT(!jobp->on_ioq);
+ if (!JOINABLE_JOB(jobp)) {
+ delete_job(jobp);
+ } else {
+ JOIN_NOTIFY(jobp);
+ }
+ rval = PR_SUCCESS;
+ } else {
+ PR_Unlock(tp->ioq.lock);
+ }
+ }
+ if (PR_FAILURE == rval) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ }
+ return rval;
+}
+
+/* join a job, wait until completion */
+PR_IMPLEMENT(PRStatus)
+PR_JoinJob(PRJob *jobp)
+{
+ if (!JOINABLE_JOB(jobp)) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ PR_Lock(jobp->tpool->join_lock);
+ while(jobp->join_wait) {
+ PR_WaitCondVar(jobp->join_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(jobp->tpool->join_lock);
+ delete_job(jobp);
+ return PR_SUCCESS;
+}
+
+/* shutdown threadpool */
+PR_IMPLEMENT(PRStatus)
+PR_ShutdownThreadPool(PRThreadPool *tpool)
+{
+ PRStatus rval = PR_SUCCESS;
+
+ PR_Lock(tpool->jobq.lock);
+ tpool->shutdown = PR_TRUE;
+ PR_NotifyAllCondVar(tpool->shutdown_cv);
+ PR_Unlock(tpool->jobq.lock);
+
+ return rval;
+}
+
+/*
+ * join thread pool
+ * wait for termination of worker threads
+ * reclaim threadpool resources
+ */
+PR_IMPLEMENT(PRStatus)
+PR_JoinThreadPool(PRThreadPool *tpool)
+{
+ PRStatus rval = PR_SUCCESS;
+ PRCList *head;
+ PRStatus rval_status;
+
+ PR_Lock(tpool->jobq.lock);
+ while (!tpool->shutdown) {
+ PR_WaitCondVar(tpool->shutdown_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ /*
+ * wakeup worker threads
+ */
+#ifdef OPT_WINNT
+ /*
+ * post shutdown notification for all threads
+ */
+ {
+ int i;
+ for(i=0; i < tpool->current_threads; i++) {
+ PostQueuedCompletionStatus(tpool->jobq.nt_completion_port, 0,
+ TRUE, NULL);
+ }
+ }
+#else
+ PR_NotifyAllCondVar(tpool->jobq.cv);
+#endif
+
+ /*
+ * wakeup io thread(s)
+ */
+ notify_ioq(tpool);
+
+ /*
+ * wakeup timer thread(s)
+ */
+ PR_Lock(tpool->timerq.lock);
+ notify_timerq(tpool);
+ PR_Unlock(tpool->timerq.lock);
+
+ while (!PR_CLIST_IS_EMPTY(&tpool->jobq.wthreads)) {
+ wthread *wthrp;
+
+ head = PR_LIST_HEAD(&tpool->jobq.wthreads);
+ PR_REMOVE_AND_INIT_LINK(head);
+ PR_Unlock(tpool->jobq.lock);
+ wthrp = WTHREAD_LINKS_PTR(head);
+ rval_status = PR_JoinThread(wthrp->thread);
+ PR_ASSERT(PR_SUCCESS == rval_status);
+ PR_DELETE(wthrp);
+ PR_Lock(tpool->jobq.lock);
+ }
+ PR_Unlock(tpool->jobq.lock);
+ while (!PR_CLIST_IS_EMPTY(&tpool->ioq.wthreads)) {
+ wthread *wthrp;
+
+ head = PR_LIST_HEAD(&tpool->ioq.wthreads);
+ PR_REMOVE_AND_INIT_LINK(head);
+ wthrp = WTHREAD_LINKS_PTR(head);
+ rval_status = PR_JoinThread(wthrp->thread);
+ PR_ASSERT(PR_SUCCESS == rval_status);
+ PR_DELETE(wthrp);
+ }
+
+ while (!PR_CLIST_IS_EMPTY(&tpool->timerq.wthreads)) {
+ wthread *wthrp;
+
+ head = PR_LIST_HEAD(&tpool->timerq.wthreads);
+ PR_REMOVE_AND_INIT_LINK(head);
+ wthrp = WTHREAD_LINKS_PTR(head);
+ rval_status = PR_JoinThread(wthrp->thread);
+ PR_ASSERT(PR_SUCCESS == rval_status);
+ PR_DELETE(wthrp);
+ }
+
+ /*
+ * Delete queued jobs
+ */
+ while (!PR_CLIST_IS_EMPTY(&tpool->jobq.list)) {
+ PRJob *jobp;
+
+ head = PR_LIST_HEAD(&tpool->jobq.list);
+ PR_REMOVE_AND_INIT_LINK(head);
+ jobp = JOB_LINKS_PTR(head);
+ tpool->jobq.cnt--;
+ delete_job(jobp);
+ }
+
+ /* delete io jobs */
+ while (!PR_CLIST_IS_EMPTY(&tpool->ioq.list)) {
+ PRJob *jobp;
+
+ head = PR_LIST_HEAD(&tpool->ioq.list);
+ PR_REMOVE_AND_INIT_LINK(head);
+ tpool->ioq.cnt--;
+ jobp = JOB_LINKS_PTR(head);
+ delete_job(jobp);
+ }
+
+ /* delete timer jobs */
+ while (!PR_CLIST_IS_EMPTY(&tpool->timerq.list)) {
+ PRJob *jobp;
+
+ head = PR_LIST_HEAD(&tpool->timerq.list);
+ PR_REMOVE_AND_INIT_LINK(head);
+ tpool->timerq.cnt--;
+ jobp = JOB_LINKS_PTR(head);
+ delete_job(jobp);
+ }
+
+ PR_ASSERT(0 == tpool->jobq.cnt);
+ PR_ASSERT(0 == tpool->ioq.cnt);
+ PR_ASSERT(0 == tpool->timerq.cnt);
+
+ delete_threadpool(tpool);
+ return rval;
+}
diff --git a/nsprpub/pr/src/misc/prtrace.c b/nsprpub/pr/src/misc/prtrace.c
new file mode 100644
index 0000000000..ef6b65104e
--- /dev/null
+++ b/nsprpub/pr/src/misc/prtrace.c
@@ -0,0 +1,901 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** prtrace.c -- NSPR Trace Instrumentation
+**
+** Implement the API defined in prtrace.h
+**
+**
+**
+*/
+
+#include <string.h>
+#include "primpl.h"
+
+
+#define DEFAULT_TRACE_BUFSIZE ( 1024 * 1024 )
+#define DEFAULT_BUFFER_SEGMENTS 2
+
+/*
+** Enumerate states in a RName structure
+*/
+typedef enum TraceState
+{
+ Running = 1,
+ Suspended = 2
+} TraceState;
+
+/*
+** Define QName structure
+*/
+typedef struct QName
+{
+ PRCList link;
+ PRCList rNameList;
+ char name[PRTRACE_NAME_MAX+1];
+} QName;
+
+/*
+** Define RName structure
+*/
+typedef struct RName
+{
+ PRCList link;
+ PRLock *lock;
+ QName *qName;
+ TraceState state;
+ char name[PRTRACE_NAME_MAX+1];
+ char desc[PRTRACE_DESC_MAX+1];
+} RName;
+
+
+/*
+** The Trace Facility database
+**
+*/
+static PRLogModuleInfo *lm;
+
+static PRLock *traceLock; /* Facility Lock */
+static PRCList qNameList; /* anchor to all QName structures */
+static TraceState traceState = Running;
+
+/*
+** in-memory trace buffer controls
+*/
+static PRTraceEntry *tBuf; /* pointer to buffer */
+static PRInt32 bufSize; /* size of buffer, in bytes, rounded up to sizeof(PRTraceEntry) */
+static volatile PRInt32 next; /* index to next PRTraceEntry */
+static PRInt32 last; /* index of highest numbered trace entry */
+
+/*
+** Real-time buffer capture controls
+*/
+static PRInt32 fetchLastSeen = 0;
+static PRBool fetchLostData = PR_FALSE;
+
+/*
+** Buffer write-to-file controls
+*/
+static PRLock *logLock; /* Sync lock */
+static PRCondVar *logCVar; /* Sync Condidtion Variable */
+/*
+** Inter-thread state communication.
+** Controling thread writes to logOrder under protection of logCVar
+** the logging thread reads logOrder and sets logState on Notify.
+**
+** logSegments, logCount, logLostData must be read and written under
+** protection of logLock, logCVar.
+**
+*/
+static enum LogState
+{
+ LogNotRunning, /* Initial state */
+ LogReset, /* Causes logger to re-calc controls */
+ LogActive, /* Logging in progress, set only by log thread */
+ LogSuspend, /* Suspend Logging */
+ LogResume, /* Resume Logging => LogActive */
+ LogStop /* Stop the log thread */
+} logOrder, logState, localState; /* controlling state variables */
+static PRInt32 logSegments; /* Number of buffer segments */
+static PRInt32 logEntries; /* number of Trace Entries in the buffer */
+static PRInt32 logEntriesPerSegment; /* number of PRTraceEntries per buffer segment */
+static PRInt32 logSegSize; /* size of buffer segment */
+static PRInt32 logCount; /* number of segments pending output */
+static PRInt32 logLostData; /* number of lost log buffer segments */
+
+/*
+** end Trace Database
+**
+*/
+
+/*
+** _PR_InitializeTrace() -- Initialize the trace facility
+*/
+static void NewTraceBuffer( PRInt32 size )
+{
+ /*
+ ** calculate the size of the buffer
+ ** round down so that each segment has the same number of
+ ** trace entries
+ */
+ logSegments = DEFAULT_BUFFER_SEGMENTS;
+ logEntries = size / sizeof(PRTraceEntry);
+ logEntriesPerSegment = logEntries / logSegments;
+ logEntries = logSegments * logEntriesPerSegment;
+ bufSize = logEntries * sizeof(PRTraceEntry);
+ logSegSize = logEntriesPerSegment * sizeof(PRTraceEntry);
+ PR_ASSERT( bufSize != 0);
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("NewTraceBuffer: logSegments: %ld, logEntries: %ld, logEntriesPerSegment: %ld, logSegSize: %ld",
+ logSegments, logEntries, logEntriesPerSegment, logSegSize ));
+
+
+ tBuf = PR_Malloc( bufSize );
+ if ( tBuf == NULL )
+ {
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PRTrace: Failed to get trace buffer"));
+ PR_ASSERT( 0 );
+ }
+ else
+ {
+ PR_LOG( lm, PR_LOG_NOTICE,
+ ("PRTrace: Got trace buffer of size: %ld, at %p", bufSize, tBuf));
+ }
+
+ next = 0;
+ last = logEntries -1;
+ logCount = 0;
+ logLostData = PR_TRUE; /* not really on first call */
+ logOrder = LogReset;
+
+} /* end NewTraceBuffer() */
+
+/*
+** _PR_InitializeTrace() -- Initialize the trace facility
+*/
+static void _PR_InitializeTrace( void )
+{
+ /* The lock pointer better be null on this call */
+ PR_ASSERT( traceLock == NULL );
+
+ traceLock = PR_NewLock();
+ PR_ASSERT( traceLock != NULL );
+
+ PR_Lock( traceLock );
+
+ PR_INIT_CLIST( &qNameList );
+
+ lm = PR_NewLogModule("trace");
+
+ bufSize = DEFAULT_TRACE_BUFSIZE;
+ NewTraceBuffer( bufSize );
+
+ /* Initialize logging controls */
+ logLock = PR_NewLock();
+ logCVar = PR_NewCondVar( logLock );
+
+ PR_Unlock( traceLock );
+ return;
+} /* end _PR_InitializeTrace() */
+
+/*
+** Create a Trace Handle
+*/
+PR_IMPLEMENT(PRTraceHandle)
+PR_CreateTrace(
+ const char *qName, /* QName for this trace handle */
+ const char *rName, /* RName for this trace handle */
+ const char *description /* description for this trace handle */
+)
+{
+ QName *qnp;
+ RName *rnp;
+ PRBool matchQname = PR_FALSE;
+
+ /* Self initialize, if necessary */
+ if ( traceLock == NULL ) {
+ _PR_InitializeTrace();
+ }
+
+ /* Validate input arguments */
+ PR_ASSERT( strlen(qName) <= PRTRACE_NAME_MAX );
+ PR_ASSERT( strlen(rName) <= PRTRACE_NAME_MAX );
+ PR_ASSERT( strlen(description) <= PRTRACE_DESC_MAX );
+
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRTRACE: CreateTrace: Qname: %s, RName: %s", qName, rName));
+
+ /* Lock the Facility */
+ PR_Lock( traceLock );
+
+ /* Do we already have a matching QName? */
+ if (!PR_CLIST_IS_EMPTY( &qNameList ))
+ {
+ qnp = (QName *) PR_LIST_HEAD( &qNameList );
+ do {
+ if ( strcmp(qnp->name, qName) == 0)
+ {
+ matchQname = PR_TRUE;
+ break;
+ }
+ qnp = (QName *)PR_NEXT_LINK( &qnp->link );
+ } while( qnp != (QName *)&qNameList );
+ }
+ /*
+ ** If we did not find a matching QName,
+ ** allocate one and initialize it.
+ ** link it onto the qNameList.
+ **
+ */
+ if ( matchQname != PR_TRUE )
+ {
+ qnp = PR_NEWZAP( QName );
+ PR_ASSERT( qnp != NULL );
+ PR_INIT_CLIST( &qnp->link );
+ PR_INIT_CLIST( &qnp->rNameList );
+ strcpy( qnp->name, qName );
+ PR_APPEND_LINK( &qnp->link, &qNameList );
+ }
+
+ /* Do we already have a matching RName? */
+ if (!PR_CLIST_IS_EMPTY( &qnp->rNameList ))
+ {
+ rnp = (RName *) PR_LIST_HEAD( &qnp->rNameList );
+ do {
+ /*
+ ** No duplicate RNames are allowed within a QName
+ **
+ */
+ PR_ASSERT( strcmp(rnp->name, rName));
+ rnp = (RName *)PR_NEXT_LINK( &rnp->link );
+ } while( rnp != (RName *)&qnp->rNameList );
+ }
+
+ /* Get a new RName structure; initialize its members */
+ rnp = PR_NEWZAP( RName );
+ PR_ASSERT( rnp != NULL );
+ PR_INIT_CLIST( &rnp->link );
+ strcpy( rnp->name, rName );
+ strcpy( rnp->desc, description );
+ rnp->lock = PR_NewLock();
+ rnp->state = Running;
+ if ( rnp->lock == NULL )
+ {
+ PR_ASSERT(0);
+ }
+
+ PR_APPEND_LINK( &rnp->link, &qnp->rNameList ); /* add RName to QName's rnList */
+ rnp->qName = qnp; /* point the RName to the QName */
+
+ /* Unlock the Facility */
+ PR_Unlock( traceLock );
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Create: QName: %s %p, RName: %s %p\n\t",
+ qName, qnp, rName, rnp ));
+
+ return((PRTraceHandle)rnp);
+} /* end PR_CreateTrace() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+PR_DestroyTrace(
+ PRTraceHandle handle /* Handle to be destroyed */
+)
+{
+ RName *rnp = (RName *)handle;
+ QName *qnp = rnp->qName;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Deleting: QName: %s, RName: %s",
+ qnp->name, rnp->name));
+
+ /* Lock the Facility */
+ PR_Lock( traceLock );
+
+ /*
+ ** Remove RName from the list of RNames in QName
+ ** and free RName
+ */
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Deleting RName: %s, %p",
+ rnp->name, rnp));
+ PR_REMOVE_LINK( &rnp->link );
+ PR_Free( rnp->lock );
+ PR_DELETE( rnp );
+
+ /*
+ ** If this is the last RName within QName
+ ** remove QName from the qNameList and free it
+ */
+ if ( PR_CLIST_IS_EMPTY( &qnp->rNameList ) )
+ {
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Deleting unused QName: %s, %p",
+ qnp->name, qnp));
+ PR_REMOVE_LINK( &qnp->link );
+ PR_DELETE( qnp );
+ }
+
+ /* Unlock the Facility */
+ PR_Unlock( traceLock );
+ return;
+} /* end PR_DestroyTrace() */
+
+/*
+** Create a TraceEntry in the trace buffer
+*/
+PR_IMPLEMENT(void)
+PR_Trace(
+ PRTraceHandle handle, /* use this trace handle */
+ PRUint32 userData0, /* User supplied data word 0 */
+ PRUint32 userData1, /* User supplied data word 1 */
+ PRUint32 userData2, /* User supplied data word 2 */
+ PRUint32 userData3, /* User supplied data word 3 */
+ PRUint32 userData4, /* User supplied data word 4 */
+ PRUint32 userData5, /* User supplied data word 5 */
+ PRUint32 userData6, /* User supplied data word 6 */
+ PRUint32 userData7 /* User supplied data word 7 */
+)
+{
+ PRTraceEntry *tep;
+ PRInt32 mark;
+
+ if ( (traceState == Suspended )
+ || ( ((RName *)handle)->state == Suspended )) {
+ return;
+ }
+
+ /*
+ ** Get the next trace entry slot w/ minimum delay
+ */
+ PR_Lock( traceLock );
+
+ tep = &tBuf[next++];
+ if ( next > last ) {
+ next = 0;
+ }
+ if ( fetchLostData == PR_FALSE && next == fetchLastSeen ) {
+ fetchLostData = PR_TRUE;
+ }
+
+ mark = next;
+
+ PR_Unlock( traceLock );
+
+ /*
+ ** We have a trace entry. Fill it in.
+ */
+ tep->thread = PR_GetCurrentThread();
+ tep->handle = handle;
+ tep->time = PR_Now();
+ tep->userData[0] = userData0;
+ tep->userData[1] = userData1;
+ tep->userData[2] = userData2;
+ tep->userData[3] = userData3;
+ tep->userData[4] = userData4;
+ tep->userData[5] = userData5;
+ tep->userData[6] = userData6;
+ tep->userData[7] = userData7;
+
+ /* When buffer segment is full, signal trace log thread to run */
+ if (( mark % logEntriesPerSegment) == 0 )
+ {
+ PR_Lock( logLock );
+ logCount++;
+ PR_NotifyCondVar( logCVar );
+ PR_Unlock( logLock );
+ /*
+ ** Gh0D! This is awful!
+ ** Anyway, to minimize lost trace data segments,
+ ** I inserted the PR_Sleep(0) to cause a context switch
+ ** so that the log thread could run.
+ ** I know, it perturbs the universe and may cause
+ ** funny things to happen in the optimized builds.
+ ** Take it out, lose data; leave it in risk Heisenberg.
+ */
+ /* PR_Sleep(0); */
+ }
+
+ return;
+} /* end PR_Trace() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+PR_SetTraceOption(
+ PRTraceOption command, /* One of the enumerated values */
+ void *value /* command value or NULL */
+)
+{
+ RName * rnp;
+
+ switch ( command )
+ {
+ case PRTraceBufSize :
+ PR_Lock( traceLock );
+ PR_Free( tBuf );
+ bufSize = *(PRInt32 *)value;
+ NewTraceBuffer( bufSize );
+ PR_Unlock( traceLock );
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceBufSize: %ld", bufSize));
+ break;
+
+ case PRTraceEnable :
+ rnp = *(RName **)value;
+ rnp->state = Running;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceEnable: %p", rnp));
+ break;
+
+ case PRTraceDisable :
+ rnp = *(RName **)value;
+ rnp->state = Suspended;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceDisable: %p", rnp));
+ break;
+
+ case PRTraceSuspend :
+ traceState = Suspended;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceSuspend"));
+ break;
+
+ case PRTraceResume :
+ traceState = Running;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceResume"));
+ break;
+
+ case PRTraceSuspendRecording :
+ PR_Lock( logLock );
+ logOrder = LogSuspend;
+ PR_NotifyCondVar( logCVar );
+ PR_Unlock( logLock );
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceSuspendRecording"));
+ break;
+
+ case PRTraceResumeRecording :
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceResumeRecording"));
+ if ( logState != LogSuspend ) {
+ break;
+ }
+ PR_Lock( logLock );
+ logOrder = LogResume;
+ PR_NotifyCondVar( logCVar );
+ PR_Unlock( logLock );
+ break;
+
+ case PRTraceStopRecording :
+ PR_Lock( logLock );
+ logOrder = LogStop;
+ PR_NotifyCondVar( logCVar );
+ PR_Unlock( logLock );
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceStopRecording"));
+ break;
+
+ case PRTraceLockHandles :
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceLockTraceHandles"));
+ PR_Lock( traceLock );
+ break;
+
+ case PRTraceUnLockHandles :
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceUnLockHandles"));
+ PR_Unlock( traceLock );
+ break;
+
+ default:
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PRSetTraceOption: Invalid command %ld", command ));
+ PR_ASSERT( 0 );
+ break;
+ } /* end switch() */
+ return;
+} /* end PR_SetTraceOption() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+PR_GetTraceOption(
+ PRTraceOption command, /* One of the enumerated values */
+ void *value /* command value or NULL */
+)
+{
+ switch ( command )
+ {
+ case PRTraceBufSize :
+ *((PRInt32 *)value) = bufSize;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRGetTraceOption: PRTraceBufSize: %ld", bufSize ));
+ break;
+
+ default:
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PRGetTraceOption: Invalid command %ld", command ));
+ PR_ASSERT( 0 );
+ break;
+ } /* end switch() */
+ return;
+} /* end PR_GetTraceOption() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRTraceHandle)
+PR_GetTraceHandleFromName(
+ const char *qName, /* QName search argument */
+ const char *rName /* RName search argument */
+)
+{
+ const char *qn, *rn, *desc;
+ PRTraceHandle qh, rh = NULL;
+ RName *rnp = NULL;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: GetTraceHandleFromName:\n\t"
+ "QName: %s, RName: %s", qName, rName ));
+
+ qh = PR_FindNextTraceQname( NULL );
+ while (qh != NULL)
+ {
+ rh = PR_FindNextTraceRname( NULL, qh );
+ while ( rh != NULL )
+ {
+ PR_GetTraceNameFromHandle( rh, &qn, &rn, &desc );
+ if ( (strcmp( qName, qn ) == 0)
+ && (strcmp( rName, rn ) == 0 ))
+ {
+ rnp = (RName *)rh;
+ goto foundIt;
+ }
+ rh = PR_FindNextTraceRname( rh, qh );
+ }
+ qh = PR_FindNextTraceQname( NULL );
+ }
+
+foundIt:
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetConterHandleFromName: %p", rnp ));
+ return(rh);
+} /* end PR_GetTraceHandleFromName() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+PR_GetTraceNameFromHandle(
+ PRTraceHandle handle, /* handle as search argument */
+ const char **qName, /* pointer to associated QName */
+ const char **rName, /* pointer to associated RName */
+ const char **description /* pointer to associated description */
+)
+{
+ RName *rnp = (RName *)handle;
+ QName *qnp = rnp->qName;
+
+ *qName = qnp->name;
+ *rName = rnp->name;
+ *description = rnp->desc;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: GetConterNameFromHandle: "
+ "QNp: %p, RNp: %p,\n\tQName: %s, RName: %s, Desc: %s",
+ qnp, rnp, qnp->name, rnp->name, rnp->desc ));
+
+ return;
+} /* end PR_GetTraceNameFromHandle() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRTraceHandle)
+PR_FindNextTraceQname(
+ PRTraceHandle handle
+)
+{
+ QName *qnp = (QName *)handle;
+
+ if ( PR_CLIST_IS_EMPTY( &qNameList )) {
+ qnp = NULL;
+ }
+ else if ( qnp == NULL ) {
+ qnp = (QName *)PR_LIST_HEAD( &qNameList );
+ }
+ else if ( PR_NEXT_LINK( &qnp->link ) == &qNameList ) {
+ qnp = NULL;
+ }
+ else {
+ qnp = (QName *)PR_NEXT_LINK( &qnp->link );
+ }
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: FindNextQname: Handle: %p, Returns: %p",
+ handle, qnp ));
+
+ return((PRTraceHandle)qnp);
+} /* end PR_FindNextTraceQname() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRTraceHandle)
+PR_FindNextTraceRname(
+ PRTraceHandle rhandle,
+ PRTraceHandle qhandle
+)
+{
+ RName *rnp = (RName *)rhandle;
+ QName *qnp = (QName *)qhandle;
+
+
+ if ( PR_CLIST_IS_EMPTY( &qnp->rNameList )) {
+ rnp = NULL;
+ }
+ else if ( rnp == NULL ) {
+ rnp = (RName *)PR_LIST_HEAD( &qnp->rNameList );
+ }
+ else if ( PR_NEXT_LINK( &rnp->link ) == &qnp->rNameList ) {
+ rnp = NULL;
+ }
+ else {
+ rnp = (RName *)PR_NEXT_LINK( &rnp->link );
+ }
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: FindNextRname: Rhandle: %p, QHandle: %p, Returns: %p",
+ rhandle, qhandle, rnp ));
+
+ return((PRTraceHandle)rnp);
+} /* end PR_FindNextTraceRname() */
+
+/*
+**
+*/
+static PRFileDesc * InitializeRecording( void )
+{
+ char *logFileName;
+ PRFileDesc *logFile;
+
+ /* Self initialize, if necessary */
+ if ( traceLock == NULL ) {
+ _PR_InitializeTrace();
+ }
+
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PR_RecordTraceEntries: begins"));
+
+ logLostData = 0; /* reset at entry */
+ logState = LogReset;
+
+ /* Get the filename for the logfile from the environment */
+ logFileName = PR_GetEnvSecure( "NSPR_TRACE_LOG" );
+ if ( logFileName == NULL )
+ {
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("RecordTraceEntries: Environment variable not defined. Exiting"));
+ return NULL;
+ }
+
+ /* Open the logfile */
+ logFile = PR_Open( logFileName, PR_WRONLY | PR_CREATE_FILE, 0666 );
+ if ( logFile == NULL )
+ {
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("RecordTraceEntries: Cannot open %s as trace log file. OS error: %ld",
+ logFileName, PR_GetOSError()));
+ return NULL;
+ }
+ return logFile;
+} /* end InitializeRecording() */
+
+/*
+**
+*/
+static void ProcessOrders( void )
+{
+ switch ( logOrder )
+ {
+ case LogReset :
+ logOrder = logState = localState;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: LogReset"));
+ break;
+
+ case LogSuspend :
+ localState = logOrder = logState = LogSuspend;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: LogSuspend"));
+ break;
+
+ case LogResume :
+ localState = logOrder = logState = LogActive;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: LogResume"));
+ break;
+
+ case LogStop :
+ logOrder = logState = LogStop;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: LogStop"));
+ break;
+
+ default :
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("RecordTraceEntries: Invalid logOrder: %ld", logOrder ));
+ PR_ASSERT( 0 );
+ break;
+ } /* end switch() */
+ return ;
+} /* end ProcessOrders() */
+
+/*
+**
+*/
+static void WriteTraceSegment( PRFileDesc *logFile, void *buf, PRInt32 amount )
+{
+ PRInt32 rc;
+
+
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("WriteTraceSegment: Buffer: %p, Amount: %ld", buf, amount));
+ rc = PR_Write( logFile, buf, amount );
+ if ( rc == -1 )
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("RecordTraceEntries: PR_Write() failed. Error: %ld", PR_GetError() ));
+ else if ( rc != amount )
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("RecordTraceEntries: PR_Write() Tried to write: %ld, Wrote: %ld", amount, rc));
+ else
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: PR_Write(): Buffer: %p, bytes: %ld", buf, amount));
+
+ return;
+} /* end WriteTraceSegment() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+PR_RecordTraceEntries(
+ void
+)
+{
+ PRFileDesc *logFile;
+ PRInt32 lostSegments;
+ PRInt32 currentSegment = 0;
+ void *buf;
+ PRBool doWrite;
+
+ logFile = InitializeRecording();
+ if ( logFile == NULL )
+ {
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PR_RecordTraceEntries: Failed to initialize"));
+ return;
+ }
+
+ /* Do this until told to stop */
+ while ( logState != LogStop )
+ {
+
+ PR_Lock( logLock );
+
+ while ( (logCount == 0) && ( logOrder == logState ) ) {
+ PR_WaitCondVar( logCVar, PR_INTERVAL_NO_TIMEOUT );
+ }
+
+ /* Handle state transitions */
+ if ( logOrder != logState ) {
+ ProcessOrders();
+ }
+
+ /* recalculate local controls */
+ if ( logCount )
+ {
+ lostSegments = logCount - logSegments;
+ if ( lostSegments > 0 )
+ {
+ logLostData += ( logCount - logSegments );
+ logCount = (logCount % logSegments);
+ currentSegment = logCount;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PR_RecordTraceEntries: LostData segments: %ld", logLostData));
+ }
+ else
+ {
+ logCount--;
+ }
+
+ buf = tBuf + ( logEntriesPerSegment * currentSegment );
+ if (++currentSegment >= logSegments ) {
+ currentSegment = 0;
+ }
+ doWrite = PR_TRUE;
+ }
+ else {
+ doWrite = PR_FALSE;
+ }
+
+ PR_Unlock( logLock );
+
+ if ( doWrite == PR_TRUE )
+ {
+ if ( localState != LogSuspend ) {
+ WriteTraceSegment( logFile, buf, logSegSize );
+ }
+ else
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: PR_Write(): is suspended" ));
+ }
+
+ } /* end while(logState...) */
+
+ PR_Close( logFile );
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: exiting"));
+ return;
+} /* end PR_RecordTraceEntries() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRIntn)
+PR_GetTraceEntries(
+ PRTraceEntry *buffer, /* where to write output */
+ PRInt32 count, /* number to get */
+ PRInt32 *found /* number you got */
+)
+{
+ PRInt32 rc;
+ PRInt32 copied = 0;
+
+ PR_Lock( traceLock );
+
+ /*
+ ** Depending on where the LastSeen and Next indices are,
+ ** copy the trace buffer in one or two pieces.
+ */
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PR_GetTraceEntries: Next: %ld, LastSeen: %ld", next, fetchLastSeen));
+
+ if ( fetchLastSeen <= next )
+ {
+ while (( count-- > 0 ) && (fetchLastSeen < next ))
+ {
+ *(buffer + copied++) = *(tBuf + fetchLastSeen++);
+ }
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied, fetchLastSeen));
+ }
+ else /* copy in 2 parts */
+ {
+ while ( count-- > 0 && fetchLastSeen <= last )
+ {
+ *(buffer + copied++) = *(tBuf + fetchLastSeen++);
+ }
+ fetchLastSeen = 0;
+
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied, fetchLastSeen));
+
+ while ( count-- > 0 && fetchLastSeen < next )
+ {
+ *(buffer + copied++) = *(tBuf + fetchLastSeen++);
+ }
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied, fetchLastSeen));
+ }
+
+ *found = copied;
+ rc = ( fetchLostData == PR_TRUE )? 1 : 0;
+ fetchLostData = PR_FALSE;
+
+ PR_Unlock( traceLock );
+ return rc;
+} /* end PR_GetTraceEntries() */
+
+/* end prtrace.c */
diff --git a/nsprpub/pr/src/nspr.def b/nsprpub/pr/src/nspr.def
new file mode 100644
index 0000000000..49a437b1fa
--- /dev/null
+++ b/nsprpub/pr/src/nspr.def
@@ -0,0 +1,468 @@
+;+#
+;+# This Source Code Form is subject to the terms of the Mozilla Public
+;+# License, v. 2.0. If a copy of the MPL was not distributed with this
+;+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;+#
+;+# OK, this file is meant to support SUN, LINUX, AIX, OS/2 and WINDOWS
+;+# 1. For all unix platforms, the string ";-" means "remove this line"
+;+# 2. For all unix platforms, the string " DATA " will be removed from any
+;+# line on which it occurs.
+;+# 3. Lines containing ";+" will have ";+" removed on SUN and LINUX.
+;+# On AIX, lines containing ";+" will be removed.
+;+# 4. For all unix platforms, the string ";;" will thave the ";;" removed.
+;+# 5. For all unix platforms, after the above processing has taken place,
+;+# all characters after the first ";" on the line will be removed.
+;+# And for AIX, the first ";" will also be removed.
+;+# This file is passed directly to windows. Since ';' is a comment, all UNIX
+;+# directives are hidden behind ";", ";+", and ";-"
+;+#
+;+NSPR_4.0 {
+;+ global:
+LIBRARY nspr4 ;-
+EXPORTS ;-
+ LL_MaxInt;
+ LL_MinInt;
+ LL_Zero;
+ PR_Abort;
+ PR_AddToCounter;
+ PR_Accept;
+ PR_AcceptRead;
+ PR_Access;
+ PR_AddWaitFileDesc;
+ PR_AllocFileDesc;
+ PR_Assert;
+ PR_AtomicAdd;
+ PR_AtomicDecrement;
+ PR_AtomicIncrement;
+ PR_AtomicSet;
+ PR_AttachSharedMemory;
+ PR_AttachThread;
+ PR_Available;
+ PR_Available64;
+ PR_Bind;
+ PR_BlockClockInterrupts;
+ PR_BlockInterrupt;
+ PR_CEnterMonitor;
+ PR_CExitMonitor;
+ PR_CNotify;
+ PR_CNotifyAll;
+ PR_CSetOnMonitorRecycle;
+ PR_CWait;
+ PR_CallOnce;
+ PR_Calloc;
+ PR_CancelJob;
+ PR_CancelWaitFileDesc;
+ PR_CancelWaitGroup;
+ PR_CeilingLog2;
+ PR_ChangeFileDescNativeHandle;
+ PR_Cleanup;
+ PR_ClearInterrupt;
+ PR_ClearThreadGCAble;
+ PR_Close;
+ PR_CloseDir;
+ PR_CloseFileMap;
+ PR_CloseSemaphore;
+ PR_CloseSharedMemory;
+ PR_Connect;
+ PR_CreateCounter;
+ PR_ConvertIPv4AddrToIPv6;
+ PR_CreateAlarm;
+ PR_CreateFileMap;
+ PR_CreateIOLayerStub;
+ PR_CreateOrderedLock;
+ PR_CreateMWaitEnumerator;
+ PR_CreatePipe;
+ PR_CreateProcess;
+ PR_CreateProcessDetached;
+ PR_CreateSocketPollFd;
+ PR_CreateStack;
+ PR_CreateThread;
+ PR_CreateThreadGCAble;
+ PR_CreateTrace;
+ PR_CreateThreadPool;
+ PR_DecrementCounter;
+ PR_CreateWaitGroup;
+ PR_Delete;
+ PR_DeleteSemaphore;
+ PR_DeleteSharedMemory;
+ PR_DestroyAlarm;
+ PR_DestroyCounter;
+ PR_DestroyCondVar;
+ PR_DestroyLock;
+ PR_DestroyMWaitEnumerator;
+ PR_DestroyOrderedLock;
+ PR_DestroyMonitor;
+ PR_DestroyPollableEvent;
+ PR_DestroyProcessAttr;
+ PR_DestroyRWLock;
+ PR_DestroySem;
+ PR_DestroySocketPollFd;
+ PR_DestroyTrace;
+ PR_DestroyStack;
+ PR_DestroyWaitGroup;
+ PR_DetachProcess;
+ PR_DetachSharedMemory;
+ PR_DetachThread;
+ PR_DisableClockInterrupts;
+ PR_EnableClockInterrupts;
+ PR_EnterMonitor;
+ PR_EnumerateHostEnt;
+ PR_EnumerateThreads;
+ PR_EnumerateWaitGroup;
+ PR_ErrorInstallCallback;
+ PR_ErrorInstallTable;
+ PR_ErrorLanguages;
+ PR_ErrorToName;
+ PR_ErrorToString;
+ PR_ExitMonitor;
+ PR_ExplodeTime;
+ PR_ExportFileMapAsString;
+ PR_FD_CLR;
+ PR_FD_ISSET;
+ PR_FD_NCLR;
+ PR_FD_NISSET;
+ PR_FD_NSET;
+ PR_FD_SET;
+ PR_FD_ZERO;
+ PR_FileDesc2NativeHandle;
+ PR_FindSymbol;
+ PR_FindSymbolAndLibrary;
+ PR_FloorLog2;
+ PR_FormatTime;
+ PR_FindNextCounterQname;
+ PR_FindNextCounterRname;
+ PR_FindNextTraceQname;
+ PR_FindNextTraceRname;
+ PR_FormatTimeUSEnglish;
+ PR_Free;
+ PR_FreeLibraryName;
+ PR_GMTParameters;
+ PR_GetConnectStatus;
+ PR_GetCurrentThread;
+ PR_GetDefaultIOMethods;
+ PR_GetDescType;
+ PR_GetDirectorySeparator;
+ PR_GetCounter;
+ PR_GetCounterHandleFromName;
+ PR_GetCounterNameFromHandle;
+ PR_GetDirectorySepartor;
+ PR_GetEnv;
+ PR_GetError;
+ PR_GetErrorText;
+ PR_GetErrorTextLength;
+ PR_GetFileInfo;
+ PR_GetFileInfo64;
+ PR_GetFileMethods;
+ PR_GetGCRegisters;
+ PR_GetHostByAddr;
+ PR_GetHostByName;
+ PR_GetIPNodeByName;
+ PR_GetIdentitiesLayer;
+ PR_GetInheritedFD;
+ PR_GetInheritedFileMap;
+ PR_GetLayersIdentity;
+ PR_GetLibraryName;
+ PR_GetLibraryPath;
+ PR_GetMonitorEntryCount;
+ PR_GetNameForIdentity;
+ PR_GetOSError;
+ PR_GetOpenFileInfo;
+ PR_GetOpenFileInfo64;
+ PR_GetPageShift;
+ PR_GetPageSize;
+ PR_GetPeerName;
+ PR_GetPipeMethods;
+ PR_GetProtoByName;
+ PR_GetProtoByNumber;
+ PR_GetRandomNoise;
+ PR_GetSP;
+ PR_GetSockName;
+ PR_GetSocketOption;
+ PR_GetSpecialFD;
+ PR_GetStackSpaceLeft;
+ PR_GetSysfdTableMax;
+ PR_GetSystemInfo;
+ PR_GetTCPMethods;
+ PR_GetThreadAffinityMask;
+ PR_GetThreadID;
+ PR_GetThreadPriority;
+ PR_GetThreadPrivate;
+ PR_GetThreadScope;
+ PR_GetThreadState;
+ PR_GetThreadType;
+ PR_GetUDPMethods;
+ PR_GetUniqueIdentity;
+ PR_ImplodeTime;
+ PR_ImportFile;
+ PR_ImportFileMapFromString;
+ PR_ImportTCPSocket;
+ PR_ImportUDPSocket;
+ PR_GetTraceEntries;
+ PR_GetTraceHandleFromName;
+ PR_GetTraceNameFromHandle;
+ PR_GetTraceOption;
+ PR_Init;
+ PR_Initialize;
+ PR_InitializeNetAddr;
+ PR_Initialized;
+ PR_Interrupt;
+ PR_IntervalNow;
+ PR_IntervalToMicroseconds;
+ PR_IntervalToMilliseconds;
+ PR_IncrementCounter;
+ PR_IntervalToSeconds;
+ PR_IsNetAddrType;
+ PR_JoinJob;
+ PR_JoinThread;
+ PR_JoinThreadPool;
+ PR_KillProcess;
+ PR_Listen;
+ PR_LoadLibrary;
+ PR_LoadLibraryWithFlags;
+ PR_LoadStaticLibrary;
+ PR_LocalTimeParameters;
+ PR_Lock;
+ PR_LockFile;
+ PR_LogFlush;
+ PR_LogPrint;
+ PR_MakeDir;
+ PR_Malloc;
+ PR_MemMap;
+ PR_MemUnmap;
+ PR_MicrosecondsToInterval;
+ PR_MillisecondsToInterval;
+ PR_LockOrderedLock;
+ PR_MkDir;
+ PR_NetAddrToString;
+ PR_NewCondVar;
+ PR_NewLock;
+ PR_NewLogModule;
+ PR_NewMonitor;
+ PR_NewNamedMonitor;
+ PR_NewPollableEvent;
+ PR_NewProcessAttr;
+ PR_NewRWLock;
+ PR_NewSem;
+ PR_NewTCPSocket;
+ PR_NewTCPSocketPair;
+ PR_NewThreadPrivateIndex;
+ PR_NewUDPSocket;
+ PR_NormalizeTime;
+ PR_Notify;
+ PR_NotifyAll;
+ PR_NotifyAllCondVar;
+ PR_NotifyCondVar;
+ PR_Now;
+ PR_Open;
+ PR_OpenAnonFileMap;
+ PR_OpenDir;
+ PR_OpenFile;
+ PR_OpenSemaphore;
+ PR_OpenSharedMemory;
+ PR_OpenTCPSocket;
+ PR_OpenUDPSocket;
+ PR_ParseTimeString;
+ PR_Poll;
+ PR_PopIOLayer;
+ PR_PostSem;
+ PR_PostSemaphore;
+ PR_ProcessAttrSetCurrentDirectory;
+ PR_ProcessAttrSetInheritableFD;
+ PR_ProcessAttrSetInheritableFileMap;
+ PR_ProcessAttrSetStdioRedirect;
+ PR_ProcessExit;
+ PR_PushIOLayer;
+ PR_QueueJob;
+ PR_QueueJob_Accept;
+ PR_QueueJob_Connect;
+ PR_QueueJob_Read;
+ PR_QueueJob_Timer;
+ PR_QueueJob_Write;
+ PR_RWLock_Rlock;
+ PR_RWLock_Unlock;
+ PR_RWLock_Wlock;
+ PR_Read;
+ PR_ReadDir;
+ PR_Realloc;
+ PR_Recv;
+ PR_RecvFrom;
+ PR_Rename;
+ PR_ResetAlarm;
+ PR_ResetProcessAttr;
+ PR_ResumeAll;
+ PR_RmDir;
+ PR_ScanStackPointers;
+ PR_RecordTraceEntries;
+ PR_SecondsToInterval;
+ PR_Seek;
+ PR_Seek64;
+ PR_Select;
+ PR_Send;
+ PR_SendFile;
+ PR_SendTo;
+ PR_SetAlarm;
+ PR_SetConcurrency;
+ PR_SetError;
+ PR_SetErrorText;
+ PR_SetFDCacheSize;
+ PR_SetFDInheritable;
+ PR_SetLibraryPath;
+ PR_SetLogBuffering;
+ PR_SetLogFile;
+ PR_SetNetAddr;
+ PR_SetPollableEvent;
+ PR_SetSocketOption;
+ PR_SetCounter;
+ PR_SetStdioRedirect;
+ PR_SetSysfdTableSize;
+ PR_SetThreadAffinityMask;
+ PR_SetThreadDumpProc;
+ PR_SetThreadGCAble;
+ PR_SetThreadPriority;
+ PR_SetThreadPrivate;
+ PR_SetThreadRecycleMode;
+ PR_Shutdown;
+ PR_ShutdownThreadPool;
+ PR_Sleep;
+ PR_Socket;
+ PR_StackPop;
+ PR_StackPush;
+ PR_Stat;
+ PR_StringToNetAddr;
+ PR_SuspendAll;
+ PR_Sync;
+ PR_TLockFile;
+ PR_ThreadScanStackPointers;
+ PR_SetTraceOption;
+ PR_TicksPerSecond;
+ PR_TransmitFile;
+ PR_USPacificTimeParameters;
+ PR_UnblockClockInterrupts;
+ PR_UnblockInterrupt;
+ PR_UnloadLibrary;
+ PR_SubtractFromCounter;
+ PR_Unlock;
+ PR_UnlockFile;
+ PR_VersionCheck;
+ PR_Wait;
+ PR_WaitCondVar;
+ PR_WaitForPollableEvent;
+ PR_Trace;
+ PR_WaitProcess;
+ PR_WaitRecvReady;
+ PR_WaitSem;
+ PR_WaitSemaphore;
+ PR_Write;
+ PR_Writev;
+ PR_Yield;
+ PR_UnlockOrderedLock;
+ PR_cnvtf;
+ PR_dtoa;
+ PR_fprintf;
+ PR_htonl;
+ PR_htonll;
+ PR_htons;
+ PR_ntohl;
+ PR_ntohll;
+ PR_ntohs;
+ PR_smprintf;
+ PR_smprintf_free;
+ PR_snprintf;
+ PR_sprintf_append;
+ PR_sscanf;
+ PR_strtod;
+ PR_sxprintf;
+ PR_vfprintf;
+ PR_vsmprintf;
+ PR_vsnprintf;
+ PR_vsprintf_append;
+ PR_vsxprintf;
+ PRP_DestroyNakedCondVar;
+ PRP_NakedBroadcast;
+ PRP_NakedNotify;
+ PRP_NakedWait;
+ PRP_NewNakedCondVar;
+ PRP_TryLock;
+ libVersionPoint;
+;+ local: *;
+;+};
+;+
+;+NSPRprivate {
+;+ global:
+ GetExecutionEnvironment;
+ PT_FPrintStats;
+ SetExecutionEnvironment;
+;+ local: *;
+;+};
+;+
+;+NSPR_4.1 {
+;+ global:
+ PR_ConnectContinue;
+ PR_CreateIOLayer;
+ PR_EmulateAcceptRead;
+ PR_EmulateSendFile;
+ PR_FindFunctionSymbol;
+ PR_FindFunctionSymbolAndLibrary;
+ PR_GetMemMapAlignment;
+ PR_GetNumberOfProcessors;
+ PR_ImportPipe;
+ PR_SetEnv;
+;+} NSPR_4.0;
+;+
+;+NSPR_4.3 {
+;+ global:
+ LL_MaxUint;
+ PR_CallOnceWithArg;
+ PR_GetLibraryFilePathname;
+;+} NSPR_4.1;
+;+
+;+NSPR_4.4 {
+;+ global:
+ PR_GetPathSeparator;
+;+} NSPR_4.3;
+;+
+;+NSPR_4.5 {
+;+ global:
+ PR_EnumerateAddrInfo;
+ PR_FreeAddrInfo;
+ PR_GetAddrInfoByName;
+ PR_GetCanonNameFromAddrInfo;
+;+} NSPR_4.4;
+;+
+;+NSPR_4.6 {
+;+ global:
+ PR_GetPhysicalMemorySize;
+;+} NSPR_4.5;
+;+NSPR_4.7 {
+;+ global:
+ PR_ParseTimeStringToExplodedTime;
+;+} NSPR_4.6;
+;+NSPR_4.8 {
+;+ global:
+ PR_AssertCurrentThreadOwnsLock;
+ PR_AssertCurrentThreadInMonitor;
+;+} NSPR_4.7;
+;+NSPR_4.8.9 {
+;+ global:
+ PR_GetVersion;
+;+} NSPR_4.8;
+;+NSPR_4.9.2 {
+;+ global:
+ PR_GetThreadName;
+ PR_SetCurrentThreadName;
+;+} NSPR_4.8.9;
+;+NSPR_4.10.3 {
+;+ global:
+ PR_SyncMemMap;
+;+} NSPR_4.9.2;
+;+# Function PR_DuplicateEnvironment had been added in NSPR 4.10.9,
+;+# but we neglected to add it to nspr.def until NSPR 4.12
+;+NSPR_4.12 {
+;+ global:
+ PR_DuplicateEnvironment;
+ PR_GetEnvSecure;
+;+} NSPR_4.10.3;
+;+NSPR_4.34 {
+;+ global:
+ PR_GetPrefLoopbackAddrInfo;
+;+} NSPR_4.12;
diff --git a/nsprpub/pr/src/nspr.rc b/nsprpub/pr/src/nspr.rc
new file mode 100644
index 0000000000..bdfb1bdc3f
--- /dev/null
+++ b/nsprpub/pr/src/nspr.rc
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prinit.h"
+#include <winver.h>
+
+#define MY_LIBNAME "nspr"
+#define MY_FILEDESCRIPTION "NSPR Library"
+
+#define STRINGIZE(x) #x
+#define STRINGIZE2(x) STRINGIZE(x)
+#define PR_VMAJOR_STR STRINGIZE2(PR_VMAJOR)
+
+#ifdef _DEBUG
+#define MY_DEBUG_STR " (debug)"
+#define MY_FILEFLAGS_1 VS_FF_DEBUG
+#else
+#define MY_DEBUG_STR ""
+#define MY_FILEFLAGS_1 0x0L
+#endif
+#if PR_BETA
+#define MY_FILEFLAGS_2 MY_FILEFLAGS_1|VS_FF_PRERELEASE
+#else
+#define MY_FILEFLAGS_2 MY_FILEFLAGS_1
+#endif
+
+#ifdef WINNT
+#define MY_FILEOS VOS_NT_WINDOWS32
+#define MY_INTERNAL_NAME "lib" MY_LIBNAME PR_VMAJOR_STR
+#else
+#define MY_FILEOS VOS__WINDOWS32
+#define MY_INTERNAL_NAME MY_LIBNAME PR_VMAJOR_STR
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version-information resource
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION PR_VMAJOR,PR_VMINOR,PR_VPATCH,0
+ PRODUCTVERSION PR_VMAJOR,PR_VMINOR,PR_VPATCH,0
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS MY_FILEFLAGS_2
+ FILEOS MY_FILEOS
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L // not used
+
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "CompanyName", "Mozilla Foundation\0"
+ VALUE "FileDescription", MY_FILEDESCRIPTION MY_DEBUG_STR "\0"
+ VALUE "FileVersion", PR_VERSION "\0"
+ VALUE "InternalName", MY_INTERNAL_NAME "\0"
+ VALUE "OriginalFilename", MY_INTERNAL_NAME ".dll\0"
+ VALUE "ProductName", "Netscape Portable Runtime\0"
+ VALUE "ProductVersion", PR_VERSION "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/nsprpub/pr/src/os2extra.def b/nsprpub/pr/src/os2extra.def
new file mode 100644
index 0000000000..2cbf5ad4ca
--- /dev/null
+++ b/nsprpub/pr/src/os2extra.def
@@ -0,0 +1,20 @@
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ ;
+ ; Support plugins that were explicitly linked to the Visual Age
+ ; version of nspr4.dll.
+ ;
+ PR_NewMonitor
+ PR_EnterMonitor
+ PR_ExitMonitor
+ PR_GetCurrentThread
+ PR_AttachThread
+ PR_DetachThread
+ ;
+ ; Exception handler functions that are used by nsAppRunner.cpp
+ ;
+ _PR_OS2_SetFloatExcpHandler
+ _PR_OS2_UnsetFloatExcpHandler
+
diff --git a/nsprpub/pr/src/prvrsion.c b/nsprpub/pr/src/prvrsion.c
new file mode 100644
index 0000000000..09dcc66597
--- /dev/null
+++ b/nsprpub/pr/src/prvrsion.c
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prinit.h"
+#include "prvrsion.h"
+
+/************************************************************************/
+/**************************IDENTITY AND VERSIONING***********************/
+/************************************************************************/
+#include "_pr_bld.h"
+#if !defined(_BUILD_TIME)
+#ifdef HAVE_LONG_LONG
+#define _BUILD_TIME 0
+#else
+#define _BUILD_TIME {0, 0}
+#endif
+#endif
+#if !defined(_BUILD_STRING)
+#define _BUILD_STRING ""
+#endif
+#if !defined(_PRODUCTION)
+#define _PRODUCTION ""
+#endif
+#if defined(DEBUG)
+#define _DEBUG_STRING " (debug)"
+#else
+#define _DEBUG_STRING ""
+#endif
+
+/*
+ * A trick to expand the PR_VMAJOR macro before concatenation.
+ */
+#define CONCAT(x, y) x ## y
+#define CONCAT2(x, y) CONCAT(x, y)
+#define VERSION_DESC_NAME CONCAT2(prVersionDescription_libnspr, PR_VMAJOR)
+
+PRVersionDescription VERSION_DESC_NAME =
+{
+ /* version */ 2, /* this is the only one supported */
+ /* buildTime */ _BUILD_TIME, /* usecs since midnight 1/1/1970 GMT */
+ /* buildTimeString */ _BUILD_STRING, /* ditto, but human readable */
+ /* vMajor */ PR_VMAJOR, /* NSPR's version number */
+ /* vMinor */ PR_VMINOR, /* and minor version */
+ /* vPatch */ PR_VPATCH, /* and patch */
+ /* beta */ PR_BETA, /* beta build boolean */
+#if defined(DEBUG)
+ /* debug */ PR_TRUE, /* a debug build */
+#else
+ /* debug */ PR_FALSE, /* an optomized build */
+#endif
+ /* special */ PR_FALSE, /* they're all special, but ... */
+ /* filename */ _PRODUCTION, /* the produced library name */
+ /* description */ "Portable runtime", /* what we are */
+ /* security */ "N/A", /* not applicable here */
+ /* copywrite */ "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.",
+ /* comment */ "License information: http://www.mozilla.org/MPL/",
+ /* specialString */ ""
+};
+
+#ifdef XP_UNIX
+
+/*
+ * Version information for the 'ident' and 'what commands
+ *
+ * NOTE: the first component of the concatenated rcsid string
+ * must not end in a '$' to prevent rcs keyword substitution.
+ */
+static char rcsid[] = "$Header: NSPR " PR_VERSION _DEBUG_STRING
+ " " _BUILD_STRING " $";
+static char sccsid[] = "@(#)NSPR " PR_VERSION _DEBUG_STRING
+ " " _BUILD_STRING;
+
+#endif /* XP_UNIX */
+
+#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
+PR_IMPLEMENT(const PRVersionDescription*) libVersionPoint(void)
+{
+#ifdef XP_UNIX
+ /*
+ * Add dummy references to rcsid and sccsid to prevent them
+ * from being optimized away as unused variables.
+ */
+ const char *dummy;
+
+ dummy = rcsid;
+ dummy = sccsid;
+#endif
+ return &VERSION_DESC_NAME;
+} /* versionEntryPointType */
+#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
+
+/* prvrsion.c */
+
diff --git a/nsprpub/pr/src/pthreads/Makefile.in b/nsprpub/pr/src/pthreads/Makefile.in
new file mode 100644
index 0000000000..c8191c749a
--- /dev/null
+++ b/nsprpub/pr/src/pthreads/Makefile.in
@@ -0,0 +1,35 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+CSRCS = \
+ ptio.c \
+ ptsynch.c \
+ ptthread.c \
+ ptmisc.c \
+ $(NULL)
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
+
diff --git a/nsprpub/pr/src/pthreads/ptio.c b/nsprpub/pr/src/pthreads/ptio.c
new file mode 100644
index 0000000000..a2bc3e714d
--- /dev/null
+++ b/nsprpub/pr/src/pthreads/ptio.c
@@ -0,0 +1,5387 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: ptio.c
+** Descritpion: Implemenation of I/O methods for pthreads
+*/
+
+#if defined(_PR_PTHREADS)
+
+#if defined(_PR_POLL_WITH_SELECT)
+#if !(defined(HPUX) && defined(_USE_BIG_FDS))
+/* set fd limit for select(), before including system header files */
+#define FD_SETSIZE (16 * 1024)
+#endif
+#endif
+
+#include <pthread.h>
+#include <string.h> /* for memset() */
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#if defined(DARWIN)
+#include <sys/utsname.h> /* for uname */
+#endif
+#if defined(SOLARIS) || defined(UNIXWARE)
+#include <sys/filio.h> /* to pick up FIONREAD */
+#endif
+#ifdef _PR_POLL_AVAILABLE
+#include <poll.h>
+#endif
+#ifdef AIX
+/* To pick up sysconf() */
+#include <unistd.h>
+#include <dlfcn.h> /* for dlopen */
+#else
+/* To pick up getrlimit() etc. */
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+
+#ifdef SOLARIS
+/*
+ * Define HAVE_SENDFILEV if the system has the sendfilev() system call.
+ * Code built this way won't run on a system without sendfilev().
+ * We can define HAVE_SENDFILEV by default when the minimum release
+ * of Solaris that NSPR supports has sendfilev().
+ */
+#ifdef HAVE_SENDFILEV
+
+#include <sys/sendfile.h>
+
+#define SOLARIS_SENDFILEV(a, b, c, d) sendfilev((a), (b), (c), (d))
+
+#else
+
+#include <dlfcn.h> /* for dlopen */
+
+/*
+ * Match the definitions in <sys/sendfile.h>.
+ */
+typedef struct sendfilevec {
+ int sfv_fd; /* input fd */
+ uint_t sfv_flag; /* flags */
+ off_t sfv_off; /* offset to start reading from */
+ size_t sfv_len; /* amount of data */
+} sendfilevec_t;
+
+#define SFV_FD_SELF (-2)
+
+/*
+ * extern ssize_t sendfilev(int, const struct sendfilevec *, int, size_t *);
+ */
+static ssize_t (*pt_solaris_sendfilev_fptr)() = NULL;
+
+#define SOLARIS_SENDFILEV(a, b, c, d) \
+ (*pt_solaris_sendfilev_fptr)((a), (b), (c), (d))
+
+#endif /* HAVE_SENDFILEV */
+#endif /* SOLARIS */
+
+/*
+ * The send_file() system call is available in AIX 4.3.2 or later.
+ * If this file is compiled on an older AIX system, it attempts to
+ * look up the send_file symbol at run time to determine whether
+ * we can use the faster PR_SendFile/PR_TransmitFile implementation based on
+ * send_file(). On AIX 4.3.2 or later, we can safely skip this
+ * runtime function dispatching and just use the send_file based
+ * implementation.
+ */
+#ifdef AIX
+#ifdef SF_CLOSE
+#define HAVE_SEND_FILE
+#endif
+
+#ifdef HAVE_SEND_FILE
+
+#define AIX_SEND_FILE(a, b, c) send_file(a, b, c)
+
+#else /* HAVE_SEND_FILE */
+
+/*
+ * The following definitions match those in <sys/socket.h>
+ * on AIX 4.3.2.
+ */
+
+/*
+ * Structure for the send_file() system call
+ */
+struct sf_parms {
+ /* --------- header parms ---------- */
+ void *header_data; /* Input/Output. Points to header buf */
+ uint_t header_length; /* Input/Output. Length of the header */
+ /* --------- file parms ------------ */
+ int file_descriptor; /* Input. File descriptor of the file */
+ unsigned long long file_size; /* Output. Size of the file */
+ unsigned long long file_offset; /* Input/Output. Starting offset */
+ long long file_bytes; /* Input/Output. no. of bytes to send */
+ /* --------- trailer parms --------- */
+ void *trailer_data; /* Input/Output. Points to trailer buf */
+ uint_t trailer_length; /* Input/Output. Length of the trailer */
+ /* --------- return info ----------- */
+ unsigned long long bytes_sent; /* Output. no. of bytes sent */
+};
+
+/*
+ * Flags for the send_file() system call
+ */
+#define SF_CLOSE 0x00000001 /* close the socket after completion */
+#define SF_REUSE 0x00000002 /* reuse socket. not supported */
+#define SF_DONT_CACHE 0x00000004 /* don't apply network buffer cache */
+#define SF_SYNC_CACHE 0x00000008 /* sync/update network buffer cache */
+
+/*
+ * prototype: size_t send_file(int *, struct sf_parms *, uint_t);
+ */
+static ssize_t (*pt_aix_sendfile_fptr)() = NULL;
+
+#define AIX_SEND_FILE(a, b, c) (*pt_aix_sendfile_fptr)(a, b, c)
+
+#endif /* HAVE_SEND_FILE */
+#endif /* AIX */
+
+#ifdef LINUX
+#include <sys/sendfile.h>
+#endif
+
+#include "primpl.h"
+
+#if defined(LINUX) || defined(ANDROID)
+#include <netinet/in.h>
+#endif
+
+#ifdef DARWIN
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#endif
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h> /* TCP_NODELAY, TCP_MAXSEG */
+#endif
+
+#ifdef LINUX
+/* TCP_CORK is not defined in <netinet/tcp.h> on Red Hat Linux 6.0 */
+#ifndef TCP_CORK
+#define TCP_CORK 3
+#endif
+#ifndef MSG_FASTOPEN
+#define MSG_FASTOPEN 0x20000000
+#endif
+#endif
+
+#ifdef _PR_IPV6_V6ONLY_PROBE
+static PRBool _pr_ipv6_v6only_on_by_default;
+#endif
+
+#if (defined(HPUX) && !defined(HPUX10_30) && !defined(HPUX11))
+#define _PRSelectFdSetArg_t int *
+#elif defined(AIX4_1)
+#define _PRSelectFdSetArg_t void *
+#elif (defined(AIX) && !defined(AIX4_1)) \
+ || defined(SOLARIS) \
+ || defined(HPUX10_30) || defined(HPUX11) \
+ || defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \
+ || defined(FREEBSD) || defined(NETBSD) || defined(OPENBSD) \
+ || defined(BSDI) || defined(NTO) || defined(DARWIN) \
+ || defined(UNIXWARE) || defined(RISCOS)
+#define _PRSelectFdSetArg_t fd_set *
+#else
+#error "Cannot determine architecture"
+#endif
+
+#if defined(SOLARIS)
+#ifndef PROTO_SDP
+/* on solaris, SDP is a new type of protocol */
+#define PROTO_SDP 257
+#endif
+#define _PR_HAVE_SDP
+#elif defined(LINUX)
+#ifndef AF_INET_SDP
+/* on linux, SDP is a new type of address family */
+#define AF_INET_SDP 27
+#endif
+#define _PR_HAVE_SDP
+#endif /* LINUX */
+
+static PRFileDesc *pt_SetMethods(
+ PRIntn osfd, PRDescType type, PRBool isAcceptedSocket, PRBool imported);
+
+static PRLock *_pr_flock_lock; /* For PR_LockFile() etc. */
+static PRCondVar *_pr_flock_cv; /* For PR_LockFile() etc. */
+static PRLock *_pr_rename_lock; /* For PR_Rename() */
+
+/**************************************************************************/
+
+/* These two functions are only used in assertions. */
+#if defined(DEBUG)
+
+PRBool IsValidNetAddr(const PRNetAddr *addr)
+{
+ if ((addr != NULL)
+ && (addr->raw.family != AF_UNIX)
+ && (addr->raw.family != PR_AF_INET6)
+ && (addr->raw.family != AF_INET)) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+static PRBool IsValidNetAddrLen(const PRNetAddr *addr, PRInt32 addr_len)
+{
+ /*
+ * The definition of the length of a Unix domain socket address
+ * is not uniform, so we don't check it.
+ */
+ if ((addr != NULL)
+ && (addr->raw.family != AF_UNIX)
+ && (PR_NETADDR_SIZE(addr) != addr_len)) {
+#if defined(LINUX) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 1
+ /*
+ * In glibc 2.1, struct sockaddr_in6 is 24 bytes. In glibc 2.2
+ * and in the 2.4 kernel, struct sockaddr_in6 has the scope_id
+ * field and is 28 bytes. It is possible for socket functions
+ * to return an addr_len greater than sizeof(struct sockaddr_in6).
+ * We need to allow that. (Bugzilla bug #77264)
+ */
+ if ((PR_AF_INET6 == addr->raw.family)
+ && (sizeof(addr->ipv6) == addr_len)) {
+ return PR_TRUE;
+ }
+#endif
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+#endif /* DEBUG */
+
+/*****************************************************************************/
+/************************* I/O Continuation machinery ************************/
+/*****************************************************************************/
+
+/*
+ * The polling interval defines the maximum amount of time that a thread
+ * might hang up before an interrupt is noticed.
+ */
+#define PT_DEFAULT_POLL_MSEC 5000
+#if defined(_PR_POLL_WITH_SELECT)
+#define PT_DEFAULT_SELECT_SEC (PT_DEFAULT_POLL_MSEC/PR_MSEC_PER_SEC)
+#define PT_DEFAULT_SELECT_USEC \
+ ((PT_DEFAULT_POLL_MSEC % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC)
+#endif
+
+/*
+ * pt_SockLen is the type for the length of a socket address
+ * structure, used in the address length argument to bind,
+ * connect, accept, getsockname, getpeername, etc. Posix.1g
+ * defines this type as socklen_t. It is size_t or int on
+ * most current systems.
+ */
+#if defined(HAVE_SOCKLEN_T) \
+ || (defined(__GLIBC__) && __GLIBC__ >= 2)
+typedef socklen_t pt_SockLen;
+#elif (defined(AIX) && !defined(AIX4_1))
+typedef PRSize pt_SockLen;
+#else
+typedef PRIntn pt_SockLen;
+#endif
+
+typedef struct pt_Continuation pt_Continuation;
+typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revents);
+
+typedef enum pr_ContuationStatus
+{
+ pt_continuation_pending,
+ pt_continuation_done
+} pr_ContuationStatus;
+
+struct pt_Continuation
+{
+ /* The building of the continuation operation */
+ ContinuationFn function; /* what function to continue */
+ union {
+ PRIntn osfd;
+ } arg1; /* #1 - the op's fd */
+ union {
+ void* buffer;
+ } arg2; /* #2 - primary transfer buffer */
+ union {
+ PRSize amount; /* #3 - size of 'buffer', or */
+ pt_SockLen *addr_len; /* - length of address */
+#ifdef HPUX11
+ /*
+ * For sendfile()
+ */
+ struct file_spec {
+ off_t offset; /* offset in file to send */
+ size_t nbytes; /* length of file data to send */
+ size_t st_size; /* file size */
+ } file_spec;
+#endif
+ } arg3;
+ union {
+ PRIntn flags;
+ } arg4; /* #4 - read/write flags */
+ union {
+ PRNetAddr *addr;
+ } arg5; /* #5 - send/recv address */
+
+#ifdef HPUX11
+ /*
+ * For sendfile()
+ */
+ int filedesc; /* descriptor of file to send */
+ int nbytes_to_send; /* size of header and file */
+#endif /* HPUX11 */
+
+#ifdef SOLARIS
+ /*
+ * For sendfilev()
+ */
+ int nbytes_to_send; /* size of header and file */
+#endif /* SOLARIS */
+
+#ifdef LINUX
+ /*
+ * For sendfile()
+ */
+ int in_fd; /* descriptor of file to send */
+ off_t offset;
+ size_t count;
+#endif /* LINUX */
+
+ PRIntervalTime timeout; /* client (relative) timeout */
+
+ PRInt16 event; /* flags for poll()'s events */
+
+ /*
+ ** The representation and notification of the results of the operation.
+ ** These function can either return an int return code or a pointer to
+ ** some object.
+ */
+ union {
+ PRSize code;
+ void *object;
+ } result;
+
+ PRIntn syserrno; /* in case it failed, why (errno) */
+ pr_ContuationStatus status; /* the status of the operation */
+};
+
+#if defined(DEBUG)
+
+PTDebug pt_debug; /* this is shared between several modules */
+
+PR_IMPLEMENT(void) PT_FPrintStats(PRFileDesc *debug_out, const char *msg)
+{
+ PTDebug stats;
+ char buffer[100];
+ PRExplodedTime tod;
+ PRInt64 elapsed, aMil;
+ stats = pt_debug; /* a copy */
+ PR_ExplodeTime(stats.timeStarted, PR_LocalTimeParameters, &tod);
+ (void)PR_FormatTime(buffer, sizeof(buffer), "%T", &tod);
+
+ LL_SUB(elapsed, PR_Now(), stats.timeStarted);
+ LL_I2L(aMil, 1000000);
+ LL_DIV(elapsed, elapsed, aMil);
+
+ if (NULL != msg) {
+ PR_fprintf(debug_out, "%s", msg);
+ }
+ PR_fprintf(
+ debug_out, "\tstarted: %s[%lld]\n", buffer, elapsed);
+ PR_fprintf(
+ debug_out, "\tlocks [created: %u, destroyed: %u]\n",
+ stats.locks_created, stats.locks_destroyed);
+ PR_fprintf(
+ debug_out, "\tlocks [acquired: %u, released: %u]\n",
+ stats.locks_acquired, stats.locks_released);
+ PR_fprintf(
+ debug_out, "\tcvars [created: %u, destroyed: %u]\n",
+ stats.cvars_created, stats.cvars_destroyed);
+ PR_fprintf(
+ debug_out, "\tcvars [notified: %u, delayed_delete: %u]\n",
+ stats.cvars_notified, stats.delayed_cv_deletes);
+} /* PT_FPrintStats */
+
+#else
+
+PR_IMPLEMENT(void) PT_FPrintStats(PRFileDesc *debug_out, const char *msg)
+{
+ /* do nothing */
+} /* PT_FPrintStats */
+
+#endif /* DEBUG */
+
+#if defined(_PR_POLL_WITH_SELECT)
+/*
+ * HPUX report the POLLHUP event for a socket when the
+ * shutdown(SHUT_WR) operation is called for the remote end, even though
+ * the socket is still writeable. Use select(), instead of poll(), to
+ * workaround this problem.
+ */
+static void pt_poll_now_with_select(pt_Continuation *op)
+{
+ PRInt32 msecs;
+ fd_set rd, wr, *rdp, *wrp;
+ struct timeval tv;
+ PRIntervalTime epoch, now, elapsed, remaining;
+ PRBool wait_for_remaining;
+ PRThread *self = PR_GetCurrentThread();
+
+ PR_ASSERT(PR_INTERVAL_NO_WAIT != op->timeout);
+ PR_ASSERT(op->arg1.osfd < FD_SETSIZE);
+
+ switch (op->timeout) {
+ case PR_INTERVAL_NO_TIMEOUT:
+ tv.tv_sec = PT_DEFAULT_SELECT_SEC;
+ tv.tv_usec = PT_DEFAULT_SELECT_USEC;
+ do
+ {
+ PRIntn rv;
+
+ if (op->event & POLLIN) {
+ FD_ZERO(&rd);
+ FD_SET(op->arg1.osfd, &rd);
+ rdp = &rd;
+ } else {
+ rdp = NULL;
+ }
+ if (op->event & POLLOUT) {
+ FD_ZERO(&wr);
+ FD_SET(op->arg1.osfd, &wr);
+ wrp = &wr;
+ } else {
+ wrp = NULL;
+ }
+
+ rv = select(op->arg1.osfd + 1, rdp, wrp, NULL, &tv);
+
+ if (_PT_THREAD_INTERRUPTED(self))
+ {
+ self->state &= ~PT_THREAD_ABORTED;
+ op->result.code = -1;
+ op->syserrno = EINTR;
+ op->status = pt_continuation_done;
+ return;
+ }
+
+ if ((-1 == rv) && ((errno == EINTR) || (errno == EAGAIN))) {
+ continue; /* go around the loop again */
+ }
+
+ if (rv > 0)
+ {
+ PRInt16 revents = 0;
+
+ if ((op->event & POLLIN) && FD_ISSET(op->arg1.osfd, &rd)) {
+ revents |= POLLIN;
+ }
+ if ((op->event & POLLOUT) && FD_ISSET(op->arg1.osfd, &wr)) {
+ revents |= POLLOUT;
+ }
+
+ if (op->function(op, revents)) {
+ op->status = pt_continuation_done;
+ }
+ } else if (rv == -1) {
+ op->result.code = -1;
+ op->syserrno = errno;
+ op->status = pt_continuation_done;
+ }
+ /* else, select timed out */
+ } while (pt_continuation_done != op->status);
+ break;
+ default:
+ now = epoch = PR_IntervalNow();
+ remaining = op->timeout;
+ do
+ {
+ PRIntn rv;
+
+ if (op->event & POLLIN) {
+ FD_ZERO(&rd);
+ FD_SET(op->arg1.osfd, &rd);
+ rdp = &rd;
+ } else {
+ rdp = NULL;
+ }
+ if (op->event & POLLOUT) {
+ FD_ZERO(&wr);
+ FD_SET(op->arg1.osfd, &wr);
+ wrp = &wr;
+ } else {
+ wrp = NULL;
+ }
+
+ wait_for_remaining = PR_TRUE;
+ msecs = (PRInt32)PR_IntervalToMilliseconds(remaining);
+ if (msecs > PT_DEFAULT_POLL_MSEC) {
+ wait_for_remaining = PR_FALSE;
+ msecs = PT_DEFAULT_POLL_MSEC;
+ }
+ tv.tv_sec = msecs/PR_MSEC_PER_SEC;
+ tv.tv_usec = (msecs % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC;
+ rv = select(op->arg1.osfd + 1, rdp, wrp, NULL, &tv);
+
+ if (_PT_THREAD_INTERRUPTED(self))
+ {
+ self->state &= ~PT_THREAD_ABORTED;
+ op->result.code = -1;
+ op->syserrno = EINTR;
+ op->status = pt_continuation_done;
+ return;
+ }
+
+ if (rv > 0) {
+ PRInt16 revents = 0;
+
+ if ((op->event & POLLIN) && FD_ISSET(op->arg1.osfd, &rd)) {
+ revents |= POLLIN;
+ }
+ if ((op->event & POLLOUT) && FD_ISSET(op->arg1.osfd, &wr)) {
+ revents |= POLLOUT;
+ }
+
+ if (op->function(op, revents)) {
+ op->status = pt_continuation_done;
+ }
+
+ } else if ((rv == 0) ||
+ ((errno == EINTR) || (errno == EAGAIN))) {
+ if (rv == 0) { /* select timed out */
+ if (wait_for_remaining) {
+ now += remaining;
+ }
+ else {
+ now += PR_MillisecondsToInterval(msecs);
+ }
+ } else {
+ now = PR_IntervalNow();
+ }
+ elapsed = (PRIntervalTime) (now - epoch);
+ if (elapsed >= op->timeout) {
+ op->result.code = -1;
+ op->syserrno = ETIMEDOUT;
+ op->status = pt_continuation_done;
+ } else {
+ remaining = op->timeout - elapsed;
+ }
+ } else {
+ op->result.code = -1;
+ op->syserrno = errno;
+ op->status = pt_continuation_done;
+ }
+ } while (pt_continuation_done != op->status);
+ break;
+ }
+
+} /* pt_poll_now_with_select */
+
+#endif /* _PR_POLL_WITH_SELECT */
+
+static void pt_poll_now(pt_Continuation *op)
+{
+ PRInt32 msecs;
+ PRIntervalTime epoch, now, elapsed, remaining;
+ PRBool wait_for_remaining;
+ PRThread *self = PR_GetCurrentThread();
+
+ PR_ASSERT(PR_INTERVAL_NO_WAIT != op->timeout);
+#if defined (_PR_POLL_WITH_SELECT)
+ /*
+ * If the fd is small enough call the select-based poll operation
+ */
+ if (op->arg1.osfd < FD_SETSIZE) {
+ pt_poll_now_with_select(op);
+ return;
+ }
+#endif
+
+ switch (op->timeout) {
+ case PR_INTERVAL_NO_TIMEOUT:
+ msecs = PT_DEFAULT_POLL_MSEC;
+ do
+ {
+ PRIntn rv;
+ struct pollfd tmp_pfd;
+
+ tmp_pfd.revents = 0;
+ tmp_pfd.fd = op->arg1.osfd;
+ tmp_pfd.events = op->event;
+
+ rv = poll(&tmp_pfd, 1, msecs);
+
+ if (_PT_THREAD_INTERRUPTED(self))
+ {
+ self->state &= ~PT_THREAD_ABORTED;
+ op->result.code = -1;
+ op->syserrno = EINTR;
+ op->status = pt_continuation_done;
+ return;
+ }
+
+ if ((-1 == rv) && ((errno == EINTR) || (errno == EAGAIN))) {
+ continue; /* go around the loop again */
+ }
+
+ if (rv > 0)
+ {
+ PRInt16 events = tmp_pfd.events;
+ PRInt16 revents = tmp_pfd.revents;
+
+ if ((revents & POLLNVAL) /* busted in all cases */
+ || ((events & POLLOUT) && (revents & POLLHUP)))
+ /* write op & hup */
+ {
+ op->result.code = -1;
+ if (POLLNVAL & revents) {
+ op->syserrno = EBADF;
+ }
+ else if (POLLHUP & revents) {
+ op->syserrno = EPIPE;
+ }
+ op->status = pt_continuation_done;
+ } else {
+ if (op->function(op, revents)) {
+ op->status = pt_continuation_done;
+ }
+ }
+ } else if (rv == -1) {
+ op->result.code = -1;
+ op->syserrno = errno;
+ op->status = pt_continuation_done;
+ }
+ /* else, poll timed out */
+ } while (pt_continuation_done != op->status);
+ break;
+ default:
+ now = epoch = PR_IntervalNow();
+ remaining = op->timeout;
+ do
+ {
+ PRIntn rv;
+ struct pollfd tmp_pfd;
+
+ tmp_pfd.revents = 0;
+ tmp_pfd.fd = op->arg1.osfd;
+ tmp_pfd.events = op->event;
+
+ wait_for_remaining = PR_TRUE;
+ msecs = (PRInt32)PR_IntervalToMilliseconds(remaining);
+ if (msecs > PT_DEFAULT_POLL_MSEC)
+ {
+ wait_for_remaining = PR_FALSE;
+ msecs = PT_DEFAULT_POLL_MSEC;
+ }
+ rv = poll(&tmp_pfd, 1, msecs);
+
+ if (_PT_THREAD_INTERRUPTED(self))
+ {
+ self->state &= ~PT_THREAD_ABORTED;
+ op->result.code = -1;
+ op->syserrno = EINTR;
+ op->status = pt_continuation_done;
+ return;
+ }
+
+ if (rv > 0)
+ {
+ PRInt16 events = tmp_pfd.events;
+ PRInt16 revents = tmp_pfd.revents;
+
+ if ((revents & POLLNVAL) /* busted in all cases */
+ || ((events & POLLOUT) && (revents & POLLHUP)))
+ /* write op & hup */
+ {
+ op->result.code = -1;
+ if (POLLNVAL & revents) {
+ op->syserrno = EBADF;
+ }
+ else if (POLLHUP & revents) {
+ op->syserrno = EPIPE;
+ }
+ op->status = pt_continuation_done;
+ } else {
+ if (op->function(op, revents))
+ {
+ op->status = pt_continuation_done;
+ }
+ }
+ } else if ((rv == 0) ||
+ ((errno == EINTR) || (errno == EAGAIN))) {
+ if (rv == 0) /* poll timed out */
+ {
+ if (wait_for_remaining) {
+ now += remaining;
+ }
+ else {
+ now += PR_MillisecondsToInterval(msecs);
+ }
+ }
+ else {
+ now = PR_IntervalNow();
+ }
+ elapsed = (PRIntervalTime) (now - epoch);
+ if (elapsed >= op->timeout) {
+ op->result.code = -1;
+ op->syserrno = ETIMEDOUT;
+ op->status = pt_continuation_done;
+ } else {
+ remaining = op->timeout - elapsed;
+ }
+ } else {
+ op->result.code = -1;
+ op->syserrno = errno;
+ op->status = pt_continuation_done;
+ }
+ } while (pt_continuation_done != op->status);
+ break;
+ }
+
+} /* pt_poll_now */
+
+static PRIntn pt_Continue(pt_Continuation *op)
+{
+ op->status = pt_continuation_pending; /* set default value */
+ /*
+ * let each thread call poll directly
+ */
+ pt_poll_now(op);
+ PR_ASSERT(pt_continuation_done == op->status);
+ return op->result.code;
+} /* pt_Continue */
+
+/*****************************************************************************/
+/*********************** specific continuation functions *********************/
+/*****************************************************************************/
+static PRBool pt_connect_cont(pt_Continuation *op, PRInt16 revents)
+{
+ op->syserrno = _MD_unix_get_nonblocking_connect_error(op->arg1.osfd);
+ if (op->syserrno != 0) {
+ op->result.code = -1;
+ } else {
+ op->result.code = 0;
+ }
+ return PR_TRUE; /* this one is cooked */
+} /* pt_connect_cont */
+
+static PRBool pt_accept_cont(pt_Continuation *op, PRInt16 revents)
+{
+ op->syserrno = 0;
+ op->result.code = accept(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.addr_len);
+ if (-1 == op->result.code)
+ {
+ op->syserrno = errno;
+ if (EWOULDBLOCK == errno || EAGAIN == errno || ECONNABORTED == errno) {
+ return PR_FALSE; /* do nothing - this one ain't finished */
+ }
+ }
+ return PR_TRUE;
+} /* pt_accept_cont */
+
+static PRBool pt_read_cont(pt_Continuation *op, PRInt16 revents)
+{
+ /*
+ * Any number of bytes will complete the operation. It need
+ * not (and probably will not) satisfy the request. The only
+ * error we continue is EWOULDBLOCK|EAGAIN.
+ */
+ op->result.code = read(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount);
+ op->syserrno = errno;
+ return ((-1 == op->result.code) &&
+ (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_read_cont */
+
+static PRBool pt_recv_cont(pt_Continuation *op, PRInt16 revents)
+{
+ /*
+ * Any number of bytes will complete the operation. It need
+ * not (and probably will not) satisfy the request. The only
+ * error we continue is EWOULDBLOCK|EAGAIN.
+ */
+#if defined(SOLARIS)
+ if (0 == op->arg4.flags)
+ op->result.code = read(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount);
+ else
+ op->result.code = recv(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags);
+#else
+ op->result.code = recv(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags);
+#endif
+ op->syserrno = errno;
+ return ((-1 == op->result.code) &&
+ (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_recv_cont */
+
+static PRBool pt_send_cont(pt_Continuation *op, PRInt16 revents)
+{
+ PRIntn bytes;
+#if defined(SOLARIS)
+ PRInt32 tmp_amount = op->arg3.amount;
+#endif
+ /*
+ * We want to write the entire amount out, no matter how many
+ * tries it takes. Keep advancing the buffer and the decrementing
+ * the amount until the amount goes away. Return the total bytes
+ * (which should be the original amount) when finished (or an
+ * error).
+ */
+#if defined(SOLARIS)
+retry:
+ bytes = write(op->arg1.osfd, op->arg2.buffer, tmp_amount);
+#else
+ bytes = send(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags);
+#endif
+ op->syserrno = errno;
+
+#if defined(SOLARIS)
+ /*
+ * The write system call has been reported to return the ERANGE error
+ * on occasion. Try to write in smaller chunks to workaround this bug.
+ */
+ if ((bytes == -1) && (op->syserrno == ERANGE))
+ {
+ if (tmp_amount > 1)
+ {
+ tmp_amount = tmp_amount/2; /* half the bytes */
+ goto retry;
+ }
+ }
+#endif
+
+ if (bytes >= 0) /* this is progress */
+ {
+ char *bp = (char*)op->arg2.buffer;
+ bp += bytes; /* adjust the buffer pointer */
+ op->arg2.buffer = bp;
+ op->result.code += bytes; /* accumulate the number sent */
+ op->arg3.amount -= bytes; /* and reduce the required count */
+ return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
+ }
+ if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno))
+ {
+ op->result.code = -1;
+ return PR_TRUE;
+ }
+ else {
+ return PR_FALSE;
+ }
+} /* pt_send_cont */
+
+static PRBool pt_write_cont(pt_Continuation *op, PRInt16 revents)
+{
+ PRIntn bytes;
+ /*
+ * We want to write the entire amount out, no matter how many
+ * tries it takes. Keep advancing the buffer and the decrementing
+ * the amount until the amount goes away. Return the total bytes
+ * (which should be the original amount) when finished (or an
+ * error).
+ */
+ bytes = write(op->arg1.osfd, op->arg2.buffer, op->arg3.amount);
+ op->syserrno = errno;
+ if (bytes >= 0) /* this is progress */
+ {
+ char *bp = (char*)op->arg2.buffer;
+ bp += bytes; /* adjust the buffer pointer */
+ op->arg2.buffer = bp;
+ op->result.code += bytes; /* accumulate the number sent */
+ op->arg3.amount -= bytes; /* and reduce the required count */
+ return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
+ }
+ if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno))
+ {
+ op->result.code = -1;
+ return PR_TRUE;
+ }
+ else {
+ return PR_FALSE;
+ }
+} /* pt_write_cont */
+
+static PRBool pt_writev_cont(pt_Continuation *op, PRInt16 revents)
+{
+ PRIntn bytes;
+ struct iovec *iov = (struct iovec*)op->arg2.buffer;
+ /*
+ * Same rules as write, but continuing seems to be a bit more
+ * complicated. As the number of bytes sent grows, we have to
+ * redefine the vector we're pointing at. We might have to
+ * modify an individual vector parms or we might have to eliminate
+ * a pair altogether.
+ */
+ bytes = writev(op->arg1.osfd, iov, op->arg3.amount);
+ op->syserrno = errno;
+ if (bytes >= 0) /* this is progress */
+ {
+ PRIntn iov_index;
+ op->result.code += bytes; /* accumulate the number sent */
+ for (iov_index = 0; iov_index < op->arg3.amount; ++iov_index)
+ {
+ /* how much progress did we make in the i/o vector? */
+ if (bytes < iov[iov_index].iov_len)
+ {
+ /* this element's not done yet */
+ char **bp = (char**)&(iov[iov_index].iov_base);
+ iov[iov_index].iov_len -= bytes; /* there's that much left */
+ *bp += bytes; /* starting there */
+ break; /* go off and do that */
+ }
+ bytes -= iov[iov_index].iov_len; /* that element's consumed */
+ }
+ op->arg2.buffer = &iov[iov_index]; /* new start of array */
+ op->arg3.amount -= iov_index; /* and array length */
+ return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
+ }
+ if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno))
+ {
+ op->result.code = -1;
+ return PR_TRUE;
+ }
+ else {
+ return PR_FALSE;
+ }
+} /* pt_writev_cont */
+
+static PRBool pt_sendto_cont(pt_Continuation *op, PRInt16 revents)
+{
+ PRIntn bytes = sendto(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags,
+ (struct sockaddr*)op->arg5.addr, PR_NETADDR_SIZE(op->arg5.addr));
+ op->syserrno = errno;
+ if (bytes >= 0) /* this is progress */
+ {
+ char *bp = (char*)op->arg2.buffer;
+ bp += bytes; /* adjust the buffer pointer */
+ op->arg2.buffer = bp;
+ op->result.code += bytes; /* accumulate the number sent */
+ op->arg3.amount -= bytes; /* and reduce the required count */
+ return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
+ }
+ if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno))
+ {
+ op->result.code = -1;
+ return PR_TRUE;
+ }
+ else {
+ return PR_FALSE;
+ }
+} /* pt_sendto_cont */
+
+static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents)
+{
+ pt_SockLen addr_len = sizeof(PRNetAddr);
+ op->result.code = recvfrom(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount,
+ op->arg4.flags, (struct sockaddr*)op->arg5.addr, &addr_len);
+ op->syserrno = errno;
+ return ((-1 == op->result.code) &&
+ (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_recvfrom_cont */
+
+#ifdef AIX
+static PRBool pt_aix_sendfile_cont(pt_Continuation *op, PRInt16 revents)
+{
+ struct sf_parms *sf_struct = (struct sf_parms *) op->arg2.buffer;
+ ssize_t rv;
+ unsigned long long saved_file_offset;
+ long long saved_file_bytes;
+
+ saved_file_offset = sf_struct->file_offset;
+ saved_file_bytes = sf_struct->file_bytes;
+ sf_struct->bytes_sent = 0;
+
+ if ((sf_struct->file_bytes > 0) && (sf_struct->file_size > 0))
+ PR_ASSERT((sf_struct->file_bytes + sf_struct->file_offset) <=
+ sf_struct->file_size);
+ rv = AIX_SEND_FILE(&op->arg1.osfd, sf_struct, op->arg4.flags);
+ op->syserrno = errno;
+
+ if (rv != -1) {
+ op->result.code += sf_struct->bytes_sent;
+ /*
+ * A bug in AIX 4.3.2 prevents the 'file_bytes' field from
+ * being updated. So, 'file_bytes' is maintained by NSPR to
+ * avoid conflict when this bug is fixed in AIX, in the future.
+ */
+ if (saved_file_bytes != -1) {
+ saved_file_bytes -= (sf_struct->file_offset - saved_file_offset);
+ }
+ sf_struct->file_bytes = saved_file_bytes;
+ } else if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) {
+ op->result.code = -1;
+ } else {
+ return PR_FALSE;
+ }
+
+ if (rv == 1) { /* more data to send */
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+#endif /* AIX */
+
+#ifdef HPUX11
+static PRBool pt_hpux_sendfile_cont(pt_Continuation *op, PRInt16 revents)
+{
+ struct iovec *hdtrl = (struct iovec *) op->arg2.buffer;
+ int count;
+
+ count = sendfile(op->arg1.osfd, op->filedesc, op->arg3.file_spec.offset,
+ op->arg3.file_spec.nbytes, hdtrl, op->arg4.flags);
+ PR_ASSERT(count <= op->nbytes_to_send);
+ op->syserrno = errno;
+
+ if (count != -1) {
+ op->result.code += count;
+ } else if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) {
+ op->result.code = -1;
+ } else {
+ return PR_FALSE;
+ }
+ if (count != -1 && count < op->nbytes_to_send) {
+ if (count < hdtrl[0].iov_len) {
+ /* header not sent */
+
+ hdtrl[0].iov_base = ((char *) hdtrl[0].iov_base) + count;
+ hdtrl[0].iov_len -= count;
+
+ } else if (count < (hdtrl[0].iov_len + op->arg3.file_spec.nbytes)) {
+ /* header sent, file not sent */
+ PRUint32 file_nbytes_sent = count - hdtrl[0].iov_len;
+
+ hdtrl[0].iov_base = NULL;
+ hdtrl[0].iov_len = 0;
+
+ op->arg3.file_spec.offset += file_nbytes_sent;
+ op->arg3.file_spec.nbytes -= file_nbytes_sent;
+ } else if (count < (hdtrl[0].iov_len + op->arg3.file_spec.nbytes +
+ hdtrl[1].iov_len)) {
+ PRUint32 trailer_nbytes_sent = count - (hdtrl[0].iov_len +
+ op->arg3.file_spec.nbytes);
+
+ /* header sent, file sent, trailer not sent */
+
+ hdtrl[0].iov_base = NULL;
+ hdtrl[0].iov_len = 0;
+ /*
+ * set file offset and len so that no more file data is
+ * sent
+ */
+ op->arg3.file_spec.offset = op->arg3.file_spec.st_size;
+ op->arg3.file_spec.nbytes = 0;
+
+ hdtrl[1].iov_base =((char *) hdtrl[1].iov_base)+ trailer_nbytes_sent;
+ hdtrl[1].iov_len -= trailer_nbytes_sent;
+ }
+ op->nbytes_to_send -= count;
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+#endif /* HPUX11 */
+
+#ifdef SOLARIS
+static PRBool pt_solaris_sendfile_cont(pt_Continuation *op, PRInt16 revents)
+{
+ struct sendfilevec *vec = (struct sendfilevec *) op->arg2.buffer;
+ size_t xferred;
+ ssize_t count;
+
+ count = SOLARIS_SENDFILEV(op->arg1.osfd, vec, op->arg3.amount, &xferred);
+ op->syserrno = errno;
+ PR_ASSERT((count == -1) || (count == xferred));
+
+ if (count == -1) {
+ if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN
+ && op->syserrno != EINTR) {
+ op->result.code = -1;
+ return PR_TRUE;
+ }
+ count = xferred;
+ } else if (count == 0) {
+ /*
+ * We are now at EOF. The file was truncated. Solaris sendfile is
+ * supposed to return 0 and no error in this case, though some versions
+ * may return -1 and EINVAL .
+ */
+ op->result.code = -1;
+ op->syserrno = 0; /* will be treated as EOF */
+ return PR_TRUE;
+ }
+ PR_ASSERT(count <= op->nbytes_to_send);
+
+ op->result.code += count;
+ if (count < op->nbytes_to_send) {
+ op->nbytes_to_send -= count;
+
+ while (count >= vec->sfv_len) {
+ count -= vec->sfv_len;
+ vec++;
+ op->arg3.amount--;
+ }
+ PR_ASSERT(op->arg3.amount > 0);
+
+ vec->sfv_off += count;
+ vec->sfv_len -= count;
+ PR_ASSERT(vec->sfv_len > 0);
+ op->arg2.buffer = vec;
+
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+#endif /* SOLARIS */
+
+#ifdef LINUX
+static PRBool pt_linux_sendfile_cont(pt_Continuation *op, PRInt16 revents)
+{
+ ssize_t rv;
+ off_t oldoffset;
+
+ oldoffset = op->offset;
+ rv = sendfile(op->arg1.osfd, op->in_fd, &op->offset, op->count);
+ op->syserrno = errno;
+
+ if (rv == -1) {
+ if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) {
+ op->result.code = -1;
+ return PR_TRUE;
+ }
+ rv = 0;
+ }
+ PR_ASSERT(rv == op->offset - oldoffset);
+ op->result.code += rv;
+ if (rv < op->count) {
+ op->count -= rv;
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+#endif /* LINUX */
+
+void _PR_InitIO(void)
+{
+#if defined(DEBUG)
+ memset(&pt_debug, 0, sizeof(PTDebug));
+ pt_debug.timeStarted = PR_Now();
+#endif
+
+ _pr_flock_lock = PR_NewLock();
+ PR_ASSERT(NULL != _pr_flock_lock);
+ _pr_flock_cv = PR_NewCondVar(_pr_flock_lock);
+ PR_ASSERT(NULL != _pr_flock_cv);
+ _pr_rename_lock = PR_NewLock();
+ PR_ASSERT(NULL != _pr_rename_lock);
+
+ _PR_InitFdCache(); /* do that */
+
+ _pr_stdin = pt_SetMethods(0, PR_DESC_FILE, PR_FALSE, PR_TRUE);
+ _pr_stdout = pt_SetMethods(1, PR_DESC_FILE, PR_FALSE, PR_TRUE);
+ _pr_stderr = pt_SetMethods(2, PR_DESC_FILE, PR_FALSE, PR_TRUE);
+ PR_ASSERT(_pr_stdin && _pr_stdout && _pr_stderr);
+
+#ifdef _PR_IPV6_V6ONLY_PROBE
+ /* In Mac OS X v10.3 Panther Beta the IPV6_V6ONLY socket option
+ * is turned on by default, contrary to what RFC 3493, Section
+ * 5.3 says. So we have to turn it off. Find out whether we
+ * are running on such a system.
+ */
+ {
+ int osfd;
+ osfd = socket(AF_INET6, SOCK_STREAM, 0);
+ if (osfd != -1) {
+ int on;
+ socklen_t optlen = sizeof(on);
+ if (getsockopt(osfd, IPPROTO_IPV6, IPV6_V6ONLY,
+ &on, &optlen) == 0) {
+ _pr_ipv6_v6only_on_by_default = on;
+ }
+ close(osfd);
+ }
+ }
+#endif
+} /* _PR_InitIO */
+
+void _PR_CleanupIO(void)
+{
+ _PR_Putfd(_pr_stdin);
+ _pr_stdin = NULL;
+ _PR_Putfd(_pr_stdout);
+ _pr_stdout = NULL;
+ _PR_Putfd(_pr_stderr);
+ _pr_stderr = NULL;
+
+ _PR_CleanupFdCache();
+
+ if (_pr_flock_cv)
+ {
+ PR_DestroyCondVar(_pr_flock_cv);
+ _pr_flock_cv = NULL;
+ }
+ if (_pr_flock_lock)
+ {
+ PR_DestroyLock(_pr_flock_lock);
+ _pr_flock_lock = NULL;
+ }
+ if (_pr_rename_lock)
+ {
+ PR_DestroyLock(_pr_rename_lock);
+ _pr_rename_lock = NULL;
+ }
+} /* _PR_CleanupIO */
+
+PR_IMPLEMENT(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD osfd)
+{
+ PRFileDesc *result = NULL;
+ PR_ASSERT(osfd >= PR_StandardInput && osfd <= PR_StandardError);
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ switch (osfd)
+ {
+ case PR_StandardInput: result = _pr_stdin; break;
+ case PR_StandardOutput: result = _pr_stdout; break;
+ case PR_StandardError: result = _pr_stderr; break;
+ default:
+ (void)PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ }
+ return result;
+} /* PR_GetSpecialFD */
+
+/*****************************************************************************/
+/***************************** I/O private methods ***************************/
+/*****************************************************************************/
+
+static PRBool pt_TestAbort(void)
+{
+ PRThread *me = PR_GetCurrentThread();
+ if(_PT_THREAD_INTERRUPTED(me))
+ {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ me->state &= ~PT_THREAD_ABORTED;
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+} /* pt_TestAbort */
+
+static void pt_MapError(void (*mapper)(PRIntn), PRIntn syserrno)
+{
+ switch (syserrno)
+ {
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); break;
+ case ETIMEDOUT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0); break;
+ default:
+ mapper(syserrno);
+ }
+} /* pt_MapError */
+
+static PRStatus pt_Close(PRFileDesc *fd)
+{
+ if ((NULL == fd) || (NULL == fd->secret)
+ || ((_PR_FILEDESC_OPEN != fd->secret->state)
+ && (_PR_FILEDESC_CLOSED != fd->secret->state)))
+ {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ if (_PR_FILEDESC_OPEN == fd->secret->state)
+ {
+ if (-1 == close(fd->secret->md.osfd))
+ {
+ pt_MapError(_PR_MD_MAP_CLOSE_ERROR, errno);
+ return PR_FAILURE;
+ }
+ fd->secret->state = _PR_FILEDESC_CLOSED;
+ }
+ _PR_Putfd(fd);
+ return PR_SUCCESS;
+} /* pt_Close */
+
+static PRInt32 pt_Read(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ PRInt32 syserrno, bytes = -1;
+
+ if (pt_TestAbort()) {
+ return bytes;
+ }
+
+ bytes = read(fd->secret->md.osfd, buf, amount);
+ syserrno = errno;
+
+ if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking))
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = buf;
+ op.arg3.amount = amount;
+ op.timeout = PR_INTERVAL_NO_TIMEOUT;
+ op.function = pt_read_cont;
+ op.event = POLLIN | POLLPRI;
+ bytes = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ if (bytes < 0) {
+ pt_MapError(_PR_MD_MAP_READ_ERROR, syserrno);
+ }
+ return bytes;
+} /* pt_Read */
+
+static PRInt32 pt_Write(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ PRInt32 syserrno, bytes = -1;
+ PRBool fNeedContinue = PR_FALSE;
+
+ if (pt_TestAbort()) {
+ return bytes;
+ }
+
+ bytes = write(fd->secret->md.osfd, buf, amount);
+ syserrno = errno;
+
+ if ( (bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking) )
+ {
+ buf = (char *) buf + bytes;
+ amount -= bytes;
+ fNeedContinue = PR_TRUE;
+ }
+ if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking) )
+ {
+ bytes = 0;
+ fNeedContinue = PR_TRUE;
+ }
+
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = (void*)buf;
+ op.arg3.amount = amount;
+ op.timeout = PR_INTERVAL_NO_TIMEOUT;
+ op.result.code = bytes; /* initialize the number sent */
+ op.function = pt_write_cont;
+ op.event = POLLOUT | POLLPRI;
+ bytes = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ if (bytes == -1) {
+ pt_MapError(_PR_MD_MAP_WRITE_ERROR, syserrno);
+ }
+ return bytes;
+} /* pt_Write */
+
+static PRInt32 pt_Writev(
+ PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_len, PRIntervalTime timeout)
+{
+ PRIntn iov_index;
+ PRBool fNeedContinue = PR_FALSE;
+ PRInt32 syserrno, bytes, rv = -1;
+ struct iovec osiov_local[PR_MAX_IOVECTOR_SIZE], *osiov;
+ int osiov_len;
+
+ if (pt_TestAbort()) {
+ return rv;
+ }
+
+ /* Ensured by PR_Writev */
+ PR_ASSERT(iov_len <= PR_MAX_IOVECTOR_SIZE);
+
+ /*
+ * We can't pass iov to writev because PRIOVec and struct iovec
+ * may not be binary compatible. Make osiov a copy of iov and
+ * pass osiov to writev. We can modify osiov if we need to
+ * continue the operation.
+ */
+ osiov = osiov_local;
+ osiov_len = iov_len;
+ for (iov_index = 0; iov_index < osiov_len; iov_index++)
+ {
+ osiov[iov_index].iov_base = iov[iov_index].iov_base;
+ osiov[iov_index].iov_len = iov[iov_index].iov_len;
+ }
+
+ rv = bytes = writev(fd->secret->md.osfd, osiov, osiov_len);
+ syserrno = errno;
+
+ if (!fd->secret->nonblocking)
+ {
+ if (bytes >= 0)
+ {
+ /*
+ * If we moved some bytes, how does that implicate the
+ * i/o vector list? In other words, exactly where are
+ * we within that array? What are the parameters for
+ * resumption? Maybe we're done!
+ */
+ for ( ; osiov_len > 0; osiov++, osiov_len--)
+ {
+ if (bytes < osiov->iov_len)
+ {
+ /* this one's not done yet */
+ osiov->iov_base = (char*)osiov->iov_base + bytes;
+ osiov->iov_len -= bytes;
+ break; /* go off and do that */
+ }
+ bytes -= osiov->iov_len; /* this one's done cooked */
+ }
+ PR_ASSERT(osiov_len > 0 || bytes == 0);
+ if (osiov_len > 0)
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout)
+ {
+ rv = -1;
+ syserrno = ETIMEDOUT;
+ }
+ else {
+ fNeedContinue = PR_TRUE;
+ }
+ }
+ }
+ else if (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) {
+ syserrno = ETIMEDOUT;
+ }
+ else
+ {
+ rv = 0;
+ fNeedContinue = PR_TRUE;
+ }
+ }
+ }
+
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = (void*)osiov;
+ op.arg3.amount = osiov_len;
+ op.timeout = timeout;
+ op.result.code = rv;
+ op.function = pt_writev_cont;
+ op.event = POLLOUT | POLLPRI;
+ rv = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_WRITEV_ERROR, syserrno);
+ }
+ return rv;
+} /* pt_Writev */
+
+static PRInt32 pt_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence)
+{
+ return _PR_MD_LSEEK(fd, offset, whence);
+} /* pt_Seek */
+
+static PRInt64 pt_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence)
+{
+ return _PR_MD_LSEEK64(fd, offset, whence);
+} /* pt_Seek64 */
+
+static PRInt32 pt_Available_f(PRFileDesc *fd)
+{
+ PRInt32 result, cur, end;
+
+ cur = _PR_MD_LSEEK(fd, 0, PR_SEEK_CUR);
+
+ if (cur >= 0) {
+ end = _PR_MD_LSEEK(fd, 0, PR_SEEK_END);
+ }
+
+ if ((cur < 0) || (end < 0)) {
+ return -1;
+ }
+
+ result = end - cur;
+ _PR_MD_LSEEK(fd, cur, PR_SEEK_SET);
+
+ return result;
+} /* pt_Available_f */
+
+static PRInt64 pt_Available64_f(PRFileDesc *fd)
+{
+ PRInt64 result, cur, end;
+ PRInt64 minus_one;
+
+ LL_I2L(minus_one, -1);
+ cur = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_CUR);
+
+ if (LL_GE_ZERO(cur)) {
+ end = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_END);
+ }
+
+ if (!LL_GE_ZERO(cur) || !LL_GE_ZERO(end)) {
+ return minus_one;
+ }
+
+ LL_SUB(result, end, cur);
+ (void)_PR_MD_LSEEK64(fd, cur, PR_SEEK_SET);
+
+ return result;
+} /* pt_Available64_f */
+
+static PRInt32 pt_Available_s(PRFileDesc *fd)
+{
+ PRInt32 rv, bytes = -1;
+ if (pt_TestAbort()) {
+ return bytes;
+ }
+
+ rv = ioctl(fd->secret->md.osfd, FIONREAD, &bytes);
+
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_SOCKETAVAILABLE_ERROR, errno);
+ }
+ return bytes;
+} /* pt_Available_s */
+
+static PRInt64 pt_Available64_s(PRFileDesc *fd)
+{
+ PRInt64 rv;
+ LL_I2L(rv, pt_Available_s(fd));
+ return rv;
+} /* pt_Available64_s */
+
+static PRStatus pt_FileInfo(PRFileDesc *fd, PRFileInfo *info)
+{
+ PRInt32 rv = _PR_MD_GETOPENFILEINFO(fd, info);
+ return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
+} /* pt_FileInfo */
+
+static PRStatus pt_FileInfo64(PRFileDesc *fd, PRFileInfo64 *info)
+{
+ PRInt32 rv = _PR_MD_GETOPENFILEINFO64(fd, info);
+ return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
+} /* pt_FileInfo64 */
+
+static PRStatus pt_Synch(PRFileDesc *fd)
+{
+ return (NULL == fd) ? PR_FAILURE : PR_SUCCESS;
+} /* pt_Synch */
+
+static PRStatus pt_Fsync(PRFileDesc *fd)
+{
+ PRIntn rv = -1;
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ rv = fsync(fd->secret->md.osfd);
+ if (rv < 0) {
+ pt_MapError(_PR_MD_MAP_FSYNC_ERROR, errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* pt_Fsync */
+
+static PRStatus pt_Connect(
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRIntn rv = -1, syserrno;
+ pt_SockLen addr_len;
+ const PRNetAddr *addrp = addr;
+#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6)
+ PRNetAddr addrCopy;
+#endif
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ PRUint16 md_af = addr->raw.family;
+#endif
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+ addr_len = PR_NETADDR_SIZE(addr);
+#ifdef _PR_INET6
+ if (addr->raw.family == PR_AF_INET6) {
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ md_af = AF_INET6;
+#else
+ addrCopy = *addr;
+ addrCopy.raw.family = AF_INET6;
+ addrp = &addrCopy;
+#endif
+ }
+#endif
+
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ addrCopy = *addr;
+ ((struct sockaddr*)&addrCopy)->sa_len = addr_len;
+ ((struct sockaddr*)&addrCopy)->sa_family = md_af;
+ addrp = &addrCopy;
+#endif
+ rv = connect(fd->secret->md.osfd, (struct sockaddr*)addrp, addr_len);
+ syserrno = errno;
+ if ((-1 == rv) && (EINPROGRESS == syserrno) && (!fd->secret->nonblocking))
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) {
+ syserrno = ETIMEDOUT;
+ }
+ else
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = (void*)addrp;
+ op.arg3.amount = addr_len;
+ op.timeout = timeout;
+ op.function = pt_connect_cont;
+ op.event = POLLOUT | POLLPRI;
+ rv = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ }
+ if (-1 == rv) {
+ pt_MapError(_PR_MD_MAP_CONNECT_ERROR, syserrno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* pt_Connect */
+
+static PRStatus pt_ConnectContinue(
+ PRFileDesc *fd, PRInt16 out_flags)
+{
+ int err;
+ PRInt32 osfd;
+
+ if (out_flags & PR_POLL_NVAL)
+ {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if ((out_flags & (PR_POLL_WRITE | PR_POLL_EXCEPT | PR_POLL_ERR
+ | PR_POLL_HUP)) == 0)
+ {
+ PR_ASSERT(out_flags == 0);
+ PR_SetError(PR_IN_PROGRESS_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ osfd = fd->secret->md.osfd;
+
+ err = _MD_unix_get_nonblocking_connect_error(osfd);
+ if (err != 0)
+ {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* pt_ConnectContinue */
+
+PR_IMPLEMENT(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd)
+{
+ /* Find the NSPR layer and invoke its connectcontinue method */
+ PRFileDesc *bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+
+ if (NULL == bottom)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ return pt_ConnectContinue(bottom, pd->out_flags);
+} /* PR_GetConnectStatus */
+
+static PRFileDesc* pt_Accept(
+ PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRFileDesc *newfd = NULL;
+ PRIntn syserrno, osfd = -1;
+ pt_SockLen addr_len = sizeof(PRNetAddr);
+
+ if (pt_TestAbort()) {
+ return newfd;
+ }
+
+#ifdef _PR_STRICT_ADDR_LEN
+ if (addr)
+ {
+ /*
+ * Set addr->raw.family just so that we can use the
+ * PR_NETADDR_SIZE macro.
+ */
+ addr->raw.family = fd->secret->af;
+ addr_len = PR_NETADDR_SIZE(addr);
+ }
+#endif
+
+ osfd = accept(fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len);
+ syserrno = errno;
+
+ if (osfd == -1)
+ {
+ if (fd->secret->nonblocking) {
+ goto failed;
+ }
+
+ if (EWOULDBLOCK != syserrno && EAGAIN != syserrno
+ && ECONNABORTED != syserrno) {
+ goto failed;
+ }
+ else
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) {
+ syserrno = ETIMEDOUT;
+ }
+ else
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = addr;
+ op.arg3.addr_len = &addr_len;
+ op.timeout = timeout;
+ op.function = pt_accept_cont;
+ op.event = POLLIN | POLLPRI;
+ osfd = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ if (osfd < 0) {
+ goto failed;
+ }
+ }
+ }
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ /* ignore the sa_len field of struct sockaddr */
+ if (addr)
+ {
+ addr->raw.family = ((struct sockaddr*)addr)->sa_family;
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+#ifdef _PR_INET6
+ if (addr && (AF_INET6 == addr->raw.family)) {
+ addr->raw.family = PR_AF_INET6;
+ }
+#endif
+ newfd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP, PR_TRUE, PR_FALSE);
+ if (newfd == NULL) {
+ close(osfd); /* $$$ whoops! this doesn't work $$$ */
+ }
+ else
+ {
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+ PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE);
+#ifdef LINUX
+ /*
+ * On Linux, experiments showed that the accepted sockets
+ * inherit the TCP_NODELAY socket option of the listening
+ * socket.
+ */
+ newfd->secret->md.tcp_nodelay = fd->secret->md.tcp_nodelay;
+#endif
+ }
+ return newfd;
+
+failed:
+ pt_MapError(_PR_MD_MAP_ACCEPT_ERROR, syserrno);
+ return NULL;
+} /* pt_Accept */
+
+static PRStatus pt_Bind(PRFileDesc *fd, const PRNetAddr *addr)
+{
+ PRIntn rv;
+ pt_SockLen addr_len;
+ const PRNetAddr *addrp = addr;
+#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6)
+ PRNetAddr addrCopy;
+#endif
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ PRUint16 md_af = addr->raw.family;
+#endif
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+ if (addr->raw.family == AF_UNIX)
+ {
+ /* Disallow relative pathnames */
+ if (addr->local.path[0] != '/'
+#if defined(LINUX)
+ /* Linux has abstract socket address support */
+ && addr->local.path[0] != 0
+#endif
+ )
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ }
+
+#ifdef _PR_INET6
+ if (addr->raw.family == PR_AF_INET6) {
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ md_af = AF_INET6;
+#else
+ addrCopy = *addr;
+ addrCopy.raw.family = AF_INET6;
+ addrp = &addrCopy;
+#endif
+ }
+#endif
+
+ addr_len = PR_NETADDR_SIZE(addr);
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ addrCopy = *addr;
+ ((struct sockaddr*)&addrCopy)->sa_len = addr_len;
+ ((struct sockaddr*)&addrCopy)->sa_family = md_af;
+ addrp = &addrCopy;
+#endif
+ rv = bind(fd->secret->md.osfd, (struct sockaddr*)addrp, addr_len);
+
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_BIND_ERROR, errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* pt_Bind */
+
+static PRStatus pt_Listen(PRFileDesc *fd, PRIntn backlog)
+{
+ PRIntn rv;
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ rv = listen(fd->secret->md.osfd, backlog);
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_LISTEN_ERROR, errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* pt_Listen */
+
+static PRStatus pt_Shutdown(PRFileDesc *fd, PRIntn how)
+{
+ PRIntn rv = -1;
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ rv = shutdown(fd->secret->md.osfd, how);
+
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_SHUTDOWN_ERROR, errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* pt_Shutdown */
+
+static PRInt16 pt_Poll(PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
+{
+ *out_flags = 0;
+ return in_flags;
+} /* pt_Poll */
+
+static PRInt32 pt_Recv(
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ PRInt32 syserrno, bytes = -1;
+ PRIntn osflags;
+
+ if (0 == flags) {
+ osflags = 0;
+ }
+ else if (PR_MSG_PEEK == flags)
+ {
+ osflags = MSG_PEEK;
+ }
+ else
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return bytes;
+ }
+
+ if (pt_TestAbort()) {
+ return bytes;
+ }
+
+ /* recv() is a much slower call on pre-2.6 Solaris than read(). */
+#if defined(SOLARIS)
+ if (0 == osflags) {
+ bytes = read(fd->secret->md.osfd, buf, amount);
+ }
+ else {
+ bytes = recv(fd->secret->md.osfd, buf, amount, osflags);
+ }
+#else
+ bytes = recv(fd->secret->md.osfd, buf, amount, osflags);
+#endif
+ syserrno = errno;
+
+ if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking))
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) {
+ syserrno = ETIMEDOUT;
+ }
+ else
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = osflags;
+ op.timeout = timeout;
+ op.function = pt_recv_cont;
+ op.event = POLLIN | POLLPRI;
+ bytes = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ }
+ if (bytes < 0) {
+ pt_MapError(_PR_MD_MAP_RECV_ERROR, syserrno);
+ }
+ return bytes;
+} /* pt_Recv */
+
+static PRInt32 pt_SocketRead(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ return pt_Recv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
+} /* pt_SocketRead */
+
+static PRInt32 pt_Send(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ PRInt32 syserrno, bytes = -1;
+ PRBool fNeedContinue = PR_FALSE;
+#if defined(SOLARIS)
+ PRInt32 tmp_amount = amount;
+#endif
+
+ if (pt_TestAbort()) {
+ return bytes;
+ }
+
+ /*
+ * On pre-2.6 Solaris, send() is much slower than write().
+ * On 2.6 and beyond, with in-kernel sockets, send() and
+ * write() are fairly equivalent in performance.
+ */
+#if defined(SOLARIS)
+ PR_ASSERT(0 == flags);
+retry:
+ bytes = write(fd->secret->md.osfd, buf, tmp_amount);
+#else
+ bytes = send(fd->secret->md.osfd, buf, amount, flags);
+#endif
+ syserrno = errno;
+
+#if defined(SOLARIS)
+ /*
+ * The write system call has been reported to return the ERANGE error
+ * on occasion. Try to write in smaller chunks to workaround this bug.
+ */
+ if ((bytes == -1) && (syserrno == ERANGE))
+ {
+ if (tmp_amount > 1)
+ {
+ tmp_amount = tmp_amount/2; /* half the bytes */
+ goto retry;
+ }
+ }
+#endif
+
+ if ( (bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking) )
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout)
+ {
+ bytes = -1;
+ syserrno = ETIMEDOUT;
+ }
+ else
+ {
+ buf = (char *) buf + bytes;
+ amount -= bytes;
+ fNeedContinue = PR_TRUE;
+ }
+ }
+ if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking) )
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) {
+ syserrno = ETIMEDOUT;
+ }
+ else
+ {
+ bytes = 0;
+ fNeedContinue = PR_TRUE;
+ }
+ }
+
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = (void*)buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.timeout = timeout;
+ op.result.code = bytes; /* initialize the number sent */
+ op.function = pt_send_cont;
+ op.event = POLLOUT | POLLPRI;
+ bytes = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ if (bytes == -1) {
+ pt_MapError(_PR_MD_MAP_SEND_ERROR, syserrno);
+ }
+ return bytes;
+} /* pt_Send */
+
+static PRInt32 pt_SocketWrite(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ return pt_Send(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
+} /* pt_SocketWrite */
+
+static PRInt32 pt_SendTo(
+ PRFileDesc *fd, const void *buf,
+ PRInt32 amount, PRIntn flags, const PRNetAddr *addr,
+ PRIntervalTime timeout)
+{
+ PRInt32 syserrno, bytes = -1;
+ PRBool fNeedContinue = PR_FALSE;
+ pt_SockLen addr_len;
+ const PRNetAddr *addrp = addr;
+#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6)
+ PRNetAddr addrCopy;
+#endif
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ PRUint16 md_af = addr->raw.family;
+#endif
+
+ if (pt_TestAbort()) {
+ return bytes;
+ }
+
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+#ifdef _PR_INET6
+ if (addr->raw.family == PR_AF_INET6) {
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ md_af = AF_INET6;
+#else
+ addrCopy = *addr;
+ addrCopy.raw.family = AF_INET6;
+ addrp = &addrCopy;
+#endif
+ }
+#endif
+
+ addr_len = PR_NETADDR_SIZE(addr);
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ addrCopy = *addr;
+ ((struct sockaddr*)&addrCopy)->sa_len = addr_len;
+ ((struct sockaddr*)&addrCopy)->sa_family = md_af;
+ addrp = &addrCopy;
+#endif
+ bytes = sendto(
+ fd->secret->md.osfd, buf, amount, flags,
+ (struct sockaddr*)addrp, addr_len);
+ syserrno = errno;
+ if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking) )
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) {
+ syserrno = ETIMEDOUT;
+ }
+ else {
+ fNeedContinue = PR_TRUE;
+ }
+ }
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = (void*)buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.arg5.addr = (PRNetAddr*)addrp;
+ op.timeout = timeout;
+ op.result.code = 0; /* initialize the number sent */
+ op.function = pt_sendto_cont;
+ op.event = POLLOUT | POLLPRI;
+ bytes = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ if (bytes < 0) {
+ pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno);
+ }
+ return bytes;
+} /* pt_SendTo */
+
+#if defined(LINUX) || defined(DARWIN)
+/* Linux uses SendTo to send data during TCP Fast Open. OSX uses connectx, but
+ * we will make it imitate the Linux's interface. */
+static PRInt32 pt_TCP_SendTo(
+ PRFileDesc *fd, const void *buf,
+ PRInt32 amount, PRIntn flags, const PRNetAddr *addr,
+ PRIntervalTime timeout)
+{
+#if defined(LINUX) || HAS_CONNECTX
+ PRInt32 syserrno, bytes = -1;
+ PRBool fNeedContinue = PR_FALSE;
+ pt_SockLen addr_len;
+ const PRNetAddr *addrp = addr;
+#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6)
+ PRNetAddr addrCopy;
+#endif
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ PRUint16 md_af = addr->raw.family;
+#endif
+
+ if (pt_TestAbort()) {
+ return bytes;
+ }
+
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+ addr_len = PR_NETADDR_SIZE(addr);
+#if defined(_PR_INET6)
+ if (addr->raw.family == PR_AF_INET6) {
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ md_af = AF_INET6;
+#else
+ /* If _PR_INET6 is defined and it is PR_AF_INET6 we set family
+ * to AF_INET6. */
+ addrCopy = *addr;
+ addrCopy.raw.family = AF_INET6;
+ addrp = &addrCopy;
+#endif
+ }
+#endif
+
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ /* if _PR_HAVE_SOCKADDR_LEN is defined and it is PR_AF_INET6 we set family
+ * to AF_INET6 and we set address length. */
+ addrCopy = *addr;
+ ((struct sockaddr*)&addrCopy)->sa_len = addr_len;
+ ((struct sockaddr*)&addrCopy)->sa_family = md_af;
+ addrp = &addrCopy;
+#endif
+
+#ifndef HAS_CONNECTX
+ bytes = sendto(
+ fd->secret->md.osfd, buf, amount, MSG_FASTOPEN,
+ (struct sockaddr*)addrp, addr_len);
+#else
+ sa_endpoints_t endpoints;
+ endpoints.sae_srcif = 0;
+ endpoints.sae_srcaddr = NULL;
+ endpoints.sae_srcaddrlen = 0;
+ endpoints.sae_dstaddr = (struct sockaddr *)addrp;
+ endpoints.sae_dstaddrlen = addr_len;
+ struct iovec iov[1];
+ iov[0].iov_base = buf;
+ iov[0].iov_len = amount;
+ PRInt32 rv = connectx(fd->secret->md.osfd, &endpoints, SAE_ASSOCID_ANY,
+ CONNECT_DATA_IDEMPOTENT, iov, 1, &bytes, NULL);
+#endif
+ syserrno = errno;
+ if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking) ) {
+ if (PR_INTERVAL_NO_WAIT == timeout) {
+ syserrno = ETIMEDOUT;
+ }
+ else {
+ fNeedContinue = PR_TRUE;
+ }
+ }
+ if (fNeedContinue == PR_TRUE) {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = (void*)buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.arg5.addr = (PRNetAddr*)addrp;
+ op.timeout = timeout;
+ op.result.code = 0; /* initialize the number sent */
+ op.function = pt_sendto_cont;
+ op.event = POLLOUT | POLLPRI;
+ bytes = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ if (bytes < 0) {
+ pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno);
+ }
+ return bytes;
+#else /* !HAS_CONNECTX */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return -1;
+#endif
+} /* pt_TCP_SendTo */
+#endif /* LINUX || DARWIN */
+
+static PRInt32 pt_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRBool fNeedContinue = PR_FALSE;
+ PRInt32 syserrno, bytes = -1;
+ pt_SockLen addr_len = sizeof(PRNetAddr);
+
+ if (pt_TestAbort()) {
+ return bytes;
+ }
+
+ bytes = recvfrom(
+ fd->secret->md.osfd, buf, amount, flags,
+ (struct sockaddr*)addr, &addr_len);
+ syserrno = errno;
+
+ if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking) )
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) {
+ syserrno = ETIMEDOUT;
+ }
+ else {
+ fNeedContinue = PR_TRUE;
+ }
+ }
+
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.arg5.addr = addr;
+ op.timeout = timeout;
+ op.function = pt_recvfrom_cont;
+ op.event = POLLIN | POLLPRI;
+ bytes = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ if (bytes >= 0)
+ {
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ /* ignore the sa_len field of struct sockaddr */
+ if (addr)
+ {
+ addr->raw.family = ((struct sockaddr*)addr)->sa_family;
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+#ifdef _PR_INET6
+ if (addr && (AF_INET6 == addr->raw.family)) {
+ addr->raw.family = PR_AF_INET6;
+ }
+#endif
+ }
+ else {
+ pt_MapError(_PR_MD_MAP_RECVFROM_ERROR, syserrno);
+ }
+ return bytes;
+} /* pt_RecvFrom */
+
+#ifdef AIX
+#ifndef HAVE_SEND_FILE
+static pthread_once_t pt_aix_sendfile_once_block = PTHREAD_ONCE_INIT;
+
+static void pt_aix_sendfile_init_routine(void)
+{
+ void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
+ pt_aix_sendfile_fptr = (ssize_t (*)()) dlsym(handle, "send_file");
+ dlclose(handle);
+}
+
+/*
+ * pt_AIXDispatchSendFile
+ */
+static PRInt32 pt_AIXDispatchSendFile(PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ int rv;
+
+ rv = pthread_once(&pt_aix_sendfile_once_block,
+ pt_aix_sendfile_init_routine);
+ PR_ASSERT(0 == rv);
+ if (pt_aix_sendfile_fptr) {
+ return pt_AIXSendFile(sd, sfd, flags, timeout);
+ } else {
+ return PR_EmulateSendFile(sd, sfd, flags, timeout);
+ }
+}
+#endif /* !HAVE_SEND_FILE */
+
+
+/*
+ * pt_AIXSendFile
+ *
+ * Send file sfd->fd across socket sd. If specified, header and trailer
+ * buffers are sent before and after the file, respectively.
+ *
+ * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
+ *
+ * return number of bytes sent or -1 on error
+ *
+ * This implementation takes advantage of the send_file() system
+ * call available in AIX 4.3.2.
+ */
+
+static PRInt32 pt_AIXSendFile(PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ struct sf_parms sf_struct;
+ uint_t send_flags;
+ ssize_t rv;
+ int syserrno;
+ PRInt32 count;
+ unsigned long long saved_file_offset;
+ long long saved_file_bytes;
+
+ sf_struct.header_data = (void *) sfd->header; /* cast away the 'const' */
+ sf_struct.header_length = sfd->hlen;
+ sf_struct.file_descriptor = sfd->fd->secret->md.osfd;
+ sf_struct.file_size = 0;
+ sf_struct.file_offset = sfd->file_offset;
+ if (sfd->file_nbytes == 0) {
+ sf_struct.file_bytes = -1;
+ }
+ else {
+ sf_struct.file_bytes = sfd->file_nbytes;
+ }
+ sf_struct.trailer_data = (void *) sfd->trailer;
+ sf_struct.trailer_length = sfd->tlen;
+ sf_struct.bytes_sent = 0;
+
+ saved_file_offset = sf_struct.file_offset;
+ saved_file_bytes = sf_struct.file_bytes;
+
+ send_flags = 0; /* flags processed at the end */
+
+ /* The first argument to send_file() is int*. */
+ PR_ASSERT(sizeof(int) == sizeof(sd->secret->md.osfd));
+ do {
+ rv = AIX_SEND_FILE(&sd->secret->md.osfd, &sf_struct, send_flags);
+ } while (rv == -1 && (syserrno = errno) == EINTR);
+
+ if (rv == -1) {
+ if (syserrno == EAGAIN || syserrno == EWOULDBLOCK) {
+ count = 0; /* Not a real error. Need to continue. */
+ } else {
+ count = -1;
+ }
+ } else {
+ count = sf_struct.bytes_sent;
+ /*
+ * A bug in AIX 4.3.2 prevents the 'file_bytes' field from
+ * being updated. So, 'file_bytes' is maintained by NSPR to
+ * avoid conflict when this bug is fixed in AIX, in the future.
+ */
+ if (saved_file_bytes != -1) {
+ saved_file_bytes -= (sf_struct.file_offset - saved_file_offset);
+ }
+ sf_struct.file_bytes = saved_file_bytes;
+ }
+
+ if ((rv == 1) || ((rv == -1) && (count == 0))) {
+ pt_Continuation op;
+
+ op.arg1.osfd = sd->secret->md.osfd;
+ op.arg2.buffer = &sf_struct;
+ op.arg4.flags = send_flags;
+ op.result.code = count;
+ op.timeout = timeout;
+ op.function = pt_aix_sendfile_cont;
+ op.event = POLLOUT | POLLPRI;
+ count = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+
+ if (count == -1) {
+ pt_MapError(_MD_aix_map_sendfile_error, syserrno);
+ return -1;
+ }
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
+ PR_Close(sd);
+ }
+ PR_ASSERT(count == (sfd->hlen + sfd->tlen +
+ ((sfd->file_nbytes == 0) ?
+ sf_struct.file_size - sfd->file_offset :
+ sfd->file_nbytes)));
+ return count;
+}
+#endif /* AIX */
+
+#ifdef HPUX11
+/*
+ * pt_HPUXSendFile
+ *
+ * Send file sfd->fd across socket sd. If specified, header and trailer
+ * buffers are sent before and after the file, respectively.
+ *
+ * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
+ *
+ * return number of bytes sent or -1 on error
+ *
+ * This implementation takes advantage of the sendfile() system
+ * call available in HP-UX B.11.00.
+ */
+
+static PRInt32 pt_HPUXSendFile(PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ struct stat statbuf;
+ size_t nbytes_to_send, file_nbytes_to_send;
+ struct iovec hdtrl[2]; /* optional header and trailer buffers */
+ int send_flags;
+ PRInt32 count;
+ int syserrno;
+
+ if (sfd->file_nbytes == 0) {
+ /* Get file size */
+ if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) {
+ _PR_MD_MAP_FSTAT_ERROR(errno);
+ return -1;
+ }
+ file_nbytes_to_send = statbuf.st_size - sfd->file_offset;
+ } else {
+ file_nbytes_to_send = sfd->file_nbytes;
+ }
+ nbytes_to_send = sfd->hlen + sfd->tlen + file_nbytes_to_send;
+
+ hdtrl[0].iov_base = (void *) sfd->header; /* cast away the 'const' */
+ hdtrl[0].iov_len = sfd->hlen;
+ hdtrl[1].iov_base = (void *) sfd->trailer;
+ hdtrl[1].iov_len = sfd->tlen;
+ /*
+ * SF_DISCONNECT seems to close the socket even if sendfile()
+ * only does a partial send on a nonblocking socket. This
+ * would prevent the subsequent sendfile() calls on that socket
+ * from working. So we don't use the SD_DISCONNECT flag.
+ */
+ send_flags = 0;
+
+ do {
+ count = sendfile(sd->secret->md.osfd, sfd->fd->secret->md.osfd,
+ sfd->file_offset, file_nbytes_to_send, hdtrl, send_flags);
+ } while (count == -1 && (syserrno = errno) == EINTR);
+
+ if (count == -1 && (syserrno == EAGAIN || syserrno == EWOULDBLOCK)) {
+ count = 0;
+ }
+ if (count != -1 && count < nbytes_to_send) {
+ pt_Continuation op;
+
+ if (count < sfd->hlen) {
+ /* header not sent */
+
+ hdtrl[0].iov_base = ((char *) sfd->header) + count;
+ hdtrl[0].iov_len = sfd->hlen - count;
+ op.arg3.file_spec.offset = sfd->file_offset;
+ op.arg3.file_spec.nbytes = file_nbytes_to_send;
+ } else if (count < (sfd->hlen + file_nbytes_to_send)) {
+ /* header sent, file not sent */
+
+ hdtrl[0].iov_base = NULL;
+ hdtrl[0].iov_len = 0;
+
+ op.arg3.file_spec.offset = sfd->file_offset + count - sfd->hlen;
+ op.arg3.file_spec.nbytes = file_nbytes_to_send - (count - sfd->hlen);
+ } else if (count < (sfd->hlen + file_nbytes_to_send + sfd->tlen)) {
+ PRUint32 trailer_nbytes_sent;
+
+ /* header sent, file sent, trailer not sent */
+
+ hdtrl[0].iov_base = NULL;
+ hdtrl[0].iov_len = 0;
+ /*
+ * set file offset and len so that no more file data is
+ * sent
+ */
+ op.arg3.file_spec.offset = statbuf.st_size;
+ op.arg3.file_spec.nbytes = 0;
+
+ trailer_nbytes_sent = count - sfd->hlen - file_nbytes_to_send;
+ hdtrl[1].iov_base = ((char *) sfd->trailer) + trailer_nbytes_sent;
+ hdtrl[1].iov_len = sfd->tlen - trailer_nbytes_sent;
+ }
+
+ op.arg1.osfd = sd->secret->md.osfd;
+ op.filedesc = sfd->fd->secret->md.osfd;
+ op.arg2.buffer = hdtrl;
+ op.arg3.file_spec.st_size = statbuf.st_size;
+ op.arg4.flags = send_flags;
+ op.nbytes_to_send = nbytes_to_send - count;
+ op.result.code = count;
+ op.timeout = timeout;
+ op.function = pt_hpux_sendfile_cont;
+ op.event = POLLOUT | POLLPRI;
+ count = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+
+ if (count == -1) {
+ pt_MapError(_MD_hpux_map_sendfile_error, syserrno);
+ return -1;
+ }
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
+ PR_Close(sd);
+ }
+ PR_ASSERT(count == nbytes_to_send);
+ return count;
+}
+
+#endif /* HPUX11 */
+
+#ifdef SOLARIS
+
+/*
+ * pt_SolarisSendFile
+ *
+ * Send file sfd->fd across socket sd. If specified, header and trailer
+ * buffers are sent before and after the file, respectively.
+ *
+ * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
+ *
+ * return number of bytes sent or -1 on error
+ *
+ * This implementation takes advantage of the sendfilev() system
+ * call available in Solaris 8.
+ */
+
+static PRInt32 pt_SolarisSendFile(PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ struct stat statbuf;
+ size_t nbytes_to_send, file_nbytes_to_send;
+ struct sendfilevec sfv_struct[3];
+ int sfvcnt = 0;
+ size_t xferred;
+ PRInt32 count;
+ int syserrno;
+
+ if (sfd->file_nbytes == 0) {
+ /* Get file size */
+ if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) {
+ _PR_MD_MAP_FSTAT_ERROR(errno);
+ return -1;
+ }
+ file_nbytes_to_send = statbuf.st_size - sfd->file_offset;
+ } else {
+ file_nbytes_to_send = sfd->file_nbytes;
+ }
+
+ nbytes_to_send = sfd->hlen + sfd->tlen + file_nbytes_to_send;
+
+ if (sfd->hlen != 0) {
+ sfv_struct[sfvcnt].sfv_fd = SFV_FD_SELF;
+ sfv_struct[sfvcnt].sfv_flag = 0;
+ sfv_struct[sfvcnt].sfv_off = (off_t) sfd->header;
+ sfv_struct[sfvcnt].sfv_len = sfd->hlen;
+ sfvcnt++;
+ }
+
+ if (file_nbytes_to_send != 0) {
+ sfv_struct[sfvcnt].sfv_fd = sfd->fd->secret->md.osfd;
+ sfv_struct[sfvcnt].sfv_flag = 0;
+ sfv_struct[sfvcnt].sfv_off = sfd->file_offset;
+ sfv_struct[sfvcnt].sfv_len = file_nbytes_to_send;
+ sfvcnt++;
+ }
+
+ if (sfd->tlen != 0) {
+ sfv_struct[sfvcnt].sfv_fd = SFV_FD_SELF;
+ sfv_struct[sfvcnt].sfv_flag = 0;
+ sfv_struct[sfvcnt].sfv_off = (off_t) sfd->trailer;
+ sfv_struct[sfvcnt].sfv_len = sfd->tlen;
+ sfvcnt++;
+ }
+
+ if (0 == sfvcnt) {
+ count = 0;
+ goto done;
+ }
+
+ /*
+ * Strictly speaking, we may have sent some bytes when the
+ * sendfilev() is interrupted and we should retry it from an
+ * updated offset. We are not doing that here.
+ */
+ count = SOLARIS_SENDFILEV(sd->secret->md.osfd, sfv_struct,
+ sfvcnt, &xferred);
+
+ PR_ASSERT((count == -1) || (count == xferred));
+
+ if (count == -1) {
+ syserrno = errno;
+ if (syserrno == EINTR
+ || syserrno == EAGAIN || syserrno == EWOULDBLOCK) {
+ count = xferred;
+ }
+ } else if (count == 0) {
+ /*
+ * We are now at EOF. The file was truncated. Solaris sendfile is
+ * supposed to return 0 and no error in this case, though some versions
+ * may return -1 and EINVAL .
+ */
+ count = -1;
+ syserrno = 0; /* will be treated as EOF */
+ }
+
+ if (count != -1 && count < nbytes_to_send) {
+ pt_Continuation op;
+ struct sendfilevec *vec = sfv_struct;
+ PRInt32 rem = count;
+
+ while (rem >= vec->sfv_len) {
+ rem -= vec->sfv_len;
+ vec++;
+ sfvcnt--;
+ }
+ PR_ASSERT(sfvcnt > 0);
+
+ vec->sfv_off += rem;
+ vec->sfv_len -= rem;
+ PR_ASSERT(vec->sfv_len > 0);
+
+ op.arg1.osfd = sd->secret->md.osfd;
+ op.arg2.buffer = vec;
+ op.arg3.amount = sfvcnt;
+ op.arg4.flags = 0;
+ op.nbytes_to_send = nbytes_to_send - count;
+ op.result.code = count;
+ op.timeout = timeout;
+ op.function = pt_solaris_sendfile_cont;
+ op.event = POLLOUT | POLLPRI;
+ count = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+
+done:
+ if (count == -1) {
+ pt_MapError(_MD_solaris_map_sendfile_error, syserrno);
+ return -1;
+ }
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
+ PR_Close(sd);
+ }
+ PR_ASSERT(count == nbytes_to_send);
+ return count;
+}
+
+#ifndef HAVE_SENDFILEV
+static pthread_once_t pt_solaris_sendfilev_once_block = PTHREAD_ONCE_INIT;
+
+static void pt_solaris_sendfilev_init_routine(void)
+{
+ void *handle;
+ PRBool close_it = PR_FALSE;
+
+ /*
+ * We do not want to unload libsendfile.so. This handle is leaked
+ * intentionally.
+ */
+ handle = dlopen("libsendfile.so", RTLD_LAZY | RTLD_GLOBAL);
+ PR_LOG(_pr_io_lm, PR_LOG_DEBUG,
+ ("dlopen(libsendfile.so) returns %p", handle));
+
+ if (NULL == handle) {
+ /*
+ * The dlopen(0, mode) call is to allow for the possibility that
+ * sendfilev() may become part of a standard system library in a
+ * future Solaris release.
+ */
+ handle = dlopen(0, RTLD_LAZY | RTLD_GLOBAL);
+ PR_LOG(_pr_io_lm, PR_LOG_DEBUG,
+ ("dlopen(0) returns %p", handle));
+ close_it = PR_TRUE;
+ }
+ pt_solaris_sendfilev_fptr = (ssize_t (*)()) dlsym(handle, "sendfilev");
+ PR_LOG(_pr_io_lm, PR_LOG_DEBUG,
+ ("dlsym(sendfilev) returns %p", pt_solaris_sendfilev_fptr));
+
+ if (close_it) {
+ dlclose(handle);
+ }
+}
+
+/*
+ * pt_SolarisDispatchSendFile
+ */
+static PRInt32 pt_SolarisDispatchSendFile(PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ int rv;
+
+ rv = pthread_once(&pt_solaris_sendfilev_once_block,
+ pt_solaris_sendfilev_init_routine);
+ PR_ASSERT(0 == rv);
+ if (pt_solaris_sendfilev_fptr) {
+ return pt_SolarisSendFile(sd, sfd, flags, timeout);
+ } else {
+ return PR_EmulateSendFile(sd, sfd, flags, timeout);
+ }
+}
+#endif /* !HAVE_SENDFILEV */
+
+#endif /* SOLARIS */
+
+#ifdef LINUX
+/*
+ * pt_LinuxSendFile
+ *
+ * Send file sfd->fd across socket sd. If specified, header and trailer
+ * buffers are sent before and after the file, respectively.
+ *
+ * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
+ *
+ * return number of bytes sent or -1 on error
+ *
+ * This implementation takes advantage of the sendfile() system
+ * call available in Linux kernel 2.2 or higher.
+ */
+
+static PRInt32 pt_LinuxSendFile(PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ struct stat statbuf;
+ size_t file_nbytes_to_send;
+ PRInt32 count = 0;
+ ssize_t rv;
+ int syserrno;
+ off_t offset;
+ PRBool tcp_cork_enabled = PR_FALSE;
+ int tcp_cork;
+
+ if (sfd->file_nbytes == 0) {
+ /* Get file size */
+ if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) {
+ _PR_MD_MAP_FSTAT_ERROR(errno);
+ return -1;
+ }
+ file_nbytes_to_send = statbuf.st_size - sfd->file_offset;
+ } else {
+ file_nbytes_to_send = sfd->file_nbytes;
+ }
+
+ if ((sfd->hlen != 0 || sfd->tlen != 0)
+ && sd->secret->md.tcp_nodelay == 0) {
+ tcp_cork = 1;
+ if (setsockopt(sd->secret->md.osfd, SOL_TCP, TCP_CORK,
+ &tcp_cork, sizeof tcp_cork) == 0) {
+ tcp_cork_enabled = PR_TRUE;
+ } else {
+ syserrno = errno;
+ if (syserrno != EINVAL) {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(syserrno);
+ return -1;
+ }
+ /*
+ * The most likely reason for the EINVAL error is that
+ * TCP_NODELAY is set (with a function other than
+ * PR_SetSocketOption). This is not fatal, so we keep
+ * on going.
+ */
+ PR_LOG(_pr_io_lm, PR_LOG_WARNING,
+ ("pt_LinuxSendFile: "
+ "setsockopt(TCP_CORK) failed with EINVAL\n"));
+ }
+ }
+
+ if (sfd->hlen != 0) {
+ count = PR_Send(sd, sfd->header, sfd->hlen, 0, timeout);
+ if (count == -1) {
+ goto failed;
+ }
+ }
+
+ if (file_nbytes_to_send != 0) {
+ offset = sfd->file_offset;
+ do {
+ rv = sendfile(sd->secret->md.osfd, sfd->fd->secret->md.osfd,
+ &offset, file_nbytes_to_send);
+ } while (rv == -1 && (syserrno = errno) == EINTR);
+ if (rv == -1) {
+ if (syserrno != EAGAIN && syserrno != EWOULDBLOCK) {
+ _MD_linux_map_sendfile_error(syserrno);
+ count = -1;
+ goto failed;
+ }
+ rv = 0;
+ }
+ PR_ASSERT(rv == offset - sfd->file_offset);
+ count += rv;
+
+ if (rv < file_nbytes_to_send) {
+ pt_Continuation op;
+
+ op.arg1.osfd = sd->secret->md.osfd;
+ op.in_fd = sfd->fd->secret->md.osfd;
+ op.offset = offset;
+ op.count = file_nbytes_to_send - rv;
+ op.result.code = count;
+ op.timeout = timeout;
+ op.function = pt_linux_sendfile_cont;
+ op.event = POLLOUT | POLLPRI;
+ count = pt_Continue(&op);
+ syserrno = op.syserrno;
+ if (count == -1) {
+ pt_MapError(_MD_linux_map_sendfile_error, syserrno);
+ goto failed;
+ }
+ }
+ }
+
+ if (sfd->tlen != 0) {
+ rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout);
+ if (rv == -1) {
+ count = -1;
+ goto failed;
+ }
+ count += rv;
+ }
+
+failed:
+ if (tcp_cork_enabled) {
+ tcp_cork = 0;
+ if (setsockopt(sd->secret->md.osfd, SOL_TCP, TCP_CORK,
+ &tcp_cork, sizeof tcp_cork) == -1 && count != -1) {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(errno);
+ count = -1;
+ }
+ }
+ if (count != -1) {
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
+ PR_Close(sd);
+ }
+ PR_ASSERT(count == sfd->hlen + sfd->tlen + file_nbytes_to_send);
+ }
+ return count;
+}
+#endif /* LINUX */
+
+#ifdef AIX
+extern int _pr_aix_send_file_use_disabled;
+#endif
+
+static PRInt32 pt_SendFile(
+ PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ if (pt_TestAbort()) {
+ return -1;
+ }
+ /* The socket must be in blocking mode. */
+ if (sd->secret->nonblocking)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+#ifdef HPUX11
+ return(pt_HPUXSendFile(sd, sfd, flags, timeout));
+#elif defined(AIX)
+#ifdef HAVE_SEND_FILE
+ /*
+ * A bug in AIX 4.3.2 results in corruption of data transferred by
+ * send_file(); AIX patch PTF U463956 contains the fix. A user can
+ * disable the use of send_file function in NSPR, when this patch is
+ * not installed on the system, by setting the envionment variable
+ * NSPR_AIX_SEND_FILE_USE_DISABLED to 1.
+ */
+ if (_pr_aix_send_file_use_disabled) {
+ return(PR_EmulateSendFile(sd, sfd, flags, timeout));
+ }
+ else {
+ return(pt_AIXSendFile(sd, sfd, flags, timeout));
+ }
+#else
+ return(PR_EmulateSendFile(sd, sfd, flags, timeout));
+ /* return(pt_AIXDispatchSendFile(sd, sfd, flags, timeout));*/
+#endif /* HAVE_SEND_FILE */
+#elif defined(SOLARIS)
+#ifdef HAVE_SENDFILEV
+ return(pt_SolarisSendFile(sd, sfd, flags, timeout));
+#else
+ return(pt_SolarisDispatchSendFile(sd, sfd, flags, timeout));
+#endif /* HAVE_SENDFILEV */
+#elif defined(LINUX)
+ return(pt_LinuxSendFile(sd, sfd, flags, timeout));
+#else
+ return(PR_EmulateSendFile(sd, sfd, flags, timeout));
+#endif
+}
+
+static PRInt32 pt_TransmitFile(
+ PRFileDesc *sd, PRFileDesc *fd, const void *headers,
+ PRInt32 hlen, PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ PRSendFileData sfd;
+
+ sfd.fd = fd;
+ sfd.file_offset = 0;
+ sfd.file_nbytes = 0;
+ sfd.header = headers;
+ sfd.hlen = hlen;
+ sfd.trailer = NULL;
+ sfd.tlen = 0;
+
+ return(pt_SendFile(sd, &sfd, flags, timeout));
+} /* pt_TransmitFile */
+
+static PRInt32 pt_AcceptRead(
+ PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
+ void *buf, PRInt32 amount, PRIntervalTime timeout)
+{
+ PRInt32 rv = -1;
+
+ if (pt_TestAbort()) {
+ return rv;
+ }
+ /* The socket must be in blocking mode. */
+ if (sd->secret->nonblocking)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return rv;
+ }
+
+ rv = PR_EmulateAcceptRead(sd, nd, raddr, buf, amount, timeout);
+ return rv;
+} /* pt_AcceptRead */
+
+static PRStatus pt_GetSockName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ PRIntn rv = -1;
+ pt_SockLen addr_len = sizeof(PRNetAddr);
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ rv = getsockname(
+ fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len);
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_GETSOCKNAME_ERROR, errno);
+ return PR_FAILURE;
+ }
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ /* ignore the sa_len field of struct sockaddr */
+ if (addr)
+ {
+ addr->raw.family = ((struct sockaddr*)addr)->sa_family;
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+#ifdef _PR_INET6
+ if (AF_INET6 == addr->raw.family) {
+ addr->raw.family = PR_AF_INET6;
+ }
+#endif
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+ PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE);
+ return PR_SUCCESS;
+} /* pt_GetSockName */
+
+static PRStatus pt_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ PRIntn rv = -1;
+ pt_SockLen addr_len = sizeof(PRNetAddr);
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ rv = getpeername(
+ fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len);
+
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_GETPEERNAME_ERROR, errno);
+ return PR_FAILURE;
+ }
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ /* ignore the sa_len field of struct sockaddr */
+ if (addr)
+ {
+ addr->raw.family = ((struct sockaddr*)addr)->sa_family;
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+#ifdef _PR_INET6
+ if (AF_INET6 == addr->raw.family) {
+ addr->raw.family = PR_AF_INET6;
+ }
+#endif
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+ PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE);
+ return PR_SUCCESS;
+} /* pt_GetPeerName */
+
+static PRStatus pt_GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data)
+{
+ PRIntn rv;
+ pt_SockLen length;
+ PRInt32 level, name;
+
+ /*
+ * PR_SockOpt_Nonblocking is a special case that does not
+ * translate to a getsockopt() call
+ */
+ if (PR_SockOpt_Nonblocking == data->option)
+ {
+ data->value.non_blocking = fd->secret->nonblocking;
+ return PR_SUCCESS;
+ }
+
+ rv = _PR_MapOptionName(data->option, &level, &name);
+ if (PR_SUCCESS == rv)
+ {
+ switch (data->option)
+ {
+ case PR_SockOpt_Linger:
+ {
+ struct linger linger;
+ length = sizeof(linger);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name, (char *) &linger, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(linger) == length));
+ data->value.linger.polarity =
+ (linger.l_onoff) ? PR_TRUE : PR_FALSE;
+ data->value.linger.linger =
+ PR_SecondsToInterval(linger.l_linger);
+ break;
+ }
+ case PR_SockOpt_Reuseaddr:
+ case PR_SockOpt_Keepalive:
+ case PR_SockOpt_NoDelay:
+ case PR_SockOpt_Broadcast:
+ case PR_SockOpt_Reuseport:
+ {
+ PRIntn value;
+ length = sizeof(PRIntn);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name, (char*)&value, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length));
+ data->value.reuse_addr = (0 == value) ? PR_FALSE : PR_TRUE;
+ break;
+ }
+ case PR_SockOpt_McastLoopback:
+ {
+ PRUint8 xbool;
+ length = sizeof(xbool);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&xbool, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(xbool) == length));
+ data->value.mcast_loopback = (0 == xbool) ? PR_FALSE : PR_TRUE;
+ break;
+ }
+ case PR_SockOpt_RecvBufferSize:
+ case PR_SockOpt_SendBufferSize:
+ case PR_SockOpt_MaxSegment:
+ {
+ PRIntn value;
+ length = sizeof(PRIntn);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name, (char*)&value, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length));
+ data->value.recv_buffer_size = value;
+ break;
+ }
+ case PR_SockOpt_IpTimeToLive:
+ case PR_SockOpt_IpTypeOfService:
+ {
+ length = sizeof(PRUintn);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&data->value.ip_ttl, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length));
+ break;
+ }
+ case PR_SockOpt_McastTimeToLive:
+ {
+ PRUint8 ttl;
+ length = sizeof(ttl);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&ttl, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(ttl) == length));
+ data->value.mcast_ttl = ttl;
+ break;
+ }
+ case PR_SockOpt_AddMember:
+ case PR_SockOpt_DropMember:
+ {
+ struct ip_mreq mreq;
+ length = sizeof(mreq);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name, (char*)&mreq, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(mreq) == length));
+ data->value.add_member.mcaddr.inet.ip =
+ mreq.imr_multiaddr.s_addr;
+ data->value.add_member.ifaddr.inet.ip =
+ mreq.imr_interface.s_addr;
+ break;
+ }
+ case PR_SockOpt_McastInterface:
+ {
+ length = sizeof(data->value.mcast_if.inet.ip);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&data->value.mcast_if.inet.ip, &length);
+ PR_ASSERT((-1 == rv)
+ || (sizeof(data->value.mcast_if.inet.ip) == length));
+ break;
+ }
+ case PR_SockOpt_DontFrag:
+ {
+#if !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)
+ PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
+ rv = PR_FAILURE;
+#else
+ PRIntn value;
+ length = sizeof(value);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name, (char*)&value,
+ &length);
+#if defined(DARWIN)
+ data->value.dont_fragment = value;
+#else
+ data->value.dont_fragment = (value == IP_PMTUDISC_DO) ? 1 : 0;
+#endif
+#endif /* !(!defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)) */
+ break;
+ }
+ default:
+ PR_NOT_REACHED("Unknown socket option");
+ break;
+ }
+ if (-1 == rv) {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(errno);
+ }
+ }
+ return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
+} /* pt_GetSocketOption */
+
+static PRStatus pt_SetSocketOption(PRFileDesc *fd, const PRSocketOptionData *data)
+{
+ PRIntn rv;
+ PRInt32 level, name;
+
+ /*
+ * PR_SockOpt_Nonblocking is a special case that does not
+ * translate to a setsockopt call.
+ */
+ if (PR_SockOpt_Nonblocking == data->option)
+ {
+ fd->secret->nonblocking = data->value.non_blocking;
+ return PR_SUCCESS;
+ }
+
+ rv = _PR_MapOptionName(data->option, &level, &name);
+ if (PR_SUCCESS == rv)
+ {
+ switch (data->option)
+ {
+ case PR_SockOpt_Linger:
+ {
+ struct linger linger;
+ linger.l_onoff = data->value.linger.polarity;
+ linger.l_linger = PR_IntervalToSeconds(data->value.linger.linger);
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name, (char*)&linger, sizeof(linger));
+ break;
+ }
+ case PR_SockOpt_Reuseaddr:
+ case PR_SockOpt_Keepalive:
+ case PR_SockOpt_NoDelay:
+ case PR_SockOpt_Broadcast:
+ case PR_SockOpt_Reuseport:
+ {
+ PRIntn value = (data->value.reuse_addr) ? 1 : 0;
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&value, sizeof(PRIntn));
+#ifdef LINUX
+ /* for pt_LinuxSendFile */
+ if (name == TCP_NODELAY && rv == 0) {
+ fd->secret->md.tcp_nodelay = value;
+ }
+#endif
+ break;
+ }
+ case PR_SockOpt_McastLoopback:
+ {
+ PRUint8 xbool = data->value.mcast_loopback ? 1 : 0;
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&xbool, sizeof(xbool));
+ break;
+ }
+ case PR_SockOpt_RecvBufferSize:
+ case PR_SockOpt_SendBufferSize:
+ case PR_SockOpt_MaxSegment:
+ {
+ PRIntn value = data->value.recv_buffer_size;
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&value, sizeof(PRIntn));
+ break;
+ }
+ case PR_SockOpt_IpTimeToLive:
+ case PR_SockOpt_IpTypeOfService:
+ {
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&data->value.ip_ttl, sizeof(PRUintn));
+ break;
+ }
+ case PR_SockOpt_McastTimeToLive:
+ {
+ PRUint8 ttl = data->value.mcast_ttl;
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&ttl, sizeof(ttl));
+ break;
+ }
+ case PR_SockOpt_AddMember:
+ case PR_SockOpt_DropMember:
+ {
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr.s_addr =
+ data->value.add_member.mcaddr.inet.ip;
+ mreq.imr_interface.s_addr =
+ data->value.add_member.ifaddr.inet.ip;
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&mreq, sizeof(mreq));
+ break;
+ }
+ case PR_SockOpt_McastInterface:
+ {
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&data->value.mcast_if.inet.ip,
+ sizeof(data->value.mcast_if.inet.ip));
+ break;
+ }
+ case PR_SockOpt_DontFrag:
+ {
+#if !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)
+ PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
+ rv = PR_FAILURE;
+#else
+ PRIntn value;
+#if defined(LINUX) || defined(ANDROID)
+ value = (data->value.dont_fragment) ? IP_PMTUDISC_DO
+ : IP_PMTUDISC_DONT;
+#elif defined(DARWIN)
+ value = data->value.dont_fragment;
+#endif
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name, (char*)&value,
+ sizeof(value));
+#endif /* !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)) */
+ break;
+ }
+ default:
+ PR_NOT_REACHED("Unknown socket option");
+ break;
+ }
+ if (-1 == rv) {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(errno);
+ }
+ }
+ return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
+} /* pt_SetSocketOption */
+
+/*****************************************************************************/
+/****************************** I/O method objects ***************************/
+/*****************************************************************************/
+
+static PRIOMethods _pr_file_methods = {
+ PR_DESC_FILE,
+ pt_Close,
+ pt_Read,
+ pt_Write,
+ pt_Available_f,
+ pt_Available64_f,
+ pt_Fsync,
+ pt_Seek,
+ pt_Seek64,
+ pt_FileInfo,
+ pt_FileInfo64,
+ (PRWritevFN)_PR_InvalidInt,
+ (PRConnectFN)_PR_InvalidStatus,
+ (PRAcceptFN)_PR_InvalidDesc,
+ (PRBindFN)_PR_InvalidStatus,
+ (PRListenFN)_PR_InvalidStatus,
+ (PRShutdownFN)_PR_InvalidStatus,
+ (PRRecvFN)_PR_InvalidInt,
+ (PRSendFN)_PR_InvalidInt,
+ (PRRecvfromFN)_PR_InvalidInt,
+ (PRSendtoFN)_PR_InvalidInt,
+ pt_Poll,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ (PRGetsocknameFN)_PR_InvalidStatus,
+ (PRGetpeernameFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRGetsocketoptionFN)_PR_InvalidStatus,
+ (PRSetsocketoptionFN)_PR_InvalidStatus,
+ (PRSendfileFN)_PR_InvalidInt,
+ (PRConnectcontinueFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+static PRIOMethods _pr_pipe_methods = {
+ PR_DESC_PIPE,
+ pt_Close,
+ pt_Read,
+ pt_Write,
+ pt_Available_s,
+ pt_Available64_s,
+ pt_Synch,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ (PRWritevFN)_PR_InvalidInt,
+ (PRConnectFN)_PR_InvalidStatus,
+ (PRAcceptFN)_PR_InvalidDesc,
+ (PRBindFN)_PR_InvalidStatus,
+ (PRListenFN)_PR_InvalidStatus,
+ (PRShutdownFN)_PR_InvalidStatus,
+ (PRRecvFN)_PR_InvalidInt,
+ (PRSendFN)_PR_InvalidInt,
+ (PRRecvfromFN)_PR_InvalidInt,
+ (PRSendtoFN)_PR_InvalidInt,
+ pt_Poll,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ (PRGetsocknameFN)_PR_InvalidStatus,
+ (PRGetpeernameFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRGetsocketoptionFN)_PR_InvalidStatus,
+ (PRSetsocketoptionFN)_PR_InvalidStatus,
+ (PRSendfileFN)_PR_InvalidInt,
+ (PRConnectcontinueFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+static PRIOMethods _pr_tcp_methods = {
+ PR_DESC_SOCKET_TCP,
+ pt_Close,
+ pt_SocketRead,
+ pt_SocketWrite,
+ pt_Available_s,
+ pt_Available64_s,
+ pt_Synch,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ pt_Writev,
+ pt_Connect,
+ pt_Accept,
+ pt_Bind,
+ pt_Listen,
+ pt_Shutdown,
+ pt_Recv,
+ pt_Send,
+ (PRRecvfromFN)_PR_InvalidInt,
+#if defined(LINUX) || defined(DARWIN)
+ pt_TCP_SendTo, /* This is for TCP Fast Open. Linux uses SendTo function for this. OSX uses connectx, but we imitate Linux. */
+#else
+ (PRSendtoFN)_PR_InvalidInt,
+#endif
+ pt_Poll,
+ pt_AcceptRead,
+ pt_TransmitFile,
+ pt_GetSockName,
+ pt_GetPeerName,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ pt_GetSocketOption,
+ pt_SetSocketOption,
+ pt_SendFile,
+ pt_ConnectContinue,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+static PRIOMethods _pr_udp_methods = {
+ PR_DESC_SOCKET_UDP,
+ pt_Close,
+ pt_SocketRead,
+ pt_SocketWrite,
+ pt_Available_s,
+ pt_Available64_s,
+ pt_Synch,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ pt_Writev,
+ pt_Connect,
+ (PRAcceptFN)_PR_InvalidDesc,
+ pt_Bind,
+ pt_Listen,
+ pt_Shutdown,
+ pt_Recv,
+ pt_Send,
+ pt_RecvFrom,
+ pt_SendTo,
+ pt_Poll,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ pt_GetSockName,
+ pt_GetPeerName,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ pt_GetSocketOption,
+ pt_SetSocketOption,
+ (PRSendfileFN)_PR_InvalidInt,
+ (PRConnectcontinueFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+static PRIOMethods _pr_socketpollfd_methods = {
+ (PRDescType) 0,
+ (PRCloseFN)_PR_InvalidStatus,
+ (PRReadFN)_PR_InvalidInt,
+ (PRWriteFN)_PR_InvalidInt,
+ (PRAvailableFN)_PR_InvalidInt,
+ (PRAvailable64FN)_PR_InvalidInt64,
+ (PRFsyncFN)_PR_InvalidStatus,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ (PRWritevFN)_PR_InvalidInt,
+ (PRConnectFN)_PR_InvalidStatus,
+ (PRAcceptFN)_PR_InvalidDesc,
+ (PRBindFN)_PR_InvalidStatus,
+ (PRListenFN)_PR_InvalidStatus,
+ (PRShutdownFN)_PR_InvalidStatus,
+ (PRRecvFN)_PR_InvalidInt,
+ (PRSendFN)_PR_InvalidInt,
+ (PRRecvfromFN)_PR_InvalidInt,
+ (PRSendtoFN)_PR_InvalidInt,
+ pt_Poll,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ (PRGetsocknameFN)_PR_InvalidStatus,
+ (PRGetpeernameFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRGetsocketoptionFN)_PR_InvalidStatus,
+ (PRSetsocketoptionFN)_PR_InvalidStatus,
+ (PRSendfileFN)_PR_InvalidInt,
+ (PRConnectcontinueFN)_PR_InvalidStatus,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt,
+ (PRReservedFN)_PR_InvalidInt
+};
+
+#if defined(HPUX) || defined(SOLARIS) \
+ || defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \
+ || defined(AIX) || defined(FREEBSD) || defined(NETBSD) \
+ || defined(OPENBSD) || defined(BSDI) || defined(NTO) \
+ || defined(DARWIN) || defined(UNIXWARE) || defined(RISCOS)
+#define _PR_FCNTL_FLAGS O_NONBLOCK
+#else
+#error "Can't determine architecture"
+#endif
+
+/*
+ * Put a Unix file descriptor in non-blocking mode.
+ */
+static void pt_MakeFdNonblock(PRIntn osfd)
+{
+ PRIntn flags;
+ flags = fcntl(osfd, F_GETFL, 0);
+ flags |= _PR_FCNTL_FLAGS;
+ (void)fcntl(osfd, F_SETFL, flags);
+}
+
+/*
+ * Put a Unix socket fd in non-blocking mode that can
+ * ideally be inherited by an accepted socket.
+ *
+ * Why doesn't pt_MakeFdNonblock do? This is to deal with
+ * the special case of HP-UX. HP-UX has three kinds of
+ * non-blocking modes for sockets: the fcntl() O_NONBLOCK
+ * and O_NDELAY flags and ioctl() FIOSNBIO request. Only
+ * the ioctl() FIOSNBIO form of non-blocking mode is
+ * inherited by an accepted socket.
+ *
+ * Other platforms just use the generic pt_MakeFdNonblock
+ * to put a socket in non-blocking mode.
+ */
+#ifdef HPUX
+static void pt_MakeSocketNonblock(PRIntn osfd)
+{
+ PRIntn one = 1;
+ (void)ioctl(osfd, FIOSNBIO, &one);
+}
+#else
+#define pt_MakeSocketNonblock pt_MakeFdNonblock
+#endif
+
+static PRFileDesc *pt_SetMethods(
+ PRIntn osfd, PRDescType type, PRBool isAcceptedSocket, PRBool imported)
+{
+ PRFileDesc *fd = _PR_Getfd();
+
+ if (fd == NULL) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ else
+ {
+ fd->secret->md.osfd = osfd;
+ fd->secret->state = _PR_FILEDESC_OPEN;
+ if (imported) {
+ fd->secret->inheritable = _PR_TRI_UNKNOWN;
+ }
+ else
+ {
+ /* By default, a Unix fd is not closed on exec. */
+#ifdef DEBUG
+ PRIntn flags;
+ flags = fcntl(osfd, F_GETFD, 0);
+ PR_ASSERT(0 == flags);
+#endif
+ fd->secret->inheritable = _PR_TRI_TRUE;
+ }
+ switch (type)
+ {
+ case PR_DESC_FILE:
+ fd->methods = PR_GetFileMethods();
+ break;
+ case PR_DESC_SOCKET_TCP:
+ fd->methods = PR_GetTCPMethods();
+#ifdef _PR_ACCEPT_INHERIT_NONBLOCK
+ if (!isAcceptedSocket) {
+ pt_MakeSocketNonblock(osfd);
+ }
+#else
+ pt_MakeSocketNonblock(osfd);
+#endif
+ break;
+ case PR_DESC_SOCKET_UDP:
+ fd->methods = PR_GetUDPMethods();
+ pt_MakeFdNonblock(osfd);
+ break;
+ case PR_DESC_PIPE:
+ fd->methods = PR_GetPipeMethods();
+ pt_MakeFdNonblock(osfd);
+ break;
+ default:
+ break;
+ }
+ }
+ return fd;
+} /* pt_SetMethods */
+
+PR_IMPLEMENT(const PRIOMethods*) PR_GetFileMethods(void)
+{
+ return &_pr_file_methods;
+} /* PR_GetFileMethods */
+
+PR_IMPLEMENT(const PRIOMethods*) PR_GetPipeMethods(void)
+{
+ return &_pr_pipe_methods;
+} /* PR_GetPipeMethods */
+
+PR_IMPLEMENT(const PRIOMethods*) PR_GetTCPMethods(void)
+{
+ return &_pr_tcp_methods;
+} /* PR_GetTCPMethods */
+
+PR_IMPLEMENT(const PRIOMethods*) PR_GetUDPMethods(void)
+{
+ return &_pr_udp_methods;
+} /* PR_GetUDPMethods */
+
+static const PRIOMethods* PR_GetSocketPollFdMethods(void)
+{
+ return &_pr_socketpollfd_methods;
+} /* PR_GetSocketPollFdMethods */
+
+PR_IMPLEMENT(PRFileDesc*) PR_AllocFileDesc(
+ PRInt32 osfd, const PRIOMethods *methods)
+{
+ PRFileDesc *fd = _PR_Getfd();
+
+ if (NULL == fd) {
+ goto failed;
+ }
+
+ fd->methods = methods;
+ fd->secret->md.osfd = osfd;
+ /* Make fd non-blocking */
+ if (osfd > 2)
+ {
+ /* Don't mess around with stdin, stdout or stderr */
+ if (&_pr_tcp_methods == methods) {
+ pt_MakeSocketNonblock(osfd);
+ }
+ else {
+ pt_MakeFdNonblock(osfd);
+ }
+ }
+ fd->secret->state = _PR_FILEDESC_OPEN;
+ fd->secret->inheritable = _PR_TRI_UNKNOWN;
+ return fd;
+
+failed:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return fd;
+} /* PR_AllocFileDesc */
+
+#if !defined(_PR_INET6) || defined(_PR_INET6_PROBE)
+PR_EXTERN(PRStatus) _pr_push_ipv6toipv4_layer(PRFileDesc *fd);
+#if defined(_PR_INET6_PROBE)
+extern PRBool _pr_ipv6_is_present(void);
+PR_IMPLEMENT(PRBool) _pr_test_ipv6_socket()
+{
+ int osfd;
+
+#if defined(DARWIN)
+ /*
+ * Disable IPv6 if Darwin version is less than 7.0.0 (OS X 10.3). IPv6 on
+ * lesser versions is not ready for general use (see bug 222031).
+ */
+ {
+ struct utsname u;
+ if (uname(&u) != 0 || atoi(u.release) < 7) {
+ return PR_FALSE;
+ }
+ }
+#endif
+
+ /*
+ * HP-UX only: HP-UX IPv6 Porting Guide (dated February 2001)
+ * suggests that we call open("/dev/ip6", O_RDWR) to determine
+ * whether IPv6 APIs and the IPv6 stack are on the system.
+ * Our portable test below seems to work fine, so I am using it.
+ */
+ osfd = socket(AF_INET6, SOCK_STREAM, 0);
+ if (osfd != -1) {
+ close(osfd);
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+#endif /* _PR_INET6_PROBE */
+#endif
+
+PR_IMPLEMENT(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto)
+{
+ PRIntn osfd;
+ PRDescType ftype;
+ PRFileDesc *fd = NULL;
+#if defined(_PR_INET6_PROBE) || !defined(_PR_INET6)
+ PRInt32 tmp_domain = domain;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (pt_TestAbort()) {
+ return NULL;
+ }
+
+ if (PF_INET != domain
+ && PR_AF_INET6 != domain
+#if defined(_PR_HAVE_SDP)
+ && PR_AF_INET_SDP != domain
+#if defined(SOLARIS)
+ && PR_AF_INET6_SDP != domain
+#endif /* SOLARIS */
+#endif /* _PR_HAVE_SDP */
+ && PF_UNIX != domain)
+ {
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0);
+ return fd;
+ }
+ if (type == SOCK_STREAM) {
+ ftype = PR_DESC_SOCKET_TCP;
+ }
+ else if (type == SOCK_DGRAM) {
+ ftype = PR_DESC_SOCKET_UDP;
+ }
+ else
+ {
+ (void)PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0);
+ return fd;
+ }
+#if defined(_PR_HAVE_SDP)
+#if defined(LINUX)
+ if (PR_AF_INET_SDP == domain) {
+ domain = AF_INET_SDP;
+ }
+#elif defined(SOLARIS)
+ if (PR_AF_INET_SDP == domain) {
+ domain = AF_INET;
+ proto = PROTO_SDP;
+ } else if(PR_AF_INET6_SDP == domain) {
+ domain = AF_INET6;
+ proto = PROTO_SDP;
+ }
+#endif /* SOLARIS */
+#endif /* _PR_HAVE_SDP */
+#if defined(_PR_INET6_PROBE)
+ if (PR_AF_INET6 == domain) {
+ domain = _pr_ipv6_is_present() ? AF_INET6 : AF_INET;
+ }
+#elif defined(_PR_INET6)
+ if (PR_AF_INET6 == domain) {
+ domain = AF_INET6;
+ }
+#else
+ if (PR_AF_INET6 == domain) {
+ domain = AF_INET;
+ }
+#endif
+
+ osfd = socket(domain, type, proto);
+ if (osfd == -1) {
+ pt_MapError(_PR_MD_MAP_SOCKET_ERROR, errno);
+ }
+ else
+ {
+#ifdef _PR_IPV6_V6ONLY_PROBE
+ if ((domain == AF_INET6) && _pr_ipv6_v6only_on_by_default)
+ {
+ int on = 0;
+ (void)setsockopt(osfd, IPPROTO_IPV6, IPV6_V6ONLY,
+ &on, sizeof(on));
+ }
+#endif
+ fd = pt_SetMethods(osfd, ftype, PR_FALSE, PR_FALSE);
+ if (fd == NULL) {
+ close(osfd);
+ }
+ }
+#ifdef _PR_NEED_SECRET_AF
+ if (fd != NULL) {
+ fd->secret->af = domain;
+ }
+#endif
+#if defined(_PR_INET6_PROBE) || !defined(_PR_INET6)
+ if (fd != NULL) {
+ /*
+ * For platforms with no support for IPv6
+ * create layered socket for IPv4-mapped IPv6 addresses
+ */
+ if (PR_AF_INET6 == tmp_domain && PR_AF_INET == domain) {
+ if (PR_FAILURE == _pr_push_ipv6toipv4_layer(fd)) {
+ PR_Close(fd);
+ fd = NULL;
+ }
+ }
+ }
+#endif
+ return fd;
+} /* PR_Socket */
+
+/*****************************************************************************/
+/****************************** I/O public methods ***************************/
+/*****************************************************************************/
+
+PR_IMPLEMENT(PRFileDesc*) PR_OpenFile(
+ const char *name, PRIntn flags, PRIntn mode)
+{
+ PRFileDesc *fd = NULL;
+ PRIntn syserrno, osfd = -1, osflags = 0;;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (pt_TestAbort()) {
+ return NULL;
+ }
+
+ if (flags & PR_RDONLY) {
+ osflags |= O_RDONLY;
+ }
+ if (flags & PR_WRONLY) {
+ osflags |= O_WRONLY;
+ }
+ if (flags & PR_RDWR) {
+ osflags |= O_RDWR;
+ }
+ if (flags & PR_APPEND) {
+ osflags |= O_APPEND;
+ }
+ if (flags & PR_TRUNCATE) {
+ osflags |= O_TRUNC;
+ }
+ if (flags & PR_EXCL) {
+ osflags |= O_EXCL;
+ }
+ if (flags & PR_SYNC)
+ {
+#if defined(O_SYNC)
+ osflags |= O_SYNC;
+#elif defined(O_FSYNC)
+ osflags |= O_FSYNC;
+#else
+#error "Neither O_SYNC nor O_FSYNC is defined on this platform"
+#endif
+ }
+
+ /*
+ ** We have to hold the lock across the creation in order to
+ ** enforce the sematics of PR_Rename(). (see the latter for
+ ** more details)
+ */
+ if (flags & PR_CREATE_FILE)
+ {
+ osflags |= O_CREAT;
+ if (NULL !=_pr_rename_lock) {
+ PR_Lock(_pr_rename_lock);
+ }
+ }
+
+ osfd = _md_iovector._open64(name, osflags, mode);
+ syserrno = errno;
+
+ if ((flags & PR_CREATE_FILE) && (NULL !=_pr_rename_lock)) {
+ PR_Unlock(_pr_rename_lock);
+ }
+
+ if (osfd == -1) {
+ pt_MapError(_PR_MD_MAP_OPEN_ERROR, syserrno);
+ }
+ else
+ {
+ fd = pt_SetMethods(osfd, PR_DESC_FILE, PR_FALSE, PR_FALSE);
+ if (fd == NULL) {
+ close(osfd); /* $$$ whoops! this is bad $$$ */
+ }
+ }
+ return fd;
+} /* PR_OpenFile */
+
+PR_IMPLEMENT(PRFileDesc*) PR_Open(const char *name, PRIntn flags, PRIntn mode)
+{
+ return PR_OpenFile(name, flags, mode);
+} /* PR_Open */
+
+PR_IMPLEMENT(PRStatus) PR_Delete(const char *name)
+{
+ PRIntn rv = -1;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ rv = unlink(name);
+
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_UNLINK_ERROR, errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* PR_Delete */
+
+PR_IMPLEMENT(PRStatus) PR_Access(const char *name, PRAccessHow how)
+{
+ PRIntn rv;
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ switch (how)
+ {
+ case PR_ACCESS_READ_OK:
+ rv = access(name, R_OK);
+ break;
+ case PR_ACCESS_WRITE_OK:
+ rv = access(name, W_OK);
+ break;
+ case PR_ACCESS_EXISTS:
+ default:
+ rv = access(name, F_OK);
+ }
+ if (0 == rv) {
+ return PR_SUCCESS;
+ }
+ pt_MapError(_PR_MD_MAP_ACCESS_ERROR, errno);
+ return PR_FAILURE;
+
+} /* PR_Access */
+
+PR_IMPLEMENT(PRStatus) PR_GetFileInfo(const char *fn, PRFileInfo *info)
+{
+ PRInt32 rv = _PR_MD_GETFILEINFO(fn, info);
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+} /* PR_GetFileInfo */
+
+PR_IMPLEMENT(PRStatus) PR_GetFileInfo64(const char *fn, PRFileInfo64 *info)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ rv = _PR_MD_GETFILEINFO64(fn, info);
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+} /* PR_GetFileInfo64 */
+
+PR_IMPLEMENT(PRStatus) PR_Rename(const char *from, const char *to)
+{
+ PRIntn rv = -1;
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ /*
+ ** We have to acquire a lock here to stiffle anybody trying to create
+ ** a new file at the same time. And we have to hold that lock while we
+ ** test to see if the file exists and do the rename. The other place
+ ** where the lock is held is in PR_Open() when possibly creating a
+ ** new file.
+ */
+
+ PR_Lock(_pr_rename_lock);
+ rv = access(to, F_OK);
+ if (0 == rv)
+ {
+ PR_SetError(PR_FILE_EXISTS_ERROR, 0);
+ rv = -1;
+ }
+ else
+ {
+ rv = rename(from, to);
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_RENAME_ERROR, errno);
+ }
+ }
+ PR_Unlock(_pr_rename_lock);
+ return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
+} /* PR_Rename */
+
+PR_IMPLEMENT(PRStatus) PR_CloseDir(PRDir *dir)
+{
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ if (NULL != dir->md.d)
+ {
+ if (closedir(dir->md.d) == -1)
+ {
+ _PR_MD_MAP_CLOSEDIR_ERROR(errno);
+ return PR_FAILURE;
+ }
+ dir->md.d = NULL;
+ PR_DELETE(dir);
+ }
+ return PR_SUCCESS;
+} /* PR_CloseDir */
+
+PR_IMPLEMENT(PRStatus) PR_MakeDir(const char *name, PRIntn mode)
+{
+ PRInt32 rv = -1;
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ /*
+ ** This lock is used to enforce rename semantics as described
+ ** in PR_Rename.
+ */
+ if (NULL !=_pr_rename_lock) {
+ PR_Lock(_pr_rename_lock);
+ }
+ rv = mkdir(name, mode);
+ if (-1 == rv) {
+ pt_MapError(_PR_MD_MAP_MKDIR_ERROR, errno);
+ }
+ if (NULL !=_pr_rename_lock) {
+ PR_Unlock(_pr_rename_lock);
+ }
+
+ return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
+} /* PR_Makedir */
+
+PR_IMPLEMENT(PRStatus) PR_MkDir(const char *name, PRIntn mode)
+{
+ return PR_MakeDir(name, mode);
+} /* PR_Mkdir */
+
+PR_IMPLEMENT(PRStatus) PR_RmDir(const char *name)
+{
+ PRInt32 rv;
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ rv = rmdir(name);
+ if (0 == rv) {
+ return PR_SUCCESS;
+ }
+ pt_MapError(_PR_MD_MAP_RMDIR_ERROR, errno);
+ return PR_FAILURE;
+} /* PR_Rmdir */
+
+
+PR_IMPLEMENT(PRDir*) PR_OpenDir(const char *name)
+{
+ DIR *osdir;
+ PRDir *dir = NULL;
+
+ if (pt_TestAbort()) {
+ return dir;
+ }
+
+ osdir = opendir(name);
+ if (osdir == NULL) {
+ pt_MapError(_PR_MD_MAP_OPENDIR_ERROR, errno);
+ }
+ else
+ {
+ dir = PR_NEWZAP(PRDir);
+ if (dir) {
+ dir->md.d = osdir;
+ }
+ else {
+ (void)closedir(osdir);
+ }
+ }
+ return dir;
+} /* PR_OpenDir */
+
+static PRInt32 _pr_poll_with_poll(
+ PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ PRInt32 ready = 0;
+ /*
+ * For restarting poll() if it is interrupted by a signal.
+ * We use these variables to figure out how much time has
+ * elapsed and how much of the timeout still remains.
+ */
+ PRIntervalTime start = 0, elapsed, remaining;
+
+ if (pt_TestAbort()) {
+ return -1;
+ }
+
+ if (0 == npds) {
+ PR_Sleep(timeout);
+ }
+ else
+ {
+#define STACK_POLL_DESC_COUNT 64
+ struct pollfd stack_syspoll[STACK_POLL_DESC_COUNT];
+ struct pollfd *syspoll;
+ PRIntn index, msecs;
+
+ if (npds <= STACK_POLL_DESC_COUNT)
+ {
+ syspoll = stack_syspoll;
+ }
+ else
+ {
+ PRThread *me = PR_GetCurrentThread();
+ if (npds > me->syspoll_count)
+ {
+ PR_Free(me->syspoll_list);
+ me->syspoll_list =
+ (struct pollfd*)PR_MALLOC(npds * sizeof(struct pollfd));
+ if (NULL == me->syspoll_list)
+ {
+ me->syspoll_count = 0;
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ me->syspoll_count = npds;
+ }
+ syspoll = me->syspoll_list;
+ }
+
+ for (index = 0; index < npds; ++index)
+ {
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if ((NULL != pds[index].fd) && (0 != pds[index].in_flags))
+ {
+ if (pds[index].in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pds[index].fd->methods->poll)(
+ pds[index].fd,
+ pds[index].in_flags & ~PR_POLL_WRITE,
+ &out_flags_read);
+ }
+ if (pds[index].in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pds[index].fd->methods->poll)(
+ pds[index].fd,
+ pds[index].in_flags & ~PR_POLL_READ,
+ &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read))
+ || (0 != (in_flags_write & out_flags_write)))
+ {
+ /* this one is ready right now */
+ if (0 == ready)
+ {
+ /*
+ * We will return without calling the system
+ * poll function. So zero the out_flags
+ * fields of all the poll descriptors before
+ * this one.
+ */
+ int i;
+ for (i = 0; i < index; i++)
+ {
+ pds[i].out_flags = 0;
+ }
+ }
+ ready += 1;
+ pds[index].out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ /* now locate the NSPR layer at the bottom of the stack */
+ PRFileDesc *bottom = PR_GetIdentitiesLayer(
+ pds[index].fd, PR_NSPR_IO_LAYER);
+ /* ignore a socket without PR_NSPR_IO_LAYER available */
+
+ pds[index].out_flags = 0; /* pre-condition */
+ if ((NULL != bottom)
+ && (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ if (0 == ready)
+ {
+ syspoll[index].fd = bottom->secret->md.osfd;
+ syspoll[index].events = 0;
+ if (in_flags_read & PR_POLL_READ)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_READ_SYS_READ;
+ syspoll[index].events |= POLLIN;
+ }
+ if (in_flags_read & PR_POLL_WRITE)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_READ_SYS_WRITE;
+ syspoll[index].events |= POLLOUT;
+ }
+ if (in_flags_write & PR_POLL_READ)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_WRITE_SYS_READ;
+ syspoll[index].events |= POLLIN;
+ }
+ if (in_flags_write & PR_POLL_WRITE)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_WRITE_SYS_WRITE;
+ syspoll[index].events |= POLLOUT;
+ }
+ if (pds[index].in_flags & PR_POLL_EXCEPT) {
+ syspoll[index].events |= POLLPRI;
+ }
+ }
+ }
+ else
+ {
+ if (0 == ready)
+ {
+ int i;
+ for (i = 0; i < index; i++)
+ {
+ pds[i].out_flags = 0;
+ }
+ }
+ ready += 1; /* this will cause an abrupt return */
+ pds[index].out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+ else
+ {
+ /* make poll() ignore this entry */
+ syspoll[index].fd = -1;
+ syspoll[index].events = 0;
+ pds[index].out_flags = 0;
+ }
+ }
+ if (0 == ready)
+ {
+ switch (timeout)
+ {
+ case PR_INTERVAL_NO_WAIT: msecs = 0; break;
+ case PR_INTERVAL_NO_TIMEOUT: msecs = -1; break;
+ default:
+ msecs = PR_IntervalToMilliseconds(timeout);
+ start = PR_IntervalNow();
+ }
+
+retry:
+ ready = poll(syspoll, npds, msecs);
+ if (-1 == ready)
+ {
+ PRIntn oserror = errno;
+
+ if (EINTR == oserror)
+ {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ goto retry;
+ }
+ else if (timeout == PR_INTERVAL_NO_WAIT) {
+ ready = 0; /* don't retry, just time out */
+ }
+ else
+ {
+ elapsed = (PRIntervalTime) (PR_IntervalNow()
+ - start);
+ if (elapsed > timeout) {
+ ready = 0; /* timed out */
+ }
+ else
+ {
+ remaining = timeout - elapsed;
+ msecs = PR_IntervalToMilliseconds(remaining);
+ goto retry;
+ }
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_POLL_ERROR(oserror);
+ }
+ }
+ else if (ready > 0)
+ {
+ for (index = 0; index < npds; ++index)
+ {
+ PRInt16 out_flags = 0;
+ if ((NULL != pds[index].fd) && (0 != pds[index].in_flags))
+ {
+ if (0 != syspoll[index].revents)
+ {
+ if (syspoll[index].revents & POLLIN)
+ {
+ if (pds[index].out_flags
+ & _PR_POLL_READ_SYS_READ)
+ {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pds[index].out_flags
+ & _PR_POLL_WRITE_SYS_READ)
+ {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (syspoll[index].revents & POLLOUT)
+ {
+ if (pds[index].out_flags
+ & _PR_POLL_READ_SYS_WRITE)
+ {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pds[index].out_flags
+ & _PR_POLL_WRITE_SYS_WRITE)
+ {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (syspoll[index].revents & POLLPRI) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ if (syspoll[index].revents & POLLERR) {
+ out_flags |= PR_POLL_ERR;
+ }
+ if (syspoll[index].revents & POLLNVAL) {
+ out_flags |= PR_POLL_NVAL;
+ }
+ if (syspoll[index].revents & POLLHUP) {
+ out_flags |= PR_POLL_HUP;
+ }
+ }
+ }
+ pds[index].out_flags = out_flags;
+ }
+ }
+ }
+ }
+ return ready;
+
+} /* _pr_poll_with_poll */
+
+#if defined(_PR_POLL_WITH_SELECT)
+/*
+ * HPUX report the POLLHUP event for a socket when the
+ * shutdown(SHUT_WR) operation is called for the remote end, even though
+ * the socket is still writeable. Use select(), instead of poll(), to
+ * workaround this problem.
+ */
+static PRInt32 _pr_poll_with_select(
+ PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ PRInt32 ready = 0;
+ /*
+ * For restarting select() if it is interrupted by a signal.
+ * We use these variables to figure out how much time has
+ * elapsed and how much of the timeout still remains.
+ */
+ PRIntervalTime start = 0, elapsed, remaining;
+
+ if (pt_TestAbort()) {
+ return -1;
+ }
+
+ if (0 == npds) {
+ PR_Sleep(timeout);
+ }
+ else
+ {
+#define STACK_POLL_DESC_COUNT 64
+ int stack_selectfd[STACK_POLL_DESC_COUNT];
+ int *selectfd;
+ fd_set rd, wr, ex, *rdp = NULL, *wrp = NULL, *exp = NULL;
+ struct timeval tv, *tvp;
+ PRIntn index, msecs, maxfd = 0;
+
+ if (npds <= STACK_POLL_DESC_COUNT)
+ {
+ selectfd = stack_selectfd;
+ }
+ else
+ {
+ PRThread *me = PR_GetCurrentThread();
+ if (npds > me->selectfd_count)
+ {
+ PR_Free(me->selectfd_list);
+ me->selectfd_list = (int *)PR_MALLOC(npds * sizeof(int));
+ if (NULL == me->selectfd_list)
+ {
+ me->selectfd_count = 0;
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ me->selectfd_count = npds;
+ }
+ selectfd = me->selectfd_list;
+ }
+ FD_ZERO(&rd);
+ FD_ZERO(&wr);
+ FD_ZERO(&ex);
+
+ for (index = 0; index < npds; ++index)
+ {
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if ((NULL != pds[index].fd) && (0 != pds[index].in_flags))
+ {
+ if (pds[index].in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pds[index].fd->methods->poll)(
+ pds[index].fd,
+ pds[index].in_flags & ~PR_POLL_WRITE,
+ &out_flags_read);
+ }
+ if (pds[index].in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pds[index].fd->methods->poll)(
+ pds[index].fd,
+ pds[index].in_flags & ~PR_POLL_READ,
+ &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read))
+ || (0 != (in_flags_write & out_flags_write)))
+ {
+ /* this one is ready right now */
+ if (0 == ready)
+ {
+ /*
+ * We will return without calling the system
+ * poll function. So zero the out_flags
+ * fields of all the poll descriptors before
+ * this one.
+ */
+ int i;
+ for (i = 0; i < index; i++)
+ {
+ pds[i].out_flags = 0;
+ }
+ }
+ ready += 1;
+ pds[index].out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ /* now locate the NSPR layer at the bottom of the stack */
+ PRFileDesc *bottom = PR_GetIdentitiesLayer(
+ pds[index].fd, PR_NSPR_IO_LAYER);
+ /* ignore a socket without PR_NSPR_IO_LAYER available */
+
+ pds[index].out_flags = 0; /* pre-condition */
+ if ((NULL != bottom)
+ && (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ if (0 == ready)
+ {
+ PRBool add_to_rd = PR_FALSE;
+ PRBool add_to_wr = PR_FALSE;
+ PRBool add_to_ex = PR_FALSE;
+
+ selectfd[index] = bottom->secret->md.osfd;
+ if (in_flags_read & PR_POLL_READ)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_READ_SYS_READ;
+ add_to_rd = PR_TRUE;
+ }
+ if (in_flags_read & PR_POLL_WRITE)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_READ_SYS_WRITE;
+ add_to_wr = PR_TRUE;
+ }
+ if (in_flags_write & PR_POLL_READ)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_WRITE_SYS_READ;
+ add_to_rd = PR_TRUE;
+ }
+ if (in_flags_write & PR_POLL_WRITE)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_WRITE_SYS_WRITE;
+ add_to_wr = PR_TRUE;
+ }
+ if (pds[index].in_flags & PR_POLL_EXCEPT)
+ {
+ add_to_ex = PR_TRUE;
+ }
+ if ((selectfd[index] > maxfd) &&
+ (add_to_rd || add_to_wr || add_to_ex))
+ {
+ maxfd = selectfd[index];
+ /*
+ * If maxfd is too large to be used with
+ * select, fall back to calling poll.
+ */
+ if (maxfd >= FD_SETSIZE) {
+ break;
+ }
+ }
+ if (add_to_rd)
+ {
+ FD_SET(bottom->secret->md.osfd, &rd);
+ rdp = &rd;
+ }
+ if (add_to_wr)
+ {
+ FD_SET(bottom->secret->md.osfd, &wr);
+ wrp = &wr;
+ }
+ if (add_to_ex)
+ {
+ FD_SET(bottom->secret->md.osfd, &ex);
+ exp = &ex;
+ }
+ }
+ }
+ else
+ {
+ if (0 == ready)
+ {
+ int i;
+ for (i = 0; i < index; i++)
+ {
+ pds[i].out_flags = 0;
+ }
+ }
+ ready += 1; /* this will cause an abrupt return */
+ pds[index].out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+ else
+ {
+ pds[index].out_flags = 0;
+ }
+ }
+ if (0 == ready)
+ {
+ if (maxfd >= FD_SETSIZE)
+ {
+ /*
+ * maxfd too large to be used with select, fall back to
+ * calling poll
+ */
+ return(_pr_poll_with_poll(pds, npds, timeout));
+ }
+ switch (timeout)
+ {
+ case PR_INTERVAL_NO_WAIT:
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ break;
+ case PR_INTERVAL_NO_TIMEOUT:
+ tvp = NULL;
+ break;
+ default:
+ msecs = PR_IntervalToMilliseconds(timeout);
+ tv.tv_sec = msecs/PR_MSEC_PER_SEC;
+ tv.tv_usec = (msecs % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC;
+ tvp = &tv;
+ start = PR_IntervalNow();
+ }
+
+retry:
+ ready = select(maxfd + 1, rdp, wrp, exp, tvp);
+ if (-1 == ready)
+ {
+ PRIntn oserror = errno;
+
+ if ((EINTR == oserror) || (EAGAIN == oserror))
+ {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ goto retry;
+ }
+ else if (timeout == PR_INTERVAL_NO_WAIT) {
+ ready = 0; /* don't retry, just time out */
+ }
+ else
+ {
+ elapsed = (PRIntervalTime) (PR_IntervalNow()
+ - start);
+ if (elapsed > timeout) {
+ ready = 0; /* timed out */
+ }
+ else
+ {
+ remaining = timeout - elapsed;
+ msecs = PR_IntervalToMilliseconds(remaining);
+ tv.tv_sec = msecs/PR_MSEC_PER_SEC;
+ tv.tv_usec = (msecs % PR_MSEC_PER_SEC) *
+ PR_USEC_PER_MSEC;
+ goto retry;
+ }
+ }
+ } else if (EBADF == oserror)
+ {
+ /* find all the bad fds */
+ ready = 0;
+ for (index = 0; index < npds; ++index)
+ {
+ pds[index].out_flags = 0;
+ if ((NULL != pds[index].fd) &&
+ (0 != pds[index].in_flags))
+ {
+ if (fcntl(selectfd[index], F_GETFL, 0) == -1)
+ {
+ pds[index].out_flags = PR_POLL_NVAL;
+ ready++;
+ }
+ }
+ }
+ } else {
+ _PR_MD_MAP_SELECT_ERROR(oserror);
+ }
+ }
+ else if (ready > 0)
+ {
+ for (index = 0; index < npds; ++index)
+ {
+ PRInt16 out_flags = 0;
+ if ((NULL != pds[index].fd) && (0 != pds[index].in_flags))
+ {
+ if (FD_ISSET(selectfd[index], &rd))
+ {
+ if (pds[index].out_flags
+ & _PR_POLL_READ_SYS_READ)
+ {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pds[index].out_flags
+ & _PR_POLL_WRITE_SYS_READ)
+ {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (FD_ISSET(selectfd[index], &wr))
+ {
+ if (pds[index].out_flags
+ & _PR_POLL_READ_SYS_WRITE)
+ {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pds[index].out_flags
+ & _PR_POLL_WRITE_SYS_WRITE)
+ {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (FD_ISSET(selectfd[index], &ex)) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ }
+ pds[index].out_flags = out_flags;
+ }
+ }
+ }
+ }
+ return ready;
+
+} /* _pr_poll_with_select */
+#endif /* _PR_POLL_WITH_SELECT */
+
+PR_IMPLEMENT(PRInt32) PR_Poll(
+ PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+#if defined(_PR_POLL_WITH_SELECT)
+ return(_pr_poll_with_select(pds, npds, timeout));
+#else
+ return(_pr_poll_with_poll(pds, npds, timeout));
+#endif
+}
+
+PR_IMPLEMENT(PRDirEntry*) PR_ReadDir(PRDir *dir, PRDirFlags flags)
+{
+ struct dirent *dp;
+
+ if (pt_TestAbort()) {
+ return NULL;
+ }
+
+ for (;;)
+ {
+ errno = 0;
+ dp = readdir(dir->md.d);
+ if (NULL == dp)
+ {
+ pt_MapError(_PR_MD_MAP_READDIR_ERROR, errno);
+ return NULL;
+ }
+ if ((flags & PR_SKIP_DOT)
+ && ('.' == dp->d_name[0])
+ && (0 == dp->d_name[1])) {
+ continue;
+ }
+ if ((flags & PR_SKIP_DOT_DOT)
+ && ('.' == dp->d_name[0])
+ && ('.' == dp->d_name[1])
+ && (0 == dp->d_name[2])) {
+ continue;
+ }
+ if ((flags & PR_SKIP_HIDDEN) && ('.' == dp->d_name[0])) {
+ continue;
+ }
+ break;
+ }
+ dir->d.name = dp->d_name;
+ return &dir->d;
+} /* PR_ReadDir */
+
+PR_IMPLEMENT(PRFileDesc*) PR_NewUDPSocket(void)
+{
+ PRIntn domain = PF_INET;
+
+ return PR_Socket(domain, SOCK_DGRAM, 0);
+} /* PR_NewUDPSocket */
+
+PR_IMPLEMENT(PRFileDesc*) PR_NewTCPSocket(void)
+{
+ PRIntn domain = PF_INET;
+
+ return PR_Socket(domain, SOCK_STREAM, 0);
+} /* PR_NewTCPSocket */
+
+PR_IMPLEMENT(PRFileDesc*) PR_OpenUDPSocket(PRIntn af)
+{
+ return PR_Socket(af, SOCK_DGRAM, 0);
+} /* PR_NewUDPSocket */
+
+PR_IMPLEMENT(PRFileDesc*) PR_OpenTCPSocket(PRIntn af)
+{
+ return PR_Socket(af, SOCK_STREAM, 0);
+} /* PR_NewTCPSocket */
+
+PR_IMPLEMENT(PRStatus) PR_NewTCPSocketPair(PRFileDesc *fds[2])
+{
+ PRInt32 osfd[2];
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, osfd) == -1) {
+ pt_MapError(_PR_MD_MAP_SOCKETPAIR_ERROR, errno);
+ return PR_FAILURE;
+ }
+
+ fds[0] = pt_SetMethods(osfd[0], PR_DESC_SOCKET_TCP, PR_FALSE, PR_FALSE);
+ if (fds[0] == NULL) {
+ close(osfd[0]);
+ close(osfd[1]);
+ return PR_FAILURE;
+ }
+ fds[1] = pt_SetMethods(osfd[1], PR_DESC_SOCKET_TCP, PR_FALSE, PR_FALSE);
+ if (fds[1] == NULL) {
+ PR_Close(fds[0]);
+ close(osfd[1]);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* PR_NewTCPSocketPair */
+
+PR_IMPLEMENT(PRStatus) PR_CreatePipe(
+ PRFileDesc **readPipe,
+ PRFileDesc **writePipe
+)
+{
+ int pipefd[2];
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ if (pipe(pipefd) == -1)
+ {
+ /* XXX map pipe error */
+ PR_SetError(PR_UNKNOWN_ERROR, errno);
+ return PR_FAILURE;
+ }
+ *readPipe = pt_SetMethods(pipefd[0], PR_DESC_PIPE, PR_FALSE, PR_FALSE);
+ if (NULL == *readPipe)
+ {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return PR_FAILURE;
+ }
+ *writePipe = pt_SetMethods(pipefd[1], PR_DESC_PIPE, PR_FALSE, PR_FALSE);
+ if (NULL == *writePipe)
+ {
+ PR_Close(*readPipe);
+ close(pipefd[1]);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+/*
+** Set the inheritance attribute of a file descriptor.
+*/
+PR_IMPLEMENT(PRStatus) PR_SetFDInheritable(
+ PRFileDesc *fd,
+ PRBool inheritable)
+{
+ /*
+ * Only a non-layered, NSPR file descriptor can be inherited
+ * by a child process.
+ */
+ if (fd->identity != PR_NSPR_IO_LAYER)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (fd->secret->inheritable != inheritable)
+ {
+ if (fcntl(fd->secret->md.osfd, F_SETFD,
+ inheritable ? 0 : FD_CLOEXEC) == -1)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return PR_FAILURE;
+ }
+ fd->secret->inheritable = (_PRTriStateBool) inheritable;
+ }
+ return PR_SUCCESS;
+}
+
+/*****************************************************************************/
+/***************************** I/O friends methods ***************************/
+/*****************************************************************************/
+
+PR_IMPLEMENT(PRFileDesc*) PR_ImportFile(PRInt32 osfd)
+{
+ PRFileDesc *fd;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ fd = pt_SetMethods(osfd, PR_DESC_FILE, PR_FALSE, PR_TRUE);
+ if (NULL == fd) {
+ close(osfd);
+ }
+ return fd;
+} /* PR_ImportFile */
+
+PR_IMPLEMENT(PRFileDesc*) PR_ImportPipe(PRInt32 osfd)
+{
+ PRFileDesc *fd;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ fd = pt_SetMethods(osfd, PR_DESC_PIPE, PR_FALSE, PR_TRUE);
+ if (NULL == fd) {
+ close(osfd);
+ }
+ return fd;
+} /* PR_ImportPipe */
+
+PR_IMPLEMENT(PRFileDesc*) PR_ImportTCPSocket(PRInt32 osfd)
+{
+ PRFileDesc *fd;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ fd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP, PR_FALSE, PR_TRUE);
+ if (NULL == fd) {
+ close(osfd);
+ }
+#ifdef _PR_NEED_SECRET_AF
+ if (NULL != fd) {
+ fd->secret->af = PF_INET;
+ }
+#endif
+ return fd;
+} /* PR_ImportTCPSocket */
+
+PR_IMPLEMENT(PRFileDesc*) PR_ImportUDPSocket(PRInt32 osfd)
+{
+ PRFileDesc *fd;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ fd = pt_SetMethods(osfd, PR_DESC_SOCKET_UDP, PR_FALSE, PR_TRUE);
+ if (NULL == fd) {
+ close(osfd);
+ }
+ return fd;
+} /* PR_ImportUDPSocket */
+
+PR_IMPLEMENT(PRFileDesc*) PR_CreateSocketPollFd(PRInt32 osfd)
+{
+ PRFileDesc *fd;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ fd = _PR_Getfd();
+
+ if (fd == NULL) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ else
+ {
+ fd->secret->md.osfd = osfd;
+ fd->secret->inheritable = _PR_TRI_FALSE;
+ fd->secret->state = _PR_FILEDESC_OPEN;
+ fd->methods = PR_GetSocketPollFdMethods();
+ }
+
+ return fd;
+} /* PR_CreateSocketPollFD */
+
+PR_IMPLEMENT(PRStatus) PR_DestroySocketPollFd(PRFileDesc *fd)
+{
+ if (NULL == fd)
+ {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+ fd->secret->state = _PR_FILEDESC_CLOSED;
+ _PR_Putfd(fd);
+ return PR_SUCCESS;
+} /* PR_DestroySocketPollFd */
+
+PR_IMPLEMENT(PRInt32) PR_FileDesc2NativeHandle(PRFileDesc *bottom)
+{
+ PRInt32 osfd = -1;
+ bottom = (NULL == bottom) ?
+ NULL : PR_GetIdentitiesLayer(bottom, PR_NSPR_IO_LAYER);
+ if (NULL == bottom) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ }
+ else {
+ osfd = bottom->secret->md.osfd;
+ }
+ return osfd;
+} /* PR_FileDesc2NativeHandle */
+
+PR_IMPLEMENT(void) PR_ChangeFileDescNativeHandle(PRFileDesc *fd,
+ PRInt32 handle)
+{
+ if (fd) {
+ fd->secret->md.osfd = handle;
+ }
+} /* PR_ChangeFileDescNativeHandle*/
+
+PR_IMPLEMENT(PRStatus) PR_LockFile(PRFileDesc *fd)
+{
+ PRStatus status = PR_SUCCESS;
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ PR_Lock(_pr_flock_lock);
+ while (-1 == fd->secret->lockCount) {
+ PR_WaitCondVar(_pr_flock_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ if (0 == fd->secret->lockCount)
+ {
+ fd->secret->lockCount = -1;
+ PR_Unlock(_pr_flock_lock);
+ status = _PR_MD_LOCKFILE(fd->secret->md.osfd);
+ PR_Lock(_pr_flock_lock);
+ fd->secret->lockCount = (PR_SUCCESS == status) ? 1 : 0;
+ PR_NotifyAllCondVar(_pr_flock_cv);
+ }
+ else
+ {
+ fd->secret->lockCount += 1;
+ }
+ PR_Unlock(_pr_flock_lock);
+
+ return status;
+} /* PR_LockFile */
+
+PR_IMPLEMENT(PRStatus) PR_TLockFile(PRFileDesc *fd)
+{
+ PRStatus status = PR_SUCCESS;
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ PR_Lock(_pr_flock_lock);
+ if (0 == fd->secret->lockCount)
+ {
+ status = _PR_MD_TLOCKFILE(fd->secret->md.osfd);
+ if (PR_SUCCESS == status) {
+ fd->secret->lockCount = 1;
+ }
+ }
+ else {
+ fd->secret->lockCount += 1;
+ }
+ PR_Unlock(_pr_flock_lock);
+
+ return status;
+} /* PR_TLockFile */
+
+PR_IMPLEMENT(PRStatus) PR_UnlockFile(PRFileDesc *fd)
+{
+ PRStatus status = PR_SUCCESS;
+
+ if (pt_TestAbort()) {
+ return PR_FAILURE;
+ }
+
+ PR_Lock(_pr_flock_lock);
+ if (fd->secret->lockCount == 1)
+ {
+ status = _PR_MD_UNLOCKFILE(fd->secret->md.osfd);
+ if (PR_SUCCESS == status) {
+ fd->secret->lockCount = 0;
+ }
+ }
+ else {
+ fd->secret->lockCount -= 1;
+ }
+ PR_Unlock(_pr_flock_lock);
+
+ return status;
+}
+
+/*
+ * The next two entry points should not be in the API, but they are
+ * defined here for historical (or hysterical) reasons.
+ */
+
+PR_IMPLEMENT(PRInt32) PR_GetSysfdTableMax(void)
+{
+#if defined(AIX)
+ return sysconf(_SC_OPEN_MAX);
+#else
+ struct rlimit rlim;
+
+ if ( getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+ return -1;
+ }
+
+ return rlim.rlim_max;
+#endif
+}
+
+PR_IMPLEMENT(PRInt32) PR_SetSysfdTableSize(PRIntn table_size)
+{
+#if defined(AIX)
+ return -1;
+#else
+ struct rlimit rlim;
+ PRInt32 tableMax = PR_GetSysfdTableMax();
+
+ if (tableMax < 0) {
+ return -1;
+ }
+ rlim.rlim_max = tableMax;
+
+ /* Grow as much as we can; even if too big */
+ if ( rlim.rlim_max < table_size ) {
+ rlim.rlim_cur = rlim.rlim_max;
+ }
+ else {
+ rlim.rlim_cur = table_size;
+ }
+
+ if ( setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+ return -1;
+ }
+
+ return rlim.rlim_cur;
+#endif
+}
+
+/*
+ * PR_Stat is supported for backward compatibility; some existing Java
+ * code uses it. New code should use PR_GetFileInfo.
+ */
+
+#ifndef NO_NSPR_10_SUPPORT
+PR_IMPLEMENT(PRInt32) PR_Stat(const char *name, struct stat *buf)
+{
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) {
+ unwarned = _PR_Obsolete("PR_Stat", "PR_GetFileInfo");
+ }
+
+ if (pt_TestAbort()) {
+ return -1;
+ }
+
+ if (-1 == stat(name, buf)) {
+ pt_MapError(_PR_MD_MAP_STAT_ERROR, errno);
+ return -1;
+ } else {
+ return 0;
+ }
+}
+#endif /* ! NO_NSPR_10_SUPPORT */
+
+
+PR_IMPLEMENT(void) PR_FD_ZERO(PR_fd_set *set)
+{
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) {
+ unwarned = _PR_Obsolete("PR_FD_ZERO (PR_Select)", "PR_Poll");
+ }
+ memset(set, 0, sizeof(PR_fd_set));
+}
+
+PR_IMPLEMENT(void) PR_FD_SET(PRFileDesc *fh, PR_fd_set *set)
+{
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) {
+ unwarned = _PR_Obsolete("PR_FD_SET (PR_Select)", "PR_Poll");
+ }
+ PR_ASSERT( set->hsize < PR_MAX_SELECT_DESC );
+
+ set->harray[set->hsize++] = fh;
+}
+
+PR_IMPLEMENT(void) PR_FD_CLR(PRFileDesc *fh, PR_fd_set *set)
+{
+ PRUint32 index, index2;
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) {
+ unwarned = _PR_Obsolete("PR_FD_CLR (PR_Select)", "PR_Poll");
+ }
+
+ for (index = 0; index<set->hsize; index++)
+ if (set->harray[index] == fh) {
+ for (index2=index; index2 < (set->hsize-1); index2++) {
+ set->harray[index2] = set->harray[index2+1];
+ }
+ set->hsize--;
+ break;
+ }
+}
+
+PR_IMPLEMENT(PRInt32) PR_FD_ISSET(PRFileDesc *fh, PR_fd_set *set)
+{
+ PRUint32 index;
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) {
+ unwarned = _PR_Obsolete("PR_FD_ISSET (PR_Select)", "PR_Poll");
+ }
+ for (index = 0; index<set->hsize; index++)
+ if (set->harray[index] == fh) {
+ return 1;
+ }
+ return 0;
+}
+
+PR_IMPLEMENT(void) PR_FD_NSET(PRInt32 fd, PR_fd_set *set)
+{
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) {
+ unwarned = _PR_Obsolete("PR_FD_NSET (PR_Select)", "PR_Poll");
+ }
+ PR_ASSERT( set->nsize < PR_MAX_SELECT_DESC );
+
+ set->narray[set->nsize++] = fd;
+}
+
+PR_IMPLEMENT(void) PR_FD_NCLR(PRInt32 fd, PR_fd_set *set)
+{
+ PRUint32 index, index2;
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) {
+ unwarned = _PR_Obsolete("PR_FD_NCLR (PR_Select)", "PR_Poll");
+ }
+
+ for (index = 0; index<set->nsize; index++)
+ if (set->narray[index] == fd) {
+ for (index2=index; index2 < (set->nsize-1); index2++) {
+ set->narray[index2] = set->narray[index2+1];
+ }
+ set->nsize--;
+ break;
+ }
+}
+
+PR_IMPLEMENT(PRInt32) PR_FD_NISSET(PRInt32 fd, PR_fd_set *set)
+{
+ PRUint32 index;
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) {
+ unwarned = _PR_Obsolete("PR_FD_NISSET (PR_Select)", "PR_Poll");
+ }
+ for (index = 0; index<set->nsize; index++)
+ if (set->narray[index] == fd) {
+ return 1;
+ }
+ return 0;
+}
+
+#include <sys/types.h>
+#include <sys/time.h>
+#if !defined(HPUX) \
+ && !defined(LINUX) && !defined(__GNU__) && !defined(__GLIBC__)
+#include <sys/select.h>
+#endif
+
+static PRInt32
+_PR_getset(PR_fd_set *pr_set, fd_set *set)
+{
+ PRUint32 index;
+ PRInt32 max = 0;
+
+ if (!pr_set) {
+ return 0;
+ }
+
+ FD_ZERO(set);
+
+ /* First set the pr file handle osfds */
+ for (index=0; index<pr_set->hsize; index++) {
+ FD_SET(pr_set->harray[index]->secret->md.osfd, set);
+ if (pr_set->harray[index]->secret->md.osfd > max) {
+ max = pr_set->harray[index]->secret->md.osfd;
+ }
+ }
+ /* Second set the native osfds */
+ for (index=0; index<pr_set->nsize; index++) {
+ FD_SET(pr_set->narray[index], set);
+ if (pr_set->narray[index] > max) {
+ max = pr_set->narray[index];
+ }
+ }
+ return max;
+}
+
+static void
+_PR_setset(PR_fd_set *pr_set, fd_set *set)
+{
+ PRUint32 index, last_used;
+
+ if (!pr_set) {
+ return;
+ }
+
+ for (last_used=0, index=0; index<pr_set->hsize; index++) {
+ if ( FD_ISSET(pr_set->harray[index]->secret->md.osfd, set) ) {
+ pr_set->harray[last_used++] = pr_set->harray[index];
+ }
+ }
+ pr_set->hsize = last_used;
+
+ for (last_used=0, index=0; index<pr_set->nsize; index++) {
+ if ( FD_ISSET(pr_set->narray[index], set) ) {
+ pr_set->narray[last_used++] = pr_set->narray[index];
+ }
+ }
+ pr_set->nsize = last_used;
+}
+
+PR_IMPLEMENT(PRInt32) PR_Select(
+ PRInt32 unused, PR_fd_set *pr_rd, PR_fd_set *pr_wr,
+ PR_fd_set *pr_ex, PRIntervalTime timeout)
+{
+ fd_set rd, wr, ex;
+ struct timeval tv, *tvp;
+ PRInt32 max, max_fd;
+ PRInt32 rv;
+ /*
+ * For restarting select() if it is interrupted by a Unix signal.
+ * We use these variables to figure out how much time has elapsed
+ * and how much of the timeout still remains.
+ */
+ PRIntervalTime start = 0, elapsed, remaining;
+
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) {
+ unwarned = _PR_Obsolete( "PR_Select", "PR_Poll");
+ }
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wr);
+ FD_ZERO(&ex);
+
+ max_fd = _PR_getset(pr_rd, &rd);
+ max_fd = (max = _PR_getset(pr_wr, &wr))>max_fd?max:max_fd;
+ max_fd = (max = _PR_getset(pr_ex, &ex))>max_fd?max:max_fd;
+
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = (PRInt32)PR_IntervalToSeconds(timeout);
+ tv.tv_usec = (PRInt32)PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ start = PR_IntervalNow();
+ }
+
+retry:
+ rv = select(max_fd + 1, (_PRSelectFdSetArg_t) &rd,
+ (_PRSelectFdSetArg_t) &wr, (_PRSelectFdSetArg_t) &ex, tvp);
+
+ if (rv == -1 && errno == EINTR) {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ goto retry;
+ } else {
+ elapsed = (PRIntervalTime) (PR_IntervalNow() - start);
+ if (elapsed > timeout) {
+ rv = 0; /* timed out */
+ } else {
+ remaining = timeout - elapsed;
+ tv.tv_sec = (PRInt32)PR_IntervalToSeconds(remaining);
+ tv.tv_usec = (PRInt32)PR_IntervalToMicroseconds(
+ remaining - PR_SecondsToInterval(tv.tv_sec));
+ goto retry;
+ }
+ }
+ }
+
+ if (rv > 0) {
+ _PR_setset(pr_rd, &rd);
+ _PR_setset(pr_wr, &wr);
+ _PR_setset(pr_ex, &ex);
+ } else if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_SELECT_ERROR, errno);
+ }
+ return rv;
+}
+#endif /* defined(_PR_PTHREADS) */
+
+#ifdef MOZ_UNICODE
+/* ================ UTF16 Interfaces ================================ */
+PR_IMPLEMENT(PRFileDesc*) PR_OpenFileUTF16(
+ const PRUnichar *name, PRIntn flags, PRIntn mode)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CloseDirUTF16(PRDir *dir)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRDirUTF16*) PR_OpenDirUTF16(const PRUnichar *name)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+PR_IMPLEMENT(PRDirEntryUTF16*) PR_ReadDirUTF16(PRDirUTF16 *dir, PRDirFlags flags)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetFileInfo64UTF16(const PRUnichar *fn, PRFileInfo64 *info)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+/* ================ UTF16 Interfaces ================================ */
+#endif /* MOZ_UNICODE */
+
+/* ptio.c */
diff --git a/nsprpub/pr/src/pthreads/ptmisc.c b/nsprpub/pr/src/pthreads/ptmisc.c
new file mode 100644
index 0000000000..4069f585ab
--- /dev/null
+++ b/nsprpub/pr/src/pthreads/ptmisc.c
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: ptmisc.c
+** Descritpion: Implemenation of miscellaneous methods for pthreads
+*/
+
+#if defined(_PR_PTHREADS)
+
+#include "primpl.h"
+
+#include <stdio.h>
+#ifdef SOLARIS
+#include <thread.h>
+#endif
+
+#define PT_LOG(f)
+
+void _PR_InitCPUs(void) {
+ PT_LOG("_PR_InitCPUs")
+}
+void _PR_InitStacks(void) {
+ PT_LOG("_PR_InitStacks")
+}
+
+PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs)
+{
+#ifdef SOLARIS
+ thr_setconcurrency(numCPUs);
+#else
+ PT_LOG("PR_SetConcurrency");
+#endif
+}
+
+PR_IMPLEMENT(void) PR_SetThreadRecycleMode(PRUint32 flag)
+{
+ PT_LOG("PR_SetThreadRecycleMode")
+}
+
+#endif /* defined(_PR_PTHREADS) */
+
+/* ptmisc.c */
diff --git a/nsprpub/pr/src/pthreads/ptsynch.c b/nsprpub/pr/src/pthreads/ptsynch.c
new file mode 100644
index 0000000000..26f58b2d0f
--- /dev/null
+++ b/nsprpub/pr/src/pthreads/ptsynch.c
@@ -0,0 +1,1295 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: ptsynch.c
+** Descritpion: Implemenation for thread synchronization using pthreads
+** Exports: prlock.h, prcvar.h, prmon.h, prcmon.h
+*/
+
+#if defined(_PR_PTHREADS)
+
+#include "primpl.h"
+#include "obsolete/prsem.h"
+
+#include <string.h>
+#include <pthread.h>
+#include <sys/time.h>
+
+static pthread_mutexattr_t _pt_mattr;
+static pthread_condattr_t _pt_cvar_attr;
+
+#if defined(DEBUG)
+extern PTDebug pt_debug; /* this is shared between several modules */
+#endif /* defined(DEBUG) */
+
+#if defined(FREEBSD)
+/*
+ * On older versions of FreeBSD, pthread_mutex_trylock returns EDEADLK.
+ * Newer versions return EBUSY. We still need to support both.
+ */
+static int
+pt_pthread_mutex_is_locked(pthread_mutex_t *m)
+{
+ int rv = pthread_mutex_trylock(m);
+ return (EBUSY == rv || EDEADLK == rv);
+}
+#endif
+
+/**************************************************************/
+/**************************************************************/
+/*****************************LOCKS****************************/
+/**************************************************************/
+/**************************************************************/
+
+void _PR_InitLocks(void)
+{
+ int rv;
+ rv = _PT_PTHREAD_MUTEXATTR_INIT(&_pt_mattr);
+ PR_ASSERT(0 == rv);
+
+#if (defined(LINUX) && (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)) || \
+ (defined(FREEBSD) && __FreeBSD_version > 700055)
+ rv = pthread_mutexattr_settype(&_pt_mattr, PTHREAD_MUTEX_ADAPTIVE_NP);
+ PR_ASSERT(0 == rv);
+#endif
+
+ rv = _PT_PTHREAD_CONDATTR_INIT(&_pt_cvar_attr);
+ PR_ASSERT(0 == rv);
+}
+
+static void pt_PostNotifies(PRLock *lock, PRBool unlock)
+{
+ PRIntn index, rv;
+ _PT_Notified post;
+ _PT_Notified *notified, *prev = NULL;
+ /*
+ * Time to actually notify any conditions that were affected
+ * while the lock was held. Get a copy of the list that's in
+ * the lock structure and then zero the original. If it's
+ * linked to other such structures, we own that storage.
+ */
+ post = lock->notified; /* a safe copy; we own the lock */
+
+#if defined(DEBUG)
+ memset(&lock->notified, 0, sizeof(_PT_Notified)); /* reset */
+#else
+ lock->notified.length = 0; /* these are really sufficient */
+ lock->notified.link = NULL;
+#endif
+
+ /* should (may) we release lock before notifying? */
+ if (unlock)
+ {
+ rv = pthread_mutex_unlock(&lock->mutex);
+ PR_ASSERT(0 == rv);
+ }
+
+ notified = &post; /* this is where we start */
+ do
+ {
+ for (index = 0; index < notified->length; ++index)
+ {
+ PRCondVar *cv = notified->cv[index].cv;
+ PR_ASSERT(NULL != cv);
+ PR_ASSERT(0 != notified->cv[index].times);
+ if (-1 == notified->cv[index].times)
+ {
+ rv = pthread_cond_broadcast(&cv->cv);
+ PR_ASSERT(0 == rv);
+ }
+ else
+ {
+ while (notified->cv[index].times-- > 0)
+ {
+ rv = pthread_cond_signal(&cv->cv);
+ PR_ASSERT(0 == rv);
+ }
+ }
+#if defined(DEBUG)
+ pt_debug.cvars_notified += 1;
+ if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending))
+ {
+ pt_debug.delayed_cv_deletes += 1;
+ PR_DestroyCondVar(cv);
+ }
+#else /* defined(DEBUG) */
+ if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) {
+ PR_DestroyCondVar(cv);
+ }
+#endif /* defined(DEBUG) */
+ }
+ prev = notified;
+ notified = notified->link;
+ if (&post != prev) {
+ PR_DELETE(prev);
+ }
+ } while (NULL != notified);
+} /* pt_PostNotifies */
+
+PR_IMPLEMENT(PRLock*) PR_NewLock(void)
+{
+ PRIntn rv;
+ PRLock *lock;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ lock = PR_NEWZAP(PRLock);
+ if (lock != NULL)
+ {
+ rv = _PT_PTHREAD_MUTEX_INIT(lock->mutex, _pt_mattr);
+ PR_ASSERT(0 == rv);
+ }
+#if defined(DEBUG)
+ pt_debug.locks_created += 1;
+#endif
+ return lock;
+} /* PR_NewLock */
+
+PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock)
+{
+ PRIntn rv;
+ PR_ASSERT(NULL != lock);
+ PR_ASSERT(PR_FALSE == lock->locked);
+ PR_ASSERT(0 == lock->notified.length);
+ PR_ASSERT(NULL == lock->notified.link);
+ rv = pthread_mutex_destroy(&lock->mutex);
+ PR_ASSERT(0 == rv);
+#if defined(DEBUG)
+ memset(lock, 0xaf, sizeof(PRLock));
+ pt_debug.locks_destroyed += 1;
+#endif
+ PR_Free(lock);
+} /* PR_DestroyLock */
+
+PR_IMPLEMENT(void) PR_Lock(PRLock *lock)
+{
+ /* Nb: PR_Lock must not call PR_GetCurrentThread to access the |id| or
+ * |tid| field of the current thread's PRThread structure because
+ * _pt_root calls PR_Lock before setting thred->id and thred->tid. */
+ PRIntn rv;
+ PR_ASSERT(lock != NULL);
+ rv = pthread_mutex_lock(&lock->mutex);
+ PR_ASSERT(0 == rv);
+ PR_ASSERT(0 == lock->notified.length);
+ PR_ASSERT(NULL == lock->notified.link);
+ PR_ASSERT(PR_FALSE == lock->locked);
+ /* Nb: the order of the next two statements is not critical to
+ * the correctness of PR_AssertCurrentThreadOwnsLock(), but
+ * this particular order makes the assertion more likely to
+ * catch errors. */
+ lock->owner = pthread_self();
+ lock->locked = PR_TRUE;
+#if defined(DEBUG)
+ pt_debug.locks_acquired += 1;
+#endif
+} /* PR_Lock */
+
+PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock)
+{
+ pthread_t self = pthread_self();
+ PRIntn rv;
+
+ PR_ASSERT(lock != NULL);
+ PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(lock->mutex));
+ PR_ASSERT(PR_TRUE == lock->locked);
+ PR_ASSERT(pthread_equal(lock->owner, self));
+
+ if (!lock->locked || !pthread_equal(lock->owner, self)) {
+ return PR_FAILURE;
+ }
+
+ lock->locked = PR_FALSE;
+ if (0 == lock->notified.length) /* shortcut */
+ {
+ rv = pthread_mutex_unlock(&lock->mutex);
+ PR_ASSERT(0 == rv);
+ }
+ else {
+ pt_PostNotifies(lock, PR_TRUE);
+ }
+
+#if defined(DEBUG)
+ pt_debug.locks_released += 1;
+#endif
+ return PR_SUCCESS;
+} /* PR_Unlock */
+
+PR_IMPLEMENT(void) PR_AssertCurrentThreadOwnsLock(PRLock *lock)
+{
+ /* Nb: the order of the |locked| and |owner==me| checks is not critical
+ * to the correctness of PR_AssertCurrentThreadOwnsLock(), but
+ * this particular order makes the assertion more likely to
+ * catch errors. */
+ PR_ASSERT(lock->locked && pthread_equal(lock->owner, pthread_self()));
+}
+
+/**************************************************************/
+/**************************************************************/
+/***************************CONDITIONS*************************/
+/**************************************************************/
+/**************************************************************/
+
+
+/*
+ * This code is used to compute the absolute time for the wakeup.
+ * It's moderately ugly, so it's defined here and called in a
+ * couple of places.
+ */
+#define PT_NANOPERMICRO 1000UL
+#define PT_BILLION 1000000000UL
+
+static PRIntn pt_TimedWait(
+ pthread_cond_t *cv, pthread_mutex_t *ml, PRIntervalTime timeout)
+{
+ int rv;
+ struct timeval now;
+ struct timespec tmo;
+ PRUint32 ticks = PR_TicksPerSecond();
+
+ tmo.tv_sec = (PRInt32)(timeout / ticks);
+ tmo.tv_nsec = (PRInt32)(timeout - (tmo.tv_sec * ticks));
+ tmo.tv_nsec = (PRInt32)PR_IntervalToMicroseconds(PT_NANOPERMICRO * tmo.tv_nsec);
+
+ /* pthreads wants this in absolute time, off we go ... */
+ (void)GETTIMEOFDAY(&now);
+ /* that one's usecs, this one's nsecs - grrrr! */
+ tmo.tv_sec += now.tv_sec;
+ tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec);
+ tmo.tv_sec += tmo.tv_nsec / PT_BILLION;
+ tmo.tv_nsec %= PT_BILLION;
+
+ rv = pthread_cond_timedwait(cv, ml, &tmo);
+
+ /* NSPR doesn't report timeouts */
+ return (rv == ETIMEDOUT) ? 0 : rv;
+} /* pt_TimedWait */
+
+
+/*
+ * Notifies just get posted to the protecting mutex. The
+ * actual notification is done when the lock is released so that
+ * MP systems don't contend for a lock that they can't have.
+ */
+static void pt_PostNotifyToCvar(PRCondVar *cvar, PRBool broadcast)
+{
+ PRIntn index = 0;
+ _PT_Notified *notified = &cvar->lock->notified;
+
+ PR_ASSERT(PR_TRUE == cvar->lock->locked);
+ PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));
+ PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex));
+
+ while (1)
+ {
+ for (index = 0; index < notified->length; ++index)
+ {
+ if (notified->cv[index].cv == cvar)
+ {
+ if (broadcast) {
+ notified->cv[index].times = -1;
+ }
+ else if (-1 != notified->cv[index].times) {
+ notified->cv[index].times += 1;
+ }
+ return; /* we're finished */
+ }
+ }
+ /* if not full, enter new CV in this array */
+ if (notified->length < PT_CV_NOTIFIED_LENGTH) {
+ break;
+ }
+
+ /* if there's no link, create an empty array and link it */
+ if (NULL == notified->link) {
+ notified->link = PR_NEWZAP(_PT_Notified);
+ }
+ notified = notified->link;
+ }
+
+ /* A brand new entry in the array */
+ (void)PR_ATOMIC_INCREMENT(&cvar->notify_pending);
+ notified->cv[index].times = (broadcast) ? -1 : 1;
+ notified->cv[index].cv = cvar;
+ notified->length += 1;
+} /* pt_PostNotifyToCvar */
+
+PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock)
+{
+ PRCondVar *cv = PR_NEW(PRCondVar);
+ PR_ASSERT(lock != NULL);
+ if (cv != NULL)
+ {
+ int rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr);
+ PR_ASSERT(0 == rv);
+ if (0 == rv)
+ {
+ cv->lock = lock;
+ cv->notify_pending = 0;
+#if defined(DEBUG)
+ pt_debug.cvars_created += 1;
+#endif
+ }
+ else
+ {
+ PR_DELETE(cv);
+ cv = NULL;
+ }
+ }
+ return cv;
+} /* PR_NewCondVar */
+
+PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar)
+{
+ if (0 > PR_ATOMIC_DECREMENT(&cvar->notify_pending))
+ {
+ PRIntn rv = pthread_cond_destroy(&cvar->cv);
+#if defined(DEBUG)
+ PR_ASSERT(0 == rv);
+ memset(cvar, 0xaf, sizeof(PRCondVar));
+ pt_debug.cvars_destroyed += 1;
+#else
+ (void)rv;
+#endif
+ PR_Free(cvar);
+ }
+} /* PR_DestroyCondVar */
+
+PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout)
+{
+ PRIntn rv;
+ PRThread *thred = PR_GetCurrentThread();
+
+ PR_ASSERT(cvar != NULL);
+ /* We'd better be locked */
+ PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex));
+ PR_ASSERT(PR_TRUE == cvar->lock->locked);
+ /* and it better be by us */
+ PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));
+
+ if (_PT_THREAD_INTERRUPTED(thred)) {
+ goto aborted;
+ }
+
+ /*
+ * The thread waiting is used for PR_Interrupt
+ */
+ thred->waiting = cvar; /* this is where we're waiting */
+
+ /*
+ * If we have pending notifies, post them now.
+ *
+ * This is not optimal. We're going to post these notifies
+ * while we're holding the lock. That means on MP systems
+ * that they are going to collide for the lock that we will
+ * hold until we actually wait.
+ */
+ if (0 != cvar->lock->notified.length) {
+ pt_PostNotifies(cvar->lock, PR_FALSE);
+ }
+
+ /*
+ * We're surrendering the lock, so clear out the locked field.
+ */
+ cvar->lock->locked = PR_FALSE;
+
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ rv = pthread_cond_wait(&cvar->cv, &cvar->lock->mutex);
+ }
+ else {
+ rv = pt_TimedWait(&cvar->cv, &cvar->lock->mutex, timeout);
+ }
+
+ /* We just got the lock back - this better be empty */
+ PR_ASSERT(PR_FALSE == cvar->lock->locked);
+ cvar->lock->locked = PR_TRUE;
+ cvar->lock->owner = pthread_self();
+
+ PR_ASSERT(0 == cvar->lock->notified.length);
+ thred->waiting = NULL; /* and now we're not */
+ if (_PT_THREAD_INTERRUPTED(thred)) {
+ goto aborted;
+ }
+ if (rv != 0)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(rv);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+
+aborted:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thred->state &= ~PT_THREAD_ABORTED;
+ return PR_FAILURE;
+} /* PR_WaitCondVar */
+
+PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar)
+{
+ PR_ASSERT(cvar != NULL);
+ pt_PostNotifyToCvar(cvar, PR_FALSE);
+ return PR_SUCCESS;
+} /* PR_NotifyCondVar */
+
+PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar)
+{
+ PR_ASSERT(cvar != NULL);
+ pt_PostNotifyToCvar(cvar, PR_TRUE);
+ return PR_SUCCESS;
+} /* PR_NotifyAllCondVar */
+
+/**************************************************************/
+/**************************************************************/
+/***************************MONITORS***************************/
+/**************************************************************/
+/**************************************************************/
+
+/*
+ * Notifies just get posted to the monitor. The actual notification is done
+ * when the monitor is fully exited so that MP systems don't contend for a
+ * monitor that they can't enter.
+ */
+static void pt_PostNotifyToMonitor(PRMonitor *mon, PRBool broadcast)
+{
+ PR_ASSERT(NULL != mon);
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mon);
+
+ /* mon->notifyTimes is protected by the monitor, so we don't need to
+ * acquire mon->lock.
+ */
+ if (broadcast) {
+ mon->notifyTimes = -1;
+ }
+ else if (-1 != mon->notifyTimes) {
+ mon->notifyTimes += 1;
+ }
+} /* pt_PostNotifyToMonitor */
+
+static void pt_PostNotifiesFromMonitor(pthread_cond_t *cv, PRIntn times)
+{
+ PRIntn rv;
+
+ /*
+ * Time to actually notify any waits that were affected while the monitor
+ * was entered.
+ */
+ PR_ASSERT(NULL != cv);
+ PR_ASSERT(0 != times);
+ if (-1 == times)
+ {
+ rv = pthread_cond_broadcast(cv);
+ PR_ASSERT(0 == rv);
+ }
+ else
+ {
+ while (times-- > 0)
+ {
+ rv = pthread_cond_signal(cv);
+ PR_ASSERT(0 == rv);
+ }
+ }
+} /* pt_PostNotifiesFromMonitor */
+
+PR_IMPLEMENT(PRMonitor*) PR_NewMonitor(void)
+{
+ PRMonitor *mon;
+ int rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ mon = PR_NEWZAP(PRMonitor);
+ if (mon == NULL)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+
+ rv = _PT_PTHREAD_MUTEX_INIT(mon->lock, _pt_mattr);
+ PR_ASSERT(0 == rv);
+ if (0 != rv) {
+ goto error1;
+ }
+
+ _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);
+
+ rv = _PT_PTHREAD_COND_INIT(mon->entryCV, _pt_cvar_attr);
+ PR_ASSERT(0 == rv);
+ if (0 != rv) {
+ goto error2;
+ }
+
+ rv = _PT_PTHREAD_COND_INIT(mon->waitCV, _pt_cvar_attr);
+ PR_ASSERT(0 == rv);
+ if (0 != rv) {
+ goto error3;
+ }
+
+ mon->notifyTimes = 0;
+ mon->entryCount = 0;
+ mon->refCount = 1;
+ mon->name = NULL;
+ return mon;
+
+error3:
+ pthread_cond_destroy(&mon->entryCV);
+error2:
+ pthread_mutex_destroy(&mon->lock);
+error1:
+ PR_Free(mon);
+ _PR_MD_MAP_DEFAULT_ERROR(rv);
+ return NULL;
+} /* PR_NewMonitor */
+
+PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name)
+{
+ PRMonitor* mon = PR_NewMonitor();
+ if (mon) {
+ mon->name = name;
+ }
+ return mon;
+}
+
+PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon)
+{
+ int rv;
+
+ PR_ASSERT(mon != NULL);
+ if (PR_ATOMIC_DECREMENT(&mon->refCount) == 0)
+ {
+ rv = pthread_cond_destroy(&mon->waitCV); PR_ASSERT(0 == rv);
+ rv = pthread_cond_destroy(&mon->entryCV); PR_ASSERT(0 == rv);
+ rv = pthread_mutex_destroy(&mon->lock); PR_ASSERT(0 == rv);
+#if defined(DEBUG)
+ memset(mon, 0xaf, sizeof(PRMonitor));
+#endif
+ PR_Free(mon);
+ }
+} /* PR_DestroyMonitor */
+
+/* The GC uses this; it is quite arguably a bad interface. I'm just
+ * duplicating it for now - XXXMB
+ */
+PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon)
+{
+ pthread_t self = pthread_self();
+ PRIntn rv;
+ PRIntn count = 0;
+
+ rv = pthread_mutex_lock(&mon->lock);
+ PR_ASSERT(0 == rv);
+ if (pthread_equal(mon->owner, self)) {
+ count = mon->entryCount;
+ }
+ rv = pthread_mutex_unlock(&mon->lock);
+ PR_ASSERT(0 == rv);
+ return count;
+}
+
+PR_IMPLEMENT(void) PR_AssertCurrentThreadInMonitor(PRMonitor *mon)
+{
+#if defined(DEBUG) || defined(FORCE_PR_ASSERT)
+ PRIntn rv;
+
+ rv = pthread_mutex_lock(&mon->lock);
+ PR_ASSERT(0 == rv);
+ PR_ASSERT(mon->entryCount != 0 &&
+ pthread_equal(mon->owner, pthread_self()));
+ rv = pthread_mutex_unlock(&mon->lock);
+ PR_ASSERT(0 == rv);
+#endif
+}
+
+PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon)
+{
+ pthread_t self = pthread_self();
+ PRIntn rv;
+
+ PR_ASSERT(mon != NULL);
+ rv = pthread_mutex_lock(&mon->lock);
+ PR_ASSERT(0 == rv);
+ if (mon->entryCount != 0)
+ {
+ if (pthread_equal(mon->owner, self)) {
+ goto done;
+ }
+ while (mon->entryCount != 0)
+ {
+ rv = pthread_cond_wait(&mon->entryCV, &mon->lock);
+ PR_ASSERT(0 == rv);
+ }
+ }
+ /* and now I have the monitor */
+ PR_ASSERT(0 == mon->notifyTimes);
+ PR_ASSERT(_PT_PTHREAD_THR_HANDLE_IS_INVALID(mon->owner));
+ _PT_PTHREAD_COPY_THR_HANDLE(self, mon->owner);
+
+done:
+ mon->entryCount += 1;
+ rv = pthread_mutex_unlock(&mon->lock);
+ PR_ASSERT(0 == rv);
+} /* PR_EnterMonitor */
+
+PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon)
+{
+ pthread_t self = pthread_self();
+ PRIntn rv;
+ PRBool notifyEntryWaiter = PR_FALSE;
+ PRIntn notifyTimes = 0;
+
+ PR_ASSERT(mon != NULL);
+ rv = pthread_mutex_lock(&mon->lock);
+ PR_ASSERT(0 == rv);
+ /* the entries should be > 0 and we'd better be the owner */
+ PR_ASSERT(mon->entryCount > 0);
+ PR_ASSERT(pthread_equal(mon->owner, self));
+ if (mon->entryCount == 0 || !pthread_equal(mon->owner, self))
+ {
+ rv = pthread_mutex_unlock(&mon->lock);
+ PR_ASSERT(0 == rv);
+ return PR_FAILURE;
+ }
+
+ mon->entryCount -= 1; /* reduce by one */
+ if (mon->entryCount == 0)
+ {
+ /* and if it transitioned to zero - notify an entry waiter */
+ /* make the owner unknown */
+ _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);
+ notifyEntryWaiter = PR_TRUE;
+ notifyTimes = mon->notifyTimes;
+ mon->notifyTimes = 0;
+ /* We will access the members of 'mon' after unlocking mon->lock.
+ * Add a reference. */
+ PR_ATOMIC_INCREMENT(&mon->refCount);
+ }
+ rv = pthread_mutex_unlock(&mon->lock);
+ PR_ASSERT(0 == rv);
+ if (notifyEntryWaiter)
+ {
+ if (notifyTimes) {
+ pt_PostNotifiesFromMonitor(&mon->waitCV, notifyTimes);
+ }
+ rv = pthread_cond_signal(&mon->entryCV);
+ PR_ASSERT(0 == rv);
+ /* We are done accessing the members of 'mon'. Release the
+ * reference. */
+ PR_DestroyMonitor(mon);
+ }
+ return PR_SUCCESS;
+} /* PR_ExitMonitor */
+
+PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime timeout)
+{
+ PRStatus rv;
+ PRUint32 saved_entries;
+ pthread_t saved_owner;
+
+ PR_ASSERT(mon != NULL);
+ rv = pthread_mutex_lock(&mon->lock);
+ PR_ASSERT(0 == rv);
+ /* the entries better be positive */
+ PR_ASSERT(mon->entryCount > 0);
+ /* and it better be owned by us */
+ PR_ASSERT(pthread_equal(mon->owner, pthread_self()));
+
+ /* tuck these away 'till later */
+ saved_entries = mon->entryCount;
+ mon->entryCount = 0;
+ _PT_PTHREAD_COPY_THR_HANDLE(mon->owner, saved_owner);
+ _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);
+ /*
+ * If we have pending notifies, post them now.
+ *
+ * This is not optimal. We're going to post these notifies
+ * while we're holding the lock. That means on MP systems
+ * that they are going to collide for the lock that we will
+ * hold until we actually wait.
+ */
+ if (0 != mon->notifyTimes)
+ {
+ pt_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes);
+ mon->notifyTimes = 0;
+ }
+ rv = pthread_cond_signal(&mon->entryCV);
+ PR_ASSERT(0 == rv);
+
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ rv = pthread_cond_wait(&mon->waitCV, &mon->lock);
+ }
+ else {
+ rv = pt_TimedWait(&mon->waitCV, &mon->lock, timeout);
+ }
+ PR_ASSERT(0 == rv);
+
+ while (mon->entryCount != 0)
+ {
+ rv = pthread_cond_wait(&mon->entryCV, &mon->lock);
+ PR_ASSERT(0 == rv);
+ }
+ PR_ASSERT(0 == mon->notifyTimes);
+ /* reinstate the interesting information */
+ mon->entryCount = saved_entries;
+ _PT_PTHREAD_COPY_THR_HANDLE(saved_owner, mon->owner);
+
+ rv = pthread_mutex_unlock(&mon->lock);
+ PR_ASSERT(0 == rv);
+ return rv;
+} /* PR_Wait */
+
+PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon)
+{
+ pt_PostNotifyToMonitor(mon, PR_FALSE);
+ return PR_SUCCESS;
+} /* PR_Notify */
+
+PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon)
+{
+ pt_PostNotifyToMonitor(mon, PR_TRUE);
+ return PR_SUCCESS;
+} /* PR_NotifyAll */
+
+/**************************************************************/
+/**************************************************************/
+/**************************SEMAPHORES**************************/
+/**************************************************************/
+/**************************************************************/
+PR_IMPLEMENT(void) PR_PostSem(PRSemaphore *semaphore)
+{
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete(
+ "PR_PostSem", "locks & condition variables");
+ PR_Lock(semaphore->cvar->lock);
+ PR_NotifyCondVar(semaphore->cvar);
+ semaphore->count += 1;
+ PR_Unlock(semaphore->cvar->lock);
+} /* PR_PostSem */
+
+PR_IMPLEMENT(PRStatus) PR_WaitSem(PRSemaphore *semaphore)
+{
+ PRStatus status = PR_SUCCESS;
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete(
+ "PR_WaitSem", "locks & condition variables");
+ PR_Lock(semaphore->cvar->lock);
+ while ((semaphore->count == 0) && (PR_SUCCESS == status)) {
+ status = PR_WaitCondVar(semaphore->cvar, PR_INTERVAL_NO_TIMEOUT);
+ }
+ if (PR_SUCCESS == status) {
+ semaphore->count -= 1;
+ }
+ PR_Unlock(semaphore->cvar->lock);
+ return status;
+} /* PR_WaitSem */
+
+PR_IMPLEMENT(void) PR_DestroySem(PRSemaphore *semaphore)
+{
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete(
+ "PR_DestroySem", "locks & condition variables");
+ PR_DestroyLock(semaphore->cvar->lock);
+ PR_DestroyCondVar(semaphore->cvar);
+ PR_Free(semaphore);
+} /* PR_DestroySem */
+
+PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value)
+{
+ PRSemaphore *semaphore;
+ static PRBool unwarned = PR_TRUE;
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (unwarned) unwarned = _PR_Obsolete(
+ "PR_NewSem", "locks & condition variables");
+
+ semaphore = PR_NEWZAP(PRSemaphore);
+ if (NULL != semaphore)
+ {
+ PRLock *lock = PR_NewLock();
+ if (NULL != lock)
+ {
+ semaphore->cvar = PR_NewCondVar(lock);
+ if (NULL != semaphore->cvar)
+ {
+ semaphore->count = value;
+ return semaphore;
+ }
+ PR_DestroyLock(lock);
+ }
+ PR_Free(semaphore);
+ }
+ return NULL;
+}
+
+/*
+ * Define the interprocess named semaphore functions.
+ * There are three implementations:
+ * 1. POSIX semaphore based;
+ * 2. System V semaphore based;
+ * 3. unsupported (fails with PR_NOT_IMPLEMENTED_ERROR).
+ */
+
+#ifdef _PR_HAVE_POSIX_SEMAPHORES
+#include <fcntl.h>
+
+PR_IMPLEMENT(PRSem *) PR_OpenSemaphore(
+ const char *name,
+ PRIntn flags,
+ PRIntn mode,
+ PRUintn value)
+{
+ PRSem *sem;
+ char osname[PR_IPC_NAME_SIZE];
+
+ if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
+ == PR_FAILURE)
+ {
+ return NULL;
+ }
+
+ sem = PR_NEW(PRSem);
+ if (NULL == sem)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+
+ if (flags & PR_SEM_CREATE)
+ {
+ int oflag = O_CREAT;
+
+ if (flags & PR_SEM_EXCL) {
+ oflag |= O_EXCL;
+ }
+ sem->sem = sem_open(osname, oflag, mode, value);
+ }
+ else
+ {
+#ifdef HPUX
+ /* Pass 0 as the mode and value arguments to work around a bug. */
+ sem->sem = sem_open(osname, 0, 0, 0);
+#else
+ sem->sem = sem_open(osname, 0);
+#endif
+ }
+ if ((sem_t *) -1 == sem->sem)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ PR_Free(sem);
+ return NULL;
+ }
+ return sem;
+}
+
+PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem)
+{
+ int rv;
+ rv = sem_wait(sem->sem);
+ if (0 != rv)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem)
+{
+ int rv;
+ rv = sem_post(sem->sem);
+ if (0 != rv)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem)
+{
+ int rv;
+ rv = sem_close(sem->sem);
+ if (0 != rv)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return PR_FAILURE;
+ }
+ PR_Free(sem);
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name)
+{
+ int rv;
+ char osname[PR_IPC_NAME_SIZE];
+
+ if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
+ == PR_FAILURE)
+ {
+ return PR_FAILURE;
+ }
+ rv = sem_unlink(osname);
+ if (0 != rv)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+#elif defined(_PR_HAVE_SYSV_SEMAPHORES)
+
+#include <fcntl.h>
+#include <sys/sem.h>
+
+/*
+ * From the semctl(2) man page in glibc 2.0
+ */
+#if (defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)) \
+ || (defined(FREEBSD) && __FreeBSD_version < 1200059) \
+ || defined(OPENBSD) || defined(BSDI) \
+ || defined(DARWIN)
+/* union semun is defined by including <sys/sem.h> */
+#else
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+ int val;
+ struct semid_ds *buf;
+ unsigned short *array;
+};
+#endif
+
+/*
+ * 'a' (97) is the final closing price of NSCP stock.
+ */
+#define NSPR_IPC_KEY_ID 'a' /* the id argument for ftok() */
+
+#define NSPR_SEM_MODE 0666
+
+PR_IMPLEMENT(PRSem *) PR_OpenSemaphore(
+ const char *name,
+ PRIntn flags,
+ PRIntn mode,
+ PRUintn value)
+{
+ PRSem *sem;
+ key_t key;
+ union semun arg;
+ struct sembuf sop;
+ struct semid_ds seminfo;
+#define MAX_TRIES 60
+ PRIntn i;
+ char osname[PR_IPC_NAME_SIZE];
+
+ if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
+ == PR_FAILURE)
+ {
+ return NULL;
+ }
+
+ /* Make sure the file exists before calling ftok. */
+ if (flags & PR_SEM_CREATE)
+ {
+ int osfd = open(osname, O_RDWR|O_CREAT, mode);
+ if (-1 == osfd)
+ {
+ _PR_MD_MAP_OPEN_ERROR(errno);
+ return NULL;
+ }
+ if (close(osfd) == -1)
+ {
+ _PR_MD_MAP_CLOSE_ERROR(errno);
+ return NULL;
+ }
+ }
+ key = ftok(osname, NSPR_IPC_KEY_ID);
+ if ((key_t)-1 == key)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return NULL;
+ }
+
+ sem = PR_NEW(PRSem);
+ if (NULL == sem)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+
+ if (flags & PR_SEM_CREATE)
+ {
+ sem->semid = semget(key, 1, mode|IPC_CREAT|IPC_EXCL);
+ if (sem->semid >= 0)
+ {
+ /* creator of a semaphore is responsible for initializing it */
+ arg.val = 0;
+ if (semctl(sem->semid, 0, SETVAL, arg) == -1)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ PR_Free(sem);
+ return NULL;
+ }
+ /* call semop to set sem_otime to nonzero */
+ sop.sem_num = 0;
+ sop.sem_op = value;
+ sop.sem_flg = 0;
+ if (semop(sem->semid, &sop, 1) == -1)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ PR_Free(sem);
+ return NULL;
+ }
+ return sem;
+ }
+
+ if (errno != EEXIST || flags & PR_SEM_EXCL)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ PR_Free(sem);
+ return NULL;
+ }
+ }
+
+ sem->semid = semget(key, 1, NSPR_SEM_MODE);
+ if (sem->semid == -1)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ PR_Free(sem);
+ return NULL;
+ }
+ for (i = 0; i < MAX_TRIES; i++)
+ {
+ arg.buf = &seminfo;
+ semctl(sem->semid, 0, IPC_STAT, arg);
+ if (seminfo.sem_otime != 0) {
+ break;
+ }
+ sleep(1);
+ }
+ if (i == MAX_TRIES)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ PR_Free(sem);
+ return NULL;
+ }
+ return sem;
+}
+
+PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem)
+{
+ struct sembuf sop;
+
+ sop.sem_num = 0;
+ sop.sem_op = -1;
+ sop.sem_flg = 0;
+ if (semop(sem->semid, &sop, 1) == -1)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem)
+{
+ struct sembuf sop;
+
+ sop.sem_num = 0;
+ sop.sem_op = 1;
+ sop.sem_flg = 0;
+ if (semop(sem->semid, &sop, 1) == -1)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem)
+{
+ PR_Free(sem);
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name)
+{
+ key_t key;
+ int semid;
+ /* On some systems (e.g., glibc 2.0) semctl requires a fourth argument */
+ union semun unused;
+ char osname[PR_IPC_NAME_SIZE];
+
+ if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
+ == PR_FAILURE)
+ {
+ return PR_FAILURE;
+ }
+ key = ftok(osname, NSPR_IPC_KEY_ID);
+ if ((key_t) -1 == key)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return PR_FAILURE;
+ }
+ if (unlink(osname) == -1)
+ {
+ _PR_MD_MAP_UNLINK_ERROR(errno);
+ return PR_FAILURE;
+ }
+ semid = semget(key, 1, NSPR_SEM_MODE);
+ if (-1 == semid)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return PR_FAILURE;
+ }
+ unused.val = 0;
+ if (semctl(semid, 0, IPC_RMID, unused) == -1)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+#else /* neither POSIX nor System V semaphores are available */
+
+PR_IMPLEMENT(PRSem *) PR_OpenSemaphore(
+ const char *name,
+ PRIntn flags,
+ PRIntn mode,
+ PRUintn value)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+#endif /* end of interprocess named semaphore functions */
+
+/**************************************************************/
+/**************************************************************/
+/******************ROUTINES FOR DCE EMULATION******************/
+/**************************************************************/
+/**************************************************************/
+
+#include "prpdce.h"
+
+PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock)
+{
+ PRIntn rv = pthread_mutex_trylock(&lock->mutex);
+ if (rv == 0)
+ {
+ PR_ASSERT(PR_FALSE == lock->locked);
+ lock->locked = PR_TRUE;
+ lock->owner = pthread_self();
+ }
+ /* XXX set error code? */
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+} /* PRP_TryLock */
+
+PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar(void)
+{
+ PRCondVar *cv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ cv = PR_NEW(PRCondVar);
+ if (cv != NULL)
+ {
+ int rv;
+ rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr);
+ PR_ASSERT(0 == rv);
+ if (0 == rv)
+ {
+ cv->lock = _PR_NAKED_CV_LOCK;
+ }
+ else
+ {
+ PR_DELETE(cv);
+ cv = NULL;
+ }
+ }
+ return cv;
+} /* PRP_NewNakedCondVar */
+
+PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar *cvar)
+{
+ int rv;
+ rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv);
+#if defined(DEBUG)
+ memset(cvar, 0xaf, sizeof(PRCondVar));
+#endif
+ PR_Free(cvar);
+} /* PRP_DestroyNakedCondVar */
+
+PR_IMPLEMENT(PRStatus) PRP_NakedWait(
+ PRCondVar *cvar, PRLock *ml, PRIntervalTime timeout)
+{
+ PRIntn rv;
+ PR_ASSERT(cvar != NULL);
+ /* XXX do we really want to assert this in a naked wait? */
+ PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(ml->mutex));
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ rv = pthread_cond_wait(&cvar->cv, &ml->mutex);
+ }
+ else {
+ rv = pt_TimedWait(&cvar->cv, &ml->mutex, timeout);
+ }
+ if (rv != 0)
+ {
+ _PR_MD_MAP_DEFAULT_ERROR(rv);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* PRP_NakedWait */
+
+PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar *cvar)
+{
+ int rv;
+ PR_ASSERT(cvar != NULL);
+ rv = pthread_cond_signal(&cvar->cv);
+ PR_ASSERT(0 == rv);
+ return PR_SUCCESS;
+} /* PRP_NakedNotify */
+
+PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar)
+{
+ int rv;
+ PR_ASSERT(cvar != NULL);
+ rv = pthread_cond_broadcast(&cvar->cv);
+ PR_ASSERT(0 == rv);
+ return PR_SUCCESS;
+} /* PRP_NakedBroadcast */
+
+#endif /* defined(_PR_PTHREADS) */
+
+/* ptsynch.c */
diff --git a/nsprpub/pr/src/pthreads/ptthread.c b/nsprpub/pr/src/pthreads/ptthread.c
new file mode 100644
index 0000000000..92c9c7889e
--- /dev/null
+++ b/nsprpub/pr/src/pthreads/ptthread.c
@@ -0,0 +1,1749 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: ptthread.c
+** Descritpion: Implemenation for threds using pthreds
+** Exports: ptthread.h
+*/
+
+#if defined(_PR_PTHREADS)
+
+#include "prlog.h"
+#include "primpl.h"
+#include "prpdce.h"
+
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <dlfcn.h>
+
+#if defined(OPENBSD) || defined(FREEBSD) || defined(DRAGONFLY)
+#include <pthread_np.h>
+#endif
+
+#if defined(ANDROID)
+#include <sys/prctl.h>
+#endif
+
+#ifdef _PR_NICE_PRIORITY_SCHEDULING
+#undef _POSIX_THREAD_PRIORITY_SCHEDULING
+#include <sys/resource.h>
+#ifndef HAVE_GETTID
+#define gettid() (syscall(SYS_gettid))
+#endif
+#endif
+
+/*
+ * Record whether or not we have the privilege to set the scheduling
+ * policy and priority of threads. 0 means that privilege is available.
+ * EPERM means that privilege is not available.
+ */
+
+static PRIntn pt_schedpriv = 0;
+extern PRLock *_pr_sleeplock;
+
+static struct _PT_Bookeeping
+{
+ PRLock *ml; /* a lock to protect ourselves */
+ PRCondVar *cv; /* used to signal global things */
+ PRInt32 system, user; /* a count of the two different types */
+ PRUintn this_many; /* number of threads allowed for exit */
+ pthread_key_t key; /* thread private data key */
+ PRBool keyCreated; /* whether 'key' should be deleted */
+ PRThread *first, *last; /* list of threads we know about */
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+ PRInt32 minPrio, maxPrio; /* range of scheduling priorities */
+#endif
+} pt_book = {0};
+
+static void _pt_thread_death(void *arg);
+static void _pt_thread_death_internal(void *arg, PRBool callDestructors);
+static void init_pthread_gc_support(void);
+
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+static PRIntn pt_PriorityMap(PRThreadPriority pri)
+{
+#ifdef NTO
+ /* This priority algorithm causes lots of problems on Neutrino
+ * for now I have just hard coded everything to run at priority 10
+ * until I can come up with a new algorithm.
+ * Jerry.Kirk@Nexwarecorp.com
+ */
+ return 10;
+#else
+ return pt_book.minPrio +
+ pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST;
+#endif
+}
+#elif defined(_PR_NICE_PRIORITY_SCHEDULING)
+/*
+ * This functions maps higher priorities to lower nice values relative to the
+ * nice value specified in the |nice| parameter. The corresponding relative
+ * adjustments are:
+ *
+ * PR_PRIORITY_LOW +1
+ * PR_PRIORITY_NORMAL 0
+ * PR_PRIORITY_HIGH -1
+ * PR_PRIORITY_URGENT -2
+ */
+static int pt_RelativePriority(int nice, PRThreadPriority pri)
+{
+ return nice + (1 - pri);
+}
+#endif
+
+/*
+** Initialize a stack for a native pthread thread
+*/
+static void _PR_InitializeStack(PRThreadStack *ts)
+{
+ if( ts && (ts->stackTop == 0) ) {
+ ts->allocBase = (char *) &ts;
+ ts->allocSize = ts->stackSize;
+
+ /*
+ ** Setup stackTop and stackBottom values.
+ */
+#ifdef HAVE_STACK_GROWING_UP
+ ts->stackBottom = ts->allocBase + ts->stackSize;
+ ts->stackTop = ts->allocBase;
+#else
+ ts->stackTop = ts->allocBase;
+ ts->stackBottom = ts->allocBase - ts->stackSize;
+#endif
+ }
+}
+
+static void *_pt_root(void *arg)
+{
+ PRIntn rv;
+ PRThread *thred = (PRThread*)arg;
+ PRBool detached = (thred->state & PT_THREAD_DETACHED) ? PR_TRUE : PR_FALSE;
+ pthread_t id = pthread_self();
+#ifdef _PR_NICE_PRIORITY_SCHEDULING
+ pid_t tid;
+#endif
+
+#ifdef _PR_NICE_PRIORITY_SCHEDULING
+ /*
+ * We need to know the kernel thread ID of each thread in order to
+ * set its nice value hence we do it here instead of at creation time.
+ */
+ tid = gettid();
+ errno = 0;
+ rv = getpriority(PRIO_PROCESS, 0);
+
+ /* If we cannot read the main thread's nice value don't try to change the
+ * new thread's nice value. */
+ if (errno == 0) {
+ setpriority(PRIO_PROCESS, tid,
+ pt_RelativePriority(rv, thred->priority));
+ }
+#endif
+
+ /* Set up the thread stack information */
+ _PR_InitializeStack(thred->stack);
+
+ /*
+ * Set within the current thread the pointer to our object.
+ * This object will be deleted when the thread termintates,
+ * whether in a join or detached (see _PR_InitThreads()).
+ */
+ rv = pthread_setspecific(pt_book.key, thred);
+ PR_ASSERT(0 == rv);
+
+ /* make the thread visible to the rest of the runtime */
+ PR_Lock(pt_book.ml);
+ /*
+ * Both the parent thread and this new thread set thred->id.
+ * The new thread must ensure that thred->id is set before
+ * it executes its startFunc. The parent thread must ensure
+ * that thred->id is set before PR_CreateThread() returns.
+ * Both threads set thred->id while holding pt_book.ml and
+ * use thred->idSet to ensure thred->id is written only once.
+ */
+ if (!thred->idSet)
+ {
+ thred->id = id;
+ thred->idSet = PR_TRUE;
+ }
+ else
+ {
+ PR_ASSERT(pthread_equal(thred->id, id));
+ }
+
+#ifdef _PR_NICE_PRIORITY_SCHEDULING
+ thred->tid = tid;
+ PR_NotifyAllCondVar(pt_book.cv);
+#endif
+
+ /* If this is a GCABLE thread, set its state appropriately */
+ if (thred->suspend & PT_THREAD_SETGCABLE) {
+ thred->state |= PT_THREAD_GCABLE;
+ }
+ thred->suspend = 0;
+
+ thred->prev = pt_book.last;
+ if (pt_book.last) {
+ pt_book.last->next = thred;
+ }
+ else {
+ pt_book.first = thred;
+ }
+ thred->next = NULL;
+ pt_book.last = thred;
+ PR_Unlock(pt_book.ml);
+
+ thred->startFunc(thred->arg); /* make visible to the client */
+
+ /* unhook the thread from the runtime */
+ PR_Lock(pt_book.ml);
+ /*
+ * At this moment, PR_CreateThread() may not have set thred->id yet.
+ * It is safe for a detached thread to free thred only after
+ * PR_CreateThread() has accessed thred->id and thred->idSet.
+ */
+ if (detached)
+ {
+ while (!thred->okToDelete) {
+ PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ }
+
+ if (thred->state & PT_THREAD_SYSTEM) {
+ pt_book.system -= 1;
+ }
+ else if (--pt_book.user == pt_book.this_many) {
+ PR_NotifyAllCondVar(pt_book.cv);
+ }
+ if (NULL == thred->prev) {
+ pt_book.first = thred->next;
+ }
+ else {
+ thred->prev->next = thred->next;
+ }
+ if (NULL == thred->next) {
+ pt_book.last = thred->prev;
+ }
+ else {
+ thred->next->prev = thred->prev;
+ }
+ PR_Unlock(pt_book.ml);
+
+ /*
+ * Here we set the pthread's backpointer to the PRThread to NULL.
+ * Otherwise the destructor would get called eagerly as the thread
+ * returns to the pthread runtime. The joining thread would them be
+ * the proud possessor of a dangling reference. However, this is the
+ * last chance to delete the object if the thread is detached, so
+ * just let the destructor do the work.
+ */
+ if (PR_FALSE == detached)
+ {
+ /* Call TPD destructors on this thread. */
+ _PR_DestroyThreadPrivate(thred);
+ rv = pthread_setspecific(pt_book.key, NULL);
+ PR_ASSERT(0 == rv);
+ }
+
+ return NULL;
+} /* _pt_root */
+
+static PRThread* pt_AttachThread(void)
+{
+ PRThread *thred = NULL;
+
+ /*
+ * NSPR must have been initialized when PR_AttachThread is called.
+ * We cannot have PR_AttachThread call implicit initialization
+ * because if multiple threads call PR_AttachThread simultaneously,
+ * NSPR may be initialized more than once.
+ * We can't call any function that calls PR_GetCurrentThread()
+ * either (e.g., PR_SetError()) as that will result in infinite
+ * recursion.
+ */
+ if (!_pr_initialized) {
+ return NULL;
+ }
+
+ /* PR_NEWZAP must not call PR_GetCurrentThread() */
+ thred = PR_NEWZAP(PRThread);
+ if (NULL != thred)
+ {
+ int rv;
+
+ thred->priority = PR_PRIORITY_NORMAL;
+ thred->id = pthread_self();
+ thred->idSet = PR_TRUE;
+#ifdef _PR_NICE_PRIORITY_SCHEDULING
+ thred->tid = gettid();
+#endif
+ rv = pthread_setspecific(pt_book.key, thred);
+ PR_ASSERT(0 == rv);
+
+ thred->state = PT_THREAD_GLOBAL | PT_THREAD_FOREIGN;
+ PR_Lock(pt_book.ml);
+
+ /* then put it into the list */
+ thred->prev = pt_book.last;
+ if (pt_book.last) {
+ pt_book.last->next = thred;
+ }
+ else {
+ pt_book.first = thred;
+ }
+ thred->next = NULL;
+ pt_book.last = thred;
+ PR_Unlock(pt_book.ml);
+
+ }
+ return thred; /* may be NULL */
+} /* pt_AttachThread */
+
+static PRThread* _PR_CreateThread(
+ PRThreadType type, void (*start)(void *arg),
+ void *arg, PRThreadPriority priority, PRThreadScope scope,
+ PRThreadState state, PRUint32 stackSize, PRBool isGCAble)
+{
+ int rv;
+ PRThread *thred;
+ pthread_attr_t tattr;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)priority) {
+ priority = PR_PRIORITY_FIRST;
+ }
+ else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)priority) {
+ priority = PR_PRIORITY_LAST;
+ }
+
+ rv = _PT_PTHREAD_ATTR_INIT(&tattr);
+ PR_ASSERT(0 == rv);
+
+ if (EPERM != pt_schedpriv)
+ {
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+ struct sched_param schedule;
+#endif
+
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+ rv = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED);
+ PR_ASSERT(0 == rv);
+#endif
+
+ /* Use the default scheduling policy */
+
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+ rv = pthread_attr_getschedparam(&tattr, &schedule);
+ PR_ASSERT(0 == rv);
+ schedule.sched_priority = pt_PriorityMap(priority);
+ rv = pthread_attr_setschedparam(&tattr, &schedule);
+ PR_ASSERT(0 == rv);
+#ifdef NTO
+ rv = pthread_attr_setschedpolicy(&tattr, SCHED_RR); /* Round Robin */
+ PR_ASSERT(0 == rv);
+#endif
+#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING > 0 */
+ }
+
+ rv = pthread_attr_setdetachstate(&tattr,
+ ((PR_JOINABLE_THREAD == state) ?
+ PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED));
+ PR_ASSERT(0 == rv);
+
+ /*
+ * If stackSize is 0, we use the default pthread stack size.
+ */
+ if (stackSize)
+ {
+#ifdef _MD_MINIMUM_STACK_SIZE
+ if (stackSize < _MD_MINIMUM_STACK_SIZE) {
+ stackSize = _MD_MINIMUM_STACK_SIZE;
+ }
+#endif
+ rv = pthread_attr_setstacksize(&tattr, stackSize);
+ PR_ASSERT(0 == rv);
+ }
+
+ thred = PR_NEWZAP(PRThread);
+ if (NULL == thred)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, errno);
+ goto done;
+ }
+ else
+ {
+ pthread_t id;
+
+ thred->arg = arg;
+ thred->startFunc = start;
+ thred->priority = priority;
+ if (PR_UNJOINABLE_THREAD == state) {
+ thred->state |= PT_THREAD_DETACHED;
+ }
+
+ if (PR_LOCAL_THREAD == scope) {
+ scope = PR_GLOBAL_THREAD;
+ }
+
+ if (PR_GLOBAL_BOUND_THREAD == scope) {
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+ rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
+ if (rv) {
+ /*
+ * system scope not supported
+ */
+ scope = PR_GLOBAL_THREAD;
+ /*
+ * reset scope
+ */
+ rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS);
+ PR_ASSERT(0 == rv);
+ }
+#endif
+ }
+ if (PR_GLOBAL_THREAD == scope) {
+ thred->state |= PT_THREAD_GLOBAL;
+ }
+ else if (PR_GLOBAL_BOUND_THREAD == scope) {
+ thred->state |= (PT_THREAD_GLOBAL | PT_THREAD_BOUND);
+ }
+ else { /* force it global */
+ thred->state |= PT_THREAD_GLOBAL;
+ }
+ if (PR_SYSTEM_THREAD == type) {
+ thred->state |= PT_THREAD_SYSTEM;
+ }
+
+ thred->suspend =(isGCAble) ? PT_THREAD_SETGCABLE : 0;
+
+ thred->stack = PR_NEWZAP(PRThreadStack);
+ if (thred->stack == NULL) {
+ PRIntn oserr = errno;
+ PR_Free(thred); /* all that work ... poof! */
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, oserr);
+ thred = NULL; /* and for what? */
+ goto done;
+ }
+ thred->stack->stackSize = stackSize;
+ thred->stack->thr = thred;
+
+#ifdef PT_NO_SIGTIMEDWAIT
+ pthread_mutex_init(&thred->suspendResumeMutex,NULL);
+ pthread_cond_init(&thred->suspendResumeCV,NULL);
+#endif
+
+ /* make the thread counted to the rest of the runtime */
+ PR_Lock(pt_book.ml);
+ if (PR_SYSTEM_THREAD == type) {
+ pt_book.system += 1;
+ }
+ else {
+ pt_book.user += 1;
+ }
+ PR_Unlock(pt_book.ml);
+
+ /*
+ * We pass a pointer to a local copy (instead of thred->id)
+ * to pthread_create() because who knows what wacky things
+ * pthread_create() may be doing to its argument.
+ */
+ rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);
+
+ if (EPERM == rv)
+ {
+ /* Remember that we don't have thread scheduling privilege. */
+ pt_schedpriv = EPERM;
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("_PR_CreateThread: no thread scheduling privilege"));
+ /* Try creating the thread again without setting priority. */
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+ rv = pthread_attr_setinheritsched(&tattr, PTHREAD_INHERIT_SCHED);
+ PR_ASSERT(0 == rv);
+#endif
+ rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);
+ }
+
+ if (0 != rv)
+ {
+ PRIntn oserr = rv;
+ PR_Lock(pt_book.ml);
+ if (thred->state & PT_THREAD_SYSTEM) {
+ pt_book.system -= 1;
+ }
+ else if (--pt_book.user == pt_book.this_many) {
+ PR_NotifyAllCondVar(pt_book.cv);
+ }
+ PR_Unlock(pt_book.ml);
+
+ PR_Free(thred->stack);
+ PR_Free(thred); /* all that work ... poof! */
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, oserr);
+ thred = NULL; /* and for what? */
+ goto done;
+ }
+
+ PR_Lock(pt_book.ml);
+ /*
+ * Both the parent thread and this new thread set thred->id.
+ * The parent thread must ensure that thred->id is set before
+ * PR_CreateThread() returns. (See comments in _pt_root().)
+ */
+ if (!thred->idSet)
+ {
+ thred->id = id;
+ thred->idSet = PR_TRUE;
+ }
+ else
+ {
+ PR_ASSERT(pthread_equal(thred->id, id));
+ }
+
+ /*
+ * If the new thread is detached, tell it that PR_CreateThread() has
+ * accessed thred->id and thred->idSet so it's ok to delete thred.
+ */
+ if (PR_UNJOINABLE_THREAD == state)
+ {
+ thred->okToDelete = PR_TRUE;
+ PR_NotifyAllCondVar(pt_book.cv);
+ }
+ PR_Unlock(pt_book.ml);
+ }
+
+done:
+ rv = _PT_PTHREAD_ATTR_DESTROY(&tattr);
+ PR_ASSERT(0 == rv);
+
+ return thred;
+} /* _PR_CreateThread */
+
+PR_IMPLEMENT(PRThread*) PR_CreateThread(
+ PRThreadType type, void (*start)(void *arg), void *arg,
+ PRThreadPriority priority, PRThreadScope scope,
+ PRThreadState state, PRUint32 stackSize)
+{
+ return _PR_CreateThread(
+ type, start, arg, priority, scope, state, stackSize, PR_FALSE);
+} /* PR_CreateThread */
+
+PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble(
+ PRThreadType type, void (*start)(void *arg), void *arg,
+ PRThreadPriority priority, PRThreadScope scope,
+ PRThreadState state, PRUint32 stackSize)
+{
+ return _PR_CreateThread(
+ type, start, arg, priority, scope, state, stackSize, PR_TRUE);
+} /* PR_CreateThreadGCAble */
+
+PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thred)
+{
+ return thred->environment;
+} /* GetExecutionEnvironment */
+
+PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thred, void *env)
+{
+ thred->environment = env;
+} /* SetExecutionEnvironment */
+
+PR_IMPLEMENT(PRThread*) PR_AttachThread(
+ PRThreadType type, PRThreadPriority priority, PRThreadStack *stack)
+{
+ return PR_GetCurrentThread();
+} /* PR_AttachThread */
+
+
+PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thred)
+{
+ int rv = -1;
+ void *result = NULL;
+ PR_ASSERT(thred != NULL);
+
+ if ((0xafafafaf == thred->state)
+ || (PT_THREAD_DETACHED == (PT_THREAD_DETACHED & thred->state))
+ || (PT_THREAD_FOREIGN == (PT_THREAD_FOREIGN & thred->state)))
+ {
+ /*
+ * This might be a bad address, but if it isn't, the state should
+ * either be an unjoinable thread or it's already had the object
+ * deleted. However, the client that called join on a detached
+ * thread deserves all the rath I can muster....
+ */
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ PR_LogPrint(
+ "PR_JoinThread: %p not joinable | already smashed\n", thred);
+ }
+ else
+ {
+ pthread_t id = thred->id;
+ rv = pthread_join(id, &result);
+ PR_ASSERT(rv == 0 && result == NULL);
+ if (0 == rv)
+ {
+ /*
+ * PR_FALSE, because the thread already called the TPD
+ * destructors before exiting _pt_root.
+ */
+ _pt_thread_death_internal(thred, PR_FALSE);
+ }
+ else
+ {
+ PRErrorCode prerror;
+ switch (rv)
+ {
+ case EINVAL: /* not a joinable thread */
+ case ESRCH: /* no thread with given ID */
+ prerror = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case EDEADLK: /* a thread joining with itself */
+ prerror = PR_DEADLOCK_ERROR;
+ break;
+ default:
+ prerror = PR_UNKNOWN_ERROR;
+ break;
+ }
+ PR_SetError(prerror, rv);
+ }
+ }
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+} /* PR_JoinThread */
+
+PR_IMPLEMENT(void) PR_DetachThread(void)
+{
+ void *thred;
+ int rv;
+
+ _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
+ if (NULL == thred) {
+ return;
+ }
+ _pt_thread_death(thred);
+ rv = pthread_setspecific(pt_book.key, NULL);
+ PR_ASSERT(0 == rv);
+} /* PR_DetachThread */
+
+PR_IMPLEMENT(PRThread*) PR_GetCurrentThread(void)
+{
+ void *thred;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
+ if (NULL == thred) {
+ thred = pt_AttachThread();
+ }
+ PR_ASSERT(NULL != thred);
+ return (PRThread*)thred;
+} /* PR_GetCurrentThread */
+
+PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thred)
+{
+ return (thred->state & PT_THREAD_BOUND) ?
+ PR_GLOBAL_BOUND_THREAD : PR_GLOBAL_THREAD;
+} /* PR_GetThreadScope() */
+
+PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thred)
+{
+ return (thred->state & PT_THREAD_SYSTEM) ?
+ PR_SYSTEM_THREAD : PR_USER_THREAD;
+}
+
+PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thred)
+{
+ return (thred->state & PT_THREAD_DETACHED) ?
+ PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD;
+} /* PR_GetThreadState */
+
+PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thred)
+{
+ PR_ASSERT(thred != NULL);
+ return thred->priority;
+} /* PR_GetThreadPriority */
+
+PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred, PRThreadPriority newPri)
+{
+ PRIntn rv;
+
+ PR_ASSERT(NULL != thred);
+
+ if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)newPri) {
+ newPri = PR_PRIORITY_FIRST;
+ }
+ else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)newPri) {
+ newPri = PR_PRIORITY_LAST;
+ }
+
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+ if (EPERM != pt_schedpriv)
+ {
+ int policy;
+ struct sched_param schedule;
+
+ rv = pthread_getschedparam(thred->id, &policy, &schedule);
+ if(0 == rv) {
+ schedule.sched_priority = pt_PriorityMap(newPri);
+ rv = pthread_setschedparam(thred->id, policy, &schedule);
+ if (EPERM == rv)
+ {
+ pt_schedpriv = EPERM;
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("PR_SetThreadPriority: no thread scheduling privilege"));
+ }
+ }
+ if (rv != 0) {
+ rv = -1;
+ }
+ }
+#elif defined(_PR_NICE_PRIORITY_SCHEDULING)
+ PR_Lock(pt_book.ml);
+ while (thred->tid == 0) {
+ PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(pt_book.ml);
+
+ errno = 0;
+ rv = getpriority(PRIO_PROCESS, 0);
+
+ /* Do not proceed unless we know the main thread's nice value. */
+ if (errno == 0) {
+ rv = setpriority(PRIO_PROCESS, thred->tid,
+ pt_RelativePriority(rv, newPri));
+
+ if (rv == -1)
+ {
+ /* We don't set pt_schedpriv to EPERM in case errno == EPERM
+ * because adjusting the nice value might be permitted for certain
+ * ranges but not for others. */
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("PR_SetThreadPriority: setpriority failed with error %d",
+ errno));
+ }
+ }
+#else
+ (void)rv; /* rv is unused */
+#endif
+
+ thred->priority = newPri;
+} /* PR_SetThreadPriority */
+
+PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thred)
+{
+ /*
+ ** If the target thread indicates that it's waiting,
+ ** find the condition and broadcast to it. Broadcast
+ ** since we don't know which thread (if there are more
+ ** than one). This sounds risky, but clients must
+ ** test their invariants when resumed from a wait and
+ ** I don't expect very many threads to be waiting on
+ ** a single condition and I don't expect interrupt to
+ ** be used very often.
+ **
+ ** I don't know why I thought this would work. Must have
+ ** been one of those weaker momements after I'd been
+ ** smelling the vapors.
+ **
+ ** Even with the followng changes it is possible that
+ ** the pointer to the condition variable is pointing
+ ** at a bogus value. Will the unerlying code detect
+ ** that?
+ */
+ PRCondVar *cv;
+ PR_ASSERT(NULL != thred);
+ if (NULL == thred) {
+ return PR_FAILURE;
+ }
+
+ thred->state |= PT_THREAD_ABORTED;
+
+ cv = thred->waiting;
+ if ((NULL != cv) && !thred->interrupt_blocked)
+ {
+ PRIntn rv;
+ (void)PR_ATOMIC_INCREMENT(&cv->notify_pending);
+ rv = pthread_cond_broadcast(&cv->cv);
+ PR_ASSERT(0 == rv);
+ if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) {
+ PR_DestroyCondVar(cv);
+ }
+ }
+ return PR_SUCCESS;
+} /* PR_Interrupt */
+
+PR_IMPLEMENT(void) PR_ClearInterrupt(void)
+{
+ PRThread *me = PR_GetCurrentThread();
+ me->state &= ~PT_THREAD_ABORTED;
+} /* PR_ClearInterrupt */
+
+PR_IMPLEMENT(void) PR_BlockInterrupt(void)
+{
+ PRThread *me = PR_GetCurrentThread();
+ _PT_THREAD_BLOCK_INTERRUPT(me);
+} /* PR_BlockInterrupt */
+
+PR_IMPLEMENT(void) PR_UnblockInterrupt(void)
+{
+ PRThread *me = PR_GetCurrentThread();
+ _PT_THREAD_UNBLOCK_INTERRUPT(me);
+} /* PR_UnblockInterrupt */
+
+PR_IMPLEMENT(PRStatus) PR_Yield(void)
+{
+ static PRBool warning = PR_TRUE;
+ if (warning) warning = _PR_Obsolete(
+ "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)");
+ return PR_Sleep(PR_INTERVAL_NO_WAIT);
+}
+
+PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime ticks)
+{
+ PRStatus rv = PR_SUCCESS;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (PR_INTERVAL_NO_WAIT == ticks)
+ {
+ _PT_PTHREAD_YIELD();
+ }
+ else
+ {
+ PRCondVar *cv;
+ PRIntervalTime timein;
+
+ timein = PR_IntervalNow();
+ cv = PR_NewCondVar(_pr_sleeplock);
+ PR_ASSERT(cv != NULL);
+ PR_Lock(_pr_sleeplock);
+ do
+ {
+ PRIntervalTime now = PR_IntervalNow();
+ PRIntervalTime delta = now - timein;
+ if (delta > ticks) {
+ break;
+ }
+ rv = PR_WaitCondVar(cv, ticks - delta);
+ } while (PR_SUCCESS == rv);
+ PR_Unlock(_pr_sleeplock);
+ PR_DestroyCondVar(cv);
+ }
+ return rv;
+} /* PR_Sleep */
+
+static void _pt_thread_death(void *arg)
+{
+ void *thred;
+ int rv;
+
+ _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
+ if (NULL == thred)
+ {
+ /*
+ * Have PR_GetCurrentThread return the expected value to the
+ * destructors.
+ */
+ rv = pthread_setspecific(pt_book.key, arg);
+ PR_ASSERT(0 == rv);
+ }
+
+ /* PR_TRUE for: call destructors */
+ _pt_thread_death_internal(arg, PR_TRUE);
+
+ if (NULL == thred)
+ {
+ rv = pthread_setspecific(pt_book.key, NULL);
+ PR_ASSERT(0 == rv);
+ }
+}
+
+static void _pt_thread_death_internal(void *arg, PRBool callDestructors)
+{
+ PRThread *thred = (PRThread*)arg;
+
+ if (thred->state & (PT_THREAD_FOREIGN|PT_THREAD_PRIMORD))
+ {
+ PR_Lock(pt_book.ml);
+ if (NULL == thred->prev) {
+ pt_book.first = thred->next;
+ }
+ else {
+ thred->prev->next = thred->next;
+ }
+ if (NULL == thred->next) {
+ pt_book.last = thred->prev;
+ }
+ else {
+ thred->next->prev = thred->prev;
+ }
+ PR_Unlock(pt_book.ml);
+ }
+ if (callDestructors) {
+ _PR_DestroyThreadPrivate(thred);
+ }
+ PR_Free(thred->privateData);
+ if (NULL != thred->errorString) {
+ PR_Free(thred->errorString);
+ }
+ if (NULL != thred->name) {
+ PR_Free(thred->name);
+ }
+ PR_Free(thred->stack);
+ if (NULL != thred->syspoll_list) {
+ PR_Free(thred->syspoll_list);
+ }
+#if defined(_PR_POLL_WITH_SELECT)
+ if (NULL != thred->selectfd_list) {
+ PR_Free(thred->selectfd_list);
+ }
+#endif
+#if defined(DEBUG)
+ memset(thred, 0xaf, sizeof(PRThread));
+#endif /* defined(DEBUG) */
+ PR_Free(thred);
+} /* _pt_thread_death */
+
+void _PR_InitThreads(
+ PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs)
+{
+ int rv;
+ PRThread *thred;
+
+ PR_ASSERT(priority == PR_PRIORITY_NORMAL);
+
+#ifdef _PR_NEED_PTHREAD_INIT
+ /*
+ * On BSD/OS (3.1 and 4.0), the pthread subsystem is lazily
+ * initialized, but pthread_self() fails to initialize
+ * pthreads and hence returns a null thread ID if invoked
+ * by the primordial thread before any other pthread call.
+ * So we explicitly initialize pthreads here.
+ */
+ pthread_init();
+#endif
+
+#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
+#if defined(FREEBSD)
+ {
+ pthread_attr_t attr;
+ int policy;
+ /* get the min and max priorities of the default policy */
+ pthread_attr_init(&attr);
+ pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
+ pthread_attr_getschedpolicy(&attr, &policy);
+ pt_book.minPrio = sched_get_priority_min(policy);
+ PR_ASSERT(-1 != pt_book.minPrio);
+ pt_book.maxPrio = sched_get_priority_max(policy);
+ PR_ASSERT(-1 != pt_book.maxPrio);
+ pthread_attr_destroy(&attr);
+ }
+#else
+ /*
+ ** These might be function evaluations
+ */
+ pt_book.minPrio = PT_PRIO_MIN;
+ pt_book.maxPrio = PT_PRIO_MAX;
+#endif
+#endif
+
+ PR_ASSERT(NULL == pt_book.ml);
+ pt_book.ml = PR_NewLock();
+ PR_ASSERT(NULL != pt_book.ml);
+ pt_book.cv = PR_NewCondVar(pt_book.ml);
+ PR_ASSERT(NULL != pt_book.cv);
+ thred = PR_NEWZAP(PRThread);
+ PR_ASSERT(NULL != thred);
+ thred->arg = NULL;
+ thred->startFunc = NULL;
+ thred->priority = priority;
+ thred->id = pthread_self();
+ thred->idSet = PR_TRUE;
+#ifdef _PR_NICE_PRIORITY_SCHEDULING
+ thred->tid = gettid();
+#endif
+
+ thred->state = (PT_THREAD_DETACHED | PT_THREAD_PRIMORD);
+ if (PR_SYSTEM_THREAD == type)
+ {
+ thred->state |= PT_THREAD_SYSTEM;
+ pt_book.system += 1;
+ pt_book.this_many = 0;
+ }
+ else
+ {
+ pt_book.user += 1;
+ pt_book.this_many = 1;
+ }
+ thred->next = thred->prev = NULL;
+ pt_book.first = pt_book.last = thred;
+
+ thred->stack = PR_NEWZAP(PRThreadStack);
+ PR_ASSERT(thred->stack != NULL);
+ thred->stack->stackSize = 0;
+ thred->stack->thr = thred;
+ _PR_InitializeStack(thred->stack);
+
+ /*
+ * Create a key for our use to store a backpointer in the pthread
+ * to our PRThread object. This object gets deleted when the thread
+ * returns from its root in the case of a detached thread. Other
+ * threads delete the objects in Join.
+ *
+ * NB: The destructor logic seems to have a bug so it isn't used.
+ * NBB: Oh really? I'm going to give it a spin - AOF 19 June 1998.
+ * More info - the problem is that pthreads calls the destructor
+ * eagerly as the thread returns from its root, rather than lazily
+ * after the thread is joined. Therefore, threads that are joining
+ * and holding PRThread references are actually holding pointers to
+ * nothing.
+ */
+ rv = _PT_PTHREAD_KEY_CREATE(&pt_book.key, _pt_thread_death);
+ if (0 != rv) {
+ PR_Assert("0 == rv", __FILE__, __LINE__);
+ }
+ pt_book.keyCreated = PR_TRUE;
+ rv = pthread_setspecific(pt_book.key, thred);
+ PR_ASSERT(0 == rv);
+} /* _PR_InitThreads */
+
+#ifdef __GNUC__
+/*
+ * GCC supports the constructor and destructor attributes as of
+ * version 2.5.
+ */
+#if defined(DARWIN)
+/*
+ * The dynamic linker on OSX doesn't execute __attribute__((destructor))
+ * destructors in the right order wrt non-__attribute((destructor)) destructors
+ * in other libraries. So use atexit() instead, which does.
+ * See https://bugzilla.mozilla.org/show_bug.cgi?id=1399746#c99
+ */
+static void _PR_Fini(void);
+
+__attribute__ ((constructor))
+static void _register_PR_Fini() {
+ atexit(_PR_Fini);
+}
+#else
+static void _PR_Fini(void) __attribute__ ((destructor));
+#endif
+
+#elif defined(__SUNPRO_C)
+/*
+ * Sun Studio compiler
+ */
+#pragma fini(_PR_Fini)
+static void _PR_Fini(void);
+#elif defined(HPUX)
+/*
+ * Current versions of HP C compiler define __HP_cc.
+ * HP C compiler A.11.01.20 doesn't define __HP_cc.
+ */
+#if defined(__ia64) || defined(_LP64)
+#pragma FINI "_PR_Fini"
+static void _PR_Fini(void);
+#else
+/*
+ * Only HP-UX 10.x style initializers are supported in 32-bit links.
+ * Need to use the +I PR_HPUX10xInit linker option.
+ */
+#include <dl.h>
+
+static void _PR_Fini(void);
+
+void PR_HPUX10xInit(shl_t handle, int loading)
+{
+ /*
+ * This function is called when a shared library is loaded as well
+ * as when the shared library is unloaded. Note that it may not
+ * be called when the user's program terminates.
+ *
+ * handle is the shl_load API handle for the shared library being
+ * initialized.
+ *
+ * loading is non-zero at startup and zero at termination.
+ */
+ if (loading) {
+ /* ... do some initializations ... */
+ } else {
+ _PR_Fini();
+ }
+}
+#endif
+#elif defined(AIX)
+/* Need to use the -binitfini::_PR_Fini linker option. */
+#endif
+
+void _PR_Fini(void)
+{
+ void *thred;
+ int rv;
+
+ if (!_pr_initialized) {
+ /* Either NSPR was never successfully initialized or
+ * PR_Cleanup has been called already. */
+ if (pt_book.keyCreated)
+ {
+ rv = pthread_key_delete(pt_book.key);
+ PR_ASSERT(0 == rv);
+ pt_book.keyCreated = PR_FALSE;
+ }
+ return;
+ }
+
+ _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
+ if (NULL != thred)
+ {
+ /*
+ * PR_FALSE, because it is unsafe to call back to the
+ * thread private data destructors at final cleanup.
+ */
+ _pt_thread_death_internal(thred, PR_FALSE);
+ rv = pthread_setspecific(pt_book.key, NULL);
+ PR_ASSERT(0 == rv);
+ }
+ rv = pthread_key_delete(pt_book.key);
+ PR_ASSERT(0 == rv);
+ pt_book.keyCreated = PR_FALSE;
+ /* TODO: free other resources used by NSPR */
+ /* _pr_initialized = PR_FALSE; */
+} /* _PR_Fini */
+
+PR_IMPLEMENT(PRStatus) PR_Cleanup(void)
+{
+ PRThread *me = PR_GetCurrentThread();
+ int rv;
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR"));
+ PR_ASSERT(me->state & PT_THREAD_PRIMORD);
+ if (me->state & PT_THREAD_PRIMORD)
+ {
+ PR_Lock(pt_book.ml);
+ while (pt_book.user > pt_book.this_many) {
+ PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ if (me->state & PT_THREAD_SYSTEM) {
+ pt_book.system -= 1;
+ }
+ else {
+ pt_book.user -= 1;
+ }
+ PR_Unlock(pt_book.ml);
+
+ _PR_MD_EARLY_CLEANUP();
+
+ _PR_CleanupMW();
+ _PR_CleanupTime();
+ _PR_CleanupDtoa();
+ _PR_CleanupCallOnce();
+ _PR_ShutdownLinker();
+ _PR_LogCleanup();
+ _PR_CleanupNet();
+ /* Close all the fd's before calling _PR_CleanupIO */
+ _PR_CleanupIO();
+ _PR_CleanupCMon();
+
+ _pt_thread_death(me);
+ rv = pthread_setspecific(pt_book.key, NULL);
+ PR_ASSERT(0 == rv);
+ /*
+ * I am not sure if it's safe to delete the cv and lock here,
+ * since there may still be "system" threads around. If this
+ * call isn't immediately prior to exiting, then there's a
+ * problem.
+ */
+ if (0 == pt_book.system)
+ {
+ PR_DestroyCondVar(pt_book.cv); pt_book.cv = NULL;
+ PR_DestroyLock(pt_book.ml); pt_book.ml = NULL;
+ }
+ PR_DestroyLock(_pr_sleeplock);
+ _pr_sleeplock = NULL;
+ _PR_CleanupLayerCache();
+ _PR_CleanupEnv();
+#ifdef _PR_ZONE_ALLOCATOR
+ _PR_DestroyZones();
+#endif
+ _pr_initialized = PR_FALSE;
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+} /* PR_Cleanup */
+
+PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status)
+{
+ _exit(status);
+}
+
+PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thred)
+{
+ return (PRUint32)thred->id; /* and I don't know what they will do with it */
+}
+
+/*
+ * $$$
+ * The following two thread-to-processor affinity functions are not
+ * yet implemented for pthreads. By the way, these functions should return
+ * PRStatus rather than PRInt32 to indicate the success/failure status.
+ * $$$
+ */
+
+PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask)
+{
+ return 0; /* not implemented */
+}
+
+PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask )
+{
+ return 0; /* not implemented */
+}
+
+PR_IMPLEMENT(void)
+PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg)
+{
+ thread->dump = dump;
+ thread->dumpArg = arg;
+}
+
+/*
+ * Garbage collection support follows.
+ */
+
+/* a bogus signal mask for forcing a timed wait */
+/* Not so bogus in AIX as we really do a sigwait */
+static sigset_t sigwait_set;
+
+static struct timespec onemillisec = {0, 1000000L};
+#ifndef PT_NO_SIGTIMEDWAIT
+static struct timespec hundredmillisec = {0, 100000000L};
+#endif
+
+static void suspend_signal_handler(PRIntn sig);
+
+#ifdef PT_NO_SIGTIMEDWAIT
+static void null_signal_handler(PRIntn sig);
+#endif
+
+/*
+ * Linux pthreads use SIGUSR1 and SIGUSR2 internally, which
+ * conflict with the use of these two signals in our GC support.
+ * So we don't know how to support GC on Linux pthreads.
+ */
+static void init_pthread_gc_support(void)
+{
+ PRIntn rv;
+
+ {
+ struct sigaction sigact_usr2;
+
+ sigact_usr2.sa_handler = suspend_signal_handler;
+ sigact_usr2.sa_flags = SA_RESTART;
+ sigemptyset (&sigact_usr2.sa_mask);
+
+ rv = sigaction (SIGUSR2, &sigact_usr2, NULL);
+ PR_ASSERT(0 == rv);
+
+ sigemptyset (&sigwait_set);
+#if defined(PT_NO_SIGTIMEDWAIT)
+ sigaddset (&sigwait_set, SIGUSR1);
+#else
+ sigaddset (&sigwait_set, SIGUSR2);
+#endif /* defined(PT_NO_SIGTIMEDWAIT) */
+ }
+#if defined(PT_NO_SIGTIMEDWAIT)
+ {
+ struct sigaction sigact_null;
+ sigact_null.sa_handler = null_signal_handler;
+ sigact_null.sa_flags = SA_RESTART;
+ sigemptyset (&sigact_null.sa_mask);
+ rv = sigaction (SIGUSR1, &sigact_null, NULL);
+ PR_ASSERT(0 ==rv);
+ }
+#endif /* defined(PT_NO_SIGTIMEDWAIT) */
+}
+
+PR_IMPLEMENT(void) PR_SetThreadGCAble(void)
+{
+ PR_Lock(pt_book.ml);
+ PR_GetCurrentThread()->state |= PT_THREAD_GCABLE;
+ PR_Unlock(pt_book.ml);
+}
+
+PR_IMPLEMENT(void) PR_ClearThreadGCAble(void)
+{
+ PR_Lock(pt_book.ml);
+ PR_GetCurrentThread()->state &= (~PT_THREAD_GCABLE);
+ PR_Unlock(pt_book.ml);
+}
+
+#if defined(DEBUG)
+static PRBool suspendAllOn = PR_FALSE;
+#endif
+
+static PRBool suspendAllSuspended = PR_FALSE;
+
+PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg)
+{
+ PRIntn count = 0;
+ PRStatus rv = PR_SUCCESS;
+ PRThread* thred = pt_book.first;
+
+#if defined(DEBUG) || defined(FORCE_PR_ASSERT)
+ PRThread *me = PR_GetCurrentThread();
+#endif
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_EnumerateThreads\n"));
+ /*
+ * $$$
+ * Need to suspend all threads other than me before doing this.
+ * This is really a gross and disgusting thing to do. The only
+ * good thing is that since all other threads are suspended, holding
+ * the lock during a callback seems like child's play.
+ * $$$
+ */
+ PR_ASSERT(suspendAllOn);
+
+ while (thred != NULL)
+ {
+ /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking
+ * qp->next after applying the function "func". In particular, "func"
+ * might remove the thread from the queue and put it into another one in
+ * which case qp->next no longer points to the next entry in the original
+ * queue.
+ *
+ * To get around this problem, we save qp->next in qp_next before applying
+ * "func" and use that saved value as the next value after applying "func".
+ */
+ PRThread* next = thred->next;
+
+ if (_PT_IS_GCABLE_THREAD(thred))
+ {
+ PR_ASSERT((thred == me) || (thred->suspend & PT_THREAD_SUSPENDED));
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("In PR_EnumerateThreads callback thread %p thid = %X\n",
+ thred, thred->id));
+
+ rv = func(thred, count++, arg);
+ if (rv != PR_SUCCESS) {
+ return rv;
+ }
+ }
+ thred = next;
+ }
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("End PR_EnumerateThreads count = %d \n", count));
+ return rv;
+} /* PR_EnumerateThreads */
+
+/*
+ * PR_SuspendAll and PR_ResumeAll are called during garbage collection. The strategy
+ * we use is to send a SIGUSR2 signal to every gc able thread that we intend to suspend.
+ * The signal handler will record the stack pointer and will block until resumed by
+ * the resume call. Since the signal handler is the last routine called for the
+ * suspended thread, the stack pointer will also serve as a place where all the
+ * registers have been saved on the stack for the previously executing routines.
+ *
+ * Through global variables, we also make sure that PR_Suspend and PR_Resume does not
+ * proceed until the thread is suspended or resumed.
+ */
+
+/*
+ * In the signal handler, we can not use condition variable notify or wait.
+ * This does not work consistently across all pthread platforms. We also can not
+ * use locking since that does not seem to work reliably across platforms.
+ * Only thing we can do is yielding while testing for a global condition
+ * to change. This does work on pthread supported platforms. We may have
+ * to play with priortities if there are any problems detected.
+ */
+
+/*
+ * In AIX, you cannot use ANY pthread calls in the signal handler except perhaps
+ * pthread_yield. But that is horribly inefficient. Hence we use only sigwait, no
+ * sigtimedwait is available. We need to use another user signal, SIGUSR1. Actually
+ * SIGUSR1 is also used by exec in Java. So our usage here breaks the exec in Java,
+ * for AIX. You cannot use pthread_cond_wait or pthread_delay_np in the signal
+ * handler as all synchronization mechanisms just break down.
+ */
+
+#if defined(PT_NO_SIGTIMEDWAIT)
+static void null_signal_handler(PRIntn sig)
+{
+ return;
+}
+#endif
+
+static void suspend_signal_handler(PRIntn sig)
+{
+ PRThread *me = PR_GetCurrentThread();
+
+ PR_ASSERT(me != NULL);
+ PR_ASSERT(_PT_IS_GCABLE_THREAD(me));
+ PR_ASSERT((me->suspend & PT_THREAD_SUSPENDED) == 0);
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("Begin suspend_signal_handler thred %p thread id = %X\n",
+ me, me->id));
+
+ /*
+ * save stack pointer
+ */
+ me->sp = &me;
+
+ /*
+ At this point, the thread's stack pointer has been saved,
+ And it is going to enter a wait loop until it is resumed.
+ So it is _really_ suspended
+ */
+
+ me->suspend |= PT_THREAD_SUSPENDED;
+
+ /*
+ * now, block current thread
+ */
+#if defined(PT_NO_SIGTIMEDWAIT)
+ pthread_cond_signal(&me->suspendResumeCV);
+ while (me->suspend & PT_THREAD_SUSPENDED)
+ {
+#if !defined(FREEBSD) && !defined(NETBSD) && !defined(OPENBSD) \
+ && !defined(BSDI) && !defined(UNIXWARE) \
+ && !defined(DARWIN) && !defined(RISCOS)
+ PRIntn rv;
+ sigwait(&sigwait_set, &rv);
+#endif
+ }
+ me->suspend |= PT_THREAD_RESUMED;
+ pthread_cond_signal(&me->suspendResumeCV);
+#else /* defined(PT_NO_SIGTIMEDWAIT) */
+ while (me->suspend & PT_THREAD_SUSPENDED)
+ {
+ PRIntn rv = sigtimedwait(&sigwait_set, NULL, &hundredmillisec);
+ PR_ASSERT(-1 == rv);
+ }
+ me->suspend |= PT_THREAD_RESUMED;
+#endif
+
+ /*
+ * At this point, thread has been resumed, so set a global condition.
+ * The ResumeAll needs to know that this has really been resumed.
+ * So the signal handler sets a flag which PR_ResumeAll will reset.
+ * The PR_ResumeAll must reset this flag ...
+ */
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("End suspend_signal_handler thred = %p tid = %X\n", me, me->id));
+} /* suspend_signal_handler */
+
+static void pt_SuspendSet(PRThread *thred)
+{
+ PRIntn rv;
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("pt_SuspendSet thred %p thread id = %X\n", thred, thred->id));
+
+
+ /*
+ * Check the thread state and signal the thread to suspend
+ */
+
+ PR_ASSERT((thred->suspend & PT_THREAD_SUSPENDED) == 0);
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("doing pthread_kill in pt_SuspendSet thred %p tid = %X\n",
+ thred, thred->id));
+ rv = pthread_kill (thred->id, SIGUSR2);
+ PR_ASSERT(0 == rv);
+}
+
+static void pt_SuspendTest(PRThread *thred)
+{
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("Begin pt_SuspendTest thred %p thread id = %X\n", thred, thred->id));
+
+
+ /*
+ * Wait for the thread to be really suspended. This happens when the
+ * suspend signal handler stores the stack pointer and sets the state
+ * to suspended.
+ */
+
+#if defined(PT_NO_SIGTIMEDWAIT)
+ pthread_mutex_lock(&thred->suspendResumeMutex);
+ while ((thred->suspend & PT_THREAD_SUSPENDED) == 0)
+ {
+ pthread_cond_timedwait(
+ &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec);
+ }
+ pthread_mutex_unlock(&thred->suspendResumeMutex);
+#else
+ while ((thred->suspend & PT_THREAD_SUSPENDED) == 0)
+ {
+ PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
+ PR_ASSERT(-1 == rv);
+ }
+#endif
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("End pt_SuspendTest thred %p tid %X\n", thred, thred->id));
+} /* pt_SuspendTest */
+
+static void pt_ResumeSet(PRThread *thred)
+{
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("pt_ResumeSet thred %p thread id = %X\n", thred, thred->id));
+
+ /*
+ * Clear the global state and set the thread state so that it will
+ * continue past yield loop in the suspend signal handler
+ */
+
+ PR_ASSERT(thred->suspend & PT_THREAD_SUSPENDED);
+
+
+ thred->suspend &= ~PT_THREAD_SUSPENDED;
+
+#if defined(PT_NO_SIGTIMEDWAIT)
+ pthread_kill(thred->id, SIGUSR1);
+#endif
+
+} /* pt_ResumeSet */
+
+static void pt_ResumeTest(PRThread *thred)
+{
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("Begin pt_ResumeTest thred %p thread id = %X\n", thred, thred->id));
+
+ /*
+ * Wait for the threads resume state to change
+ * to indicate it is really resumed
+ */
+#if defined(PT_NO_SIGTIMEDWAIT)
+ pthread_mutex_lock(&thred->suspendResumeMutex);
+ while ((thred->suspend & PT_THREAD_RESUMED) == 0)
+ {
+ pthread_cond_timedwait(
+ &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec);
+ }
+ pthread_mutex_unlock(&thred->suspendResumeMutex);
+#else
+ while ((thred->suspend & PT_THREAD_RESUMED) == 0) {
+ PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
+ PR_ASSERT(-1 == rv);
+ }
+#endif
+
+ thred->suspend &= ~PT_THREAD_RESUMED;
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, (
+ "End pt_ResumeTest thred %p tid %X\n", thred, thred->id));
+} /* pt_ResumeTest */
+
+static pthread_once_t pt_gc_support_control = PTHREAD_ONCE_INIT;
+
+PR_IMPLEMENT(void) PR_SuspendAll(void)
+{
+#ifdef DEBUG
+ PRIntervalTime stime, etime;
+#endif
+ PRThread* thred = pt_book.first;
+ PRThread *me = PR_GetCurrentThread();
+ int rv;
+
+ rv = pthread_once(&pt_gc_support_control, init_pthread_gc_support);
+ PR_ASSERT(0 == rv);
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n"));
+ /*
+ * Stop all threads which are marked GC able.
+ */
+ PR_Lock(pt_book.ml);
+#ifdef DEBUG
+ suspendAllOn = PR_TRUE;
+ stime = PR_IntervalNow();
+#endif
+ while (thred != NULL)
+ {
+ if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) {
+ pt_SuspendSet(thred);
+ }
+ thred = thred->next;
+ }
+
+ /* Wait till they are really suspended */
+ thred = pt_book.first;
+ while (thred != NULL)
+ {
+ if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) {
+ pt_SuspendTest(thred);
+ }
+ thred = thred->next;
+ }
+
+ suspendAllSuspended = PR_TRUE;
+
+#ifdef DEBUG
+ etime = PR_IntervalNow();
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,\
+ ("End PR_SuspendAll (time %dms)\n",
+ PR_IntervalToMilliseconds(etime - stime)));
+#endif
+} /* PR_SuspendAll */
+
+PR_IMPLEMENT(void) PR_ResumeAll(void)
+{
+#ifdef DEBUG
+ PRIntervalTime stime, etime;
+#endif
+ PRThread* thred = pt_book.first;
+ PRThread *me = PR_GetCurrentThread();
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n"));
+ /*
+ * Resume all previously suspended GC able threads.
+ */
+ suspendAllSuspended = PR_FALSE;
+#ifdef DEBUG
+ stime = PR_IntervalNow();
+#endif
+
+ while (thred != NULL)
+ {
+ if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) {
+ pt_ResumeSet(thred);
+ }
+ thred = thred->next;
+ }
+
+ thred = pt_book.first;
+ while (thred != NULL)
+ {
+ if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) {
+ pt_ResumeTest(thred);
+ }
+ thred = thred->next;
+ }
+
+ PR_Unlock(pt_book.ml);
+#ifdef DEBUG
+ suspendAllOn = PR_FALSE;
+ etime = PR_IntervalNow();
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("End PR_ResumeAll (time %dms)\n",
+ PR_IntervalToMilliseconds(etime - stime)));
+#endif
+} /* PR_ResumeAll */
+
+/* Return the stack pointer for the given thread- used by the GC */
+PR_IMPLEMENT(void *)PR_GetSP(PRThread *thred)
+{
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("in PR_GetSP thred %p thid = %X, sp = %p\n",
+ thred, thred->id, thred->sp));
+ return thred->sp;
+} /* PR_GetSP */
+
+PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char *name)
+{
+ PRThread *thread;
+ size_t nameLen;
+ int result = 0;
+
+ if (!name) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ thread = PR_GetCurrentThread();
+ if (!thread) {
+ return PR_FAILURE;
+ }
+
+ PR_Free(thread->name);
+ nameLen = strlen(name);
+ thread->name = (char *)PR_Malloc(nameLen + 1);
+ if (!thread->name) {
+ return PR_FAILURE;
+ }
+ memcpy(thread->name, name, nameLen + 1);
+
+#if defined(OPENBSD) || defined(FREEBSD) || defined(DRAGONFLY)
+ pthread_set_name_np(thread->id, name);
+#elif defined(ANDROID)
+ prctl(PR_SET_NAME, (unsigned long)(name));
+#elif defined(NETBSD)
+ result = pthread_setname_np(thread->id, "%s", (void *)name);
+#else /* not BSD */
+ /*
+ * On OSX, pthread_setname_np is only available in 10.6 or later, so test
+ * for it at runtime. It also may not be available on all linux distros.
+ */
+#if defined(DARWIN)
+ int (*dynamic_pthread_setname_np)(const char*);
+#else
+ int (*dynamic_pthread_setname_np)(pthread_t, const char*);
+#endif
+
+ *(void**)(&dynamic_pthread_setname_np) =
+ dlsym(RTLD_DEFAULT, "pthread_setname_np");
+ if (!dynamic_pthread_setname_np) {
+ return PR_SUCCESS;
+ }
+
+#if defined(DARWIN)
+ /* Mac OS X has a length limit of 63 characters, but there is no API
+ * exposing it.
+ */
+#define SETNAME_LENGTH_CONSTRAINT 63
+#else
+ /*
+ * The 15-character name length limit is an experimentally determined
+ * length of a null-terminated string that most linux distros accept
+ * as an argument to pthread_setname_np. Otherwise the E2BIG
+ * error is returned by the function.
+ */
+#define SETNAME_LENGTH_CONSTRAINT 15
+#endif
+#define SETNAME_FRAGMENT1_LENGTH (SETNAME_LENGTH_CONSTRAINT >> 1)
+#define SETNAME_FRAGMENT2_LENGTH \
+ (SETNAME_LENGTH_CONSTRAINT - SETNAME_FRAGMENT1_LENGTH - 1)
+ char name_dup[SETNAME_LENGTH_CONSTRAINT + 1];
+ if (nameLen > SETNAME_LENGTH_CONSTRAINT) {
+ memcpy(name_dup, name, SETNAME_FRAGMENT1_LENGTH);
+ name_dup[SETNAME_FRAGMENT1_LENGTH] = '~';
+ /* Note that this also copies the null terminator. */
+ memcpy(name_dup + SETNAME_FRAGMENT1_LENGTH + 1,
+ name + nameLen - SETNAME_FRAGMENT2_LENGTH,
+ SETNAME_FRAGMENT2_LENGTH + 1);
+ name = name_dup;
+ }
+
+#if defined(DARWIN)
+ result = dynamic_pthread_setname_np(name);
+#else
+ result = dynamic_pthread_setname_np(thread->id, name);
+#endif
+#endif /* not BSD */
+
+ if (result) {
+ PR_SetError(PR_UNKNOWN_ERROR, result);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(const char *) PR_GetThreadName(const PRThread *thread)
+{
+ if (!thread) {
+ return NULL;
+ }
+ return thread->name;
+}
+
+#endif /* defined(_PR_PTHREADS) */
+
+/* ptthread.c */
diff --git a/nsprpub/pr/src/threads/Makefile.in b/nsprpub/pr/src/threads/Makefile.in
new file mode 100644
index 0000000000..5c5c254e3a
--- /dev/null
+++ b/nsprpub/pr/src/threads/Makefile.in
@@ -0,0 +1,62 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+ifdef USE_PTHREADS
+ DIRS =
+else
+ifdef USE_BTHREADS
+ DIRS =
+else
+ DIRS = combined
+endif
+endif
+
+ifdef USE_PTHREADS
+CSRCS = \
+ prcmon.c \
+ prrwlock.c \
+ prtpd.c \
+ $(NULL)
+else
+ifdef USE_BTHREADS
+CSRCS = \
+ prcmon.c \
+ prrwlock.c \
+ prtpd.c \
+ $(NULL)
+else
+CSRCS = \
+ prcmon.c \
+ prdump.c \
+ prmon.c \
+ prsem.c \
+ prrwlock.c \
+ prcthr.c \
+ prtpd.c \
+ $(NULL)
+endif
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
diff --git a/nsprpub/pr/src/threads/combined/Makefile.in b/nsprpub/pr/src/threads/combined/Makefile.in
new file mode 100644
index 0000000000..14b9eac007
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/Makefile.in
@@ -0,0 +1,40 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+#! gmake
+
+MOD_DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+ifdef USE_PTHREADS
+CSRCS = \
+ $(NULL)
+else
+CSRCS = \
+ prucpu.c \
+ prucv.c \
+ prulock.c \
+ pruthr.c \
+ prustack.c \
+ $(NULL)
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(TARGETS)
+
diff --git a/nsprpub/pr/src/threads/combined/README b/nsprpub/pr/src/threads/combined/README
new file mode 100644
index 0000000000..aa26665282
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/README
@@ -0,0 +1,62 @@
+NSPR 2.0 evolution
+------------------
+
+
+Phase 1- today
+
+Currently (Oct 10, 1996) NSPR 2.0 has two modes. Either _PR_NTHREAD
+is defined, in which case the PR_CreateThread() call always creates a
+native kernel thread, or _PR_NTHREAD is not defined and PR_CreateThread()
+always creates user level threads within the single, original process. This
+source code is reflected in two directories, nspr20/pr/src/threads/native, and
+nspr20/pr/src/threads/user. Although the PR_CreateThread() function has
+a paramter to specify the "scope" of a thread, this parameter is not yet
+used- except on solaris where it uses it to specify bound vs unbound threads.
+
+Phase 2 - next week
+
+The next step is to provide a combination of user and native threads. The
+idea, of course, is to have some small number of native threads and each of
+those threads be able to run user level threads. The number of native
+threads created will most likely be proportional to the number of CPUs in
+the system. For this reason, the specific set of native threads which are
+used to run the user-level threads will be called "CPU" threads.
+
+The user level threads which will be run on the CPU threads are able to
+run on any of the CPU threads available, and over the course of a user-level
+thread's lifetime, it may drift from one CPU thread to another. All
+user-level threads will compete for processing time via a single run queue.
+
+Creation of a CPU thread will be primarily controlled by NSPR itself or by
+the user running a function PR_Concurrency(). The details of PR_Concurrency()
+have not yet been worked out; but the idea is that the user can specify to
+NSPR how many CPU threads are desired.
+
+In this system, user-level threads are created by using PR_CreateThread() and
+specifying the PR_LOCAL_SCOPE option. LOCAL_SCOPE indicates that the thread
+will be under the control of the "local" scheduler. Creating threads with
+GLOBAL_SCOPE, on the other hand will create a thread which is under the
+control of the system's scheduler. In otherwords, this creates a native thread
+which is not a CPU thread; it runs a single thread task and never has more
+than one task to run. LOCAL_SCOPE is much like creating a Solaris unbound
+thread, while GLOBAL_SCOPE is similar to creating a Solaris bound thread.
+
+To implement this architecture, the source code will still maintain the "user"
+and "native" directories which is has today. However a third directory
+"combined" will also exist. To compile a version of NSPR which only creates
+native threads, the user can define _PR_NTHREAD. For exclusive user-level
+threads, do not define _PR_NTHREAD. To get the combined threads, define
+_PR_NTHREAD and _PR_USE_CPUS.
+
+
+Phase 3 - later than next week
+
+The goal is to eliminate the 3 directories. Once these three models are in
+place, the remaining work will be to eliminate the native and user thread
+directories for all platforms, so that the entire thread model is contained
+within what is today called the "combined" model. This new and glorious
+source code will attempt to make the "combined" model on any platforms which
+provide the necessary underlying native threading, but will also be
+capable of using exclusive user-level threads on systems which don't have
+native threads.
+
diff --git a/nsprpub/pr/src/threads/combined/prucpu.c b/nsprpub/pr/src/threads/combined/prucpu.c
new file mode 100644
index 0000000000..25ffcce862
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/prucpu.c
@@ -0,0 +1,424 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+_PRCPU *_pr_primordialCPU = NULL;
+
+PRInt32 _pr_md_idle_cpus; /* number of idle cpus */
+/*
+ * The idle threads in MxN models increment/decrement _pr_md_idle_cpus.
+ * If _PR_HAVE_ATOMIC_OPS is not defined, they can't use the atomic
+ * increment/decrement routines (which are based on PR_Lock/PR_Unlock),
+ * because PR_Lock asserts that the calling thread is not an idle thread.
+ * So we use a _MDLock to protect _pr_md_idle_cpus.
+ */
+#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
+#ifndef _PR_HAVE_ATOMIC_OPS
+static _MDLock _pr_md_idle_cpus_lock;
+#endif
+#endif
+PRUintn _pr_numCPU;
+PRInt32 _pr_cpus_exit;
+PRUint32 _pr_cpu_affinity_mask = 0;
+
+#if !defined (_PR_GLOBAL_THREADS_ONLY)
+
+static PRUintn _pr_cpuID;
+
+static void PR_CALLBACK _PR_CPU_Idle(void *);
+
+static _PRCPU *_PR_CreateCPU(void);
+static PRStatus _PR_StartCPU(_PRCPU *cpu, PRThread *thread);
+
+#if !defined(_PR_LOCAL_THREADS_ONLY)
+static void _PR_RunCPU(void *arg);
+#endif
+
+void _PR_InitCPUs()
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_native_threads_only) {
+ return;
+ }
+
+ _pr_cpuID = 0;
+ _MD_NEW_LOCK( &_pr_cpuLock);
+#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
+#ifndef _PR_HAVE_ATOMIC_OPS
+ _MD_NEW_LOCK(&_pr_md_idle_cpus_lock);
+#endif
+#endif
+
+#ifdef _PR_LOCAL_THREADS_ONLY
+
+#ifdef HAVE_CUSTOM_USER_THREADS
+ _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
+#endif
+
+ /* Now start the first CPU. */
+ _pr_primordialCPU = _PR_CreateCPU();
+ _pr_numCPU = 1;
+ _PR_StartCPU(_pr_primordialCPU, me);
+
+ _PR_MD_SET_CURRENT_CPU(_pr_primordialCPU);
+
+ /* Initialize cpu for current thread (could be different from me) */
+ _PR_MD_CURRENT_THREAD()->cpu = _pr_primordialCPU;
+
+ _PR_MD_SET_LAST_THREAD(me);
+
+#else /* Combined MxN model */
+
+ _pr_primordialCPU = _PR_CreateCPU();
+ _pr_numCPU = 1;
+ _PR_CreateThread(PR_SYSTEM_THREAD,
+ _PR_RunCPU,
+ _pr_primordialCPU,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0,
+ _PR_IDLE_THREAD);
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+ _PR_MD_INIT_CPUS();
+}
+
+#ifdef WINNT
+/*
+ * Right now this function merely stops the CPUs and does
+ * not do any other cleanup.
+ *
+ * It is only implemented for WINNT because bug 161998 only
+ * affects the WINNT version of NSPR, but it would be nice
+ * to implement this function for other platforms too.
+ */
+void _PR_CleanupCPUs(void)
+{
+ PRUintn i;
+ PRCList *qp;
+ _PRCPU *cpu;
+
+ _pr_cpus_exit = 1;
+ for (i = 0; i < _pr_numCPU; i++) {
+ _PR_MD_WAKEUP_WAITER(NULL);
+ }
+ for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) {
+ cpu = _PR_CPU_PTR(qp);
+ _PR_MD_JOIN_THREAD(&cpu->thread->md);
+ }
+}
+#endif
+
+static _PRCPUQueue *_PR_CreateCPUQueue(void)
+{
+ PRInt32 index;
+ _PRCPUQueue *cpuQueue;
+ cpuQueue = PR_NEWZAP(_PRCPUQueue);
+
+ _MD_NEW_LOCK( &cpuQueue->runQLock );
+ _MD_NEW_LOCK( &cpuQueue->sleepQLock );
+ _MD_NEW_LOCK( &cpuQueue->miscQLock );
+
+ for (index = 0; index < PR_ARRAY_SIZE(cpuQueue->runQ); index++) {
+ PR_INIT_CLIST( &(cpuQueue->runQ[index]) );
+ }
+ PR_INIT_CLIST( &(cpuQueue->sleepQ) );
+ PR_INIT_CLIST( &(cpuQueue->pauseQ) );
+ PR_INIT_CLIST( &(cpuQueue->suspendQ) );
+ PR_INIT_CLIST( &(cpuQueue->waitingToJoinQ) );
+
+ cpuQueue->numCPUs = 1;
+
+ return cpuQueue;
+}
+
+/*
+ * Create a new CPU.
+ *
+ * This function initializes enough of the _PRCPU structure so
+ * that it can be accessed safely by a global thread or another
+ * CPU. This function does not create the native thread that
+ * will run the CPU nor does it initialize the parts of _PRCPU
+ * that must be initialized by that native thread.
+ *
+ * The reason we cannot simply have the native thread create
+ * and fully initialize a new CPU is that we need to be able to
+ * create a usable _pr_primordialCPU in _PR_InitCPUs without
+ * assuming that the primordial CPU thread we created can run
+ * during NSPR initialization. For example, on Windows while
+ * new threads can be created by DllMain, they won't be able
+ * to run during DLL initialization. If NSPR is initialized
+ * by DllMain, the primordial CPU thread won't run until DLL
+ * initialization is finished.
+ */
+static _PRCPU *_PR_CreateCPU(void)
+{
+ _PRCPU *cpu;
+
+ cpu = PR_NEWZAP(_PRCPU);
+ if (cpu) {
+ cpu->queue = _PR_CreateCPUQueue();
+ if (!cpu->queue) {
+ PR_DELETE(cpu);
+ return NULL;
+ }
+ }
+ return cpu;
+}
+
+/*
+ * Start a new CPU.
+ *
+ * 'cpu' is a _PRCPU structure created by _PR_CreateCPU().
+ * 'thread' is the native thread that will run the CPU.
+ *
+ * If this function fails, 'cpu' is destroyed.
+ */
+static PRStatus _PR_StartCPU(_PRCPU *cpu, PRThread *thread)
+{
+ /*
+ ** Start a new cpu. The assumption this code makes is that the
+ ** underlying operating system creates a stack to go with the new
+ ** native thread. That stack will be used by the cpu when pausing.
+ */
+
+ PR_ASSERT(!_native_threads_only);
+
+ cpu->last_clock = PR_IntervalNow();
+
+ /* Before we create any threads on this CPU we have to
+ * set the current CPU
+ */
+ _PR_MD_SET_CURRENT_CPU(cpu);
+ _PR_MD_INIT_RUNNING_CPU(cpu);
+ thread->cpu = cpu;
+
+ cpu->idle_thread = _PR_CreateThread(PR_SYSTEM_THREAD,
+ _PR_CPU_Idle,
+ (void *)cpu,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0,
+ _PR_IDLE_THREAD);
+
+ if (!cpu->idle_thread) {
+ /* didn't clean up CPU queue XXXMB */
+ PR_DELETE(cpu);
+ return PR_FAILURE;
+ }
+ PR_ASSERT(cpu->idle_thread->cpu == cpu);
+
+ cpu->idle_thread->no_sched = 0;
+
+ cpu->thread = thread;
+
+ if (_pr_cpu_affinity_mask) {
+ PR_SetThreadAffinityMask(thread, _pr_cpu_affinity_mask);
+ }
+
+ /* Created and started a new CPU */
+ _PR_CPU_LIST_LOCK();
+ cpu->id = _pr_cpuID++;
+ PR_APPEND_LINK(&cpu->links, &_PR_CPUQ());
+ _PR_CPU_LIST_UNLOCK();
+
+ return PR_SUCCESS;
+}
+
+#if !defined(_PR_GLOBAL_THREADS_ONLY) && !defined(_PR_LOCAL_THREADS_ONLY)
+/*
+** This code is used during a cpu's initial creation.
+*/
+static void _PR_RunCPU(void *arg)
+{
+ _PRCPU *cpu = (_PRCPU *)arg;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(NULL != me);
+
+ /*
+ * _PR_StartCPU calls _PR_CreateThread to create the
+ * idle thread. Because _PR_CreateThread calls PR_Lock,
+ * the current thread has to remain a global thread
+ * during the _PR_StartCPU call so that it can wait for
+ * the lock if the lock is held by another thread. If
+ * we clear the _PR_GLOBAL_SCOPE flag in
+ * _PR_MD_CREATE_PRIMORDIAL_THREAD, the current thread
+ * will be treated as a local thread and have trouble
+ * waiting for the lock because the CPU is not fully
+ * constructed yet.
+ *
+ * After the CPU is started, it is safe to mark the
+ * current thread as a local thread.
+ */
+
+#ifdef HAVE_CUSTOM_USER_THREADS
+ _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
+#endif
+
+ me->no_sched = 1;
+ _PR_StartCPU(cpu, me);
+
+#ifdef HAVE_CUSTOM_USER_THREADS
+ me->flags &= (~_PR_GLOBAL_SCOPE);
+#endif
+
+ _PR_MD_SET_CURRENT_CPU(cpu);
+ _PR_MD_SET_CURRENT_THREAD(cpu->thread);
+ me->cpu = cpu;
+
+ while(1) {
+ PRInt32 is;
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_MD_START_INTERRUPTS();
+ _PR_MD_SWITCH_CONTEXT(me);
+ }
+}
+#endif
+
+static void PR_CALLBACK _PR_CPU_Idle(void *_cpu)
+{
+ _PRCPU *cpu = (_PRCPU *)_cpu;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(NULL != me);
+
+ me->cpu = cpu;
+ cpu->idle_thread = me;
+ if (_MD_LAST_THREAD()) {
+ _MD_LAST_THREAD()->no_sched = 0;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_MD_SET_INTSOFF(0);
+ }
+ while(1) {
+ PRInt32 is;
+ PRIntervalTime timeout;
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+
+ _PR_RUNQ_LOCK(cpu);
+#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
+#ifdef _PR_HAVE_ATOMIC_OPS
+ _PR_MD_ATOMIC_INCREMENT(&_pr_md_idle_cpus);
+#else
+ _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
+ _pr_md_idle_cpus++;
+ _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
+#endif /* _PR_HAVE_ATOMIC_OPS */
+#endif
+ /* If someone on runq; do a nonblocking PAUSECPU */
+ if (_PR_RUNQREADYMASK(me->cpu) != 0) {
+ _PR_RUNQ_UNLOCK(cpu);
+ timeout = PR_INTERVAL_NO_WAIT;
+ } else {
+ _PR_RUNQ_UNLOCK(cpu);
+
+ _PR_SLEEPQ_LOCK(cpu);
+ if (PR_CLIST_IS_EMPTY(&_PR_SLEEPQ(me->cpu))) {
+ timeout = PR_INTERVAL_NO_TIMEOUT;
+ } else {
+ PRThread *wakeThread;
+ wakeThread = _PR_THREAD_PTR(_PR_SLEEPQ(me->cpu).next);
+ timeout = wakeThread->sleep;
+ }
+ _PR_SLEEPQ_UNLOCK(cpu);
+ }
+
+ /* Wait for an IO to complete */
+ (void)_PR_MD_PAUSE_CPU(timeout);
+
+#ifdef WINNT
+ if (_pr_cpus_exit) {
+ /* _PR_CleanupCPUs tells us to exit */
+ _PR_MD_END_THREAD();
+ }
+#endif
+
+#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
+#ifdef _PR_HAVE_ATOMIC_OPS
+ _PR_MD_ATOMIC_DECREMENT(&_pr_md_idle_cpus);
+#else
+ _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
+ _pr_md_idle_cpus--;
+ _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
+#endif /* _PR_HAVE_ATOMIC_OPS */
+#endif
+
+ _PR_ClockInterrupt();
+
+ /* Now schedule any thread that is on the runq
+ * INTS must be OFF when calling PR_Schedule()
+ */
+ me->state = _PR_RUNNABLE;
+ _PR_MD_SWITCH_CONTEXT(me);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_FAST_INTSON(is);
+ }
+ }
+}
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+
+PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs)
+{
+#if defined(_PR_GLOBAL_THREADS_ONLY) || defined(_PR_LOCAL_THREADS_ONLY)
+
+ /* do nothing */
+
+#else /* combined, MxN thread model */
+
+ PRUintn newCPU;
+ _PRCPU *cpu;
+ PRThread *thr;
+
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (_native_threads_only) {
+ return;
+ }
+
+ _PR_CPU_LIST_LOCK();
+ if (_pr_numCPU < numCPUs) {
+ newCPU = numCPUs - _pr_numCPU;
+ _pr_numCPU = numCPUs;
+ } else {
+ newCPU = 0;
+ }
+ _PR_CPU_LIST_UNLOCK();
+
+ for (; newCPU; newCPU--) {
+ cpu = _PR_CreateCPU();
+ thr = _PR_CreateThread(PR_SYSTEM_THREAD,
+ _PR_RunCPU,
+ cpu,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0,
+ _PR_IDLE_THREAD);
+ }
+#endif
+}
+
+PR_IMPLEMENT(_PRCPU *) _PR_GetPrimordialCPU(void)
+{
+ if (_pr_primordialCPU) {
+ return _pr_primordialCPU;
+ }
+ else {
+ return _PR_MD_CURRENT_CPU();
+ }
+}
diff --git a/nsprpub/pr/src/threads/combined/prucv.c b/nsprpub/pr/src/threads/combined/prucv.c
new file mode 100644
index 0000000000..801d3d2fdd
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/prucv.c
@@ -0,0 +1,679 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "primpl.h"
+#include "prinrval.h"
+#include "prtypes.h"
+
+#if defined(WIN95)
+/*
+** Some local variables report warnings on Win95 because the code paths
+** using them are conditioned on HAVE_CUSTOME_USER_THREADS.
+** The pragma suppresses the warning.
+**
+*/
+#pragma warning(disable : 4101)
+#endif
+
+
+/*
+** Notify one thread that it has finished waiting on a condition variable
+** Caller must hold the _PR_CVAR_LOCK(cv)
+*/
+PRBool _PR_NotifyThread (PRThread *thread, PRThread *me)
+{
+ PRBool rv;
+
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+ _PR_THREAD_LOCK(thread);
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+ if ( !_PR_IS_NATIVE_THREAD(thread) ) {
+ if (thread->wait.cvar != NULL) {
+ thread->wait.cvar = NULL;
+
+ _PR_SLEEPQ_LOCK(thread->cpu);
+ /* The notify and timeout can collide; in which case both may
+ * attempt to delete from the sleepQ; only let one do it.
+ */
+ if (thread->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) {
+ _PR_DEL_SLEEPQ(thread, PR_TRUE);
+ }
+ _PR_SLEEPQ_UNLOCK(thread->cpu);
+
+ if (thread->flags & _PR_SUSPENDING) {
+ /*
+ * set thread state to SUSPENDED; a Resume operation
+ * on the thread will move it to the runQ
+ */
+ thread->state = _PR_SUSPENDED;
+ _PR_MISCQ_LOCK(thread->cpu);
+ _PR_ADD_SUSPENDQ(thread, thread->cpu);
+ _PR_MISCQ_UNLOCK(thread->cpu);
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* Make thread runnable */
+ thread->state = _PR_RUNNABLE;
+ _PR_THREAD_UNLOCK(thread);
+
+ _PR_AddThreadToRunQ(me, thread);
+ _PR_MD_WAKEUP_WAITER(thread);
+ }
+
+ rv = PR_TRUE;
+ } else {
+ /* Thread has already been notified */
+ _PR_THREAD_UNLOCK(thread);
+ rv = PR_FALSE;
+ }
+ } else { /* If the thread is a native thread */
+ if (thread->wait.cvar) {
+ thread->wait.cvar = NULL;
+
+ if (thread->flags & _PR_SUSPENDING) {
+ /*
+ * set thread state to SUSPENDED; a Resume operation
+ * on the thread will enable the thread to run
+ */
+ thread->state = _PR_SUSPENDED;
+ } else {
+ thread->state = _PR_RUNNING;
+ }
+ _PR_THREAD_UNLOCK(thread);
+ _PR_MD_WAKEUP_WAITER(thread);
+ rv = PR_TRUE;
+ } else {
+ _PR_THREAD_UNLOCK(thread);
+ rv = PR_FALSE;
+ }
+ }
+
+ return rv;
+}
+
+/*
+ * Notify thread waiting on cvar; called when thread is interrupted
+ * The thread lock is held on entry and released before return
+ */
+void _PR_NotifyLockedThread (PRThread *thread)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRCondVar *cvar;
+ PRThreadPriority pri;
+
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);
+ }
+
+ cvar = thread->wait.cvar;
+ thread->wait.cvar = NULL;
+ _PR_THREAD_UNLOCK(thread);
+
+ _PR_CVAR_LOCK(cvar);
+ _PR_THREAD_LOCK(thread);
+
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_SLEEPQ_LOCK(thread->cpu);
+ /* The notify and timeout can collide; in which case both may
+ * attempt to delete from the sleepQ; only let one do it.
+ */
+ if (thread->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) {
+ _PR_DEL_SLEEPQ(thread, PR_TRUE);
+ }
+ _PR_SLEEPQ_UNLOCK(thread->cpu);
+
+ /* Make thread runnable */
+ pri = thread->priority;
+ thread->state = _PR_RUNNABLE;
+
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+
+ _PR_AddThreadToRunQ(me, thread);
+ _PR_THREAD_UNLOCK(thread);
+
+ _PR_MD_WAKEUP_WAITER(thread);
+ } else {
+ if (thread->flags & _PR_SUSPENDING) {
+ /*
+ * set thread state to SUSPENDED; a Resume operation
+ * on the thread will enable the thread to run
+ */
+ thread->state = _PR_SUSPENDED;
+ } else {
+ thread->state = _PR_RUNNING;
+ }
+ _PR_THREAD_UNLOCK(thread);
+ _PR_MD_WAKEUP_WAITER(thread);
+ }
+
+ _PR_CVAR_UNLOCK(cvar);
+ return;
+}
+
+/*
+** Make the given thread wait for the given condition variable
+*/
+PRStatus _PR_WaitCondVar(
+ PRThread *thread, PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout)
+{
+ PRIntn is;
+ PRStatus rv = PR_SUCCESS;
+
+ PR_ASSERT(thread == _PR_MD_CURRENT_THREAD());
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ if (_PR_PENDING_INTERRUPT(thread)) {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thread->flags &= ~_PR_INTERRUPT;
+ return PR_FAILURE;
+ }
+
+ thread->wait.cvar = cvar;
+ lock->owner = NULL;
+ _PR_MD_WAIT_CV(&cvar->md,&lock->ilock, timeout);
+ thread->wait.cvar = NULL;
+ lock->owner = thread;
+ if (_PR_PENDING_INTERRUPT(thread)) {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thread->flags &= ~_PR_INTERRUPT;
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+ if ( !_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_INTSOFF(is);
+ }
+
+ _PR_CVAR_LOCK(cvar);
+ _PR_THREAD_LOCK(thread);
+
+ if (_PR_PENDING_INTERRUPT(thread)) {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thread->flags &= ~_PR_INTERRUPT;
+ _PR_CVAR_UNLOCK(cvar);
+ _PR_THREAD_UNLOCK(thread);
+ if ( !_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_INTSON(is);
+ }
+ return PR_FAILURE;
+ }
+
+ thread->state = _PR_COND_WAIT;
+ thread->wait.cvar = cvar;
+
+ /*
+ ** Put the caller thread on the condition variable's wait Q
+ */
+ PR_APPEND_LINK(&thread->waitQLinks, &cvar->condQ);
+
+ /* Note- for global scope threads, we don't put them on the
+ * global sleepQ, so each global thread must put itself
+ * to sleep only for the time it wants to.
+ */
+ if ( !_PR_IS_NATIVE_THREAD(thread) ) {
+ _PR_SLEEPQ_LOCK(thread->cpu);
+ _PR_ADD_SLEEPQ(thread, timeout);
+ _PR_SLEEPQ_UNLOCK(thread->cpu);
+ }
+ _PR_CVAR_UNLOCK(cvar);
+ _PR_THREAD_UNLOCK(thread);
+
+ /*
+ ** Release lock protecting the condition variable and thereby giving time
+ ** to the next thread which can potentially notify on the condition variable
+ */
+ PR_Unlock(lock);
+
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN,
+ ("PR_Wait: cvar=%p waiting for %d", cvar, timeout));
+
+ rv = _PR_MD_WAIT(thread, timeout);
+
+ _PR_CVAR_LOCK(cvar);
+ PR_REMOVE_LINK(&thread->waitQLinks);
+ _PR_CVAR_UNLOCK(cvar);
+
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN,
+ ("PR_Wait: cvar=%p done waiting", cvar));
+
+ if ( !_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_INTSON(is);
+ }
+
+ /* Acquire lock again that we had just relinquished */
+ PR_Lock(lock);
+
+ if (_PR_PENDING_INTERRUPT(thread)) {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thread->flags &= ~_PR_INTERRUPT;
+ return PR_FAILURE;
+ }
+
+ return rv;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+void _PR_NotifyCondVar(PRCondVar *cvar, PRThread *me)
+{
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ _PR_MD_NOTIFY_CV(&cvar->md, &cvar->lock->ilock);
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+ PRCList *q;
+ PRIntn is;
+
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+ _PR_CVAR_LOCK(cvar);
+ q = cvar->condQ.next;
+ while (q != &cvar->condQ) {
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("_PR_NotifyCondVar: cvar=%p", cvar));
+ if (_PR_THREAD_CONDQ_PTR(q)->wait.cvar) {
+ if (_PR_NotifyThread(_PR_THREAD_CONDQ_PTR(q), me) == PR_TRUE) {
+ break;
+ }
+ }
+ q = q->next;
+ }
+ _PR_CVAR_UNLOCK(cvar);
+
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/*
+** Cndition variable debugging log info.
+*/
+PRUint32 _PR_CondVarToString(PRCondVar *cvar, char *buf, PRUint32 buflen)
+{
+ PRUint32 nb;
+
+ if (cvar->lock->owner) {
+ nb = PR_snprintf(buf, buflen, "[%p] owner=%ld[%p]",
+ cvar, cvar->lock->owner->id, cvar->lock->owner);
+ } else {
+ nb = PR_snprintf(buf, buflen, "[%p]", cvar);
+ }
+ return nb;
+}
+
+/*
+** Expire condition variable waits that are ready to expire. "now" is the current
+** time.
+*/
+void _PR_ClockInterrupt(void)
+{
+ PRThread *thread, *me = _PR_MD_CURRENT_THREAD();
+ _PRCPU *cpu = me->cpu;
+ PRIntervalTime elapsed, now;
+
+ PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);
+ /* Figure out how much time elapsed since the last clock tick */
+ now = PR_IntervalNow();
+ elapsed = now - cpu->last_clock;
+ cpu->last_clock = now;
+
+ PR_LOG(_pr_clock_lm, PR_LOG_MAX,
+ ("ExpireWaits: elapsed=%lld usec", elapsed));
+
+ while(1) {
+ _PR_SLEEPQ_LOCK(cpu);
+ if (_PR_SLEEPQ(cpu).next == &_PR_SLEEPQ(cpu)) {
+ _PR_SLEEPQ_UNLOCK(cpu);
+ break;
+ }
+
+ thread = _PR_THREAD_PTR(_PR_SLEEPQ(cpu).next);
+ PR_ASSERT(thread->cpu == cpu);
+
+ if (elapsed < thread->sleep) {
+ thread->sleep -= elapsed;
+ _PR_SLEEPQMAX(thread->cpu) -= elapsed;
+ _PR_SLEEPQ_UNLOCK(cpu);
+ break;
+ }
+ _PR_SLEEPQ_UNLOCK(cpu);
+
+ PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));
+
+ _PR_THREAD_LOCK(thread);
+
+ if (thread->cpu != cpu) {
+ /*
+ ** The thread was switched to another CPU
+ ** between the time we unlocked the sleep
+ ** queue and the time we acquired the thread
+ ** lock, so it is none of our business now.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ continue;
+ }
+
+ /*
+ ** Consume this sleeper's amount of elapsed time from the elapsed
+ ** time value. The next remaining piece of elapsed time will be
+ ** available for the next sleeping thread's timer.
+ */
+ _PR_SLEEPQ_LOCK(cpu);
+ PR_ASSERT(!(thread->flags & _PR_ON_PAUSEQ));
+ if (thread->flags & _PR_ON_SLEEPQ) {
+ _PR_DEL_SLEEPQ(thread, PR_FALSE);
+ elapsed -= thread->sleep;
+ _PR_SLEEPQ_UNLOCK(cpu);
+ } else {
+ /* Thread was already handled; Go get another one */
+ _PR_SLEEPQ_UNLOCK(cpu);
+ _PR_THREAD_UNLOCK(thread);
+ continue;
+ }
+
+ /* Notify the thread waiting on the condition variable */
+ if (thread->flags & _PR_SUSPENDING) {
+ PR_ASSERT((thread->state == _PR_IO_WAIT) ||
+ (thread->state == _PR_COND_WAIT));
+ /*
+ ** Thread is suspended and its condition timeout
+ ** expired. Transfer thread from sleepQ to suspendQ.
+ */
+ thread->wait.cvar = NULL;
+ _PR_MISCQ_LOCK(cpu);
+ thread->state = _PR_SUSPENDED;
+ _PR_ADD_SUSPENDQ(thread, cpu);
+ _PR_MISCQ_UNLOCK(cpu);
+ } else {
+ if (thread->wait.cvar) {
+ PRThreadPriority pri;
+
+ /* Do work very similar to what _PR_NotifyThread does */
+ PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) );
+
+ /* Make thread runnable */
+ pri = thread->priority;
+ thread->state = _PR_RUNNABLE;
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+
+ PR_ASSERT(thread->cpu == cpu);
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thread, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+
+ if (pri > me->priority) {
+ _PR_SET_RESCHED_FLAG();
+ }
+
+ thread->wait.cvar = NULL;
+
+ _PR_MD_WAKEUP_WAITER(thread);
+
+ } else if (thread->io_pending == PR_TRUE) {
+ /* Need to put IO sleeper back on runq */
+ int pri = thread->priority;
+
+ thread->io_suspended = PR_TRUE;
+#ifdef WINNT
+ /*
+ * For NT, record the cpu on which I/O was issued
+ * I/O cancellation is done on the same cpu
+ */
+ thread->md.thr_bound_cpu = cpu;
+#endif
+
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+ PR_ASSERT(thread->cpu == cpu);
+ thread->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thread, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ }
+ }
+ _PR_THREAD_UNLOCK(thread);
+ }
+}
+
+/************************************************************************/
+
+/*
+** Create a new condition variable.
+** "lock" is the lock to use with the condition variable.
+**
+** Condition variables are synchronization objects that threads can use
+** to wait for some condition to occur.
+**
+** This may fail if memory is tight or if some operating system resource
+** is low.
+*/
+PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock)
+{
+ PRCondVar *cvar;
+
+ cvar = PR_NEWZAP(PRCondVar);
+ if (cvar) {
+ if (_PR_InitCondVar(cvar, lock) != PR_SUCCESS) {
+ PR_DELETE(cvar);
+ return NULL;
+ }
+ } else {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ return cvar;
+}
+
+PRStatus _PR_InitCondVar(PRCondVar *cvar, PRLock *lock)
+{
+ PR_ASSERT(lock != NULL);
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ if(_PR_MD_NEW_CV(&cvar->md)) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ return PR_FAILURE;
+ }
+#endif
+ if (_PR_MD_NEW_LOCK(&(cvar->ilock)) != PR_SUCCESS) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ return PR_FAILURE;
+ }
+ cvar->lock = lock;
+ PR_INIT_CLIST(&cvar->condQ);
+ return PR_SUCCESS;
+}
+
+/*
+** Destroy a condition variable. There must be no thread
+** waiting on the condvar. The caller is responsible for guaranteeing
+** that the condvar is no longer in use.
+**
+*/
+PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar)
+{
+ _PR_FreeCondVar(cvar);
+ PR_DELETE(cvar);
+}
+
+void _PR_FreeCondVar(PRCondVar *cvar)
+{
+ PR_ASSERT(cvar->condQ.next == &cvar->condQ);
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ _PR_MD_FREE_CV(&cvar->md);
+#endif
+ _PR_MD_FREE_LOCK(&(cvar->ilock));
+}
+
+/*
+** Wait for a notify on the condition variable. Sleep for "tiemout" amount
+** of ticks (if "timeout" is zero then the sleep is indefinite). While
+** the thread is waiting it unlocks lock. When the wait has
+** finished the thread regains control of the condition variable after
+** locking the associated lock.
+**
+** The thread waiting on the condvar will be resumed when the condvar is
+** notified (assuming the thread is the next in line to receive the
+** notify) or when the timeout elapses.
+**
+** Returns PR_FAILURE if the caller has not locked the lock associated
+** with the condition variable or the thread has been interrupted.
+*/
+extern PRThread *suspendAllThread;
+PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(cvar->lock->owner == me);
+ PR_ASSERT(me != suspendAllThread);
+ if (cvar->lock->owner != me) {
+ return PR_FAILURE;
+ }
+
+ return _PR_WaitCondVar(me, cvar, cvar->lock, timeout);
+}
+
+/*
+** Notify the highest priority thread waiting on the condition
+** variable. If a thread is waiting on the condition variable (using
+** PR_Wait) then it is awakened and begins waiting on the lock.
+*/
+PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(cvar->lock->owner == me);
+ PR_ASSERT(me != suspendAllThread);
+ if (cvar->lock->owner != me) {
+ return PR_FAILURE;
+ }
+
+ _PR_NotifyCondVar(cvar, me);
+ return PR_SUCCESS;
+}
+
+/*
+** Notify all of the threads waiting on the condition variable. All of
+** threads are notified in turn. The highest priority thread will
+** probably acquire the lock.
+*/
+PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar)
+{
+ PRCList *q;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(cvar->lock->owner == me);
+ if (cvar->lock->owner != me) {
+ return PR_FAILURE;
+ }
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ _PR_MD_NOTIFYALL_CV(&cvar->md, &cvar->lock->ilock);
+ return PR_SUCCESS;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_CVAR_LOCK(cvar);
+ q = cvar->condQ.next;
+ while (q != &cvar->condQ) {
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_NotifyAll: cvar=%p", cvar));
+ _PR_NotifyThread(_PR_THREAD_CONDQ_PTR(q), me);
+ q = q->next;
+ }
+ _PR_CVAR_UNLOCK(cvar);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+
+ return PR_SUCCESS;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+
+/*********************************************************************/
+/*********************************************************************/
+/********************ROUTINES FOR DCE EMULATION***********************/
+/*********************************************************************/
+/*********************************************************************/
+#include "prpdce.h"
+
+PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar(void)
+{
+ PRCondVar *cvar = PR_NEWZAP(PRCondVar);
+ if (NULL != cvar)
+ {
+ if (_PR_MD_NEW_LOCK(&(cvar->ilock)) == PR_FAILURE)
+ {
+ PR_DELETE(cvar); cvar = NULL;
+ }
+ else
+ {
+ PR_INIT_CLIST(&cvar->condQ);
+ cvar->lock = _PR_NAKED_CV_LOCK;
+ }
+
+ }
+ return cvar;
+}
+
+PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar *cvar)
+{
+ PR_ASSERT(cvar->condQ.next == &cvar->condQ);
+ PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock);
+
+ _PR_MD_FREE_LOCK(&(cvar->ilock));
+
+ PR_DELETE(cvar);
+}
+
+PR_IMPLEMENT(PRStatus) PRP_NakedWait(
+ PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock);
+ return _PR_WaitCondVar(me, cvar, lock, timeout);
+} /* PRP_NakedWait */
+
+PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar *cvar)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock);
+
+ _PR_NotifyCondVar(cvar, me);
+
+ return PR_SUCCESS;
+} /* PRP_NakedNotify */
+
+PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar)
+{
+ PRCList *q;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock);
+
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_MD_LOCK( &(cvar->ilock) );
+ q = cvar->condQ.next;
+ while (q != &cvar->condQ) {
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_NotifyAll: cvar=%p", cvar));
+ _PR_NotifyThread(_PR_THREAD_CONDQ_PTR(q), me);
+ q = q->next;
+ }
+ _PR_MD_UNLOCK( &(cvar->ilock) );
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+
+ return PR_SUCCESS;
+} /* PRP_NakedBroadcast */
+
diff --git a/nsprpub/pr/src/threads/combined/prulock.c b/nsprpub/pr/src/threads/combined/prulock.c
new file mode 100644
index 0000000000..69c27d149f
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/prulock.c
@@ -0,0 +1,457 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#if defined(WIN95)
+/*
+** Some local variables report warnings on Win95 because the code paths
+** using them are conditioned on HAVE_CUSTOME_USER_THREADS.
+** The pragma suppresses the warning.
+**
+*/
+#pragma warning(disable : 4101)
+#endif
+
+
+void _PR_InitLocks(void)
+{
+ _PR_MD_INIT_LOCKS();
+}
+
+/*
+** Deal with delayed interrupts/requested reschedule during interrupt
+** re-enables.
+*/
+void _PR_IntsOn(_PRCPU *cpu)
+{
+ PRUintn missed, pri, i;
+ _PRInterruptTable *it;
+ PRThread *me;
+
+ PR_ASSERT(cpu); /* Global threads don't have CPUs */
+ PR_ASSERT(_PR_MD_GET_INTSOFF() > 0);
+ me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+
+ /*
+ ** Process delayed interrupts. This logic is kinda scary because we
+ ** need to avoid losing an interrupt (it's ok to delay an interrupt
+ ** until later).
+ **
+ ** There are two missed state words. _pr_ints.where indicates to the
+ ** interrupt handler which state word is currently safe for
+ ** modification.
+ **
+ ** This code scans both interrupt state words, using the where flag
+ ** to indicate to the interrupt which state word is safe for writing.
+ ** If an interrupt comes in during a scan the other word will be
+ ** modified. This modification will be noticed during the next
+ ** iteration of the loop or during the next call to this routine.
+ */
+ for (i = 0; i < 2; i++) {
+ cpu->where = (1 - i);
+ missed = cpu->u.missed[i];
+ if (missed != 0) {
+ cpu->u.missed[i] = 0;
+ for (it = _pr_interruptTable; it->name; it++) {
+ if (missed & it->missed_bit) {
+ PR_LOG(_pr_sched_lm, PR_LOG_MIN,
+ ("IntsOn[0]: %s intr", it->name));
+ (*it->handler)();
+ }
+ }
+ }
+ }
+
+ if (cpu->u.missed[3] != 0) {
+ _PRCPU *cpu;
+
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_RUNNABLE;
+ pri = me->priority;
+
+ cpu = me->cpu;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(me, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_SWITCH_CONTEXT(me);
+ }
+}
+
+/*
+** Unblock the first runnable waiting thread. Skip over
+** threads that are trying to be suspended
+** Note: Caller must hold _PR_LOCK_LOCK()
+*/
+void _PR_UnblockLockWaiter(PRLock *lock)
+{
+ PRThread *t = NULL;
+ PRThread *me;
+ PRCList *q;
+
+ q = lock->waitQ.next;
+ PR_ASSERT(q != &lock->waitQ);
+ while (q != &lock->waitQ) {
+ /* Unblock first waiter */
+ t = _PR_THREAD_CONDQ_PTR(q);
+
+ /*
+ ** We are about to change the thread's state to runnable and for local
+ ** threads, we are going to assign a cpu to it. So, protect thread's
+ ** data structure.
+ */
+ _PR_THREAD_LOCK(t);
+
+ if (t->flags & _PR_SUSPENDING) {
+ q = q->next;
+ _PR_THREAD_UNLOCK(t);
+ continue;
+ }
+
+ /* Found a runnable thread */
+ PR_ASSERT(t->state == _PR_LOCK_WAIT);
+ PR_ASSERT(t->wait.lock == lock);
+ t->wait.lock = 0;
+ PR_REMOVE_LINK(&t->waitQLinks); /* take it off lock's waitQ */
+
+ /*
+ ** If this is a native thread, nothing else to do except to wake it
+ ** up by calling the machine dependent wakeup routine.
+ **
+ ** If this is a local thread, we need to assign it a cpu and
+ ** put the thread on that cpu's run queue. There are two cases to
+ ** take care of. If the currently running thread is also a local
+ ** thread, we just assign our own cpu to that thread and put it on
+ ** the cpu's run queue. If the the currently running thread is a
+ ** native thread, we assign the primordial cpu to it (on NT,
+ ** MD_WAKEUP handles the cpu assignment).
+ */
+
+ if ( !_PR_IS_NATIVE_THREAD(t) ) {
+
+ t->state = _PR_RUNNABLE;
+
+ me = _PR_MD_CURRENT_THREAD();
+
+ _PR_AddThreadToRunQ(me, t);
+ _PR_THREAD_UNLOCK(t);
+ } else {
+ t->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(t);
+ }
+ _PR_MD_WAKEUP_WAITER(t);
+ break;
+ }
+ return;
+}
+
+/************************************************************************/
+
+
+PR_IMPLEMENT(PRLock*) PR_NewLock(void)
+{
+ PRLock *lock;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ lock = PR_NEWZAP(PRLock);
+ if (lock) {
+ if (_PR_InitLock(lock) != PR_SUCCESS) {
+ PR_DELETE(lock);
+ return NULL;
+ }
+ }
+ return lock;
+}
+
+PRStatus _PR_InitLock(PRLock *lock)
+{
+ if (_PR_MD_NEW_LOCK(&lock->ilock) != PR_SUCCESS) {
+ return PR_FAILURE;
+ }
+ PR_INIT_CLIST(&lock->links);
+ PR_INIT_CLIST(&lock->waitQ);
+ return PR_SUCCESS;
+}
+
+/*
+** Destroy the given lock "lock". There is no point in making this race
+** free because if some other thread has the pointer to this lock all
+** bets are off.
+*/
+PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock)
+{
+ _PR_FreeLock(lock);
+ PR_DELETE(lock);
+}
+
+void _PR_FreeLock(PRLock *lock)
+{
+ PR_ASSERT(lock->owner == 0);
+ _PR_MD_FREE_LOCK(&lock->ilock);
+}
+
+extern PRThread *suspendAllThread;
+/*
+** Lock the lock.
+*/
+PR_IMPLEMENT(void) PR_Lock(PRLock *lock)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntn is;
+ PRThread *t;
+ PRCList *q;
+
+ PR_ASSERT(me != suspendAllThread);
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+ PR_ASSERT(lock != NULL);
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ _PR_MD_LOCK(&lock->ilock);
+ PR_ASSERT(lock->owner == 0);
+ lock->owner = me;
+ return;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+ if (_native_threads_only) {
+ _PR_MD_LOCK(&lock->ilock);
+ PR_ASSERT(lock->owner == 0);
+ lock->owner = me;
+ return;
+ }
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+retry:
+ _PR_LOCK_LOCK(lock);
+ if (lock->owner == 0) {
+ /* Just got the lock */
+ lock->owner = me;
+ lock->priority = me->priority;
+ /* Add the granted lock to this owning thread's lock list */
+ PR_APPEND_LINK(&lock->links, &me->lockList);
+ _PR_LOCK_UNLOCK(lock);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_FAST_INTSON(is);
+ }
+ return;
+ }
+
+ /* If this thread already owns this lock, then it is a deadlock */
+ PR_ASSERT(lock->owner != me);
+
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+#if 0
+ if (me->priority > lock->owner->priority) {
+ /*
+ ** Give the lock owner a priority boost until we get the
+ ** lock. Record the priority we boosted it to.
+ */
+ lock->boostPriority = me->priority;
+ _PR_SetThreadPriority(lock->owner, me->priority);
+ }
+#endif
+
+ /*
+ Add this thread to the asked for lock's list of waiting threads. We
+ add this thread thread in the right priority order so when the unlock
+ occurs, the thread with the higher priority will get the lock.
+ */
+ q = lock->waitQ.next;
+ if (q == &lock->waitQ || _PR_THREAD_CONDQ_PTR(q)->priority ==
+ _PR_THREAD_CONDQ_PTR(lock->waitQ.prev)->priority) {
+ /*
+ * If all the threads in the lock waitQ have the same priority,
+ * then avoid scanning the list: insert the element at the end.
+ */
+ q = &lock->waitQ;
+ } else {
+ /* Sort thread into lock's waitQ at appropriate point */
+ /* Now scan the list for where to insert this entry */
+ while (q != &lock->waitQ) {
+ t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next);
+ if (me->priority > t->priority) {
+ /* Found a lower priority thread to insert in front of */
+ break;
+ }
+ q = q->next;
+ }
+ }
+ PR_INSERT_BEFORE(&me->waitQLinks, q);
+
+ /*
+ Now grab the threadLock since we are about to change the state. We have
+ to do this since a PR_Suspend or PR_SetThreadPriority type call that takes
+ a PRThread* as an argument could be changing the state of this thread from
+ a thread running on a different cpu.
+ */
+
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_LOCK_WAIT;
+ me->wait.lock = lock;
+ _PR_THREAD_UNLOCK(me);
+
+ _PR_LOCK_UNLOCK(lock);
+
+ _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
+ goto retry;
+
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/*
+** Unlock the lock.
+*/
+PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock)
+{
+ PRCList *q;
+ PRThreadPriority pri, boost;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(lock != NULL);
+ PR_ASSERT(lock->owner == me);
+ PR_ASSERT(me != suspendAllThread);
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+ if (lock->owner != me) {
+ return PR_FAILURE;
+ }
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ lock->owner = 0;
+ _PR_MD_UNLOCK(&lock->ilock);
+ return PR_SUCCESS;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+ if (_native_threads_only) {
+ lock->owner = 0;
+ _PR_MD_UNLOCK(&lock->ilock);
+ return PR_SUCCESS;
+ }
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_LOCK_LOCK(lock);
+
+ /* Remove the lock from the owning thread's lock list */
+ PR_REMOVE_LINK(&lock->links);
+ pri = lock->priority;
+ boost = lock->boostPriority;
+ if (boost > pri) {
+ /*
+ ** We received a priority boost during the time we held the lock.
+ ** We need to figure out what priority to move to by scanning
+ ** down our list of lock's that we are still holding and using
+ ** the highest boosted priority found.
+ */
+ q = me->lockList.next;
+ while (q != &me->lockList) {
+ PRLock *ll = _PR_LOCK_PTR(q);
+ if (ll->boostPriority > pri) {
+ pri = ll->boostPriority;
+ }
+ q = q->next;
+ }
+ if (pri != me->priority) {
+ _PR_SetThreadPriority(me, pri);
+ }
+ }
+
+ /* Unblock the first waiting thread */
+ q = lock->waitQ.next;
+ if (q != &lock->waitQ) {
+ _PR_UnblockLockWaiter(lock);
+ }
+ lock->boostPriority = PR_PRIORITY_LOW;
+ lock->owner = 0;
+ _PR_LOCK_UNLOCK(lock);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+ return PR_SUCCESS;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/*
+** If the current thread owns |lock|, this assertion is guaranteed to
+** succeed. Otherwise, the behavior of this function is undefined.
+*/
+PR_IMPLEMENT(void) PR_AssertCurrentThreadOwnsLock(PRLock *lock)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(lock->owner == me);
+}
+
+/*
+** Test and then lock the lock if it's not already locked by some other
+** thread. Return PR_FALSE if some other thread owned the lock at the
+** time of the call.
+*/
+PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock *lock)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRBool rv = PR_FALSE;
+ PRIntn is;
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ is = _PR_MD_TEST_AND_LOCK(&lock->ilock);
+ if (is == 0) {
+ lock->owner = me;
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+#ifndef _PR_LOCAL_THREADS_ONLY
+ if (_native_threads_only) {
+ is = _PR_MD_TEST_AND_LOCK(&lock->ilock);
+ if (is == 0) {
+ lock->owner = me;
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+ }
+#endif
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+
+ _PR_LOCK_LOCK(lock);
+ if (lock->owner == 0) {
+ /* Just got the lock */
+ lock->owner = me;
+ lock->priority = me->priority;
+ /* Add the granted lock to this owning thread's lock list */
+ PR_APPEND_LINK(&lock->links, &me->lockList);
+ rv = PR_TRUE;
+ }
+ _PR_LOCK_UNLOCK(lock);
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+ return rv;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/************************************************************************/
+/************************************************************************/
+/***********************ROUTINES FOR DCE EMULATION***********************/
+/************************************************************************/
+/************************************************************************/
+PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock)
+{
+ return (PR_TestAndLock(lock)) ? PR_SUCCESS : PR_FAILURE;
+}
diff --git a/nsprpub/pr/src/threads/combined/prustack.c b/nsprpub/pr/src/threads/combined/prustack.c
new file mode 100644
index 0000000000..3f5452c48c
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/prustack.c
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+/* List of free stack virtual memory chunks */
+PRLock *_pr_stackLock;
+PRCList _pr_freeStacks = PR_INIT_STATIC_CLIST(&_pr_freeStacks);
+PRIntn _pr_numFreeStacks;
+PRIntn _pr_maxFreeStacks = 4;
+
+#ifdef DEBUG
+/*
+** A variable that can be set via the debugger...
+*/
+PRBool _pr_debugStacks = PR_FALSE;
+#endif
+
+/* How much space to leave between the stacks, at each end */
+#define REDZONE (2 << _pr_pageShift)
+
+#define _PR_THREAD_STACK_PTR(_qp) \
+ ((PRThreadStack*) ((char*) (_qp) - offsetof(PRThreadStack,links)))
+
+void _PR_InitStacks(void)
+{
+ _pr_stackLock = PR_NewLock();
+}
+
+void _PR_CleanupStacks(void)
+{
+ if (_pr_stackLock) {
+ PR_DestroyLock(_pr_stackLock);
+ _pr_stackLock = NULL;
+ }
+}
+
+/*
+** Allocate a stack for a thread.
+*/
+PRThreadStack *_PR_NewStack(PRUint32 stackSize)
+{
+ PRCList *qp;
+ PRThreadStack *ts;
+ PRThread *thr;
+
+ /*
+ ** Trim the list of free stacks. Trim it backwards, tossing out the
+ ** oldest stack found first (this way more recent stacks have a
+ ** chance of being present in the data cache).
+ */
+ PR_Lock(_pr_stackLock);
+ qp = _pr_freeStacks.prev;
+ while ((_pr_numFreeStacks > _pr_maxFreeStacks) && (qp != &_pr_freeStacks)) {
+ ts = _PR_THREAD_STACK_PTR(qp);
+ thr = _PR_THREAD_STACK_TO_PTR(ts);
+ qp = qp->prev;
+ /*
+ * skip stacks which are still being used
+ */
+ if (thr->no_sched) {
+ continue;
+ }
+ PR_REMOVE_LINK(&ts->links);
+
+ /* Give platform OS to clear out the stack for debugging */
+ _PR_MD_CLEAR_STACK(ts);
+
+ _pr_numFreeStacks--;
+ _PR_DestroySegment(ts->seg);
+ PR_DELETE(ts);
+ }
+
+ /*
+ ** Find a free thread stack. This searches the list of free'd up
+ ** virtually mapped thread stacks.
+ */
+ qp = _pr_freeStacks.next;
+ ts = 0;
+ while (qp != &_pr_freeStacks) {
+ ts = _PR_THREAD_STACK_PTR(qp);
+ thr = _PR_THREAD_STACK_TO_PTR(ts);
+ qp = qp->next;
+ /*
+ * skip stacks which are still being used
+ */
+ if ((!(thr->no_sched)) && ((ts->allocSize - 2*REDZONE) >= stackSize)) {
+ /*
+ ** Found a stack that is not in use and is big enough. Change
+ ** stackSize to fit it.
+ */
+ stackSize = ts->allocSize - 2*REDZONE;
+ PR_REMOVE_LINK(&ts->links);
+ _pr_numFreeStacks--;
+ ts->links.next = 0;
+ ts->links.prev = 0;
+ PR_Unlock(_pr_stackLock);
+ goto done;
+ }
+ ts = 0;
+ }
+ PR_Unlock(_pr_stackLock);
+
+ if (!ts) {
+ /* Make a new thread stack object. */
+ ts = PR_NEWZAP(PRThreadStack);
+ if (!ts) {
+ return NULL;
+ }
+
+ /*
+ ** Assign some of the virtual space to the new stack object. We
+ ** may not get that piece of VM, but if nothing else we will
+ ** advance the pointer so we don't collide (unless the OS screws
+ ** up).
+ */
+ ts->allocSize = stackSize + 2*REDZONE;
+ ts->seg = _PR_NewSegment(ts->allocSize, 0);
+ if (!ts->seg) {
+ PR_DELETE(ts);
+ return NULL;
+ }
+ }
+
+done:
+ ts->allocBase = (char*)ts->seg->vaddr;
+ ts->flags = _PR_STACK_MAPPED;
+ ts->stackSize = stackSize;
+
+#ifdef HAVE_STACK_GROWING_UP
+ ts->stackTop = ts->allocBase + REDZONE;
+ ts->stackBottom = ts->stackTop + stackSize;
+#else
+ ts->stackBottom = ts->allocBase + REDZONE;
+ ts->stackTop = ts->stackBottom + stackSize;
+#endif
+
+ PR_LOG(_pr_thread_lm, PR_LOG_NOTICE,
+ ("thread stack: base=0x%x limit=0x%x bottom=0x%x top=0x%x\n",
+ ts->allocBase, ts->allocBase + ts->allocSize - 1,
+ ts->allocBase + REDZONE,
+ ts->allocBase + REDZONE + stackSize - 1));
+
+ _PR_MD_INIT_STACK(ts,REDZONE);
+
+ return ts;
+}
+
+/*
+** Free the stack for the current thread
+*/
+void _PR_FreeStack(PRThreadStack *ts)
+{
+ if (!ts) {
+ return;
+ }
+ if (ts->flags & _PR_STACK_PRIMORDIAL) {
+ PR_DELETE(ts);
+ return;
+ }
+
+ /*
+ ** Put the stack on the free list. This is done because we are still
+ ** using the stack. Next time a thread is created we will trim the
+ ** list down; it's safe to do it then because we will have had to
+ ** context switch to a live stack before another thread can be
+ ** created.
+ */
+ PR_Lock(_pr_stackLock);
+ PR_APPEND_LINK(&ts->links, _pr_freeStacks.prev);
+ _pr_numFreeStacks++;
+ PR_Unlock(_pr_stackLock);
+}
diff --git a/nsprpub/pr/src/threads/combined/pruthr.c b/nsprpub/pr/src/threads/combined/pruthr.c
new file mode 100644
index 0000000000..44a0820072
--- /dev/null
+++ b/nsprpub/pr/src/threads/combined/pruthr.c
@@ -0,0 +1,1942 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#include <signal.h>
+#include <string.h>
+
+#if defined(WIN95)
+/*
+** Some local variables report warnings on Win95 because the code paths
+** using them are conditioned on HAVE_CUSTOME_USER_THREADS.
+** The pragma suppresses the warning.
+**
+*/
+#pragma warning(disable : 4101)
+#endif
+
+/* _pr_activeLock protects the following global variables */
+PRLock *_pr_activeLock;
+PRInt32 _pr_primordialExitCount; /* In PR_Cleanup(), the primordial thread
+ * waits until all other user (non-system)
+ * threads have terminated before it exits.
+ * So whenever we decrement _pr_userActive,
+ * it is compared with
+ * _pr_primordialExitCount.
+ * If the primordial thread is a system
+ * thread, then _pr_primordialExitCount
+ * is 0. If the primordial thread is
+ * itself a user thread, then
+ * _pr_primordialThread is 1.
+ */
+PRCondVar *_pr_primordialExitCVar; /* When _pr_userActive is decremented to
+ * _pr_primordialExitCount, this condition
+ * variable is notified.
+ */
+
+PRLock *_pr_deadQLock;
+PRUint32 _pr_numNativeDead;
+PRUint32 _pr_numUserDead;
+PRCList _pr_deadNativeQ;
+PRCList _pr_deadUserQ;
+
+PRUint32 _pr_join_counter;
+
+PRUint32 _pr_local_threads;
+PRUint32 _pr_global_threads;
+
+PRBool suspendAllOn = PR_FALSE;
+PRThread *suspendAllThread = NULL;
+
+extern PRCList _pr_active_global_threadQ;
+extern PRCList _pr_active_local_threadQ;
+
+static void _PR_DecrActiveThreadCount(PRThread *thread);
+static PRThread *_PR_AttachThread(PRThreadType, PRThreadPriority, PRThreadStack *);
+static void _PR_InitializeNativeStack(PRThreadStack *ts);
+static void _PR_InitializeRecycledThread(PRThread *thread);
+static void _PR_UserRunThread(void);
+
+void _PR_InitThreads(PRThreadType type, PRThreadPriority priority,
+ PRUintn maxPTDs)
+{
+ PRThread *thread;
+ PRThreadStack *stack;
+
+ PR_ASSERT(priority == PR_PRIORITY_NORMAL);
+
+ _pr_terminationCVLock = PR_NewLock();
+ _pr_activeLock = PR_NewLock();
+
+#ifndef HAVE_CUSTOM_USER_THREADS
+ stack = PR_NEWZAP(PRThreadStack);
+#ifdef HAVE_STACK_GROWING_UP
+ stack->stackTop = (char*) ((((PRWord)&type) >> _pr_pageShift)
+ << _pr_pageShift);
+#else
+#if defined(SOLARIS) || defined (UNIXWARE) && defined (USR_SVR4_THREADS)
+ stack->stackTop = (char*) &thread;
+#else
+ stack->stackTop = (char*) ((((PRWord)&type + _pr_pageSize - 1)
+ >> _pr_pageShift) << _pr_pageShift);
+#endif
+#endif
+#else
+ /* If stack is NULL, we're using custom user threads like NT fibers. */
+ stack = PR_NEWZAP(PRThreadStack);
+ if (stack) {
+ stack->stackSize = 0;
+ _PR_InitializeNativeStack(stack);
+ }
+#endif /* HAVE_CUSTOM_USER_THREADS */
+
+ thread = _PR_AttachThread(type, priority, stack);
+ if (thread) {
+ _PR_MD_SET_CURRENT_THREAD(thread);
+
+ if (type == PR_SYSTEM_THREAD) {
+ thread->flags = _PR_SYSTEM;
+ _pr_systemActive++;
+ _pr_primordialExitCount = 0;
+ } else {
+ _pr_userActive++;
+ _pr_primordialExitCount = 1;
+ }
+ thread->no_sched = 1;
+ _pr_primordialExitCVar = PR_NewCondVar(_pr_activeLock);
+ }
+
+ if (!thread) {
+ PR_Abort();
+ }
+#ifdef _PR_LOCAL_THREADS_ONLY
+ thread->flags |= _PR_PRIMORDIAL;
+#else
+ thread->flags |= _PR_PRIMORDIAL | _PR_GLOBAL_SCOPE;
+#endif
+
+ /*
+ * Needs _PR_PRIMORDIAL flag set before calling
+ * _PR_MD_INIT_THREAD()
+ */
+ if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
+ /*
+ * XXX do what?
+ */
+ }
+
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ());
+ _pr_global_threads++;
+ } else {
+ PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ());
+ _pr_local_threads++;
+ }
+
+ _pr_recycleThreads = 0;
+ _pr_deadQLock = PR_NewLock();
+ _pr_numNativeDead = 0;
+ _pr_numUserDead = 0;
+ PR_INIT_CLIST(&_pr_deadNativeQ);
+ PR_INIT_CLIST(&_pr_deadUserQ);
+}
+
+void _PR_CleanupThreads(void)
+{
+ if (_pr_terminationCVLock) {
+ PR_DestroyLock(_pr_terminationCVLock);
+ _pr_terminationCVLock = NULL;
+ }
+ if (_pr_activeLock) {
+ PR_DestroyLock(_pr_activeLock);
+ _pr_activeLock = NULL;
+ }
+ if (_pr_primordialExitCVar) {
+ PR_DestroyCondVar(_pr_primordialExitCVar);
+ _pr_primordialExitCVar = NULL;
+ }
+ /* TODO _pr_dead{Native,User}Q need to be deleted */
+ if (_pr_deadQLock) {
+ PR_DestroyLock(_pr_deadQLock);
+ _pr_deadQLock = NULL;
+ }
+}
+
+/*
+** Initialize a stack for a native thread
+*/
+static void _PR_InitializeNativeStack(PRThreadStack *ts)
+{
+ if( ts && (ts->stackTop == 0) ) {
+ ts->allocSize = ts->stackSize;
+
+ /*
+ ** Setup stackTop and stackBottom values.
+ */
+#ifdef HAVE_STACK_GROWING_UP
+ ts->allocBase = (char*) ((((PRWord)&ts) >> _pr_pageShift)
+ << _pr_pageShift);
+ ts->stackBottom = ts->allocBase + ts->stackSize;
+ ts->stackTop = ts->allocBase;
+#else
+ ts->allocBase = (char*) ((((PRWord)&ts + _pr_pageSize - 1)
+ >> _pr_pageShift) << _pr_pageShift);
+ ts->stackTop = ts->allocBase;
+ ts->stackBottom = ts->allocBase - ts->stackSize;
+#endif
+ }
+}
+
+void _PR_NotifyJoinWaiters(PRThread *thread)
+{
+ /*
+ ** Handle joinable threads. Change the state to waiting for join.
+ ** Remove from our run Q and put it on global waiting to join Q.
+ ** Notify on our "termination" condition variable so that joining
+ ** thread will know about our termination. Switch our context and
+ ** come back later on to continue the cleanup.
+ */
+ PR_ASSERT(thread == _PR_MD_CURRENT_THREAD());
+ if (thread->term != NULL) {
+ PR_Lock(_pr_terminationCVLock);
+ _PR_THREAD_LOCK(thread);
+ thread->state = _PR_JOIN_WAIT;
+ if ( !_PR_IS_NATIVE_THREAD(thread) ) {
+ _PR_MISCQ_LOCK(thread->cpu);
+ _PR_ADD_JOINQ(thread, thread->cpu);
+ _PR_MISCQ_UNLOCK(thread->cpu);
+ }
+ _PR_THREAD_UNLOCK(thread);
+ PR_NotifyCondVar(thread->term);
+ PR_Unlock(_pr_terminationCVLock);
+ _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(thread->state != _PR_JOIN_WAIT);
+ }
+
+}
+
+/*
+ * Zero some of the data members of a recycled thread.
+ *
+ * Note that we can do this either when a dead thread is added to
+ * the dead thread queue or when it is reused. Here, we are doing
+ * this lazily, when the thread is reused in _PR_CreateThread().
+ */
+static void _PR_InitializeRecycledThread(PRThread *thread)
+{
+ /*
+ * Assert that the following data members are already zeroed
+ * by _PR_CleanupThread().
+ */
+#ifdef DEBUG
+ if (thread->privateData) {
+ unsigned int i;
+ for (i = 0; i < thread->tpdLength; i++) {
+ PR_ASSERT(thread->privateData[i] == NULL);
+ }
+ }
+#endif
+ PR_ASSERT(thread->dumpArg == 0 && thread->dump == 0);
+ PR_ASSERT(thread->errorString == 0 && thread->errorStringSize == 0);
+ PR_ASSERT(thread->errorStringLength == 0);
+ PR_ASSERT(thread->name == 0);
+
+ /* Reset data members in thread structure */
+ thread->errorCode = thread->osErrorCode = 0;
+ thread->io_pending = thread->io_suspended = PR_FALSE;
+ thread->environment = 0;
+ PR_INIT_CLIST(&thread->lockList);
+}
+
+PRStatus _PR_RecycleThread(PRThread *thread)
+{
+ if ( _PR_IS_NATIVE_THREAD(thread) &&
+ _PR_NUM_DEADNATIVE < _pr_recycleThreads) {
+ _PR_DEADQ_LOCK;
+ PR_APPEND_LINK(&thread->links, &_PR_DEADNATIVEQ);
+ _PR_INC_DEADNATIVE;
+ _PR_DEADQ_UNLOCK;
+ return (PR_SUCCESS);
+ } else if ( !_PR_IS_NATIVE_THREAD(thread) &&
+ _PR_NUM_DEADUSER < _pr_recycleThreads) {
+ _PR_DEADQ_LOCK;
+ PR_APPEND_LINK(&thread->links, &_PR_DEADUSERQ);
+ _PR_INC_DEADUSER;
+ _PR_DEADQ_UNLOCK;
+ return (PR_SUCCESS);
+ }
+ return (PR_FAILURE);
+}
+
+/*
+ * Decrement the active thread count, either _pr_systemActive or
+ * _pr_userActive, depending on whether the thread is a system thread
+ * or a user thread. If all the user threads, except possibly
+ * the primordial thread, have terminated, we notify the primordial
+ * thread of this condition.
+ *
+ * Since this function will lock _pr_activeLock, do not call this
+ * function while holding the _pr_activeLock lock, as this will result
+ * in a deadlock.
+ */
+
+static void
+_PR_DecrActiveThreadCount(PRThread *thread)
+{
+ PR_Lock(_pr_activeLock);
+ if (thread->flags & _PR_SYSTEM) {
+ _pr_systemActive--;
+ } else {
+ _pr_userActive--;
+ if (_pr_userActive == _pr_primordialExitCount) {
+ PR_NotifyCondVar(_pr_primordialExitCVar);
+ }
+ }
+ PR_Unlock(_pr_activeLock);
+}
+
+/*
+** Detach thread structure
+*/
+static void
+_PR_DestroyThread(PRThread *thread)
+{
+ _PR_MD_FREE_LOCK(&thread->threadLock);
+ PR_DELETE(thread);
+}
+
+void
+_PR_NativeDestroyThread(PRThread *thread)
+{
+ if(thread->term) {
+ PR_DestroyCondVar(thread->term);
+ thread->term = 0;
+ }
+ if (NULL != thread->privateData) {
+ PR_ASSERT(0 != thread->tpdLength);
+ PR_DELETE(thread->privateData);
+ thread->tpdLength = 0;
+ }
+ PR_DELETE(thread->stack);
+ _PR_DestroyThread(thread);
+}
+
+void
+_PR_UserDestroyThread(PRThread *thread)
+{
+ if(thread->term) {
+ PR_DestroyCondVar(thread->term);
+ thread->term = 0;
+ }
+ if (NULL != thread->privateData) {
+ PR_ASSERT(0 != thread->tpdLength);
+ PR_DELETE(thread->privateData);
+ thread->tpdLength = 0;
+ }
+ _PR_MD_FREE_LOCK(&thread->threadLock);
+ if (thread->threadAllocatedOnStack == 1) {
+ _PR_MD_CLEAN_THREAD(thread);
+ /*
+ * Because the no_sched field is set, this thread/stack will
+ * will not be re-used until the flag is cleared by the thread
+ * we will context switch to.
+ */
+ _PR_FreeStack(thread->stack);
+ } else {
+#ifdef WINNT
+ _PR_MD_CLEAN_THREAD(thread);
+#else
+ /*
+ * This assertion does not apply to NT. On NT, every fiber
+ * has its threadAllocatedOnStack equal to 0. Elsewhere,
+ * only the primordial thread has its threadAllocatedOnStack
+ * equal to 0.
+ */
+ PR_ASSERT(thread->flags & _PR_PRIMORDIAL);
+#endif
+ }
+}
+
+
+/*
+** Run a thread's start function. When the start function returns the
+** thread is done executing and no longer needs the CPU. If there are no
+** more user threads running then we can exit the program.
+*/
+void _PR_NativeRunThread(void *arg)
+{
+ PRThread *thread = (PRThread *)arg;
+
+ _PR_MD_SET_CURRENT_THREAD(thread);
+
+ _PR_MD_SET_CURRENT_CPU(NULL);
+
+ /* Set up the thread stack information */
+ _PR_InitializeNativeStack(thread->stack);
+
+ /* Set up the thread md information */
+ if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
+ /*
+ * thread failed to initialize itself, possibly due to
+ * failure to allocate per-thread resources
+ */
+ return;
+ }
+
+ while(1) {
+ thread->state = _PR_RUNNING;
+
+ /*
+ * Add to list of active threads
+ */
+ PR_Lock(_pr_activeLock);
+ PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ());
+ _pr_global_threads++;
+ PR_Unlock(_pr_activeLock);
+
+ (*thread->startFunc)(thread->arg);
+
+ /*
+ * The following two assertions are meant for NT asynch io.
+ *
+ * The thread should have no asynch io in progress when it
+ * exits, otherwise the overlapped buffer, which is part of
+ * the thread structure, would become invalid.
+ */
+ PR_ASSERT(thread->io_pending == PR_FALSE);
+ /*
+ * This assertion enforces the programming guideline that
+ * if an io function times out or is interrupted, the thread
+ * should close the fd to force the asynch io to abort
+ * before it exits. Right now, closing the fd is the only
+ * way to clear the io_suspended flag.
+ */
+ PR_ASSERT(thread->io_suspended == PR_FALSE);
+
+ /*
+ * remove thread from list of active threads
+ */
+ PR_Lock(_pr_activeLock);
+ PR_REMOVE_LINK(&thread->active);
+ _pr_global_threads--;
+ PR_Unlock(_pr_activeLock);
+
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting"));
+
+ /* All done, time to go away */
+ _PR_CleanupThread(thread);
+
+ _PR_NotifyJoinWaiters(thread);
+
+ _PR_DecrActiveThreadCount(thread);
+
+ thread->state = _PR_DEAD_STATE;
+
+ if (!_pr_recycleThreads || (_PR_RecycleThread(thread) ==
+ PR_FAILURE)) {
+ /*
+ * thread not recycled
+ * platform-specific thread exit processing
+ * - for stuff like releasing native-thread resources, etc.
+ */
+ _PR_MD_EXIT_THREAD(thread);
+ /*
+ * Free memory allocated for the thread
+ */
+ _PR_NativeDestroyThread(thread);
+ /*
+ * thread gone, cannot de-reference thread now
+ */
+ return;
+ }
+
+ /* Now wait for someone to activate us again... */
+ _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT);
+ }
+}
+
+static void _PR_UserRunThread(void)
+{
+ PRThread *thread = _PR_MD_CURRENT_THREAD();
+ PRIntn is;
+
+ if (_MD_LAST_THREAD()) {
+ _MD_LAST_THREAD()->no_sched = 0;
+ }
+
+#ifdef HAVE_CUSTOM_USER_THREADS
+ if (thread->stack == NULL) {
+ thread->stack = PR_NEWZAP(PRThreadStack);
+ _PR_InitializeNativeStack(thread->stack);
+ }
+#endif /* HAVE_CUSTOM_USER_THREADS */
+
+ while(1) {
+ /* Run thread main */
+ if ( !_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_MD_SET_INTSOFF(0);
+ }
+
+ /*
+ * Add to list of active threads
+ */
+ if (!(thread->flags & _PR_IDLE_THREAD)) {
+ PR_Lock(_pr_activeLock);
+ PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ());
+ _pr_local_threads++;
+ PR_Unlock(_pr_activeLock);
+ }
+
+ (*thread->startFunc)(thread->arg);
+
+ /*
+ * The following two assertions are meant for NT asynch io.
+ *
+ * The thread should have no asynch io in progress when it
+ * exits, otherwise the overlapped buffer, which is part of
+ * the thread structure, would become invalid.
+ */
+ PR_ASSERT(thread->io_pending == PR_FALSE);
+ /*
+ * This assertion enforces the programming guideline that
+ * if an io function times out or is interrupted, the thread
+ * should close the fd to force the asynch io to abort
+ * before it exits. Right now, closing the fd is the only
+ * way to clear the io_suspended flag.
+ */
+ PR_ASSERT(thread->io_suspended == PR_FALSE);
+
+ PR_Lock(_pr_activeLock);
+ /*
+ * remove thread from list of active threads
+ */
+ if (!(thread->flags & _PR_IDLE_THREAD)) {
+ PR_REMOVE_LINK(&thread->active);
+ _pr_local_threads--;
+ }
+ PR_Unlock(_pr_activeLock);
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting"));
+
+ /* All done, time to go away */
+ _PR_CleanupThread(thread);
+
+ _PR_INTSOFF(is);
+
+ _PR_NotifyJoinWaiters(thread);
+
+ _PR_DecrActiveThreadCount(thread);
+
+ thread->state = _PR_DEAD_STATE;
+
+ if (!_pr_recycleThreads || (_PR_RecycleThread(thread) ==
+ PR_FAILURE)) {
+ /*
+ ** Destroy the thread resources
+ */
+ _PR_UserDestroyThread(thread);
+ }
+
+ /*
+ ** Find another user thread to run. This cpu has finished the
+ ** previous threads main and is now ready to run another thread.
+ */
+ {
+ PRInt32 is;
+ _PR_INTSOFF(is);
+ _PR_MD_SWITCH_CONTEXT(thread);
+ }
+
+ /* Will land here when we get scheduled again if we are recycling... */
+ }
+}
+
+void _PR_SetThreadPriority(PRThread *thread, PRThreadPriority newPri)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntn is;
+
+ if ( _PR_IS_NATIVE_THREAD(thread) ) {
+ _PR_MD_SET_PRIORITY(&(thread->md), newPri);
+ return;
+ }
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_THREAD_LOCK(thread);
+ if (newPri != thread->priority) {
+ _PRCPU *cpu = thread->cpu;
+
+ switch (thread->state) {
+ case _PR_RUNNING:
+ /* Change my priority */
+
+ _PR_RUNQ_LOCK(cpu);
+ thread->priority = newPri;
+ if (_PR_RUNQREADYMASK(cpu) >> (newPri + 1)) {
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_SET_RESCHED_FLAG();
+ }
+ }
+ _PR_RUNQ_UNLOCK(cpu);
+ break;
+
+ case _PR_RUNNABLE:
+
+ _PR_RUNQ_LOCK(cpu);
+ /* Move to different runQ */
+ _PR_DEL_RUNQ(thread);
+ thread->priority = newPri;
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+ _PR_ADD_RUNQ(thread, cpu, newPri);
+ _PR_RUNQ_UNLOCK(cpu);
+
+ if (newPri > me->priority) {
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_SET_RESCHED_FLAG();
+ }
+ }
+
+ break;
+
+ case _PR_LOCK_WAIT:
+ case _PR_COND_WAIT:
+ case _PR_IO_WAIT:
+ case _PR_SUSPENDED:
+
+ thread->priority = newPri;
+ break;
+ }
+ }
+ _PR_THREAD_UNLOCK(thread);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+}
+
+/*
+** Suspend the named thread and copy its gc registers into regBuf
+*/
+static void _PR_Suspend(PRThread *thread)
+{
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(thread != me);
+ PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread) || (!thread->cpu));
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_THREAD_LOCK(thread);
+ switch (thread->state) {
+ case _PR_RUNNABLE:
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_RUNQ_LOCK(thread->cpu);
+ _PR_DEL_RUNQ(thread);
+ _PR_RUNQ_UNLOCK(thread->cpu);
+
+ _PR_MISCQ_LOCK(thread->cpu);
+ _PR_ADD_SUSPENDQ(thread, thread->cpu);
+ _PR_MISCQ_UNLOCK(thread->cpu);
+ } else {
+ /*
+ * Only LOCAL threads are suspended by _PR_Suspend
+ */
+ PR_ASSERT(0);
+ }
+ thread->state = _PR_SUSPENDED;
+ break;
+
+ case _PR_RUNNING:
+ /*
+ * The thread being suspended should be a LOCAL thread with
+ * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state
+ */
+ PR_ASSERT(0);
+ break;
+
+ case _PR_LOCK_WAIT:
+ case _PR_IO_WAIT:
+ case _PR_COND_WAIT:
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_MD_SUSPEND_THREAD(thread);
+ }
+ thread->flags |= _PR_SUSPENDING;
+ break;
+
+ default:
+ PR_Abort();
+ }
+ _PR_THREAD_UNLOCK(thread);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+}
+
+static void _PR_Resume(PRThread *thread)
+{
+ PRThreadPriority pri;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_THREAD_LOCK(thread);
+ switch (thread->state) {
+ case _PR_SUSPENDED:
+ thread->state = _PR_RUNNABLE;
+ thread->flags &= ~_PR_SUSPENDING;
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_MISCQ_LOCK(thread->cpu);
+ _PR_DEL_SUSPENDQ(thread);
+ _PR_MISCQ_UNLOCK(thread->cpu);
+
+ pri = thread->priority;
+
+ _PR_RUNQ_LOCK(thread->cpu);
+ _PR_ADD_RUNQ(thread, thread->cpu, pri);
+ _PR_RUNQ_UNLOCK(thread->cpu);
+
+ if (pri > _PR_MD_CURRENT_THREAD()->priority) {
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_SET_RESCHED_FLAG();
+ }
+ }
+ } else {
+ PR_ASSERT(0);
+ }
+ break;
+
+ case _PR_IO_WAIT:
+ case _PR_COND_WAIT:
+ thread->flags &= ~_PR_SUSPENDING;
+ /* PR_ASSERT(thread->wait.monitor->stickyCount == 0); */
+ break;
+
+ case _PR_LOCK_WAIT:
+ {
+ PRLock *wLock = thread->wait.lock;
+
+ thread->flags &= ~_PR_SUSPENDING;
+
+ _PR_LOCK_LOCK(wLock);
+ if (thread->wait.lock->owner == 0) {
+ _PR_UnblockLockWaiter(thread->wait.lock);
+ }
+ _PR_LOCK_UNLOCK(wLock);
+ break;
+ }
+ case _PR_RUNNABLE:
+ break;
+ case _PR_RUNNING:
+ /*
+ * The thread being suspended should be a LOCAL thread with
+ * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state
+ */
+ PR_ASSERT(0);
+ break;
+
+ default:
+ /*
+ * thread should have been in one of the above-listed blocked states
+ * (_PR_JOIN_WAIT, _PR_IO_WAIT, _PR_UNBORN, _PR_DEAD_STATE)
+ */
+ PR_Abort();
+ }
+ _PR_THREAD_UNLOCK(thread);
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+
+}
+
+#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
+static PRThread *get_thread(_PRCPU *cpu, PRBool *wakeup_cpus)
+{
+ PRThread *thread;
+ PRIntn pri;
+ PRUint32 r;
+ PRCList *qp;
+ PRIntn priMin, priMax;
+
+ _PR_RUNQ_LOCK(cpu);
+ r = _PR_RUNQREADYMASK(cpu);
+ if (r==0) {
+ priMin = priMax = PR_PRIORITY_FIRST;
+ } else if (r == (1<<PR_PRIORITY_NORMAL) ) {
+ priMin = priMax = PR_PRIORITY_NORMAL;
+ } else {
+ priMin = PR_PRIORITY_FIRST;
+ priMax = PR_PRIORITY_LAST;
+ }
+ thread = NULL;
+ for (pri = priMax; pri >= priMin ; pri-- ) {
+ if (r & (1 << pri)) {
+ for (qp = _PR_RUNQ(cpu)[pri].next;
+ qp != &_PR_RUNQ(cpu)[pri];
+ qp = qp->next) {
+ thread = _PR_THREAD_PTR(qp);
+ /*
+ * skip non-schedulable threads
+ */
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+ if (thread->no_sched) {
+ thread = NULL;
+ /*
+ * Need to wakeup cpus to avoid missing a
+ * runnable thread
+ * Waking up all CPU's need happen only once.
+ */
+
+ *wakeup_cpus = PR_TRUE;
+ continue;
+ } else if (thread->flags & _PR_BOUND_THREAD) {
+ /*
+ * Thread bound to cpu 0
+ */
+
+ thread = NULL;
+ continue;
+ } else if (thread->io_pending == PR_TRUE) {
+ /*
+ * A thread that is blocked for I/O needs to run
+ * on the same cpu on which it was blocked. This is because
+ * the cpu's ioq is accessed without lock protection and scheduling
+ * the thread on a different cpu would preclude this optimization.
+ */
+ thread = NULL;
+ continue;
+ } else {
+ /* Pull thread off of its run queue */
+ _PR_DEL_RUNQ(thread);
+ _PR_RUNQ_UNLOCK(cpu);
+ return(thread);
+ }
+ }
+ }
+ thread = NULL;
+ }
+ _PR_RUNQ_UNLOCK(cpu);
+ return(thread);
+}
+#endif /* !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) */
+
+/*
+** Schedule this native thread by finding the highest priority nspr
+** thread that is ready to run.
+**
+** Note- everyone really needs to call _PR_MD_SWITCH_CONTEXT (which calls
+** PR_Schedule() rather than calling PR_Schedule. Otherwise if there
+** is initialization required for switching from SWITCH_CONTEXT,
+** it will not get done!
+*/
+void _PR_Schedule(void)
+{
+ PRThread *thread, *me = _PR_MD_CURRENT_THREAD();
+ _PRCPU *cpu = _PR_MD_CURRENT_CPU();
+ PRIntn pri;
+ PRUint32 r;
+ PRCList *qp;
+ PRIntn priMin, priMax;
+#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
+ PRBool wakeup_cpus;
+#endif
+
+ /* Interrupts must be disabled */
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+ /* Since we are rescheduling, we no longer want to */
+ _PR_CLEAR_RESCHED_FLAG();
+
+ /*
+ ** Find highest priority thread to run. Bigger priority numbers are
+ ** higher priority threads
+ */
+ _PR_RUNQ_LOCK(cpu);
+ /*
+ * if we are in SuspendAll mode, can schedule only the thread
+ * that called PR_SuspendAll
+ *
+ * The thread may be ready to run now, after completing an I/O
+ * operation, for example
+ */
+ if ((thread = suspendAllThread) != 0) {
+ if ((!(thread->no_sched)) && (thread->state == _PR_RUNNABLE)) {
+ /* Pull thread off of its run queue */
+ _PR_DEL_RUNQ(thread);
+ _PR_RUNQ_UNLOCK(cpu);
+ goto found_thread;
+ } else {
+ thread = NULL;
+ _PR_RUNQ_UNLOCK(cpu);
+ goto idle_thread;
+ }
+ }
+ r = _PR_RUNQREADYMASK(cpu);
+ if (r==0) {
+ priMin = priMax = PR_PRIORITY_FIRST;
+ } else if (r == (1<<PR_PRIORITY_NORMAL) ) {
+ priMin = priMax = PR_PRIORITY_NORMAL;
+ } else {
+ priMin = PR_PRIORITY_FIRST;
+ priMax = PR_PRIORITY_LAST;
+ }
+ thread = NULL;
+ for (pri = priMax; pri >= priMin ; pri-- ) {
+ if (r & (1 << pri)) {
+ for (qp = _PR_RUNQ(cpu)[pri].next;
+ qp != &_PR_RUNQ(cpu)[pri];
+ qp = qp->next) {
+ thread = _PR_THREAD_PTR(qp);
+ /*
+ * skip non-schedulable threads
+ */
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+ if ((thread->no_sched) && (me != thread)) {
+ thread = NULL;
+ continue;
+ } else {
+ /* Pull thread off of its run queue */
+ _PR_DEL_RUNQ(thread);
+ _PR_RUNQ_UNLOCK(cpu);
+ goto found_thread;
+ }
+ }
+ }
+ thread = NULL;
+ }
+ _PR_RUNQ_UNLOCK(cpu);
+
+#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
+
+ wakeup_cpus = PR_FALSE;
+ _PR_CPU_LIST_LOCK();
+ for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) {
+ if (cpu != _PR_CPU_PTR(qp)) {
+ if ((thread = get_thread(_PR_CPU_PTR(qp), &wakeup_cpus))
+ != NULL) {
+ thread->cpu = cpu;
+ _PR_CPU_LIST_UNLOCK();
+ if (wakeup_cpus == PR_TRUE) {
+ _PR_MD_WAKEUP_CPUS();
+ }
+ goto found_thread;
+ }
+ }
+ }
+ _PR_CPU_LIST_UNLOCK();
+ if (wakeup_cpus == PR_TRUE) {
+ _PR_MD_WAKEUP_CPUS();
+ }
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+idle_thread:
+ /*
+ ** There are no threads to run. Switch to the idle thread
+ */
+ PR_LOG(_pr_sched_lm, PR_LOG_MAX, ("pausing"));
+ thread = _PR_MD_CURRENT_CPU()->idle_thread;
+
+found_thread:
+ PR_ASSERT((me == thread) || ((thread->state == _PR_RUNNABLE) &&
+ (!(thread->no_sched))));
+
+ /* Resume the thread */
+ PR_LOG(_pr_sched_lm, PR_LOG_MAX,
+ ("switching to %d[%p]", thread->id, thread));
+ PR_ASSERT(thread->state != _PR_RUNNING);
+ thread->state = _PR_RUNNING;
+
+ /* If we are on the runq, it just means that we went to sleep on some
+ * resource, and by the time we got here another real native thread had
+ * already given us the resource and put us back on the runqueue
+ */
+ PR_ASSERT(thread->cpu == _PR_MD_CURRENT_CPU());
+ if (thread != me) {
+ _PR_MD_RESTORE_CONTEXT(thread);
+ }
+#if 0
+ /* XXXMB; with setjmp/longjmp it is impossible to land here, but
+ * it is not with fibers... Is this a bad thing? I believe it is
+ * still safe.
+ */
+ PR_NOT_REACHED("impossible return from schedule");
+#endif
+}
+
+/*
+** Attaches a thread.
+** Does not set the _PR_MD_CURRENT_THREAD.
+** Does not specify the scope of the thread.
+*/
+static PRThread *
+_PR_AttachThread(PRThreadType type, PRThreadPriority priority,
+ PRThreadStack *stack)
+{
+ PRThread *thread;
+ char *mem;
+
+ if (priority > PR_PRIORITY_LAST) {
+ priority = PR_PRIORITY_LAST;
+ } else if (priority < PR_PRIORITY_FIRST) {
+ priority = PR_PRIORITY_FIRST;
+ }
+
+ mem = (char*) PR_CALLOC(sizeof(PRThread));
+ if (mem) {
+ thread = (PRThread*) mem;
+ thread->priority = priority;
+ thread->stack = stack;
+ thread->state = _PR_RUNNING;
+ PR_INIT_CLIST(&thread->lockList);
+ if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) {
+ PR_DELETE(thread);
+ return 0;
+ }
+
+ return thread;
+ }
+ return 0;
+}
+
+
+
+PR_IMPLEMENT(PRThread*)
+_PR_NativeCreateThread(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize,
+ PRUint32 flags)
+{
+ PRThread *thread;
+
+ thread = _PR_AttachThread(type, priority, NULL);
+
+ if (thread) {
+ PR_Lock(_pr_activeLock);
+ thread->flags = (flags | _PR_GLOBAL_SCOPE);
+ thread->id = ++_pr_utid;
+ if (type == PR_SYSTEM_THREAD) {
+ thread->flags |= _PR_SYSTEM;
+ _pr_systemActive++;
+ } else {
+ _pr_userActive++;
+ }
+ PR_Unlock(_pr_activeLock);
+
+ thread->stack = PR_NEWZAP(PRThreadStack);
+ if (!thread->stack) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto done;
+ }
+ thread->stack->stackSize = stackSize?stackSize:_MD_DEFAULT_STACK_SIZE;
+ thread->stack->thr = thread;
+ thread->startFunc = start;
+ thread->arg = arg;
+
+ /*
+ Set thread flags related to scope and joinable state. If joinable
+ thread, allocate a "termination" conidition variable.
+ */
+ if (state == PR_JOINABLE_THREAD) {
+ thread->term = PR_NewCondVar(_pr_terminationCVLock);
+ if (thread->term == NULL) {
+ PR_DELETE(thread->stack);
+ goto done;
+ }
+ }
+
+ thread->state = _PR_RUNNING;
+ if (_PR_MD_CREATE_THREAD(thread, _PR_NativeRunThread, priority,
+ scope,state,stackSize) == PR_SUCCESS) {
+ return thread;
+ }
+ if (thread->term) {
+ PR_DestroyCondVar(thread->term);
+ thread->term = NULL;
+ }
+ PR_DELETE(thread->stack);
+ }
+
+done:
+ if (thread) {
+ _PR_DecrActiveThreadCount(thread);
+ _PR_DestroyThread(thread);
+ }
+ return NULL;
+}
+
+/************************************************************************/
+
+PR_IMPLEMENT(PRThread*) _PR_CreateThread(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize,
+ PRUint32 flags)
+{
+ PRThread *me;
+ PRThread *thread = NULL;
+ PRThreadStack *stack;
+ char *top;
+ PRIntn is;
+ PRIntn native = 0;
+ PRIntn useRecycled = 0;
+ PRBool status;
+
+ /*
+ First, pin down the priority. Not all compilers catch passing out of
+ range enum here. If we let bad values thru, priority queues won't work.
+ */
+ if (priority > PR_PRIORITY_LAST) {
+ priority = PR_PRIORITY_LAST;
+ } else if (priority < PR_PRIORITY_FIRST) {
+ priority = PR_PRIORITY_FIRST;
+ }
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (! (flags & _PR_IDLE_THREAD)) {
+ me = _PR_MD_CURRENT_THREAD();
+ }
+
+#if defined(_PR_GLOBAL_THREADS_ONLY)
+ /*
+ * can create global threads only
+ */
+ if (scope == PR_LOCAL_THREAD) {
+ scope = PR_GLOBAL_THREAD;
+ }
+#endif
+
+ if (_native_threads_only) {
+ scope = PR_GLOBAL_THREAD;
+ }
+
+ native = (((scope == PR_GLOBAL_THREAD)|| (scope == PR_GLOBAL_BOUND_THREAD))
+ && _PR_IS_NATIVE_THREAD_SUPPORTED());
+
+ _PR_ADJUST_STACKSIZE(stackSize);
+
+ if (native) {
+ /*
+ * clear the IDLE_THREAD flag which applies to LOCAL
+ * threads only
+ */
+ flags &= ~_PR_IDLE_THREAD;
+ flags |= _PR_GLOBAL_SCOPE;
+ if (_PR_NUM_DEADNATIVE > 0) {
+ _PR_DEADQ_LOCK;
+
+ if (_PR_NUM_DEADNATIVE == 0) { /* Thread safe check */
+ _PR_DEADQ_UNLOCK;
+ } else {
+ thread = _PR_THREAD_PTR(_PR_DEADNATIVEQ.next);
+ PR_REMOVE_LINK(&thread->links);
+ _PR_DEC_DEADNATIVE;
+ _PR_DEADQ_UNLOCK;
+
+ _PR_InitializeRecycledThread(thread);
+ thread->startFunc = start;
+ thread->arg = arg;
+ thread->flags = (flags | _PR_GLOBAL_SCOPE);
+ if (type == PR_SYSTEM_THREAD)
+ {
+ thread->flags |= _PR_SYSTEM;
+ PR_ATOMIC_INCREMENT(&_pr_systemActive);
+ }
+ else {
+ PR_ATOMIC_INCREMENT(&_pr_userActive);
+ }
+
+ if (state == PR_JOINABLE_THREAD) {
+ if (!thread->term) {
+ thread->term = PR_NewCondVar(_pr_terminationCVLock);
+ }
+ }
+ else {
+ if(thread->term) {
+ PR_DestroyCondVar(thread->term);
+ thread->term = 0;
+ }
+ }
+
+ thread->priority = priority;
+ _PR_MD_SET_PRIORITY(&(thread->md), priority);
+ /* XXX what about stackSize? */
+ thread->state = _PR_RUNNING;
+ _PR_MD_WAKEUP_WAITER(thread);
+ return thread;
+ }
+ }
+ thread = _PR_NativeCreateThread(type, start, arg, priority,
+ scope, state, stackSize, flags);
+ } else {
+ if (_PR_NUM_DEADUSER > 0) {
+ _PR_DEADQ_LOCK;
+
+ if (_PR_NUM_DEADUSER == 0) { /* thread safe check */
+ _PR_DEADQ_UNLOCK;
+ } else {
+ PRCList *ptr;
+
+ /* Go down list checking for a recycled thread with a
+ * large enough stack. XXXMB - this has a bad degenerate case.
+ */
+ ptr = _PR_DEADUSERQ.next;
+ while( ptr != &_PR_DEADUSERQ ) {
+ thread = _PR_THREAD_PTR(ptr);
+ if ((thread->stack->stackSize >= stackSize) &&
+ (!thread->no_sched)) {
+ PR_REMOVE_LINK(&thread->links);
+ _PR_DEC_DEADUSER;
+ break;
+ } else {
+ ptr = ptr->next;
+ thread = NULL;
+ }
+ }
+
+ _PR_DEADQ_UNLOCK;
+
+ if (thread) {
+ _PR_InitializeRecycledThread(thread);
+ thread->startFunc = start;
+ thread->arg = arg;
+ thread->priority = priority;
+ if (state == PR_JOINABLE_THREAD) {
+ if (!thread->term) {
+ thread->term = PR_NewCondVar(_pr_terminationCVLock);
+ }
+ } else {
+ if(thread->term) {
+ PR_DestroyCondVar(thread->term);
+ thread->term = 0;
+ }
+ }
+ useRecycled++;
+ }
+ }
+ }
+ if (thread == NULL) {
+#ifndef HAVE_CUSTOM_USER_THREADS
+ stack = _PR_NewStack(stackSize);
+ if (!stack) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+
+ /* Allocate thread object and per-thread data off the top of the stack*/
+ top = stack->stackTop;
+#ifdef HAVE_STACK_GROWING_UP
+ thread = (PRThread*) top;
+ top = top + sizeof(PRThread);
+ /*
+ * Make stack 64-byte aligned
+ */
+ if ((PRUptrdiff)top & 0x3f) {
+ top = (char*)(((PRUptrdiff)top + 0x40) & ~0x3f);
+ }
+#else
+ top = top - sizeof(PRThread);
+ thread = (PRThread*) top;
+ /*
+ * Make stack 64-byte aligned
+ */
+ if ((PRUptrdiff)top & 0x3f) {
+ top = (char*)((PRUptrdiff)top & ~0x3f);
+ }
+#endif
+ stack->thr = thread;
+ memset(thread, 0, sizeof(PRThread));
+ thread->threadAllocatedOnStack = 1;
+#else
+ thread = _PR_MD_CREATE_USER_THREAD(stackSize, start, arg);
+ if (!thread) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ thread->threadAllocatedOnStack = 0;
+ stack = NULL;
+ top = NULL;
+#endif
+
+ /* Initialize thread */
+ thread->tpdLength = 0;
+ thread->privateData = NULL;
+ thread->stack = stack;
+ thread->priority = priority;
+ thread->startFunc = start;
+ thread->arg = arg;
+ PR_INIT_CLIST(&thread->lockList);
+
+ if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
+ if (thread->threadAllocatedOnStack == 1) {
+ _PR_FreeStack(thread->stack);
+ }
+ else {
+ PR_DELETE(thread);
+ }
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ return NULL;
+ }
+
+ if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) {
+ if (thread->threadAllocatedOnStack == 1) {
+ _PR_FreeStack(thread->stack);
+ }
+ else {
+ PR_DELETE(thread->privateData);
+ PR_DELETE(thread);
+ }
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ return NULL;
+ }
+
+ _PR_MD_INIT_CONTEXT(thread, top, _PR_UserRunThread, &status);
+
+ if (status == PR_FALSE) {
+ _PR_MD_FREE_LOCK(&thread->threadLock);
+ if (thread->threadAllocatedOnStack == 1) {
+ _PR_FreeStack(thread->stack);
+ }
+ else {
+ PR_DELETE(thread->privateData);
+ PR_DELETE(thread);
+ }
+ return NULL;
+ }
+
+ /*
+ Set thread flags related to scope and joinable state. If joinable
+ thread, allocate a "termination" condition variable.
+ */
+ if (state == PR_JOINABLE_THREAD) {
+ thread->term = PR_NewCondVar(_pr_terminationCVLock);
+ if (thread->term == NULL) {
+ _PR_MD_FREE_LOCK(&thread->threadLock);
+ if (thread->threadAllocatedOnStack == 1) {
+ _PR_FreeStack(thread->stack);
+ }
+ else {
+ PR_DELETE(thread->privateData);
+ PR_DELETE(thread);
+ }
+ return NULL;
+ }
+ }
+
+ }
+
+ /* Update thread type counter */
+ PR_Lock(_pr_activeLock);
+ thread->flags = flags;
+ thread->id = ++_pr_utid;
+ if (type == PR_SYSTEM_THREAD) {
+ thread->flags |= _PR_SYSTEM;
+ _pr_systemActive++;
+ } else {
+ _pr_userActive++;
+ }
+
+ /* Make thread runnable */
+ thread->state = _PR_RUNNABLE;
+ /*
+ * Add to list of active threads
+ */
+ PR_Unlock(_pr_activeLock);
+
+ if ((! (thread->flags & _PR_IDLE_THREAD)) && _PR_IS_NATIVE_THREAD(me) ) {
+ thread->cpu = _PR_GetPrimordialCPU();
+ }
+ else {
+ thread->cpu = _PR_MD_CURRENT_CPU();
+ }
+
+ PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));
+
+ if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ _PR_RUNQ_LOCK(thread->cpu);
+ _PR_ADD_RUNQ(thread, thread->cpu, priority);
+ _PR_RUNQ_UNLOCK(thread->cpu);
+ }
+
+ if (thread->flags & _PR_IDLE_THREAD) {
+ /*
+ ** If the creating thread is a kernel thread, we need to
+ ** awaken the user thread idle thread somehow; potentially
+ ** it could be sleeping in its idle loop, and we need to poke
+ ** it. To do so, wake the idle thread...
+ */
+ _PR_MD_WAKEUP_WAITER(NULL);
+ } else if (_PR_IS_NATIVE_THREAD(me)) {
+ _PR_MD_WAKEUP_WAITER(thread);
+ }
+ if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me) ) {
+ _PR_INTSON(is);
+ }
+ }
+
+ return thread;
+}
+
+PR_IMPLEMENT(PRThread*) PR_CreateThread(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ return _PR_CreateThread(type, start, arg, priority, scope, state,
+ stackSize, 0);
+}
+
+/*
+** Associate a thread object with an existing native thread.
+** "type" is the type of thread object to attach
+** "priority" is the priority to assign to the thread
+** "stack" defines the shape of the threads stack
+**
+** This can return NULL if some kind of error occurs, or if memory is
+** tight.
+**
+** This call is not normally needed unless you create your own native
+** thread. PR_Init does this automatically for the primordial thread.
+*/
+PRThread* _PRI_AttachThread(PRThreadType type,
+ PRThreadPriority priority, PRThreadStack *stack, PRUint32 flags)
+{
+ PRThread *thread;
+
+ if ((thread = _PR_MD_GET_ATTACHED_THREAD()) != NULL) {
+ return thread;
+ }
+ _PR_MD_SET_CURRENT_THREAD(NULL);
+
+ /* Clear out any state if this thread was attached before */
+ _PR_MD_SET_CURRENT_CPU(NULL);
+
+ thread = _PR_AttachThread(type, priority, stack);
+ if (thread) {
+ PRIntn is;
+
+ _PR_MD_SET_CURRENT_THREAD(thread);
+
+ thread->flags = flags | _PR_GLOBAL_SCOPE | _PR_ATTACHED;
+
+ if (!stack) {
+ thread->stack = PR_NEWZAP(PRThreadStack);
+ if (!thread->stack) {
+ _PR_DestroyThread(thread);
+ return NULL;
+ }
+ thread->stack->stackSize = _MD_DEFAULT_STACK_SIZE;
+ }
+ PR_INIT_CLIST(&thread->links);
+
+ if (_PR_MD_INIT_ATTACHED_THREAD(thread) == PR_FAILURE) {
+ PR_DELETE(thread->stack);
+ _PR_DestroyThread(thread);
+ return NULL;
+ }
+
+ _PR_MD_SET_CURRENT_CPU(NULL);
+
+ if (_PR_MD_CURRENT_CPU()) {
+ _PR_INTSOFF(is);
+ PR_Lock(_pr_activeLock);
+ }
+ if (type == PR_SYSTEM_THREAD) {
+ thread->flags |= _PR_SYSTEM;
+ _pr_systemActive++;
+ } else {
+ _pr_userActive++;
+ }
+ if (_PR_MD_CURRENT_CPU()) {
+ PR_Unlock(_pr_activeLock);
+ _PR_INTSON(is);
+ }
+ }
+ return thread;
+}
+
+PR_IMPLEMENT(PRThread*) PR_AttachThread(PRThreadType type,
+ PRThreadPriority priority, PRThreadStack *stack)
+{
+ return PR_GetCurrentThread();
+}
+
+PR_IMPLEMENT(void) PR_DetachThread(void)
+{
+ /*
+ * On Solaris, and Windows, foreign threads are detached when
+ * they terminate.
+ */
+#if !defined(WIN32) \
+ && !(defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY))
+ PRThread *me;
+ if (_pr_initialized) {
+ me = _PR_MD_GET_ATTACHED_THREAD();
+ if ((me != NULL) && (me->flags & _PR_ATTACHED)) {
+ _PRI_DetachThread();
+ }
+ }
+#endif
+}
+
+void _PRI_DetachThread(void)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me->flags & _PR_PRIMORDIAL) {
+ /*
+ * ignore, if primordial thread
+ */
+ return;
+ }
+ PR_ASSERT(me->flags & _PR_ATTACHED);
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me));
+ _PR_CleanupThread(me);
+ PR_DELETE(me->privateData);
+
+ _PR_DecrActiveThreadCount(me);
+
+ _PR_MD_CLEAN_THREAD(me);
+ _PR_MD_SET_CURRENT_THREAD(NULL);
+ if (!me->threadAllocatedOnStack) {
+ PR_DELETE(me->stack);
+ }
+ _PR_MD_FREE_LOCK(&me->threadLock);
+ PR_DELETE(me);
+}
+
+/*
+** Wait for thread termination:
+** "thread" is the target thread
+**
+** This can return PR_FAILURE if no joinable thread could be found
+** corresponding to the specified target thread.
+**
+** The calling thread is suspended until the target thread completes.
+** Several threads cannot wait for the same thread to complete; one thread
+** will complete successfully and others will terminate with an error PR_FAILURE.
+** The calling thread will not be blocked if the target thread has already
+** terminated.
+*/
+PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thread)
+{
+ PRIntn is;
+ PRCondVar *term;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ term = thread->term;
+ /* can't join a non-joinable thread */
+ if (term == NULL) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ goto ErrorExit;
+ }
+
+ /* multiple threads can't wait on the same joinable thread */
+ if (term->condQ.next != &term->condQ) {
+ goto ErrorExit;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+
+ /* wait for the target thread's termination cv invariant */
+ PR_Lock (_pr_terminationCVLock);
+ while (thread->state != _PR_JOIN_WAIT) {
+ (void) PR_WaitCondVar(term, PR_INTERVAL_NO_TIMEOUT);
+ }
+ (void) PR_Unlock (_pr_terminationCVLock);
+
+ /*
+ Remove target thread from global waiting to join Q; make it runnable
+ again and put it back on its run Q. When it gets scheduled later in
+ _PR_RunThread code, it will clean up its stack.
+ */
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ thread->state = _PR_RUNNABLE;
+ if ( !_PR_IS_NATIVE_THREAD(thread) ) {
+ _PR_THREAD_LOCK(thread);
+
+ _PR_MISCQ_LOCK(thread->cpu);
+ _PR_DEL_JOINQ(thread);
+ _PR_MISCQ_UNLOCK(thread->cpu);
+
+ _PR_AddThreadToRunQ(me, thread);
+ _PR_THREAD_UNLOCK(thread);
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+
+ _PR_MD_WAKEUP_WAITER(thread);
+
+ return PR_SUCCESS;
+
+ErrorExit:
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thread,
+ PRThreadPriority newPri)
+{
+
+ /*
+ First, pin down the priority. Not all compilers catch passing out of
+ range enum here. If we let bad values thru, priority queues won't work.
+ */
+ if ((PRIntn)newPri > (PRIntn)PR_PRIORITY_LAST) {
+ newPri = PR_PRIORITY_LAST;
+ } else if ((PRIntn)newPri < (PRIntn)PR_PRIORITY_FIRST) {
+ newPri = PR_PRIORITY_FIRST;
+ }
+
+ if ( _PR_IS_NATIVE_THREAD(thread) ) {
+ thread->priority = newPri;
+ _PR_MD_SET_PRIORITY(&(thread->md), newPri);
+ } else {
+ _PR_SetThreadPriority(thread, newPri);
+ }
+}
+
+PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char *name)
+{
+ PRThread *thread;
+ size_t nameLen;
+
+ if (!name) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ thread = PR_GetCurrentThread();
+ if (!thread) {
+ return PR_FAILURE;
+ }
+
+ PR_Free(thread->name);
+ nameLen = strlen(name);
+ thread->name = (char *)PR_Malloc(nameLen + 1);
+ if (!thread->name) {
+ return PR_FAILURE;
+ }
+ memcpy(thread->name, name, nameLen + 1);
+ _PR_MD_SET_CURRENT_THREAD_NAME(thread->name);
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(const char *) PR_GetThreadName(const PRThread *thread)
+{
+ if (!thread) {
+ return NULL;
+ }
+ return thread->name;
+}
+
+
+/*
+** This routine prevents all other threads from running. This call is needed by
+** the garbage collector.
+*/
+PR_IMPLEMENT(void) PR_SuspendAll(void)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRCList *qp;
+
+ /*
+ * Stop all user and native threads which are marked GC able.
+ */
+ PR_Lock(_pr_activeLock);
+ suspendAllOn = PR_TRUE;
+ suspendAllThread = _PR_MD_CURRENT_THREAD();
+ _PR_MD_BEGIN_SUSPEND_ALL();
+ for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
+ qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) {
+ if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
+ _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) {
+ _PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp));
+ PR_ASSERT((_PR_ACTIVE_THREAD_PTR(qp))->state != _PR_RUNNING);
+ }
+ }
+ for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
+ qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) {
+ if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
+ _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp)))
+ /* PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); */
+ {
+ _PR_MD_SUSPEND_THREAD(_PR_ACTIVE_THREAD_PTR(qp));
+ }
+ }
+ _PR_MD_END_SUSPEND_ALL();
+}
+
+/*
+** This routine unblocks all other threads that were suspended from running by
+** PR_SuspendAll(). This call is needed by the garbage collector.
+*/
+PR_IMPLEMENT(void) PR_ResumeAll(void)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRCList *qp;
+
+ /*
+ * Resume all user and native threads which are marked GC able.
+ */
+ _PR_MD_BEGIN_RESUME_ALL();
+ for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
+ qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) {
+ if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
+ _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) {
+ _PR_Resume(_PR_ACTIVE_THREAD_PTR(qp));
+ }
+ }
+ for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
+ qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) {
+ if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
+ _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) {
+ _PR_MD_RESUME_THREAD(_PR_ACTIVE_THREAD_PTR(qp));
+ }
+ }
+ _PR_MD_END_RESUME_ALL();
+ suspendAllThread = NULL;
+ suspendAllOn = PR_FALSE;
+ PR_Unlock(_pr_activeLock);
+}
+
+PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg)
+{
+ PRCList *qp, *qp_next;
+ PRIntn i = 0;
+ PRStatus rv = PR_SUCCESS;
+ PRThread* t;
+
+ /*
+ ** Currently Enumerate threads happen only with suspension and
+ ** pr_activeLock held
+ */
+ PR_ASSERT(suspendAllOn);
+
+ /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking
+ * qp->next after applying the function "func". In particular, "func"
+ * might remove the thread from the queue and put it into another one in
+ * which case qp->next no longer points to the next entry in the original
+ * queue.
+ *
+ * To get around this problem, we save qp->next in qp_next before applying
+ * "func" and use that saved value as the next value after applying "func".
+ */
+
+ /*
+ * Traverse the list of local and global threads
+ */
+ for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
+ qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp_next)
+ {
+ qp_next = qp->next;
+ t = _PR_ACTIVE_THREAD_PTR(qp);
+ if (_PR_IS_GCABLE_THREAD(t))
+ {
+ rv = (*func)(t, i, arg);
+ if (rv != PR_SUCCESS) {
+ return rv;
+ }
+ i++;
+ }
+ }
+ for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
+ qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp_next)
+ {
+ qp_next = qp->next;
+ t = _PR_ACTIVE_THREAD_PTR(qp);
+ if (_PR_IS_GCABLE_THREAD(t))
+ {
+ rv = (*func)(t, i, arg);
+ if (rv != PR_SUCCESS) {
+ return rv;
+ }
+ i++;
+ }
+ }
+ return rv;
+}
+
+/* FUNCTION: _PR_AddSleepQ
+** DESCRIPTION:
+** Adds a thread to the sleep/pauseQ.
+** RESTRICTIONS:
+** Caller must have the RUNQ lock.
+** Caller must be a user level thread
+*/
+PR_IMPLEMENT(void)
+_PR_AddSleepQ(PRThread *thread, PRIntervalTime timeout)
+{
+ _PRCPU *cpu = thread->cpu;
+
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ /* append the thread to the global pause Q */
+ PR_APPEND_LINK(&thread->links, &_PR_PAUSEQ(thread->cpu));
+ thread->flags |= _PR_ON_PAUSEQ;
+ } else {
+ PRIntervalTime sleep;
+ PRCList *q;
+ PRThread *t;
+
+ /* sort onto global sleepQ */
+ sleep = timeout;
+
+ /* Check if we are longest timeout */
+ if (timeout >= _PR_SLEEPQMAX(cpu)) {
+ PR_INSERT_BEFORE(&thread->links, &_PR_SLEEPQ(cpu));
+ thread->sleep = timeout - _PR_SLEEPQMAX(cpu);
+ _PR_SLEEPQMAX(cpu) = timeout;
+ } else {
+ /* Sort thread into global sleepQ at appropriate point */
+ q = _PR_SLEEPQ(cpu).next;
+
+ /* Now scan the list for where to insert this entry */
+ while (q != &_PR_SLEEPQ(cpu)) {
+ t = _PR_THREAD_PTR(q);
+ if (sleep < t->sleep) {
+ /* Found sleeper to insert in front of */
+ break;
+ }
+ sleep -= t->sleep;
+ q = q->next;
+ }
+ thread->sleep = sleep;
+ PR_INSERT_BEFORE(&thread->links, q);
+
+ /*
+ ** Subtract our sleep time from the sleeper that follows us (there
+ ** must be one) so that they remain relative to us.
+ */
+ PR_ASSERT (thread->links.next != &_PR_SLEEPQ(cpu));
+
+ t = _PR_THREAD_PTR(thread->links.next);
+ PR_ASSERT(_PR_THREAD_PTR(t->links.prev) == thread);
+ t->sleep -= sleep;
+ }
+
+ thread->flags |= _PR_ON_SLEEPQ;
+ }
+}
+
+/* FUNCTION: _PR_DelSleepQ
+** DESCRIPTION:
+** Removes a thread from the sleep/pauseQ.
+** INPUTS:
+** If propogate_time is true, then the thread following the deleted
+** thread will be get the time from the deleted thread. This is used
+** when deleting a sleeper that has not timed out.
+** RESTRICTIONS:
+** Caller must have the RUNQ lock.
+** Caller must be a user level thread
+*/
+PR_IMPLEMENT(void)
+_PR_DelSleepQ(PRThread *thread, PRBool propogate_time)
+{
+ _PRCPU *cpu = thread->cpu;
+
+ /* Remove from pauseQ/sleepQ */
+ if (thread->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
+ if (thread->flags & _PR_ON_SLEEPQ) {
+ PRCList *q = thread->links.next;
+ if (q != &_PR_SLEEPQ(cpu)) {
+ if (propogate_time == PR_TRUE) {
+ PRThread *after = _PR_THREAD_PTR(q);
+ after->sleep += thread->sleep;
+ } else {
+ _PR_SLEEPQMAX(cpu) -= thread->sleep;
+ }
+ } else {
+ /* Check if prev is the beggining of the list; if so,
+ * we are the only element on the list.
+ */
+ if (thread->links.prev != &_PR_SLEEPQ(cpu)) {
+ _PR_SLEEPQMAX(cpu) -= thread->sleep;
+ }
+ else {
+ _PR_SLEEPQMAX(cpu) = 0;
+ }
+ }
+ thread->flags &= ~_PR_ON_SLEEPQ;
+ } else {
+ thread->flags &= ~_PR_ON_PAUSEQ;
+ }
+ PR_REMOVE_LINK(&thread->links);
+ } else {
+ PR_ASSERT(0);
+ }
+}
+
+void
+_PR_AddThreadToRunQ(
+ PRThread *me, /* the current thread */
+ PRThread *thread) /* the local thread to be added to a run queue */
+{
+ PRThreadPriority pri = thread->priority;
+ _PRCPU *cpu = thread->cpu;
+
+ PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));
+
+#if defined(WINNT)
+ /*
+ * On NT, we can only reliably know that the current CPU
+ * is not idle. We add the awakened thread to the run
+ * queue of its CPU if its CPU is the current CPU.
+ * For any other CPU, we don't really know whether it
+ * is busy or idle. So in all other cases, we just
+ * "post" the awakened thread to the IO completion port
+ * for the next idle CPU to execute (this is done in
+ * _PR_MD_WAKEUP_WAITER).
+ * Threads with a suspended I/O operation remain bound to
+ * the same cpu until I/O is cancelled
+ *
+ * NOTE: the boolean expression below must be the exact
+ * opposite of the corresponding boolean expression in
+ * _PR_MD_WAKEUP_WAITER.
+ */
+ if ((!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) ||
+ (thread->md.thr_bound_cpu)) {
+ PR_ASSERT(!thread->md.thr_bound_cpu ||
+ (thread->md.thr_bound_cpu == cpu));
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thread, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ }
+#else
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thread, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ if (!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) {
+ if (pri > me->priority) {
+ _PR_SET_RESCHED_FLAG();
+ }
+ }
+#endif
+}
diff --git a/nsprpub/pr/src/threads/prcmon.c b/nsprpub/pr/src/threads/prcmon.c
new file mode 100644
index 0000000000..c1d354ced5
--- /dev/null
+++ b/nsprpub/pr/src/threads/prcmon.c
@@ -0,0 +1,449 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+
+/* Lock used to lock the monitor cache */
+#ifdef _PR_NO_PREEMPT
+#define _PR_NEW_LOCK_MCACHE()
+#define _PR_DESTROY_LOCK_MCACHE()
+#define _PR_LOCK_MCACHE()
+#define _PR_UNLOCK_MCACHE()
+#else
+#ifdef _PR_LOCAL_THREADS_ONLY
+#define _PR_NEW_LOCK_MCACHE()
+#define _PR_DESTROY_LOCK_MCACHE()
+#define _PR_LOCK_MCACHE() { PRIntn _is; _PR_INTSOFF(_is)
+#define _PR_UNLOCK_MCACHE() _PR_INTSON(_is); }
+#else
+PRLock *_pr_mcacheLock;
+#define _PR_NEW_LOCK_MCACHE() (_pr_mcacheLock = PR_NewLock())
+#define _PR_DESTROY_LOCK_MCACHE() \
+ PR_BEGIN_MACRO \
+ if (_pr_mcacheLock) { \
+ PR_DestroyLock(_pr_mcacheLock); \
+ _pr_mcacheLock = NULL; \
+ } \
+ PR_END_MACRO
+#define _PR_LOCK_MCACHE() PR_Lock(_pr_mcacheLock)
+#define _PR_UNLOCK_MCACHE() PR_Unlock(_pr_mcacheLock)
+#endif
+#endif
+
+/************************************************************************/
+
+typedef struct MonitorCacheEntryStr MonitorCacheEntry;
+
+struct MonitorCacheEntryStr {
+ MonitorCacheEntry* next;
+ void* address;
+ PRMonitor* mon;
+ long cacheEntryCount;
+};
+
+/*
+** An array of MonitorCacheEntry's, plus a pointer to link these
+** arrays together.
+*/
+
+typedef struct MonitorCacheEntryBlockStr MonitorCacheEntryBlock;
+
+struct MonitorCacheEntryBlockStr {
+ MonitorCacheEntryBlock* next;
+ MonitorCacheEntry entries[1];
+};
+
+static PRUint32 hash_mask;
+static PRUintn num_hash_buckets;
+static PRUintn num_hash_buckets_log2;
+static MonitorCacheEntry **hash_buckets;
+static MonitorCacheEntry *free_entries;
+static PRUintn num_free_entries;
+static PRBool expanding;
+static MonitorCacheEntryBlock *mcache_blocks;
+
+static void (*OnMonitorRecycle)(void *address);
+
+#define HASH(address) \
+ ((PRUint32) ( ((PRUptrdiff)(address) >> 2) ^ \
+ ((PRUptrdiff)(address) >> 10) ) \
+ & hash_mask)
+
+/*
+** Expand the monitor cache. This grows the hash buckets and allocates a
+** new chunk of cache entries and throws them on the free list. We keep
+** as many hash buckets as there are entries.
+**
+** Because we call malloc and malloc may need the monitor cache, we must
+** ensure that there are several free monitor cache entries available for
+** malloc to get. FREE_THRESHOLD is used to prevent monitor cache
+** starvation during monitor cache expansion.
+*/
+
+#define FREE_THRESHOLD 5
+
+static PRStatus ExpandMonitorCache(PRUintn new_size_log2)
+{
+ MonitorCacheEntry **old_hash_buckets, *p;
+ PRUintn i, entries, old_num_hash_buckets, added;
+ MonitorCacheEntry **new_hash_buckets;
+ MonitorCacheEntryBlock *new_block;
+
+ entries = 1L << new_size_log2;
+
+ /*
+ ** Expand the monitor-cache-entry free list
+ */
+ new_block = (MonitorCacheEntryBlock*)
+ PR_CALLOC(sizeof(MonitorCacheEntryBlock)
+ + (entries - 1) * sizeof(MonitorCacheEntry));
+ if (NULL == new_block) {
+ return PR_FAILURE;
+ }
+
+ /*
+ ** Allocate system monitors for the new monitor cache entries. If we
+ ** run out of system monitors, break out of the loop.
+ */
+ for (i = 0, p = new_block->entries; i < entries; i++, p++) {
+ p->mon = PR_NewMonitor();
+ if (!p->mon) {
+ break;
+ }
+ }
+ added = i;
+ if (added != entries) {
+ MonitorCacheEntryBlock *realloc_block;
+
+ if (added == 0) {
+ /* Totally out of system monitors. Lossage abounds */
+ PR_DELETE(new_block);
+ return PR_FAILURE;
+ }
+
+ /*
+ ** We were able to allocate some of the system monitors. Use
+ ** realloc to shrink down the new_block memory. If that fails,
+ ** carry on with the too-large new_block.
+ */
+ realloc_block = (MonitorCacheEntryBlock*)
+ PR_REALLOC(new_block, sizeof(MonitorCacheEntryBlock)
+ + (added - 1) * sizeof(MonitorCacheEntry));
+ if (realloc_block) {
+ new_block = realloc_block;
+ }
+ }
+
+ /*
+ ** Now that we have allocated all of the system monitors, build up
+ ** the new free list. We can just update the free_list because we own
+ ** the mcache-lock and we aren't calling anyone who might want to use
+ ** it.
+ */
+ for (i = 0, p = new_block->entries; i < added - 1; i++, p++) {
+ p->next = p + 1;
+ }
+ p->next = free_entries;
+ free_entries = new_block->entries;
+ num_free_entries += added;
+ new_block->next = mcache_blocks;
+ mcache_blocks = new_block;
+
+ /* Try to expand the hash table */
+ new_hash_buckets = (MonitorCacheEntry**)
+ PR_CALLOC(entries * sizeof(MonitorCacheEntry*));
+ if (NULL == new_hash_buckets) {
+ /*
+ ** Partial lossage. In this situation we don't get any more hash
+ ** buckets, which just means that the table lookups will take
+ ** longer. This is bad, but not fatal
+ */
+ PR_LOG(_pr_cmon_lm, PR_LOG_WARNING,
+ ("unable to grow monitor cache hash buckets"));
+ return PR_SUCCESS;
+ }
+
+ /*
+ ** Compute new hash mask value. This value is used to mask an address
+ ** until it's bits are in the right spot for indexing into the hash
+ ** table.
+ */
+ hash_mask = entries - 1;
+
+ /*
+ ** Expand the hash table. We have to rehash everything in the old
+ ** table into the new table.
+ */
+ old_hash_buckets = hash_buckets;
+ old_num_hash_buckets = num_hash_buckets;
+ for (i = 0; i < old_num_hash_buckets; i++) {
+ p = old_hash_buckets[i];
+ while (p) {
+ MonitorCacheEntry *next = p->next;
+
+ /* Hash based on new table size, and then put p in the new table */
+ PRUintn hash = HASH(p->address);
+ p->next = new_hash_buckets[hash];
+ new_hash_buckets[hash] = p;
+
+ p = next;
+ }
+ }
+
+ /*
+ ** Switch over to new hash table and THEN call free of the old
+ ** table. Since free might re-enter _pr_mcache_lock, things would
+ ** break terribly if it used the old hash table.
+ */
+ hash_buckets = new_hash_buckets;
+ num_hash_buckets = entries;
+ num_hash_buckets_log2 = new_size_log2;
+ PR_DELETE(old_hash_buckets);
+
+ PR_LOG(_pr_cmon_lm, PR_LOG_NOTICE,
+ ("expanded monitor cache to %d (buckets %d)",
+ num_free_entries, entries));
+
+ return PR_SUCCESS;
+} /* ExpandMonitorCache */
+
+/*
+** Lookup a monitor cache entry by address. Return a pointer to the
+** pointer to the monitor cache entry on success, null on failure.
+*/
+static MonitorCacheEntry **LookupMonitorCacheEntry(void *address)
+{
+ PRUintn hash;
+ MonitorCacheEntry **pp, *p;
+
+ hash = HASH(address);
+ pp = hash_buckets + hash;
+ while ((p = *pp) != 0) {
+ if (p->address == address) {
+ if (p->cacheEntryCount > 0) {
+ return pp;
+ }
+ return NULL;
+ }
+ pp = &p->next;
+ }
+ return NULL;
+}
+
+/*
+** Try to create a new cached monitor. If it's already in the cache,
+** great - return it. Otherwise get a new free cache entry and set it
+** up. If the cache free space is getting low, expand the cache.
+*/
+static PRMonitor *CreateMonitor(void *address)
+{
+ PRUintn hash;
+ MonitorCacheEntry **pp, *p;
+
+ hash = HASH(address);
+ pp = hash_buckets + hash;
+ while ((p = *pp) != 0) {
+ if (p->address == address) {
+ goto gotit;
+ }
+
+ pp = &p->next;
+ }
+
+ /* Expand the monitor cache if we have run out of free slots in the table */
+ if (num_free_entries < FREE_THRESHOLD) {
+ /* Expand monitor cache */
+
+ /*
+ ** This function is called with the lock held. So what's the 'expanding'
+ ** boolean all about? Seems a bit redundant.
+ */
+ if (!expanding) {
+ PRStatus rv;
+
+ expanding = PR_TRUE;
+ rv = ExpandMonitorCache(num_hash_buckets_log2 + 1);
+ expanding = PR_FALSE;
+ if (PR_FAILURE == rv) {
+ return NULL;
+ }
+
+ /* redo the hash because it'll be different now */
+ hash = HASH(address);
+ } else {
+ /*
+ ** We are in process of expanding and we need a cache
+ ** monitor. Make sure we have enough!
+ */
+ PR_ASSERT(num_free_entries > 0);
+ }
+ }
+
+ /* Make a new monitor */
+ p = free_entries;
+ free_entries = p->next;
+ num_free_entries--;
+ if (OnMonitorRecycle && p->address) {
+ OnMonitorRecycle(p->address);
+ }
+ p->address = address;
+ p->next = hash_buckets[hash];
+ hash_buckets[hash] = p;
+ PR_ASSERT(p->cacheEntryCount == 0);
+
+gotit:
+ p->cacheEntryCount++;
+ return p->mon;
+}
+
+/*
+** Initialize the monitor cache
+*/
+void _PR_InitCMon(void)
+{
+ _PR_NEW_LOCK_MCACHE();
+ ExpandMonitorCache(3);
+}
+
+/*
+** Destroy the monitor cache
+*/
+void _PR_CleanupCMon(void)
+{
+ _PR_DESTROY_LOCK_MCACHE();
+
+ while (free_entries) {
+ PR_DestroyMonitor(free_entries->mon);
+ free_entries = free_entries->next;
+ }
+ num_free_entries = 0;
+
+ while (mcache_blocks) {
+ MonitorCacheEntryBlock *block;
+
+ block = mcache_blocks;
+ mcache_blocks = block->next;
+ PR_DELETE(block);
+ }
+
+ PR_DELETE(hash_buckets);
+ hash_mask = 0;
+ num_hash_buckets = 0;
+ num_hash_buckets_log2 = 0;
+
+ expanding = PR_FALSE;
+ OnMonitorRecycle = NULL;
+}
+
+/*
+** Create monitor for address. Don't enter the monitor while we have the
+** mcache locked because we might block!
+*/
+PR_IMPLEMENT(PRMonitor*) PR_CEnterMonitor(void *address)
+{
+ PRMonitor *mon;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ _PR_LOCK_MCACHE();
+ mon = CreateMonitor(address);
+ _PR_UNLOCK_MCACHE();
+
+ if (!mon) {
+ return NULL;
+ }
+
+ PR_EnterMonitor(mon);
+ return mon;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CExitMonitor(void *address)
+{
+ MonitorCacheEntry **pp, *p;
+ PRStatus status = PR_SUCCESS;
+
+ _PR_LOCK_MCACHE();
+ pp = LookupMonitorCacheEntry(address);
+ if (pp != NULL) {
+ p = *pp;
+ if (--p->cacheEntryCount == 0) {
+ /*
+ ** Nobody is using the system monitor. Put it on the cached free
+ ** list. We are safe from somebody trying to use it because we
+ ** have the mcache locked.
+ */
+ p->address = 0; /* defensive move */
+ *pp = p->next; /* unlink from hash_buckets */
+ p->next = free_entries; /* link into free list */
+ free_entries = p;
+ num_free_entries++; /* count it as free */
+ }
+ status = PR_ExitMonitor(p->mon);
+ } else {
+ status = PR_FAILURE;
+ }
+ _PR_UNLOCK_MCACHE();
+
+ return status;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CWait(void *address, PRIntervalTime ticks)
+{
+ MonitorCacheEntry **pp;
+ PRMonitor *mon;
+
+ _PR_LOCK_MCACHE();
+ pp = LookupMonitorCacheEntry(address);
+ mon = pp ? ((*pp)->mon) : NULL;
+ _PR_UNLOCK_MCACHE();
+
+ if (mon == NULL) {
+ return PR_FAILURE;
+ }
+ return PR_Wait(mon, ticks);
+}
+
+PR_IMPLEMENT(PRStatus) PR_CNotify(void *address)
+{
+ MonitorCacheEntry **pp;
+ PRMonitor *mon;
+
+ _PR_LOCK_MCACHE();
+ pp = LookupMonitorCacheEntry(address);
+ mon = pp ? ((*pp)->mon) : NULL;
+ _PR_UNLOCK_MCACHE();
+
+ if (mon == NULL) {
+ return PR_FAILURE;
+ }
+ return PR_Notify(mon);
+}
+
+PR_IMPLEMENT(PRStatus) PR_CNotifyAll(void *address)
+{
+ MonitorCacheEntry **pp;
+ PRMonitor *mon;
+
+ _PR_LOCK_MCACHE();
+ pp = LookupMonitorCacheEntry(address);
+ mon = pp ? ((*pp)->mon) : NULL;
+ _PR_UNLOCK_MCACHE();
+
+ if (mon == NULL) {
+ return PR_FAILURE;
+ }
+ return PR_NotifyAll(mon);
+}
+
+PR_IMPLEMENT(void)
+PR_CSetOnMonitorRecycle(void (*callback)(void *address))
+{
+ OnMonitorRecycle = callback;
+}
diff --git a/nsprpub/pr/src/threads/prcthr.c b/nsprpub/pr/src/threads/prcthr.c
new file mode 100644
index 0000000000..e7044ed531
--- /dev/null
+++ b/nsprpub/pr/src/threads/prcthr.c
@@ -0,0 +1,428 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#if defined(WIN95)
+/*
+** Some local variables report warnings on Win95 because the code paths
+** using them are conditioned on HAVE_CUSTOME_USER_THREADS.
+** The pragma suppresses the warning.
+**
+*/
+#pragma warning(disable : 4101)
+#endif
+
+
+extern PRLock *_pr_sleeplock; /* allocated and initialized in prinit */
+/*
+** Routines common to both native and user threads.
+**
+**
+** Clean up a thread object, releasing all of the attached data. Do not
+** free the object itself (it may not have been malloc'd)
+*/
+void _PR_CleanupThread(PRThread *thread)
+{
+ /* Free up per-thread-data */
+ _PR_DestroyThreadPrivate(thread);
+
+ /* Free any thread dump procs */
+ if (thread->dumpArg) {
+ PR_DELETE(thread->dumpArg);
+ }
+ thread->dump = 0;
+
+ PR_DELETE(thread->name);
+ PR_DELETE(thread->errorString);
+ thread->errorStringSize = 0;
+ thread->errorStringLength = 0;
+ thread->environment = NULL;
+}
+
+PR_IMPLEMENT(PRStatus) PR_Yield()
+{
+ static PRBool warning = PR_TRUE;
+ if (warning) warning = _PR_Obsolete(
+ "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)");
+ return (PR_Sleep(PR_INTERVAL_NO_WAIT));
+}
+
+/*
+** Make the current thread sleep until "timeout" ticks amount of time
+** has expired. If "timeout" is PR_INTERVAL_NO_WAIT then the call is
+** equivalent to a yield. Waiting for an infinite amount of time is
+** allowed in the expectation that another thread will interrupt().
+**
+** A single lock is used for all threads calling sleep. Each caller
+** does get its own condition variable since each is expected to have
+** a unique 'timeout'.
+*/
+PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime timeout)
+{
+ PRStatus rv = PR_SUCCESS;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (PR_INTERVAL_NO_WAIT == timeout)
+ {
+ /*
+ ** This is a simple yield, nothing more, nothing less.
+ */
+ PRIntn is;
+ PRThread *me = PR_GetCurrentThread();
+ PRUintn pri = me->priority;
+ _PRCPU *cpu = _PR_MD_CURRENT_CPU();
+
+ if ( _PR_IS_NATIVE_THREAD(me) ) {
+ _PR_MD_YIELD();
+ }
+ else
+ {
+ _PR_INTSOFF(is);
+ _PR_RUNQ_LOCK(cpu);
+ if (_PR_RUNQREADYMASK(cpu) >> pri) {
+ me->cpu = cpu;
+ me->state = _PR_RUNNABLE;
+ _PR_ADD_RUNQ(me, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+
+ PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("PR_Yield: yielding"));
+ _PR_MD_SWITCH_CONTEXT(me);
+ PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("PR_Yield: done"));
+
+ _PR_FAST_INTSON(is);
+ }
+ else
+ {
+ _PR_RUNQ_UNLOCK(cpu);
+ _PR_INTSON(is);
+ }
+ }
+ }
+ else
+ {
+ /*
+ ** This is waiting for some finite period of time.
+ ** A thread in this state is interruptible (PR_Interrupt()),
+ ** but the lock and cvar used are local to the implementation
+ ** and not visible to the caller, therefore not notifiable.
+ */
+ PRCondVar *cv;
+ PRIntervalTime timein;
+
+ timein = PR_IntervalNow();
+ cv = PR_NewCondVar(_pr_sleeplock);
+ PR_ASSERT(cv != NULL);
+ PR_Lock(_pr_sleeplock);
+ do
+ {
+ PRIntervalTime delta = PR_IntervalNow() - timein;
+ if (delta > timeout) {
+ break;
+ }
+ rv = PR_WaitCondVar(cv, timeout - delta);
+ } while (rv == PR_SUCCESS);
+ PR_Unlock(_pr_sleeplock);
+ PR_DestroyCondVar(cv);
+ }
+ return rv;
+}
+
+PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thread)
+{
+ return thread->id;
+}
+
+PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thread)
+{
+ return (PRThreadPriority) thread->priority;
+}
+
+PR_IMPLEMENT(PRThread *) PR_GetCurrentThread()
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ return _PR_MD_CURRENT_THREAD();
+}
+
+/*
+** Set the interrupt flag for a thread. The thread will be unable to
+** block in i/o functions when this happens. Also, any PR_Wait's in
+** progress will be undone. The interrupt remains in force until
+** PR_ClearInterrupt is called.
+*/
+PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thread)
+{
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ PRCondVar *victim;
+
+ _PR_THREAD_LOCK(thread);
+ thread->flags |= _PR_INTERRUPT;
+ victim = thread->wait.cvar;
+ _PR_THREAD_UNLOCK(thread);
+ if ((NULL != victim) && (!(thread->flags & _PR_INTERRUPT_BLOCKED))) {
+ int haveLock = (victim->lock->owner == _PR_MD_CURRENT_THREAD());
+
+ if (!haveLock) {
+ PR_Lock(victim->lock);
+ }
+ PR_NotifyAllCondVar(victim);
+ if (!haveLock) {
+ PR_Unlock(victim->lock);
+ }
+ }
+ return PR_SUCCESS;
+#else /* ! _PR_GLOBAL_THREADS_ONLY */
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+
+ _PR_THREAD_LOCK(thread);
+ thread->flags |= _PR_INTERRUPT;
+ switch (thread->state) {
+ case _PR_COND_WAIT:
+ /*
+ * call is made with thread locked;
+ * on return lock is released
+ */
+ if (!(thread->flags & _PR_INTERRUPT_BLOCKED)) {
+ _PR_NotifyLockedThread(thread);
+ }
+ break;
+ case _PR_IO_WAIT:
+ /*
+ * Need to hold the thread lock when calling
+ * _PR_Unblock_IO_Wait(). On return lock is
+ * released.
+ */
+#if defined(XP_UNIX) || defined(WINNT) || defined(WIN16)
+ if (!(thread->flags & _PR_INTERRUPT_BLOCKED)) {
+ _PR_Unblock_IO_Wait(thread);
+ }
+#else
+ _PR_THREAD_UNLOCK(thread);
+#endif
+ break;
+ case _PR_RUNNING:
+ case _PR_RUNNABLE:
+ case _PR_LOCK_WAIT:
+ default:
+ _PR_THREAD_UNLOCK(thread);
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+ return PR_SUCCESS;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/*
+** Clear the interrupt flag for self.
+*/
+PR_IMPLEMENT(void) PR_ClearInterrupt()
+{
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_THREAD_LOCK(me);
+ me->flags &= ~_PR_INTERRUPT;
+ _PR_THREAD_UNLOCK(me);
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+}
+
+PR_IMPLEMENT(void) PR_BlockInterrupt()
+{
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_THREAD_LOCK(me);
+ _PR_THREAD_BLOCK_INTERRUPT(me);
+ _PR_THREAD_UNLOCK(me);
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+} /* PR_BlockInterrupt */
+
+PR_IMPLEMENT(void) PR_UnblockInterrupt()
+{
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSOFF(is);
+ }
+ _PR_THREAD_LOCK(me);
+ _PR_THREAD_UNBLOCK_INTERRUPT(me);
+ _PR_THREAD_UNLOCK(me);
+ if ( !_PR_IS_NATIVE_THREAD(me)) {
+ _PR_INTSON(is);
+ }
+} /* PR_UnblockInterrupt */
+
+/*
+** Return the thread stack pointer of the given thread.
+*/
+PR_IMPLEMENT(void *) PR_GetSP(PRThread *thread)
+{
+ return (void *)_PR_MD_GET_SP(thread);
+}
+
+PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thread)
+{
+ return thread->environment;
+}
+
+PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thread, void *env)
+{
+ thread->environment = env;
+}
+
+
+PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask)
+{
+#ifdef HAVE_THREAD_AFFINITY
+ return _PR_MD_GETTHREADAFFINITYMASK(thread, mask);
+#else
+ return 0;
+#endif
+}
+
+PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask )
+{
+#ifdef HAVE_THREAD_AFFINITY
+ return _PR_MD_SETTHREADAFFINITYMASK(thread, mask);
+#else
+ return 0;
+#endif
+}
+
+/* This call is thread unsafe if another thread is calling SetConcurrency()
+ */
+PR_IMPLEMENT(PRInt32) PR_SetCPUAffinityMask(PRUint32 mask)
+{
+#ifdef HAVE_THREAD_AFFINITY
+ PRCList *qp;
+ extern PRUint32 _pr_cpu_affinity_mask;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ _pr_cpu_affinity_mask = mask;
+
+ qp = _PR_CPUQ().next;
+ while(qp != &_PR_CPUQ()) {
+ _PRCPU *cpu;
+
+ cpu = _PR_CPU_PTR(qp);
+ PR_SetThreadAffinityMask(cpu->thread, mask);
+
+ qp = qp->next;
+ }
+#endif
+
+ return 0;
+}
+
+PRUint32 _pr_recycleThreads = 0;
+PR_IMPLEMENT(void) PR_SetThreadRecycleMode(PRUint32 count)
+{
+ _pr_recycleThreads = count;
+}
+
+PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ return _PR_CreateThread(type, start, arg, priority, scope, state,
+ stackSize, _PR_GCABLE_THREAD);
+}
+
+#ifdef SOLARIS
+PR_IMPLEMENT(PRThread*) PR_CreateThreadBound(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRUintn priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ return _PR_CreateThread(type, start, arg, priority, scope, state,
+ stackSize, _PR_BOUND_THREAD);
+}
+#endif
+
+
+PR_IMPLEMENT(PRThread*) PR_AttachThreadGCAble(
+ PRThreadType type, PRThreadPriority priority, PRThreadStack *stack)
+{
+ /* $$$$ not sure how to finese this one */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+PR_IMPLEMENT(void) PR_SetThreadGCAble()
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_Lock(_pr_activeLock);
+ _PR_MD_CURRENT_THREAD()->flags |= _PR_GCABLE_THREAD;
+ PR_Unlock(_pr_activeLock);
+}
+
+PR_IMPLEMENT(void) PR_ClearThreadGCAble()
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_Lock(_pr_activeLock);
+ _PR_MD_CURRENT_THREAD()->flags &= (~_PR_GCABLE_THREAD);
+ PR_Unlock(_pr_activeLock);
+}
+
+PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thread)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ return (thread->flags & _PR_BOUND_THREAD) ? PR_GLOBAL_BOUND_THREAD :
+ PR_GLOBAL_THREAD;
+ } else {
+ return PR_LOCAL_THREAD;
+ }
+}
+
+PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thread)
+{
+ return (thread->flags & _PR_SYSTEM) ? PR_SYSTEM_THREAD : PR_USER_THREAD;
+}
+
+PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thread)
+{
+ return (NULL == thread->term) ? PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD;
+} /* PR_GetThreadState */
diff --git a/nsprpub/pr/src/threads/prdump.c b/nsprpub/pr/src/threads/prdump.c
new file mode 100644
index 0000000000..04d842047c
--- /dev/null
+++ b/nsprpub/pr/src/threads/prdump.c
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#if defined(WIN95)
+/*
+** Some local variables report warnings on Win95 because the code paths
+** using them are conditioned on HAVE_CUSTOME_USER_THREADS.
+** The pragma suppresses the warning.
+**
+*/
+#pragma warning(disable : 4101)
+#endif
+
+/* XXX use unbuffered nspr stdio */
+
+PRFileDesc *_pr_dumpOut;
+
+PRUint32 _PR_DumpPrintf(PRFileDesc *fd, const char *fmt, ...)
+{
+ char buf[100];
+ PRUint32 nb;
+ va_list ap;
+
+ va_start(ap, fmt);
+ nb = PR_vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ PR_Write(fd, buf, nb);
+
+ return nb;
+}
+
+void _PR_DumpThread(PRFileDesc *fd, PRThread *thread)
+{
+
+#ifndef _PR_GLOBAL_THREADS_ONLY
+ _PR_DumpPrintf(fd, "%05d[%08p] pri=%2d flags=0x%02x",
+ thread->id, thread, thread->priority, thread->flags);
+ switch (thread->state) {
+ case _PR_RUNNABLE:
+ case _PR_RUNNING:
+ break;
+ case _PR_LOCK_WAIT:
+ _PR_DumpPrintf(fd, " lock=%p", thread->wait.lock);
+ break;
+ case _PR_COND_WAIT:
+ _PR_DumpPrintf(fd, " condvar=%p sleep=%lldms",
+ thread->wait.cvar, thread->sleep);
+ break;
+ case _PR_SUSPENDED:
+ _PR_DumpPrintf(fd, " suspended");
+ break;
+ }
+ PR_Write(fd, "\n", 1);
+#endif
+
+ /* Now call dump routine */
+ if (thread->dump) {
+ thread->dump(fd, thread, thread->dumpArg);
+ }
+}
+
+static void DumpThreadQueue(PRFileDesc *fd, PRCList *list)
+{
+#ifndef _PR_GLOBAL_THREADS_ONLY
+ PRCList *q;
+
+ q = list->next;
+ while (q != list) {
+ PRThread *t = _PR_THREAD_PTR(q);
+ _PR_DumpThread(fd, t);
+ q = q->next;
+ }
+#endif
+}
+
+void _PR_DumpThreads(PRFileDesc *fd)
+{
+ PRThread *t;
+ PRIntn i;
+
+ _PR_DumpPrintf(fd, "Current Thread:\n");
+ t = _PR_MD_CURRENT_THREAD();
+ _PR_DumpThread(fd, t);
+
+ _PR_DumpPrintf(fd, "Runnable Threads:\n");
+ for (i = 0; i < PR_ARRAY_SIZE(_PR_RUNQ(t->cpu)); i++) {
+ DumpThreadQueue(fd, &_PR_RUNQ(t->cpu)[i]);
+ }
+
+ _PR_DumpPrintf(fd, "CondVar timed wait Threads:\n");
+ DumpThreadQueue(fd, &_PR_SLEEPQ(t->cpu));
+
+ _PR_DumpPrintf(fd, "CondVar wait Threads:\n");
+ DumpThreadQueue(fd, &_PR_PAUSEQ(t->cpu));
+
+ _PR_DumpPrintf(fd, "Suspended Threads:\n");
+ DumpThreadQueue(fd, &_PR_SUSPENDQ(t->cpu));
+}
+
+PR_IMPLEMENT(void) PR_ShowStatus(void)
+{
+ PRIntn is;
+
+ if ( _PR_MD_CURRENT_THREAD()
+ && !_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) {
+ _PR_INTSOFF(is);
+ }
+ _pr_dumpOut = _pr_stderr;
+ _PR_DumpThreads(_pr_dumpOut);
+ if ( _PR_MD_CURRENT_THREAD()
+ && !_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) {
+ _PR_FAST_INTSON(is);
+ }
+}
+
+PR_IMPLEMENT(void)
+PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg)
+{
+ thread->dump = dump;
+ thread->dumpArg = arg;
+}
diff --git a/nsprpub/pr/src/threads/prmon.c b/nsprpub/pr/src/threads/prmon.c
new file mode 100644
index 0000000000..b7c526eea7
--- /dev/null
+++ b/nsprpub/pr/src/threads/prmon.c
@@ -0,0 +1,357 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+/************************************************************************/
+
+/*
+ * Notifies just get posted to the monitor. The actual notification is done
+ * when the monitor is fully exited so that MP systems don't contend for a
+ * monitor that they can't enter.
+ */
+static void _PR_PostNotifyToMonitor(PRMonitor *mon, PRBool broadcast)
+{
+ PR_ASSERT(mon != NULL);
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mon);
+
+ /* mon->notifyTimes is protected by the monitor, so we don't need to
+ * acquire mon->lock.
+ */
+ if (broadcast) {
+ mon->notifyTimes = -1;
+ }
+ else if (mon->notifyTimes != -1) {
+ mon->notifyTimes += 1;
+ }
+}
+
+static void _PR_PostNotifiesFromMonitor(PRCondVar *cv, PRIntn times)
+{
+ PRStatus rv;
+
+ /*
+ * Time to actually notify any waits that were affected while the monitor
+ * was entered.
+ */
+ PR_ASSERT(cv != NULL);
+ PR_ASSERT(times != 0);
+ if (times == -1) {
+ rv = PR_NotifyAllCondVar(cv);
+ PR_ASSERT(rv == PR_SUCCESS);
+ } else {
+ while (times-- > 0) {
+ rv = PR_NotifyCondVar(cv);
+ PR_ASSERT(rv == PR_SUCCESS);
+ }
+ }
+}
+
+/*
+** Create a new monitor.
+*/
+PR_IMPLEMENT(PRMonitor*) PR_NewMonitor()
+{
+ PRMonitor *mon;
+ PRStatus rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ mon = PR_NEWZAP(PRMonitor);
+ if (mon == NULL) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+
+ rv = _PR_InitLock(&mon->lock);
+ PR_ASSERT(rv == PR_SUCCESS);
+ if (rv != PR_SUCCESS) {
+ goto error1;
+ }
+
+ mon->owner = NULL;
+
+ rv = _PR_InitCondVar(&mon->entryCV, &mon->lock);
+ PR_ASSERT(rv == PR_SUCCESS);
+ if (rv != PR_SUCCESS) {
+ goto error2;
+ }
+
+ rv = _PR_InitCondVar(&mon->waitCV, &mon->lock);
+ PR_ASSERT(rv == PR_SUCCESS);
+ if (rv != PR_SUCCESS) {
+ goto error3;
+ }
+
+ mon->notifyTimes = 0;
+ mon->entryCount = 0;
+ mon->name = NULL;
+ return mon;
+
+error3:
+ _PR_FreeCondVar(&mon->entryCV);
+error2:
+ _PR_FreeLock(&mon->lock);
+error1:
+ PR_Free(mon);
+ return NULL;
+}
+
+PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name)
+{
+ PRMonitor* mon = PR_NewMonitor();
+ if (mon) {
+ mon->name = name;
+ }
+ return mon;
+}
+
+/*
+** Destroy a monitor. There must be no thread waiting on the monitor's
+** condition variable. The caller is responsible for guaranteeing that the
+** monitor is no longer in use.
+*/
+PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon)
+{
+ PR_ASSERT(mon != NULL);
+ _PR_FreeCondVar(&mon->waitCV);
+ _PR_FreeCondVar(&mon->entryCV);
+ _PR_FreeLock(&mon->lock);
+#if defined(DEBUG)
+ memset(mon, 0xaf, sizeof(PRMonitor));
+#endif
+ PR_Free(mon);
+}
+
+/*
+** Enter the lock associated with the monitor.
+*/
+PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRStatus rv;
+
+ PR_ASSERT(mon != NULL);
+ PR_Lock(&mon->lock);
+ if (mon->entryCount != 0) {
+ if (mon->owner == me) {
+ goto done;
+ }
+ while (mon->entryCount != 0) {
+ rv = PR_WaitCondVar(&mon->entryCV, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(rv == PR_SUCCESS);
+ }
+ }
+ /* and now I have the monitor */
+ PR_ASSERT(mon->notifyTimes == 0);
+ PR_ASSERT(mon->owner == NULL);
+ mon->owner = me;
+
+done:
+ mon->entryCount += 1;
+ rv = PR_Unlock(&mon->lock);
+ PR_ASSERT(rv == PR_SUCCESS);
+}
+
+/*
+** Test and then enter the lock associated with the monitor if it's not
+** already entered by some other thread. Return PR_FALSE if some other
+** thread owned the lock at the time of the call.
+*/
+PR_IMPLEMENT(PRBool) PR_TestAndEnterMonitor(PRMonitor *mon)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRStatus rv;
+
+ PR_ASSERT(mon != NULL);
+ PR_Lock(&mon->lock);
+ if (mon->entryCount != 0) {
+ if (mon->owner == me) {
+ goto done;
+ }
+ rv = PR_Unlock(&mon->lock);
+ PR_ASSERT(rv == PR_SUCCESS);
+ return PR_FALSE;
+ }
+ /* and now I have the monitor */
+ PR_ASSERT(mon->notifyTimes == 0);
+ PR_ASSERT(mon->owner == NULL);
+ mon->owner = me;
+
+done:
+ mon->entryCount += 1;
+ rv = PR_Unlock(&mon->lock);
+ PR_ASSERT(rv == PR_SUCCESS);
+ return PR_TRUE;
+}
+
+/*
+** Exit the lock associated with the monitor once.
+*/
+PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRStatus rv;
+
+ PR_ASSERT(mon != NULL);
+ PR_Lock(&mon->lock);
+ /* the entries should be > 0 and we'd better be the owner */
+ PR_ASSERT(mon->entryCount > 0);
+ PR_ASSERT(mon->owner == me);
+ if (mon->entryCount == 0 || mon->owner != me)
+ {
+ rv = PR_Unlock(&mon->lock);
+ PR_ASSERT(rv == PR_SUCCESS);
+ return PR_FAILURE;
+ }
+
+ mon->entryCount -= 1; /* reduce by one */
+ if (mon->entryCount == 0)
+ {
+ /* and if it transitioned to zero - notify an entry waiter */
+ /* make the owner unknown */
+ mon->owner = NULL;
+ if (mon->notifyTimes != 0) {
+ _PR_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes);
+ mon->notifyTimes = 0;
+ }
+ rv = PR_NotifyCondVar(&mon->entryCV);
+ PR_ASSERT(rv == PR_SUCCESS);
+ }
+ rv = PR_Unlock(&mon->lock);
+ PR_ASSERT(rv == PR_SUCCESS);
+ return PR_SUCCESS;
+}
+
+/*
+** Return the number of times that the current thread has entered the
+** lock. Returns zero if the current thread has not entered the lock.
+*/
+PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRStatus rv;
+ PRIntn count = 0;
+
+ PR_Lock(&mon->lock);
+ if (mon->owner == me) {
+ count = mon->entryCount;
+ }
+ rv = PR_Unlock(&mon->lock);
+ PR_ASSERT(rv == PR_SUCCESS);
+ return count;
+}
+
+PR_IMPLEMENT(void) PR_AssertCurrentThreadInMonitor(PRMonitor *mon)
+{
+#if defined(DEBUG) || defined(FORCE_PR_ASSERT)
+ PRStatus rv;
+
+ PR_Lock(&mon->lock);
+ PR_ASSERT(mon->entryCount != 0 &&
+ mon->owner == _PR_MD_CURRENT_THREAD());
+ rv = PR_Unlock(&mon->lock);
+ PR_ASSERT(rv == PR_SUCCESS);
+#endif
+}
+
+/*
+** Wait for a notify on the condition variable. Sleep for "ticks" amount
+** of time (if "tick" is 0 then the sleep is indefinite). While
+** the thread is waiting it exits the monitors lock (as if it called
+** PR_ExitMonitor as many times as it had called PR_EnterMonitor). When
+** the wait has finished the thread regains control of the monitors lock
+** with the same entry count as before the wait began.
+**
+** The thread waiting on the monitor will be resumed when the monitor is
+** notified (assuming the thread is the next in line to receive the
+** notify) or when the "ticks" elapses.
+**
+** Returns PR_FAILURE if the caller has not locked the lock associated
+** with the condition variable.
+** This routine can return PR_PENDING_INTERRUPT_ERROR if the waiting thread
+** has been interrupted.
+*/
+PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime ticks)
+{
+ PRStatus rv;
+ PRUint32 saved_entries;
+ PRThread *saved_owner;
+
+ PR_ASSERT(mon != NULL);
+ PR_Lock(&mon->lock);
+ /* the entries better be positive */
+ PR_ASSERT(mon->entryCount > 0);
+ /* and it better be owned by us */
+ PR_ASSERT(mon->owner == _PR_MD_CURRENT_THREAD()); /* XXX return failure */
+
+ /* tuck these away 'till later */
+ saved_entries = mon->entryCount;
+ mon->entryCount = 0;
+ saved_owner = mon->owner;
+ mon->owner = NULL;
+ /* If we have pending notifies, post them now. */
+ if (mon->notifyTimes != 0) {
+ _PR_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes);
+ mon->notifyTimes = 0;
+ }
+ rv = PR_NotifyCondVar(&mon->entryCV);
+ PR_ASSERT(rv == PR_SUCCESS);
+
+ rv = PR_WaitCondVar(&mon->waitCV, ticks);
+ PR_ASSERT(rv == PR_SUCCESS);
+
+ while (mon->entryCount != 0) {
+ rv = PR_WaitCondVar(&mon->entryCV, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(rv == PR_SUCCESS);
+ }
+ PR_ASSERT(mon->notifyTimes == 0);
+ /* reinstate the interesting information */
+ mon->entryCount = saved_entries;
+ mon->owner = saved_owner;
+
+ rv = PR_Unlock(&mon->lock);
+ PR_ASSERT(rv == PR_SUCCESS);
+ return rv;
+}
+
+/*
+** Notify the highest priority thread waiting on the condition
+** variable. If a thread is waiting on the condition variable (using
+** PR_Wait) then it is awakened and begins waiting on the monitor's lock.
+*/
+PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon)
+{
+ _PR_PostNotifyToMonitor(mon, PR_FALSE);
+ return PR_SUCCESS;
+}
+
+/*
+** Notify all of the threads waiting on the condition variable. All of
+** threads are notified in turn. The highest priority thread will
+** probably acquire the monitor first when the monitor is exited.
+*/
+PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon)
+{
+ _PR_PostNotifyToMonitor(mon, PR_TRUE);
+ return PR_SUCCESS;
+}
+
+/************************************************************************/
+
+PRUint32 _PR_MonitorToString(PRMonitor *mon, char *buf, PRUint32 buflen)
+{
+ PRUint32 nb;
+
+ if (mon->owner) {
+ nb = PR_snprintf(buf, buflen, "[%p] owner=%d[%p] count=%ld",
+ mon, mon->owner->id, mon->owner, mon->entryCount);
+ } else {
+ nb = PR_snprintf(buf, buflen, "[%p]", mon);
+ }
+ return nb;
+}
diff --git a/nsprpub/pr/src/threads/prrwlock.c b/nsprpub/pr/src/threads/prrwlock.c
new file mode 100644
index 0000000000..0cb51e5bdf
--- /dev/null
+++ b/nsprpub/pr/src/threads/prrwlock.c
@@ -0,0 +1,498 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <string.h>
+
+#if defined(HPUX) && defined(_PR_PTHREADS)
+
+#include <pthread.h>
+#define HAVE_UNIX98_RWLOCK
+#define RWLOCK_T pthread_rwlock_t
+#define RWLOCK_INIT(lock) pthread_rwlock_init(lock, NULL)
+#define RWLOCK_DESTROY(lock) pthread_rwlock_destroy(lock)
+#define RWLOCK_RDLOCK(lock) pthread_rwlock_rdlock(lock)
+#define RWLOCK_WRLOCK(lock) pthread_rwlock_wrlock(lock)
+#define RWLOCK_UNLOCK(lock) pthread_rwlock_unlock(lock)
+
+#elif defined(SOLARIS) && (defined(_PR_PTHREADS) \
+ || defined(_PR_GLOBAL_THREADS_ONLY))
+
+#include <synch.h>
+#define HAVE_UI_RWLOCK
+#define RWLOCK_T rwlock_t
+#define RWLOCK_INIT(lock) rwlock_init(lock, USYNC_THREAD, NULL)
+#define RWLOCK_DESTROY(lock) rwlock_destroy(lock)
+#define RWLOCK_RDLOCK(lock) rw_rdlock(lock)
+#define RWLOCK_WRLOCK(lock) rw_wrlock(lock)
+#define RWLOCK_UNLOCK(lock) rw_unlock(lock)
+
+#endif
+
+/*
+ * Reader-writer lock
+ */
+struct PRRWLock {
+ char *rw_name; /* lock name */
+ PRUint32 rw_rank; /* rank of the lock */
+
+#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
+ RWLOCK_T rw_lock;
+#else
+ PRLock *rw_lock;
+ PRInt32 rw_lock_cnt; /* == 0, if unlocked */
+ /* == -1, if write-locked */
+ /* > 0 , # of read locks */
+ PRUint32 rw_reader_cnt; /* number of waiting readers */
+ PRUint32 rw_writer_cnt; /* number of waiting writers */
+ PRCondVar *rw_reader_waitq; /* cvar for readers */
+ PRCondVar *rw_writer_waitq; /* cvar for writers */
+#ifdef DEBUG
+ PRThread *rw_owner; /* lock owner for write-lock */
+#endif
+#endif
+};
+
+#ifdef DEBUG
+#define _PR_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using
+ rank-order for locks
+ */
+#endif
+
+#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
+
+static PRUintn pr_thread_rwlock_key; /* TPD key for lock stack */
+static PRUintn pr_thread_rwlock_alloc_failed;
+
+#define _PR_RWLOCK_RANK_ORDER_LIMIT 10
+
+typedef struct thread_rwlock_stack {
+ PRInt32 trs_index; /* top of stack */
+ PRRWLock *trs_stack[_PR_RWLOCK_RANK_ORDER_LIMIT]; /* stack of lock
+ pointers */
+
+} thread_rwlock_stack;
+
+static void _PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock);
+static PRUint32 _PR_GET_THREAD_RWLOCK_RANK(void);
+static void _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock);
+static void _PR_RELEASE_LOCK_STACK(void *lock_stack);
+
+#endif
+
+/*
+ * Reader/Writer Locks
+ */
+
+/*
+ * PR_NewRWLock
+ * Create a reader-writer lock, with the given lock rank and lock name
+ *
+ */
+
+PR_IMPLEMENT(PRRWLock *)
+PR_NewRWLock(PRUint32 lock_rank, const char *lock_name)
+{
+ PRRWLock *rwlock;
+#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
+ int err;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ rwlock = PR_NEWZAP(PRRWLock);
+ if (rwlock == NULL) {
+ return NULL;
+ }
+
+ rwlock->rw_rank = lock_rank;
+ if (lock_name != NULL) {
+ rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1);
+ if (rwlock->rw_name == NULL) {
+ PR_DELETE(rwlock);
+ return(NULL);
+ }
+ strcpy(rwlock->rw_name, lock_name);
+ } else {
+ rwlock->rw_name = NULL;
+ }
+
+#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
+ err = RWLOCK_INIT(&rwlock->rw_lock);
+ if (err != 0) {
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ PR_Free(rwlock->rw_name);
+ PR_DELETE(rwlock);
+ return NULL;
+ }
+ return rwlock;
+#else
+ rwlock->rw_lock = PR_NewLock();
+ if (rwlock->rw_lock == NULL) {
+ goto failed;
+ }
+ rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock);
+ if (rwlock->rw_reader_waitq == NULL) {
+ goto failed;
+ }
+ rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock);
+ if (rwlock->rw_writer_waitq == NULL) {
+ goto failed;
+ }
+ rwlock->rw_reader_cnt = 0;
+ rwlock->rw_writer_cnt = 0;
+ rwlock->rw_lock_cnt = 0;
+ return rwlock;
+
+failed:
+ if (rwlock->rw_reader_waitq != NULL) {
+ PR_DestroyCondVar(rwlock->rw_reader_waitq);
+ }
+ if (rwlock->rw_lock != NULL) {
+ PR_DestroyLock(rwlock->rw_lock);
+ }
+ PR_Free(rwlock->rw_name);
+ PR_DELETE(rwlock);
+ return NULL;
+#endif
+}
+
+/*
+** Destroy the given RWLock "lock".
+*/
+PR_IMPLEMENT(void)
+PR_DestroyRWLock(PRRWLock *rwlock)
+{
+#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
+ int err;
+ err = RWLOCK_DESTROY(&rwlock->rw_lock);
+ PR_ASSERT(err == 0);
+#else
+ PR_ASSERT(rwlock->rw_reader_cnt == 0);
+ PR_DestroyCondVar(rwlock->rw_reader_waitq);
+ PR_DestroyCondVar(rwlock->rw_writer_waitq);
+ PR_DestroyLock(rwlock->rw_lock);
+#endif
+ if (rwlock->rw_name != NULL) {
+ PR_Free(rwlock->rw_name);
+ }
+ PR_DELETE(rwlock);
+}
+
+/*
+** Read-lock the RWLock.
+*/
+PR_IMPLEMENT(void)
+PR_RWLock_Rlock(PRRWLock *rwlock)
+{
+#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
+ int err;
+#endif
+
+#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
+ /*
+ * assert that rank ordering is not violated; the rank of 'rwlock' should
+ * be equal to or greater than the highest rank of all the locks held by
+ * the thread.
+ */
+ PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) ||
+ (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
+#endif
+
+#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
+ err = RWLOCK_RDLOCK(&rwlock->rw_lock);
+ PR_ASSERT(err == 0);
+#else
+ PR_Lock(rwlock->rw_lock);
+ /*
+ * wait if write-locked or if a writer is waiting; preference for writers
+ */
+ while ((rwlock->rw_lock_cnt < 0) ||
+ (rwlock->rw_writer_cnt > 0)) {
+ rwlock->rw_reader_cnt++;
+ PR_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT);
+ rwlock->rw_reader_cnt--;
+ }
+ /*
+ * Increment read-lock count
+ */
+ rwlock->rw_lock_cnt++;
+
+ PR_Unlock(rwlock->rw_lock);
+#endif
+
+#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
+ /*
+ * update thread's lock rank
+ */
+ if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) {
+ _PR_SET_THREAD_RWLOCK_RANK(rwlock);
+ }
+#endif
+}
+
+/*
+** Write-lock the RWLock.
+*/
+PR_IMPLEMENT(void)
+PR_RWLock_Wlock(PRRWLock *rwlock)
+{
+#if defined(DEBUG)
+ PRThread *me = PR_GetCurrentThread();
+#endif
+#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
+ int err;
+#endif
+
+#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
+ /*
+ * assert that rank ordering is not violated; the rank of 'rwlock' should
+ * be equal to or greater than the highest rank of all the locks held by
+ * the thread.
+ */
+ PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) ||
+ (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
+#endif
+
+#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
+ err = RWLOCK_WRLOCK(&rwlock->rw_lock);
+ PR_ASSERT(err == 0);
+#else
+ PR_Lock(rwlock->rw_lock);
+ /*
+ * wait if read locked
+ */
+ while (rwlock->rw_lock_cnt != 0) {
+ rwlock->rw_writer_cnt++;
+ PR_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT);
+ rwlock->rw_writer_cnt--;
+ }
+ /*
+ * apply write lock
+ */
+ rwlock->rw_lock_cnt--;
+ PR_ASSERT(rwlock->rw_lock_cnt == -1);
+#ifdef DEBUG
+ PR_ASSERT(me != NULL);
+ rwlock->rw_owner = me;
+#endif
+ PR_Unlock(rwlock->rw_lock);
+#endif
+
+#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
+ /*
+ * update thread's lock rank
+ */
+ if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) {
+ _PR_SET_THREAD_RWLOCK_RANK(rwlock);
+ }
+#endif
+}
+
+/*
+** Unlock the RW lock.
+*/
+PR_IMPLEMENT(void)
+PR_RWLock_Unlock(PRRWLock *rwlock)
+{
+#if defined(DEBUG)
+ PRThread *me = PR_GetCurrentThread();
+#endif
+#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
+ int err;
+#endif
+
+#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
+ err = RWLOCK_UNLOCK(&rwlock->rw_lock);
+ PR_ASSERT(err == 0);
+#else
+ PR_Lock(rwlock->rw_lock);
+ /*
+ * lock must be read or write-locked
+ */
+ PR_ASSERT(rwlock->rw_lock_cnt != 0);
+ if (rwlock->rw_lock_cnt > 0) {
+
+ /*
+ * decrement read-lock count
+ */
+ rwlock->rw_lock_cnt--;
+ if (rwlock->rw_lock_cnt == 0) {
+ /*
+ * lock is not read-locked anymore; wakeup a waiting writer
+ */
+ if (rwlock->rw_writer_cnt > 0) {
+ PR_NotifyCondVar(rwlock->rw_writer_waitq);
+ }
+ }
+ } else {
+ PR_ASSERT(rwlock->rw_lock_cnt == -1);
+
+ rwlock->rw_lock_cnt = 0;
+#ifdef DEBUG
+ PR_ASSERT(rwlock->rw_owner == me);
+ rwlock->rw_owner = NULL;
+#endif
+ /*
+ * wakeup a writer, if present; preference for writers
+ */
+ if (rwlock->rw_writer_cnt > 0) {
+ PR_NotifyCondVar(rwlock->rw_writer_waitq);
+ }
+ /*
+ * else, wakeup all readers, if any
+ */
+ else if (rwlock->rw_reader_cnt > 0) {
+ PR_NotifyAllCondVar(rwlock->rw_reader_waitq);
+ }
+ }
+ PR_Unlock(rwlock->rw_lock);
+#endif
+
+#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
+ /*
+ * update thread's lock rank
+ */
+ if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) {
+ _PR_UNSET_THREAD_RWLOCK_RANK(rwlock);
+ }
+#endif
+ return;
+}
+
+#ifndef _PR_RWLOCK_RANK_ORDER_DEBUG
+
+void _PR_InitRWLocks(void) { }
+
+#else
+
+void _PR_InitRWLocks(void)
+{
+ /*
+ * allocated thread-private-data index for rwlock list
+ */
+ if (PR_NewThreadPrivateIndex(&pr_thread_rwlock_key,
+ _PR_RELEASE_LOCK_STACK) == PR_FAILURE) {
+ pr_thread_rwlock_alloc_failed = 1;
+ return;
+ }
+}
+
+/*
+ * _PR_SET_THREAD_RWLOCK_RANK
+ * Set a thread's lock rank, which is the highest of the ranks of all
+ * the locks held by the thread. Pointers to the locks are added to a
+ * per-thread list, which is anchored off a thread-private data key.
+ */
+
+static void
+_PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock)
+{
+ thread_rwlock_stack *lock_stack;
+ PRStatus rv;
+
+ /*
+ * allocate a lock stack
+ */
+ if ((lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key)) == NULL) {
+ lock_stack = (thread_rwlock_stack *)
+ PR_CALLOC(1 * sizeof(thread_rwlock_stack));
+ if (lock_stack) {
+ rv = PR_SetThreadPrivate(pr_thread_rwlock_key, lock_stack);
+ if (rv == PR_FAILURE) {
+ PR_DELETE(lock_stack);
+ pr_thread_rwlock_alloc_failed = 1;
+ return;
+ }
+ } else {
+ pr_thread_rwlock_alloc_failed = 1;
+ return;
+ }
+ }
+ /*
+ * add rwlock to lock stack, if limit is not exceeded
+ */
+ if (lock_stack) {
+ if (lock_stack->trs_index < _PR_RWLOCK_RANK_ORDER_LIMIT) {
+ lock_stack->trs_stack[lock_stack->trs_index++] = rwlock;
+ }
+ }
+}
+
+static void
+_PR_RELEASE_LOCK_STACK(void *lock_stack)
+{
+ PR_ASSERT(lock_stack);
+ PR_DELETE(lock_stack);
+}
+
+/*
+ * _PR_GET_THREAD_RWLOCK_RANK
+ *
+ * return thread's lock rank. If thread-private-data for the lock
+ * stack is not allocated, return PR_RWLOCK_RANK_NONE.
+ */
+
+static PRUint32
+_PR_GET_THREAD_RWLOCK_RANK(void)
+{
+ thread_rwlock_stack *lock_stack;
+
+ lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key);
+ if (lock_stack == NULL || lock_stack->trs_index == 0) {
+ return (PR_RWLOCK_RANK_NONE);
+ }
+ else {
+ return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank);
+ }
+}
+
+/*
+ * _PR_UNSET_THREAD_RWLOCK_RANK
+ *
+ * remove the rwlock from the lock stack. Since locks may not be
+ * unlocked in a FIFO order, the entire lock stack is searched.
+ */
+
+static void
+_PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock)
+{
+ thread_rwlock_stack *lock_stack;
+ int new_index = 0, index, done = 0;
+
+ lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key);
+
+ PR_ASSERT(lock_stack != NULL);
+
+ for (index = lock_stack->trs_index - 1; index >= 0; index--) {
+ if (!done && (lock_stack->trs_stack[index] == rwlock)) {
+ /*
+ * reset the slot for rwlock
+ */
+ lock_stack->trs_stack[index] = NULL;
+ done = 1;
+ }
+ /*
+ * search for the lowest-numbered empty slot, above which there are
+ * no non-empty slots
+ */
+ if (!new_index && (lock_stack->trs_stack[index] != NULL)) {
+ new_index = index + 1;
+ }
+ if (done && new_index) {
+ break;
+ }
+ }
+ /*
+ * set top of stack to highest numbered empty slot
+ */
+ lock_stack->trs_index = new_index;
+
+}
+
+#endif /* _PR_RWLOCK_RANK_ORDER_DEBUG */
diff --git a/nsprpub/pr/src/threads/prsem.c b/nsprpub/pr/src/threads/prsem.c
new file mode 100644
index 0000000000..120075b0cb
--- /dev/null
+++ b/nsprpub/pr/src/threads/prsem.c
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#include "obsolete/prsem.h"
+
+/************************************************************************/
+
+/*
+** Create a new semaphore.
+*/
+PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value)
+{
+ PRSemaphore *sem;
+ PRCondVar *cvar;
+ PRLock *lock;
+
+ sem = PR_NEWZAP(PRSemaphore);
+ if (sem) {
+#ifdef HAVE_CVAR_BUILT_ON_SEM
+ _PR_MD_NEW_SEM(&sem->md, value);
+#else
+ lock = PR_NewLock();
+ if (!lock) {
+ PR_DELETE(sem);
+ return NULL;
+ }
+
+ cvar = PR_NewCondVar(lock);
+ if (!cvar) {
+ PR_DestroyLock(lock);
+ PR_DELETE(sem);
+ return NULL;
+ }
+ sem->cvar = cvar;
+ sem->count = value;
+#endif
+ }
+ return sem;
+}
+
+/*
+** Destroy a semaphore. There must be no thread waiting on the semaphore.
+** The caller is responsible for guaranteeing that the semaphore is
+** no longer in use.
+*/
+PR_IMPLEMENT(void) PR_DestroySem(PRSemaphore *sem)
+{
+#ifdef HAVE_CVAR_BUILT_ON_SEM
+ _PR_MD_DESTROY_SEM(&sem->md);
+#else
+ PR_ASSERT(sem->waiters == 0);
+
+ PR_DestroyLock(sem->cvar->lock);
+ PR_DestroyCondVar(sem->cvar);
+#endif
+ PR_DELETE(sem);
+}
+
+/*
+** Wait on a Semaphore.
+**
+** This routine allows a calling thread to wait or proceed depending upon the
+** state of the semahore sem. The thread can proceed only if the counter value
+** of the semaphore sem is currently greater than 0. If the value of semaphore
+** sem is positive, it is decremented by one and the routine returns immediately
+** allowing the calling thread to continue. If the value of semaphore sem is 0,
+** the calling thread blocks awaiting the semaphore to be released by another
+** thread.
+**
+** This routine can return PR_PENDING_INTERRUPT if the waiting thread
+** has been interrupted.
+*/
+PR_IMPLEMENT(PRStatus) PR_WaitSem(PRSemaphore *sem)
+{
+ PRStatus status = PR_SUCCESS;
+
+#ifdef HAVE_CVAR_BUILT_ON_SEM
+ return _PR_MD_WAIT_SEM(&sem->md);
+#else
+ PR_Lock(sem->cvar->lock);
+ while (sem->count == 0) {
+ sem->waiters++;
+ status = PR_WaitCondVar(sem->cvar, PR_INTERVAL_NO_TIMEOUT);
+ sem->waiters--;
+ if (status != PR_SUCCESS) {
+ break;
+ }
+ }
+ if (status == PR_SUCCESS) {
+ sem->count--;
+ }
+ PR_Unlock(sem->cvar->lock);
+#endif
+
+ return (status);
+}
+
+/*
+** This routine increments the counter value of the semaphore. If other threads
+** are blocked for the semaphore, then the scheduler will determine which ONE
+** thread will be unblocked.
+*/
+PR_IMPLEMENT(void) PR_PostSem(PRSemaphore *sem)
+{
+#ifdef HAVE_CVAR_BUILT_ON_SEM
+ _PR_MD_POST_SEM(&sem->md);
+#else
+ PR_Lock(sem->cvar->lock);
+ if (sem->waiters) {
+ PR_NotifyCondVar(sem->cvar);
+ }
+ sem->count++;
+ PR_Unlock(sem->cvar->lock);
+#endif
+}
+
+#if DEBUG
+/*
+** Returns the value of the semaphore referenced by sem without affecting
+** the state of the semaphore. The value represents the semaphore vaule
+** at the time of the call, but may not be the actual value when the
+** caller inspects it. (FOR DEBUGGING ONLY)
+*/
+PR_IMPLEMENT(PRUintn) PR_GetValueSem(PRSemaphore *sem)
+{
+ PRUintn rv;
+
+#ifdef HAVE_CVAR_BUILT_ON_SEM
+ rv = _PR_MD_GET_VALUE_SEM(&sem->md);
+#else
+ PR_Lock(sem->cvar->lock);
+ rv = sem->count;
+ PR_Unlock(sem->cvar->lock);
+#endif
+
+ return rv;
+}
+#endif
diff --git a/nsprpub/pr/src/threads/prtpd.c b/nsprpub/pr/src/threads/prtpd.c
new file mode 100644
index 0000000000..e6385cf700
--- /dev/null
+++ b/nsprpub/pr/src/threads/prtpd.c
@@ -0,0 +1,252 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Thread Private Data
+**
+** There is an aribitrary limit on the number of keys that will be allocated
+** by the runtime. It's largish, so it is intended to be a sanity check, not
+** an impediment.
+**
+** There is a counter, initialized to zero and incremented every time a
+** client asks for a new key, that holds the high water mark for keys. All
+** threads logically have the same high water mark and are permitted to
+** ask for TPD up to that key value.
+**
+** The vector to hold the TPD are allocated when PR_SetThreadPrivate() is
+** called. The size of the vector will be some value greater than or equal
+** to the current high water mark. Each thread has its own TPD length and
+** vector.
+**
+** Threads that get private data for keys they have not set (or perhaps
+** don't even exist for that thread) get a NULL return. If the key is
+** beyond the high water mark, an error will be returned.
+*/
+
+/*
+** As of this time, BeOS has its own TPD implementation. Integrating
+** this standard one is a TODO for anyone with a bit of spare time on
+** their hand. For now, we just #ifdef out this whole file and use
+** the routines in pr/src/btthreads/
+*/
+
+
+#include "primpl.h"
+
+#include <string.h>
+
+#if defined(WIN95)
+/*
+** Some local variables report warnings on Win95 because the code paths
+** using them are conditioned on HAVE_CUSTOME_USER_THREADS.
+** The pragma suppresses the warning.
+**
+*/
+#pragma warning(disable : 4101)
+#endif
+
+#define _PR_TPD_LIMIT 128 /* arbitary limit on the TPD slots */
+static PRInt32 _pr_tpd_length = 0; /* current length of destructor vector */
+static PRInt32 _pr_tpd_highwater = 0; /* next TPD key to be assigned */
+static PRThreadPrivateDTOR *_pr_tpd_destructors = NULL;
+/* the destructors are associated with
+ the keys, therefore asserting that
+ the TPD key depicts the data's 'type' */
+
+/*
+** Initialize the thread private data manipulation
+*/
+void _PR_InitTPD(void)
+{
+ _pr_tpd_destructors = (PRThreadPrivateDTOR*)
+ PR_CALLOC(_PR_TPD_LIMIT * sizeof(PRThreadPrivateDTOR*));
+ PR_ASSERT(NULL != _pr_tpd_destructors);
+ _pr_tpd_length = _PR_TPD_LIMIT;
+}
+
+/*
+** Clean up the thread private data manipulation
+*/
+void _PR_CleanupTPD(void)
+{
+} /* _PR_CleanupTPD */
+
+/*
+** This routine returns a new index for per-thread-private data table.
+** The index is visible to all threads within a process. This index can
+** be used with the PR_SetThreadPrivate() and PR_GetThreadPrivate() routines
+** to save and retrieve data associated with the index for a thread.
+**
+** The index independently maintains specific values for each binding thread.
+** A thread can only get access to its own thread-specific-data.
+**
+** Upon a new index return the value associated with the index for all threads
+** is NULL, and upon thread creation the value associated with all indices for
+** that thread is NULL.
+**
+** "dtor" is the destructor function to invoke when the private
+** data is set or destroyed
+**
+** Returns PR_FAILURE if the total number of indices will exceed the maximun
+** allowed.
+*/
+
+PR_IMPLEMENT(PRStatus) PR_NewThreadPrivateIndex(
+ PRUintn *newIndex, PRThreadPrivateDTOR dtor)
+{
+ PRStatus rv;
+ PRInt32 index;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ PR_ASSERT(NULL != newIndex);
+ PR_ASSERT(NULL != _pr_tpd_destructors);
+
+ index = PR_ATOMIC_INCREMENT(&_pr_tpd_highwater) - 1; /* allocate index */
+ if (_PR_TPD_LIMIT <= index)
+ {
+ PR_SetError(PR_TPD_RANGE_ERROR, 0);
+ rv = PR_FAILURE; /* that's just wrong */
+ }
+ else
+ {
+ _pr_tpd_destructors[index] = dtor; /* record destructor @index */
+ *newIndex = (PRUintn)index; /* copy into client's location */
+ rv = PR_SUCCESS; /* that's okay */
+ }
+
+ return rv;
+}
+
+/*
+** Define some per-thread-private data.
+** "index" is an index into the per-thread private data table
+** "priv" is the per-thread-private data
+**
+** If the per-thread private data table has a previously registered
+** destructor function and a non-NULL per-thread-private data value,
+** the destructor function is invoked.
+**
+** This can return PR_FAILURE if index is invalid (ie., beyond the limit
+** on the TPD slots) or memory is insufficient to allocate an expanded
+** vector.
+*/
+
+PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void *priv)
+{
+ PRThread *self = PR_GetCurrentThread();
+
+ /*
+ ** To improve performance, we don't check if the index has been
+ ** allocated.
+ */
+ if (index >= _PR_TPD_LIMIT)
+ {
+ PR_SetError(PR_TPD_RANGE_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ PR_ASSERT(((NULL == self->privateData) && (0 == self->tpdLength))
+ || ((NULL != self->privateData) && (0 != self->tpdLength)));
+
+ /*
+ ** If this thread does not have a sufficient vector for the index
+ ** being set, go ahead and extend this vector now.
+ */
+ if ((NULL == self->privateData) || (self->tpdLength <= index))
+ {
+ void *extension = PR_CALLOC(_pr_tpd_length * sizeof(void*));
+ if (NULL == extension)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (self->privateData) {
+ (void)memcpy(
+ extension, self->privateData,
+ self->tpdLength * sizeof(void*));
+ PR_DELETE(self->privateData);
+ }
+ self->tpdLength = _pr_tpd_length;
+ self->privateData = (void**)extension;
+ }
+ /*
+ ** There wasn't much chance of having to call the destructor
+ ** unless the slot already existed.
+ */
+ else if (self->privateData[index] && _pr_tpd_destructors[index])
+ {
+ void *data = self->privateData[index];
+ self->privateData[index] = NULL;
+ (*_pr_tpd_destructors[index])(data);
+ }
+
+ PR_ASSERT(index < self->tpdLength);
+ self->privateData[index] = priv;
+
+ return PR_SUCCESS;
+}
+
+/*
+** Recover the per-thread-private data for the current thread. "index" is
+** the index into the per-thread private data table.
+**
+** The returned value may be NULL which is indistinguishable from an error
+** condition.
+**
+*/
+
+PR_IMPLEMENT(void*) PR_GetThreadPrivate(PRUintn index)
+{
+ PRThread *self = PR_GetCurrentThread();
+ void *tpd = ((NULL == self->privateData) || (index >= self->tpdLength)) ?
+ NULL : self->privateData[index];
+
+ return tpd;
+}
+
+/*
+** Destroy the thread's private data, if any exists. This is called at
+** thread termination time only. There should be no threading issues
+** since this is being called by the thread itself.
+*/
+void _PR_DestroyThreadPrivate(PRThread* self)
+{
+#define _PR_TPD_DESTRUCTOR_ITERATIONS 4
+
+ if (NULL != self->privateData) /* we have some */
+ {
+ PRBool clean;
+ PRUint32 index;
+ PRInt32 passes = _PR_TPD_DESTRUCTOR_ITERATIONS;
+ PR_ASSERT(0 != self->tpdLength);
+ do
+ {
+ clean = PR_TRUE;
+ for (index = 0; index < self->tpdLength; ++index)
+ {
+ void *priv = self->privateData[index]; /* extract */
+ if (NULL != priv) /* we have data at this index */
+ {
+ if (NULL != _pr_tpd_destructors[index])
+ {
+ self->privateData[index] = NULL; /* precondition */
+ (*_pr_tpd_destructors[index])(priv); /* destroy */
+ clean = PR_FALSE; /* unknown side effects */
+ }
+ }
+ }
+ } while ((--passes > 0) && !clean); /* limit # of passes */
+ /*
+ ** We give up after a fixed number of passes. Any non-NULL
+ ** thread-private data value with a registered destructor
+ ** function is not destroyed.
+ */
+ memset(self->privateData, 0, self->tpdLength * sizeof(void*));
+ }
+} /* _PR_DestroyThreadPrivate */
+
diff --git a/nsprpub/pr/tests/Makefile.in b/nsprpub/pr/tests/Makefile.in
new file mode 100644
index 0000000000..a58a846927
--- /dev/null
+++ b/nsprpub/pr/tests/Makefile.in
@@ -0,0 +1,446 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+#! gmake
+
+MOD_DEPTH = ../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+DIRS = dll
+
+CSRCS = \
+ abstract.c \
+ accept.c \
+ acceptread.c \
+ acceptreademu.c \
+ addrstr.c \
+ affinity.c \
+ alarm.c \
+ anonfm.c \
+ append.c \
+ atomic.c \
+ attach.c \
+ bigfile.c \
+ bigfile2.c \
+ bigfile3.c \
+ cleanup.c \
+ cltsrv.c \
+ concur.c \
+ cvar.c \
+ cvar2.c \
+ dceemu.c \
+ dlltest.c \
+ dtoa.c \
+ env.c \
+ errcodes.c \
+ errset.c \
+ exit.c \
+ fdcach.c \
+ fileio.c \
+ foreign.c \
+ forktest.c \
+ formattm.c \
+ fsync.c \
+ getai.c \
+ gethost.c \
+ getproto.c \
+ i2l.c \
+ initclk.c \
+ inrval.c \
+ instrumt.c \
+ intrio.c \
+ intrupt.c \
+ io_timeout.c \
+ ioconthr.c \
+ ipv6.c \
+ join.c \
+ joinkk.c \
+ joinku.c \
+ joinuk.c \
+ joinuu.c \
+ layer.c \
+ lazyinit.c \
+ libfilename.c \
+ lltest.c \
+ lock.c \
+ lockfile.c \
+ logfile.c \
+ logger.c \
+ makedir.c \
+ mbcs.c \
+ multiacc.c \
+ multiwait.c \
+ many_cv.c \
+ monref.c \
+ nameshm1.c \
+ nbconn.c \
+ nblayer.c \
+ nonblock.c \
+ ntioto.c \
+ ntoh.c \
+ obsints.c \
+ op_2long.c \
+ op_excl.c \
+ op_filnf.c \
+ op_filok.c \
+ op_noacc.c \
+ op_nofil.c \
+ openfile.c \
+ parent.c \
+ parsetm.c \
+ peek.c \
+ perf.c \
+ pipeping.c \
+ pipeping2.c \
+ pipepong.c \
+ pipepong2.c \
+ pipeself.c \
+ poll_er.c \
+ poll_nm.c \
+ poll_to.c \
+ pollable.c \
+ prfdbl.c \
+ prftest.c \
+ prftest1.c \
+ prftest2.c \
+ prfz.c \
+ primblok.c \
+ priotest.c \
+ provider.c \
+ prpoll.c \
+ prpollml.c \
+ pushtop.c \
+ ranfile.c \
+ randseed.c \
+ reinit.c \
+ rmdir.c \
+ rwlockrank.c \
+ rwlocktest.c \
+ sel_spd.c \
+ selct_er.c \
+ selct_nm.c \
+ selct_to.c \
+ select2.c \
+ selintr.c \
+ sem.c \
+ sema.c \
+ semaerr.c \
+ semaerr1.c \
+ semaping.c \
+ semapong.c \
+ sendzlf.c \
+ server_test.c \
+ servr_kk.c \
+ servr_ku.c \
+ servr_uk.c \
+ servr_uu.c \
+ short_thread.c \
+ sigpipe.c \
+ socket.c \
+ sockopt.c \
+ sockping.c \
+ sockpong.c \
+ sprintf.c \
+ stack.c \
+ stdio.c \
+ str2addr.c \
+ strod.c \
+ suspend.c \
+ switch.c \
+ system.c \
+ testbit.c \
+ testfile.c \
+ thrpool_server.c \
+ thrpool_client.c \
+ threads.c \
+ thruput.c \
+ timemac.c \
+ timetest.c \
+ tmoacc.c \
+ tmocon.c \
+ tpd.c \
+ vercheck.c \
+ version.c \
+ udpsrv.c \
+ writev.c \
+ xnotify.c \
+ y2k.c \
+ y2ktmo.c \
+ zerolen.c \
+ $(NULL)
+
+ifeq ($(OS_ARCH),WINCE)
+CFLAGS += -FImozce_shunt.h -Zi -UDEBUG -DNDEBUG
+LDOPTS += -link $(DIST)/lib/mozce_shunt.lib ws2.lib -DEBUG -DEBUGTYPE:CV -INCREMENTAL:NO -PDB:$(@:.exe=.pdb)
+endif
+
+ifeq ($(OS_TARGET),OS2)
+CSRCS += \
+ sleep.c \
+ stat.c \
+ yield.c \
+ $(NULL)
+endif
+
+ifeq (,$(filter-out WINCE WINNT OS2,$(OS_ARCH)))
+PROG_SUFFIX = .exe
+DLL_SUFFIX = .dll
+else
+PROG_SUFFIX =
+DLL_SUFFIX =
+endif
+
+PROGS = $(addprefix $(OBJDIR)/, $(CSRCS:.c=$(PROG_SUFFIX)))
+
+TARGETS = $(PROGS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+ifeq ($(OS_ARCH), WINNT)
+ifdef NS_USE_GCC
+ EXTRA_LIBS += -lws2_32
+else
+ EXTRA_LIBS += ws2_32.lib
+ LDOPTS = -NOLOGO -DEBUG -DEBUGTYPE:CV -INCREMENTAL:NO
+ CFLAGS += -Fd$(@:.$(OBJ_SUFFIX)=.pdb)
+ ifdef PROFILE
+ LDOPTS += -PROFILE -MAP
+ endif # profile
+endif # NS_USE_GCC
+endif
+
+ifeq ($(OS_ARCH),OS2)
+EXTRA_LIBS = $(OS_LIBS)
+LDOPTS = -Zomf -Zlinker /PM:VIO -Zlinker /ST:0x64000
+endif
+
+ifneq ($(OS_ARCH), WINNT)
+# Use an absolute pathname as the runtime library path (for the -R
+# or -rpath linker option or the LD_RUN_PATH environment variable).
+ifeq (,$(patsubst /%,,$(DIST)))
+# $(DIST) is already an absolute pathname.
+ABSOLUTE_LIB_DIR = $(dist_libdir)
+else
+# $(DIST) is a relative pathname: prepend the current directory.
+PWD = $(shell pwd)
+ABSOLUTE_LIB_DIR = $(PWD)/$(dist_libdir)
+endif
+endif
+
+ifeq ($(OS_ARCH), HP-UX)
+ LDOPTS += -z -Wl,+s,+b,$(ABSOLUTE_LIB_DIR)
+ ifeq ($(USE_64),1)
+ LDOPTS += +DD64
+ endif
+ ifeq ($(USE_PTHREADS),1)
+ EXTRA_LIBS = $(LIBPTHREAD)
+ endif
+endif
+
+# AIX
+ifeq ($(OS_ARCH),AIX)
+ LDOPTS += -blibpath:$(ABSOLUTE_LIB_DIR):/usr/lib:/lib
+ ifneq ($(OS_ARCH)$(OS_RELEASE),AIX4.1)
+ LDOPTS += -brtl
+ EXTRA_LIBS = -ldl
+ endif
+endif
+
+# Solaris
+ifeq ($(OS_ARCH), SunOS)
+ ifdef NS_USE_GCC
+ LDOPTS += -Xlinker -R -Xlinker $(ABSOLUTE_LIB_DIR)
+ else
+ ifeq ($(USE_CPLUS), 1)
+ CC = CC
+ endif
+ LDOPTS += -R $(ABSOLUTE_LIB_DIR)
+ endif
+
+ ifdef USE_PTHREADS
+ EXTRA_LIBS = -lpthread
+ endif
+endif # SunOS
+
+ifeq (,$(filter-out Linux GNU GNU_%,$(OS_ARCH)))
+ LDOPTS += -Xlinker -rpath $(ABSOLUTE_LIB_DIR)
+ ifeq ($(USE_PTHREADS),1)
+ EXTRA_LIBS = -lpthread
+ endif
+endif
+
+ifeq ($(OS_ARCH), SCOOS)
+# SCO Unix needs to link against -lsocket again even though we
+# already linked with these system libraries when we built libnspr.so.
+EXTRA_LIBS = -lsocket
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+export LD_RUN_PATH = $(ABSOLUTE_LIB_DIR)
+endif
+
+ifeq ($(OS_ARCH),OpenUNIX)
+export LD_RUN_PATH = $(ABSOLUTE_LIB_DIR)
+ifeq ($(USE_PTHREADS),1)
+LDOPTS += -pthread
+endif
+endif
+
+ifeq ($(OS_ARCH), UNIXWARE)
+export LD_RUN_PATH = $(ABSOLUTE_LIB_DIR)
+endif
+
+ifeq ($(OS_ARCH),FreeBSD)
+ifeq ($(USE_PTHREADS),1)
+LDOPTS += -pthread
+endif
+LDOPTS += -Xlinker -R $(ABSOLUTE_LIB_DIR)
+endif
+
+ifeq ($(OS_ARCH),OpenBSD)
+ifeq ($(USE_PTHREADS),1)
+LDOPTS += -pthread
+endif
+endif
+
+ifeq ($(OS_ARCH),BSD_OS)
+ifneq ($(OS_RELEASE),1.1)
+EXTRA_LIBS = -ldl
+endif
+endif
+
+ifeq ($(OS_ARCH),RISCOS)
+EXTRA_LIBS = -ldl
+endif
+
+ifeq ($(USE_PTHREADS),1)
+LIBPTHREAD = -lpthread
+ifeq ($(OS_ARCH),AIX)
+LIBPTHREAD = -lpthreads
+endif
+ifeq (,$(filter-out FreeBSD OpenBSD BSD_OS QNX Darwin OpenUNIX,$(OS_ARCH)))
+LIBPTHREAD =
+endif
+endif
+
+ifeq ($(OS_TARGET),Android)
+LIBPTHREAD =
+XCFLAGS = $(OS_CFLAGS)
+endif
+
+#####################################################
+#
+# The rules
+#
+#####################################################
+
+include $(topsrcdir)/config/rules.mk
+
+AIX_PRE_4_2 = 0
+ifeq ($(OS_ARCH),AIX)
+ifeq ($(OS_RELEASE),4.1)
+ifneq ($(USE_PTHREADS), 1)
+#AIX_PRE_4_2 = 1
+endif
+endif
+endif
+
+ifeq ($(AIX_PRE_4_2),1)
+
+# AIX releases prior to 4.2 need a special two-step linking hack
+# in order to both override the system select() and be able to
+# get at the original system select().
+#
+# We use a pattern rule in ns/nspr20/config/rules.mk to generate
+# the .$(OBJ_SUFFIX) file from the .c source file, then do the
+# two-step linking hack below.
+
+$(OBJDIR)/%: $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ rm -f $@ $(AIX_TMP)
+ $(CC) $(AIX_LINK_OPTS) -o $(AIX_TMP) $< $(dist_libdir)/libnspr$(MOD_MAJOR_VERSION).a
+ $(CC) -o $@ $(AIX_TMP) $(AIX_WRAP)
+ rm -f $(AIX_TMP)
+
+else
+
+# All platforms that are not AIX pre-4.2.
+
+$(OBJDIR)/%$(PROG_SUFFIX): $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ifeq ($(NS_USE_GCC)_$(OS_ARCH),_WINNT)
+ link $(LDOPTS) $(EXTRA_LDOPTS) $< $(LIBPLC) $(LIBNSPR) $(EXTRA_LIBS) -out:$@
+ifdef MT
+ @if test -f $@.manifest; then \
+ $(MT) -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \
+ rm -f $@.manifest; \
+ fi
+endif
+else
+ $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) $(EXTRA_LIBS) -o $@
+endif # WINNT
+endif # AIX_PRE_4_2
+
+export:: $(TARGETS)
+clean::
+ rm -f $(TARGETS)
+
+# The following tests call BSD socket functions, so they need to link
+# with -lsocket on some platforms.
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(USE_IPV6),1)
+$(OBJDIR)/gethost: $(OBJDIR)/gethost.o
+ $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) -lsocket $(EXTRA_LIBS) -o $@
+endif
+$(OBJDIR)/prpoll: $(OBJDIR)/prpoll.o
+ $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) -lsocket $(EXTRA_LIBS) -o $@
+endif
+
+ifeq ($(USE_PTHREADS), 1)
+$(OBJDIR)/attach: $(OBJDIR)/attach.o
+ $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@
+$(OBJDIR)/foreign: $(OBJDIR)/foreign.o
+ $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@
+$(OBJDIR)/provider: $(OBJDIR)/provider.o
+ $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@
+$(OBJDIR)/socket: $(OBJDIR)/socket.o
+ $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@
+$(OBJDIR)/testfile: $(OBJDIR)/testfile.o
+ $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@
+endif
+
+#
+# Run the test programs with no arguments
+#
+# Test output goes to the file pointed to by the environment variable
+# NSPR_TEST_LOGFILE, if set, else to /dev/null
+#
+ECHO = echo
+PROGRAMS = $(notdir $(PROGS))
+ifdef NSPR_TEST_LOGFILE
+LOGFILE = $(NSPR_TEST_LOGFILE)
+else
+ifeq (,$(filter-out WINNT OS2,$(OS_ARCH)))
+LOGFILE = nul
+else
+LOGFILE = /dev/null
+endif
+endif
+
+ifeq ($(OS_TARGET),Linux)
+ECHO = /bin/echo
+endif
+
+ALWAYS:
+
+runtests:: $(PROGS) ALWAYS
+ $(topsrcdir)/pr/tests/runtests.sh $(DIST)
diff --git a/nsprpub/pr/tests/README.TXT b/nsprpub/pr/tests/README.TXT
new file mode 100644
index 0000000000..ebae6cdceb
--- /dev/null
+++ b/nsprpub/pr/tests/README.TXT
@@ -0,0 +1,404 @@
+File: pr/tests/readme
+
+This document describes the test cases in the NSPR directory
+pr/tests.
+
+=====================================================================
+There is a sub-directory here:
+
+dll
+ sources for the .dll(.so) used by test dlltest.c
+
+=====================================================================
+The individual files are described here.
+
+The script 'runtests.ksh' enumerates and runs test cases that are
+expected to run on all platforms.
+
+
+accept.c
+ Tests PR_Accept() and related socket functions.
+
+acceptread.c
+ Tests PR_AcceptRead()
+
+alarm.c
+ Tests alarm functions declared in obsolete/pralarm.h.
+ The alarm functions are obsolete, so is this test.
+
+atomic.c
+ Tests Atomic operations defined in pratom.h
+
+attach.c
+ Test PR_AttachThread()
+ Note: This is an NSPR private function.
+
+bigfile.c
+ Test 64bit file offset functions declared in prio.h
+
+bug1test.c
+ Demonstrates a bug on NT.
+
+cleanup.c
+ Tests PR_Cleanup() declared in prinit.h
+
+cltsrv.c
+ Tests many socket functions.
+
+concur.c
+ Tests threading functions and concurrent operations.
+
+cvar.c
+ Tests condition variables.
+
+cvar2.c
+ Tests condition variables. A rather abusive test.
+
+dbmalloc.c
+ Obsolete. Originally for testing debug builds of NSPR's malloc.
+
+dbmalloc1.c
+ Obsolete. Originally for testing debug builds of NSPR's malloc.
+
+dceemu.c
+ Tests special functions for DCE emulation.
+
+depend.c
+ Obsoltet. Tests early spec for library dependency.
+
+dlltest.c
+ Tests dynamic library functions. Used with dll/my.c
+
+dtoa.c
+ Tests conversions of double to string.
+
+exit.c
+ Tests PR_ProcessExit() declared in prinit.h
+
+fileio.c
+ Tests NSPR semaphores a bit of file i/o and threading
+ functions.
+
+foreign.c
+ Test auto-attach of a thread created by something other than
+ NSPR.
+
+forktest.c
+ Limited use. Tests unix fork() and related functions.
+
+fsync.c
+ Tests use of PR_Sync() declared in prio.h
+
+getproto.c
+ Tests socket functions PR_GetProtoByName(), etc.
+
+i2l.c
+ Tests LongLong functions for converting 32bit integer to 64bit
+ integer.
+
+initclk.c
+ Tests timing on minimal use of condition variable
+
+inrval.c
+ Tests interval timing functions.
+
+instrumt.c
+ Tests instrumentation functions. prcountr.h prtrace.h
+
+intrupt.c
+ Tests PR_Interrupt()
+
+ioconthr.c
+ Tests i/o continuation mechanism in pthreads.
+
+io_timeout.c
+ Test socket i/o timeouts.
+
+io_timeoutk.c
+ Obsolete. Subsumed in io_timeout.c
+
+io_timeoutu.c
+ Obsolete. Subsumed in io_timeout.c
+
+ipv6.c
+ Tests IPv6. IPv6 is not used by NSPR clients.
+
+join.c
+ Tests PR_JoinThread()
+
+joinkk.c
+ Tests PR_JoinThread()
+
+joinku.c
+ Tests PR_JoinThread()
+
+joinuk.c
+ Tests PR_JoinThread()
+
+joinuu.c
+ Tests PR_JoinThread()
+
+layer.c
+ Tests layered I/O.
+
+lazyinit.c
+ Tests implicit initialization.
+
+lltest.c
+ Tests LongLong (64bit integer) arithmentic and conversions.
+
+lock.c
+ Tests PR_Lock() in heavily threaded environment.
+
+lockfile.c
+ Test PR_Lockfile().
+
+logger.c
+ Tests PR_LOG()
+
+makefile
+ The makefile that builds all the tests
+
+many_cv.c
+ Tests aquiring a large number of condition variables.
+
+multiwait.c
+ ???
+
+nbconn.c
+ Test non-blocking connect.
+
+nblayer.c
+ Tests NSPR's layered I/O capability.
+
+nonblock.c
+ Tests operations on non-blocking socket.
+
+op_2long.c
+ Tests PR_Open() where filename is too long.
+
+op_filnf.c
+ Tests PR_Open() where filename is NotFound.
+
+op_filok.c
+ Tests PR_Open() where filename is accessable.
+
+op_noacc.c
+ Tests PR_Open() where file permissions are wrong.
+ Limited use. Windows has no concept of Unix style file permissions.
+
+op_nofil.c
+ Tests PR_Open() where filename does not exist.
+
+parent.c
+ Test parent/child process capability
+
+perf.c
+ Tests and measures context switch times for various thread
+ syncronization functions.
+
+pipeping.c
+ Tests inter-process pipes. Run with pipepong.c
+
+pipepong.c
+ Tests inter-process pipes. Run with pipeping.c
+
+pipeself.c
+ Tests inter-thread pipes.
+
+pollable.c
+ Tests pollable events. prio.h
+
+poll_er.c
+ Tests PR_Poll() where an error is expected.
+
+poll_nm.c
+ Tests PR_Poll() where normal operation is expected.
+
+poll_to.c
+ Tests PR_Poll() where timeout is expected.
+
+prftest.c
+ Tests printf-like formatting.
+
+prftest1.c
+ Obsolete. Subsumed in prftest.c
+
+prftest2.c
+ Obsolete. Subsumed in prftest.c
+
+prfz.c
+ Tests printf handling of (s)size_t formats
+
+priotest.c
+ Limited use. Tests NSPR thread dispatching priority.
+
+provider.c
+
+prpoll.c
+ Tests PR_Poll().
+
+prselect.c
+ Obsolete. PR_Select() is obsolete.
+
+prttools.h
+ Unused file.
+
+ranfile.c
+ Tests random file access.
+
+readme
+ This file.
+
+runtests.ksh
+ A korn shell script that runs a set of tests that should run
+ on any of the NSPR supported platforms.
+
+runtests.pl
+ A perl script to run the test cases. This srcipt runs tests
+ common to all platforms and runs tests applicable to specific
+ platforms. Uses file runtests.txt to control execution.
+
+runtests.txt
+ Control file for perl script: runtests.pl
+
+rwlocktest.c
+ Tests Reader/Writer lock
+
+selct_er.c
+ Obsolete. PR_Select() is obsolete.
+
+selct_nm.c
+ Obsolete. PR_Select() is obsolete.
+
+selct_to.c
+ Obsolete. PR_Select() is obsolete.
+
+select2.c
+ Obsolete. PR_Select() is obsolete.
+
+sel_spd.c
+ Obsolete. PR_Select() is obsolete.
+
+sem.c
+ Obsolete. Semaphores are not supported.
+
+server_test.c
+ Tests sockets by simulating a server in loopback mode.
+ Makes its own client threads.
+
+servr_kk.c
+ Tests client/server sockets, threads using system threads.
+
+servr_ku.c
+ Tests client/server sockets, threads using system and user threads.
+
+servr_uk.c
+ Tests client/server sockets, threads using system and user threads.
+
+servr_uu.c
+ Tests client/server sockets, threads user threads.
+
+short_thread.c
+ Tests short-running threads. Useful for testing for race conditions.
+
+sigpipe.c
+ Tests NSPR's SIGPIPE handler. Unix only.
+
+sleep.c
+ Limited use. Tests sleep capability of platform.
+
+socket.c
+ Tests many socket functions.
+
+sockopt.c
+ Tests setting and getting socket options.
+
+sprintf.c
+ Tests sprintf.
+
+stack.c
+ Test atomic stack operations.
+
+stat.c
+ Tests performance of getfileinfo() vs. stat()
+
+stdio.c
+ Tests NSPR's handling of stdin, stdout, stderr.
+
+strod.c
+ Tests formatting of double precision floating point.
+
+suspend.c
+ Private interfaces PR_SuspendAll(), PR_ResumeAll(), etc.
+
+switch.c
+ Tests thread switching
+
+system.c
+ Tests PR_GetSystemInfo()
+
+testbit.c
+ Tests bit arrays.
+
+testfile.c
+ Tests many file I/O functions.
+
+threads.c
+ Tests thread caching.
+
+thruput.c
+ Tests socket thruput. Must be run by hand as client/server.
+ Does not self terminate.
+
+time.c
+ Incomplete. Limited use.
+
+timemac.c
+ Test time and date functions. Originally for Mac.
+
+timetest.c
+ Tests time conversion over a wide range of dates.
+
+tmoacc.c
+ Server to tmocon.c and writev.c
+ Do not run it by itself.
+
+tmocon.c
+ Client thread to tmoacc.c
+
+tpd.c
+ Tests thread private data.
+
+udpsrv.c
+ Tests UDP socket functions.
+
+ut_ttools.h
+ unused file.
+
+version.c
+ Extract and print library version data.
+
+vercheck.c
+ Test PR_VersionCheck().
+
+writev.c
+ Tests gather-write on a socket. Requires tmoacc.c
+
+xnotify.c
+ Tests cached monitors.
+
+yield.c
+ Limited use
+
+y2k.c
+ Test to verify NSPR's date functions as Y2K compliant.
+
+dll\Makefile
+ makefile for mygetval.c, mysetval.c
+
+dll\mygetval.c
+ Dynamic library test. See also dlltest.c
+
+dll\mysetval.c
+ Dynamic library test. See also dlltest.c
diff --git a/nsprpub/pr/tests/abstract.c b/nsprpub/pr/tests/abstract.c
new file mode 100644
index 0000000000..3d255c1c2b
--- /dev/null
+++ b/nsprpub/pr/tests/abstract.c
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+
+#if defined(LINUX)
+
+#include <string.h>
+#include "nspr.h"
+
+static const char abstractSocketName[] = "\0testsocket";
+
+static void
+ClientThread(void* aArg)
+{
+ PRFileDesc* socket;
+ PRNetAddr addr;
+ PRUint8 buf[1024];
+ PRInt32 len;
+ PRInt32 total;
+
+ addr.local.family = PR_AF_LOCAL;
+ memcpy(addr.local.path, abstractSocketName, sizeof(abstractSocketName));
+
+ socket = PR_OpenTCPSocket(addr.raw.family);
+ if (!socket) {
+ fprintf(stderr, "PR_OpenTCPSokcet failed\n");
+ exit(1);
+ }
+
+ if (PR_Connect(socket, &addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ fprintf(stderr, "PR_Connect failed\n");
+ exit(1);
+ }
+
+ total = 0;
+ while (total < sizeof(buf)) {
+ len = PR_Recv(socket, buf + total, sizeof(buf) - total, 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (len < 1) {
+ fprintf(stderr, "PR_Recv failed\n");
+ exit(1);
+ }
+ total += len;
+ }
+
+ total = 0;
+ while (total < sizeof(buf)) {
+ len = PR_Send(socket, buf + total, sizeof(buf) - total, 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (len < 1) {
+ fprintf(stderr, "PR_Send failed\n");
+ exit(1);
+ }
+ total += len;
+ }
+
+ if (PR_Close(socket) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+}
+
+int
+main()
+{
+ PRFileDesc* socket;
+ PRFileDesc* acceptSocket;
+ PRThread* thread;
+ PRNetAddr addr;
+ PRUint8 buf[1024];
+ PRInt32 len;
+ PRInt32 total;
+
+ addr.local.family = PR_AF_LOCAL;
+ memcpy(addr.local.path, abstractSocketName, sizeof(abstractSocketName));
+
+ socket = PR_OpenTCPSocket(addr.raw.family);
+ if (!socket) {
+ fprintf(stderr, "PR_OpenTCPSocket failed\n");
+ exit(1);
+ }
+ if (PR_Bind(socket, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_Bind failed\n");
+ exit(1);
+ }
+
+ if (PR_Listen(socket, 5) == PR_FAILURE) {
+ fprintf(stderr, "PR_Listen failed\n");
+ exit(1);
+ }
+
+ thread = PR_CreateThread(PR_USER_THREAD, ClientThread, 0, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (!thread) {
+ fprintf(stderr, "PR_CreateThread failed");
+ exit(1);
+ }
+
+ acceptSocket = PR_Accept(socket, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (!acceptSocket) {
+ fprintf(stderr, "PR_Accept failed\n");
+ exit(1);
+ }
+
+ memset(buf, 'A', sizeof(buf));
+
+ total = 0;
+ while (total < sizeof(buf)) {
+ len = PR_Send(acceptSocket, buf + total, sizeof(buf) - total, 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (len < 1) {
+ fprintf(stderr, "PR_Send failed\n");
+ exit(1);
+ }
+ total += len;
+ }
+
+ total = 0;
+ while (total < sizeof(buf)) {
+ len = PR_Recv(acceptSocket, buf + total, sizeof(buf) - total, 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (len < 1) {
+ fprintf(stderr, "PR_Recv failed\n");
+ exit(1);
+ }
+ total += len;
+ }
+
+ if (PR_Close(acceptSocket) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+
+ if (PR_JoinThread(thread) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+
+ if (PR_Close(socket) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ printf("PASS\n");
+ return 0;
+}
+
+#else
+int
+main()
+{
+ printf("PASS\n");
+ return 0;
+}
+#endif
diff --git a/nsprpub/pr/tests/accept.c b/nsprpub/pr/tests/accept.c
new file mode 100644
index 0000000000..3e3850b698
--- /dev/null
+++ b/nsprpub/pr/tests/accept.c
@@ -0,0 +1,508 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+** 1996 - Netscape Communications Corporation
+**
+** Name: accept.c
+**
+** Description: Run accept() sucessful connection tests.
+**
+** Modification History:
+** 04-Jun-97 AGarcia - Reconvert test file to return a 0 for PASS and a 1 for FAIL
+** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 Revert to return code 0 and 1.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+
+#include "nspr.h"
+#include "prpriv.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "plgetopt.h"
+#include "plerror.h"
+
+#define BASE_PORT 10000
+
+#define CLIENT_DATA 128
+
+#define ACCEPT_NORMAL 0x1
+#define ACCEPT_FAST 0x2
+#define ACCEPT_READ 0x3
+#define ACCEPT_READ_FAST 0x4
+#define ACCEPT_READ_FAST_CB 0x5
+
+#define CLIENT_NORMAL 0x1
+#define CLIENT_TIMEOUT_ACCEPT 0x2
+#define CLIENT_TIMEOUT_SEND 0x3
+
+#define SERVER_MAX_BIND_COUNT 100
+
+#if defined(XP_OS2)
+#define TIMEOUTSECS 10
+#else
+#define TIMEOUTSECS 2
+#endif
+PRIntervalTime timeoutTime;
+
+static PRInt32 count = 1;
+static PRFileDesc *output;
+static PRNetAddr serverAddr;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+static PRInt32 clientCommand;
+static PRInt32 iterations;
+static PRStatus rv;
+static PRFileDesc *listenSock;
+static PRFileDesc *clientSock = NULL;
+static PRNetAddr listenAddr;
+static PRNetAddr clientAddr;
+static PRThread *clientThread;
+static PRNetAddr *raddr;
+static char buf[4096 + 2*sizeof(PRNetAddr) + 32];
+static PRInt32 status;
+static PRInt32 bytesRead;
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+void Test_Assert(const char *msg, const char *file, PRIntn line)
+{
+ failed_already=1;
+ if (debug_mode) {
+ PR_fprintf(output, "@%s:%d ", file, line);
+ PR_fprintf(output, msg);
+ }
+} /* Test_Assert */
+
+#define TEST_ASSERT(expr) \
+ if (!(expr)) Test_Assert(#expr, __FILE__, __LINE__)
+
+#ifdef WINNT
+#define CALLBACK_MAGIC 0x12345678
+
+void timeout_callback(void *magic)
+{
+ TEST_ASSERT(magic == (void *)CALLBACK_MAGIC);
+ if (debug_mode) {
+ PR_fprintf(output, "timeout callback called okay\n");
+ }
+}
+#endif
+
+
+static void PR_CALLBACK
+ClientThread(void *_action)
+{
+ PRInt32 action = * (PRInt32 *) _action;
+ PRInt32 iterations = count;
+ PRFileDesc *sock = NULL;
+
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = listenAddr.inet.port;
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+
+ for (; iterations--;) {
+ PRInt32 rv;
+ char buf[CLIENT_DATA];
+
+ memset(buf, 0xaf, sizeof(buf)); /* initialize with arbitrary data */
+ sock = PR_NewTCPSocket();
+ if (!sock) {
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ else {
+ PR_fprintf(output, "client: unable to create socket\n");
+ }
+ return;
+ }
+
+ if (action != CLIENT_TIMEOUT_ACCEPT) {
+
+ if ((rv = PR_Connect(sock, &serverAddr,
+ timeoutTime)) < 0) {
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ else
+ PR_fprintf(output,
+ "client: unable to connect to server (%ld, %ld, %ld, %ld)\n",
+ iterations, rv, PR_GetError(), PR_GetOSError());
+ goto ErrorExit;
+ }
+
+ if (action != CLIENT_TIMEOUT_SEND) {
+ if ((rv = PR_Send(sock, buf, CLIENT_DATA,
+ 0, timeoutTime))< 0) {
+ if (!debug_mode) {
+ failed_already=1;
+ } else {
+ PR_fprintf(output,
+ "client: unable to send to server (%d, %ld, %ld)\n",
+ CLIENT_DATA, rv, PR_GetError());
+ }
+ goto ErrorExit;
+ }
+ } else {
+ PR_Sleep(PR_SecondsToInterval(TIMEOUTSECS + 1));
+ }
+ } else {
+ PR_Sleep(PR_SecondsToInterval(TIMEOUTSECS + 1));
+ }
+ if (debug_mode) {
+ PR_fprintf(output, ".");
+ }
+ PR_Close(sock);
+ sock = NULL;
+ }
+ if (debug_mode) {
+ PR_fprintf(output, "\n");
+ }
+
+ErrorExit:
+ if (sock != NULL) {
+ PR_Close(sock);
+ }
+}
+
+
+static void
+RunTest(PRInt32 acceptType, PRInt32 clientAction)
+{
+ int i;
+
+ /* First bind to the socket */
+ listenSock = PR_NewTCPSocket();
+ if (!listenSock) {
+ failed_already=1;
+ if (debug_mode) {
+ PR_fprintf(output, "unable to create listen socket\n");
+ }
+ return;
+ }
+ memset(&listenAddr, 0, sizeof(listenAddr));
+ listenAddr.inet.family = PR_AF_INET;
+ listenAddr.inet.port = PR_htons(BASE_PORT);
+ listenAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ /*
+ * try a few times to bind server's address, if addresses are in
+ * use
+ */
+ i = 0;
+ while (PR_Bind(listenSock, &listenAddr) == PR_FAILURE) {
+ if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
+ listenAddr.inet.port += 2;
+ if (i++ < SERVER_MAX_BIND_COUNT) {
+ continue;
+ }
+ }
+ failed_already=1;
+ if (debug_mode) {
+ PR_fprintf(output,"accept: ERROR - PR_Bind failed\n");
+ }
+ return;
+ }
+
+
+ rv = PR_Listen(listenSock, 100);
+ if (rv == PR_FAILURE) {
+ failed_already=1;
+ if (debug_mode) {
+ PR_fprintf(output, "unable to listen\n");
+ }
+ return;
+ }
+
+ clientCommand = clientAction;
+ clientThread = PR_CreateThread(PR_USER_THREAD, ClientThread,
+ (void *)&clientCommand, PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 0);
+ if (!clientThread) {
+ failed_already=1;
+ if (debug_mode) {
+ PR_fprintf(output, "error creating client thread\n");
+ }
+ return;
+ }
+
+ iterations = count;
+ for (; iterations--;) {
+ switch (acceptType) {
+ case ACCEPT_NORMAL:
+ clientSock = PR_Accept(listenSock, &clientAddr,
+ timeoutTime);
+ switch(clientAction) {
+ case CLIENT_TIMEOUT_ACCEPT:
+ TEST_ASSERT(clientSock == 0);
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ case CLIENT_NORMAL:
+ TEST_ASSERT(clientSock);
+ bytesRead = PR_Recv(clientSock,
+ buf, CLIENT_DATA, 0, timeoutTime);
+ TEST_ASSERT(bytesRead == CLIENT_DATA);
+ break;
+ case CLIENT_TIMEOUT_SEND:
+ TEST_ASSERT(clientSock);
+ bytesRead = PR_Recv(clientSock,
+ buf, CLIENT_DATA, 0, timeoutTime);
+ TEST_ASSERT(bytesRead == -1);
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ }
+ break;
+ case ACCEPT_READ:
+ status = PR_AcceptRead(listenSock, &clientSock,
+ &raddr, buf, CLIENT_DATA, timeoutTime);
+ switch(clientAction) {
+ case CLIENT_TIMEOUT_ACCEPT:
+ /* Invalid test case */
+ TEST_ASSERT(0);
+ break;
+ case CLIENT_NORMAL:
+ TEST_ASSERT(clientSock);
+ TEST_ASSERT(status == CLIENT_DATA);
+ break;
+ case CLIENT_TIMEOUT_SEND:
+ TEST_ASSERT(status == -1);
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ }
+ break;
+#ifdef WINNT
+ case ACCEPT_FAST:
+ clientSock = PR_NTFast_Accept(listenSock,
+ &clientAddr, timeoutTime);
+ switch(clientAction) {
+ case CLIENT_TIMEOUT_ACCEPT:
+ TEST_ASSERT(clientSock == 0);
+ if (debug_mode) {
+ PR_fprintf(output, "PR_GetError is %ld\n", PR_GetError());
+ }
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ case CLIENT_NORMAL:
+ TEST_ASSERT(clientSock);
+ bytesRead = PR_Recv(clientSock,
+ buf, CLIENT_DATA, 0, timeoutTime);
+ TEST_ASSERT(bytesRead == CLIENT_DATA);
+ break;
+ case CLIENT_TIMEOUT_SEND:
+ TEST_ASSERT(clientSock);
+ bytesRead = PR_Recv(clientSock,
+ buf, CLIENT_DATA, 0, timeoutTime);
+ TEST_ASSERT(bytesRead == -1);
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ }
+ break;
+ break;
+ case ACCEPT_READ_FAST:
+ status = PR_NTFast_AcceptRead(listenSock,
+ &clientSock, &raddr, buf, 4096, timeoutTime);
+ switch(clientAction) {
+ case CLIENT_TIMEOUT_ACCEPT:
+ /* Invalid test case */
+ TEST_ASSERT(0);
+ break;
+ case CLIENT_NORMAL:
+ TEST_ASSERT(clientSock);
+ TEST_ASSERT(status == CLIENT_DATA);
+ break;
+ case CLIENT_TIMEOUT_SEND:
+ TEST_ASSERT(clientSock == NULL);
+ TEST_ASSERT(status == -1);
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ }
+ break;
+ case ACCEPT_READ_FAST_CB:
+ status = PR_NTFast_AcceptRead_WithTimeoutCallback(
+ listenSock, &clientSock, &raddr, buf, 4096,
+ timeoutTime, timeout_callback, (void *)CALLBACK_MAGIC);
+ switch(clientAction) {
+ case CLIENT_TIMEOUT_ACCEPT:
+ /* Invalid test case */
+ TEST_ASSERT(0);
+ break;
+ case CLIENT_NORMAL:
+ TEST_ASSERT(clientSock);
+ TEST_ASSERT(status == CLIENT_DATA);
+ break;
+ case CLIENT_TIMEOUT_SEND:
+ if (debug_mode) {
+ PR_fprintf(output, "clientSock = 0x%8.8lx\n", clientSock);
+ }
+ TEST_ASSERT(clientSock == NULL);
+ TEST_ASSERT(status == -1);
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ }
+ break;
+#endif
+ }
+ if (clientSock != NULL) {
+ PR_Close(clientSock);
+ clientSock = NULL;
+ }
+ }
+ PR_Close(listenSock);
+
+ PR_JoinThread(clientThread);
+}
+
+
+void AcceptUpdatedTest(void)
+{
+ RunTest(ACCEPT_NORMAL, CLIENT_NORMAL);
+}
+void AcceptNotUpdatedTest(void)
+{
+ RunTest(ACCEPT_FAST, CLIENT_NORMAL);
+}
+void AcceptReadTest(void)
+{
+ RunTest(ACCEPT_READ, CLIENT_NORMAL);
+}
+void AcceptReadNotUpdatedTest(void)
+{
+ RunTest(ACCEPT_READ_FAST, CLIENT_NORMAL);
+}
+void AcceptReadCallbackTest(void)
+{
+ RunTest(ACCEPT_READ_FAST_CB, CLIENT_NORMAL);
+}
+
+void TimeoutAcceptUpdatedTest(void)
+{
+ RunTest(ACCEPT_NORMAL, CLIENT_TIMEOUT_ACCEPT);
+}
+void TimeoutAcceptNotUpdatedTest(void)
+{
+ RunTest(ACCEPT_FAST, CLIENT_TIMEOUT_ACCEPT);
+}
+void TimeoutAcceptReadCallbackTest(void)
+{
+ RunTest(ACCEPT_READ_FAST_CB, CLIENT_TIMEOUT_ACCEPT);
+}
+
+void TimeoutReadUpdatedTest(void)
+{
+ RunTest(ACCEPT_NORMAL, CLIENT_TIMEOUT_SEND);
+}
+void TimeoutReadNotUpdatedTest(void)
+{
+ RunTest(ACCEPT_FAST, CLIENT_TIMEOUT_SEND);
+}
+void TimeoutReadReadTest(void)
+{
+ RunTest(ACCEPT_READ, CLIENT_TIMEOUT_SEND);
+}
+void TimeoutReadReadNotUpdatedTest(void)
+{
+ RunTest(ACCEPT_READ_FAST, CLIENT_TIMEOUT_SEND);
+}
+void TimeoutReadReadCallbackTest(void)
+{
+ RunTest(ACCEPT_READ_FAST_CB, CLIENT_TIMEOUT_SEND);
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+ if (debug_mode) {
+ PR_fprintf(output, "%40s: %6.2f usec\n", msg, d / count);
+ }
+
+}
+
+int main(int argc, char **argv)
+{
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name [-d] [-c n]
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Gdc:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'G': /* global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'c': /* loop counter */
+ count = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ output = PR_STDERR;
+ PR_STDIO_INIT();
+
+ timeoutTime = PR_SecondsToInterval(TIMEOUTSECS);
+ if (debug_mode) {
+ PR_fprintf(output, "\nRun accept() sucessful connection tests\n");
+ }
+
+ Measure(AcceptUpdatedTest, "PR_Accept()");
+ Measure(AcceptReadTest, "PR_AcceptRead()");
+#ifdef WINNT
+ Measure(AcceptNotUpdatedTest, "PR_NTFast_Accept()");
+ Measure(AcceptReadNotUpdatedTest, "PR_NTFast_AcceptRead()");
+ Measure(AcceptReadCallbackTest, "PR_NTFast_AcceptRead_WithTimeoutCallback()");
+#endif
+ if (debug_mode) {
+ PR_fprintf(output, "\nRun accept() timeout in the accept tests\n");
+ }
+#ifdef WINNT
+ Measure(TimeoutReadReadCallbackTest, "PR_NTFast_AcceptRead_WithTimeoutCallback()");
+#endif
+ Measure(TimeoutReadUpdatedTest, "PR_Accept()");
+ if (debug_mode) {
+ PR_fprintf(output, "\nRun accept() timeout in the read tests\n");
+ }
+ Measure(TimeoutReadReadTest, "PR_AcceptRead()");
+#ifdef WINNT
+ Measure(TimeoutReadNotUpdatedTest, "PR_NTFast_Accept()");
+ Measure(TimeoutReadReadNotUpdatedTest, "PR_NTFast_AcceptRead()");
+ Measure(TimeoutReadReadCallbackTest, "PR_NTFast_AcceptRead_WithTimeoutCallback()");
+#endif
+ PR_fprintf(output, "%s\n", (failed_already) ? "FAIL" : "PASS");
+ return failed_already;
+}
diff --git a/nsprpub/pr/tests/acceptread.c b/nsprpub/pr/tests/acceptread.c
new file mode 100644
index 0000000000..090b084555
--- /dev/null
+++ b/nsprpub/pr/tests/acceptread.c
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <prio.h>
+#include <prprf.h>
+#include <prinit.h>
+#include <prnetdb.h>
+#include <prinrval.h>
+#include <prthread.h>
+
+#include <plerror.h>
+
+#include <stdlib.h>
+
+#ifdef DEBUG
+#define PORT_INC_DO +100
+#else
+#define PORT_INC_DO
+#endif
+#ifdef IS_64
+#define PORT_INC_3264 +200
+#else
+#define PORT_INC_3264
+#endif
+
+#define DEFAULT_PORT 12273 PORT_INC_DO PORT_INC_3264
+#define GET "GET / HTTP/1.0\n\n"
+static PRFileDesc *std_out, *err_out;
+static PRIntervalTime write_dally, accept_timeout;
+
+static PRStatus PrintAddress(const PRNetAddr* address)
+{
+ char buffer[100];
+ PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer));
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err_out, "PR_NetAddrToString");
+ }
+ else PR_fprintf(
+ std_out, "Accepted connection from (0x%p)%s:%d\n",
+ address, buffer, address->inet.port);
+ return rv;
+} /* PrintAddress */
+
+static void ConnectingThread(void *arg)
+{
+ PRInt32 nbytes;
+ char buf[1024];
+ PRFileDesc *sock;
+ PRNetAddr peer_addr, *addr;
+
+ addr = (PRNetAddr*)arg;
+
+ sock = PR_NewTCPSocket();
+ if (sock == NULL)
+ {
+ PL_FPrintError(err_out, "PR_NewTCPSocket (client) failed");
+ PR_ProcessExit(1);
+ }
+
+ if (PR_Connect(sock, addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE)
+ {
+ PL_FPrintError(err_out, "PR_Connect (client) failed");
+ PR_ProcessExit(1);
+ }
+ if (PR_GetPeerName(sock, &peer_addr) == PR_FAILURE)
+ {
+ PL_FPrintError(err_out, "PR_GetPeerName (client) failed");
+ PR_ProcessExit(1);
+ }
+
+ /*
+ ** Then wait between the connection coming up and sending the expected
+ ** data. At some point in time, the server should fail due to a timeou
+ ** on the AcceptRead() operation, which according to the document is
+ ** only due to the read() portion.
+ */
+ PR_Sleep(write_dally);
+
+ nbytes = PR_Send(sock, GET, sizeof(GET), 0, PR_INTERVAL_NO_TIMEOUT);
+ if (nbytes == -1) {
+ PL_FPrintError(err_out, "PR_Send (client) failed");
+ }
+
+ nbytes = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT);
+ if (nbytes == -1) {
+ PL_FPrintError(err_out, "PR_Recv (client) failed");
+ }
+ else
+ {
+ PR_fprintf(std_out, "PR_Recv (client) succeeded: %d bytes\n", nbytes);
+ buf[sizeof(buf) - 1] = '\0';
+ PR_fprintf(std_out, "%s\n", buf);
+ }
+
+ if (PR_FAILURE == PR_Shutdown(sock, PR_SHUTDOWN_BOTH)) {
+ PL_FPrintError(err_out, "PR_Shutdown (client) failed");
+ }
+
+ if (PR_FAILURE == PR_Close(sock)) {
+ PL_FPrintError(err_out, "PR_Close (client) failed");
+ }
+
+ return;
+} /* ConnectingThread */
+
+#define BUF_SIZE 117
+static void AcceptingThread(void *arg)
+{
+ PRStatus rv;
+ PRInt32 bytes;
+ PRSize buf_size = BUF_SIZE;
+ PRUint8 buf[BUF_SIZE + (2 * sizeof(PRNetAddr)) + 32];
+ PRNetAddr *accept_addr, *listen_addr = (PRNetAddr*)arg;
+ PRFileDesc *accept_sock, *listen_sock = PR_NewTCPSocket();
+ PRSocketOptionData sock_opt;
+
+ if (NULL == listen_sock)
+ {
+ PL_FPrintError(err_out, "PR_NewTCPSocket (server) failed");
+ PR_ProcessExit(1);
+ }
+ sock_opt.option = PR_SockOpt_Reuseaddr;
+ sock_opt.value.reuse_addr = PR_TRUE;
+ rv = PR_SetSocketOption(listen_sock, &sock_opt);
+ if (PR_FAILURE == rv)
+ {
+ PL_FPrintError(err_out, "PR_SetSocketOption (server) failed");
+ PR_ProcessExit(1);
+ }
+ rv = PR_Bind(listen_sock, listen_addr);
+ if (PR_FAILURE == rv)
+ {
+ PL_FPrintError(err_out, "PR_Bind (server) failed");
+ PR_ProcessExit(1);
+ }
+ rv = PR_Listen(listen_sock, 10);
+ if (PR_FAILURE == rv)
+ {
+ PL_FPrintError(err_out, "PR_Listen (server) failed");
+ PR_ProcessExit(1);
+ }
+ bytes = PR_AcceptRead(
+ listen_sock, &accept_sock, &accept_addr, buf, buf_size, accept_timeout);
+
+ if (-1 == bytes) {
+ PL_FPrintError(err_out, "PR_AcceptRead (server) failed");
+ }
+ else
+ {
+ PrintAddress(accept_addr);
+ PR_fprintf(
+ std_out, "(Server) read [0x%p..0x%p) %s\n",
+ buf, &buf[BUF_SIZE], buf);
+ bytes = PR_Write(accept_sock, buf, bytes);
+ rv = PR_Shutdown(accept_sock, PR_SHUTDOWN_BOTH);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err_out, "PR_Shutdown (server) failed");
+ }
+ }
+
+ if (-1 != bytes)
+ {
+ rv = PR_Close(accept_sock);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err_out, "PR_Close (server) failed");
+ }
+ }
+
+ rv = PR_Close(listen_sock);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err_out, "PR_Close (server) failed");
+ }
+} /* AcceptingThread */
+
+int main(int argc, char **argv)
+{
+ PRHostEnt he;
+ PRStatus status;
+ PRIntn next_index;
+ PRUint16 port_number;
+ char netdb_buf[PR_NETDB_BUF_SIZE];
+ PRNetAddr client_addr, server_addr;
+ PRThread *client_thread, *server_thread;
+ PRIntervalTime delta = PR_MillisecondsToInterval(500);
+
+ err_out = PR_STDERR;
+ std_out = PR_STDOUT;
+ accept_timeout = PR_SecondsToInterval(2);
+
+ if (argc != 2 && argc != 3) {
+ port_number = DEFAULT_PORT;
+ }
+ else {
+ port_number = (PRUint16)atoi(argv[(argc == 2) ? 1 : 2]);
+ }
+
+ status = PR_InitializeNetAddr(PR_IpAddrAny, port_number, &server_addr);
+ if (PR_SUCCESS != status)
+ {
+ PL_FPrintError(err_out, "PR_InitializeNetAddr failed");
+ PR_ProcessExit(1);
+ }
+ if (argc < 3)
+ {
+ status = PR_InitializeNetAddr(
+ PR_IpAddrLoopback, port_number, &client_addr);
+ if (PR_SUCCESS != status)
+ {
+ PL_FPrintError(err_out, "PR_InitializeNetAddr failed");
+ PR_ProcessExit(1);
+ }
+ }
+ else
+ {
+ status = PR_GetHostByName(
+ argv[1], netdb_buf, sizeof(netdb_buf), &he);
+ if (status == PR_FAILURE)
+ {
+ PL_FPrintError(err_out, "PR_GetHostByName failed");
+ PR_ProcessExit(1);
+ }
+ next_index = PR_EnumerateHostEnt(0, &he, port_number, &client_addr);
+ if (next_index == -1)
+ {
+ PL_FPrintError(err_out, "PR_EnumerateHostEnt failed");
+ PR_ProcessExit(1);
+ }
+ }
+
+ for (
+ write_dally = 0;
+ write_dally < accept_timeout + (2 * delta);
+ write_dally += delta)
+ {
+ PR_fprintf(
+ std_out, "Testing w/ write_dally = %d msec\n",
+ PR_IntervalToMilliseconds(write_dally));
+ server_thread = PR_CreateThread(
+ PR_USER_THREAD, AcceptingThread, &server_addr,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (server_thread == NULL)
+ {
+ PL_FPrintError(err_out, "PR_CreateThread (server) failed");
+ PR_ProcessExit(1);
+ }
+
+ PR_Sleep(delta); /* let the server pot thicken */
+
+ client_thread = PR_CreateThread(
+ PR_USER_THREAD, ConnectingThread, &client_addr,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (client_thread == NULL)
+ {
+ PL_FPrintError(err_out, "PR_CreateThread (client) failed");
+ PR_ProcessExit(1);
+ }
+
+ if (PR_JoinThread(client_thread) == PR_FAILURE) {
+ PL_FPrintError(err_out, "PR_JoinThread (client) failed");
+ }
+
+ if (PR_JoinThread(server_thread) == PR_FAILURE) {
+ PL_FPrintError(err_out, "PR_JoinThread (server) failed");
+ }
+ }
+
+ return 0;
+}
diff --git a/nsprpub/pr/tests/acceptreademu.c b/nsprpub/pr/tests/acceptreademu.c
new file mode 100644
index 0000000000..485b9d5025
--- /dev/null
+++ b/nsprpub/pr/tests/acceptreademu.c
@@ -0,0 +1,300 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This test is the same as acceptread.c except that it uses the
+ * emulated acceptread method instead of the regular acceptread.
+ */
+
+#include <prio.h>
+#include <prprf.h>
+#include <prinit.h>
+#include <prnetdb.h>
+#include <prinrval.h>
+#include <prthread.h>
+#include <pprio.h>
+
+#include <plerror.h>
+
+#include <stdlib.h>
+
+#ifdef DEBUG
+#define PORT_INC_DO +100
+#else
+#define PORT_INC_DO
+#endif
+#ifdef IS_64
+#define PORT_INC_3264 +200
+#else
+#define PORT_INC_3264
+#endif
+
+#define DEFAULT_PORT 12273 PORT_INC_DO PORT_INC_3264
+#define GET "GET / HTTP/1.0\n\n"
+static PRFileDesc *std_out, *err_out;
+static PRIntervalTime write_dally, accept_timeout;
+static PRDescIdentity emu_layer_ident;
+static PRIOMethods emu_layer_methods;
+
+/* the acceptread method in emu_layer_methods */
+static PRInt32 PR_CALLBACK emu_AcceptRead(PRFileDesc *sd, PRFileDesc **nd,
+ PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime timeout)
+{
+ return PR_EmulateAcceptRead(sd, nd, raddr, buf, amount, timeout);
+}
+
+static PRStatus PrintAddress(const PRNetAddr* address)
+{
+ char buffer[100];
+ PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer));
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err_out, "PR_NetAddrToString");
+ }
+ else PR_fprintf(
+ std_out, "Accepted connection from (0x%p)%s:%d\n",
+ address, buffer, address->inet.port);
+ return rv;
+} /* PrintAddress */
+
+static void ConnectingThread(void *arg)
+{
+ PRInt32 nbytes;
+ char buf[1024];
+ PRFileDesc *sock;
+ PRNetAddr peer_addr, *addr;
+
+ addr = (PRNetAddr*)arg;
+
+ sock = PR_NewTCPSocket();
+ if (sock == NULL)
+ {
+ PL_FPrintError(err_out, "PR_NewTCPSocket (client) failed");
+ PR_ProcessExit(1);
+ }
+
+ if (PR_Connect(sock, addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE)
+ {
+ PL_FPrintError(err_out, "PR_Connect (client) failed");
+ PR_ProcessExit(1);
+ }
+ if (PR_GetPeerName(sock, &peer_addr) == PR_FAILURE)
+ {
+ PL_FPrintError(err_out, "PR_GetPeerName (client) failed");
+ PR_ProcessExit(1);
+ }
+
+ /*
+ ** Then wait between the connection coming up and sending the expected
+ ** data. At some point in time, the server should fail due to a timeou
+ ** on the AcceptRead() operation, which according to the document is
+ ** only due to the read() portion.
+ */
+ PR_Sleep(write_dally);
+
+ nbytes = PR_Send(sock, GET, sizeof(GET), 0, PR_INTERVAL_NO_TIMEOUT);
+ if (nbytes == -1) {
+ PL_FPrintError(err_out, "PR_Send (client) failed");
+ }
+
+ nbytes = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT);
+ if (nbytes == -1) {
+ PL_FPrintError(err_out, "PR_Recv (client) failed");
+ }
+ else
+ {
+ PR_fprintf(std_out, "PR_Recv (client) succeeded: %d bytes\n", nbytes);
+ buf[sizeof(buf) - 1] = '\0';
+ PR_fprintf(std_out, "%s\n", buf);
+ }
+
+ if (PR_FAILURE == PR_Shutdown(sock, PR_SHUTDOWN_BOTH)) {
+ PL_FPrintError(err_out, "PR_Shutdown (client) failed");
+ }
+
+ if (PR_FAILURE == PR_Close(sock)) {
+ PL_FPrintError(err_out, "PR_Close (client) failed");
+ }
+
+ return;
+} /* ConnectingThread */
+
+#define BUF_SIZE 117
+static void AcceptingThread(void *arg)
+{
+ PRStatus rv;
+ PRInt32 bytes;
+ PRSize buf_size = BUF_SIZE;
+ PRUint8 buf[BUF_SIZE + (2 * sizeof(PRNetAddr)) + 32];
+ PRNetAddr *accept_addr, *listen_addr = (PRNetAddr*)arg;
+ PRFileDesc *accept_sock, *listen_sock = PR_NewTCPSocket();
+ PRFileDesc *layer;
+ PRSocketOptionData sock_opt;
+
+ if (NULL == listen_sock)
+ {
+ PL_FPrintError(err_out, "PR_NewTCPSocket (server) failed");
+ PR_ProcessExit(1);
+ }
+ layer = PR_CreateIOLayerStub(emu_layer_ident, &emu_layer_methods);
+ if (NULL == layer)
+ {
+ PL_FPrintError(err_out, "PR_CreateIOLayerStub (server) failed");
+ PR_ProcessExit(1);
+ }
+ if (PR_PushIOLayer(listen_sock, PR_TOP_IO_LAYER, layer) == PR_FAILURE)
+ {
+ PL_FPrintError(err_out, "PR_PushIOLayer (server) failed");
+ PR_ProcessExit(1);
+ }
+ sock_opt.option = PR_SockOpt_Reuseaddr;
+ sock_opt.value.reuse_addr = PR_TRUE;
+ rv = PR_SetSocketOption(listen_sock, &sock_opt);
+ if (PR_FAILURE == rv)
+ {
+ PL_FPrintError(err_out, "PR_SetSocketOption (server) failed");
+ PR_ProcessExit(1);
+ }
+ rv = PR_Bind(listen_sock, listen_addr);
+ if (PR_FAILURE == rv)
+ {
+ PL_FPrintError(err_out, "PR_Bind (server) failed");
+ PR_ProcessExit(1);
+ }
+ rv = PR_Listen(listen_sock, 10);
+ if (PR_FAILURE == rv)
+ {
+ PL_FPrintError(err_out, "PR_Listen (server) failed");
+ PR_ProcessExit(1);
+ }
+ bytes = PR_AcceptRead(
+ listen_sock, &accept_sock, &accept_addr, buf, buf_size, accept_timeout);
+
+ if (-1 == bytes) {
+ PL_FPrintError(err_out, "PR_AcceptRead (server) failed");
+ }
+ else
+ {
+ PrintAddress(accept_addr);
+ PR_fprintf(
+ std_out, "(Server) read [0x%p..0x%p) %s\n",
+ buf, &buf[BUF_SIZE], buf);
+ bytes = PR_Write(accept_sock, buf, bytes);
+ rv = PR_Shutdown(accept_sock, PR_SHUTDOWN_BOTH);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err_out, "PR_Shutdown (server) failed");
+ }
+ }
+
+ if (-1 != bytes)
+ {
+ rv = PR_Close(accept_sock);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err_out, "PR_Close (server) failed");
+ }
+ }
+
+ rv = PR_Close(listen_sock);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err_out, "PR_Close (server) failed");
+ }
+} /* AcceptingThread */
+
+int main(int argc, char **argv)
+{
+ PRHostEnt he;
+ PRStatus status;
+ PRIntn next_index;
+ PRUint16 port_number;
+ char netdb_buf[PR_NETDB_BUF_SIZE];
+ PRNetAddr client_addr, server_addr;
+ PRThread *client_thread, *server_thread;
+ PRIntervalTime delta = PR_MillisecondsToInterval(500);
+
+ err_out = PR_STDERR;
+ std_out = PR_STDOUT;
+ accept_timeout = PR_SecondsToInterval(2);
+ emu_layer_ident = PR_GetUniqueIdentity("Emulated AcceptRead");
+ emu_layer_methods = *PR_GetDefaultIOMethods();
+ emu_layer_methods.acceptread = emu_AcceptRead;
+
+ if (argc != 2 && argc != 3) {
+ port_number = DEFAULT_PORT;
+ }
+ else {
+ port_number = (PRUint16)atoi(argv[(argc == 2) ? 1 : 2]);
+ }
+
+ status = PR_InitializeNetAddr(PR_IpAddrAny, port_number, &server_addr);
+ if (PR_SUCCESS != status)
+ {
+ PL_FPrintError(err_out, "PR_InitializeNetAddr failed");
+ PR_ProcessExit(1);
+ }
+ if (argc < 3)
+ {
+ status = PR_InitializeNetAddr(
+ PR_IpAddrLoopback, port_number, &client_addr);
+ if (PR_SUCCESS != status)
+ {
+ PL_FPrintError(err_out, "PR_InitializeNetAddr failed");
+ PR_ProcessExit(1);
+ }
+ }
+ else
+ {
+ status = PR_GetHostByName(
+ argv[1], netdb_buf, sizeof(netdb_buf), &he);
+ if (status == PR_FAILURE)
+ {
+ PL_FPrintError(err_out, "PR_GetHostByName failed");
+ PR_ProcessExit(1);
+ }
+ next_index = PR_EnumerateHostEnt(0, &he, port_number, &client_addr);
+ if (next_index == -1)
+ {
+ PL_FPrintError(err_out, "PR_EnumerateHostEnt failed");
+ PR_ProcessExit(1);
+ }
+ }
+
+ for (
+ write_dally = 0;
+ write_dally < accept_timeout + (2 * delta);
+ write_dally += delta)
+ {
+ PR_fprintf(
+ std_out, "Testing w/ write_dally = %d msec\n",
+ PR_IntervalToMilliseconds(write_dally));
+ server_thread = PR_CreateThread(
+ PR_USER_THREAD, AcceptingThread, &server_addr,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (server_thread == NULL)
+ {
+ PL_FPrintError(err_out, "PR_CreateThread (server) failed");
+ PR_ProcessExit(1);
+ }
+
+ PR_Sleep(delta); /* let the server pot thicken */
+
+ client_thread = PR_CreateThread(
+ PR_USER_THREAD, ConnectingThread, &client_addr,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (client_thread == NULL)
+ {
+ PL_FPrintError(err_out, "PR_CreateThread (client) failed");
+ PR_ProcessExit(1);
+ }
+
+ if (PR_JoinThread(client_thread) == PR_FAILURE) {
+ PL_FPrintError(err_out, "PR_JoinThread (client) failed");
+ }
+
+ if (PR_JoinThread(server_thread) == PR_FAILURE) {
+ PL_FPrintError(err_out, "PR_JoinThread (server) failed");
+ }
+ }
+
+ return 0;
+}
diff --git a/nsprpub/pr/tests/addrstr.c b/nsprpub/pr/tests/addrstr.c
new file mode 100644
index 0000000000..b6736a74bf
--- /dev/null
+++ b/nsprpub/pr/tests/addrstr.c
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prnetdb.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+const char *testaddrs[] = {
+ "::", "::",
+ "::1", "::1",
+ "::ffff", "::ffff",
+ "::1:0", "::0.1.0.0",
+ "::127.0.0.1", "::127.0.0.1",
+ "::FFFF:127.0.0.1", "::ffff:127.0.0.1",
+ "::FFFE:9504:3501", "::fffe:9504:3501",
+ "0:0:1:0:35c:0:0:0", "0:0:1:0:35c::",
+ "0:0:3f4c:0:0:4552:0:0", "::3f4c:0:0:4552:0:0",
+ "0:0:1245:0:0:0:0567:0", "0:0:1245::567:0",
+ "0:1:2:3:4:5:6:7", "0:1:2:3:4:5:6:7",
+ "1:2:3:0:4:5:6:7", "1:2:3:0:4:5:6:7",
+ "1:2:3:4:5:6:7:0", "1:2:3:4:5:6:7:0",
+ "1:2:3:4:5:6:7:8", "1:2:3:4:5:6:7:8",
+ "1:2:3:4:5:6::7", "1:2:3:4:5:6:0:7",
+ 0
+};
+
+const char *badaddrs[] = {
+ "::.1.2.3",
+ "ffff::.1.2.3",
+ "1:2:3:4:5:6:7::8",
+ "1:2:3:4:5:6::7:8",
+ "::ff99.2.3.4",
+ 0
+};
+
+int failed_already = 0;
+
+int main(int argc, char **argv)
+{
+ const char **nexttestaddr = testaddrs;
+ const char **nextbadaddr = badaddrs;
+ const char *in, *expected_out;
+ PRNetAddr addr;
+ char buf[256];
+ PRStatus rv;
+
+ while ((in = *nexttestaddr++) != 0) {
+ expected_out = *nexttestaddr++;
+ rv = PR_StringToNetAddr(in, &addr);
+ if (rv) {
+ printf("cannot convert %s to addr: %d\n", in, rv);
+ failed_already = 1;
+ continue;
+ }
+ rv = PR_NetAddrToString(&addr, buf, sizeof(buf));
+ if (rv) {
+ printf("cannot convert %s back to string: %d\n", in, rv);
+ failed_already = 1;
+ continue;
+ }
+ if (strcmp(buf, expected_out)) {
+ /* This is not necessarily an error */
+ printf("%s expected %s got %s\n", in, expected_out, buf);
+ }
+ }
+ while ((in = *nextbadaddr++) != 0) {
+ if (PR_StringToNetAddr(in, &addr) == PR_SUCCESS) {
+ printf("converted bad addr %s\n", in);
+ failed_already = 1;
+ }
+ }
+ if (failed_already) {
+ printf("FAIL\n");
+ return 1;
+ }
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/affinity.c b/nsprpub/pr/tests/affinity.c
new file mode 100644
index 0000000000..14d08efc11
--- /dev/null
+++ b/nsprpub/pr/tests/affinity.c
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+#include "pprthred.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/*
+ * Test PR_GetThreadAffinityMask
+ * The function is called by each of local, global and global bound threads
+ * The test should be run on both single and multi-cpu systems
+ */
+static void PR_CALLBACK thread_start(void *arg)
+{
+ PRUint32 mask = 0;
+
+ if (PR_GetThreadAffinityMask(PR_GetCurrentThread(), &mask)) {
+ printf("\tthread_start: PR_GetCurrentThreadAffinityMask failed\n");
+ }
+ else {
+ printf("\tthread_start: AffinityMask = 0x%x\n",mask);
+ }
+
+}
+
+int main(int argc, char **argv)
+{
+ PRThread *t;
+
+ printf("main: creating local thread\n");
+
+ t = PR_CreateThread(PR_USER_THREAD,
+ thread_start, 0,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ if (NULL == t) {
+ printf("main: cannot create local thread\n");
+ exit(1);
+ }
+
+ PR_JoinThread(t);
+
+ printf("main: creating global thread\n");
+ t = PR_CreateThread(PR_USER_THREAD,
+ thread_start, 0,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ if (NULL == t) {
+ printf("main: cannot create global thread\n");
+ exit(1);
+ }
+
+ PR_JoinThread(t);
+
+ printf("main: creating global bound thread\n");
+ t = PR_CreateThread(PR_USER_THREAD,
+ thread_start, 0,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_BOUND_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ if (NULL == t) {
+ printf("main: cannot create global bound thread\n");
+ exit(1);
+ }
+
+ PR_JoinThread(t);
+
+ return 0;
+}
diff --git a/nsprpub/pr/tests/alarm.c b/nsprpub/pr/tests/alarm.c
new file mode 100644
index 0000000000..10a148e448
--- /dev/null
+++ b/nsprpub/pr/tests/alarm.c
@@ -0,0 +1,552 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+** 1996 - Netscape Communications Corporation
+**
+** Name: alarmtst.c
+**
+** Description: Test alarms
+**
+** Modification History:
+** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+
+#include "prlog.h"
+#include "prinit.h"
+#include "obsolete/pralarm.h"
+#include "prlock.h"
+#include "prlong.h"
+#include "prcvar.h"
+#include "prinrval.h"
+#include "prtime.h"
+
+/* Used to get the command line option */
+#include "plgetopt.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(XP_UNIX)
+#include <sys/time.h>
+#endif
+
+static PRIntn debug_mode;
+static PRIntn failed_already=0;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+typedef struct notifyData {
+ PRLock *ml;
+ PRCondVar *child;
+ PRCondVar *parent;
+ PRBool pending;
+ PRUint32 counter;
+} NotifyData;
+
+static void Notifier(void *arg)
+{
+ NotifyData *notifyData = (NotifyData*)arg;
+ PR_Lock(notifyData->ml);
+ while (notifyData->counter > 0)
+ {
+ while (!notifyData->pending) {
+ PR_WaitCondVar(notifyData->child, PR_INTERVAL_NO_TIMEOUT);
+ }
+ notifyData->counter -= 1;
+ notifyData->pending = PR_FALSE;
+ PR_NotifyCondVar(notifyData->parent);
+ }
+ PR_Unlock(notifyData->ml);
+} /* Notifier */
+/***********************************************************************
+** PRIVATE FUNCTION: ConditionNotify
+** DESCRIPTION:
+**
+** INPUTS: loops
+** OUTPUTS: None
+** RETURN: overhead
+** SIDE EFFECTS:
+**
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM:
+**
+***********************************************************************/
+
+
+static PRIntervalTime ConditionNotify(PRUint32 loops)
+{
+ PRThread *thread;
+ NotifyData notifyData;
+ PRIntervalTime timein, overhead;
+
+ timein = PR_IntervalNow();
+
+ notifyData.counter = loops;
+ notifyData.ml = PR_NewLock();
+ notifyData.child = PR_NewCondVar(notifyData.ml);
+ notifyData.parent = PR_NewCondVar(notifyData.ml);
+ thread = PR_CreateThread(
+ PR_USER_THREAD, Notifier, &notifyData,
+ PR_GetThreadPriority(PR_GetCurrentThread()),
+ thread_scope, PR_JOINABLE_THREAD, 0);
+
+ overhead = PR_IntervalNow() - timein; /* elapsed so far */
+
+ PR_Lock(notifyData.ml);
+ while (notifyData.counter > 0)
+ {
+ notifyData.pending = PR_TRUE;
+ PR_NotifyCondVar(notifyData.child);
+ while (notifyData.pending) {
+ PR_WaitCondVar(notifyData.parent, PR_INTERVAL_NO_TIMEOUT);
+ }
+ }
+ PR_Unlock(notifyData.ml);
+
+ timein = PR_IntervalNow();
+
+ (void)PR_JoinThread(thread);
+ PR_DestroyCondVar(notifyData.child);
+ PR_DestroyCondVar(notifyData.parent);
+ PR_DestroyLock(notifyData.ml);
+
+ overhead += (PR_IntervalNow() - timein); /* more overhead */
+
+ return overhead;
+} /* ConditionNotify */
+
+static PRIntervalTime ConditionTimeout(PRUint32 loops)
+{
+ PRUintn count;
+ PRIntervalTime overhead, timein = PR_IntervalNow();
+
+ PRLock *ml = PR_NewLock();
+ PRCondVar *cv = PR_NewCondVar(ml);
+ PRIntervalTime interval = PR_MillisecondsToInterval(50);
+
+ overhead = PR_IntervalNow() - timein;
+
+ PR_Lock(ml);
+ for (count = 0; count < loops; ++count)
+ {
+ overhead += interval;
+ PR_ASSERT(PR_WaitCondVar(cv, interval) == PR_SUCCESS);
+ }
+ PR_Unlock(ml);
+
+ timein = PR_IntervalNow();
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+ overhead += (PR_IntervalNow() - timein);
+
+ return overhead;
+} /* ConditionTimeout */
+
+typedef struct AlarmData {
+ PRLock *ml;
+ PRCondVar *cv;
+ PRUint32 rate, late, times;
+ PRIntervalTime duration, timein, period;
+} AlarmData;
+
+static PRBool AlarmFn1(PRAlarmID *id, void *clientData, PRUint32 late)
+{
+ PRStatus rv = PR_SUCCESS;
+ PRBool keepGoing, resetAlarm;
+ PRIntervalTime interval, now = PR_IntervalNow();
+ AlarmData *ad = (AlarmData*)clientData;
+
+ PR_Lock(ad->ml);
+ ad->late += late;
+ ad->times += 1;
+ keepGoing = ((PRIntervalTime)(now - ad->timein) < ad->duration) ?
+ PR_TRUE : PR_FALSE;
+ if (!keepGoing) {
+ rv = PR_NotifyCondVar(ad->cv);
+ }
+ resetAlarm = ((ad->times % 31) == 0) ? PR_TRUE : PR_FALSE;
+
+ interval = (ad->period + ad->rate - 1) / ad->rate;
+ if (!late && (interval > 10))
+ {
+ interval &= (now & 0x03) + 1;
+ PR_WaitCondVar(ad->cv, interval);
+ }
+
+ PR_Unlock(ad->ml);
+
+ if (rv != PR_SUCCESS)
+ {
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ else {
+ printf("AlarmFn: notify status: FAIL\n");
+ }
+
+ }
+
+ if (resetAlarm)
+ {
+ ad->rate += 3;
+ ad->late = ad->times = 0;
+ if (PR_ResetAlarm(id, ad->period, ad->rate) != PR_SUCCESS)
+ {
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ else {
+ printf("AlarmFn: Resetting alarm status: FAIL\n");
+ }
+
+ keepGoing = PR_FALSE;
+ }
+
+ }
+
+ return keepGoing;
+} /* AlarmFn1 */
+
+static PRIntervalTime Alarms1(PRUint32 loops)
+{
+ PRAlarm *alarm;
+ AlarmData ad;
+ PRIntervalTime overhead, timein = PR_IntervalNow();
+ PRIntervalTime duration = PR_SecondsToInterval(3);
+
+ PRLock *ml = PR_NewLock();
+ PRCondVar *cv = PR_NewCondVar(ml);
+
+ ad.ml = ml;
+ ad.cv = cv;
+ ad.rate = 1;
+ ad.times = loops;
+ ad.late = ad.times = 0;
+ ad.duration = duration;
+ ad.timein = PR_IntervalNow();
+ ad.period = PR_SecondsToInterval(1);
+
+ alarm = PR_CreateAlarm();
+
+ (void)PR_SetAlarm(
+ alarm, ad.period, ad.rate, AlarmFn1, &ad);
+
+ overhead = PR_IntervalNow() - timein;
+
+ PR_Lock(ml);
+ while ((PRIntervalTime)(PR_IntervalNow() - ad.timein) < duration) {
+ PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(ml);
+
+ timein = PR_IntervalNow();
+ (void)PR_DestroyAlarm(alarm);
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+ overhead += (PR_IntervalNow() - timein);
+
+ return duration + overhead;
+} /* Alarms1 */
+
+static PRBool AlarmFn2(PRAlarmID *id, void *clientData, PRUint32 late)
+{
+ PRBool keepGoing;
+ PRStatus rv = PR_SUCCESS;
+ AlarmData *ad = (AlarmData*)clientData;
+ PRIntervalTime interval, now = PR_IntervalNow();
+
+ PR_Lock(ad->ml);
+ ad->times += 1;
+ keepGoing = ((PRIntervalTime)(now - ad->timein) < ad->duration) ?
+ PR_TRUE : PR_FALSE;
+ interval = (ad->period + ad->rate - 1) / ad->rate;
+
+ if (!late && (interval > 10))
+ {
+ interval &= (now & 0x03) + 1;
+ PR_WaitCondVar(ad->cv, interval);
+ }
+
+ if (!keepGoing) {
+ rv = PR_NotifyCondVar(ad->cv);
+ }
+
+ PR_Unlock(ad->ml);
+
+
+ if (rv != PR_SUCCESS) {
+ failed_already=1;
+ };
+
+ return keepGoing;
+} /* AlarmFn2 */
+
+static PRIntervalTime Alarms2(PRUint32 loops)
+{
+ PRStatus rv;
+ PRAlarm *alarm;
+ PRIntervalTime overhead, timein = PR_IntervalNow();
+ AlarmData ad;
+ PRIntervalTime duration = PR_SecondsToInterval(30);
+
+ PRLock *ml = PR_NewLock();
+ PRCondVar *cv = PR_NewCondVar(ml);
+
+ ad.ml = ml;
+ ad.cv = cv;
+ ad.rate = 1;
+ ad.times = loops;
+ ad.late = ad.times = 0;
+ ad.duration = duration;
+ ad.timein = PR_IntervalNow();
+ ad.period = PR_SecondsToInterval(1);
+
+ alarm = PR_CreateAlarm();
+
+ (void)PR_SetAlarm(
+ alarm, ad.period, ad.rate, AlarmFn2, &ad);
+
+ overhead = PR_IntervalNow() - timein;
+
+ PR_Lock(ml);
+ while ((PRIntervalTime)(PR_IntervalNow() - ad.timein) < duration) {
+ PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(ml);
+
+ timein = PR_IntervalNow();
+
+ rv = PR_DestroyAlarm(alarm);
+ if (rv != PR_SUCCESS)
+ {
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ else {
+ printf("***Destroying alarm status: FAIL\n");
+ }
+ }
+
+
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+
+ overhead += (PR_IntervalNow() - timein);
+
+ return duration + overhead;
+} /* Alarms2 */
+
+static PRIntervalTime Alarms3(PRUint32 loops)
+{
+ PRIntn i;
+ PRStatus rv;
+ PRAlarm *alarm;
+ AlarmData ad[3];
+ PRIntervalTime duration = PR_SecondsToInterval(30);
+ PRIntervalTime overhead, timein = PR_IntervalNow();
+
+ PRLock *ml = PR_NewLock();
+ PRCondVar *cv = PR_NewCondVar(ml);
+
+ for (i = 0; i < 3; ++i)
+ {
+ ad[i].ml = ml;
+ ad[i].cv = cv;
+ ad[i].rate = 1;
+ ad[i].times = loops;
+ ad[i].duration = duration;
+ ad[i].late = ad[i].times = 0;
+ ad[i].timein = PR_IntervalNow();
+ ad[i].period = PR_SecondsToInterval(1);
+
+ /* more loops, faster rate => same elapsed time */
+ ad[i].times = (i + 1) * loops;
+ ad[i].rate = (i + 1) * 10;
+ }
+
+ alarm = PR_CreateAlarm();
+
+ for (i = 0; i < 3; ++i)
+ {
+ (void)PR_SetAlarm(
+ alarm, ad[i].period, ad[i].rate,
+ AlarmFn2, &ad[i]);
+ }
+
+ overhead = PR_IntervalNow() - timein;
+
+ PR_Lock(ml);
+ for (i = 0; i < 3; ++i)
+ {
+ while ((PRIntervalTime)(PR_IntervalNow() - ad[i].timein) < duration) {
+ PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ }
+ PR_Unlock(ml);
+
+ timein = PR_IntervalNow();
+
+ if (debug_mode)
+ printf
+ ("Alarms3 finished at %u, %u, %u\n",
+ ad[0].timein, ad[1].timein, ad[2].timein);
+
+ rv = PR_DestroyAlarm(alarm);
+ if (rv != PR_SUCCESS)
+ {
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ else {
+ printf("***Destroying alarm status: FAIL\n");
+ }
+ }
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+
+ overhead += (duration / 3);
+ overhead += (PR_IntervalNow() - timein);
+
+ return overhead;
+} /* Alarms3 */
+
+static PRUint32 TimeThis(
+ const char *msg, PRUint32 (*func)(PRUint32 loops), PRUint32 loops)
+{
+ PRUint32 overhead, usecs;
+ PRIntervalTime predicted, timein, timeout, ticks;
+
+ if (debug_mode) {
+ printf("Testing %s ...", msg);
+ }
+
+ timein = PR_IntervalNow();
+ predicted = func(loops);
+ timeout = PR_IntervalNow();
+
+ if (debug_mode) {
+ printf(" done\n");
+ }
+
+ ticks = timeout - timein;
+ usecs = PR_IntervalToMicroseconds(ticks);
+ overhead = PR_IntervalToMicroseconds(predicted);
+
+ if(ticks < predicted)
+ {
+ if (debug_mode) {
+ printf("\tFinished in negative time\n");
+ printf("\tpredicted overhead was %d usecs\n", overhead);
+ printf("\ttest completed in %d usecs\n\n", usecs);
+ }
+ }
+ else
+ {
+ if (debug_mode)
+ printf(
+ "\ttotal: %d usecs\n\toverhead: %d usecs\n\tcost: %6.3f usecs\n\n",
+ usecs, overhead, ((double)(usecs - overhead) / (double)loops));
+ }
+
+ return overhead;
+} /* TimeThis */
+
+int prmain(int argc, char** argv)
+{
+ PRUint32 cpu, cpus = 0, loops = 0;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name [-d]
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:c:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'G': /* GLOBAL threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'l': /* loop count */
+ loops = atoi(opt->value);
+ break;
+ case 'c': /* concurrency limit */
+ cpus = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+
+ if (cpus == 0) {
+ cpus = 1;
+ }
+ if (loops == 0) {
+ loops = 4;
+ }
+
+ if (debug_mode) {
+ printf("Alarm: Using %d loops\n", loops);
+ }
+
+ if (debug_mode) {
+ printf("Alarm: Using %d cpu(s)\n", cpus);
+ }
+
+ for (cpu = 1; cpu <= cpus; ++cpu)
+ {
+ if (debug_mode) {
+ printf("\nAlarm: Using %d CPU(s)\n", cpu);
+ }
+
+ PR_SetConcurrency(cpu);
+
+ /* some basic time test */
+ (void)TimeThis("ConditionNotify", ConditionNotify, loops);
+ (void)TimeThis("ConditionTimeout", ConditionTimeout, loops);
+ (void)TimeThis("Alarms1", Alarms1, loops);
+ (void)TimeThis("Alarms2", Alarms2, loops);
+ (void)TimeThis("Alarms3", Alarms3, loops);
+ }
+ return 0;
+}
+
+int main(int argc, char** argv)
+{
+ PR_Initialize(prmain, argc, argv, 0);
+ PR_STDIO_INIT();
+ if (failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+} /* main */
+
+
+/* alarmtst.c */
diff --git a/nsprpub/pr/tests/anonfm.c b/nsprpub/pr/tests/anonfm.c
new file mode 100644
index 0000000000..529be6fae0
--- /dev/null
+++ b/nsprpub/pr/tests/anonfm.c
@@ -0,0 +1,319 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: anonfm.c
+** Description: Test anonymous file map
+**
+** Synopsis: anonfm [options] [dirName]
+**
+** Options:
+** -d enable debug mode
+** -h display a help message
+** -s <n> size of the anonymous memory map, in KBytes. default: 100KBytes.
+** -C 1 Operate this process as ClientOne()
+** -C 2 Operate this process as ClientTwo()
+**
+** anonfn.c contains two tests, corresponding to the two protocols for
+** passing an anonymous file map to a child process.
+**
+** ServerOne()/ClientOne() tests the passing of "raw" file map; it uses
+** PR_CreateProcess() [for portability of the test case] to create the
+** child process, but does not use the PRProcessAttr structure for
+** passing the file map data.
+**
+** ServerTwo()/ClientTwo() tests the passing of the file map using the
+** PRProcessAttr structure.
+**
+*/
+#include <plgetopt.h>
+#include <nspr.h>
+#include <private/primpl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+** Test harness infrastructure
+*/
+PRLogModuleInfo *lm;
+PRLogModuleLevel msgLevel = PR_LOG_NONE;
+PRUint32 failed_already = 0;
+
+PRIntn debug = 0;
+PRIntn client = 0; /* invoke client, style */
+char dirName[512] = "."; /* directory name to contain anon mapped file */
+PRSize fmSize = (100 * 1024 );
+PRUint32 fmMode = 0600;
+PRFileMapProtect fmProt = PR_PROT_READWRITE;
+const char *fmEnvName = "nsprFileMapEnvVariable";
+
+/*
+** Emit help text for this test
+*/
+static void Help( void )
+{
+ printf("anonfm [options] [dirName]\n");
+ printf("-d -- enable debug mode\n");
+ printf("dirName is alternate directory name. Default: . (current directory)\n");
+ exit(1);
+} /* end Help() */
+
+
+/*
+** ClientOne() --
+*/
+static void ClientOne( void )
+{
+ PRFileMap *fm;
+ char *fmString;
+ char *addr;
+ PRStatus rc;
+
+ PR_LOG(lm, msgLevel,
+ ("ClientOne() starting"));
+
+ fmString = PR_GetEnv( fmEnvName );
+ if ( NULL == fmString ) {
+ failed_already = 1;
+ PR_LOG(lm, msgLevel,
+ ("ClientOne(): PR_Getenv() failed"));
+ return;
+ }
+ PR_LOG(lm, msgLevel,
+ ("ClientOne(): PR_Getenv(): found: %s", fmString));
+
+ fm = PR_ImportFileMapFromString( fmString );
+ if ( NULL == fm ) {
+ failed_already = 1;
+ PR_LOG(lm, msgLevel,
+ ("ClientOne(): PR_ImportFileMapFromString() failed"));
+ return;
+ }
+ PR_LOG(lm, msgLevel,
+ ("ClientOne(): PR_ImportFileMapFromString(): fm: %p", fm ));
+
+ addr = PR_MemMap( fm, LL_ZERO, fmSize );
+ if ( NULL == addr ) {
+ failed_already = 1;
+ PR_LOG(lm, msgLevel,
+ ("ClientOne(): PR_MemMap() failed, OSError: %d", PR_GetOSError() ));
+ return;
+ }
+ PR_LOG(lm, msgLevel,
+ ("ClientOne(): PR_MemMap(): addr: %p", addr ));
+
+ /* write to memory map to release server */
+ *addr = 1;
+
+ rc = PR_MemUnmap( addr, fmSize );
+ PR_ASSERT( rc == PR_SUCCESS );
+ PR_LOG(lm, msgLevel,
+ ("ClientOne(): PR_MemUnap(): success" ));
+
+ rc = PR_CloseFileMap( fm );
+ if ( PR_FAILURE == rc ) {
+ failed_already = 1;
+ PR_LOG(lm, msgLevel,
+ ("ClientOne(): PR_MemUnap() failed, OSError: %d", PR_GetOSError() ));
+ return;
+ }
+ PR_LOG(lm, msgLevel,
+ ("ClientOne(): PR_CloseFileMap(): success" ));
+
+ return;
+} /* end ClientOne() */
+
+/*
+** ClientTwo() --
+*/
+static void ClientTwo( void )
+{
+ failed_already = 1;
+} /* end ClientTwo() */
+
+/*
+** ServerOne() --
+*/
+static void ServerOne( void )
+{
+ PRFileMap *fm;
+ PRStatus rc;
+ PRIntn i;
+ char *addr;
+ char fmString[256];
+ char envBuf[256];
+ char *child_argv[8];
+ PRProcess *proc;
+ PRInt32 exit_status;
+
+ PR_LOG(lm, msgLevel,
+ ("ServerOne() starting"));
+
+ fm = PR_OpenAnonFileMap( dirName, fmSize, fmProt );
+ if ( NULL == fm ) {
+ failed_already = 1;
+ PR_LOG(lm, msgLevel,
+ ("PR_OpenAnonFileMap() failed"));
+ return;
+ }
+ PR_LOG(lm, msgLevel,
+ ("ServerOne(): FileMap: %p", fm ));
+
+ rc = PR_ExportFileMapAsString( fm, sizeof(fmString), fmString );
+ if ( PR_FAILURE == rc ) {
+ failed_already = 1;
+ PR_LOG(lm, msgLevel,
+ ("PR_ExportFileMap() failed"));
+ return;
+ }
+
+ /*
+ ** put the string into the environment
+ */
+ PR_snprintf( envBuf, sizeof(envBuf), "%s=%s", fmEnvName, fmString);
+ putenv( envBuf );
+
+ addr = PR_MemMap( fm, LL_ZERO, fmSize );
+ if ( NULL == addr ) {
+ failed_already = 1;
+ PR_LOG(lm, msgLevel,
+ ("PR_MemMap() failed"));
+ return;
+ }
+
+ /* set initial value for client */
+ for (i = 0; i < (PRIntn)fmSize ; i++ ) {
+ *(addr+i) = 0x00;
+ }
+
+ PR_LOG(lm, msgLevel,
+ ("ServerOne(): PR_MemMap(): addr: %p", addr ));
+
+ /*
+ ** set arguments for child process
+ */
+ child_argv[0] = "anonfm";
+ child_argv[1] = "-C";
+ child_argv[2] = "1";
+ child_argv[3] = NULL;
+
+ proc = PR_CreateProcess(child_argv[0], child_argv, NULL, NULL);
+ PR_ASSERT( proc );
+ PR_LOG(lm, msgLevel,
+ ("ServerOne(): PR_CreateProcess(): proc: %x", proc ));
+
+ /*
+ ** ClientOne() will set the memory to 1
+ */
+ PR_LOG(lm, msgLevel,
+ ("ServerOne(): waiting on Client, *addr: %x", *addr ));
+ while( *addr == 0x00 ) {
+ if ( debug ) {
+ fprintf(stderr, ".");
+ }
+ PR_Sleep(PR_MillisecondsToInterval(300));
+ }
+ if ( debug ) {
+ fprintf(stderr, "\n");
+ }
+ PR_LOG(lm, msgLevel,
+ ("ServerOne(): Client responded" ));
+
+ rc = PR_WaitProcess( proc, &exit_status );
+ PR_ASSERT( PR_FAILURE != rc );
+
+ rc = PR_MemUnmap( addr, fmSize);
+ if ( PR_FAILURE == rc ) {
+ failed_already = 1;
+ PR_LOG(lm, msgLevel,
+ ("PR_MemUnmap() failed"));
+ return;
+ }
+ PR_LOG(lm, msgLevel,
+ ("ServerOne(): PR_MemUnmap(): success" ));
+
+ rc = PR_CloseFileMap(fm);
+ if ( PR_FAILURE == rc ) {
+ failed_already = 1;
+ PR_LOG(lm, msgLevel,
+ ("PR_CloseFileMap() failed"));
+ return;
+ }
+ PR_LOG(lm, msgLevel,
+ ("ServerOne(): PR_CloseFileMap() success" ));
+
+ return;
+} /* end ServerOne() */
+
+/*
+** ServerTwo() --
+*/
+static void ServerTwo( void )
+{
+ PR_LOG(lm, msgLevel,
+ ("ServerTwo(): Not implemented yet" ));
+} /* end ServerTwo() */
+
+
+int main(int argc, char **argv)
+{
+ {
+ /*
+ ** Get command line options
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hdC:");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'C': /* Client style */
+ client = atol(opt->value);
+ break;
+ case 's': /* file size */
+ fmSize = atol( opt->value ) * 1024;
+ break;
+ case 'd': /* debug */
+ debug = 1;
+ msgLevel = PR_LOG_DEBUG;
+ break;
+ case 'h': /* help message */
+ Help();
+ break;
+ default:
+ strcpy(dirName, opt->value);
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ }
+
+ lm = PR_NewLogModule("Test"); /* Initialize logging */
+
+ if ( client == 1 ) {
+ ClientOne();
+ } else if ( client == 2 ) {
+ ClientTwo();
+ } else {
+ ServerOne();
+ if ( failed_already ) {
+ goto Finished;
+ }
+ ServerTwo();
+ }
+
+Finished:
+ if ( debug ) {
+ printf("%s\n", (failed_already)? "FAIL" : "PASS");
+ }
+ return( (failed_already == PR_TRUE )? 1 : 0 );
+} /* main() */
+/* end anonfm.c */
+
diff --git a/nsprpub/pr/tests/append.c b/nsprpub/pr/tests/append.c
new file mode 100644
index 0000000000..64fe3ff2c1
--- /dev/null
+++ b/nsprpub/pr/tests/append.c
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: append.c
+** Description: Testing File writes where PR_APPEND was used on open
+**
+** append attempts to verify that a file opened with PR_APPEND
+** will always append to the end of file, regardless where the
+** current file pointer is positioned. To do this, PR_Seek() is
+** called before each write with the position set to beginning of
+** file. Subsequent writes should always append.
+** The file is read back, summing the integer data written to the
+** file. If the expected result is equal, the test passes.
+**
+** See BugSplat: 4090
+*/
+#include "plgetopt.h"
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+PRIntn debug = 0;
+PRIntn verbose = 0;
+PRBool failedAlready = PR_FALSE;
+const PRInt32 addedBytes = 1000;
+const PRInt32 buf = 1; /* constant written to fd, addedBytes times */
+PRInt32 inBuf; /* read it back into here */
+
+int main(int argc, char **argv)
+{
+ PRStatus rc;
+ PRInt32 rv;
+ PRFileDesc *fd;
+ PRIntn i;
+ PRInt32 sum = 0;
+
+ { /* Get command line options */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "vd");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug */
+ debug = 1;
+ break;
+ case 'v': /* verbose */
+ verbose = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ } /* end block "Get command line options" */
+ /* ---------------------------------------------------------------------- */
+ fd = PR_Open( "./tmp-nsprAppend", (PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY), 0666 );
+ if ( NULL == fd ) {
+ if (debug) {
+ printf("PR_Open() failed for writing: %d\n", PR_GetError());
+ }
+ failedAlready = PR_TRUE;
+ goto Finished;
+ }
+
+ for ( i = 0; i < addedBytes ; i++ ) {
+ rv = PR_Write( fd, &buf, sizeof(buf));
+ if ( sizeof(buf) != rv ) {
+ if (debug) {
+ printf("PR_Write() failed: %d\n", PR_GetError());
+ }
+ failedAlready = PR_TRUE;
+ goto Finished;
+ }
+ rv = PR_Seek( fd, 0, PR_SEEK_SET );
+ if ( -1 == rv ) {
+ if (debug) {
+ printf("PR_Seek() failed: %d\n", PR_GetError());
+ }
+ failedAlready = PR_TRUE;
+ goto Finished;
+ }
+ }
+ rc = PR_Close( fd );
+ if ( PR_FAILURE == rc ) {
+ if (debug) {
+ printf("PR_Close() failed after writing: %d\n", PR_GetError());
+ }
+ failedAlready = PR_TRUE;
+ goto Finished;
+ }
+ /* ---------------------------------------------------------------------- */
+ fd = PR_Open( "./tmp-nsprAppend", PR_RDONLY, 0 );
+ if ( NULL == fd ) {
+ if (debug) {
+ printf("PR_Open() failed for reading: %d\n", PR_GetError());
+ }
+ failedAlready = PR_TRUE;
+ goto Finished;
+ }
+
+ for ( i = 0; i < addedBytes ; i++ ) {
+ rv = PR_Read( fd, &inBuf, sizeof(inBuf));
+ if ( sizeof(inBuf) != rv) {
+ if (debug) {
+ printf("PR_Write() failed: %d\n", PR_GetError());
+ }
+ failedAlready = PR_TRUE;
+ goto Finished;
+ }
+ sum += inBuf;
+ }
+
+ rc = PR_Close( fd );
+ if ( PR_FAILURE == rc ) {
+ if (debug) {
+ printf("PR_Close() failed after reading: %d\n", PR_GetError());
+ }
+ failedAlready = PR_TRUE;
+ goto Finished;
+ }
+ if ( sum != addedBytes ) {
+ if (debug) {
+ printf("Uh Oh! addedBytes: %d. Sum: %d\n", addedBytes, sum);
+ }
+ failedAlready = PR_TRUE;
+ goto Finished;
+ }
+
+ /* ---------------------------------------------------------------------- */
+Finished:
+ if (debug || verbose) {
+ printf("%s\n", (failedAlready)? "FAILED" : "PASSED" );
+ }
+ return( (failedAlready)? 1 : 0 );
+} /* main() */
+
+/* append.c */
diff --git a/nsprpub/pr/tests/atomic.c b/nsprpub/pr/tests/atomic.c
new file mode 100644
index 0000000000..42d25da8df
--- /dev/null
+++ b/nsprpub/pr/tests/atomic.c
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prio.h"
+#include "prprf.h"
+#include "pratom.h"
+
+/*
+ * TODO: create a macro to generate the six lines of code that are repeated
+ * for every test. Also rewrite the statement
+ * result = result | ((EXPRESSION) ? 0 : 1);
+ * as
+ * result |= !(EXPRESSION);
+ */
+
+int main(int argc, char **argv)
+{
+ PRInt32 rv, oldval, test, result = 0;
+ PRFileDesc *output = PR_GetSpecialFD(PR_StandardOutput);
+
+ /***********************/
+ /* Test the functions. */
+ /***********************/
+
+ oldval = test = -2;
+ rv = PR_AtomicIncrement(&test);
+ result = result | ((rv == -1) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicIncrement(%d) == %d: %s\n",
+ oldval, rv, (rv == -1) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_AtomicIncrement(&test);
+ result = result | ((rv == 0) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicIncrement(%d) == %d: %s\n",
+ oldval, rv, (rv == 0) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_AtomicIncrement(&test);
+ result = result | ((rv == 1) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicIncrement(%d) == %d: %s\n",
+ oldval, rv, (rv == 1) ? "PASSED" : "FAILED");
+
+ oldval = test = -2;
+ rv = PR_AtomicAdd(&test,1);
+ result = result | ((rv == -1) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicAdd(%d,%d) == %d: %s\n",
+ oldval, 1, rv, (rv == -1) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_AtomicAdd(&test, 4);
+ result = result | ((rv == 3) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicAdd(%d,%d) == %d: %s\n",
+ oldval, 4, rv, (rv == 3) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_AtomicAdd(&test, -6);
+ result = result | ((rv == -3) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicAdd(%d,%d) == %d: %s\n",
+ oldval, -6, rv, (rv == -3) ? "PASSED" : "FAILED");
+
+ oldval = test = 2;
+ rv = PR_AtomicDecrement(&test);
+ result = result | ((rv == 1) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicDecrement(%d) == %d: %s\n",
+ oldval, rv, (rv == 1) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_AtomicDecrement(&test);
+ result = result | ((rv == 0) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicDecrement(%d) == %d: %s\n",
+ oldval, rv, (rv == 0) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_AtomicDecrement(&test);
+ result = result | ((rv == -1) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicDecrement(%d) == %d: %s\n",
+ oldval, rv, (rv == -1) ? "PASSED" : "FAILED");
+
+ /* set to a different value */
+ oldval = test = -2;
+ rv = PR_AtomicSet(&test, 2);
+ result = result | (((rv == -2) && (test == 2)) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicSet(%d, %d) == %d: %s\n",
+ oldval, 2, rv, ((rv == -2) && (test == 2)) ? "PASSED" : "FAILED");
+
+ /* set to the same value */
+ oldval = test = -2;
+ rv = PR_AtomicSet(&test, -2);
+ result = result | (((rv == -2) && (test == -2)) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicSet(%d, %d) == %d: %s\n",
+ oldval, -2, rv, ((rv == -2) && (test == -2)) ? "PASSED" : "FAILED");
+
+ /***********************/
+ /* Test the macros. */
+ /***********************/
+
+ oldval = test = -2;
+ rv = PR_ATOMIC_INCREMENT(&test);
+ result = result | ((rv == -1) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_ATOMIC_INCREMENT(%d) == %d: %s\n",
+ oldval, rv, (rv == -1) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_ATOMIC_INCREMENT(&test);
+ result = result | ((rv == 0) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_ATOMIC_INCREMENT(%d) == %d: %s\n",
+ oldval, rv, (rv == 0) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_ATOMIC_INCREMENT(&test);
+ result = result | ((rv == 1) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_ATOMIC_INCREMENT(%d) == %d: %s\n",
+ oldval, rv, (rv == 1) ? "PASSED" : "FAILED");
+
+ oldval = test = -2;
+ rv = PR_ATOMIC_ADD(&test,1);
+ result = result | ((rv == -1) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_ATOMIC_ADD(%d,%d) == %d: %s\n",
+ oldval, 1, rv, (rv == -1) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_ATOMIC_ADD(&test, 4);
+ result = result | ((rv == 3) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_ATOMIC_ADD(%d,%d) == %d: %s\n",
+ oldval, 4, rv, (rv == 3) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_ATOMIC_ADD(&test, -6);
+ result = result | ((rv == -3) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_ATOMIC_ADD(%d,%d) == %d: %s\n",
+ oldval, -6, rv, (rv == -3) ? "PASSED" : "FAILED");
+
+ oldval = test = 2;
+ rv = PR_ATOMIC_DECREMENT(&test);
+ result = result | ((rv == 1) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_ATOMIC_DECREMENT(%d) == %d: %s\n",
+ oldval, rv, (rv == 1) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_ATOMIC_DECREMENT(&test);
+ result = result | ((rv == 0) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_ATOMIC_DECREMENT(%d) == %d: %s\n",
+ oldval, rv, (rv == 0) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_ATOMIC_DECREMENT(&test);
+ result = result | ((rv == -1) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_ATOMIC_DECREMENT(%d) == %d: %s\n",
+ oldval, rv, (rv == -1) ? "PASSED" : "FAILED");
+
+ /* set to a different value */
+ oldval = test = -2;
+ rv = PR_ATOMIC_SET(&test, 2);
+ result = result | (((rv == -2) && (test == 2)) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_ATOMIC_SET(%d, %d) == %d: %s\n",
+ oldval, 2, rv, ((rv == -2) && (test == 2)) ? "PASSED" : "FAILED");
+
+ /* set to the same value */
+ oldval = test = -2;
+ rv = PR_ATOMIC_SET(&test, -2);
+ result = result | (((rv == -2) && (test == -2)) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_ATOMIC_SET(%d, %d) == %d: %s\n",
+ oldval, -2, rv, ((rv == -2) && (test == -2)) ? "PASSED" : "FAILED");
+
+ PR_fprintf(
+ output, "Atomic operations test %s\n",
+ (result == 0) ? "PASSED" : "FAILED");
+ return result;
+} /* main */
+
+/* atomic.c */
diff --git a/nsprpub/pr/tests/attach.c b/nsprpub/pr/tests/attach.c
new file mode 100644
index 0000000000..2c1ac99799
--- /dev/null
+++ b/nsprpub/pr/tests/attach.c
@@ -0,0 +1,299 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+** 1996 - Netscape Communications Corporation
+**
+** Name: attach.c
+**
+** Description: Platform-specific code to create a native thread. The native thread will
+** repeatedly call PR_AttachThread and PR_DetachThread. The
+** primordial thread waits for this new thread to finish.
+**
+** Modification History:
+** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 Revert to return code 0 and 1.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+
+/* Used to get the command line option */
+#include "nspr.h"
+#include "pprthred.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+
+#ifdef WIN32
+#include <windows.h>
+#include <process.h>
+#elif defined(_PR_PTHREADS)
+#include <pthread.h>
+#include "md/_pth.h"
+#elif defined(SOLARIS)
+#include <thread.h>
+#elif defined(OS2)
+#define INCL_DOS
+#define INCL_ERRORS
+#include <os2.h>
+#include <process.h>
+#endif
+
+#define DEFAULT_COUNT 1000
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+
+int count;
+
+
+static void
+AttachDetach(void)
+{
+ PRThread *me;
+ PRInt32 index;
+
+ for (index=0; index<count; index++) {
+ me = PR_AttachThread(PR_USER_THREAD,
+ PR_PRIORITY_NORMAL,
+ NULL);
+
+ if (!me) {
+ fprintf(stderr, "Error attaching thread %d: PR_AttachThread failed\n",
+ count);
+ failed_already = 1;
+ return;
+ }
+ PR_DetachThread();
+ }
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+ if (debug_mode) {
+ printf("%40s: %6.2f usec\n", msg, d / count);
+ }
+}
+
+#ifdef WIN32
+static unsigned __stdcall threadStartFunc(void *arg)
+#else
+static void * threadStartFunc(void *arg)
+#endif
+{
+ Measure(AttachDetach, "Attach/Detach");
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+#ifdef _PR_PTHREADS
+ int rv;
+ pthread_t threadID;
+ pthread_attr_t attr;
+#elif defined(SOLARIS)
+ int rv;
+ thread_t threadID;
+#elif defined(WIN32)
+ DWORD rv;
+ unsigned threadID;
+ HANDLE hThread;
+#elif defined(OS2)
+ int rv;
+ TID threadID;
+#endif
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name [-d] [-c n]
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dc:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'c': /* loop count */
+ count = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+#if defined(WIN16)
+ printf("attach: This test is not valid for Win16\n");
+ goto exit_now;
+#endif
+
+ if(0 == count) {
+ count = DEFAULT_COUNT;
+ }
+
+ /*
+ * To force the implicit initialization of nspr20
+ */
+ PR_SetError(0, 0);
+ PR_STDIO_INIT();
+
+ /*
+ * Platform-specific code to create a native thread. The native
+ * thread will repeatedly call PR_AttachThread and PR_DetachThread.
+ * The primordial thread waits for this new thread to finish.
+ */
+
+#ifdef _PR_PTHREADS
+
+ rv = _PT_PTHREAD_ATTR_INIT(&attr);
+ if (debug_mode) {
+ PR_ASSERT(0 == rv);
+ }
+ else if (0 != rv) {
+ failed_already=1;
+ goto exit_now;
+ }
+
+ rv = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ if (debug_mode) {
+ PR_ASSERT(0 == rv);
+ }
+ else if (0 != rv) {
+ failed_already=1;
+ goto exit_now;
+ }
+ rv = _PT_PTHREAD_CREATE(&threadID, attr, threadStartFunc, NULL);
+ if (rv != 0) {
+ fprintf(stderr, "thread creation failed: error code %d\n", rv);
+ failed_already=1;
+ goto exit_now;
+ }
+ else {
+ if (debug_mode) {
+ printf ("thread creation succeeded \n");
+ }
+
+ }
+ rv = _PT_PTHREAD_ATTR_DESTROY(&attr);
+ if (debug_mode) {
+ PR_ASSERT(0 == rv);
+ }
+ else if (0 != rv) {
+ failed_already=1;
+ goto exit_now;
+ }
+ rv = pthread_join(threadID, NULL);
+ if (debug_mode) {
+ PR_ASSERT(0 == rv);
+ }
+ else if (0 != rv) {
+ failed_already=1;
+ goto exit_now;
+ }
+
+#elif defined(SOLARIS)
+
+ rv = thr_create(NULL, 0, threadStartFunc, NULL, 0, &threadID);
+ if (rv != 0) {
+ if(!debug_mode) {
+ failed_already=1;
+ goto exit_now;
+ } else {
+ fprintf(stderr, "thread creation failed: error code %d\n", rv);
+ }
+ }
+ rv = thr_join(threadID, NULL, NULL);
+ if (debug_mode) {
+ PR_ASSERT(0 == rv);
+ }
+ else if (0 != rv)
+ {
+ failed_already=1;
+ goto exit_now;
+ }
+
+
+#elif defined(WIN32)
+
+ hThread = (HANDLE) _beginthreadex(NULL, 0, threadStartFunc, NULL,
+ STACK_SIZE_PARAM_IS_A_RESERVATION, &threadID);
+ if (hThread == 0) {
+ fprintf(stderr, "thread creation failed: error code %d\n",
+ GetLastError());
+ failed_already=1;
+ goto exit_now;
+ }
+ rv = WaitForSingleObject(hThread, INFINITE);
+ if (debug_mode) {
+ PR_ASSERT(rv != WAIT_FAILED);
+ }
+ else if (rv == WAIT_FAILED) {
+ failed_already=1;
+ goto exit_now;
+ }
+
+#elif defined(OS2)
+
+ threadID = (TID) _beginthread((void *)threadStartFunc, NULL,
+ 32768, NULL);
+ if (threadID == -1) {
+ fprintf(stderr, "thread creation failed: error code %d\n", errno);
+ failed_already=1;
+ goto exit_now;
+ }
+ rv = DosWaitThread(&threadID, DCWW_WAIT);
+ if (debug_mode) {
+ PR_ASSERT(rv == NO_ERROR);
+ } else if (rv != NO_ERROR) {
+ failed_already=1;
+ goto exit_now;
+ }
+
+#else
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ else
+ printf("The attach test does not apply to this platform because\n"
+ "either this platform does not have native threads or the\n"
+ "test needs to be written for this platform.\n");
+ goto exit_now;
+#endif
+
+exit_now:
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
diff --git a/nsprpub/pr/tests/bigfile.c b/nsprpub/pr/tests/bigfile.c
new file mode 100644
index 0000000000..e17a767e69
--- /dev/null
+++ b/nsprpub/pr/tests/bigfile.c
@@ -0,0 +1,358 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prio.h"
+#include "prmem.h"
+#include "prprf.h"
+#include "prinit.h"
+#include "prerror.h"
+#include "prthread.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+#define DEFAULT_COUNT 10
+#define DEFAULT_FILESIZE 1
+#define BUFFER_SIZE 1000000
+
+typedef enum {v_silent, v_whisper, v_shout} Verbosity;
+static void Verbose(Verbosity, const char*, const char*, PRIntn);
+
+#define VERBOSE(_l, _m) Verbose(_l, _m, __FILE__, __LINE__)
+
+static PRIntn test_result = 2;
+static PRFileDesc *output = NULL;
+static PRIntn verbose = v_silent;
+static PRIntn filesize = DEFAULT_FILESIZE;
+
+static PRIntn Usage(void)
+{
+ PR_fprintf(output, "Bigfile test usage:\n");
+ PR_fprintf(output, ">bigfile [-G] [-d] [-v[*v]] [-s <n>] <filename>\n");
+ PR_fprintf(output, "\td\tdebug mode (equivalent to -vvv)\t(false)\n");
+ PR_fprintf(output, "\tv\tAdditional levels of output\t(none)\n");
+ PR_fprintf(output, "\tk\tKeep data file after exit\t(false)\n");
+ PR_fprintf(output, "\ts <n>\tFile size in megabytes\t\t(1 megabyte)\n");
+ PR_fprintf(output, "\t<filename>\tName of test file\t(none)\n");
+ return 2; /* nothing happened */
+} /* Usage */
+
+static PRStatus DeleteIfFound(const char *filename)
+{
+ PRStatus rv;
+ VERBOSE(v_shout, "Checking for existing file");
+ rv = PR_Access(filename, PR_ACCESS_WRITE_OK);
+ if (PR_SUCCESS == rv)
+ {
+ VERBOSE(v_shout, "Deleting existing file");
+ rv = PR_Delete(filename);
+ if (PR_FAILURE == rv) {
+ VERBOSE(v_shout, "Cannot delete big file");
+ }
+ }
+ else if (PR_FILE_NOT_FOUND_ERROR != PR_GetError()) {
+ VERBOSE(v_shout, "Cannot access big file");
+ }
+ else {
+ rv = PR_SUCCESS;
+ }
+ return rv;
+} /* DeleteIfFound */
+
+static PRIntn Error(const char *msg, const char *filename)
+{
+ PRInt32 error = PR_GetError();
+ if (NULL != msg)
+ {
+ if (0 == error) {
+ PR_fprintf(output, msg);
+ }
+ else {
+ PL_FPrintError(output, msg);
+ }
+ }
+ (void)DeleteIfFound(filename);
+ if (v_shout == verbose) {
+ PR_Abort();
+ }
+ return 1;
+} /* Error */
+
+static void Verbose(
+ Verbosity level, const char *msg, const char *file, PRIntn line)
+{
+ if (level <= verbose) {
+ PR_fprintf(output, "[%s : %d]: %s\n", file, line, msg);
+ }
+} /* Verbose */
+
+static void PrintInfo(PRFileInfo64 *info, const char *filename)
+{
+ PRExplodedTime tm;
+ char ctime[40], mtime[40];
+ static const char *types[] = {"FILE", "DIRECTORY", "OTHER"};
+ PR_fprintf(
+ output, "[%s : %d]: File info for %s\n",
+ __FILE__, __LINE__, filename);
+ PR_fprintf(
+ output, " type: %s, size: %llu bytes,\n",
+ types[info->type - 1], info->size);
+
+ PR_ExplodeTime(info->creationTime, PR_GMTParameters, &tm);
+ (void)PR_FormatTime(ctime, sizeof(ctime), "%c GMT", &tm);
+ PR_ExplodeTime(info->modifyTime, PR_GMTParameters, &tm);
+ (void)PR_FormatTime(mtime, sizeof(mtime), "%c GMT", &tm);
+
+ PR_fprintf(
+ output, " creation: %s,\n modify: %s\n", ctime, mtime);
+} /* PrintInfo */
+
+int main(int argc, char **argv)
+{
+ PRStatus rv;
+ char *buffer;
+ PLOptStatus os;
+ PRInt32 loop, bytes;
+ PRFileInfo small_info;
+ PRFileInfo64 big_info;
+ PRBool keep = PR_FALSE;
+ PRFileDesc *file = NULL;
+ const char *filename = NULL;
+ PRIntn count = DEFAULT_COUNT;
+ PRInt64 filesize64, big_answer, big_size, one_meg, zero_meg, big_fragment;
+ PRInt64 sevenFox = LL_INIT(0,0x7fffffff);
+
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dtvhs:");
+
+ output = PR_GetSpecialFD(PR_StandardError);
+ PR_STDIO_INIT();
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 0:
+ filename = opt->value;
+ break;
+ case 'd': /* debug mode */
+ verbose = v_shout;
+ break;
+ case 'k': /* keep file */
+ keep = PR_TRUE;
+ break;
+ case 'v': /* verbosity */
+ if (v_shout > verbose) {
+ verbose += 1;
+ }
+ break;
+ case 'c': /* loop counter */
+ count = atoi(opt->value);
+ break;
+ case 's': /* filesize */
+ filesize = atoi(opt->value);
+ break;
+ case 'h': /* confused */
+ default:
+ return Usage();
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == count) {
+ count = DEFAULT_COUNT;
+ }
+ if (0 == filesize) {
+ filesize = DEFAULT_FILESIZE;
+ }
+ if (NULL == filename)
+ {
+#define FILE_NAME "bigfile.dat"
+ if (DEFAULT_FILESIZE != filesize) {
+ return Usage();
+ }
+ else {
+ filename = FILE_NAME;
+ }
+ }
+
+ if (PR_FAILURE == DeleteIfFound(filename)) {
+ return 1;
+ }
+
+ test_result = 0;
+
+ LL_I2L(zero_meg, 0);
+ LL_I2L(one_meg, 1000000);
+ LL_I2L(filesize64, filesize);
+ buffer = (char*)PR_MALLOC(BUFFER_SIZE);
+ LL_I2L(big_fragment, BUFFER_SIZE);
+ LL_MUL(filesize64, filesize64, one_meg);
+
+ for (loop = 0; loop < BUFFER_SIZE; ++loop) {
+ buffer[loop] = (char)loop;
+ }
+
+ VERBOSE(v_whisper, "Creating big file");
+ file = PR_Open(filename, PR_CREATE_FILE | PR_WRONLY, 0666);
+ if (NULL == file) {
+ return Error("PR_Open()", filename);
+ }
+
+ VERBOSE(v_whisper, "Testing available space in empty file");
+ big_answer = file->methods->available64(file);
+ if (!LL_IS_ZERO(big_answer)) {
+ return Error("empty available64()", filename);
+ }
+
+ LL_SUB(big_size, filesize64, one_meg);
+ VERBOSE(v_whisper, "Creating sparse big file by seeking to end");
+ big_answer = file->methods->seek64(file, big_size, PR_SEEK_SET);
+ if (!LL_EQ(big_answer, big_size)) {
+ return Error("seek", filename);
+ }
+
+ VERBOSE(v_whisper, "Writing block at end of sparse file");
+ bytes = file->methods->write(file, buffer, BUFFER_SIZE);
+ if (bytes != BUFFER_SIZE) {
+ return Error("write", filename);
+ }
+
+ VERBOSE(v_whisper, "Testing available space at end of sparse file");
+ big_answer = file->methods->available64(file);
+ if (!LL_IS_ZERO(big_answer)) {
+ return Error("eof available64()", filename);
+ }
+
+ VERBOSE(v_whisper, "Getting big info on sparse big file");
+ rv = file->methods->fileInfo64(file, &big_info);
+ if (PR_FAILURE == rv) {
+ return Error("fileInfo64()", filename);
+ }
+ if (v_shout <= verbose) {
+ PrintInfo(&big_info, filename);
+ }
+
+ VERBOSE(v_whisper, "Getting small info on sparse big file");
+ rv = file->methods->fileInfo(file, &small_info);
+ if (LL_CMP(sevenFox, <, filesize64) && (PR_SUCCESS == rv))
+ {
+ VERBOSE(v_whisper, "Should have failed and didn't");
+ return Error("fileInfo()", filename);
+ }
+ else if (LL_CMP(sevenFox, >, filesize64) && (PR_FAILURE == rv))
+ {
+ VERBOSE(v_whisper, "Should have succeeded and didn't");
+ return Error("fileInfo()", filename);
+ }
+
+ VERBOSE(v_whisper, "Rewinding big file");
+ big_answer = file->methods->seek64(file, zero_meg, PR_SEEK_SET);
+ if (!LL_IS_ZERO(big_answer)) {
+ return Error("rewind seek64()", filename);
+ }
+
+ VERBOSE(v_whisper, "Establishing available space in rewound file");
+ big_answer = file->methods->available64(file);
+ if (LL_NE(filesize64, big_answer)) {
+ return Error("bof available64()", filename);
+ }
+
+ VERBOSE(v_whisper, "Closing big file");
+ rv = file->methods->close(file);
+ if (PR_FAILURE == rv) {
+ return Error("close()", filename);
+ }
+
+ VERBOSE(v_whisper, "Reopening big file");
+ file = PR_Open(filename, PR_RDWR, 0666);
+ if (NULL == file) {
+ return Error("open failed", filename);
+ }
+
+ VERBOSE(v_whisper, "Checking available data in reopened file");
+ big_answer = file->methods->available64(file);
+ if (LL_NE(filesize64, big_answer)) {
+ return Error("reopened available64()", filename);
+ }
+
+ big_answer = zero_meg;
+ VERBOSE(v_whisper, "Rewriting every byte of big file data");
+ do
+ {
+ bytes = file->methods->write(file, buffer, BUFFER_SIZE);
+ if (bytes != BUFFER_SIZE) {
+ return Error("write", filename);
+ }
+ LL_ADD(big_answer, big_answer, big_fragment);
+ } while (LL_CMP(big_answer, <, filesize64));
+
+ VERBOSE(v_whisper, "Checking position at eof");
+ big_answer = file->methods->seek64(file, zero_meg, PR_SEEK_CUR);
+ if (LL_NE(big_answer, filesize64)) {
+ return Error("file size error", filename);
+ }
+
+ VERBOSE(v_whisper, "Testing available space at eof");
+ big_answer = file->methods->available64(file);
+ if (!LL_IS_ZERO(big_answer)) {
+ return Error("eof available64()", filename);
+ }
+
+ VERBOSE(v_whisper, "Rewinding full file");
+ big_answer = file->methods->seek64(file, zero_meg, PR_SEEK_SET);
+ if (!LL_IS_ZERO(big_answer)) {
+ return Error("bof seek64()", filename);
+ }
+
+ VERBOSE(v_whisper, "Testing available space in rewound file");
+ big_answer = file->methods->available64(file);
+ if (LL_NE(big_answer, filesize64)) {
+ return Error("bof available64()", filename);
+ }
+
+ VERBOSE(v_whisper, "Seeking to end of big file");
+ big_answer = file->methods->seek64(file, filesize64, PR_SEEK_SET);
+ if (LL_NE(big_answer, filesize64)) {
+ return Error("eof seek64()", filename);
+ }
+
+ VERBOSE(v_whisper, "Getting info on big file while it's open");
+ rv = file->methods->fileInfo64(file, &big_info);
+ if (PR_FAILURE == rv) {
+ return Error("fileInfo64()", filename);
+ }
+ if (v_shout <= verbose) {
+ PrintInfo(&big_info, filename);
+ }
+
+ VERBOSE(v_whisper, "Closing big file");
+ rv = file->methods->close(file);
+ if (PR_FAILURE == rv) {
+ return Error("close()", filename);
+ }
+
+ VERBOSE(v_whisper, "Getting info on big file after it's closed");
+ rv = PR_GetFileInfo64(filename, &big_info);
+ if (PR_FAILURE == rv) {
+ return Error("fileInfo64()", filename);
+ }
+ if (v_shout <= verbose) {
+ PrintInfo(&big_info, filename);
+ }
+
+ VERBOSE(v_whisper, "Deleting big file");
+ rv = PR_Delete(filename);
+ if (PR_FAILURE == rv) {
+ return Error("PR_Delete()", filename);
+ }
+
+ PR_DELETE(buffer);
+ return test_result;
+} /* main */
+
+/* bigfile.c */
diff --git a/nsprpub/pr/tests/bigfile2.c b/nsprpub/pr/tests/bigfile2.c
new file mode 100644
index 0000000000..1a7d45b0f5
--- /dev/null
+++ b/nsprpub/pr/tests/bigfile2.c
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#define TEST_FILE_NAME "bigfile2.txt"
+#ifdef WINCE
+#define TEST_FILE_NAME_FOR_CREATEFILE L"bigfile2.txt"
+#else
+#define TEST_FILE_NAME_FOR_CREATEFILE TEST_FILE_NAME
+#endif
+
+#define MESSAGE "Hello world!"
+#define MESSAGE_SIZE 13
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *fd;
+ PRInt64 offset, position;
+ PRInt32 nbytes;
+ char buf[MESSAGE_SIZE];
+#ifdef _WIN32
+ HANDLE hFile;
+ LARGE_INTEGER li;
+#endif /* _WIN32 */
+
+ LL_I2L(offset, 1);
+ LL_SHL(offset, offset, 32);
+
+ fd = PR_Open(TEST_FILE_NAME,
+ PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0666);
+ if (fd == NULL) {
+ fprintf(stderr, "PR_Open failed\n");
+ exit(1);
+ }
+ position = PR_Seek64(fd, offset, PR_SEEK_SET);
+ if (!LL_GE_ZERO(position)) {
+ fprintf(stderr, "PR_Seek64 failed\n");
+ exit(1);
+ }
+ PR_ASSERT(LL_EQ(position, offset));
+ strcpy(buf, MESSAGE);
+ nbytes = PR_Write(fd, buf, sizeof(buf));
+ if (nbytes != sizeof(buf)) {
+ fprintf(stderr, "PR_Write failed\n");
+ exit(1);
+ }
+ if (PR_Close(fd) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+#ifdef _WIN32
+ hFile = CreateFile(TEST_FILE_NAME_FOR_CREATEFILE, GENERIC_READ, 0, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "CreateFile failed\n");
+ exit(1);
+ }
+ li.QuadPart = offset;
+ li.LowPart = SetFilePointer(hFile, li.LowPart, &li.HighPart, FILE_BEGIN);
+ if (li.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
+ fprintf(stderr, "SetFilePointer failed\n");
+ exit(1);
+ }
+ PR_ASSERT(li.QuadPart == offset);
+ if (ReadFile(hFile, buf, sizeof(buf), &nbytes, NULL) == 0) {
+ fprintf(stderr, "ReadFile failed\n");
+ exit(1);
+ }
+ PR_ASSERT(nbytes == sizeof(buf));
+ if (strcmp(buf, MESSAGE)) {
+ fprintf(stderr, "corrupt data:$%s$\n", buf);
+ exit(1);
+ }
+ if (CloseHandle(hFile) == 0) {
+ fprintf(stderr, "CloseHandle failed\n");
+ exit(1);
+ }
+#endif /* _WIN32 */
+
+ if (PR_Delete(TEST_FILE_NAME) == PR_FAILURE) {
+ fprintf(stderr, "PR_Delete failed\n");
+ exit(1);
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/bigfile3.c b/nsprpub/pr/tests/bigfile3.c
new file mode 100644
index 0000000000..aeb4d65e41
--- /dev/null
+++ b/nsprpub/pr/tests/bigfile3.c
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#define TEST_FILE_NAME "bigfile3.txt"
+#ifdef WINCE
+#define TEST_FILE_NAME_FOR_CREATEFILE L"bigfile3.txt"
+#else
+#define TEST_FILE_NAME_FOR_CREATEFILE TEST_FILE_NAME
+#endif
+
+#define MESSAGE "Hello world!"
+#define MESSAGE_SIZE 13
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *fd;
+ PRInt64 offset, position;
+ PRInt32 nbytes;
+ char buf[MESSAGE_SIZE];
+#ifdef _WIN32
+ HANDLE hFile;
+ LARGE_INTEGER li;
+#endif /* _WIN32 */
+
+ LL_I2L(offset, 1);
+ LL_SHL(offset, offset, 32);
+
+#ifdef _WIN32
+ hFile = CreateFile(TEST_FILE_NAME_FOR_CREATEFILE, GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "CreateFile failed\n");
+ exit(1);
+ }
+ li.QuadPart = offset;
+ li.LowPart = SetFilePointer(hFile, li.LowPart, &li.HighPart, FILE_BEGIN);
+ if (li.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
+ fprintf(stderr, "SetFilePointer failed\n");
+ exit(1);
+ }
+ PR_ASSERT(li.QuadPart == offset);
+ strcpy(buf, MESSAGE);
+ if (WriteFile(hFile, buf, sizeof(buf), &nbytes, NULL) == 0) {
+ fprintf(stderr, "WriteFile failed\n");
+ exit(1);
+ }
+ PR_ASSERT(nbytes == sizeof(buf));
+ if (CloseHandle(hFile) == 0) {
+ fprintf(stderr, "CloseHandle failed\n");
+ exit(1);
+ }
+#endif /* _WIN32 */
+
+ memset(buf, 0, sizeof(buf));
+
+ fd = PR_Open(TEST_FILE_NAME, PR_RDONLY, 0666);
+ if (fd == NULL) {
+ fprintf(stderr, "PR_Open failed\n");
+ exit(1);
+ }
+ position = PR_Seek64(fd, offset, PR_SEEK_SET);
+ if (!LL_GE_ZERO(position)) {
+ fprintf(stderr, "PR_Seek64 failed\n");
+ exit(1);
+ }
+ PR_ASSERT(LL_EQ(position, offset));
+ nbytes = PR_Read(fd, buf, sizeof(buf));
+ if (nbytes != sizeof(buf)) {
+ fprintf(stderr, "PR_Read failed\n");
+ exit(1);
+ }
+ if (strcmp(buf, MESSAGE)) {
+ fprintf(stderr, "corrupt data:$%s$\n", buf);
+ exit(1);
+ }
+ if (PR_Close(fd) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+
+ if (PR_Delete(TEST_FILE_NAME) == PR_FAILURE) {
+ fprintf(stderr, "PR_Delete failed\n");
+ exit(1);
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/bug1test.c b/nsprpub/pr/tests/bug1test.c
new file mode 100644
index 0000000000..62ca90d4aa
--- /dev/null
+++ b/nsprpub/pr/tests/bug1test.c
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+Attached is a test program that uses the nspr1 to demonstrate a bug
+under NT4.0. The fix has already been mentioned (add a ResetEvent just
+before leaving the critical section in _PR_CondWait in hwmon.c).
+*/
+
+#include "prthread.h"
+#include "prtypes.h"
+#include "prinit.h"
+#include "prmon.h"
+#include "prlog.h"
+
+typedef struct Arg_s
+{
+ PRInt32 a, b;
+} Arg_t;
+
+PRMonitor* gMonitor; // the monitor
+PRInt32 gReading; // number of read locks
+PRInt32 gWriteWaiting; // number of threads waiting for write lock
+PRInt32 gReadWaiting; // number of threads waiting for read lock
+
+PRInt32 gCounter; // a counter
+
+// stats
+PRInt32 gReads; // number of successful reads
+PRInt32 gMaxReads; // max number of simultaneous reads
+PRInt32 gMaxWriteWaits; // max number of writes that waited for read
+PRInt32 gMaxReadWaits; // max number of reads that waited for write wait
+
+
+void spin (PRInt32 aDelay)
+{
+ PRInt32 index;
+ PRInt32 delay = aDelay * 1000;
+
+ PR_Sleep(0);
+
+ // randomize delay a bit
+ delay = (delay / 2) + (PRInt32)((float)delay *
+ ((float)rand () / (float)RAND_MAX));
+
+ for (index = 0; index < delay * 10; index++)
+ // consume a bunch of cpu cycles
+ ;
+ PR_Sleep(0);
+}
+
+void doWriteThread (void* arg)
+{
+ PRInt32 last;
+ Arg_t *args = (Arg_t*)arg;
+ PRInt32 aWorkDelay = args->a, aWaitDelay = args->b;
+ PR_Sleep(0);
+
+ while (1)
+ {
+ // -- enter write lock
+ PR_EnterMonitor (gMonitor);
+
+ if (0 < gReading) // wait for read locks to go away
+ {
+ PRIntervalTime fiveSecs = PR_SecondsToInterval(5);
+
+ gWriteWaiting++;
+ if (gWriteWaiting > gMaxWriteWaits) { // stats
+ gMaxWriteWaits = gWriteWaiting;
+ }
+ while (0 < gReading) {
+ PR_Wait (gMonitor, fiveSecs);
+ }
+ gWriteWaiting--;
+ }
+ // -- write lock entered
+
+ last = gCounter;
+ gCounter++;
+
+ spin (aWorkDelay);
+
+ PR_ASSERT (gCounter == (last + 1)); // test invariance
+
+ // -- exit write lock
+// if (0 < gReadWaiting) // notify waiting reads (do it anyway to show off the CondWait bug)
+ PR_NotifyAll (gMonitor);
+
+ PR_ExitMonitor (gMonitor);
+ // -- write lock exited
+
+ spin (aWaitDelay);
+ }
+}
+
+void doReadThread (void* arg)
+{
+ PRInt32 last;
+ Arg_t *args = (Arg_t*)arg;
+ PRInt32 aWorkDelay = args->a, aWaitDelay = args->b;
+ PR_Sleep(0);
+
+ while (1)
+ {
+ // -- enter read lock
+ PR_EnterMonitor (gMonitor);
+
+ if (0 < gWriteWaiting) // give up the monitor to waiting writes
+ {
+ PRIntervalTime fiveSecs = PR_SecondsToInterval(5);
+
+ gReadWaiting++;
+ if (gReadWaiting > gMaxReadWaits) { // stats
+ gMaxReadWaits = gReadWaiting;
+ }
+ while (0 < gWriteWaiting) {
+ PR_Wait (gMonitor, fiveSecs);
+ }
+ gReadWaiting--;
+ }
+
+ gReading++;
+
+ gReads++; // stats
+ if (gReading > gMaxReads) { // stats
+ gMaxReads = gReading;
+ }
+
+ PR_ExitMonitor (gMonitor);
+ // -- read lock entered
+
+ last = gCounter;
+
+ spin (aWorkDelay);
+
+ PR_ASSERT (gCounter == last); // test invariance
+
+ // -- exit read lock
+ PR_EnterMonitor (gMonitor); // read unlock
+ gReading--;
+
+// if ((0 == gReading) && (0 < gWriteWaiting)) // notify waiting writes (do it anyway to show off the CondWait bug)
+ PR_NotifyAll (gMonitor);
+ PR_ExitMonitor (gMonitor);
+ // -- read lock exited
+
+ spin (aWaitDelay);
+ }
+}
+
+
+void fireThread (
+ char* aName, void (*aProc)(void *arg), Arg_t *aArg)
+{
+ PRThread *thread = PR_CreateThread(
+ PR_USER_THREAD, aProc, aArg, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+}
+
+int pseudoMain (int argc, char** argv, char *pad)
+{
+ PRInt32 lastWriteCount = gCounter;
+ PRInt32 lastReadCount = gReads;
+ Arg_t a1 = {500, 250};
+ Arg_t a2 = {500, 500};
+ Arg_t a3 = {250, 500};
+ Arg_t a4 = {750, 250};
+ Arg_t a5 = {100, 750};
+ Arg_t a6 = {100, 500};
+ Arg_t a7 = {100, 750};
+
+ gMonitor = PR_NewMonitor ();
+
+ fireThread ("R1", doReadThread, &a1);
+ fireThread ("R2", doReadThread, &a2);
+ fireThread ("R3", doReadThread, &a3);
+ fireThread ("R4", doReadThread, &a4);
+
+ fireThread ("W1", doWriteThread, &a5);
+ fireThread ("W2", doWriteThread, &a6);
+ fireThread ("W3", doWriteThread, &a7);
+
+ fireThread ("R5", doReadThread, &a1);
+ fireThread ("R6", doReadThread, &a2);
+ fireThread ("R7", doReadThread, &a3);
+ fireThread ("R8", doReadThread, &a4);
+
+ fireThread ("W4", doWriteThread, &a5);
+ fireThread ("W5", doWriteThread, &a6);
+ fireThread ("W6", doWriteThread, &a7);
+
+ while (1)
+ {
+ PRInt32 writeCount, readCount;
+ PRIntervalTime fiveSecs = PR_SecondsToInterval(5);
+ PR_Sleep (fiveSecs); // get out of the way
+
+ // print some stats, not threadsafe, informative only
+ writeCount = gCounter;
+ readCount = gReads;
+ printf ("\ntick %d writes (+%d), %d reads (+%d) [max %d, %d, %d]",
+ writeCount, writeCount - lastWriteCount,
+ readCount, readCount - lastReadCount,
+ gMaxReads, gMaxWriteWaits, gMaxReadWaits);
+ lastWriteCount = writeCount;
+ lastReadCount = readCount;
+ gMaxReads = gMaxWriteWaits = gMaxReadWaits = 0;
+ }
+ return 0;
+}
+
+
+static void padStack (int argc, char** argv)
+{
+ char pad[512]; /* Work around bug in nspr on windoze */
+ pseudoMain (argc, argv, pad);
+}
+
+int main(int argc, char **argv)
+{
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+ padStack (argc, argv);
+}
+
+
+/* bug1test.c */
diff --git a/nsprpub/pr/tests/cleanup.c b/nsprpub/pr/tests/cleanup.c
new file mode 100644
index 0000000000..679fe59c92
--- /dev/null
+++ b/nsprpub/pr/tests/cleanup.c
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prprf.h"
+#include "prio.h"
+#include "prinit.h"
+#include "prthread.h"
+#include "prinrval.h"
+
+#include "plgetopt.h"
+
+#include <stdlib.h>
+
+static void PR_CALLBACK Thread(void *sleep)
+{
+ PR_Sleep(PR_SecondsToInterval((PRUint32)sleep));
+ printf("Thread exiting\n");
+}
+
+static void Help(void)
+{
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+ PR_fprintf(err, "Cleanup usage: [-g] [-s n] [-t n] [-c n] [-h]\n");
+ PR_fprintf(err, "\t-c Call cleanup before exiting (default: false)\n");
+ PR_fprintf(err, "\t-G Use global threads only (default: local)\n");
+ PR_fprintf(err, "\t-t n Number of threads involved (default: 1)\n");
+ PR_fprintf(err, "\t-s n Seconds thread(s) should dally (defaut: 10)\n");
+ PR_fprintf(err, "\t-S n Seconds main() should dally (defaut: 5)\n");
+ PR_fprintf(err, "\t-C n Value to set concurrency (default 1)\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+int main(int argc, char **argv)
+{
+ PLOptStatus os;
+ PRBool cleanup = PR_FALSE;
+ PRThreadScope type = PR_LOCAL_THREAD;
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Ghs:S:t:cC:");
+ PRIntn concurrency = 1, child_sleep = 10, main_sleep = 5, threads = 1;
+
+ PR_STDIO_INIT();
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'c': /* call PR_Cleanup() before exiting */
+ cleanup = PR_TRUE;
+ break;
+ case 'G': /* local vs global threads */
+ type = PR_GLOBAL_THREAD;
+ break;
+ case 's': /* time to sleep */
+ child_sleep = atoi(opt->value);
+ break;
+ case 'S': /* time to sleep */
+ main_sleep = atoi(opt->value);
+ break;
+ case 'C': /* number of cpus to create */
+ concurrency = atoi(opt->value);
+ break;
+ case 't': /* number of threads to create */
+ threads = atoi(opt->value);
+ break;
+ case 'h': /* user wants some guidance */
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_fprintf(err, "Cleanup settings\n");
+ PR_fprintf(err, "\tThread type: %s\n",
+ (PR_LOCAL_THREAD == type) ? "LOCAL" : "GLOBAL");
+ PR_fprintf(err, "\tConcurrency: %d\n", concurrency);
+ PR_fprintf(err, "\tNumber of threads: %d\n", threads);
+ PR_fprintf(err, "\tThread sleep: %d\n", child_sleep);
+ PR_fprintf(err, "\tMain sleep: %d\n", main_sleep);
+ PR_fprintf(err, "\tCleanup will %sbe called\n\n", (cleanup) ? "" : "NOT ");
+
+ PR_SetConcurrency(concurrency);
+
+ while (threads-- > 0)
+ (void)PR_CreateThread(
+ PR_USER_THREAD, Thread, (void*)child_sleep, PR_PRIORITY_NORMAL,
+ type, PR_UNJOINABLE_THREAD, 0);
+ PR_Sleep(PR_SecondsToInterval(main_sleep));
+
+ if (cleanup) {
+ PR_Cleanup();
+ }
+
+ PR_fprintf(err, "main() exiting\n");
+ return 0;
+} /* main */
diff --git a/nsprpub/pr/tests/cltsrv.c b/nsprpub/pr/tests/cltsrv.c
new file mode 100644
index 0000000000..2dbe4c7ae9
--- /dev/null
+++ b/nsprpub/pr/tests/cltsrv.c
@@ -0,0 +1,1271 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *
+ * Notes:
+ * [1] lth. The call to Sleep() is a hack to get the test case to run
+ * on Windows 95. Without it, the test case fails with an error
+ * WSAECONNRESET following a recv() call. The error is caused by the
+ * server side thread termination without a shutdown() or closesocket()
+ * call. Windows docmunentation suggests that this is predicted
+ * behavior; that other platforms get away with it is ... serindipity.
+ * The test case should shutdown() or closesocket() before
+ * thread termination. I didn't have time to figure out where or how
+ * to do it. The Sleep() call inserts enough delay to allow the
+ * client side to recv() all his data before the server side thread
+ * terminates. Whew! ...
+ *
+ ** Modification History:
+ * 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+ * The debug mode will print all of the printfs associated with this test.
+ * The regress mode will be the default mode. Since the regress tool limits
+ * the output to a one line status:PASS or FAIL,all of the printf statements
+ * have been handled with an if (debug_mode) statement.
+ */
+
+#include "prclist.h"
+#include "prcvar.h"
+#include "prerror.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prio.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prtime.h"
+#include "prmem.h"
+#include "prnetdb.h"
+#include "prprf.h"
+#include "prthread.h"
+
+#include "pprio.h"
+#include "primpl.h"
+
+#include "plstr.h"
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(XP_UNIX)
+#include <math.h>
+#endif
+
+/*
+** This is the beginning of the test
+*/
+
+#define RECV_FLAGS 0
+#define SEND_FLAGS 0
+#define DEFAULT_LOW 0
+#define DEFAULT_HIGH 0
+#define BUFFER_SIZE 1024
+#define DEFAULT_BACKLOG 5
+
+#ifdef DEBUG
+#define PORT_INC_DO +100
+#else
+#define PORT_INC_DO
+#endif
+#ifdef IS_64
+#define PORT_INC_3264 +200
+#else
+#define PORT_INC_3264
+#endif
+
+#define DEFAULT_PORT 12849 PORT_INC_DO PORT_INC_3264
+
+#define DEFAULT_CLIENTS 1
+#define ALLOWED_IN_ACCEPT 1
+#define DEFAULT_CLIPPING 1000
+#define DEFAULT_WORKERS_MIN 1
+#define DEFAULT_WORKERS_MAX 1
+#define DEFAULT_SERVER "localhost"
+#define DEFAULT_EXECUTION_TIME 10
+#define DEFAULT_CLIENT_TIMEOUT 4000
+#define DEFAULT_SERVER_TIMEOUT 4000
+#define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH
+
+typedef enum CSState_e {cs_init, cs_run, cs_stop, cs_exit} CSState_t;
+
+static void PR_CALLBACK Worker(void *arg);
+typedef struct CSPool_s CSPool_t;
+typedef struct CSWorker_s CSWorker_t;
+typedef struct CSServer_s CSServer_t;
+typedef enum Verbosity
+{
+ TEST_LOG_ALWAYS,
+ TEST_LOG_ERROR,
+ TEST_LOG_WARNING,
+ TEST_LOG_NOTICE,
+ TEST_LOG_INFO,
+ TEST_LOG_STATUS,
+ TEST_LOG_VERBOSE
+} Verbosity;
+
+static PRInt32 domain = AF_INET;
+static PRInt32 protocol = 6; /* TCP */
+static PRFileDesc *debug_out = NULL;
+static PRBool debug_mode = PR_FALSE;
+static PRBool pthread_stats = PR_FALSE;
+static Verbosity verbosity = TEST_LOG_ALWAYS;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+struct CSWorker_s
+{
+ PRCList element; /* list of the server's workers */
+
+ PRThread *thread; /* this worker objects thread */
+ CSServer_t *server; /* back pointer to server structure */
+};
+
+struct CSPool_s
+{
+ PRCondVar *exiting;
+ PRCondVar *acceptComplete;
+ PRUint32 accepting, active, workers;
+};
+
+struct CSServer_s
+{
+ PRCList list; /* head of worker list */
+
+ PRLock *ml;
+ PRThread *thread; /* the main server thread */
+ PRCondVar *stateChange;
+
+ PRUint16 port; /* port we're listening on */
+ PRUint32 backlog; /* size of our listener backlog */
+ PRFileDesc *listener; /* the fd accepting connections */
+
+ CSPool_t pool; /* statistics on worker threads */
+ CSState_t state; /* the server's state */
+ struct /* controlling worker counts */
+ {
+ PRUint32 minimum, maximum, accepting;
+ } workers;
+
+ /* statistics */
+ PRIntervalTime started, stopped;
+ PRUint32 operations, bytesTransferred;
+};
+
+typedef struct CSDescriptor_s
+{
+ PRInt32 size; /* size of transfer */
+ char filename[60]; /* filename, null padded */
+} CSDescriptor_t;
+
+typedef struct CSClient_s
+{
+ PRLock *ml;
+ PRThread *thread;
+ PRCondVar *stateChange;
+ PRNetAddr serverAddress;
+
+ CSState_t state;
+
+ /* statistics */
+ PRIntervalTime started, stopped;
+ PRUint32 operations, bytesTransferred;
+} CSClient_t;
+
+#define TEST_LOG(l, p, a) \
+ do { \
+ if (debug_mode || (p <= verbosity)) printf a; \
+ } while (0)
+
+PRLogModuleInfo *cltsrv_log_file = NULL;
+
+#define MY_ASSERT(_expr) \
+ ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
+
+#define TEST_ASSERT(_expr) \
+ ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
+
+static void _MY_Assert(const char *s, const char *file, PRIntn ln)
+{
+ PL_PrintError(NULL);
+ PR_Assert(s, file, ln);
+} /* _MY_Assert */
+
+static PRBool Aborted(PRStatus rv)
+{
+ return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) ?
+ PR_TRUE : PR_FALSE;
+}
+
+static void TimeOfDayMessage(const char *msg, PRThread* me)
+{
+ char buffer[100];
+ PRExplodedTime tod;
+ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod);
+ (void)PR_FormatTime(buffer, sizeof(buffer), "%H:%M:%S", &tod);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("%s(0x%p): %s\n", msg, me, buffer));
+} /* TimeOfDayMessage */
+
+
+static void PR_CALLBACK Client(void *arg)
+{
+ PRStatus rv;
+ PRIntn index;
+ char buffer[1024];
+ PRFileDesc *fd = NULL;
+ PRUintn clipping = DEFAULT_CLIPPING;
+ PRThread *me = PR_GetCurrentThread();
+ CSClient_t *client = (CSClient_t*)arg;
+ CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
+ PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT);
+
+
+ for (index = 0; index < sizeof(buffer); ++index) {
+ buffer[index] = (char)index;
+ }
+
+ client->started = PR_IntervalNow();
+
+ PR_Lock(client->ml);
+ client->state = cs_run;
+ PR_NotifyCondVar(client->stateChange);
+ PR_Unlock(client->ml);
+
+ TimeOfDayMessage("Client started at", me);
+
+ while (cs_run == client->state)
+ {
+ PRInt32 bytes, descbytes, filebytes, netbytes;
+
+ (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer));
+ TEST_LOG(cltsrv_log_file, TEST_LOG_INFO,
+ ("\tClient(0x%p): connecting to server at %s\n", me, buffer));
+
+ fd = PR_Socket(domain, SOCK_STREAM, protocol);
+ TEST_ASSERT(NULL != fd);
+ rv = PR_Connect(fd, &client->serverAddress, timeout);
+ if (PR_FAILURE == rv)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): conection failed (%d, %d)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto aborted;
+ }
+
+ memset(descriptor, 0, sizeof(*descriptor));
+ descriptor->size = PR_htonl(descbytes = rand() % clipping);
+ PR_snprintf(
+ descriptor->filename, sizeof(descriptor->filename),
+ "CS%p%p-%p.dat", client->started, me, client->operations);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes));
+ bytes = PR_Send(
+ fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout);
+ if (sizeof(CSDescriptor_t) != bytes)
+ {
+ if (Aborted(PR_FAILURE)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): send descriptor timeout\n", me));
+ goto retry;
+ }
+ }
+ TEST_ASSERT(sizeof(*descriptor) == bytes);
+
+ netbytes = 0;
+ while (netbytes < descbytes)
+ {
+ filebytes = sizeof(buffer);
+ if ((descbytes - netbytes) < filebytes) {
+ filebytes = descbytes - netbytes;
+ }
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tClient(0x%p): sending %d bytes\n", me, filebytes));
+ bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
+ if (filebytes != bytes)
+ {
+ if (Aborted(PR_FAILURE)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): send data timeout\n", me));
+ goto retry;
+ }
+ }
+ TEST_ASSERT(bytes == filebytes);
+ netbytes += bytes;
+ }
+ filebytes = 0;
+ while (filebytes < descbytes)
+ {
+ netbytes = sizeof(buffer);
+ if ((descbytes - filebytes) < netbytes) {
+ netbytes = descbytes - filebytes;
+ }
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tClient(0x%p): receiving %d bytes\n", me, netbytes));
+ bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
+ if (-1 == bytes)
+ {
+ if (Aborted(PR_FAILURE))
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): receive data aborted\n", me));
+ goto aborted;
+ }
+ else if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): receive data timeout\n", me));
+ else
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): receive error (%d, %d)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto retry;
+ }
+ if (0 == bytes)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tClient(0x%p): unexpected end of stream\n",
+ PR_GetCurrentThread()));
+ break;
+ }
+ filebytes += bytes;
+ }
+
+ rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ TEST_ASSERT(PR_SUCCESS == rv);
+retry:
+ (void)PR_Close(fd); fd = NULL;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("\tClient(0x%p): disconnected from server\n", me));
+
+ PR_Lock(client->ml);
+ client->operations += 1;
+ client->bytesTransferred += 2 * descbytes;
+ rv = PR_WaitCondVar(client->stateChange, rand() % clipping);
+ PR_Unlock(client->ml);
+ if (Aborted(rv)) {
+ break;
+ }
+ }
+
+aborted:
+ client->stopped = PR_IntervalNow();
+
+ PR_ClearInterrupt();
+ if (NULL != fd) {
+ rv = PR_Close(fd);
+ }
+
+ PR_Lock(client->ml);
+ client->state = cs_exit;
+ PR_NotifyCondVar(client->stateChange);
+ PR_Unlock(client->ml);
+ PR_DELETE(descriptor);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("\tClient(0x%p): stopped after %u operations and %u bytes\n",
+ PR_GetCurrentThread(), client->operations, client->bytesTransferred));
+
+} /* Client */
+
+static PRStatus ProcessRequest(PRFileDesc *fd, CSServer_t *server)
+{
+ PRStatus drv, rv;
+ char buffer[1024];
+ PRFileDesc *file = NULL;
+ PRThread * me = PR_GetCurrentThread();
+ PRInt32 bytes, descbytes, netbytes, filebytes = 0;
+ CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
+ PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): receiving desciptor\n", me));
+ bytes = PR_Recv(
+ fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout);
+ if (-1 == bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto exit;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tProcessRequest(0x%p): receive timeout\n", me));
+ }
+ goto exit;
+ }
+ if (0 == bytes)
+ {
+ rv = PR_FAILURE;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tProcessRequest(0x%p): unexpected end of file\n", me));
+ goto exit;
+ }
+ descbytes = PR_ntohl(descriptor->size);
+ TEST_ASSERT(sizeof(*descriptor) == bytes);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n",
+ me, descbytes, descriptor->filename));
+
+ file = PR_Open(
+ descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666);
+ if (NULL == file)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tProcessRequest(0x%p): open file timeout\n", me));
+ goto aborted;
+ }
+ }
+ TEST_ASSERT(NULL != file);
+
+ filebytes = 0;
+ while (filebytes < descbytes)
+ {
+ netbytes = sizeof(buffer);
+ if ((descbytes - filebytes) < netbytes) {
+ netbytes = descbytes - filebytes;
+ }
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes));
+ bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
+ if (-1 == bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): receive data timeout\n", me));
+ goto aborted;
+ }
+ /*
+ * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED)
+ * on NT here. This is equivalent to ECONNRESET on Unix.
+ * -wtc
+ */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_WARNING,
+ ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto aborted;
+ }
+ if(0 == bytes)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_WARNING,
+ ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me));
+ rv = PR_FAILURE;
+ goto aborted;
+ }
+ filebytes += bytes;
+ netbytes = bytes;
+ /* The byte count for PR_Write should be positive */
+ MY_ASSERT(netbytes > 0);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes));
+ bytes = PR_Write(file, buffer, netbytes);
+ if (netbytes != bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): write file timeout\n", me));
+ goto aborted;
+ }
+ }
+ TEST_ASSERT(bytes > 0);
+ }
+
+ PR_Lock(server->ml);
+ server->operations += 1;
+ server->bytesTransferred += filebytes;
+ PR_Unlock(server->ml);
+
+ rv = PR_Close(file);
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ TEST_ASSERT(PR_SUCCESS == rv);
+ file = NULL;
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): opening %s\n", me, descriptor->filename));
+ file = PR_Open(descriptor->filename, PR_RDONLY, 0);
+ if (NULL == file)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): open file timeout\n",
+ PR_GetCurrentThread()));
+ goto aborted;
+ }
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): other file open error (%u, %u)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto aborted;
+ }
+ TEST_ASSERT(NULL != file);
+
+ netbytes = 0;
+ while (netbytes < descbytes)
+ {
+ filebytes = sizeof(buffer);
+ if ((descbytes - netbytes) < filebytes) {
+ filebytes = descbytes - netbytes;
+ }
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): read %d bytes from file\n", me, filebytes));
+ bytes = PR_Read(file, buffer, filebytes);
+ if (filebytes != bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): read file timeout\n", me));
+ else
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): other file error (%d, %d)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto aborted;
+ }
+ TEST_ASSERT(bytes > 0);
+ netbytes += bytes;
+ filebytes = bytes;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): sending %d bytes\n", me, filebytes));
+ bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
+ if (filebytes != bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): send data timeout\n", me));
+ goto aborted;
+ }
+ break;
+ }
+ TEST_ASSERT(bytes > 0);
+ }
+
+ PR_Lock(server->ml);
+ server->bytesTransferred += filebytes;
+ PR_Unlock(server->ml);
+
+ rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+
+ rv = PR_Close(file);
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ TEST_ASSERT(PR_SUCCESS == rv);
+ file = NULL;
+
+aborted:
+ PR_ClearInterrupt();
+ if (NULL != file) {
+ PR_Close(file);
+ }
+ drv = PR_Delete(descriptor->filename);
+ TEST_ASSERT(PR_SUCCESS == drv);
+exit:
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): Finished\n", me));
+
+ PR_DELETE(descriptor);
+
+#if defined(WIN95)
+ PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */
+#endif
+ return rv;
+} /* ProcessRequest */
+
+static PRStatus CreateWorker(CSServer_t *server, CSPool_t *pool)
+{
+ CSWorker_t *worker = PR_NEWZAP(CSWorker_t);
+ worker->server = server;
+ PR_INIT_CLIST(&worker->element);
+ worker->thread = PR_CreateThread(
+ PR_USER_THREAD, Worker, worker,
+ DEFAULT_SERVER_PRIORITY, thread_scope,
+ PR_UNJOINABLE_THREAD, 0);
+ if (NULL == worker->thread)
+ {
+ PR_DELETE(worker);
+ return PR_FAILURE;
+ }
+
+ TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
+ ("\tCreateWorker(0x%p): create new worker (0x%p)\n",
+ PR_GetCurrentThread(), worker->thread));
+
+ return PR_SUCCESS;
+} /* CreateWorker */
+
+static void PR_CALLBACK Worker(void *arg)
+{
+ PRStatus rv;
+ PRNetAddr from;
+ PRFileDesc *fd = NULL;
+ PRThread *me = PR_GetCurrentThread();
+ CSWorker_t *worker = (CSWorker_t*)arg;
+ CSServer_t *server = worker->server;
+ CSPool_t *pool = &server->pool;
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1));
+
+ PR_Lock(server->ml);
+ PR_APPEND_LINK(&worker->element, &server->list);
+ pool->workers += 1; /* define our existance */
+
+ while (cs_run == server->state)
+ {
+ while (pool->accepting >= server->workers.accepting)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tWorker(0x%p): waiting for accept slot[%d]\n",
+ me, pool->accepting));
+ rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT);
+ if (Aborted(rv) || (cs_run != server->state))
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\tWorker(0x%p): has been %s\n",
+ me, (Aborted(rv) ? "interrupted" : "stopped")));
+ goto exit;
+ }
+ }
+ pool->accepting += 1; /* how many are really in accept */
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tWorker(0x%p): calling accept\n", me));
+ fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT);
+
+ PR_Lock(server->ml);
+ pool->accepting -= 1;
+ PR_NotifyCondVar(pool->acceptComplete);
+
+ if ((NULL == fd) && Aborted(PR_FAILURE))
+ {
+ if (NULL != server->listener)
+ {
+ PR_Close(server->listener);
+ server->listener = NULL;
+ }
+ goto exit;
+ }
+
+ if (NULL != fd)
+ {
+ /*
+ ** Create another worker of the total number of workers is
+ ** less than the minimum specified or we have none left in
+ ** accept() AND we're not over the maximum.
+ ** This sort of presumes that the number allowed in accept
+ ** is at least as many as the minimum. Otherwise we'll keep
+ ** creating new threads and deleting them soon after.
+ */
+ PRBool another =
+ ((pool->workers < server->workers.minimum) ||
+ ((0 == pool->accepting)
+ && (pool->workers < server->workers.maximum))) ?
+ PR_TRUE : PR_FALSE;
+ pool->active += 1;
+ PR_Unlock(server->ml);
+
+ if (another) {
+ (void)CreateWorker(server, pool);
+ }
+
+ rv = ProcessRequest(fd, server);
+ if (PR_SUCCESS != rv)
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tWorker(0x%p): server process ended abnormally\n", me));
+ (void)PR_Close(fd); fd = NULL;
+
+ PR_Lock(server->ml);
+ pool->active -= 1;
+ }
+ }
+
+exit:
+ PR_ClearInterrupt();
+ PR_Unlock(server->ml);
+
+ if (NULL != fd)
+ {
+ (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
+ (void)PR_Close(fd);
+ }
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\t\tWorker(0x%p): exiting [%u]\n", PR_GetCurrentThread(), pool->workers));
+
+ PR_Lock(server->ml);
+ pool->workers -= 1; /* undefine our existance */
+ PR_REMOVE_AND_INIT_LINK(&worker->element);
+ PR_NotifyCondVar(pool->exiting);
+ PR_Unlock(server->ml);
+
+ PR_DELETE(worker); /* destruction of the "worker" object */
+
+} /* Worker */
+
+static void PR_CALLBACK Server(void *arg)
+{
+ PRStatus rv;
+ PRNetAddr serverAddress;
+ PRThread *me = PR_GetCurrentThread();
+ CSServer_t *server = (CSServer_t*)arg;
+ PRSocketOptionData sockOpt;
+
+ server->listener = PR_Socket(domain, SOCK_STREAM, protocol);
+
+ sockOpt.option = PR_SockOpt_Reuseaddr;
+ sockOpt.value.reuse_addr = PR_TRUE;
+ rv = PR_SetSocketOption(server->listener, &sockOpt);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ memset(&serverAddress, 0, sizeof(serverAddress));
+ if (PR_AF_INET6 != domain) {
+ TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("server binding to ip port %s\n", DEFAULT_PORT));
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress);
+ }
+ else {
+ TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("server binding to ipv6 port %s\n", DEFAULT_PORT));
+ rv = PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, DEFAULT_PORT,
+ &serverAddress);
+ }
+ rv = PR_Bind(server->listener, &serverAddress);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Listen(server->listener, server->backlog);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ server->started = PR_IntervalNow();
+ TimeOfDayMessage("Server started at", me);
+
+ PR_Lock(server->ml);
+ server->state = cs_run;
+ PR_NotifyCondVar(server->stateChange);
+ PR_Unlock(server->ml);
+
+ /*
+ ** Create the first worker (actually, a thread that accepts
+ ** connections and then processes the work load as needed).
+ ** From this point on, additional worker threads are created
+ ** as they are needed by existing worker threads.
+ */
+ rv = CreateWorker(server, &server->pool);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ /*
+ ** From here on this thread is merely hanging around as the contact
+ ** point for the main test driver. It's just waiting for the driver
+ ** to declare the test complete.
+ */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tServer(0x%p): waiting for state change\n", me));
+
+ PR_Lock(server->ml);
+ while ((cs_run == server->state) && !Aborted(rv))
+ {
+ rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(server->ml);
+ PR_ClearInterrupt();
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("\tServer(0x%p): shutting down workers\n", me));
+
+ /*
+ ** Get all the worker threads to exit. They know how to
+ ** clean up after themselves, so this is just a matter of
+ ** waiting for clorine in the pool to take effect. During
+ ** this stage we're ignoring interrupts.
+ */
+ server->workers.minimum = server->workers.maximum = 0;
+
+ PR_Lock(server->ml);
+ while (!PR_CLIST_IS_EMPTY(&server->list))
+ {
+ PRCList *head = PR_LIST_HEAD(&server->list);
+ CSWorker_t *worker = (CSWorker_t*)head;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker));
+ rv = PR_Interrupt(worker->thread);
+ TEST_ASSERT(PR_SUCCESS == rv);
+ PR_REMOVE_AND_INIT_LINK(head);
+ }
+
+ while (server->pool.workers > 0)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\tServer(0x%p): waiting for %u workers to exit\n",
+ me, server->pool.workers));
+ (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ server->state = cs_exit;
+ PR_NotifyCondVar(server->stateChange);
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("\tServer(0x%p): stopped after %u operations and %u bytes\n",
+ me, server->operations, server->bytesTransferred));
+
+ if (NULL != server->listener) {
+ PR_Close(server->listener);
+ }
+ server->stopped = PR_IntervalNow();
+
+} /* Server */
+
+static void WaitForCompletion(PRIntn execution)
+{
+ while (execution > 0)
+ {
+ PRIntn dally = (execution > 30) ? 30 : execution;
+ PR_Sleep(PR_SecondsToInterval(dally));
+ if (pthread_stats) {
+ PT_FPrintStats(debug_out, "\nPThread Statistics\n");
+ }
+ execution -= dally;
+ }
+} /* WaitForCompletion */
+
+static void Help(void)
+{
+ PR_fprintf(debug_out, "cltsrv test program usage:\n");
+ PR_fprintf(debug_out, "\t-a <n> threads allowed in accept (5)\n");
+ PR_fprintf(debug_out, "\t-b <n> backlock for listen (5)\n");
+ PR_fprintf(debug_out, "\t-c <threads> number of clients to create (1)\n");
+ PR_fprintf(debug_out, "\t-f <low> low water mark for fd caching (0)\n");
+ PR_fprintf(debug_out, "\t-F <high> high water mark for fd caching (0)\n");
+ PR_fprintf(debug_out, "\t-w <threads> minimal number of server threads (1)\n");
+ PR_fprintf(debug_out, "\t-W <threads> maximum number of server threads (1)\n");
+ PR_fprintf(debug_out, "\t-e <seconds> duration of the test in seconds (10)\n");
+ PR_fprintf(debug_out, "\t-s <string> dsn name of server (localhost)\n");
+ PR_fprintf(debug_out, "\t-G use GLOBAL threads (LOCAL)\n");
+ PR_fprintf(debug_out, "\t-X use XTP as transport (TCP)\n");
+ PR_fprintf(debug_out, "\t-6 Use IPv6 (IPv4)\n");
+ PR_fprintf(debug_out, "\t-v verbosity (accumulative) (0)\n");
+ PR_fprintf(debug_out, "\t-p pthread statistics (FALSE)\n");
+ PR_fprintf(debug_out, "\t-d debug mode (FALSE)\n");
+ PR_fprintf(debug_out, "\t-h this message\n");
+} /* Help */
+
+static Verbosity IncrementVerbosity(void)
+{
+ PRIntn verboge = (PRIntn)verbosity + 1;
+ return (Verbosity)verboge;
+} /* IncrementVerbosity */
+
+int main(int argc, char** argv)
+{
+ PRUintn index;
+ PRBool boolean;
+ CSClient_t *client;
+ PRStatus rv, joinStatus;
+ CSServer_t *server = NULL;
+
+ PRUintn backlog = DEFAULT_BACKLOG;
+ PRUintn clients = DEFAULT_CLIENTS;
+ const char *serverName = DEFAULT_SERVER;
+ PRBool serverIsLocal = PR_TRUE;
+ PRUintn accepting = ALLOWED_IN_ACCEPT;
+ PRUintn workersMin = DEFAULT_WORKERS_MIN;
+ PRUintn workersMax = DEFAULT_WORKERS_MAX;
+ PRIntn execution = DEFAULT_EXECUTION_TIME;
+ PRIntn low = DEFAULT_LOW, high = DEFAULT_HIGH;
+
+ /*
+ * -G use global threads
+ * -a <n> threads allowed in accept
+ * -b <n> backlock for listen
+ * -c <threads> number of clients to create
+ * -f <low> low water mark for caching FDs
+ * -F <high> high water mark for caching FDs
+ * -w <threads> minimal number of server threads
+ * -W <threads> maximum number of server threads
+ * -e <seconds> duration of the test in seconds
+ * -s <string> dsn name of server (implies no server here)
+ * -v verbosity
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:f:F:w:W:e:s:vdhp");
+
+ debug_out = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'X': /* use XTP as transport */
+ protocol = 36;
+ break;
+ case '6': /* Use IPv6 */
+ domain = PR_AF_INET6;
+ break;
+ case 'a': /* the value for accepting */
+ accepting = atoi(opt->value);
+ break;
+ case 'b': /* the value for backlock */
+ backlog = atoi(opt->value);
+ break;
+ case 'c': /* number of client threads */
+ clients = atoi(opt->value);
+ break;
+ case 'f': /* low water fd cache */
+ low = atoi(opt->value);
+ break;
+ case 'F': /* low water fd cache */
+ high = atoi(opt->value);
+ break;
+ case 'w': /* minimum server worker threads */
+ workersMin = atoi(opt->value);
+ break;
+ case 'W': /* maximum server worker threads */
+ workersMax = atoi(opt->value);
+ break;
+ case 'e': /* program execution time in seconds */
+ execution = atoi(opt->value);
+ break;
+ case 's': /* server's address */
+ serverName = opt->value;
+ break;
+ case 'v': /* verbosity */
+ verbosity = IncrementVerbosity();
+ break;
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'p': /* pthread mode */
+ pthread_stats = PR_TRUE;
+ break;
+ case 'h':
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) {
+ serverIsLocal = PR_FALSE;
+ }
+ if (0 == execution) {
+ execution = DEFAULT_EXECUTION_TIME;
+ }
+ if (0 == workersMax) {
+ workersMax = DEFAULT_WORKERS_MAX;
+ }
+ if (0 == workersMin) {
+ workersMin = DEFAULT_WORKERS_MIN;
+ }
+ if (0 == accepting) {
+ accepting = ALLOWED_IN_ACCEPT;
+ }
+ if (0 == backlog) {
+ backlog = DEFAULT_BACKLOG;
+ }
+
+ if (workersMin > accepting) {
+ accepting = workersMin;
+ }
+
+ PR_STDIO_INIT();
+ TimeOfDayMessage("Client/Server started at", PR_GetCurrentThread());
+
+ cltsrv_log_file = PR_NewLogModule("cltsrv_log");
+ MY_ASSERT(NULL != cltsrv_log_file);
+ boolean = PR_SetLogFile("cltsrv.log");
+ MY_ASSERT(boolean);
+
+ rv = PR_SetFDCacheSize(low, high);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ if (serverIsLocal)
+ {
+ /* Establish the server */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("main(0x%p): starting server\n", PR_GetCurrentThread()));
+
+ server = PR_NEWZAP(CSServer_t);
+ PR_INIT_CLIST(&server->list);
+ server->state = cs_init;
+ server->ml = PR_NewLock();
+ server->backlog = backlog;
+ server->port = DEFAULT_PORT;
+ server->workers.minimum = workersMin;
+ server->workers.maximum = workersMax;
+ server->workers.accepting = accepting;
+ server->stateChange = PR_NewCondVar(server->ml);
+ server->pool.exiting = PR_NewCondVar(server->ml);
+ server->pool.acceptComplete = PR_NewCondVar(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("main(0x%p): creating server thread\n", PR_GetCurrentThread()));
+
+ server->thread = PR_CreateThread(
+ PR_USER_THREAD, Server, server, PR_PRIORITY_HIGH,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ TEST_ASSERT(NULL != server->thread);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): waiting for server init\n", PR_GetCurrentThread()));
+
+ PR_Lock(server->ml);
+ while (server->state == cs_init) {
+ PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): server init complete (port #%d)\n",
+ PR_GetCurrentThread(), server->port));
+ }
+
+ if (clients != 0)
+ {
+ /* Create all of the clients */
+ PRHostEnt host;
+ char buffer[BUFFER_SIZE];
+ client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t));
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): creating %d client threads\n",
+ PR_GetCurrentThread(), clients));
+
+ if (!serverIsLocal)
+ {
+ rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host);
+ if (PR_SUCCESS != rv)
+ {
+ PL_FPrintError(PR_STDERR, "PR_GetHostByName");
+ return 2;
+ }
+ }
+
+ for (index = 0; index < clients; ++index)
+ {
+ client[index].state = cs_init;
+ client[index].ml = PR_NewLock();
+ if (serverIsLocal)
+ {
+ if (PR_AF_INET6 != domain) {
+ TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("loopback client ip port %s\n", DEFAULT_PORT));
+ (void)PR_InitializeNetAddr(
+ PR_IpAddrLoopback, DEFAULT_PORT,
+ &client[index].serverAddress);
+ }
+ else {
+ TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("loopback client ipv6 port %s\n", DEFAULT_PORT));
+ rv = PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET6,
+ DEFAULT_PORT, &client[index].serverAddress);
+ }
+ }
+ else
+ {
+ TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("client enumerate port %s\n", DEFAULT_PORT));
+ (void)PR_EnumerateHostEnt(
+ 0, &host, DEFAULT_PORT, &client[index].serverAddress);
+ }
+ client[index].stateChange = PR_NewCondVar(client[index].ml);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("main(0x%p): creating client threads\n", PR_GetCurrentThread()));
+ client[index].thread = PR_CreateThread(
+ PR_USER_THREAD, Client, &client[index], PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ TEST_ASSERT(NULL != client[index].thread);
+ PR_Lock(client[index].ml);
+ while (cs_init == client[index].state) {
+ PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(client[index].ml);
+ }
+ }
+
+ /* Then just let them go at it for a bit */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("main(0x%p): waiting for execution interval (%d seconds)\n",
+ PR_GetCurrentThread(), execution));
+
+ WaitForCompletion(execution);
+
+ TimeOfDayMessage("Shutting down", PR_GetCurrentThread());
+
+ if (clients != 0)
+ {
+ for (index = 0; index < clients; ++index)
+ {
+ TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
+ ("main(0x%p): notifying client(0x%p) to stop\n",
+ PR_GetCurrentThread(), client[index].thread));
+
+ PR_Lock(client[index].ml);
+ if (cs_run == client[index].state)
+ {
+ client[index].state = cs_stop;
+ PR_Interrupt(client[index].thread);
+ while (cs_stop == client[index].state)
+ PR_WaitCondVar(
+ client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(client[index].ml);
+
+ TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): joining client(0x%p)\n",
+ PR_GetCurrentThread(), client[index].thread));
+
+ joinStatus = PR_JoinThread(client[index].thread);
+ TEST_ASSERT(PR_SUCCESS == joinStatus);
+ PR_DestroyCondVar(client[index].stateChange);
+ PR_DestroyLock(client[index].ml);
+ }
+ PR_DELETE(client);
+ }
+
+ if (NULL != server)
+ {
+ /* All clients joined - retrieve the server */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("main(0x%p): notifying server(0x%p) to stop\n",
+ PR_GetCurrentThread(), server->thread));
+
+ PR_Lock(server->ml);
+ server->state = cs_stop;
+ PR_Interrupt(server->thread);
+ while (cs_exit != server->state) {
+ PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("main(0x%p): joining server(0x%p)\n",
+ PR_GetCurrentThread(), server->thread));
+ joinStatus = PR_JoinThread(server->thread);
+ TEST_ASSERT(PR_SUCCESS == joinStatus);
+
+ PR_DestroyCondVar(server->stateChange);
+ PR_DestroyCondVar(server->pool.exiting);
+ PR_DestroyCondVar(server->pool.acceptComplete);
+ PR_DestroyLock(server->ml);
+ PR_DELETE(server);
+ }
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("main(0x%p): test complete\n", PR_GetCurrentThread()));
+
+ PT_FPrintStats(debug_out, "\nPThread Statistics\n");
+
+ TimeOfDayMessage("Test exiting at", PR_GetCurrentThread());
+ PR_Cleanup();
+ return 0;
+} /* main */
+
+/* cltsrv.c */
diff --git a/nsprpub/pr/tests/concur.c b/nsprpub/pr/tests/concur.c
new file mode 100644
index 0000000000..5940290213
--- /dev/null
+++ b/nsprpub/pr/tests/concur.c
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: concur.c
+** Description: test of adding and removing concurrency options
+*/
+
+#include "prcvar.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prlock.h"
+#include "prprf.h"
+#include "prmem.h"
+#include "prlog.h"
+
+#include "plgetopt.h"
+
+#include "private/pprio.h"
+
+#include <stdlib.h>
+
+#define DEFAULT_RANGE 10
+#define DEFAULT_LOOPS 100
+
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+typedef struct Context
+{
+ PRLock *ml;
+ PRCondVar *cv;
+ PRIntn want, have;
+} Context;
+
+
+/*
+** Make the instance of 'context' static (not on the stack)
+** for Win16 threads
+*/
+static Context context = {NULL, NULL, 0, 0};
+
+static void PR_CALLBACK Dull(void *arg)
+{
+ Context *context = (Context*)arg;
+ PR_Lock(context->ml);
+ context->have += 1;
+ while (context->want >= context->have) {
+ PR_WaitCondVar(context->cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ context->have -= 1;
+ PR_Unlock(context->ml);
+} /* Dull */
+
+PRIntn PR_CALLBACK Concur(PRIntn argc, char **argv)
+{
+ PRUintn cpus;
+ PLOptStatus os;
+ PRThread **threads;
+ PRBool debug = PR_FALSE;
+ PRUintn range = DEFAULT_RANGE;
+ PRStatus rc;
+ PRUintn cnt;
+ PRUintn loops = DEFAULT_LOOPS;
+ PRIntervalTime hundredMills = PR_MillisecondsToInterval(100);
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:r:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'G': /* GLOBAL threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'd': /* debug mode */
+ debug = PR_TRUE;
+ break;
+ case 'r': /* range limit */
+ range = atoi(opt->value);
+ break;
+ case 'l': /* loop counter */
+ loops = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == range) {
+ range = DEFAULT_RANGE;
+ }
+ if (0 == loops) {
+ loops = DEFAULT_LOOPS;
+ }
+
+ context.ml = PR_NewLock();
+ context.cv = PR_NewCondVar(context.ml);
+
+ if (debug)
+ PR_fprintf(
+ PR_STDERR, "Testing with %d CPUs and %d interations\n", range, loops);
+
+ threads = (PRThread**) PR_CALLOC(sizeof(PRThread*) * range);
+ while (--loops > 0)
+ {
+ for (cpus = 1; cpus <= range; ++cpus)
+ {
+ PR_SetConcurrency(cpus);
+ context.want = cpus;
+
+ threads[cpus - 1] = PR_CreateThread(
+ PR_USER_THREAD, Dull, &context, PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ }
+
+ PR_Sleep(hundredMills);
+
+ for (cpus = range; cpus > 0; cpus--)
+ {
+ PR_SetConcurrency(cpus);
+ context.want = cpus - 1;
+
+ PR_Lock(context.ml);
+ PR_NotifyCondVar(context.cv);
+ PR_Unlock(context.ml);
+ }
+ for(cnt = 0; cnt < range; cnt++) {
+ rc = PR_JoinThread(threads[cnt]);
+ PR_ASSERT(rc == PR_SUCCESS);
+ }
+ }
+
+
+ if (debug)
+ PR_fprintf(
+ PR_STDERR, "Waiting for %d thread(s) to exit\n", context.have);
+
+ while (context.have > 0) {
+ PR_Sleep(hundredMills);
+ }
+
+ if (debug)
+ PR_fprintf(
+ PR_STDERR, "Finished [want: %d, have: %d]\n",
+ context.want, context.have);
+
+ PR_DestroyLock(context.ml);
+ PR_DestroyCondVar(context.cv);
+ PR_DELETE(threads);
+
+ PR_fprintf(PR_STDERR, "PASSED\n");
+
+ return 0;
+} /* Concur */
+
+int main(int argc, char **argv)
+{
+ PR_STDIO_INIT();
+ return PR_Initialize(Concur, argc, argv, 0);
+} /* main */
+
+/* concur.c */
diff --git a/nsprpub/pr/tests/cvar.c b/nsprpub/pr/tests/cvar.c
new file mode 100644
index 0000000000..4dc891d3f9
--- /dev/null
+++ b/nsprpub/pr/tests/cvar.c
@@ -0,0 +1,309 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+** 1996 - Netscape Communications Corporation
+**
+** Name: cvar.c
+**
+** Description: Tests Condition Variable Operations
+**
+** Modification History:
+** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 Revert to return code 0 and 1.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+
+#include "nspr.h"
+
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRMonitor *mon;
+#define DEFAULT_COUNT 1000
+PRInt32 count = 0;
+PRIntn debug_mode;
+
+#define kQSIZE 1
+
+typedef struct {
+ PRLock *bufLock;
+ int startIdx;
+ int numFull;
+ PRCondVar *notFull;
+ PRCondVar *notEmpty;
+ void *data[kQSIZE];
+} CircBuf;
+
+static PRBool failed = PR_FALSE;
+
+/*
+** NewCB creates and initializes a new circular buffer.
+*/
+static CircBuf* NewCB(void)
+{
+ CircBuf *cbp;
+
+ cbp = PR_NEW(CircBuf);
+ if (cbp == NULL) {
+ return (NULL);
+ }
+
+ cbp->bufLock = PR_NewLock();
+ cbp->startIdx = 0;
+ cbp->numFull = 0;
+ cbp->notFull = PR_NewCondVar(cbp->bufLock);
+ cbp->notEmpty = PR_NewCondVar(cbp->bufLock);
+
+ return (cbp);
+}
+
+/*
+** DeleteCB frees a circular buffer.
+*/
+static void DeleteCB(CircBuf *cbp)
+{
+ PR_DestroyLock(cbp->bufLock);
+ PR_DestroyCondVar(cbp->notFull);
+ PR_DestroyCondVar(cbp->notEmpty);
+ PR_DELETE(cbp);
+}
+
+
+/*
+** PutCBData puts new data on the queue. If the queue is full, it waits
+** until there is room.
+*/
+static void PutCBData(CircBuf *cbp, void *data)
+{
+ PR_Lock(cbp->bufLock);
+ /* wait while the buffer is full */
+ while (cbp->numFull == kQSIZE) {
+ PR_WaitCondVar(cbp->notFull,PR_INTERVAL_NO_TIMEOUT);
+ }
+ cbp->data[(cbp->startIdx + cbp->numFull) % kQSIZE] = data;
+ cbp->numFull += 1;
+
+ /* let a waiting reader know that there is data */
+ PR_NotifyCondVar(cbp->notEmpty);
+ PR_Unlock(cbp->bufLock);
+
+}
+
+
+/*
+** GetCBData gets the oldest data on the queue. If the queue is empty, it waits
+** until new data appears.
+*/
+static void* GetCBData(CircBuf *cbp)
+{
+ void *data;
+
+ PR_Lock(cbp->bufLock);
+ /* wait while the buffer is empty */
+ while (cbp->numFull == 0) {
+ PR_WaitCondVar(cbp->notEmpty,PR_INTERVAL_NO_TIMEOUT);
+ }
+ data = cbp->data[cbp->startIdx];
+ cbp->startIdx =(cbp->startIdx + 1) % kQSIZE;
+ cbp->numFull -= 1;
+
+ /* let a waiting writer know that there is room */
+ PR_NotifyCondVar(cbp->notFull);
+ PR_Unlock(cbp->bufLock);
+
+ return (data);
+}
+
+
+/************************************************************************/
+
+static int alive;
+
+static void PR_CALLBACK CXReader(void *arg)
+{
+ CircBuf *cbp = (CircBuf *)arg;
+ PRInt32 i, n;
+ void *data;
+
+ n = count / 2;
+ for (i = 0; i < n; i++) {
+ data = GetCBData(cbp);
+ if ((int)data != i)
+ if (debug_mode) {
+ printf("data mismatch at for i = %d usec\n", i);
+ }
+ }
+
+ PR_EnterMonitor(mon);
+ --alive;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+}
+
+static void PR_CALLBACK CXWriter(void *arg)
+{
+ CircBuf *cbp = (CircBuf *)arg;
+ PRInt32 i, n;
+
+ n = count / 2;
+ for (i = 0; i < n; i++) {
+ PutCBData(cbp, (void *)i);
+ }
+
+ PR_EnterMonitor(mon);
+ --alive;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+}
+
+static void CondWaitContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *t1, *t2;
+ CircBuf *cbp;
+
+ PR_EnterMonitor(mon);
+
+ alive = 2;
+
+ cbp = NewCB();
+
+ t1 = PR_CreateThread(PR_USER_THREAD,
+ CXReader, cbp,
+ PR_PRIORITY_NORMAL,
+ scope1,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t1);
+ t2 = PR_CreateThread(PR_USER_THREAD,
+ CXWriter, cbp,
+ PR_PRIORITY_NORMAL,
+ scope2,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t2);
+
+ /* Wait for both of the threads to exit */
+ while (alive) {
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ DeleteCB(cbp);
+
+ PR_ExitMonitor(mon);
+}
+
+static void CondWaitContextSwitchUU(void)
+{
+ CondWaitContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void CondWaitContextSwitchUK(void)
+{
+ CondWaitContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+static void CondWaitContextSwitchKK(void)
+{
+ CondWaitContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ if (debug_mode) {
+ printf("%40s: %6.2f usec\n", msg, d / count);
+ }
+
+ if (0 == d) {
+ failed = PR_TRUE;
+ }
+}
+
+static PRIntn PR_CALLBACK RealMain(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name [-d] [-c n]
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dc:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'c': /* loop count */
+ count = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == count) {
+ count = DEFAULT_COUNT;
+ }
+
+ mon = PR_NewMonitor();
+
+ Measure(CondWaitContextSwitchUU, "cond var wait context switch- user/user");
+ Measure(CondWaitContextSwitchUK, "cond var wait context switch- user/kernel");
+ Measure(CondWaitContextSwitchKK, "cond var wait context switch- kernel/kernel");
+
+ PR_DestroyMonitor(mon);
+
+ if (debug_mode) {
+ printf("%s\n", (failed) ? "FAILED" : "PASSED");
+ }
+
+ if(failed) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/nsprpub/pr/tests/cvar2.c b/nsprpub/pr/tests/cvar2.c
new file mode 100644
index 0000000000..c9f57d5cc6
--- /dev/null
+++ b/nsprpub/pr/tests/cvar2.c
@@ -0,0 +1,976 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+** 1996 - Netscape Communications Corporation
+**
+** Name: cvar2.c
+**
+** Description: Simple test creates several local and global threads;
+** half use a single,shared condvar, and the
+** other half have their own condvar. The main thread then loops
+** notifying them to wakeup.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+
+#include "nspr.h"
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int _debug_on = 0;
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+#define DEFAULT_COUNT 100
+#define DEFAULT_THREADS 5
+PRInt32 count = DEFAULT_COUNT;
+
+typedef struct threadinfo {
+ PRThread *thread;
+ PRInt32 id;
+ PRBool internal;
+ PRInt32 *tcount;
+ PRLock *lock;
+ PRCondVar *cvar;
+ PRIntervalTime timeout;
+ PRInt32 loops;
+
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+ PRInt32 *exitcount;
+} threadinfo;
+
+/*
+** Make exitcount, tcount static. for Win16.
+*/
+static PRInt32 exitcount=0;
+static PRInt32 tcount=0;
+
+
+/* Thread that gets notified; many threads share the same condvar */
+void PR_CALLBACK
+SharedCondVarThread(void *_info)
+{
+ threadinfo *info = (threadinfo *)_info;
+ PRInt32 index;
+
+ for (index=0; index<info->loops; index++) {
+ PR_Lock(info->lock);
+ if (*info->tcount == 0) {
+ PR_WaitCondVar(info->cvar, info->timeout);
+ }
+#if 0
+ printf("shared thread %ld notified in loop %ld\n", info->id, index);
+#endif
+ (*info->tcount)--;
+ PR_Unlock(info->lock);
+
+ PR_Lock(info->exitlock);
+ (*info->exitcount)++;
+ PR_NotifyCondVar(info->exitcvar);
+ PR_Unlock(info->exitlock);
+ }
+#if 0
+ printf("shared thread %ld terminating\n", info->id);
+#endif
+}
+
+/* Thread that gets notified; no other threads use the same condvar */
+void PR_CALLBACK
+PrivateCondVarThread(void *_info)
+{
+ threadinfo *info = (threadinfo *)_info;
+ PRInt32 index;
+
+ for (index=0; index<info->loops; index++) {
+ PR_Lock(info->lock);
+ if (*info->tcount == 0) {
+ DPRINTF(("PrivateCondVarThread: thread 0x%lx waiting on cvar = 0x%lx\n",
+ PR_GetCurrentThread(), info->cvar));
+ PR_WaitCondVar(info->cvar, info->timeout);
+ }
+#if 0
+ printf("solo thread %ld notified in loop %ld\n", info->id, index);
+#endif
+ (*info->tcount)--;
+ PR_Unlock(info->lock);
+
+ PR_Lock(info->exitlock);
+ (*info->exitcount)++;
+ PR_NotifyCondVar(info->exitcvar);
+ DPRINTF(("PrivateCondVarThread: thread 0x%lx notified exitcvar = 0x%lx cnt = %ld\n",
+ PR_GetCurrentThread(), info->exitcvar,(*info->exitcount)));
+ PR_Unlock(info->exitlock);
+ }
+#if 0
+ printf("solo thread %ld terminating\n", info->id);
+#endif
+}
+
+void
+CreateTestThread(threadinfo *info,
+ PRInt32 id,
+ PRLock *lock,
+ PRCondVar *cvar,
+ PRInt32 loops,
+ PRIntervalTime timeout,
+ PRInt32 *tcount,
+ PRLock *exitlock,
+ PRCondVar *exitcvar,
+ PRInt32 *exitcount,
+ PRBool shared,
+ PRThreadScope scope)
+{
+ info->id = id;
+ info->internal = (shared) ? PR_FALSE : PR_TRUE;
+ info->lock = lock;
+ info->cvar = cvar;
+ info->loops = loops;
+ info->timeout = timeout;
+ info->tcount = tcount;
+ info->exitlock = exitlock;
+ info->exitcvar = exitcvar;
+ info->exitcount = exitcount;
+ info->thread = PR_CreateThread(
+ PR_USER_THREAD,
+ shared?SharedCondVarThread:PrivateCondVarThread,
+ info,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!info->thread) {
+ PL_PrintError("error creating thread\n");
+ }
+}
+
+
+void
+CondVarTestSUU(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+
+ exitcount=0;
+ tcount=0;
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg; ) {
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_LOCAL_THREAD);
+ index++;
+ DPRINTF(("CondVarTestSUU: created thread 0x%lx\n",list[index].thread));
+ }
+
+ for (loops = 0; loops < count; loops++) {
+ /* Notify the threads */
+ for(index=0; index<(arg); index++) {
+ PR_Lock(list[index].lock);
+ (*list[index].tcount)++;
+ PR_NotifyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ DPRINTF(("PrivateCondVarThread: thread 0x%lx notified cvar = 0x%lx\n",
+ PR_GetCurrentThread(), list[index].cvar));
+ }
+
+ /* Wait for threads to finish */
+ PR_Lock(exitlock);
+ while(exitcount < arg) {
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ }
+ PR_ASSERT(exitcount >= arg);
+ exitcount -= arg;
+ PR_Unlock(exitlock);
+ }
+
+ /* Join all the threads */
+ for(index=0; index<(arg); index++) {
+ PR_JoinThread(list[index].thread);
+ }
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+ PR_DestroyCondVar(exitcvar);
+ PR_DestroyLock(exitlock);
+
+ PR_DELETE(list);
+}
+
+void
+CondVarTestSUK(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+ exitcount=0;
+ tcount=0;
+
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg; ) {
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_GLOBAL_THREAD);
+ index++;
+ }
+
+ for (loops = 0; loops < count; loops++) {
+ /* Notify the threads */
+ for(index=0; index<(arg); index++) {
+
+ PR_Lock(list[index].lock);
+ (*list[index].tcount)++;
+ PR_NotifyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ }
+
+#if 0
+ printf("wait for threads to be done\n");
+#endif
+ /* Wait for threads to finish */
+ PR_Lock(exitlock);
+ while(exitcount < arg) {
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ }
+ PR_ASSERT(exitcount >= arg);
+ exitcount -= arg;
+ PR_Unlock(exitlock);
+#if 0
+ printf("threads ready\n");
+#endif
+ }
+
+ /* Join all the threads */
+ for(index=0; index<(arg); index++) {
+ PR_JoinThread(list[index].thread);
+ }
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+ PR_DestroyCondVar(exitcvar);
+ PR_DestroyLock(exitlock);
+
+ PR_DELETE(list);
+}
+
+void
+CondVarTestPUU(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+ PRInt32 *tcount, *saved_tcount;
+
+ exitcount=0;
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+ saved_tcount = tcount = (PRInt32 *)PR_CALLOC(sizeof(*tcount) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg; ) {
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_LOCAL_THREAD);
+
+ DPRINTF(("CondVarTestPUU: created thread 0x%lx\n",list[index].thread));
+ index++;
+ tcount++;
+ }
+
+ for (loops = 0; loops < count; loops++) {
+ /* Notify the threads */
+ for(index=0; index<(arg); index++) {
+
+ PR_Lock(list[index].lock);
+ (*list[index].tcount)++;
+ PR_NotifyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ }
+
+ PR_Lock(exitlock);
+ /* Wait for threads to finish */
+ while(exitcount < arg) {
+ DPRINTF(("CondVarTestPUU: thread 0x%lx waiting on exitcvar = 0x%lx cnt = %ld\n",
+ PR_GetCurrentThread(), exitcvar, exitcount));
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ }
+ PR_ASSERT(exitcount >= arg);
+ exitcount -= arg;
+ PR_Unlock(exitlock);
+ }
+
+ /* Join all the threads */
+ for(index=0; index<(arg); index++) {
+ DPRINTF(("CondVarTestPUU: joining thread 0x%lx\n",list[index].thread));
+ PR_JoinThread(list[index].thread);
+ if (list[index].internal) {
+ PR_Lock(list[index].lock);
+ PR_DestroyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ PR_DestroyLock(list[index].lock);
+ }
+ }
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+ PR_DestroyCondVar(exitcvar);
+ PR_DestroyLock(exitlock);
+
+ PR_DELETE(list);
+ PR_DELETE(saved_tcount);
+}
+
+void
+CondVarTestPUK(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+ PRInt32 *tcount, *saved_tcount;
+
+ exitcount=0;
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+ saved_tcount = tcount = (PRInt32 *)PR_CALLOC(sizeof(*tcount) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg; ) {
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_GLOBAL_THREAD);
+
+ index++;
+ tcount++;
+ }
+
+ for (loops = 0; loops < count; loops++) {
+ /* Notify the threads */
+ for(index=0; index<(arg); index++) {
+
+ PR_Lock(list[index].lock);
+ (*list[index].tcount)++;
+ PR_NotifyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ }
+
+ /* Wait for threads to finish */
+ PR_Lock(exitlock);
+ while(exitcount < arg) {
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ }
+ PR_ASSERT(exitcount >= arg);
+ exitcount -= arg;
+ PR_Unlock(exitlock);
+ }
+
+ /* Join all the threads */
+ for(index=0; index<(arg); index++) {
+ PR_JoinThread(list[index].thread);
+ if (list[index].internal) {
+ PR_Lock(list[index].lock);
+ PR_DestroyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ PR_DestroyLock(list[index].lock);
+ }
+ }
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+ PR_DestroyCondVar(exitcvar);
+ PR_DestroyLock(exitlock);
+
+ PR_DELETE(list);
+ PR_DELETE(saved_tcount);
+}
+
+void
+CondVarTest(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+ PRInt32 *ptcount, *saved_ptcount;
+
+ exitcount=0;
+ tcount=0;
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+ saved_ptcount = ptcount = (PRInt32 *)PR_CALLOC(sizeof(*ptcount) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg*4; ) {
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_LOCAL_THREAD);
+
+ index++;
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_GLOBAL_THREAD);
+
+ index++;
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ ptcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_LOCAL_THREAD);
+ index++;
+ ptcount++;
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ ptcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_GLOBAL_THREAD);
+
+ index++;
+ ptcount++;
+ }
+
+ for (loops = 0; loops < count; loops++) {
+
+ /* Notify the threads */
+ for(index=0; index<(arg*4); index++) {
+ PR_Lock(list[index].lock);
+ (*list[index].tcount)++;
+ PR_NotifyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ }
+
+#if 0
+ printf("wait for threads done\n");
+#endif
+
+ /* Wait for threads to finish */
+ PR_Lock(exitlock);
+ while(exitcount < arg*4) {
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ }
+ PR_ASSERT(exitcount >= arg*4);
+ exitcount -= arg*4;
+ PR_Unlock(exitlock);
+#if 0
+ printf("threads ready\n");
+#endif
+ }
+
+ /* Join all the threads */
+ for(index=0; index<(arg*4); index++) {
+ PR_JoinThread(list[index].thread);
+ if (list[index].internal) {
+ PR_Lock(list[index].lock);
+ PR_DestroyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ PR_DestroyLock(list[index].lock);
+ }
+ }
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+ PR_DestroyCondVar(exitcvar);
+ PR_DestroyLock(exitlock);
+
+ PR_DELETE(list);
+ PR_DELETE(saved_ptcount);
+}
+
+void
+CondVarTimeoutTest(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg*4; ) {
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_LOCAL_THREAD);
+ index++;
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_GLOBAL_THREAD);
+ index++;
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_LOCAL_THREAD);
+ index++;
+
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_GLOBAL_THREAD);
+
+ index++;
+ }
+
+ for (loops = 0; loops < count; loops++) {
+
+ /* Wait for threads to finish */
+ PR_Lock(exitlock);
+ while(exitcount < arg*4) {
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ }
+ PR_ASSERT(exitcount >= arg*4);
+ exitcount -= arg*4;
+ PR_Unlock(exitlock);
+ }
+
+
+ /* Join all the threads */
+ for(index=0; index<(arg*4); index++) {
+ PR_JoinThread(list[index].thread);
+ if (list[index].internal) {
+ PR_Lock(list[index].lock);
+ PR_DestroyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ PR_DestroyLock(list[index].lock);
+ }
+ }
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+ PR_DestroyCondVar(exitcvar);
+ PR_DestroyLock(exitlock);
+
+ PR_DELETE(list);
+}
+
+void
+CondVarMixedTest(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+ PRInt32 *ptcount;
+
+ exitcount=0;
+ tcount=0;
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+ ptcount = (PRInt32 *)PR_CALLOC(sizeof(*ptcount) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg*4; ) {
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_LOCAL_THREAD);
+ index++;
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_GLOBAL_THREAD);
+ index++;
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ ptcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_LOCAL_THREAD);
+ index++;
+ ptcount++;
+
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ ptcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_GLOBAL_THREAD);
+ index++;
+ ptcount++;
+ }
+
+
+ /* Notify every 3rd thread */
+ for (loops = 0; loops < count; loops++) {
+
+ /* Notify the threads */
+ for(index=0; index<(arg*4); index+=3) {
+
+ PR_Lock(list[index].lock);
+ *list[index].tcount++;
+ PR_NotifyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+
+ }
+ /* Wait for threads to finish */
+ PR_Lock(exitlock);
+ while(exitcount < arg*4) {
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ }
+ PR_ASSERT(exitcount >= arg*4);
+ exitcount -= arg*4;
+ PR_Unlock(exitlock);
+ }
+
+ /* Join all the threads */
+ for(index=0; index<(arg*4); index++) {
+ PR_JoinThread(list[index].thread);
+ if (list[index].internal) {
+ PR_Lock(list[index].lock);
+ PR_DestroyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ PR_DestroyLock(list[index].lock);
+ }
+ }
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+
+ PR_DELETE(list);
+}
+
+void
+CondVarCombinedTest(void *arg)
+{
+ PRThread *threads[3];
+
+ threads[0] = PR_CreateThread(PR_USER_THREAD,
+ CondVarTest,
+ (void *)arg,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ threads[1] = PR_CreateThread(PR_USER_THREAD,
+ CondVarTimeoutTest,
+ (void *)arg,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ threads[2] = PR_CreateThread(PR_USER_THREAD,
+ CondVarMixedTest,
+ (void *)arg,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ PR_JoinThread(threads[0]);
+ PR_JoinThread(threads[1]);
+ PR_JoinThread(threads[2]);
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void *), PRInt32 arg, const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)((void *)arg);
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ printf("%40s: %6.2f usec\n", msg, d / count);
+}
+
+static PRIntn PR_CALLBACK RealMain(int argc, char **argv)
+{
+ PRInt32 threads, default_threads = DEFAULT_THREADS;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "vc:t:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'v': /* debug mode */
+ _debug_on = 1;
+ break;
+ case 'c': /* loop counter */
+ count = atoi(opt->value);
+ break;
+ case 't': /* number of threads involved */
+ default_threads = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == count) {
+ count = DEFAULT_COUNT;
+ }
+ if (0 == default_threads) {
+ default_threads = DEFAULT_THREADS;
+ }
+
+ printf("\n\
+CondVar Test: \n\
+ \n\
+Simple test creates several local and global threads; half use a single,\n\
+shared condvar, and the other half have their own condvar. The main \n\
+thread then loops notifying them to wakeup. \n\
+ \n\
+The timeout test is very similar except that the threads are not \n\
+notified. They will all wakeup on a 1 second timeout. \n\
+ \n\
+The mixed test combines the simple test and the timeout test; every \n\
+third thread is notified, the other threads are expected to timeout \n\
+correctly. \n\
+ \n\
+Lastly, the combined test creates a thread for each of the above three \n\
+cases and they all run simultaneously. \n\
+ \n\
+This test is run with %d, %d, %d, and %d threads of each type.\n\n",
+ default_threads, default_threads*2, default_threads*3, default_threads*4);
+
+ PR_SetConcurrency(2);
+
+ for (threads = default_threads; threads < default_threads*5; threads+=default_threads) {
+ printf("\n%ld Thread tests\n", threads);
+ Measure(CondVarTestSUU, threads, "Condvar simple test shared UU");
+ Measure(CondVarTestSUK, threads, "Condvar simple test shared UK");
+ Measure(CondVarTestPUU, threads, "Condvar simple test priv UU");
+ Measure(CondVarTestPUK, threads, "Condvar simple test priv UK");
+ Measure(CondVarTest, threads, "Condvar simple test All");
+ Measure(CondVarTimeoutTest, threads, "Condvar timeout test");
+#if 0
+ Measure(CondVarMixedTest, threads, "Condvar mixed timeout test");
+ Measure(CondVarCombinedTest, threads, "Combined condvar test");
+#endif
+ }
+
+ printf("PASS\n");
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/nsprpub/pr/tests/dbmalloc.c b/nsprpub/pr/tests/dbmalloc.c
new file mode 100644
index 0000000000..2a7ad4fa85
--- /dev/null
+++ b/nsprpub/pr/tests/dbmalloc.c
@@ -0,0 +1,339 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: dbmalloc.c
+**
+** Description: Testing malloc (OBSOLETE)
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "nspr.h"
+
+void
+usage
+(
+ void
+)
+{
+ fprintf(stderr, "Usage: dbmalloc ('-m'|'-s') '-f' num_fails ('-d'|'-n') filename [...]\n");
+ exit(0);
+}
+
+typedef struct node_struct
+{
+ struct node_struct *next, *prev;
+ int line;
+ char value[4];
+}
+node_t,
+*node_pt;
+
+node_pt get_node(const char *line)
+{
+ node_pt rv;
+ int l = strlen(line);
+ rv = (node_pt)PR_MALLOC(sizeof(node_t) + l + 1 - 4);
+ if( (node_pt)0 == rv ) {
+ return (node_pt)0;
+ }
+ memcpy(&rv->value[0], line, l+1);
+ rv->next = rv->prev = (node_pt)0;
+ return rv;
+}
+
+void
+dump
+(
+ const char *name,
+ node_pt node,
+ int mf,
+ int debug
+)
+{
+ if( (node_pt)0 != node->prev ) {
+ dump(name, node->prev, mf, debug);
+ }
+ if( 0 != debug ) {
+ printf("[%s]: %6d: %s", name, node->line, node->value);
+ }
+ if( node->line == mf ) {
+ fprintf(stderr, "[%s]: Line %d was allocated!\n", name, node->line);
+ }
+ if( (node_pt)0 != node->next ) {
+ dump(name, node->next, mf, debug);
+ }
+ return;
+}
+
+void
+release
+(
+ node_pt node
+)
+{
+ if( (node_pt)0 != node->prev ) {
+ release(node->prev);
+ }
+ if( (node_pt)0 != node->next ) {
+ release(node->next);
+ }
+ PR_DELETE(node);
+}
+
+int
+t2
+(
+ const char *name,
+ int mf,
+ int debug
+)
+{
+ int rv;
+ FILE *fp;
+ int l = 0;
+ node_pt head = (node_pt)0;
+ char buffer[ BUFSIZ ];
+
+ fp = fopen(name, "r");
+ if( (FILE *)0 == fp )
+ {
+ fprintf(stderr, "[%s]: Cannot open \"%s.\"\n", name, name);
+ return -1;
+ }
+
+ /* fgets mallocs a buffer, first time through. */
+ if( (char *)0 == fgets(buffer, BUFSIZ, fp) )
+ {
+ fprintf(stderr, "[%s]: \"%s\" is empty.\n", name, name);
+ (void)fclose(fp);
+ return -1;
+ }
+
+ rewind(fp);
+
+ if( PR_SUCCESS != PR_ClearMallocCount() )
+ {
+ fprintf(stderr, "[%s]: Cannot clear malloc count.\n", name);
+ (void)fclose(fp);
+ return -1;
+ }
+
+ if( PR_SUCCESS != PR_SetMallocCountdown(mf) )
+ {
+ fprintf(stderr, "[%s]: Cannot set malloc countdown to %d\n", name, mf);
+ (void)fclose(fp);
+ return -1;
+ }
+
+ while( fgets(buffer, BUFSIZ, fp) )
+ {
+ node_pt n;
+ node_pt *w = &head;
+
+ if( (strlen(buffer) == (BUFSIZ-1)) && (buffer[BUFSIZ-2] != '\n') ) {
+ buffer[BUFSIZ-2] == '\n';
+ }
+
+ l++;
+
+ n = get_node(buffer);
+ if( (node_pt)0 == n )
+ {
+ printf("[%s]: Line %d: malloc failure!\n", name, l);
+ continue;
+ }
+
+ n->line = l;
+
+ while( 1 )
+ {
+ int comp;
+
+ if( (node_pt)0 == *w )
+ {
+ *w = n;
+ break;
+ }
+
+ comp = strcmp((*w)->value, n->value);
+ if( comp < 0 ) {
+ w = &(*w)->next;
+ }
+ else {
+ w = &(*w)->prev;
+ }
+ }
+ }
+
+ (void)fclose(fp);
+
+ dump(name, head, mf, debug);
+
+ rv = PR_GetMallocCount();
+ PR_ClearMallocCountdown();
+
+ release(head);
+
+ return rv;
+}
+
+int nf = 0;
+int debug = 0;
+
+void
+test
+(
+ const char *name
+)
+{
+ int n, i;
+
+ extern int nf, debug;
+
+ printf("[%s]: starting test 0\n", name);
+ n = t2(name, 0, debug);
+ if( -1 == n ) {
+ return;
+ }
+ printf("[%s]: test 0 had %ld allocations.\n", name, n);
+
+ if( 0 >= n ) {
+ return;
+ }
+
+ for( i = 0; i < nf; i++ )
+ {
+ int which = rand() % n;
+ if( 0 == which ) {
+ printf("[%s]: starting test %d -- no allocation should fail\n", name, i+1);
+ }
+ else {
+ printf("[%s]: starting test %d -- allocation %d should fail\n", name, i+1, which);
+ }
+ (void)t2(name, which, debug);
+ printf("[%s]: test %d done.\n", name, i+1);
+ }
+
+ return;
+}
+
+int main(int argc, char **argv)
+{
+ int okay = 0;
+ int multithread = 0;
+
+ struct threadlist
+ {
+ struct threadlist *next;
+ PRThread *thread;
+ }
+ *threadhead = (struct threadlist *)0;
+
+ extern int nf, debug;
+
+ srand(time(0));
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ printf("[main]: We %s using the debugging malloc.\n",
+ PR_IsDebuggingMalloc() ? "ARE" : "ARE NOT");
+
+ while( argv++, --argc )
+ {
+ if( '-' == argv[0][0] )
+ {
+ switch( argv[0][1] )
+ {
+ case 'f':
+ nf = atoi(argv[0][2] ? &argv[0][2] :
+ --argc ? *++argv : "0");
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'n':
+ debug = 0;
+ break;
+ case 'm':
+ multithread = 1;
+ break;
+ case 's':
+ multithread = 0;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ else
+ {
+ FILE *fp = fopen(*argv, "r");
+ if( (FILE *)0 == fp )
+ {
+ fprintf(stderr, "Cannot open \"%s.\"\n", *argv);
+ continue;
+ }
+
+ okay++;
+ (void)fclose(fp);
+ if( multithread )
+ {
+ struct threadlist *n;
+
+ n = (struct threadlist *)malloc(sizeof(struct threadlist));
+ if( (struct threadlist *)0 == n )
+ {
+ fprintf(stderr, "This is getting tedious. \"%s\"\n", *argv);
+ continue;
+ }
+
+ n->next = threadhead;
+ n->thread = PR_CreateThread(PR_USER_THREAD, (void (*)(void *))test,
+ *argv, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD,
+ 0);
+ if( (PRThread *)0 == n->thread )
+ {
+ fprintf(stderr, "Can't create thread for \"%s.\"\n", *argv);
+ continue;
+ }
+ else
+ {
+ threadhead = n;
+ }
+ }
+ else
+ {
+ test(*argv);
+ }
+ }
+ }
+
+ if( okay == 0 ) {
+ usage();
+ }
+ else while( (struct threadlist *)0 != threadhead )
+ {
+ struct threadlist *x = threadhead->next;
+ (void)PR_JoinThread(threadhead->thread);
+ PR_DELETE(threadhead);
+ threadhead = x;
+ }
+
+ return 0;
+}
+
diff --git a/nsprpub/pr/tests/dbmalloc1.c b/nsprpub/pr/tests/dbmalloc1.c
new file mode 100644
index 0000000000..a7e995c409
--- /dev/null
+++ b/nsprpub/pr/tests/dbmalloc1.c
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: dbmalloc1.c (OBSOLETE)
+**
+** Description: Tests PR_SetMallocCountdown PR_ClearMallocCountdown functions.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+**
+** 12-June-97 AGarcia Revert to return code 0 and 1, remove debug option (obsolete).
+***********************************************************************/
+
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include "nspr.h"
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+/* variable used for both r1 and r2 tests */
+int should_fail =0;
+int actually_failed=0;
+
+
+void
+r1
+(
+ void
+)
+{
+ int i;
+ actually_failed=0;
+ for( i = 0; i < 5; i++ )
+ {
+ void *x = PR_MALLOC(128);
+ if( (void *)0 == x ) {
+ if (debug_mode) {
+ printf("\tMalloc %d failed.\n", i+1);
+ }
+ actually_failed = 1;
+ }
+ PR_DELETE(x);
+ }
+
+ if (((should_fail != actually_failed) & (!debug_mode))) {
+ failed_already=1;
+ }
+
+
+ return;
+}
+
+void
+r2
+(
+ void
+)
+{
+ int i;
+
+ for( i = 0; i <= 5; i++ )
+ {
+ should_fail =0;
+ if( 0 == i ) {
+ if (debug_mode) {
+ printf("No malloc should fail:\n");
+ }
+ }
+ else {
+ if (debug_mode) {
+ printf("Malloc %d should fail:\n", i);
+ }
+ should_fail = 1;
+ }
+ PR_SetMallocCountdown(i);
+ r1();
+ PR_ClearMallocCountdown();
+ }
+}
+
+int main(int argc, char **argv)
+{
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+ r2();
+
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+
+}
+
diff --git a/nsprpub/pr/tests/dceemu.c b/nsprpub/pr/tests/dceemu.c
new file mode 100644
index 0000000000..2b1d0901a9
--- /dev/null
+++ b/nsprpub/pr/tests/dceemu.c
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: dceemu.c
+** Description: testing the DCE emulation api
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 Revert to return code 0 and 1, remove debug option (obsolete).
+**/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+
+#include "prlog.h"
+#include "prinit.h"
+#include "prpdce.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode=0;
+
+static PRIntn prmain(PRIntn argc, char **argv)
+{
+ PRStatus rv;
+ PRLock *ml = PR_NewLock();
+ PRCondVar *cv = PRP_NewNakedCondVar();
+ PRIntervalTime tenmsecs = PR_MillisecondsToInterval(10);
+
+ rv = PRP_TryLock(ml);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if ((rv != PR_SUCCESS) & (!debug_mode)) {
+ failed_already=1;
+ }
+
+ rv = PRP_TryLock(ml);
+ PR_ASSERT(PR_FAILURE == rv);
+ if ((rv != PR_FAILURE) & (!debug_mode)) {
+ failed_already=1;
+ }
+
+ rv = PRP_NakedNotify(cv);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if ((rv != PR_SUCCESS) & (!debug_mode)) {
+ failed_already=1;
+ }
+
+ rv = PRP_NakedBroadcast(cv);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if ((rv != PR_SUCCESS) & (!debug_mode)) {
+ failed_already=1;
+ }
+
+ rv = PRP_NakedWait(cv, ml, tenmsecs);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if ((rv != PR_SUCCESS) & (!debug_mode)) {
+ failed_already=1;
+ }
+
+ PR_Unlock(ml);
+
+ rv = PRP_NakedNotify(cv);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if ((rv != PR_SUCCESS) & (!debug_mode)) {
+ failed_already=1;
+ }
+
+ rv = PRP_NakedBroadcast(cv);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if ((rv != PR_SUCCESS) & (!debug_mode)) {
+ failed_already=1;
+ }
+
+ PRP_DestroyNakedCondVar(cv);
+ PR_DestroyLock(ml);
+
+ if (debug_mode) {
+ printf("Test succeeded\n");
+ }
+
+ return 0;
+
+} /* prmain */
+
+int main(int argc, char **argv)
+{
+ PR_Initialize(prmain, argc, argv, 0);
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+} /* main */
+
+
+/* decemu.c */
diff --git a/nsprpub/pr/tests/depend.c b/nsprpub/pr/tests/depend.c
new file mode 100644
index 0000000000..f3689bc460
--- /dev/null
+++ b/nsprpub/pr/tests/depend.c
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+** 1996 - Netscape Communications Corporation
+**
+**
+** Name: depend.c
+** Description: Test to enumerate the dependencies
+*
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+#include "prinit.h"
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void PrintVersion(
+ const char *msg, const PRVersion* info, PRIntn tab)
+{
+ static const len = 20;
+ static const char *tabs = {" "};
+
+ tab *= 2;
+ if (tab > len) {
+ tab = len;
+ }
+ printf("%s", &tabs[len - tab]);
+ printf("%s ", msg);
+ printf("%s ", info->id);
+ printf("%d.%d", info->major, info->minor);
+ if (0 != info->patch) {
+ printf(".p%d", info->patch);
+ }
+ printf("\n");
+} /* PrintDependency */
+
+static void ChaseDependents(const PRVersionInfo *info, PRIntn tab)
+{
+ PrintVersion("exports", &info->selfExport, tab);
+ if (NULL != info->importEnumerator)
+ {
+ const PRDependencyInfo *dependent = NULL;
+ while (NULL != (dependent = info->importEnumerator(dependent)))
+ {
+ const PRVersionInfo *import = dependent->exportInfoFn();
+ PrintVersion("imports", &dependent->importNeeded, tab);
+ ChaseDependents(import, tab + 1);
+ }
+ }
+} /* ChaseDependents */
+
+static PRVersionInfo hack_export;
+static PRVersionInfo dummy_export;
+static PRDependencyInfo dummy_imports[2];
+
+static const PRVersionInfo *HackExportInfo(void)
+{
+ hack_export.selfExport.major = 11;
+ hack_export.selfExport.minor = 10;
+ hack_export.selfExport.patch = 200;
+ hack_export.selfExport.id = "Hack";
+ hack_export.importEnumerator = NULL;
+ return &hack_export;
+}
+
+static const PRDependencyInfo *DummyImports(
+ const PRDependencyInfo *previous)
+{
+ if (NULL == previous) {
+ return &dummy_imports[0];
+ }
+ else if (&dummy_imports[0] == previous) {
+ return &dummy_imports[1];
+ }
+ else if (&dummy_imports[1] == previous) {
+ return NULL;
+ }
+} /* DummyImports */
+
+static const PRVersionInfo *DummyLibVersion(void)
+{
+ dummy_export.selfExport.major = 1;
+ dummy_export.selfExport.minor = 0;
+ dummy_export.selfExport.patch = 0;
+ dummy_export.selfExport.id = "Dumbass application";
+ dummy_export.importEnumerator = DummyImports;
+
+ dummy_imports[0].importNeeded.major = 2;
+ dummy_imports[0].importNeeded.minor = 0;
+ dummy_imports[0].importNeeded.patch = 0;
+ dummy_imports[0].importNeeded.id = "Netscape Portable Runtime";
+ dummy_imports[0].exportInfoFn = PR_ExportInfo;
+
+ dummy_imports[1].importNeeded.major = 5;
+ dummy_imports[1].importNeeded.minor = 1;
+ dummy_imports[1].importNeeded.patch = 2;
+ dummy_imports[1].importNeeded.id = "Hack Library";
+ dummy_imports[1].exportInfoFn = HackExportInfo;
+
+ return &dummy_export;
+} /* DummyLibVersion */
+
+int main(int argc, char **argv)
+{
+ PRIntn tab = 0;
+ const PRVersionInfo *info = DummyLibVersion();
+ const char *buildDate = __DATE__, *buildTime = __TIME__;
+
+ printf("Depend.c build time is %s %s\n", buildDate, buildTime);
+
+ if (NULL != info) {
+ ChaseDependents(info, tab);
+ }
+
+ return 0;
+} /* main */
+
+/* depend.c */
diff --git a/nsprpub/pr/tests/dll/Makefile.in b/nsprpub/pr/tests/dll/Makefile.in
new file mode 100644
index 0000000000..8b8c5ac20d
--- /dev/null
+++ b/nsprpub/pr/tests/dll/Makefile.in
@@ -0,0 +1,77 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+CSRCS = mygetval.c mysetval.c
+
+INCLUDES = -I$(dist_includedir)
+
+OBJS = $(OBJDIR)/mygetval.$(OBJ_SUFFIX) \
+ $(OBJDIR)/mysetval.$(OBJ_SUFFIX)
+
+ifeq ($(OS_TARGET), WIN16)
+W16OBJS = $(subst $(space),$(comma)$(space),$(OBJS))
+endif
+
+ifeq ($(OS_ARCH), WINNT)
+ifeq ($(OS_TARGET), WIN16)
+# do nothing
+else
+RES=$(OBJDIR)/my.res
+RESNAME=../../../pr/src/nspr.rc
+endif
+endif
+
+ifeq (,$(filter-out WINNT OS2,$(OS_ARCH)))
+IMPORT_LIBRARY = $(OBJDIR)/my.$(LIB_SUFFIX)
+SHARED_LIBRARY = $(OBJDIR)/my.dll
+ifeq ($(OS_ARCH), OS2)
+MAPFILE = $(OBJDIR)/my.def
+GARBAGE += $(MAPFILE)
+MKSHLIB += $(MAPFILE)
+endif
+TARGETS = $(SHARED_LIBRARY) $(IMPORT_LIBRARY)
+else
+ifdef MKSHLIB
+SHARED_LIBRARY = $(OBJDIR)/libmy.$(DLL_SUFFIX)
+endif
+TARGETS = $(SHARED_LIBRARY)
+endif
+
+#
+# To create a loadable module on Darwin, we must override
+# -dynamiclib with -bundle.
+#
+ifeq ($(OS_ARCH),Darwin)
+DSO_LDOPTS = -bundle
+endif
+
+include $(topsrcdir)/config/rules.mk
+
+ifeq ($(OS_TARGET), WIN16)
+# Note: The Win16 target: my.dll requires these macros
+# to be overridden to build the test .dll
+# default values in win16...mk are for release targets.
+#
+OS_DLL_OPTION = NOCASEEXACT
+OS_LIB_FLAGS = -irn
+endif
+
+ifdef SHARED_LIBRARY
+export:: $(TARGETS)
+
+clean::
+ rm -rf $(TARGETS)
+endif
diff --git a/nsprpub/pr/tests/dll/my.def b/nsprpub/pr/tests/dll/my.def
new file mode 100644
index 0000000000..75b71f5ac1
--- /dev/null
+++ b/nsprpub/pr/tests/dll/my.def
@@ -0,0 +1,26 @@
+;+#
+;+# This Source Code Form is subject to the terms of the Mozilla Public
+;+# License, v. 2.0. If a copy of the MPL was not distributed with this
+;+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;+#
+;+# OK, this file is meant to support SUN, LINUX, AIX, OS/2 and WINDOWS
+;+# 1. For all unix platforms, the string ";-" means "remove this line"
+;+# 2. For all unix platforms, the string " DATA " will be removed from any
+;+# line on which it occurs.
+;+# 3. Lines containing ";+" will have ";+" removed on SUN and LINUX.
+;+# On AIX, lines containing ";+" will be removed.
+;+# 4. For all unix platforms, the string ";;" will thave the ";;" removed.
+;+# 5. For all unix platforms, after the above processing has taken place,
+;+# all characters after the first ";" on the line will be removed.
+;+# And for AIX, the first ";" will also be removed.
+;+# This file is passed directly to windows. Since ';' is a comment, all UNIX
+;+# directives are hidden behind ";", ";+", and ";-"
+;+#
+;+MY_1.0 {
+;+ global:
+LIBRARY my ;-
+EXPORTS ;-
+ My_GetValue;
+ My_SetValue;
+;+ local: *;
+;+};
diff --git a/nsprpub/pr/tests/dll/mygetval.c b/nsprpub/pr/tests/dll/mygetval.c
new file mode 100644
index 0000000000..7fb7002441
--- /dev/null
+++ b/nsprpub/pr/tests/dll/mygetval.c
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#if defined(WIN16)
+#include <windows.h>
+#endif
+#include "prtypes.h"
+
+extern PRIntn my_global;
+
+PR_IMPLEMENT(PRIntn) My_GetValue()
+{
+ return my_global;
+}
+
+#if defined(WIN16)
+int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg,
+ WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ return TRUE;
+}
+#endif /* WIN16 */
+
diff --git a/nsprpub/pr/tests/dll/mysetval.c b/nsprpub/pr/tests/dll/mysetval.c
new file mode 100644
index 0000000000..232428adf8
--- /dev/null
+++ b/nsprpub/pr/tests/dll/mysetval.c
@@ -0,0 +1,13 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prtypes.h"
+
+PRIntn my_global = 0;
+
+PR_IMPLEMENT(void) My_SetValue(PRIntn val)
+{
+ my_global = val;
+}
diff --git a/nsprpub/pr/tests/dlltest.c b/nsprpub/pr/tests/dlltest.c
new file mode 100644
index 0000000000..d687322810
--- /dev/null
+++ b/nsprpub/pr/tests/dlltest.c
@@ -0,0 +1,216 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: dlltest.c
+**
+** Description: test dll functionality.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 Revert to return code 0 and 1.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+#include "prinit.h"
+#include "prlink.h"
+#include "prmem.h"
+#include "prerror.h"
+
+#include "plstr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef PRIntn (PR_CALLBACK *GetFcnType)(void);
+typedef void (PR_CALLBACK *SetFcnType)(PRIntn);
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+int main(int argc, char** argv)
+{
+ PRLibrary *lib, *lib2; /* two handles to the same library */
+ GetFcnType getFcn;
+ SetFcnType setFcn;
+ PRIntn value;
+ PRStatus status;
+ char *libName;
+
+ if (argc >= 2 && PL_strcmp(argv[1], "-d") == 0) {
+ debug_mode = 1;
+ }
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ /*
+ * Test 1: load the library, look up the symbols, call the functions,
+ * and check the results.
+ */
+
+ libName = PR_GetLibraryName("dll", "my");
+ if (debug_mode) {
+ printf("Loading library %s\n", libName);
+ }
+ lib = PR_LoadLibrary(libName);
+ PR_FreeLibraryName(libName);
+ if (lib == NULL) {
+ PRInt32 textLength = PR_GetErrorTextLength();
+ char *text = (char*)PR_MALLOC(textLength + 1);
+ text[0] = '\0';
+ (void)PR_GetErrorText(text);
+ fprintf(
+ stderr, "PR_LoadLibrary failed (%d, %d, %s)\n",
+ PR_GetError(), PR_GetOSError(), text);
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ }
+ getFcn = (GetFcnType) PR_FindSymbol(lib, "My_GetValue");
+ setFcn = (SetFcnType) PR_FindFunctionSymbol(lib, "My_SetValue");
+ (*setFcn)(888);
+ value = (*getFcn)();
+ if (value != 888) {
+ fprintf(stderr, "Test 1 failed: set value to 888, but got %d\n", value);
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ }
+ if (debug_mode) {
+ printf("Test 1 passed\n");
+ }
+
+ /*
+ * Test 2: get a second handle to the same library (this should increment
+ * the reference count), look up the symbols, call the functions, and
+ * check the results.
+ */
+
+ getFcn = (GetFcnType) PR_FindSymbolAndLibrary("My_GetValue", &lib2);
+ if (NULL == getFcn || lib != lib2) {
+ fprintf(stderr, "Test 2 failed: handles for the same library are not "
+ "equal: handle 1: %p, handle 2: %p\n", lib, lib2);
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ }
+ setFcn = (SetFcnType) PR_FindSymbol(lib2, "My_SetValue");
+ value = (*getFcn)();
+ if (value != 888) {
+ fprintf(stderr, "Test 2 failed: value should be 888, but got %d\n",
+ value);
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ }
+ (*setFcn)(777);
+ value = (*getFcn)();
+ if (value != 777) {
+ fprintf(stderr, "Test 2 failed: set value to 777, but got %d\n", value);
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ if (debug_mode) {
+ printf("Test 2 passed\n");
+ }
+
+ /*
+ * Test 3: unload the library. The library should still be accessible
+ * via the second handle. do the same things as above.
+ */
+
+ status = PR_UnloadLibrary(lib);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "Test 3 failed: cannot unload library: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ getFcn = (GetFcnType) PR_FindFunctionSymbol(lib2, "My_GetValue");
+ setFcn = (SetFcnType) PR_FindSymbol(lib2, "My_SetValue");
+ (*setFcn)(666);
+ value = (*getFcn)();
+ if (value != 666) {
+ fprintf(stderr, "Test 3 failed: set value to 666, but got %d\n", value);
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ if (debug_mode) {
+ printf("Test 3 passed\n");
+ }
+
+ /*
+ * Test 4: unload the library, testing the reference count mechanism.
+ */
+
+ status = PR_UnloadLibrary(lib2);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "Test 4 failed: cannot unload library: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ getFcn = (GetFcnType) PR_FindFunctionSymbolAndLibrary("My_GetValue", &lib2);
+ if (NULL != getFcn) {
+ fprintf(stderr, "Test 4 failed: how can we find a symbol "
+ "in an already unloaded library?\n");
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ if (debug_mode) {
+ printf("Test 4 passed\n");
+ }
+
+ /*
+ ** Test 5: LoadStaticLibrary()
+ */
+ {
+ PRStaticLinkTable slt[10];
+ PRLibrary *lib;
+
+ lib = PR_LoadStaticLibrary( "my.dll", slt );
+ if ( lib == NULL )
+ {
+ fprintf(stderr, "Test 5: LoadStatiLibrary() failed\n" );
+ goto exit_now;
+ }
+ if (debug_mode)
+ {
+ printf("Test 5 passed\n");
+ }
+ }
+
+ goto exit_now;
+exit_now:
+ PR_Cleanup();
+
+ if (failed_already) {
+ printf("FAILED\n");
+ return 1;
+ } else {
+ printf("PASSED\n");
+ return 0;
+ }
+}
diff --git a/nsprpub/pr/tests/dtoa.c b/nsprpub/pr/tests/dtoa.c
new file mode 100644
index 0000000000..ef2e4dc4de
--- /dev/null
+++ b/nsprpub/pr/tests/dtoa.c
@@ -0,0 +1,213 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/******************************************************************************
+ *
+ * This file contains a test program for the function conversion functions
+ * for double precision code:
+ * PR_strtod
+ * PR_dtoa
+ * PR_cnvtf
+ *
+ * This file was ns/nspr/tests/dtoa.c, created by rrj on 1996/06/22.
+ *
+ *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include <locale.h>
+#include "prprf.h"
+#include "prdtoa.h"
+
+static int failed_already = 0;
+
+int main(int argc, char **argv)
+{
+ double num;
+ double num1;
+ double zero = 0.0;
+ char cnvt[50];
+ char *thousands;
+
+ num = 1e24;
+ num1 = PR_strtod("1e24",NULL);
+ if(num1 != num) {
+ fprintf(stderr,"Failed to convert numeric value %s\n","1e24");
+ failed_already = 1;
+ }
+
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("1e+24",cnvt) != 0) {
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = 0.001e7;
+ num1 = PR_strtod("0.001e7",NULL);
+ if(num1 != num) {
+ fprintf(stderr,"Failed to convert numeric value %s\n","0.001e7");
+ failed_already = 1;
+ }
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("10000",cnvt) != 0) {
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = 0.0000000000000753;
+ num1 = PR_strtod("0.0000000000000753",NULL);
+ if(num1 != num) {
+ fprintf(stderr,"Failed to convert numeric value %s\n",
+ "0.0000000000000753");
+ failed_already = 1;
+ }
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("7.53e-14",cnvt) != 0) {
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = 1.867e73;
+ num1 = PR_strtod("1.867e73",NULL);
+ if(num1 != num) {
+ fprintf(stderr,"Failed to convert numeric value %s\n","1.867e73");
+ failed_already = 1;
+ }
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("1.867e+73",cnvt) != 0) {
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+
+ num = -1.867e73;
+ num1 = PR_strtod("-1.867e73",NULL);
+ if(num1 != num) {
+ fprintf(stderr,"Failed to convert numeric value %s\n","-1.867e73");
+ failed_already = 1;
+ }
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("-1.867e+73",cnvt) != 0) {
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = -1.867e-73;
+ num1 = PR_strtod("-1.867e-73",NULL);
+ if(num1 != num) {
+ fprintf(stderr,"Failed to convert numeric value %s\n","-1.867e-73");
+ failed_already = 1;
+ }
+
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("-1.867e-73",cnvt) != 0) {
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ /* Testing for infinity */
+ num = 1.0 / zero;
+ num1 = PR_strtod("1.867e765",NULL);
+ if(num1 != num) {
+ fprintf(stderr,"Failed to convert numeric value %s\n","1.867e765");
+ failed_already = 1;
+ }
+
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("Infinity",cnvt) != 0) {
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = -1.0 / zero;
+ num1 = PR_strtod("-1.867e765",NULL);
+ if(num1 != num) {
+ fprintf(stderr,"Failed to convert numeric value %s\n","-1.867e765");
+ failed_already = 1;
+ }
+
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("-Infinity",cnvt) != 0) {
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ /* Testing for NaN. PR_strtod can't parse "NaN" and "Infinity" */
+ num = zero / zero;
+
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("NaN",cnvt) != 0) {
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = - zero / zero;
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("NaN",cnvt) != 0) {
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = 1.0000000001e21;
+ num1 = PR_strtod("1.0000000001e21",NULL);
+ if(num1 != num) {
+ fprintf(stderr,"Failed to convert numeric value %s\n",
+ "1.0000000001e21");
+ failed_already = 1;
+ }
+
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("1.0000000001e+21",cnvt) != 0) {
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = -1.0000000001e-21;
+ num1 = PR_strtod("-1.0000000001e-21",NULL);
+ if(num1 != num) {
+ fprintf(stderr,"Failed to convert numeric value %s\n",
+ "-1.0000000001e-21");
+ failed_already = 1;
+ }
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("-1.0000000001e-21",cnvt) != 0) {
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ /*
+ * Bug 414772: should not exit with "Zero passed to d2b" in debug
+ * build.
+ */
+ num1 = PR_strtod("4e-356",NULL);
+
+ /*
+ * A very long input with ~384K digits.
+ * Bug 516396: Should not crash.
+ * Bug 521306: Should return 0 without converting the input.
+ */
+#define LENGTH (384 * 1024)
+ thousands = (char *)malloc(LENGTH);
+ thousands[0] = '0';
+ thousands[1] = '.';
+ memset(&thousands[2], '1', LENGTH - 3);
+ thousands[LENGTH - 1] = '\0';
+ num = 0;
+ num1 = PR_strtod(thousands,NULL);
+ free(thousands);
+ if(num1 != num) {
+ fprintf(stderr,"Failed to convert numeric value %s\n",
+ "0.1111111111111111...");
+ failed_already = 1;
+ }
+
+ if (failed_already) {
+ printf("FAILED\n");
+ } else {
+ printf("PASSED\n");
+ }
+ return failed_already;
+}
diff --git a/nsprpub/pr/tests/env.c b/nsprpub/pr/tests/env.c
new file mode 100644
index 0000000000..17b0eeb9b8
--- /dev/null
+++ b/nsprpub/pr/tests/env.c
@@ -0,0 +1,370 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: env.c
+** Description: Testing environment variable operations
+**
+*/
+#include "prenv.h"
+#include "prmem.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRIntn debug = 0;
+PRIntn verbose = 0;
+PRIntn secure = 0;
+PRBool failedAlready = PR_FALSE;
+
+#define ENVNAME "NSPR_ENVIRONMENT_TEST_VARIABLE"
+#define ENVVALUE "The expected result"
+#define ENVBUFSIZE 256
+
+char *envBuf; /* buffer pointer. We leak memory here on purpose! */
+
+static char * NewBuffer( size_t size )
+{
+ char *buf = malloc( size );
+ if ( NULL == buf ) {
+ printf("env: NewBuffer() failed\n");
+ exit(1);
+ }
+ return(buf);
+} /* end NewBuffer() */
+
+int main(int argc, char **argv)
+{
+ char *value;
+ PRStatus rc;
+
+ { /* Get command line options */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "vds");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug */
+ debug = 1;
+ break;
+ case 'v': /* verbose */
+ verbose = 1;
+ break;
+ case 's': /* secure / set[ug]id */
+ /*
+ ** To test PR_GetEnvSecure, make this executable (or a
+ ** copy of it) setuid / setgid / otherwise inherently
+ ** privileged (e.g., file capabilities) and run it
+ ** with this flag.
+ */
+ secure = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ } /* end block "Get command line options" */
+
+#if 0
+ {
+ /*
+ ** This uses Windows native environment manipulation
+ ** as an experiment. Note the separation of namespace!
+ */
+ BOOL rv;
+ DWORD size;
+ rv = SetEnvironmentVariable( ENVNAME, ENVVALUE );
+ if ( rv == 0 ) {
+ if (debug) {
+ printf("env: Shit! SetEnvironmentVariable() failed\n");
+ }
+ failedAlready = PR_TRUE;
+ }
+ if (verbose) {
+ printf("env: SetEnvironmentVariable() worked\n");
+ }
+
+ size = GetEnvironmentVariable( ENVNAME, envBuf, ENVBUFSIZE );
+ if ( size == 0 ) {
+ if (debug) {
+ printf("env: Shit! GetEnvironmentVariable() failed. Found: %s\n", envBuf );
+ }
+ failedAlready = PR_TRUE;
+ }
+ if (verbose) {
+ printf("env: GetEnvironmentVariable() worked. Found: %s\n", envBuf);
+ }
+
+ value = PR_GetEnv( ENVNAME );
+ if ( (NULL == value ) || (strcmp( value, ENVVALUE))) {
+ if (debug) {
+ printf( "env: PR_GetEnv() failed retrieving WinNative. Found: %s\n", value);
+ }
+ failedAlready = PR_TRUE;
+ }
+ if (verbose) {
+ printf("env: PR_GetEnv() worked. Found: %s\n", value);
+ }
+ }
+#endif
+
+ /* set an environment variable, read it back */
+ envBuf = NewBuffer( ENVBUFSIZE );
+ sprintf( envBuf, ENVNAME "=" ENVVALUE );
+ rc = PR_SetEnv( envBuf );
+ if ( PR_FAILURE == rc ) {
+ if (debug) {
+ printf( "env: PR_SetEnv() failed setting\n");
+ }
+ failedAlready = PR_TRUE;
+ } else {
+ if (verbose) {
+ printf("env: PR_SetEnv() worked.\n");
+ }
+ }
+
+ value = PR_GetEnv( ENVNAME );
+ if ( (NULL == value ) || (strcmp( value, ENVVALUE))) {
+ if (debug) {
+ printf( "env: PR_GetEnv() Failed after setting\n" );
+ }
+ failedAlready = PR_TRUE;
+ } else {
+ if (verbose) {
+ printf("env: PR_GetEnv() worked after setting it. Found: %s\n", value );
+ }
+ }
+
+ if ( secure ) {
+ /*
+ ** In this case we've been run with elevated privileges, so
+ ** test that PR_GetEnvSecure *doesn't* find that env var.
+ */
+ value = PR_GetEnvSecure( ENVNAME );
+ if ( NULL != value ) {
+ if (debug) {
+ printf( "env: PR_GetEnvSecure() failed; expected NULL, found \"%s\"\n", value );
+ }
+ failedAlready = PR_TRUE;
+ } else {
+ if (verbose) {
+ printf("env: PR_GetEnvSecure() worked\n" );
+ }
+ }
+ } else {
+ /*
+ ** In this case the program is being run normally, so do the
+ ** same check for PR_GetEnvSecure as for PR_GetEnv.
+ */
+ value = PR_GetEnvSecure( ENVNAME );
+ if ( (NULL == value ) || (strcmp( value, ENVVALUE))) {
+ if (debug) {
+ printf( "env: PR_GetEnvSecure() Failed after setting\n" );
+ }
+ failedAlready = PR_TRUE;
+ } else {
+ if (verbose) {
+ printf("env: PR_GetEnvSecure() worked after setting it. Found: %s\n", value );
+ }
+ }
+ }
+
+ /* ---------------------------------------------------------------------- */
+ /* check that PR_DuplicateEnvironment() agrees with PR_GetEnv() */
+ {
+#if defined(XP_UNIX) && (!defined(DARWIN) || defined(HAVE_CRT_EXTERNS_H))
+ static const PRBool expect_failure = PR_FALSE;
+#else
+ static const PRBool expect_failure = PR_TRUE;
+#endif
+ char **i, **dupenv = PR_DuplicateEnvironment();
+
+
+ if ( NULL == dupenv ) {
+ if (expect_failure) {
+ if (verbose) printf("env: PR_DuplicateEnvironment failed, "
+ "as expected on this platform.\n");
+ } else {
+ if (debug) {
+ printf("env: PR_DuplicateEnvironment() failed.\n");
+ }
+ failedAlready = PR_TRUE;
+ }
+ } else {
+ unsigned found = 0;
+
+ if (expect_failure) {
+ if (debug) printf("env: PR_DuplicateEnvironment() succeeded, "
+ "but failure is expected on this platform.\n");
+ failedAlready = PR_TRUE;
+ } else {
+ if (verbose) {
+ printf("env: PR_DuplicateEnvironment() succeeded.\n");
+ }
+ }
+ for (i = dupenv; *i; i++) {
+ char *equals = strchr(*i, '=');
+
+ if ( equals == NULL ) {
+ if (debug) printf("env: PR_DuplicateEnvironment() returned a string"
+ " with no '=': %s\n", *i);
+ failedAlready = PR_TRUE;
+ } else {
+ /* We own this string, so we can temporarily alter it */
+ /* *i is the null-terminated name; equals + 1 is the value */
+ *equals = '\0';
+
+ if ( strcmp(*i, ENVNAME) == 0) {
+ found++;
+ if (verbose) printf("env: PR_DuplicateEnvironment() found " ENVNAME
+ " (%u so far).\n", found);
+ }
+
+ /* Multiple values for the same name can't happen, according to POSIX. */
+ value = PR_GetEnv(*i);
+ if ( value == NULL ) {
+ if (debug) printf("env: PR_DuplicateEnvironment() returned a name"
+ " which PR_GetEnv() failed to find: %s\n", *i);
+ failedAlready = PR_TRUE;
+ } else if ( strcmp(equals + 1, value) != 0) {
+ if (debug) printf("env: PR_DuplicateEnvironment() returned the wrong"
+ " value for %s: expected %s; found %s\n",
+ *i, value, equals + 1);
+ failedAlready = PR_TRUE;
+ } else {
+ if (verbose) printf("env: PR_DuplicateEnvironment() agreed with"
+ " PR_GetEnv() about %s\n", *i);
+ }
+ }
+ PR_Free(*i);
+ }
+ PR_Free(dupenv);
+
+ if (found != 1) {
+ if (debug) printf("env: PR_DuplicateEnvironment() found %u entries for " ENVNAME
+ " (expected 1)\n", found);
+ failedAlready = PR_TRUE;
+ } else {
+ if (verbose) {
+ printf("env: PR_DuplicateEnvironment() found 1 entry for " ENVNAME "\n");
+ }
+ }
+ }
+ }
+
+ /* ---------------------------------------------------------------------- */
+ /* un-set the variable, using RAW name... should not work */
+ envBuf = NewBuffer( ENVBUFSIZE );
+ sprintf( envBuf, ENVNAME );
+ rc = PR_SetEnv( envBuf );
+ if ( PR_FAILURE == rc ) {
+ if (verbose) {
+ printf( "env: PR_SetEnv() not un-set using RAW name. Good!\n");
+ }
+ } else {
+ if (debug) {
+ printf("env: PR_SetEnv() un-set using RAW name. Bad!\n" );
+ }
+ failedAlready = PR_TRUE;
+ }
+
+ value = PR_GetEnv( ENVNAME );
+ if ( NULL == value ) {
+ if (debug) {
+ printf("env: PR_GetEnv() after un-set using RAW name. Bad!\n" );
+ }
+ failedAlready = PR_TRUE;
+ } else {
+ if (verbose) {
+ printf( "env: PR_GetEnv() after RAW un-set found: %s\n", value );
+ }
+ }
+
+ /* ---------------------------------------------------------------------- */
+ /* set it again ... */
+ envBuf = NewBuffer( ENVBUFSIZE );
+ sprintf( envBuf, ENVNAME "=" ENVVALUE );
+ rc = PR_SetEnv( envBuf );
+ if ( PR_FAILURE == rc ) {
+ if (debug) {
+ printf( "env: PR_SetEnv() failed setting the second time.\n");
+ }
+ failedAlready = PR_TRUE;
+ } else {
+ if (verbose) {
+ printf("env: PR_SetEnv() worked.\n");
+ }
+ }
+
+ /* un-set the variable using the form name= */
+ envBuf = NewBuffer( ENVBUFSIZE );
+ sprintf( envBuf, ENVNAME "=" );
+ rc = PR_SetEnv( envBuf );
+ if ( PR_FAILURE == rc ) {
+ if (debug) {
+ printf( "env: PR_SetEnv() failed un-setting using name=\n");
+ }
+ failedAlready = PR_TRUE;
+ } else {
+ if (verbose) {
+ printf("env: PR_SetEnv() un-set using name= worked\n" );
+ }
+ }
+
+ value = PR_GetEnv( ENVNAME );
+ if (( NULL == value ) || ( 0x00 == *value )) {
+ if (verbose) {
+ printf("env: PR_GetEnv() after un-set using name= worked\n" );
+ }
+ } else {
+ if (debug) {
+ printf( "env: PR_GetEnv() after un-set using name=. Found: %s\n", value );
+ }
+ failedAlready = PR_TRUE;
+ }
+ /* ---------------------------------------------------------------------- */
+ /* un-set the variable using the form name= */
+ envBuf = NewBuffer( ENVBUFSIZE );
+ sprintf( envBuf, ENVNAME "999=" );
+ rc = PR_SetEnv( envBuf );
+ if ( PR_FAILURE == rc ) {
+ if (debug) {
+ printf( "env: PR_SetEnv() failed un-setting using name=\n");
+ }
+ failedAlready = PR_TRUE;
+ } else {
+ if (verbose) {
+ printf("env: PR_SetEnv() un-set using name= worked\n" );
+ }
+ }
+
+ value = PR_GetEnv( ENVNAME "999" );
+ if (( NULL == value ) || ( 0x00 == *value )) {
+ if (verbose) {
+ printf("env: PR_GetEnv() after un-set using name= worked\n" );
+ }
+ } else {
+ if (debug) {
+ printf( "env: PR_GetEnv() after un-set using name=. Found: %s\n", value );
+ }
+ failedAlready = PR_TRUE;
+ }
+
+ /* ---------------------------------------------------------------------- */
+ if (debug || verbose) {
+ printf("\n%s\n", (failedAlready)? "FAILED" : "PASSED" );
+ }
+ return( (failedAlready)? 1 : 0 );
+} /* main() */
+
+/* env.c */
diff --git a/nsprpub/pr/tests/errcodes.c b/nsprpub/pr/tests/errcodes.c
new file mode 100644
index 0000000000..591b630b23
--- /dev/null
+++ b/nsprpub/pr/tests/errcodes.c
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: errcodes.c
+**
+** Description: print nspr error codes
+**
+*/
+#include "prerror.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+
+static int _debug_on = 0;
+
+struct errinfo {
+ PRErrorCode errcode;
+ char *errname;
+};
+
+struct errinfo errcodes[] = {
+ {PR_OUT_OF_MEMORY_ERROR, "PR_OUT_OF_MEMORY_ERROR"},
+ {PR_BAD_DESCRIPTOR_ERROR, "PR_BAD_DESCRIPTOR_ERROR"},
+ {PR_WOULD_BLOCK_ERROR, "PR_WOULD_BLOCK_ERROR"},
+ {PR_ACCESS_FAULT_ERROR, "PR_ACCESS_FAULT_ERROR"},
+ {PR_INVALID_METHOD_ERROR, "PR_INVALID_METHOD_ERROR"},
+ {PR_ILLEGAL_ACCESS_ERROR, "PR_ILLEGAL_ACCESS_ERROR"},
+ {PR_UNKNOWN_ERROR, "PR_UNKNOWN_ERROR"},
+ {PR_PENDING_INTERRUPT_ERROR, "PR_PENDING_INTERRUPT_ERROR"},
+ {PR_NOT_IMPLEMENTED_ERROR, "PR_NOT_IMPLEMENTED_ERROR"},
+ {PR_IO_ERROR, "PR_IO_ERROR"},
+ {PR_IO_TIMEOUT_ERROR, "PR_IO_TIMEOUT_ERROR"},
+ {PR_IO_PENDING_ERROR, "PR_IO_PENDING_ERROR"},
+ {PR_DIRECTORY_OPEN_ERROR, "PR_DIRECTORY_OPEN_ERROR"},
+ {PR_INVALID_ARGUMENT_ERROR, "PR_INVALID_ARGUMENT_ERROR"},
+ {PR_ADDRESS_NOT_AVAILABLE_ERROR, "PR_ADDRESS_NOT_AVAILABLE_ERROR"},
+ {PR_ADDRESS_NOT_SUPPORTED_ERROR, "PR_ADDRESS_NOT_SUPPORTED_ERROR"},
+ {PR_IS_CONNECTED_ERROR, "PR_IS_CONNECTED_ERROR"},
+ {PR_BAD_ADDRESS_ERROR, "PR_BAD_ADDRESS_ERROR"},
+ {PR_ADDRESS_IN_USE_ERROR, "PR_ADDRESS_IN_USE_ERROR"},
+ {PR_CONNECT_REFUSED_ERROR, "PR_CONNECT_REFUSED_ERROR"},
+ {PR_NETWORK_UNREACHABLE_ERROR, "PR_NETWORK_UNREACHABLE_ERROR"},
+ {PR_CONNECT_TIMEOUT_ERROR, "PR_CONNECT_TIMEOUT_ERROR"},
+ {PR_NOT_CONNECTED_ERROR, "PR_NOT_CONNECTED_ERROR"},
+ {PR_LOAD_LIBRARY_ERROR, "PR_LOAD_LIBRARY_ERROR"},
+ {PR_UNLOAD_LIBRARY_ERROR, "PR_UNLOAD_LIBRARY_ERROR"},
+ {PR_FIND_SYMBOL_ERROR, "PR_FIND_SYMBOL_ERROR"},
+ {PR_INSUFFICIENT_RESOURCES_ERROR, "PR_INSUFFICIENT_RESOURCES_ERROR"},
+ {PR_DIRECTORY_LOOKUP_ERROR, "PR_DIRECTORY_LOOKUP_ERROR"},
+ {PR_TPD_RANGE_ERROR, "PR_TPD_RANGE_ERROR"},
+ {PR_PROC_DESC_TABLE_FULL_ERROR, "PR_PROC_DESC_TABLE_FULL_ERROR"},
+ {PR_SYS_DESC_TABLE_FULL_ERROR, "PR_SYS_DESC_TABLE_FULL_ERROR"},
+ {PR_NOT_SOCKET_ERROR, "PR_NOT_SOCKET_ERROR"},
+ {PR_NOT_TCP_SOCKET_ERROR, "PR_NOT_TCP_SOCKET_ERROR"},
+ {PR_SOCKET_ADDRESS_IS_BOUND_ERROR, "PR_SOCKET_ADDRESS_IS_BOUND_ERROR"},
+ {PR_NO_ACCESS_RIGHTS_ERROR, "PR_NO_ACCESS_RIGHTS_ERROR"},
+ {PR_OPERATION_NOT_SUPPORTED_ERROR, "PR_OPERATION_NOT_SUPPORTED_ERROR"},
+ {PR_PROTOCOL_NOT_SUPPORTED_ERROR, "PR_PROTOCOL_NOT_SUPPORTED_ERROR"},
+ {PR_REMOTE_FILE_ERROR, "PR_REMOTE_FILE_ERROR"},
+ {PR_BUFFER_OVERFLOW_ERROR, "PR_BUFFER_OVERFLOW_ERROR"},
+ {PR_CONNECT_RESET_ERROR, "PR_CONNECT_RESET_ERROR"},
+ {PR_RANGE_ERROR, "PR_RANGE_ERROR"},
+ {PR_DEADLOCK_ERROR, "PR_DEADLOCK_ERROR"},
+ {PR_FILE_IS_LOCKED_ERROR, "PR_FILE_IS_LOCKED_ERROR"},
+ {PR_FILE_TOO_BIG_ERROR, "PR_FILE_TOO_BIG_ERROR"},
+ {PR_NO_DEVICE_SPACE_ERROR, "PR_NO_DEVICE_SPACE_ERROR"},
+ {PR_PIPE_ERROR, "PR_PIPE_ERROR"},
+ {PR_NO_SEEK_DEVICE_ERROR, "PR_NO_SEEK_DEVICE_ERROR"},
+ {PR_IS_DIRECTORY_ERROR, "PR_IS_DIRECTORY_ERROR"},
+ {PR_LOOP_ERROR, "PR_LOOP_ERROR"},
+ {PR_NAME_TOO_LONG_ERROR, "PR_NAME_TOO_LONG_ERROR"},
+ {PR_FILE_NOT_FOUND_ERROR, "PR_FILE_NOT_FOUND_ERROR"},
+ {PR_NOT_DIRECTORY_ERROR, "PR_NOT_DIRECTORY_ERROR"},
+ {PR_READ_ONLY_FILESYSTEM_ERROR, "PR_READ_ONLY_FILESYSTEM_ERROR"},
+ {PR_DIRECTORY_NOT_EMPTY_ERROR, "PR_DIRECTORY_NOT_EMPTY_ERROR"},
+ {PR_FILESYSTEM_MOUNTED_ERROR, "PR_FILESYSTEM_MOUNTED_ERROR"},
+ {PR_NOT_SAME_DEVICE_ERROR, "PR_NOT_SAME_DEVICE_ERROR"},
+ {PR_DIRECTORY_CORRUPTED_ERROR, "PR_DIRECTORY_CORRUPTED_ERROR"},
+ {PR_FILE_EXISTS_ERROR, "PR_FILE_EXISTS_ERROR"},
+ {PR_MAX_DIRECTORY_ENTRIES_ERROR, "PR_MAX_DIRECTORY_ENTRIES_ERROR"},
+ {PR_INVALID_DEVICE_STATE_ERROR, "PR_INVALID_DEVICE_STATE_ERROR"},
+ {PR_DEVICE_IS_LOCKED_ERROR, "PR_DEVICE_IS_LOCKED_ERROR"},
+ {PR_NO_MORE_FILES_ERROR, "PR_NO_MORE_FILES_ERROR"},
+ {PR_END_OF_FILE_ERROR, "PR_END_OF_FILE_ERROR"},
+ {PR_FILE_SEEK_ERROR, "PR_FILE_SEEK_ERROR"},
+ {PR_FILE_IS_BUSY_ERROR, "PR_FILE_IS_BUSY_ERROR"},
+ {PR_IN_PROGRESS_ERROR, "PR_IN_PROGRESS_ERROR"},
+ {PR_ALREADY_INITIATED_ERROR, "PR_ALREADY_INITIATED_ERROR"},
+ {PR_GROUP_EMPTY_ERROR, "PR_GROUP_EMPTY_ERROR"},
+ {PR_INVALID_STATE_ERROR, "PR_INVALID_STATE_ERROR"},
+ {PR_NETWORK_DOWN_ERROR, "PR_NETWORK_DOWN_ERROR"},
+ {PR_SOCKET_SHUTDOWN_ERROR, "PR_SOCKET_SHUTDOWN_ERROR"},
+ {PR_CONNECT_ABORTED_ERROR, "PR_CONNECT_ABORTED_ERROR"},
+ {PR_HOST_UNREACHABLE_ERROR, "PR_HOST_UNREACHABLE_ERROR"}
+};
+
+int main(int argc, char **argv)
+{
+
+ int count, errnum;
+
+ /*
+ * -d debug mode
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ count = sizeof(errcodes)/sizeof(errcodes[0]);
+ printf("\nNumber of error codes = %d\n\n",count);
+ for (errnum = 0; errnum < count; errnum++) {
+ printf("%-40s = %d\n",errcodes[errnum].errname,
+ errcodes[errnum].errcode);
+ }
+
+ return 0;
+}
diff --git a/nsprpub/pr/tests/errset.c b/nsprpub/pr/tests/errset.c
new file mode 100644
index 0000000000..fb76a6fa9d
--- /dev/null
+++ b/nsprpub/pr/tests/errset.c
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: errset.c
+**
+** Description: errset.c exercises the functions in prerror.c.
+** This code is a unit test of the prerror.c capability.
+**
+** Note: There's some fluff in here. The guts of the test
+** were plagerized from another test. So, sue me.
+**
+**
+*/
+#include "prerror.h"
+#include "plgetopt.h"
+#include "prlog.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static int _debug_on = 0;
+
+struct errinfo {
+ PRErrorCode errcode;
+ char *errname;
+};
+
+struct errinfo errcodes[] = {
+ {PR_OUT_OF_MEMORY_ERROR, "PR_OUT_OF_MEMORY_ERROR"},
+ {PR_UNKNOWN_ERROR, "An intentionally long error message text intended to force a delete of the current errorString buffer and get another one."},
+ {PR_BAD_DESCRIPTOR_ERROR, "PR_BAD_DESCRIPTOR_ERROR"},
+ {PR_WOULD_BLOCK_ERROR, "PR_WOULD_BLOCK_ERROR"},
+ {PR_ACCESS_FAULT_ERROR, "PR_ACCESS_FAULT_ERROR"},
+ {PR_INVALID_METHOD_ERROR, "PR_INVALID_METHOD_ERROR"},
+ {PR_ILLEGAL_ACCESS_ERROR, "PR_ILLEGAL_ACCESS_ERROR"},
+ {PR_UNKNOWN_ERROR, "PR_UNKNOWN_ERROR"},
+ {PR_PENDING_INTERRUPT_ERROR, "PR_PENDING_INTERRUPT_ERROR"},
+ {PR_NOT_IMPLEMENTED_ERROR, "PR_NOT_IMPLEMENTED_ERROR"},
+ {PR_IO_ERROR, "PR_IO_ERROR"},
+ {PR_IO_TIMEOUT_ERROR, "PR_IO_TIMEOUT_ERROR"},
+ {PR_IO_PENDING_ERROR, "PR_IO_PENDING_ERROR"},
+ {PR_DIRECTORY_OPEN_ERROR, "PR_DIRECTORY_OPEN_ERROR"},
+ {PR_INVALID_ARGUMENT_ERROR, "PR_INVALID_ARGUMENT_ERROR"},
+ {PR_ADDRESS_NOT_AVAILABLE_ERROR, "PR_ADDRESS_NOT_AVAILABLE_ERROR"},
+ {PR_ADDRESS_NOT_SUPPORTED_ERROR, "PR_ADDRESS_NOT_SUPPORTED_ERROR"},
+ {PR_IS_CONNECTED_ERROR, "PR_IS_CONNECTED_ERROR"},
+ {PR_BAD_ADDRESS_ERROR, "PR_BAD_ADDRESS_ERROR"},
+ {PR_ADDRESS_IN_USE_ERROR, "PR_ADDRESS_IN_USE_ERROR"},
+ {PR_CONNECT_REFUSED_ERROR, "PR_CONNECT_REFUSED_ERROR"},
+ {PR_NETWORK_UNREACHABLE_ERROR, "PR_NETWORK_UNREACHABLE_ERROR"},
+ {PR_CONNECT_TIMEOUT_ERROR, "PR_CONNECT_TIMEOUT_ERROR"},
+ {PR_NOT_CONNECTED_ERROR, "PR_NOT_CONNECTED_ERROR"},
+ {PR_LOAD_LIBRARY_ERROR, "PR_LOAD_LIBRARY_ERROR"},
+ {PR_UNLOAD_LIBRARY_ERROR, "PR_UNLOAD_LIBRARY_ERROR"},
+ {PR_FIND_SYMBOL_ERROR, "PR_FIND_SYMBOL_ERROR"},
+ {PR_INSUFFICIENT_RESOURCES_ERROR, "PR_INSUFFICIENT_RESOURCES_ERROR"},
+ {PR_DIRECTORY_LOOKUP_ERROR, "PR_DIRECTORY_LOOKUP_ERROR"},
+ {PR_TPD_RANGE_ERROR, "PR_TPD_RANGE_ERROR"},
+ {PR_PROC_DESC_TABLE_FULL_ERROR, "PR_PROC_DESC_TABLE_FULL_ERROR"},
+ {PR_SYS_DESC_TABLE_FULL_ERROR, "PR_SYS_DESC_TABLE_FULL_ERROR"},
+ {PR_NOT_SOCKET_ERROR, "PR_NOT_SOCKET_ERROR"},
+ {PR_NOT_TCP_SOCKET_ERROR, "PR_NOT_TCP_SOCKET_ERROR"},
+ {PR_SOCKET_ADDRESS_IS_BOUND_ERROR, "PR_SOCKET_ADDRESS_IS_BOUND_ERROR"},
+ {PR_NO_ACCESS_RIGHTS_ERROR, "PR_NO_ACCESS_RIGHTS_ERROR"},
+ {PR_OPERATION_NOT_SUPPORTED_ERROR, "PR_OPERATION_NOT_SUPPORTED_ERROR"},
+ {PR_PROTOCOL_NOT_SUPPORTED_ERROR, "PR_PROTOCOL_NOT_SUPPORTED_ERROR"},
+ {PR_REMOTE_FILE_ERROR, "PR_REMOTE_FILE_ERROR"},
+ {PR_BUFFER_OVERFLOW_ERROR, "PR_BUFFER_OVERFLOW_ERROR"},
+ {PR_CONNECT_RESET_ERROR, "PR_CONNECT_RESET_ERROR"},
+ {PR_RANGE_ERROR, "PR_RANGE_ERROR"},
+ {PR_DEADLOCK_ERROR, "PR_DEADLOCK_ERROR"},
+ {PR_FILE_IS_LOCKED_ERROR, "PR_FILE_IS_LOCKED_ERROR"},
+ {PR_FILE_TOO_BIG_ERROR, "PR_FILE_TOO_BIG_ERROR"},
+ {PR_NO_DEVICE_SPACE_ERROR, "PR_NO_DEVICE_SPACE_ERROR"},
+ {PR_PIPE_ERROR, "PR_PIPE_ERROR"},
+ {PR_NO_SEEK_DEVICE_ERROR, "PR_NO_SEEK_DEVICE_ERROR"},
+ {PR_IS_DIRECTORY_ERROR, "PR_IS_DIRECTORY_ERROR"},
+ {PR_LOOP_ERROR, "PR_LOOP_ERROR"},
+ {PR_NAME_TOO_LONG_ERROR, "PR_NAME_TOO_LONG_ERROR"},
+ {PR_FILE_NOT_FOUND_ERROR, "PR_FILE_NOT_FOUND_ERROR"},
+ {PR_NOT_DIRECTORY_ERROR, "PR_NOT_DIRECTORY_ERROR"},
+ {PR_READ_ONLY_FILESYSTEM_ERROR, "PR_READ_ONLY_FILESYSTEM_ERROR"},
+ {PR_DIRECTORY_NOT_EMPTY_ERROR, "PR_DIRECTORY_NOT_EMPTY_ERROR"},
+ {PR_FILESYSTEM_MOUNTED_ERROR, "PR_FILESYSTEM_MOUNTED_ERROR"},
+ {PR_NOT_SAME_DEVICE_ERROR, "PR_NOT_SAME_DEVICE_ERROR"},
+ {PR_DIRECTORY_CORRUPTED_ERROR, "PR_DIRECTORY_CORRUPTED_ERROR"},
+ {PR_FILE_EXISTS_ERROR, "PR_FILE_EXISTS_ERROR"},
+ {PR_MAX_DIRECTORY_ENTRIES_ERROR, "PR_MAX_DIRECTORY_ENTRIES_ERROR"},
+ {PR_INVALID_DEVICE_STATE_ERROR, "PR_INVALID_DEVICE_STATE_ERROR"},
+ {PR_DEVICE_IS_LOCKED_ERROR, "PR_DEVICE_IS_LOCKED_ERROR"},
+ {PR_NO_MORE_FILES_ERROR, "PR_NO_MORE_FILES_ERROR"},
+ {PR_END_OF_FILE_ERROR, "PR_END_OF_FILE_ERROR"},
+ {PR_FILE_SEEK_ERROR, "PR_FILE_SEEK_ERROR"},
+ {PR_FILE_IS_BUSY_ERROR, "PR_FILE_IS_BUSY_ERROR"},
+ {PR_IN_PROGRESS_ERROR, "PR_IN_PROGRESS_ERROR"},
+ {PR_ALREADY_INITIATED_ERROR, "PR_ALREADY_INITIATED_ERROR"},
+ {PR_GROUP_EMPTY_ERROR, "PR_GROUP_EMPTY_ERROR"},
+ {PR_INVALID_STATE_ERROR, "PR_INVALID_STATE_ERROR"},
+ {PR_NETWORK_DOWN_ERROR, "PR_NETWORK_DOWN_ERROR"},
+ {PR_SOCKET_SHUTDOWN_ERROR, "PR_SOCKET_SHUTDOWN_ERROR"},
+ {PR_CONNECT_ABORTED_ERROR, "PR_CONNECT_ABORTED_ERROR"},
+ {PR_HOST_UNREACHABLE_ERROR, "PR_HOST_UNREACHABLE_ERROR"}
+};
+
+int main(int argc, char **argv)
+{
+
+ int count, errnum;
+
+ /*
+ * -d debug mode
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ count = sizeof(errcodes)/sizeof(errcodes[0]);
+ printf("\nNumber of error codes = %d\n\n",count);
+ for (errnum = 0; errnum < count; errnum++) {
+ PRInt32 len1, len2, err;
+ char msg[256];
+
+ PR_SetError( errnum, -5 );
+ err = PR_GetError();
+ PR_ASSERT( err == errnum );
+ err = PR_GetOSError();
+ PR_ASSERT( err == -5 );
+ PR_SetErrorText( strlen(errcodes[errnum].errname), errcodes[errnum].errname );
+ len1 = PR_GetErrorTextLength();
+ len2 = PR_GetErrorText( msg );
+ PR_ASSERT( len1 == len2 );
+ printf("%5.5d -- %s\n", errnum, msg );
+ }
+
+ return 0;
+}
diff --git a/nsprpub/pr/tests/exit.c b/nsprpub/pr/tests/exit.c
new file mode 100644
index 0000000000..dd3d44427e
--- /dev/null
+++ b/nsprpub/pr/tests/exit.c
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prio.h"
+#include "prprf.h"
+#include "prinit.h"
+#include "prthread.h"
+#include "prproces.h"
+#include "prinrval.h"
+
+#include "plgetopt.h"
+
+#include <stdlib.h>
+
+static PRInt32 dally = 0;
+static PRFileDesc *err = NULL;
+static PRBool verbose = PR_FALSE, force = PR_FALSE;
+
+static void Help(void)
+{
+ PR_fprintf(err, "Usage: [-t s] [-h]\n");
+ PR_fprintf(err, "\t-d Verbose output (default: FALSE)\n");
+ PR_fprintf(err, "\t-x Forced termination (default: FALSE)\n");
+ PR_fprintf(err, "\t-t Time for thread to block (default: 10 seconds)\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+static void Dull(void *arg)
+{
+ PR_Sleep(PR_SecondsToInterval(dally));
+ if (verbose && force) {
+ PR_fprintf(err, "If you see this, the test failed\n");
+ }
+} /* Dull */
+
+static PRIntn PR_CALLBACK RealMain(PRIntn argc, char **argv)
+{
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "ht:dx");
+
+ err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* verbosity */
+ verbose = PR_TRUE;
+ break;
+ case 'x': /* force exit */
+ force = PR_TRUE;
+ break;
+ case 't': /* seconds to dally in child */
+ dally = atoi(opt->value);
+ break;
+ case 'h': /* user wants some guidance */
+ default:
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == dally) {
+ dally = 10;
+ }
+
+ /*
+ * Create LOCAL and GLOBAL threads
+ */
+ (void)PR_CreateThread(
+ PR_USER_THREAD, Dull, NULL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+
+ (void)PR_CreateThread(
+ PR_USER_THREAD, Dull, NULL, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+
+ if (verbose)
+ PR_fprintf(
+ err, "Main is exiting now. Program should exit %s.\n",
+ (force) ? "immediately" : "after child dally time");
+
+ if (force)
+ {
+ PR_ProcessExit(0);
+ if (verbose)
+ {
+ PR_fprintf(err, "You should not have gotten here.\n");
+ return 1;
+ }
+ }
+ return 0;
+
+}
+
+
+int main(int argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/nsprpub/pr/tests/fdcach.c b/nsprpub/pr/tests/fdcach.c
new file mode 100644
index 0000000000..5fa51b706a
--- /dev/null
+++ b/nsprpub/pr/tests/fdcach.c
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: fdcach.c
+ * Description:
+ * This test verifies that the fd cache is working
+ * correctly.
+ */
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+ * Define ORDER_PRESERVED if the implementation of PR_SetFDCacheSize
+ * preserves the ordering of the fd's when moving them between the
+ * cache.
+ */
+#define ORDER_PRESERVED 1
+
+/*
+ * NUM_FDS must be <= FD_CACHE_SIZE.
+ */
+#define FD_CACHE_SIZE 1024
+#define NUM_FDS 20
+
+int main(int argc, char **argv)
+{
+ int i;
+ PRFileDesc *fds[NUM_FDS];
+ PRFileDesc *savefds[NUM_FDS];
+ int numfds = sizeof(fds)/sizeof(fds[0]);
+
+ PR_SetFDCacheSize(0, FD_CACHE_SIZE);
+
+ /* Add some fd's to the fd cache. */
+ for (i = 0; i < numfds; i++) {
+ savefds[i] = PR_NewTCPSocket();
+ if (NULL == savefds[i]) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ exit(1);
+ }
+ }
+ for (i = 0; i < numfds; i++) {
+ if (PR_Close(savefds[i]) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ }
+
+ /*
+ * Create some fd's. These fd's should come from
+ * the fd cache. Verify the FIFO ordering of the fd
+ * cache.
+ */
+ for (i = 0; i < numfds; i++) {
+ fds[i] = PR_NewTCPSocket();
+ if (NULL == fds[i]) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ exit(1);
+ }
+ if (fds[i] != savefds[i]) {
+ fprintf(stderr, "fd cache malfunctioned\n");
+ exit(1);
+ }
+ }
+ /* Put the fd's back to the fd cache. */
+ for (i = 0; i < numfds; i++) {
+ if (PR_Close(savefds[i]) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ }
+
+ /* Switch to the fd cache. */
+ PR_SetFDCacheSize(0, FD_CACHE_SIZE);
+
+ for (i = 0; i < numfds; i++) {
+ fds[i] = PR_NewTCPSocket();
+ if (NULL == fds[i]) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ exit(1);
+ }
+#ifdef ORDER_PRESERVED
+ if (fds[i] != savefds[i]) {
+ fprintf(stderr, "fd cache malfunctioned\n");
+ exit(1);
+ }
+#else
+ savefds[i] = fds[i];
+#endif
+ }
+ for (i = 0; i < numfds; i++) {
+ if (PR_Close(savefds[i]) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ }
+
+ for (i = 0; i < numfds; i++) {
+ fds[i] = PR_NewTCPSocket();
+ if (NULL == fds[i]) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ exit(1);
+ }
+ if (fds[i] != savefds[i]) {
+ fprintf(stderr, "fd cache malfunctioned\n");
+ exit(1);
+ }
+ }
+ for (i = 0; i < numfds; i++) {
+ if (PR_Close(savefds[i]) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ }
+
+ PR_Cleanup();
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/fileio.c b/nsprpub/pr/tests/fileio.c
new file mode 100644
index 0000000000..a0be3bb10a
--- /dev/null
+++ b/nsprpub/pr/tests/fileio.c
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: fileio.c
+**
+** Description: Program to copy one file to another.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 Revert to return code 0 and 1, remove debug option (obsolete).
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+#include "prinit.h"
+#include "prthread.h"
+#include "prlock.h"
+#include "prcvar.h"
+#include "prmon.h"
+#include "prmem.h"
+#include "prio.h"
+#include "prlog.h"
+
+#include <stdio.h>
+
+#include "obsolete/prsem.h"
+
+
+#define TBSIZE 1024
+
+static PRUint8 tbuf[TBSIZE];
+
+static PRFileDesc *t1, *t2;
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+static void InitialSetup(void)
+{
+ PRUintn i;
+ PRInt32 nWritten, rv;
+
+ t1 = PR_Open("t1.tmp", PR_CREATE_FILE | PR_RDWR, 0);
+ PR_ASSERT(t1 != NULL);
+
+ for (i=0; i<TBSIZE; i++) {
+ tbuf[i] = i;
+ }
+
+ nWritten = PR_Write((PRFileDesc*)t1, tbuf, TBSIZE);
+ PR_ASSERT(nWritten == TBSIZE);
+
+ rv = PR_Seek(t1,0,PR_SEEK_SET);
+ PR_ASSERT(rv == 0);
+
+ t2 = PR_Open("t2.tmp", PR_CREATE_FILE | PR_RDWR, 0);
+ PR_ASSERT(t2 != NULL);
+}
+
+
+static void VerifyAndCleanup(void)
+{
+ PRUintn i;
+ PRInt32 nRead, rv;
+
+ for (i=0; i<TBSIZE; i++) {
+ tbuf[i] = 0;
+ }
+
+ rv = PR_Seek(t2,0,PR_SEEK_SET);
+ PR_ASSERT(rv == 0);
+
+ nRead = PR_Read((PRFileDesc*)t2, tbuf, TBSIZE);
+ PR_ASSERT(nRead == TBSIZE);
+
+ for (i=0; i<TBSIZE; i++)
+ if (tbuf[i] != (PRUint8)i) {
+ if (debug_mode) {
+ printf("data mismatch for index= %d \n", i);
+ }
+ else {
+ failed_already=1;
+ }
+ }
+ PR_Close(t1);
+ PR_Close(t2);
+
+ PR_Delete("t1.tmp");
+ PR_Delete("t2.tmp");
+
+ if (debug_mode) {
+ printf("fileio test passed\n");
+ }
+}
+
+
+/*------------------ Following is the real test program ---------*/
+/*
+ Program to copy one file to another. Two temporary files get
+ created. First one gets written in one write call. Then,
+ a reader thread reads from this file into a double buffer.
+ The writer thread writes from double buffer into the other
+ temporary file. The second temporary file gets verified
+ for accurate data.
+*/
+
+PRSemaphore *emptyBufs; /* number of empty buffers */
+PRSemaphore *fullBufs; /* number of buffers that are full */
+
+#define BSIZE 100
+
+struct {
+ char data[BSIZE];
+ PRUintn nbytes; /* number of bytes in this buffer */
+} buf[2];
+
+static void PR_CALLBACK reader(void *arg)
+{
+ PRUintn i = 0;
+ PRInt32 nbytes;
+
+ do {
+ (void) PR_WaitSem(emptyBufs);
+ nbytes = PR_Read((PRFileDesc*)arg, buf[i].data, BSIZE);
+ if (nbytes >= 0) {
+ buf[i].nbytes = nbytes;
+ PR_PostSem(fullBufs);
+ i = (i + 1) % 2;
+ }
+ } while (nbytes > 0);
+}
+
+static void PR_CALLBACK writer(void *arg)
+{
+ PRUintn i = 0;
+ PRInt32 nbytes;
+
+ do {
+ (void) PR_WaitSem(fullBufs);
+ nbytes = buf[i].nbytes;
+ if (nbytes > 0) {
+ nbytes = PR_Write((PRFileDesc*)arg, buf[i].data, nbytes);
+ PR_PostSem(emptyBufs);
+ i = (i + 1) % 2;
+ }
+ } while (nbytes > 0);
+}
+
+int main(int argc, char **argv)
+{
+ PRThread *r, *w;
+
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ emptyBufs = PR_NewSem(2); /* two empty buffers */
+
+ fullBufs = PR_NewSem(0); /* zero full buffers */
+
+ /* Create initial temp file setup */
+ InitialSetup();
+
+ /* create the reader thread */
+
+ r = PR_CreateThread(PR_USER_THREAD,
+ reader, t1,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ w = PR_CreateThread(PR_USER_THREAD,
+ writer, t2,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ /* Do the joining for both threads */
+ (void) PR_JoinThread(r);
+ (void) PR_JoinThread(w);
+
+ /* Do the verification and clean up */
+ VerifyAndCleanup();
+
+ PR_DestroySem(emptyBufs);
+ PR_DestroySem(fullBufs);
+
+ PR_Cleanup();
+
+ if(failed_already)
+ {
+ printf("Fail\n");
+ return 1;
+ }
+ else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+
+
+}
diff --git a/nsprpub/pr/tests/foreign.c b/nsprpub/pr/tests/foreign.c
new file mode 100644
index 0000000000..a1f5b5cbcc
--- /dev/null
+++ b/nsprpub/pr/tests/foreign.c
@@ -0,0 +1,321 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: foreign.c
+** Description: Testing various functions w/ foreign threads
+**
+** We create a thread and get it to call exactly one runtime function.
+** The thread is allowed to be created by some other environment that
+** NSPR, but it does not announce itself to the runtime prior to calling
+** in.
+**
+** The goal: try to survive.
+**
+*/
+
+#include "prcvar.h"
+#include "prenv.h"
+#include "prerror.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prio.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prmem.h"
+#include "prthread.h"
+#include "prtypes.h"
+#include "prprf.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static enum {
+ thread_nspr, thread_pthread, thread_sproc, thread_win32
+} thread_provider;
+
+typedef void (*StartFn)(void*);
+typedef struct StartObject
+{
+ StartFn start;
+ void *arg;
+} StartObject;
+
+static PRFileDesc *output;
+
+static int _debug_on = 0;
+
+#define DEFAULT_THREAD_COUNT 10
+
+#define DPRINTF(arg) if (_debug_on) PR_fprintf arg
+
+#if defined(_PR_PTHREADS)
+#include <pthread.h>
+#include "md/_pth.h"
+static void *pthread_start(void *arg)
+{
+ StartFn start = ((StartObject*)arg)->start;
+ void *data = ((StartObject*)arg)->arg;
+ PR_Free(arg);
+ start(data);
+ return NULL;
+} /* pthread_start */
+#endif /* defined(_PR_PTHREADS) */
+
+#if defined(WIN32)
+#include <windows.h>
+#include <process.h> /* for _beginthreadex() */
+
+static PRUintn __stdcall windows_start(void *arg)
+{
+ StartObject *so = (StartObject*)arg;
+ StartFn start = so->start;
+ void *data = so->arg;
+ PR_Free(so);
+ start(data);
+ return 0;
+} /* windows_start */
+#endif /* defined(WIN32) */
+
+static PRStatus NSPRPUB_TESTS_CreateThread(StartFn start, void *arg)
+{
+ PRStatus rv;
+
+ switch (thread_provider)
+ {
+ case thread_nspr:
+ {
+ PRThread *thread = PR_CreateThread(
+ PR_USER_THREAD, start, arg,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ rv = (NULL == thread) ? PR_FAILURE : PR_SUCCESS;
+ }
+ break;
+ case thread_pthread:
+#if defined(_PR_PTHREADS)
+ {
+ int rv;
+ pthread_t id;
+ pthread_attr_t tattr;
+ StartObject *start_object;
+ start_object = PR_NEW(StartObject);
+ PR_ASSERT(NULL != start_object);
+ start_object->start = start;
+ start_object->arg = arg;
+
+ rv = _PT_PTHREAD_ATTR_INIT(&tattr);
+ PR_ASSERT(0 == rv);
+
+ rv = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
+ PR_ASSERT(0 == rv);
+
+ rv = pthread_attr_setstacksize(&tattr, 64 * 1024);
+ PR_ASSERT(0 == rv);
+
+ rv = _PT_PTHREAD_CREATE(&id, tattr, pthread_start, start_object);
+ (void)_PT_PTHREAD_ATTR_DESTROY(&tattr);
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+ }
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+ break;
+#endif /* defined(_PR_PTHREADS) */
+
+ case thread_sproc:
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+ break;
+ case thread_win32:
+#if defined(WIN32)
+ {
+ void *th;
+ PRUintn id;
+ StartObject *start_object;
+ start_object = PR_NEW(StartObject);
+ PR_ASSERT(NULL != start_object);
+ start_object->start = start;
+ start_object->arg = arg;
+ th = (void*)_beginthreadex(
+ NULL, /* LPSECURITY_ATTRIBUTES - pointer to thread security attributes */
+ 0U, /* DWORD - initial thread stack size, in bytes */
+ windows_start, /* LPTHREAD_START_ROUTINE - pointer to thread function */
+ start_object, /* LPVOID - argument for new thread */
+ STACK_SIZE_PARAM_IS_A_RESERVATION, /*DWORD dwCreationFlags - creation flags */
+ &id /* LPDWORD - pointer to returned thread identifier */ );
+
+ rv = (NULL == th) ? PR_FAILURE : PR_SUCCESS;
+ }
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+#endif
+ break;
+ default:
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ return rv;
+} /* NSPRPUB_TESTS_CreateThread */
+
+static void PR_CALLBACK lazyEntry(void *arg)
+{
+ PR_ASSERT(NULL == arg);
+} /* lazyEntry */
+
+
+static void OneShot(void *arg)
+{
+ PRUintn pdkey;
+ PRLock *lock;
+ PRFileDesc *fd;
+ PRDir *dir;
+ PRFileDesc *pair[2];
+ PRIntn test = (PRIntn)arg;
+
+ for (test = 0; test < 12; ++test) {
+
+ switch (test)
+ {
+ case 0:
+ lock = PR_NewLock();
+ DPRINTF((output,"Thread[0x%x] called PR_NewLock\n",
+ PR_GetCurrentThread()));
+ PR_DestroyLock(lock);
+ break;
+
+ case 1:
+ (void)PR_SecondsToInterval(1);
+ DPRINTF((output,"Thread[0x%x] called PR_SecondsToInterval\n",
+ PR_GetCurrentThread()));
+ break;
+
+ case 2: (void)PR_CreateThread(
+ PR_USER_THREAD, lazyEntry, NULL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+ DPRINTF((output,"Thread[0x%x] called PR_CreateThread\n",
+ PR_GetCurrentThread()));
+ break;
+
+ case 3:
+ fd = PR_Open("foreign.tmp", PR_CREATE_FILE | PR_RDWR, 0666);
+ DPRINTF((output,"Thread[0x%x] called PR_Open\n",
+ PR_GetCurrentThread()));
+ PR_Close(fd);
+ break;
+
+ case 4:
+ fd = PR_NewUDPSocket();
+ DPRINTF((output,"Thread[0x%x] called PR_NewUDPSocket\n",
+ PR_GetCurrentThread()));
+ PR_Close(fd);
+ break;
+
+ case 5:
+ fd = PR_NewTCPSocket();
+ DPRINTF((output,"Thread[0x%x] called PR_NewTCPSocket\n",
+ PR_GetCurrentThread()));
+ PR_Close(fd);
+ break;
+
+ case 6:
+#define TEMP_DIR "./tmp"
+ PR_MkDir(TEMP_DIR, 0700);
+ dir = PR_OpenDir(TEMP_DIR);
+ DPRINTF((output,"Thread[0x%x] called PR_OpenDir\n",
+ PR_GetCurrentThread()));
+ PR_CloseDir(dir);
+ break;
+
+ case 7:
+ (void)PR_NewThreadPrivateIndex(&pdkey, NULL);
+ DPRINTF((output,"Thread[0x%x] called PR_NewThreadPrivateIndex\n",
+ PR_GetCurrentThread()));
+ break;
+
+ case 8:
+ (void)PR_GetEnv("PATH");
+ DPRINTF((output,"Thread[0x%x] called PR_GetEnv\n",
+ PR_GetCurrentThread()));
+ break;
+
+ case 9:
+ (void)PR_NewTCPSocketPair(pair);
+ DPRINTF((output,"Thread[0x%x] called PR_NewTCPSocketPair\n",
+ PR_GetCurrentThread()));
+ PR_Close(pair[0]);
+ PR_Close(pair[1]);
+ break;
+
+ case 10:
+ PR_SetConcurrency(2);
+ DPRINTF((output,"Thread[0x%x] called PR_SetConcurrency\n",
+ PR_GetCurrentThread()));
+ break;
+
+ case 11:
+ PR_SetThreadPriority(PR_GetCurrentThread(), PR_PRIORITY_HIGH);
+ DPRINTF((output,"Thread[0x%x] called PR_SetThreadPriority\n",
+ PR_GetCurrentThread()));
+ break;
+
+ default:
+ break;
+ } /* switch() */
+ }
+} /* OneShot */
+
+int main(int argc, char **argv)
+{
+ PRStatus rv;
+ PRInt32 thread_cnt = DEFAULT_THREAD_COUNT;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dt:");
+
+#if defined(WIN32)
+ thread_provider = thread_win32;
+#elif defined(_PR_PTHREADS)
+ thread_provider = thread_pthread;
+#else
+ thread_provider = thread_nspr;
+#endif
+
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ case 't': /* thread count */
+ thread_cnt = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_SetConcurrency(2);
+
+ output = PR_GetSpecialFD(PR_StandardOutput);
+
+ while (thread_cnt-- > 0)
+ {
+ rv = NSPRPUB_TESTS_CreateThread(OneShot, (void*)thread_cnt);
+ PR_ASSERT(PR_SUCCESS == rv);
+ PR_Sleep(PR_MillisecondsToInterval(5));
+ }
+ PR_Sleep(PR_SecondsToInterval(3));
+ return (PR_SUCCESS == PR_Cleanup()) ? 0 : 1;
+} /* main */
+
+/* foreign.c */
diff --git a/nsprpub/pr/tests/forktest.c b/nsprpub/pr/tests/forktest.c
new file mode 100644
index 0000000000..a6d4bc4bb4
--- /dev/null
+++ b/nsprpub/pr/tests/forktest.c
@@ -0,0 +1,254 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: forktest.c
+**
+** Description: UNIX test for fork functions.
+**
+** Modification History:
+** 15-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 AGarcic - Revert to return code 0 and 1, remove debug option (obsolete).
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+PRIntn failed_already=0;
+
+#ifdef XP_UNIX
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+
+static char *message = "Hello world!";
+
+static void
+ClientThreadFunc(void *arg)
+{
+ PRNetAddr addr;
+ PRFileDesc *sock = NULL;
+ PRInt32 tmp = (PRInt32)arg;
+
+ /*
+ * Make sure the PR_Accept call will block
+ */
+
+ printf("Wait one second before connect\n");
+ fflush(stdout);
+ PR_Sleep(PR_SecondsToInterval(1));
+
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = 0;
+ if ((sock = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "failed to create TCP socket: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ if (PR_Bind(sock, &addr) != PR_SUCCESS) {
+ fprintf(stderr, "PR_Bind failed: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ addr.inet.ip = PR_htonl(INADDR_LOOPBACK);
+ addr.inet.port = PR_htons((PRInt16)tmp);
+ printf("Connecting to port %hu\n", PR_ntohs(addr.inet.port));
+ fflush(stdout);
+ if (PR_Connect(sock, &addr, PR_SecondsToInterval(5)) !=
+ PR_SUCCESS) {
+ fprintf(stderr, "PR_Connect failed: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ printf("Writing message \"%s\"\n", message);
+ fflush(stdout);
+ if (PR_Send(sock, message, strlen(message) + 1, 0, PR_INTERVAL_NO_TIMEOUT) ==
+ -1) {
+ fprintf(stderr, "PR_Send failed: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+finish:
+ if (sock) {
+ PR_Close(sock);
+ }
+ return;
+}
+
+/*
+ * DoIO --
+ * This function creates a thread that acts as a client and itself.
+ * acts as a server. Then it joins the client thread.
+ */
+static void
+DoIO(void)
+{
+ PRThread *clientThread;
+ PRFileDesc *listenSock = NULL;
+ PRFileDesc *sock = NULL;
+ PRNetAddr addr;
+ PRInt32 nBytes;
+ char buf[128];
+
+ listenSock = PR_NewTCPSocket();
+ if (!listenSock) {
+ fprintf(stderr, "failed to create a TCP socket: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = 0;
+ if (PR_Bind(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "failed to bind socket: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "failed to get socket port number: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ if (PR_Listen(listenSock, 5) == PR_FAILURE) {
+ fprintf(stderr, "PR_Listen failed: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ clientThread = PR_CreateThread( PR_USER_THREAD, ClientThreadFunc,
+ (void *) PR_ntohs(addr.inet.port), PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "Cannot create client thread: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already = 1;
+ goto finish;
+ }
+ printf("Accepting connection at port %hu\n", PR_ntohs(addr.inet.port));
+ fflush(stdout);
+ sock = PR_Accept(listenSock, &addr, PR_SecondsToInterval(5));
+ if (!sock) {
+ fprintf(stderr, "PR_Accept failed: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ nBytes = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT);
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Recv failed: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+
+ /*
+ * Make sure it has proper null byte to mark end of string
+ */
+
+ buf[sizeof(buf) - 1] = '\0';
+ printf("Received \"%s\" from the client\n", buf);
+ fflush(stdout);
+ if (!strcmp(buf, message)) {
+ PR_JoinThread(clientThread);
+
+ printf("The message is received correctly\n");
+ fflush(stdout);
+ } else {
+ fprintf(stderr, "The message should be \"%s\"\n",
+ message);
+ failed_already = 1;
+ }
+
+finish:
+ if (listenSock) {
+ PR_Close(listenSock);
+ }
+ if (sock) {
+ PR_Close(sock);
+ }
+ return;
+}
+
+int main(int argc, char **argv)
+{
+ pid_t pid;
+
+ /* main test program */
+
+ DoIO();
+
+ pid = fork();
+
+ if (pid == (pid_t) -1) {
+ fprintf(stderr, "Fork failed: errno %d\n", errno);
+ failed_already=1;
+ return 1;
+ } else if (pid > 0) {
+ int childStatus;
+
+ printf("Fork succeeded. Parent process continues.\n");
+ DoIO();
+ if (waitpid(pid, &childStatus, 0) != pid) {
+ {
+ fprintf(stderr, "waitpid failed: %d\n", errno);
+ failed_already = 1;
+ }
+ } else if (!WIFEXITED(childStatus)
+ || WEXITSTATUS(childStatus) != 0) {
+ failed_already = 1;
+ }
+ printf("Parent process exits.\n");
+ if (!failed_already) {
+ printf("PASSED\n");
+ } else {
+ printf("FAILED\n");
+ }
+ return failed_already;
+ } else {
+ printf("Fork succeeded. Child process continues.\n");
+ DoIO();
+ printf("Child process exits.\n");
+ return failed_already;
+ }
+}
+
+#else /* XP_UNIX */
+
+int main( int argc,
+ char *argv[]
+ )
+{
+
+ printf("The fork test is applicable to Unix only.\n");
+ return 0;
+
+}
+
+#endif /* XP_UNIX */
diff --git a/nsprpub/pr/tests/formattm.c b/nsprpub/pr/tests/formattm.c
new file mode 100644
index 0000000000..63355b28b7
--- /dev/null
+++ b/nsprpub/pr/tests/formattm.c
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* A test program for PR_FormatTime and PR_FormatTimeUSEnglish */
+
+#include "prtime.h"
+
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ char buffer[256];
+ char small_buffer[8];
+ PRTime now;
+ PRExplodedTime tod;
+
+ now = PR_Now();
+ PR_ExplodeTime(now, PR_LocalTimeParameters, &tod);
+
+ if (PR_FormatTime(buffer, sizeof(buffer),
+ "%a %b %d %H:%M:%S %Z %Y", &tod) != 0) {
+ printf("%s\n", buffer);
+ } else {
+ fprintf(stderr, "PR_FormatTime(buffer) failed\n");
+ return 1;
+ }
+
+ small_buffer[0] = '?';
+ if (PR_FormatTime(small_buffer, sizeof(small_buffer),
+ "%a %b %d %H:%M:%S %Z %Y", &tod) == 0) {
+ if (small_buffer[0] != '\0') {
+ fprintf(stderr, "PR_FormatTime(small_buffer) did not output "
+ "an empty string on failure\n");
+ return 1;
+ }
+ printf("%s\n", small_buffer);
+ } else {
+ fprintf(stderr, "PR_FormatTime(small_buffer) succeeded "
+ "unexpectedly\n");
+ return 1;
+ }
+
+ (void)PR_FormatTimeUSEnglish(buffer, sizeof(buffer),
+ "%a %b %d %H:%M:%S %Z %Y", &tod);
+ printf("%s\n", buffer);
+
+ return 0;
+}
diff --git a/nsprpub/pr/tests/freeif.c b/nsprpub/pr/tests/freeif.c
new file mode 100644
index 0000000000..1d38d0e31a
--- /dev/null
+++ b/nsprpub/pr/tests/freeif.c
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * A test to see if the macros PR_DELETE and PR_FREEIF are
+ * properly defined. (See Bugzilla bug #39110.)
+ */
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void Noop(void) { }
+
+static void Fail(void)
+{
+ printf("FAIL\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ int foo = 1;
+ char *ptr = NULL;
+
+ /* this fails to compile with the old definition of PR_DELETE */
+ if (foo) {
+ PR_DELETE(ptr);
+ }
+ else {
+ Noop();
+ }
+
+ /* this nests incorrectly with the old definition of PR_FREEIF */
+ if (foo) {
+ PR_FREEIF(ptr);
+ }
+ else {
+ Fail();
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/fsync.c b/nsprpub/pr/tests/fsync.c
new file mode 100644
index 0000000000..3e0fb175c1
--- /dev/null
+++ b/nsprpub/pr/tests/fsync.c
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prio.h"
+#include "prmem.h"
+#include "prprf.h"
+#include "prinrval.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+static PRFileDesc *err = NULL;
+
+static void Help(void)
+{
+ PR_fprintf(err, "Usage: [-S] [-K <n>] [-h] <filename>\n");
+ PR_fprintf(err, "\t-c Nuber of iterations (default: 10)\n");
+ PR_fprintf(err, "\t-S Sync the file (default: FALSE)\n");
+ PR_fprintf(err, "\t-K Size of file (K bytes) (default: 10)\n");
+ PR_fprintf(err, "\t Name of file to write (default: ./tmp-sync.dat)\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+int main(int argc, char **argv)
+{
+ PRStatus rv;
+ PLOptStatus os;
+ PRUint8 *buffer;
+ PRFileDesc *file = NULL;
+ const char *filename = "sync.dat";
+ PRUint32 index, loops, iterations = 10, filesize = 10;
+ PRIntn flags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hSK:c:");
+ PRIntervalTime time, total = 0, shortest = 0x7fffffff, longest = 0;
+
+ err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 0: /* Name of file to create */
+ filename = opt->value;
+ break;
+ case 'S': /* Use sych option on file */
+ flags |= PR_SYNC;
+ break;
+ case 'K': /* Size of file to write */
+ filesize = atoi(opt->value);
+ break;
+ case 'c': /* Number of iterations */
+ iterations = atoi(opt->value);
+ break;
+ case 'h': /* user wants some guidance */
+ default: /* user needs some guidance */
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ file = PR_Open(filename, flags, 0666);
+ if (NULL == file)
+ {
+ PL_FPrintError(err, "Failed to open file");
+ return 1;
+ }
+
+ buffer = (PRUint8*)PR_CALLOC(1024);
+ if (NULL == buffer)
+ {
+ PL_FPrintError(err, "Cannot allocate buffer");
+ return 1;
+ }
+
+ for (index = 0; index < sizeof(buffer); ++index) {
+ buffer[index] = (PRUint8)index;
+ }
+
+ for (loops = 0; loops < iterations; ++loops)
+ {
+ time = PR_IntervalNow();
+ for (index = 0; index < filesize; ++index)
+ {
+ PR_Write(file, buffer, 1024);
+ }
+ time = (PR_IntervalNow() - time);
+
+ total += time;
+ if (time < shortest) {
+ shortest = time;
+ }
+ else if (time > longest) {
+ longest = time;
+ }
+ if (0 != PR_Seek(file, 0, PR_SEEK_SET))
+ {
+ PL_FPrintError(err, "Rewinding file");
+ return 1;
+ }
+ }
+
+ total = total / iterations;
+ PR_fprintf(
+ err, "%u iterations over a %u kbyte %sfile: %u [%u] %u\n",
+ iterations, filesize, ((flags & PR_SYNC) ? "SYNCH'd " : ""),
+ PR_IntervalToMicroseconds(shortest),
+ PR_IntervalToMicroseconds(total),
+ PR_IntervalToMicroseconds(longest));
+
+ PR_DELETE(buffer);
+ rv = PR_Close(file);
+ if (PR_SUCCESS != rv)
+ {
+ PL_FPrintError(err, "Closing file failed");
+ return 1;
+ }
+ rv = PR_Delete(filename);
+ if (PR_SUCCESS != rv)
+ {
+ PL_FPrintError(err, "Deleting file failed");
+ return 1;
+ }
+ return 0;
+}
diff --git a/nsprpub/pr/tests/getai.c b/nsprpub/pr/tests/getai.c
new file mode 100644
index 0000000000..d759ed827d
--- /dev/null
+++ b/nsprpub/pr/tests/getai.c
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv)
+{
+ PRAddrInfo *ai;
+ void *iter;
+ PRNetAddr addr;
+
+ ai = PR_GetAddrInfoByName(argv[1], PR_AF_UNSPEC, PR_AI_ADDRCONFIG);
+ if (ai == NULL) {
+ fprintf(stderr, "PR_GetAddrInfoByName failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ printf("%s\n", PR_GetCanonNameFromAddrInfo(ai));
+ iter = NULL;
+ while ((iter = PR_EnumerateAddrInfo(iter, ai, 0, &addr)) != NULL) {
+ char buf[128];
+ PR_NetAddrToString(&addr, buf, sizeof buf);
+ printf("%s\n", buf);
+ }
+ PR_FreeAddrInfo(ai);
+ return 0;
+}
diff --git a/nsprpub/pr/tests/gethost.c b/nsprpub/pr/tests/gethost.c
new file mode 100644
index 0000000000..71704a8454
--- /dev/null
+++ b/nsprpub/pr/tests/gethost.c
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: gethost.c
+ *
+ * Description: tests various functions in prnetdb.h
+ *
+ * Usage: gethost [-6] [hostname]
+ */
+
+#include "prio.h"
+#include "prnetdb.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define DEFAULT_HOST_NAME "mozilla.org"
+
+static void Help(void)
+{
+ fprintf(stderr, "Usage: gethost [-h] [hostname]\n");
+ fprintf(stderr, "\t-h help\n");
+ fprintf(stderr, "\thostname Name of host (default: %s)\n",
+ DEFAULT_HOST_NAME);
+} /* Help */
+
+/*
+ * Prints the contents of a PRHostEnt structure
+ */
+void PrintHostent(const PRHostEnt *he)
+{
+ int i;
+ int j;
+
+ printf("h_name: %s\n", he->h_name);
+ for (i = 0; he->h_aliases[i]; i++) {
+ printf("h_aliases[%d]: %s\n", i, he->h_aliases[i]);
+ }
+ printf("h_addrtype: %d\n", he->h_addrtype);
+ printf("h_length: %d\n", he->h_length);
+ for (i = 0; he->h_addr_list[i]; i++) {
+ printf("h_addr_list[%d]: ", i);
+ for (j = 0; j < he->h_length; j++) {
+ if (j != 0) {
+ printf(".");
+ }
+ printf("%u", (unsigned char)he->h_addr_list[i][j]);
+ }
+ printf("\n");
+ }
+}
+
+int main(int argc, char **argv)
+{
+ const char *hostName = DEFAULT_HOST_NAME;
+ PRHostEnt he, reversehe;
+ char buf[PR_NETDB_BUF_SIZE];
+ char reversebuf[PR_NETDB_BUF_SIZE];
+ PRIntn idx;
+ PRNetAddr addr;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "h");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option) {
+ case 0: /* naked */
+ hostName = opt->value;
+ break;
+ case 'h': /* Help message */
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (PR_GetHostByName(hostName, buf, sizeof(buf), &he) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetHostByName failed\n");
+ exit(1);
+ }
+ PrintHostent(&he);
+ idx = 0;
+ while (1) {
+ idx = PR_EnumerateHostEnt(idx, &he, 0, &addr);
+ if (idx == -1) {
+ fprintf(stderr, "PR_EnumerateHostEnt failed\n");
+ exit(1);
+ }
+ if (idx == 0) {
+ break; /* normal loop termination */
+ }
+ printf("reverse lookup\n");
+ if (PR_GetHostByAddr(&addr, reversebuf, sizeof(reversebuf),
+ &reversehe) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetHostByAddr failed\n");
+ exit(1);
+ }
+ PrintHostent(&reversehe);
+ }
+
+ printf("PR_GetIPNodeByName with PR_AF_INET\n");
+ if (PR_GetIPNodeByName(hostName, PR_AF_INET, PR_AI_DEFAULT,
+ buf, sizeof(buf), &he) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetIPNodeByName failed\n");
+ exit(1);
+ }
+ PrintHostent(&he);
+ printf("PR_GetIPNodeByName with PR_AF_INET6\n");
+ if (PR_GetIPNodeByName(hostName, PR_AF_INET6, PR_AI_DEFAULT,
+ buf, sizeof(buf), &he) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetIPNodeByName failed\n");
+ exit(1);
+ }
+ PrintHostent(&he);
+ idx = 0;
+ printf("PR_GetHostByAddr with PR_AF_INET6\n");
+ while (1) {
+ idx = PR_EnumerateHostEnt(idx, &he, 0, &addr);
+ if (idx == -1) {
+ fprintf(stderr, "PR_EnumerateHostEnt failed\n");
+ exit(1);
+ }
+ if (idx == 0) {
+ break; /* normal loop termination */
+ }
+ printf("reverse lookup\n");
+ if (PR_GetHostByAddr(&addr, reversebuf, sizeof(reversebuf),
+ &reversehe) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetHostByAddr failed\n");
+ exit(1);
+ }
+ PrintHostent(&reversehe);
+ }
+ printf("PR_GetHostByAddr with PR_AF_INET6 done\n");
+
+ PR_StringToNetAddr("::1", &addr);
+ if (PR_IsNetAddrType(&addr, PR_IpAddrV4Mapped) == PR_TRUE) {
+ fprintf(stderr, "addr should not be ipv4 mapped address\n");
+ exit(1);
+ }
+ if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) {
+ fprintf(stderr, "addr should be loopback address\n");
+ exit(1);
+ }
+
+ PR_StringToNetAddr("127.0.0.1", &addr);
+ if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) {
+ fprintf(stderr, "addr should be loopback address\n");
+ exit(1);
+ }
+ PR_StringToNetAddr("::FFFF:127.0.0.1", &addr);
+ if (PR_IsNetAddrType(&addr, PR_IpAddrV4Mapped) == PR_FALSE) {
+ fprintf(stderr, "addr should be ipv4 mapped address\n");
+ exit(1);
+ }
+ if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) {
+ fprintf(stderr, "addr should be loopback address\n");
+ exit(1);
+ }
+
+ if (PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_InitializeNetAddr failed\n");
+ exit(1);
+ }
+ if (PR_IsNetAddrType(&addr, PR_IpAddrAny) == PR_FALSE) {
+ fprintf(stderr, "addr should be unspecified address\n");
+ exit(1);
+ }
+ if (PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_InitializeNetAddr failed\n");
+ exit(1);
+ }
+ if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) {
+ fprintf(stderr, "addr should be loopback address\n");
+ exit(1);
+ }
+
+ if (PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET, 0, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_SetNetAddr failed\n");
+ exit(1);
+ }
+ if (PR_IsNetAddrType(&addr, PR_IpAddrAny) == PR_FALSE) {
+ fprintf(stderr, "addr should be unspecified address\n");
+ exit(1);
+ }
+ if (PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET, 0, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_SetNetAddr failed\n");
+ exit(1);
+ }
+ if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) {
+ fprintf(stderr, "addr should be loopback address\n");
+ exit(1);
+ }
+
+ addr.inet.family = PR_AF_INET;
+ addr.inet.port = 0;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ if (PR_IsNetAddrType(&addr, PR_IpAddrAny) == PR_FALSE) {
+ fprintf(stderr, "addr should be unspecified address\n");
+ exit(1);
+ }
+ {
+ char buf[256];
+ PR_NetAddrToString(&addr, buf, 256);
+ printf("IPv4 INADDRANY: %s\n", buf);
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.port = 0;
+ addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+ if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) {
+ fprintf(stderr, "addr should be loopback address\n");
+ exit(1);
+ }
+ {
+ char buf[256];
+ PR_NetAddrToString(&addr, buf, 256);
+ printf("IPv4 LOOPBACK: %s\n", buf);
+ }
+
+ if (PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, 0, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_SetNetAddr failed\n");
+ exit(1);
+ }
+ if (PR_IsNetAddrType(&addr, PR_IpAddrAny) == PR_FALSE) {
+ fprintf(stderr, "addr should be unspecified address\n");
+ exit(1);
+ }
+ {
+ char buf[256];
+ PR_NetAddrToString(&addr, buf, 256);
+ printf("IPv6 INADDRANY: %s\n", buf);
+ }
+ if (PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET6, 0, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_SetNetAddr failed\n");
+ exit(1);
+ }
+ if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) {
+ fprintf(stderr, "addr should be loopback address\n");
+ exit(1);
+ }
+ {
+ char buf[256];
+ PR_NetAddrToString(&addr, buf, 256);
+ printf("IPv6 LOOPBACK: %s\n", buf);
+ }
+ {
+ PRIPv6Addr v6addr;
+ char tmp_buf[256];
+
+ PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET, 0, &addr);
+
+ PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &v6addr);
+ PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, 0, &addr);
+ addr.ipv6.ip = v6addr;
+ PR_NetAddrToString(&addr, tmp_buf, 256);
+ printf("IPv4-mapped IPv6 LOOPBACK: %s\n", tmp_buf);
+ }
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/getproto.c b/nsprpub/pr/tests/getproto.c
new file mode 100644
index 0000000000..1a33b1f256
--- /dev/null
+++ b/nsprpub/pr/tests/getproto.c
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *************************************************************************
+ *
+ * File: getproto.c
+ *
+ * A test program for PR_GetProtoByName and PR_GetProtoByNumber
+ *
+ *************************************************************************
+ */
+
+#include "plstr.h"
+#include "plerror.h"
+#include "prinit.h"
+#include "prprf.h"
+#include "prnetdb.h"
+#include "prerror.h"
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *prstderr = PR_GetSpecialFD(PR_StandardError);
+ PRBool failed = PR_FALSE;
+ PRProtoEnt proto;
+ char buf[2048];
+ PRStatus rv;
+
+ PR_STDIO_INIT();
+ rv = PR_GetProtoByName("tcp", buf, sizeof(buf), &proto);
+ if (PR_FAILURE == rv) {
+ failed = PR_TRUE;
+ PL_FPrintError(prstderr, "PR_GetProtoByName failed");
+ }
+ else if (6 != proto.p_num) {
+ PR_fprintf(
+ prstderr,"tcp is usually 6, but is %d on this machine\n",
+ proto.p_num);
+ }
+ else {
+ PR_fprintf(prstderr, "tcp is protocol number %d\n", proto.p_num);
+ }
+
+ rv = PR_GetProtoByName("udp", buf, sizeof(buf), &proto);
+ if (PR_FAILURE == rv) {
+ failed = PR_TRUE;
+ PL_FPrintError(prstderr, "PR_GetProtoByName failed");
+ }
+ else if (17 != proto.p_num) {
+ PR_fprintf(
+ prstderr, "udp is usually 17, but is %d on this machine\n",
+ proto.p_num);
+ }
+ else {
+ PR_fprintf(prstderr, "udp is protocol number %d\n", proto.p_num);
+ }
+
+ rv = PR_GetProtoByNumber(6, buf, sizeof(buf), &proto);
+ if (PR_FAILURE == rv) {
+ failed = PR_TRUE;
+ PL_FPrintError(prstderr, "PR_GetProtoByNumber failed");
+ }
+ else if (PL_strcmp("tcp", proto.p_name)) {
+ PR_fprintf(
+ prstderr, "Protocol number 6 is usually tcp, but is %s"
+ " on this platform\n", proto.p_name);
+ }
+ else {
+ PR_fprintf(prstderr, "Protocol number 6 is %s\n", proto.p_name);
+ }
+
+ rv = PR_GetProtoByNumber(17, buf, sizeof(buf), &proto);
+ if (PR_FAILURE == rv) {
+ failed = PR_TRUE;
+ PL_FPrintError(prstderr, "PR_GetProtoByNumber failed");
+ }
+ else if (PL_strcmp("udp", proto.p_name)) {
+ PR_fprintf(
+ prstderr, "Protocol number 17 is usually udp, but is %s"
+ " on this platform\n", proto.p_name);
+ }
+ else {
+ PR_fprintf(prstderr, "Protocol number 17 is %s\n", proto.p_name);
+ }
+
+ PR_fprintf(prstderr, (failed) ? "FAILED\n" : "PASSED\n");
+ return (failed) ? 1 : 0;
+}
diff --git a/nsprpub/pr/tests/i2l.c b/nsprpub/pr/tests/i2l.c
new file mode 100644
index 0000000000..a28cd16a80
--- /dev/null
+++ b/nsprpub/pr/tests/i2l.c
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdlib.h>
+
+#include "prio.h"
+#include "prinit.h"
+#include "prprf.h"
+#include "prlong.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+typedef union Overlay_i
+{
+ PRInt32 i;
+ PRInt64 l;
+} Overlay_i;
+
+typedef union Overlay_u
+{
+ PRUint32 i;
+ PRUint64 l;
+} Overlay_u;
+
+static PRFileDesc *err = NULL;
+
+static void Help(void)
+{
+ PR_fprintf(err, "Usage: -i n | -u n | -h\n");
+ PR_fprintf(err, "\t-i n treat following number as signed integer\n");
+ PR_fprintf(err, "\t-u n treat following number as unsigned integer\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+static PRIntn PR_CALLBACK RealMain(PRIntn argc, char **argv)
+{
+ Overlay_i si;
+ Overlay_u ui;
+ PLOptStatus os;
+ PRBool bsi = PR_FALSE, bui = PR_FALSE;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hi:u:");
+ err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'i': /* signed integer */
+ si.i = (PRInt32)atoi(opt->value);
+ bsi = PR_TRUE;
+ break;
+ case 'u': /* unsigned */
+ ui.i = (PRUint32)atoi(opt->value);
+ bui = PR_TRUE;
+ break;
+ case 'h': /* user wants some guidance */
+ default:
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+#if defined(HAVE_LONG_LONG)
+ PR_fprintf(err, "We have long long\n");
+#else
+ PR_fprintf(err, "We don't have long long\n");
+#endif
+
+ if (bsi)
+ {
+ PR_fprintf(err, "Converting %ld: ", si.i);
+ LL_I2L(si.l, si.i);
+ PR_fprintf(err, "%lld\n", si.l);
+ }
+
+ if (bui)
+ {
+ PR_fprintf(err, "Converting %lu: ", ui.i);
+ LL_I2L(ui.l, ui.i);
+ PR_fprintf(err, "%llu\n", ui.l);
+ }
+ return 0;
+
+} /* main */
+
+
+int main(int argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
+
+/* i2l.c */
diff --git a/nsprpub/pr/tests/initclk.c b/nsprpub/pr/tests/initclk.c
new file mode 100644
index 0000000000..65d8b17e71
--- /dev/null
+++ b/nsprpub/pr/tests/initclk.c
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This is a regression test for the bug that the interval timer
+ * is not initialized when _PR_CreateCPU calls PR_IntervalNow.
+ * The bug would make this test program finish prematurely,
+ * when the SHORT_TIMEOUT period expires. The correct behavior
+ * is for the test to finish when the LONG_TIMEOUT period expires.
+ */
+
+#include "prlock.h"
+#include "prcvar.h"
+#include "prthread.h"
+#include "prinrval.h"
+#include "prlog.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+/* The timeouts, in milliseconds */
+#define SHORT_TIMEOUT 1000
+#define LONG_TIMEOUT 3000
+
+PRLock *lock1, *lock2;
+PRCondVar *cv1, *cv2;
+
+void ThreadFunc(void *arg)
+{
+ PR_Lock(lock1);
+ PR_WaitCondVar(cv1, PR_MillisecondsToInterval(SHORT_TIMEOUT));
+ PR_Unlock(lock1);
+}
+
+int main(int argc, char **argv)
+{
+ PRThread *thread;
+ PRIntervalTime start, end;
+ PRUint32 elapsed_ms;
+
+ lock1 = PR_NewLock();
+ PR_ASSERT(NULL != lock1);
+ cv1 = PR_NewCondVar(lock1);
+ PR_ASSERT(NULL != cv1);
+ lock2 = PR_NewLock();
+ PR_ASSERT(NULL != lock2);
+ cv2 = PR_NewCondVar(lock2);
+ PR_ASSERT(NULL != cv2);
+ start = PR_IntervalNow();
+ thread = PR_CreateThread(
+ PR_USER_THREAD,
+ ThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ PR_ASSERT(NULL != thread);
+ PR_Lock(lock2);
+ PR_WaitCondVar(cv2, PR_MillisecondsToInterval(LONG_TIMEOUT));
+ PR_Unlock(lock2);
+ PR_JoinThread(thread);
+ end = PR_IntervalNow();
+ elapsed_ms = PR_IntervalToMilliseconds((PRIntervalTime)(end - start));
+ /* Allow 100ms imprecision */
+ if (elapsed_ms < LONG_TIMEOUT - 100 || elapsed_ms > LONG_TIMEOUT + 100) {
+ printf("Elapsed time should be %u ms but is %u ms\n",
+ LONG_TIMEOUT, elapsed_ms);
+ printf("FAIL\n");
+ exit(1);
+ }
+ printf("Elapsed time: %u ms, expected time: %u ms\n",
+ LONG_TIMEOUT, elapsed_ms);
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/inrval.c b/nsprpub/pr/tests/inrval.c
new file mode 100644
index 0000000000..52f7b71da1
--- /dev/null
+++ b/nsprpub/pr/tests/inrval.c
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** file: inrval.c
+** description: Interval conversion test.
+** Modification History:
+** 15-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+**/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "obsolete/pralarm.h"
+
+#include "prio.h"
+#include "prprf.h"
+#include "prlock.h"
+#include "prlong.h"
+#include "prcvar.h"
+#include "prinrval.h"
+#include "prtime.h"
+
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static PRIntn debug_mode;
+static PRFileDesc *output;
+
+
+static void TestConversions(void)
+{
+ PRIntervalTime ticks = PR_TicksPerSecond();
+
+ if (debug_mode) {
+ PR_fprintf(output, "PR_TicksPerSecond: %ld\n\n", ticks);
+ PR_fprintf(output, "PR_SecondsToInterval(1): %ld\n", PR_SecondsToInterval(1));
+ PR_fprintf(output, "PR_MillisecondsToInterval(1000): %ld\n", PR_MillisecondsToInterval(1000));
+ PR_fprintf(output, "PR_MicrosecondsToInterval(1000000): %ld\n\n", PR_MicrosecondsToInterval(1000000));
+
+ PR_fprintf(output, "PR_SecondsToInterval(3): %ld\n", PR_SecondsToInterval(3));
+ PR_fprintf(output, "PR_MillisecondsToInterval(3000): %ld\n", PR_MillisecondsToInterval(3000));
+ PR_fprintf(output, "PR_MicrosecondsToInterval(3000000): %ld\n\n", PR_MicrosecondsToInterval(3000000));
+
+ PR_fprintf(output, "PR_IntervalToSeconds(%ld): %ld\n", ticks, PR_IntervalToSeconds(ticks));
+ PR_fprintf(output, "PR_IntervalToMilliseconds(%ld): %ld\n", ticks, PR_IntervalToMilliseconds(ticks));
+ PR_fprintf(output, "PR_IntervalToMicroseconds(%ld): %ld\n\n", ticks, PR_IntervalToMicroseconds(ticks));
+
+ ticks *= 3;
+ PR_fprintf(output, "PR_IntervalToSeconds(%ld): %ld\n", ticks, PR_IntervalToSeconds(ticks));
+ PR_fprintf(output, "PR_IntervalToMilliseconds(%ld): %ld\n", ticks, PR_IntervalToMilliseconds(ticks));
+ PR_fprintf(output, "PR_IntervalToMicroseconds(%ld): %ld\n\n", ticks, PR_IntervalToMicroseconds(ticks));
+ } /*end debug mode */
+} /* TestConversions */
+
+static void TestIntervalOverhead(void)
+{
+ /* Hopefully the optimizer won't delete this function */
+ PRUint32 elapsed, per_call, loops = 1000000;
+
+ PRIntervalTime timeout, timein = PR_IntervalNow();
+ while (--loops > 0) {
+ timeout = PR_IntervalNow();
+ }
+
+ elapsed = 1000U * PR_IntervalToMicroseconds(timeout - timein);
+ per_call = elapsed / 1000000U;
+ PR_fprintf(
+ output, "Overhead of 'PR_IntervalNow()' is %u nsecs\n\n", per_call);
+} /* TestIntervalOverhead */
+
+static void TestNowOverhead(void)
+{
+ PRTime timeout, timein;
+ PRInt32 overhead, loops = 1000000;
+ PRInt64 elapsed, per_call, ten23rd, ten26th;
+
+ LL_I2L(ten23rd, 1000);
+ LL_I2L(ten26th, 1000000);
+
+ timein = PR_Now();
+ while (--loops > 0) {
+ timeout = PR_Now();
+ }
+
+ LL_SUB(elapsed, timeout, timein);
+ LL_MUL(elapsed, elapsed, ten23rd);
+ LL_DIV(per_call, elapsed, ten26th);
+ LL_L2I(overhead, per_call);
+ PR_fprintf(
+ output, "Overhead of 'PR_Now()' is %u nsecs\n\n", overhead);
+} /* TestNowOverhead */
+
+static void TestIntervals(void)
+{
+ PRStatus rv;
+ PRUint32 delta;
+ PRInt32 seconds;
+ PRUint64 elapsed, thousand;
+ PRTime timein, timeout;
+ PRLock *ml = PR_NewLock();
+ PRCondVar *cv = PR_NewCondVar(ml);
+ for (seconds = 0; seconds < 10; ++seconds)
+ {
+ PRIntervalTime ticks = PR_SecondsToInterval(seconds);
+ PR_Lock(ml);
+ timein = PR_Now();
+ rv = PR_WaitCondVar(cv, ticks);
+ timeout = PR_Now();
+ PR_Unlock(ml);
+ LL_SUB(elapsed, timeout, timein);
+ LL_I2L(thousand, 1000);
+ LL_DIV(elapsed, elapsed, thousand);
+ LL_L2UI(delta, elapsed);
+ if (debug_mode) PR_fprintf(output,
+ "TestIntervals: %swaiting %ld seconds took %ld msecs\n",
+ ((rv == PR_SUCCESS) ? "" : "FAILED "), seconds, delta);
+ }
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+ if (debug_mode) {
+ PR_fprintf(output, "\n");
+ }
+} /* TestIntervals */
+
+static PRIntn PR_CALLBACK RealMain(int argc, char** argv)
+{
+ PRUint32 vcpu, cpus = 0, loops = 1000;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+
+ /* main test */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dl:c:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'c': /* concurrency counter */
+ cpus = atoi(opt->value);
+ break;
+ case 'l': /* loop counter */
+ loops = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ output = PR_GetSpecialFD(PR_StandardOutput);
+ PR_fprintf(output, "inrval: Examine stdout to determine results.\n");
+
+ if (cpus == 0) {
+ cpus = 8;
+ }
+ if (loops == 0) {
+ loops = 1000;
+ }
+
+ if (debug_mode > 0)
+ {
+ PR_fprintf(output, "Inrval: Using %d loops\n", loops);
+ PR_fprintf(output, "Inrval: Using 1 and %d cpu(s)\n", cpus);
+ }
+
+ for (vcpu = 1; vcpu <= cpus; vcpu += cpus - 1)
+ {
+ if (debug_mode) {
+ PR_fprintf(output, "\nInrval: Using %d CPU(s)\n\n", vcpu);
+ }
+ PR_SetConcurrency(vcpu);
+
+ TestNowOverhead();
+ TestIntervalOverhead();
+ TestConversions();
+ TestIntervals();
+ }
+
+ return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
+
diff --git a/nsprpub/pr/tests/instrumt.c b/nsprpub/pr/tests/instrumt.c
new file mode 100644
index 0000000000..e1ca94d035
--- /dev/null
+++ b/nsprpub/pr/tests/instrumt.c
@@ -0,0 +1,478 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: instrumt.c
+** Description: This test is for the NSPR debug aids defined in
+** prcountr.h, prtrace.h, prolock.h
+**
+** The test case tests the three debug aids in NSPR:
+**
+** Diagnostic messages can be enabled using "instrumt -v 6"
+** This sets the msgLevel to something that PR_LOG() likes.
+** Also define in the environment "NSPR_LOG_MODULES=Test:6"
+**
+** CounterTest() tests the counter facility. This test
+** creates 4 threads. Each thread either increments, decrements,
+** adds to or subtracts from a counter, depending on an argument
+** passed to the thread at thread-create time. Each of these threads
+** does COUNT_LIMIT iterations doing its thing. When all 4 threads
+** are done, the result of the counter is evaluated. If all was atomic,
+** the the value of the counter should be zero.
+**
+** TraceTest():
+** This test mingles with the counter test. Counters trace.
+** A thread to extract trace entries on the fly is started.
+** A thread to dump trace entries to a file is started.
+**
+** OrderedLockTest():
+**
+**
+**
+**
+**
+*/
+
+#include <stdio.h>
+#include <plstr.h>
+#include <prclist.h>
+#include <prmem.h>
+#include <plgetopt.h>
+#include <prlog.h>
+#include <prmon.h>
+#include <pratom.h>
+#include <prtrace.h>
+#include <prcountr.h>
+#include <prolock.h>
+
+#define COUNT_LIMIT (10 * ( 1024))
+
+#define SMALL_TRACE_BUFSIZE ( 60 * 1024 )
+
+typedef enum
+{
+ CountLoop = 1,
+ TraceLoop = 2,
+ TraceFlow = 3
+} TraceTypes;
+
+
+PRLogModuleLevel msgLevel = PR_LOG_ALWAYS;
+
+PRBool help = PR_FALSE;
+PRBool failed = PR_FALSE;
+
+
+PRLogModuleInfo *lm;
+PRMonitor *mon;
+PRInt32 activeThreads = 0;
+PR_DEFINE_COUNTER( hCounter );
+PR_DEFINE_TRACE( hTrace );
+
+static void Help(void)
+{
+ printf("Help? ... Ha!\n");
+}
+
+static void ListCounters(void)
+{
+ PR_DEFINE_COUNTER( qh );
+ PR_DEFINE_COUNTER( rh );
+ const char *qn, *rn, *dn;
+ const char **qname = &qn, **rname = &rn, **desc = &dn;
+ PRUint32 tCtr;
+
+ PR_INIT_COUNTER_HANDLE( qh, NULL );
+ PR_FIND_NEXT_COUNTER_QNAME(qh, qh );
+ while ( qh != NULL )
+ {
+ PR_INIT_COUNTER_HANDLE( rh, NULL );
+ PR_FIND_NEXT_COUNTER_RNAME(rh, rh, qh );
+ while ( rh != NULL )
+ {
+ PR_GET_COUNTER_NAME_FROM_HANDLE( rh, qname, rname, desc );
+ PR_GET_COUNTER(tCtr, rh);
+ PR_LOG( lm, msgLevel,
+ ( "QName: %s RName: %s Desc: %s Value: %ld\n",
+ qn, rn, dn, tCtr ));
+ PR_FIND_NEXT_COUNTER_RNAME(rh, rh, qh );
+ }
+ PR_FIND_NEXT_COUNTER_QNAME(qh, qh);
+ }
+ return;
+} /* end ListCounters() */
+
+static void ListTraces(void)
+{
+ PR_DEFINE_TRACE( qh );
+ PR_DEFINE_TRACE( rh );
+ const char *qn, *rn, *dn;
+ const char **qname = &qn, **rname = &rn, **desc = &dn;
+
+ PR_INIT_TRACE_HANDLE( qh, NULL );
+ PR_FIND_NEXT_TRACE_QNAME(qh, qh );
+ while ( qh != NULL )
+ {
+ PR_INIT_TRACE_HANDLE( rh, NULL );
+ PR_FIND_NEXT_TRACE_RNAME(rh, rh, qh );
+ while ( rh != NULL )
+ {
+ PR_GET_TRACE_NAME_FROM_HANDLE( rh, qname, rname, desc );
+ PR_LOG( lm, msgLevel,
+ ( "QName: %s RName: %s Desc: %s",
+ qn, rn, dn ));
+ PR_FIND_NEXT_TRACE_RNAME(rh, rh, qh );
+ }
+ PR_FIND_NEXT_TRACE_QNAME(qh, qh);
+ }
+ return;
+} /* end ListCounters() */
+
+
+static PRInt32 one = 1;
+static PRInt32 two = 2;
+static PRInt32 three = 3;
+static PRInt32 four = 4;
+
+/*
+** Thread to iteratively count something.
+*/
+static void PR_CALLBACK CountSomething( void *arg )
+{
+ PRInt32 switchVar = *((PRInt32 *)arg);
+ PRInt32 i;
+
+ PR_LOG( lm, msgLevel,
+ ("CountSomething: begin thread %ld", switchVar ));
+
+ for ( i = 0; i < COUNT_LIMIT ; i++)
+ {
+ switch ( switchVar )
+ {
+ case 1 :
+ PR_INCREMENT_COUNTER( hCounter );
+ break;
+ case 2 :
+ PR_DECREMENT_COUNTER( hCounter );
+ break;
+ case 3 :
+ PR_ADD_TO_COUNTER( hCounter, 1 );
+ break;
+ case 4 :
+ PR_SUBTRACT_FROM_COUNTER( hCounter, 1 );
+ break;
+ default :
+ PR_ASSERT( 0 );
+ break;
+ }
+ PR_TRACE( hTrace, CountLoop, switchVar, i, 0, 0, 0, 0, 0 );
+ } /* end for() */
+
+ PR_LOG( lm, msgLevel,
+ ("CounterSomething: end thread %ld", switchVar ));
+
+ PR_EnterMonitor(mon);
+ --activeThreads;
+ PR_Notify( mon );
+ PR_ExitMonitor(mon);
+
+ return;
+} /* end CountSomething() */
+
+/*
+** Create the counter threads.
+*/
+static void CounterTest( void )
+{
+ PRThread *t1, *t2, *t3, *t4;
+ PRIntn i = 0;
+ PR_DEFINE_COUNTER( tc );
+ PR_DEFINE_COUNTER( zCounter );
+
+ PR_LOG( lm, msgLevel,
+ ("Begin CounterTest"));
+
+ /*
+ ** Test Get and Set of a counter.
+ **
+ */
+ PR_CREATE_COUNTER( zCounter, "Atomic", "get/set test", "test get and set of counter" );
+ PR_SET_COUNTER( zCounter, 9 );
+ PR_GET_COUNTER( i, zCounter );
+ if ( i != 9 )
+ {
+ failed = PR_TRUE;
+ PR_LOG( lm, msgLevel,
+ ("Counter set/get failed"));
+ }
+
+ activeThreads += 4;
+ PR_CREATE_COUNTER( hCounter, "Atomic", "SMP Tests", "test atomic nature of counter" );
+
+ PR_GET_COUNTER_HANDLE_FROM_NAME( tc, "Atomic", "SMP Tests" );
+ PR_ASSERT( tc == hCounter );
+
+ t1 = PR_CreateThread(PR_USER_THREAD,
+ CountSomething, &one,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t1);
+
+ t2 = PR_CreateThread(PR_USER_THREAD,
+ CountSomething, &two,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t2);
+
+ t3 = PR_CreateThread(PR_USER_THREAD,
+ CountSomething, &three,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t3);
+
+ t4 = PR_CreateThread(PR_USER_THREAD,
+ CountSomething, &four,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t4);
+
+ PR_LOG( lm, msgLevel,
+ ("Counter Threads started"));
+
+ ListCounters();
+ return;
+} /* end CounterTest() */
+
+/*
+** Thread to dump trace buffer to a file.
+*/
+static void PR_CALLBACK RecordTrace(void *arg )
+{
+ PR_RECORD_TRACE_ENTRIES();
+
+ PR_EnterMonitor(mon);
+ --activeThreads;
+ PR_Notify( mon );
+ PR_ExitMonitor(mon);
+
+ return;
+} /* end RecordTrace() */
+
+
+
+#define NUM_TRACE_RECORDS ( 10000 )
+/*
+** Thread to extract and print trace entries from the buffer.
+*/
+static void PR_CALLBACK SampleTrace( void *arg )
+{
+#if defined(DEBUG) || defined(FORCE_NSPR_TRACE)
+ PRInt32 found, rc;
+ PRTraceEntry *foundEntries;
+ PRInt32 i;
+
+ foundEntries = (PRTraceEntry *)PR_Malloc( NUM_TRACE_RECORDS * sizeof(PRTraceEntry));
+ PR_ASSERT(foundEntries != NULL );
+
+ do
+ {
+ rc = PR_GetTraceEntries( foundEntries, NUM_TRACE_RECORDS, &found);
+ PR_LOG( lm, msgLevel,
+ ("SampleTrace: Lost Data: %ld found: %ld", rc, found ));
+
+ if ( found != 0)
+ {
+ for ( i = 0 ; i < found; i++ )
+ {
+ PR_LOG( lm, msgLevel,
+ ("SampleTrace, detail: Thread: %p, Time: %llX, UD0: %ld, UD1: %ld, UD2: %8.8ld",
+ (foundEntries +i)->thread,
+ (foundEntries +i)->time,
+ (foundEntries +i)->userData[0],
+ (foundEntries +i)->userData[1],
+ (foundEntries +i)->userData[2] ));
+ }
+ }
+ PR_Sleep(PR_MillisecondsToInterval(50));
+ }
+ while( found != 0 && activeThreads >= 1 );
+
+ PR_Free( foundEntries );
+
+ PR_EnterMonitor(mon);
+ --activeThreads;
+ PR_Notify( mon );
+ PR_ExitMonitor(mon);
+
+ PR_LOG( lm, msgLevel,
+ ("SampleTrace(): exiting"));
+
+#endif
+ return;
+} /* end RecordTrace() */
+
+/*
+** Basic trace test.
+*/
+static void TraceTest( void )
+{
+ PRInt32 i;
+ PRInt32 size;
+ PR_DEFINE_TRACE( th );
+ PRThread *t1, *t2;
+
+ PR_LOG( lm, msgLevel,
+ ("Begin TraceTest"));
+
+ size = SMALL_TRACE_BUFSIZE;
+ PR_SET_TRACE_OPTION( PRTraceBufSize, &size );
+ PR_GET_TRACE_OPTION( PRTraceBufSize, &i );
+
+ PR_CREATE_TRACE( th, "TraceTest", "tt2", "A description for the trace test" );
+ PR_CREATE_TRACE( th, "TraceTest", "tt3", "A description for the trace test" );
+ PR_CREATE_TRACE( th, "TraceTest", "tt4", "A description for the trace test" );
+ PR_CREATE_TRACE( th, "TraceTest", "tt5", "A description for the trace test" );
+ PR_CREATE_TRACE( th, "TraceTest", "tt6", "A description for the trace test" );
+ PR_CREATE_TRACE( th, "TraceTest", "tt7", "A description for the trace test" );
+ PR_CREATE_TRACE( th, "TraceTest", "tt8", "A description for the trace test" );
+
+ PR_CREATE_TRACE( th, "Trace Test", "tt0", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt1", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt2", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt3", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt4", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt5", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt6", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt7", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt8", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt9", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt10", "QName is Trace Test, not TraceTest" );
+
+
+
+ activeThreads += 2;
+ t1 = PR_CreateThread(PR_USER_THREAD,
+ RecordTrace, NULL,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t1);
+
+ t2 = PR_CreateThread(PR_USER_THREAD,
+ SampleTrace, 0,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t2);
+
+ ListTraces();
+
+ PR_GET_TRACE_HANDLE_FROM_NAME( th, "TraceTest","tt1" );
+ PR_ASSERT( th == hTrace );
+
+ PR_LOG( lm, msgLevel,
+ ("End TraceTest"));
+ return;
+} /* end TraceTest() */
+
+
+/*
+** Ordered lock test.
+*/
+static void OrderedLockTest( void )
+{
+ PR_LOG( lm, msgLevel,
+ ("Begin OrderedLockTest"));
+
+
+} /* end OrderedLockTest() */
+
+
+int main(int argc, char **argv)
+{
+#if defined(DEBUG) || defined(FORCE_NSPR_TRACE)
+ PRUint32 counter;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hdv:");
+ lm = PR_NewLogModule("Test");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'v': /* verbose mode */
+ msgLevel = (PRLogModuleLevel)atol( opt->value);
+ break;
+ case 'h': /* help message */
+ Help();
+ help = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_CREATE_TRACE( hTrace, "TraceTest", "tt1", "A description for the trace test" );
+ mon = PR_NewMonitor();
+ PR_EnterMonitor( mon );
+
+ TraceTest();
+ CounterTest();
+ OrderedLockTest();
+
+ /* Wait for all threads to exit */
+ while ( activeThreads > 0 ) {
+ if ( activeThreads == 1 ) {
+ PR_SET_TRACE_OPTION( PRTraceStopRecording, NULL );
+ }
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ PR_GET_COUNTER( counter, hCounter );
+ }
+ PR_ExitMonitor( mon );
+
+ /*
+ ** Evaluate results
+ */
+ PR_GET_COUNTER( counter, hCounter );
+ if ( counter != 0 )
+ {
+ failed = PR_TRUE;
+ PR_LOG( lm, msgLevel,
+ ("Expected counter == 0, found: %ld", counter));
+ printf("FAIL\n");
+ }
+ else
+ {
+ printf("PASS\n");
+ }
+
+
+ PR_DESTROY_COUNTER( hCounter );
+
+ PR_DestroyMonitor( mon );
+
+ PR_TRACE( hTrace, TraceFlow, 0xfff,0,0,0,0,0,0);
+ PR_DESTROY_TRACE( hTrace );
+#else
+ printf("Test not defined\n");
+#endif
+ return 0;
+} /* main() */
+/* end instrumt.c */
+
diff --git a/nsprpub/pr/tests/intrio.c b/nsprpub/pr/tests/intrio.c
new file mode 100644
index 0000000000..23d834d667
--- /dev/null
+++ b/nsprpub/pr/tests/intrio.c
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: intrio.c
+ * Purpose: testing i/o interrupts (see Bugzilla bug #31120)
+ */
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* for synchronization between the main thread and iothread */
+static PRLock *lock;
+static PRCondVar *cvar;
+static PRBool iothread_ready;
+
+static void PR_CALLBACK AbortIO(void *arg)
+{
+ PRStatus rv;
+ PR_Sleep(PR_SecondsToInterval(2));
+ rv = PR_Interrupt((PRThread*)arg);
+ PR_ASSERT(PR_SUCCESS == rv);
+} /* AbortIO */
+
+static void PR_CALLBACK IOThread(void *arg)
+{
+ PRFileDesc *sock, *newsock;
+ PRNetAddr addr;
+
+ sock = PR_OpenTCPSocket(PR_AF_INET6);
+ if (sock == NULL) {
+ fprintf(stderr, "PR_OpenTCPSocket failed\n");
+ exit(1);
+ }
+ memset(&addr, 0, sizeof(addr));
+ if (PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, 0, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_SetNetAddr failed\n");
+ exit(1);
+ }
+ if (PR_Bind(sock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_Bind failed\n");
+ exit(1);
+ }
+ if (PR_Listen(sock, 5) == PR_FAILURE) {
+ fprintf(stderr, "PR_Listen failed\n");
+ exit(1);
+ }
+ /* tell the main thread that we are ready */
+ PR_Lock(lock);
+ iothread_ready = PR_TRUE;
+ PR_NotifyCondVar(cvar);
+ PR_Unlock(lock);
+ newsock = PR_Accept(sock, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (newsock != NULL) {
+ fprintf(stderr, "PR_Accept shouldn't have succeeded\n");
+ exit(1);
+ }
+ if (PR_GetError() != PR_PENDING_INTERRUPT_ERROR) {
+ fprintf(stderr, "PR_Accept failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ printf("PR_Accept() is interrupted as expected\n");
+ if (PR_Close(sock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+}
+
+static void Test(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *iothread, *abortio;
+
+ printf("A %s thread will be interrupted by a %s thread\n",
+ (scope1 == PR_LOCAL_THREAD ? "local" : "global"),
+ (scope2 == PR_LOCAL_THREAD ? "local" : "global"));
+ iothread_ready = PR_FALSE;
+ iothread = PR_CreateThread(
+ PR_USER_THREAD, IOThread, NULL, PR_PRIORITY_NORMAL,
+ scope1, PR_JOINABLE_THREAD, 0);
+ if (iothread == NULL) {
+ fprintf(stderr, "cannot create thread\n");
+ exit(1);
+ }
+ PR_Lock(lock);
+ while (!iothread_ready) {
+ PR_WaitCondVar(cvar, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(lock);
+ abortio = PR_CreateThread(
+ PR_USER_THREAD, AbortIO, iothread, PR_PRIORITY_NORMAL,
+ scope2, PR_JOINABLE_THREAD, 0);
+ if (abortio == NULL) {
+ fprintf(stderr, "cannot create thread\n");
+ exit(1);
+ }
+ if (PR_JoinThread(iothread) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+ if (PR_JoinThread(abortio) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PR_STDIO_INIT();
+ lock = PR_NewLock();
+ if (lock == NULL) {
+ fprintf(stderr, "PR_NewLock failed\n");
+ exit(1);
+ }
+ cvar = PR_NewCondVar(lock);
+ if (cvar == NULL) {
+ fprintf(stderr, "PR_NewCondVar failed\n");
+ exit(1);
+ }
+ /* test all four combinations */
+ Test(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+ Test(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+ Test(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
+ Test(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+ printf("PASSED\n");
+ return 0;
+} /* main */
diff --git a/nsprpub/pr/tests/intrupt.c b/nsprpub/pr/tests/intrupt.c
new file mode 100644
index 0000000000..950d30d469
--- /dev/null
+++ b/nsprpub/pr/tests/intrupt.c
@@ -0,0 +1,379 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: intrupt.c
+ * Purpose: testing thread interrupts
+ */
+
+#include "plgetopt.h"
+#include "prcvar.h"
+#include "prerror.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prio.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prthread.h"
+#include "prtypes.h"
+#include "prnetdb.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#define DEFAULT_TCP_PORT 12500
+
+static PRLock *ml = NULL;
+static PRCondVar *cv = NULL;
+
+static PRBool passed = PR_TRUE;
+static PRBool debug_mode = PR_FALSE;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+static void PR_CALLBACK AbortCV(void *arg)
+{
+ PRStatus rv;
+ PRThread *me = PR_GetCurrentThread();
+
+ /* some other thread (main) is doing the interrupt */
+ PR_Lock(ml);
+ rv = PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
+ if (debug_mode) {
+ printf( "Expected interrupt on wait CV and ");
+ }
+ if (PR_FAILURE == rv)
+ {
+ if (PR_PENDING_INTERRUPT_ERROR == PR_GetError())
+ {
+ if (debug_mode) {
+ printf("got it\n");
+ }
+ }
+ else
+ {
+ if (debug_mode) {
+ printf("got random error\n");
+ }
+ passed = PR_FALSE;
+ }
+ }
+ else
+ {
+ if (debug_mode) {
+ printf("got a successful completion\n");
+ }
+ passed = PR_FALSE;
+ }
+
+ rv = PR_WaitCondVar(cv, 10);
+ if (debug_mode)
+ {
+ printf(
+ "Expected success on wait CV and %s\n",
+ (PR_SUCCESS == rv) ? "got it" : "failed");
+ }
+ passed = ((PR_TRUE == passed) && (PR_SUCCESS == rv)) ? PR_TRUE : PR_FALSE;
+
+ /* interrupt myself, then clear */
+ PR_Interrupt(me);
+ PR_ClearInterrupt();
+ rv = PR_WaitCondVar(cv, 10);
+ if (debug_mode)
+ {
+ printf("Expected success on wait CV and ");
+ if (PR_FAILURE == rv)
+ {
+ printf(
+ "%s\n", (PR_PENDING_INTERRUPT_ERROR == PR_GetError()) ?
+ "got interrupted" : "a random failure");
+ }
+ printf("got it\n");
+ }
+ passed = ((PR_TRUE == passed) && (PR_SUCCESS == rv)) ? PR_TRUE : PR_FALSE;
+
+ /* set, then wait - interrupt - then wait again */
+ PR_Interrupt(me);
+ rv = PR_WaitCondVar(cv, 10);
+ if (debug_mode) {
+ printf( "Expected interrupt on wait CV and ");
+ }
+ if (PR_FAILURE == rv)
+ {
+ if (PR_PENDING_INTERRUPT_ERROR == PR_GetError())
+ {
+ if (debug_mode) {
+ printf("got it\n");
+ }
+ }
+ else
+ {
+ if (debug_mode) {
+ printf("failed\n");
+ }
+ passed = PR_FALSE;
+ }
+ }
+ else
+ {
+ if (debug_mode) {
+ printf("got a successful completion\n");
+ }
+ passed = PR_FALSE;
+ }
+
+ rv = PR_WaitCondVar(cv, 10);
+ if (debug_mode)
+ {
+ printf(
+ "Expected success on wait CV and %s\n",
+ (PR_SUCCESS == rv) ? "got it" : "failed");
+ }
+ passed = ((PR_TRUE == passed) && (PR_SUCCESS == rv)) ? PR_TRUE : PR_FALSE;
+
+ PR_Unlock(ml);
+
+} /* AbortCV */
+
+static void PR_CALLBACK AbortIO(void *arg)
+{
+ PRStatus rv;
+ PR_Sleep(PR_SecondsToInterval(2));
+ rv = PR_Interrupt((PRThread*)arg);
+ PR_ASSERT(PR_SUCCESS == rv);
+} /* AbortIO */
+
+static void PR_CALLBACK AbortJoin(void *arg)
+{
+} /* AbortJoin */
+
+static void setup_listen_socket(PRFileDesc **listner, PRNetAddr *netaddr)
+{
+ PRStatus rv;
+ PRInt16 port = DEFAULT_TCP_PORT;
+
+ *listner = PR_NewTCPSocket();
+ PR_ASSERT(*listner != NULL);
+ memset(netaddr, 0, sizeof(*netaddr));
+ (*netaddr).inet.ip = PR_htonl(PR_INADDR_ANY);
+ (*netaddr).inet.family = PR_AF_INET;
+ do
+ {
+ (*netaddr).inet.port = PR_htons(port);
+ rv = PR_Bind(*listner, netaddr);
+ port += 1;
+ PR_ASSERT(port < (DEFAULT_TCP_PORT + 10));
+ } while (PR_FAILURE == rv);
+
+ rv = PR_Listen(*listner, 5);
+
+ if (PR_GetSockName(*listner, netaddr) < 0) {
+ if (debug_mode) {
+ printf("intrupt: ERROR - PR_GetSockName failed\n");
+ }
+ passed = PR_FALSE;
+ return;
+ }
+
+}
+
+static void PR_CALLBACK IntrBlock(void *arg)
+{
+ PRStatus rv;
+ PRNetAddr netaddr;
+ PRFileDesc *listner;
+
+ /* some other thread (main) is doing the interrupt */
+ /* block the interrupt */
+ PR_BlockInterrupt();
+ PR_Lock(ml);
+ rv = PR_WaitCondVar(cv, PR_SecondsToInterval(4));
+ PR_Unlock(ml);
+ if (debug_mode)
+ {
+ printf("Expected success on wait CV and ");
+ if (PR_FAILURE == rv)
+ {
+ printf(
+ "%s\n", (PR_PENDING_INTERRUPT_ERROR == PR_GetError()) ?
+ "got interrupted" : "got a random failure");
+ } else {
+ printf("got it\n");
+ }
+ }
+ passed = ((PR_TRUE == passed) && (PR_SUCCESS == rv)) ? PR_TRUE : PR_FALSE;
+
+ setup_listen_socket(&listner, &netaddr);
+ PR_UnblockInterrupt();
+ if (PR_Accept(listner, &netaddr, PR_INTERVAL_NO_TIMEOUT) == NULL)
+ {
+ PRInt32 error = PR_GetError();
+ if (debug_mode) {
+ printf("Expected interrupt on PR_Accept() and ");
+ }
+ if (PR_PENDING_INTERRUPT_ERROR == error)
+ {
+ if (debug_mode) {
+ printf("got it\n");
+ }
+ }
+ else
+ {
+ if (debug_mode) {
+ printf("failed\n");
+ }
+ passed = PR_FALSE;
+ }
+ }
+ else
+ {
+ if (debug_mode) {
+ printf("Failed to interrupt PR_Accept()\n");
+ }
+ passed = PR_FALSE;
+ }
+
+ (void)PR_Close(listner); listner = NULL;
+} /* TestIntrBlock */
+
+void PR_CALLBACK Intrupt(void *arg)
+{
+ PRStatus rv;
+ PRNetAddr netaddr;
+ PRFileDesc *listner;
+ PRThread *abortCV, *abortIO, *abortJoin, *intrBlock;
+
+ ml = PR_NewLock();
+ cv = PR_NewCondVar(ml);
+
+ /* Part I */
+ if (debug_mode) {
+ printf("Part I\n");
+ }
+ abortCV = PR_CreateThread(
+ PR_USER_THREAD, AbortCV, 0, PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+
+ PR_Sleep(PR_SecondsToInterval(2));
+ rv = PR_Interrupt(abortCV);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(abortCV);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ /* Part II */
+ if (debug_mode) {
+ printf("Part II\n");
+ }
+ abortJoin = PR_CreateThread(
+ PR_USER_THREAD, AbortJoin, 0, PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ PR_Sleep(PR_SecondsToInterval(2));
+ if (debug_mode) {
+ printf("Expecting to interrupt an exited thread ");
+ }
+ rv = PR_Interrupt(abortJoin);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(abortJoin);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if (debug_mode) {
+ printf("and succeeded\n");
+ }
+
+ /* Part III */
+ if (debug_mode) {
+ printf("Part III\n");
+ }
+ setup_listen_socket(&listner, &netaddr);
+ abortIO = PR_CreateThread(
+ PR_USER_THREAD, AbortIO, PR_GetCurrentThread(), PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+
+ if (PR_Accept(listner, &netaddr, PR_INTERVAL_NO_TIMEOUT) == NULL)
+ {
+ PRInt32 error = PR_GetError();
+ if (debug_mode) {
+ printf("Expected interrupt on PR_Accept() and ");
+ }
+ if (PR_PENDING_INTERRUPT_ERROR == error)
+ {
+ if (debug_mode) {
+ printf("got it\n");
+ }
+ }
+ else
+ {
+ if (debug_mode) {
+ printf("failed\n");
+ }
+ passed = PR_FALSE;
+ }
+ }
+ else
+ {
+ if (debug_mode) {
+ printf("Failed to interrupt PR_Accept()\n");
+ }
+ passed = PR_FALSE;
+ }
+
+ (void)PR_Close(listner); listner = NULL;
+
+ rv = PR_JoinThread(abortIO);
+ PR_ASSERT(PR_SUCCESS == rv);
+ /* Part VI */
+ if (debug_mode) {
+ printf("Part VI\n");
+ }
+ intrBlock = PR_CreateThread(
+ PR_USER_THREAD, IntrBlock, 0, PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+
+ PR_Sleep(PR_SecondsToInterval(2));
+ rv = PR_Interrupt(intrBlock);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(intrBlock);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+} /* Intrupt */
+
+int main(int argc, char **argv)
+{
+ PRThread *intrupt;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dG");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ PR_STDIO_INIT();
+ intrupt = PR_CreateThread(
+ PR_USER_THREAD, Intrupt, NULL, PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ if (intrupt == NULL) {
+ fprintf(stderr, "cannot create thread\n");
+ passed = PR_FALSE;
+ } else {
+ PRStatus rv;
+ rv = PR_JoinThread(intrupt);
+ PR_ASSERT(rv == PR_SUCCESS);
+ }
+ printf("%s\n", ((passed) ? "PASSED" : "FAILED"));
+ return ((passed) ? 0 : 1);
+} /* main */
+
+/* intrupt.c */
diff --git a/nsprpub/pr/tests/io_timeout.c b/nsprpub/pr/tests/io_timeout.c
new file mode 100644
index 0000000000..19d3daeccf
--- /dev/null
+++ b/nsprpub/pr/tests/io_timeout.c
@@ -0,0 +1,269 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** Test socket IO timeouts
+**
+**
+**
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include "nspr.h"
+
+#define NUM_THREADS 1
+#define BASE_PORT 8000
+#define DEFAULT_ACCEPT_TIMEOUT 2
+
+typedef struct threadInfo {
+ PRInt16 id;
+ PRInt16 accept_timeout;
+ PRLock *dead_lock;
+ PRCondVar *dead_cv;
+ PRInt32 *alive;
+} threadInfo;
+
+PRIntn failed_already = 0;
+PRIntn debug_mode = 0;
+
+#define LOCAL_SCOPE_STRING "LOCAL scope"
+#define GLOBAL_SCOPE_STRING "GLOBAL scope"
+#define GLOBAL_BOUND_SCOPE_STRING "GLOBAL_BOUND scope"
+
+void
+thread_main(void *_info)
+{
+ threadInfo *info = (threadInfo *)_info;
+ PRNetAddr listenAddr;
+ PRNetAddr clientAddr;
+ PRFileDesc *listenSock = NULL;
+ PRFileDesc *clientSock;
+ PRStatus rv;
+ PRThreadScope tscope;
+ char *scope_str;
+
+
+ if (debug_mode) {
+ printf("thread %d is alive\n", info->id);
+ }
+ tscope = PR_GetThreadScope(PR_GetCurrentThread());
+
+ switch(tscope) {
+ case PR_LOCAL_THREAD:
+ scope_str = LOCAL_SCOPE_STRING;
+ break;
+ case PR_GLOBAL_THREAD:
+ scope_str = GLOBAL_SCOPE_STRING;
+ break;
+ case PR_GLOBAL_BOUND_THREAD:
+ scope_str = GLOBAL_BOUND_SCOPE_STRING;
+ break;
+ default:
+ PR_NOT_REACHED("Invalid thread scope");
+ break;
+ }
+ printf("thread id %d, scope %s\n", info->id, scope_str);
+
+ listenSock = PR_NewTCPSocket();
+ if (!listenSock) {
+ if (debug_mode) {
+ printf("unable to create listen socket\n");
+ }
+ failed_already=1;
+ goto dead;
+ }
+
+ listenAddr.inet.family = PR_AF_INET;
+ listenAddr.inet.port = PR_htons(BASE_PORT + info->id);
+ listenAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ rv = PR_Bind(listenSock, &listenAddr);
+ if (rv == PR_FAILURE) {
+ if (debug_mode) {
+ printf("unable to bind\n");
+ }
+ failed_already=1;
+ goto dead;
+ }
+
+ rv = PR_Listen(listenSock, 4);
+ if (rv == PR_FAILURE) {
+ if (debug_mode) {
+ printf("unable to listen\n");
+ }
+ failed_already=1;
+ goto dead;
+ }
+
+ if (debug_mode)
+ printf("thread %d going into accept for %d seconds\n",
+ info->id, info->accept_timeout + info->id);
+
+ clientSock = PR_Accept(listenSock, &clientAddr, PR_SecondsToInterval(info->accept_timeout +info->id));
+
+ if (clientSock == NULL) {
+ if (PR_GetError() == PR_IO_TIMEOUT_ERROR) {
+ if (debug_mode) {
+ printf("PR_Accept() timeout worked!\n");
+ printf("TEST PASSED! PR_Accept() returned error %d\n",
+ PR_IO_TIMEOUT_ERROR);
+ }
+ } else {
+ if (debug_mode)
+ printf("TEST FAILED! PR_Accept() returned error %d\n",
+ PR_GetError());
+ failed_already=1;
+ }
+ } else {
+ if (debug_mode) {
+ printf ("TEST FAILED! PR_Accept() succeeded?\n");
+ }
+ failed_already=1;
+ PR_Close(clientSock);
+ }
+
+dead:
+ if (listenSock) {
+ PR_Close(listenSock);
+ }
+ PR_Lock(info->dead_lock);
+ (*info->alive)--;
+ PR_NotifyCondVar(info->dead_cv);
+ PR_Unlock(info->dead_lock);
+
+ if (debug_mode) {
+ printf("thread %d is dead\n", info->id);
+ }
+
+ PR_Free(info);
+}
+
+void
+thread_test(PRThreadScope scope, PRInt32 num_threads)
+{
+ PRInt32 index;
+ PRThread *thr;
+ PRLock *dead_lock;
+ PRCondVar *dead_cv;
+ PRInt32 alive;
+
+ if (debug_mode) {
+ printf("IO Timeout test started with %d threads\n", num_threads);
+ }
+
+ dead_lock = PR_NewLock();
+ dead_cv = PR_NewCondVar(dead_lock);
+ alive = num_threads;
+
+ for (index = 0; index < num_threads; index++) {
+ threadInfo *info = (threadInfo *)PR_Malloc(sizeof(threadInfo));
+
+ info->id = index;
+ info->dead_lock = dead_lock;
+ info->dead_cv = dead_cv;
+ info->alive = &alive;
+ info->accept_timeout = DEFAULT_ACCEPT_TIMEOUT;
+
+ thr = PR_CreateThread( PR_USER_THREAD,
+ thread_main,
+ (void *)info,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_UNJOINABLE_THREAD,
+ 0);
+
+ if (!thr) {
+ printf("Failed to create thread, error = %d(%d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+
+ PR_Lock(dead_lock);
+ alive--;
+ PR_Unlock(dead_lock);
+ }
+ }
+
+ PR_Lock(dead_lock);
+ while(alive) {
+ if (debug_mode) {
+ printf("main loop awake; alive = %d\n", alive);
+ }
+ PR_WaitCondVar(dead_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(dead_lock);
+
+ PR_DestroyCondVar(dead_cv);
+ PR_DestroyLock(dead_lock);
+}
+
+int main(int argc, char **argv)
+{
+ PRInt32 num_threads = 0;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name [-d] [-t <threads>]
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dt:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 't': /* threads to involve */
+ num_threads = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ if (0 == num_threads) {
+ num_threads = NUM_THREADS;
+ }
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_LOW, 0);
+ PR_STDIO_INIT();
+
+ printf("test with global bound thread\n");
+ thread_test(PR_GLOBAL_BOUND_THREAD, num_threads);
+
+ printf("test with local thread\n");
+ thread_test(PR_LOCAL_THREAD, num_threads);
+
+ printf("test with global thread\n");
+ thread_test(PR_GLOBAL_THREAD, num_threads);
+
+ PR_Cleanup();
+
+ if (failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
diff --git a/nsprpub/pr/tests/io_timeoutk.c b/nsprpub/pr/tests/io_timeoutk.c
new file mode 100644
index 0000000000..a058655fdb
--- /dev/null
+++ b/nsprpub/pr/tests/io_timeoutk.c
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** name io_timeoutk.c
+** Description:Test socket IO timeouts (kernel level)
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include "nspr.h"
+
+#define NUM_THREADS 1
+#define BASE_PORT 8000
+#define DEFAULT_ACCEPT_TIMEOUT 2
+
+typedef struct threadInfo {
+ PRInt16 id;
+ PRInt16 accept_timeout;
+ PRLock *dead_lock;
+ PRCondVar *dead_cv;
+ PRInt32 *alive;
+} threadInfo;
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+
+void
+thread_main(void *_info)
+{
+ threadInfo *info = (threadInfo *)_info;
+ PRNetAddr listenAddr;
+ PRNetAddr clientAddr;
+ PRFileDesc *listenSock = NULL;
+ PRFileDesc *clientSock;
+ PRStatus rv;
+
+ if (debug_mode) {
+ printf("thread %d is alive\n", info->id);
+ }
+
+ listenSock = PR_NewTCPSocket();
+ if (!listenSock) {
+ if (debug_mode) {
+ printf("unable to create listen socket\n");
+ }
+ goto dead;
+ }
+
+ listenAddr.inet.family = AF_INET;
+ listenAddr.inet.port = PR_htons(BASE_PORT + info->id);
+ listenAddr.inet.ip = PR_htonl(INADDR_ANY);
+ rv = PR_Bind(listenSock, &listenAddr);
+ if (rv == PR_FAILURE) {
+ if (debug_mode) {
+ printf("unable to bind\n");
+ }
+ goto dead;
+ }
+
+ rv = PR_Listen(listenSock, 4);
+ if (rv == PR_FAILURE) {
+ if (debug_mode) {
+ printf("unable to listen\n");
+ }
+ goto dead;
+ }
+
+ if (debug_mode) printf("thread %d going into accept for %d seconds\n",
+ info->id, info->accept_timeout + info->id);
+
+ clientSock = PR_Accept(listenSock, &clientAddr, PR_SecondsToInterval(info->accept_timeout +info->id));
+
+ if (clientSock == NULL) {
+ if (PR_GetError() == PR_IO_TIMEOUT_ERROR)
+ if (debug_mode) {
+ printf("PR_Accept() timeout worked!\n");
+ printf("TEST FAILED! PR_Accept() returned error %d\n",
+ PR_GetError());
+ }
+ else {
+ failed_already=1;
+ }
+ } else {
+ if (debug_mode) {
+ printf ("TEST FAILED! PR_Accept() succeeded?\n");
+ }
+ else {
+ failed_already=1;
+ }
+ PR_Close(clientSock);
+ }
+
+dead:
+ if (listenSock) {
+ PR_Close(listenSock);
+ }
+ PR_Lock(info->dead_lock);
+ (*info->alive)--;
+ PR_NotifyCondVar(info->dead_cv);
+ PR_Unlock(info->dead_lock);
+
+ if (debug_mode) {
+ printf("thread %d is dead\n", info->id);
+ }
+}
+
+void
+thread_test(PRInt32 scope, PRInt32 num_threads)
+{
+ PRInt32 index;
+ PRThread *thr;
+ PRLock *dead_lock;
+ PRCondVar *dead_cv;
+ PRInt32 alive;
+
+ if (debug_mode) {
+ printf("IO Timeout test started with %d threads\n", num_threads);
+ }
+
+ dead_lock = PR_NewLock();
+ dead_cv = PR_NewCondVar(dead_lock);
+ alive = num_threads;
+
+ for (index = 0; index < num_threads; index++) {
+ threadInfo *info = (threadInfo *)malloc(sizeof(threadInfo));
+
+ info->id = index;
+ info->dead_lock = dead_lock;
+ info->dead_cv = dead_cv;
+ info->alive = &alive;
+ info->accept_timeout = DEFAULT_ACCEPT_TIMEOUT;
+
+ thr = PR_CreateThread( PR_USER_THREAD,
+ thread_main,
+ (void *)info,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_UNJOINABLE_THREAD,
+ 0);
+
+ if (!thr) {
+ PR_Lock(dead_lock);
+ alive--;
+ PR_Unlock(dead_lock);
+ }
+ }
+
+ PR_Lock(dead_lock);
+ while(alive) {
+ if (debug_mode) {
+ printf("main loop awake; alive = %d\n", alive);
+ }
+ PR_WaitCondVar(dead_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(dead_lock);
+}
+
+int main(int argc, char **argv)
+{
+ PRInt32 num_threads;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ if (argc > 2) {
+ num_threads = atoi(argv[2]);
+ }
+ else {
+ num_threads = NUM_THREADS;
+ }
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_LOW, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("kernel level test\n");
+ }
+ thread_test(PR_GLOBAL_THREAD, num_threads);
+
+ PR_Cleanup();
+
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+}
diff --git a/nsprpub/pr/tests/io_timeoutu.c b/nsprpub/pr/tests/io_timeoutu.c
new file mode 100644
index 0000000000..b6d9ccfa36
--- /dev/null
+++ b/nsprpub/pr/tests/io_timeoutu.c
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+/*
+** name io_timeoutu.c
+** Description: Test socket IO timeouts (user level)
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include "nspr.h"
+
+#define NUM_THREADS 1
+#define BASE_PORT 8000
+#define DEFAULT_ACCEPT_TIMEOUT 2
+
+typedef struct threadInfo {
+ PRInt16 id;
+ PRInt16 accept_timeout;
+ PRLock *dead_lock;
+ PRCondVar *dead_cv;
+ PRInt32 *alive;
+} threadInfo;
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+void
+thread_main(void *_info)
+{
+ threadInfo *info = (threadInfo *)_info;
+ PRNetAddr listenAddr;
+ PRNetAddr clientAddr;
+ PRFileDesc *listenSock = NULL;
+ PRFileDesc *clientSock;
+ PRStatus rv;
+
+ if (debug_mode) {
+ printf("thread %d is alive\n", info->id);
+ }
+
+ listenSock = PR_NewTCPSocket();
+ if (!listenSock) {
+ if (debug_mode) {
+ printf("unable to create listen socket\n");
+ }
+ goto dead;
+ }
+
+ listenAddr.inet.family = AF_INET;
+ listenAddr.inet.port = PR_htons(BASE_PORT + info->id);
+ listenAddr.inet.ip = PR_htonl(INADDR_ANY);
+ rv = PR_Bind(listenSock, &listenAddr);
+ if (rv == PR_FAILURE) {
+ if (debug_mode) {
+ printf("unable to bind\n");
+ }
+ goto dead;
+ }
+
+ rv = PR_Listen(listenSock, 4);
+ if (rv == PR_FAILURE) {
+ if (debug_mode) {
+ printf("unable to listen\n");
+ }
+ goto dead;
+ }
+
+ if (debug_mode) printf("thread %d going into accept for %d seconds\n",
+ info->id, info->accept_timeout + info->id);
+
+ clientSock = PR_Accept(
+ listenSock, &clientAddr, PR_SecondsToInterval(
+ info->accept_timeout + info->id));
+
+ if (clientSock == NULL) {
+ if (PR_GetError() == PR_IO_TIMEOUT_ERROR)
+ if (debug_mode) {
+ printf("PR_Accept() timeout worked!\n");
+ printf("TEST FAILED! PR_Accept() returned error %d\n",
+ }
+ PR_GetError());
+ else {
+ failed_already=1;
+ }
+ } else {
+ if (debug_mode) {
+ printf ("TEST FAILED! PR_Accept() succeeded?\n");
+ }
+ else {
+ failed_already=1;
+ }
+ PR_Close(clientSock);
+ }
+
+dead:
+ if (listenSock) {
+ PR_Close(listenSock);
+ }
+ PR_Lock(info->dead_lock);
+ (*info->alive)--;
+ PR_NotifyCondVar(info->dead_cv);
+ PR_Unlock(info->dead_lock);
+
+ if (debug_mode) {
+ printf("thread %d is dead\n", info->id);
+ }
+}
+
+void
+thread_test(PRInt32 scope, PRInt32 num_threads)
+{
+ PRInt32 index;
+ PRThread *thr;
+ PRLock *dead_lock;
+ PRCondVar *dead_cv;
+ PRInt32 alive;
+
+ if (debug_mode) {
+ printf("IO Timeout test started with %d threads\n", num_threads);
+ }
+
+ dead_lock = PR_NewLock();
+ dead_cv = PR_NewCondVar(dead_lock);
+ alive = num_threads;
+
+ for (index = 0; index < num_threads; index++) {
+ threadInfo *info = (threadInfo *)malloc(sizeof(threadInfo));
+
+ info->id = index;
+ info->dead_lock = dead_lock;
+ info->dead_cv = dead_cv;
+ info->alive = &alive;
+ info->accept_timeout = DEFAULT_ACCEPT_TIMEOUT;
+
+ thr = PR_CreateThread( PR_USER_THREAD,
+ thread_main,
+ (void *)info,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_UNJOINABLE_THREAD,
+ 0);
+
+ if (!thr) {
+ PR_Lock(dead_lock);
+ alive--;
+ PR_Unlock(dead_lock);
+ }
+ }
+
+ PR_Lock(dead_lock);
+ while(alive) {
+ if (debug_mode) {
+ printf("main loop awake; alive = %d\n", alive);
+ }
+ PR_WaitCondVar(dead_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(dead_lock);
+}
+
+int main(int argc, char **argv)
+{
+ PRInt32 num_threads;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ if (argc > 2) {
+ num_threads = atoi(argv[2]);
+ }
+ else {
+ num_threads = NUM_THREADS;
+ }
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_LOW, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("user level test\n");
+ }
+ thread_test(PR_LOCAL_THREAD, num_threads);
+
+ PR_Cleanup();
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+
+}
diff --git a/nsprpub/pr/tests/ioconthr.c b/nsprpub/pr/tests/ioconthr.c
new file mode 100644
index 0000000000..3f5adcd572
--- /dev/null
+++ b/nsprpub/pr/tests/ioconthr.c
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This is a test for the io continuation thread machinery
+ * in pthreads.
+ */
+
+#include "nspr.h"
+#include <stdio.h>
+
+int num_threads = 10; /* must be an even number */
+PRThreadScope thread_scope = PR_GLOBAL_THREAD;
+
+void ThreadFunc(void *arg)
+{
+ PRFileDesc *fd = (PRFileDesc *) arg;
+ char buf[1024];
+ PRInt32 nbytes;
+ PRErrorCode err;
+
+ nbytes = PR_Recv(fd, buf, sizeof(buf), 0, PR_SecondsToInterval(20));
+ if (nbytes == -1) {
+ err = PR_GetError();
+ if (err != PR_PENDING_INTERRUPT_ERROR) {
+ fprintf(stderr, "PR_Recv failed: (%d, %d)\n",
+ err, PR_GetOSError());
+ PR_ProcessExit(1);
+ }
+ /*
+ * After getting an I/O interrupt, this thread must
+ * close the fd before it exits due to a limitation
+ * of our NT implementation.
+ */
+ if (PR_Close(fd) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ PR_ProcessExit(1);
+ }
+ } else {
+ fprintf(stderr, "PR_Recv received %d bytes!?\n", nbytes);
+ PR_ProcessExit(1);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc **fds;
+ PRThread **threads;
+ PRIntervalTime start, elapsed;
+ int index;
+
+ fds = (PRFileDesc **) PR_MALLOC(2 * num_threads * sizeof(PRFileDesc *));
+ PR_ASSERT(fds != NULL);
+ threads = (PRThread **) PR_MALLOC(num_threads * sizeof(PRThread *));
+ PR_ASSERT(threads != NULL);
+
+ for (index = 0; index < num_threads; index++) {
+ if (PR_NewTCPSocketPair(&fds[2 * index]) == PR_FAILURE) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ PR_ProcessExit(1);
+ }
+ threads[index] = PR_CreateThread(
+ PR_USER_THREAD, ThreadFunc, fds[2 * index],
+ PR_PRIORITY_NORMAL, thread_scope, PR_JOINABLE_THREAD, 0);
+ if (NULL == threads[index]) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ PR_ProcessExit(1);
+ }
+ }
+
+ /* Let the threads block in PR_Recv */
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ printf("Interrupting the threads\n");
+ fflush(stdout);
+ start = PR_IntervalNow();
+ for (index = 0; index < num_threads; index++) {
+ if (PR_Interrupt(threads[index]) == PR_FAILURE) {
+ fprintf(stderr, "PR_Interrupt failed\n");
+ PR_ProcessExit(1);
+ }
+ }
+ for (index = 0; index < num_threads; index++) {
+ if (PR_JoinThread(threads[index]) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ PR_ProcessExit(1);
+ }
+ }
+ elapsed = (PRIntervalTime)(PR_IntervalNow() - start);
+ printf("Threads terminated in %d milliseconds\n",
+ PR_IntervalToMilliseconds(elapsed));
+ fflush(stdout);
+
+ /* We are being very generous and allow 10 seconds. */
+ if (elapsed >= PR_SecondsToInterval(10)) {
+ fprintf(stderr, "Interrupting threads took longer than 10 seconds!!\n");
+ PR_ProcessExit(1);
+ }
+
+ for (index = 0; index < num_threads; index++) {
+ /* fds[2 * index] was passed to and closed by threads[index]. */
+ if (PR_Close(fds[2 * index + 1]) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ PR_ProcessExit(1);
+ }
+ }
+ PR_DELETE(threads);
+ PR_DELETE(fds);
+ printf("PASS\n");
+ PR_Cleanup();
+ return 0;
+}
diff --git a/nsprpub/pr/tests/ipv6.c b/nsprpub/pr/tests/ipv6.c
new file mode 100644
index 0000000000..cc323e1e6c
--- /dev/null
+++ b/nsprpub/pr/tests/ipv6.c
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prio.h"
+#include "prenv.h"
+#include "prmem.h"
+#include "prlink.h"
+#include "prsystem.h"
+#include "prnetdb.h"
+#include "prprf.h"
+#include "prvrsion.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+#include "obsolete/probslet.h"
+
+#include <string.h>
+
+#define DNS_BUFFER 100
+#define ADDR_BUFFER 100
+#define HOST_BUFFER 1024
+#define PROTO_BUFFER 1500
+
+#define NETADDR_SIZE(addr) \
+ (PR_AF_INET == (addr)->raw.family ? \
+ sizeof((addr)->inet) : sizeof((addr)->ipv6))
+
+static PRFileDesc *err = NULL;
+
+static void Help(void)
+{
+ PR_fprintf(err, "Usage: [-V] [-h]\n");
+ PR_fprintf(err, "\t<nul> Name of host to lookup (default: self)\n");
+ PR_fprintf(err, "\t-V Display runtime version info (default: FALSE)\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+static void DumpAddr(const PRNetAddr* address, const char *msg)
+{
+ PRUint32 *word = (PRUint32*)address;
+ PRUint32 addr_len = sizeof(PRNetAddr);
+ PR_fprintf(err, "%s[%d]\t", msg, NETADDR_SIZE(address));
+ while (addr_len > 0)
+ {
+ PR_fprintf(err, " %08x", *word++);
+ addr_len -= sizeof(PRUint32);
+ }
+ PR_fprintf(err, "\n");
+} /* DumpAddr */
+
+static PRStatus PrintAddress(const PRNetAddr* address)
+{
+ PRNetAddr translation;
+ char buffer[ADDR_BUFFER];
+ PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer));
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err, "PR_NetAddrToString");
+ }
+ else
+ {
+ PR_fprintf(err, "\t%s\n", buffer);
+ memset(&translation, 0, sizeof(translation));
+ rv = PR_StringToNetAddr(buffer, &translation);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err, "PR_StringToNetAddr");
+ }
+ else
+ {
+ PRSize addr_len = NETADDR_SIZE(address);
+ if (0 != memcmp(address, &translation, addr_len))
+ {
+ PR_fprintf(err, "Address translations do not match\n");
+ DumpAddr(address, "original");
+ DumpAddr(&translation, "translate");
+ rv = PR_FAILURE;
+ }
+ }
+ }
+ return rv;
+} /* PrintAddress */
+
+int main(int argc, char **argv)
+{
+ PRStatus rv;
+ PLOptStatus os;
+ PRHostEnt host;
+ PRProtoEnt proto;
+ const char *name = NULL;
+ PRBool failed = PR_FALSE, version = PR_FALSE;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Vh");
+
+ err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 0: /* Name of host to lookup */
+ name = opt->value;
+ break;
+ case 'V': /* Do version discovery */
+ version = PR_TRUE;
+ break;
+ case 'h': /* user wants some guidance */
+ default:
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (version)
+ {
+#if defined(WINNT)
+#define NSPR_LIB "libnspr4"
+#else
+#define NSPR_LIB "nspr4"
+#endif
+ const PRVersionDescription *version_info;
+ char *nspr_path = PR_GetEnv("LD_LIBRARY_PATH");
+ char *nspr_name = PR_GetLibraryName(nspr_path, NSPR_LIB);
+ PRLibrary *runtime = PR_LoadLibrary(nspr_name);
+ if (NULL == runtime) {
+ PL_FPrintError(err, "PR_LoadLibrary");
+ }
+ else
+ {
+ versionEntryPointType versionPoint = (versionEntryPointType)
+ PR_FindSymbol(runtime, "libVersionPoint");
+ if (NULL == versionPoint) {
+ PL_FPrintError(err, "PR_FindSymbol");
+ }
+ else
+ {
+ char buffer[100];
+ PRExplodedTime exploded;
+ version_info = versionPoint();
+ (void)PR_fprintf(err, "Runtime library version information\n");
+ PR_ExplodeTime(
+ version_info->buildTime, PR_GMTParameters, &exploded);
+ (void)PR_FormatTime(
+ buffer, sizeof(buffer), "%d %b %Y %H:%M:%S", &exploded);
+ (void)PR_fprintf(err, " Build time: %s GMT\n", buffer);
+ (void)PR_fprintf(
+ err, " Build time: %s\n", version_info->buildTimeString);
+ (void)PR_fprintf(
+ err, " %s V%u.%u.%u (%s%s%s)\n",
+ version_info->description,
+ version_info->vMajor,
+ version_info->vMinor,
+ version_info->vPatch,
+ (version_info->beta ? " beta " : ""),
+ (version_info->debug ? " debug " : ""),
+ (version_info->special ? " special" : ""));
+ (void)PR_fprintf(err, " filename: %s\n", version_info->filename);
+ (void)PR_fprintf(err, " security: %s\n", version_info->security);
+ (void)PR_fprintf(err, " copyright: %s\n", version_info->copyright);
+ (void)PR_fprintf(err, " comment: %s\n", version_info->comment);
+ }
+ }
+ if (NULL != nspr_name) {
+ PR_FreeLibraryName(nspr_name);
+ }
+ }
+
+ {
+ if (NULL == name)
+ {
+ char *me = (char*)PR_MALLOC(DNS_BUFFER);
+ rv = PR_GetSystemInfo(PR_SI_HOSTNAME, me, DNS_BUFFER);
+ if (PR_FAILURE == rv)
+ {
+ failed = PR_TRUE;
+ PL_FPrintError(err, "PR_GetSystemInfo");
+ return 2;
+ }
+ name = me; /* just leak the storage */
+ }
+ }
+
+ {
+ char buffer[HOST_BUFFER];
+ PR_fprintf(err, "Translating the name %s ...", name);
+
+ rv = PR_GetHostByName(name, buffer, sizeof(buffer), &host);
+ if (PR_FAILURE == rv)
+ {
+ failed = PR_TRUE;
+ PL_FPrintError(err, "PR_GetHostByName");
+ }
+ else
+ {
+ PRIntn index = 0;
+ PRNetAddr address;
+ memset(&address, 0, sizeof(PRNetAddr));
+ PR_fprintf(err, "success .. enumerating results\n");
+ do
+ {
+ index = PR_EnumerateHostEnt(index, &host, 0, &address);
+ if (index > 0) {
+ PrintAddress(&address);
+ }
+ else if (-1 == index)
+ {
+ failed = PR_TRUE;
+ PL_FPrintError(err, "PR_EnumerateHostEnt");
+ }
+ } while (index > 0);
+ }
+ }
+
+
+ {
+ char buffer[PROTO_BUFFER];
+ /*
+ ** Get Proto by name/number
+ */
+ rv = PR_GetProtoByName("tcp", &buffer[1], sizeof(buffer) - 1, &proto);
+ rv = PR_GetProtoByNumber(6, &buffer[3], sizeof(buffer) - 3, &proto);
+ }
+
+ return (failed) ? 1 : 0;
+}
diff --git a/nsprpub/pr/tests/join.c b/nsprpub/pr/tests/join.c
new file mode 100644
index 0000000000..24238e6e05
--- /dev/null
+++ b/nsprpub/pr/tests/join.c
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: dbmalloc1.c
+**
+** Description: Tests PR_SetMallocCountdown PR_ClearMallocCountdown functions.
+**
+** Modification History:
+**
+** 19-May-97 AGarcia - separate the four join tests into different unit test modules.
+** AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+#include "prttools.h"
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/***********************************************************************
+** PRIVATE FUNCTION: Test_Result
+** DESCRIPTION: Used in conjunction with the regress tool, prints out the
+** status of the test case.
+** INPUTS: PASS/FAIL
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+**
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM: Determine what the status is and print accordingly.
+**
+***********************************************************************/
+
+
+static void Test_Result (int result)
+{
+ if (result == PASS) {
+ printf ("PASS\n");
+ }
+ else {
+ printf ("FAIL\n");
+ }
+ exit (1);
+}
+
+
+/*
+ Program to test joining of threads. Two threads are created. One
+ to be waited upon until it has started. The other to join after it has
+ completed.
+*/
+
+
+static void PR_CALLBACK lowPriority(void *arg)
+{
+}
+
+static void PR_CALLBACK highPriority(void *arg)
+{
+}
+
+static void PR_CALLBACK unjoinable(void *arg)
+{
+ PR_Sleep(PR_INTERVAL_NO_TIMEOUT);
+}
+
+void runTest(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *low,*high;
+
+ /* create the low and high priority threads */
+
+ low = PR_CreateThread(PR_USER_THREAD,
+ lowPriority, 0,
+ PR_PRIORITY_LOW,
+ scope1,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!low) {
+ if (debug_mode) {
+ printf("\tcannot create low priority thread\n");
+ }
+ else {
+ Test_Result(FAIL);
+ }
+ return;
+ }
+
+ high = PR_CreateThread(PR_USER_THREAD,
+ highPriority, 0,
+ PR_PRIORITY_HIGH,
+ scope2,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!high) {
+ if (debug_mode) {
+ printf("\tcannot create high priority thread\n");
+ }
+ else {
+ Test_Result(FAIL);
+ }
+ return;
+ }
+
+ /* Do the joining for both threads */
+ if (PR_JoinThread(low) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tcannot join low priority thread\n");
+ }
+ else {
+ Test_Result (FAIL);
+ }
+ return;
+ } else {
+ if (debug_mode) {
+ printf("\tjoined low priority thread\n");
+ }
+ }
+ if (PR_JoinThread(high) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tcannot join high priority thread\n");
+ }
+ else {
+ Test_Result(FAIL);
+ }
+ return;
+ } else {
+ if (debug_mode) {
+ printf("\tjoined high priority thread\n");
+ }
+ }
+}
+
+void joinWithUnjoinable(void)
+{
+ PRThread *thread;
+
+ /* create the unjoinable thread */
+
+ thread = PR_CreateThread(PR_USER_THREAD,
+ unjoinable, 0,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (!thread) {
+ if (debug_mode) {
+ printf("\tcannot create unjoinable thread\n");
+ }
+ else {
+ Test_Result(FAIL);
+ }
+ return;
+ }
+
+ if (PR_JoinThread(thread) == PR_SUCCESS) {
+ if (debug_mode) {
+ printf("\tsuccessfully joined with unjoinable thread?!\n");
+ }
+ else {
+ Test_Result(FAIL);
+ }
+ return;
+ } else {
+ if (debug_mode) {
+ printf("\tcannot join with unjoinable thread, as expected\n");
+ }
+ if (PR_GetError() != PR_INVALID_ARGUMENT_ERROR) {
+ if (debug_mode) {
+ printf("\tWrong error code\n");
+ }
+ else {
+ Test_Result(FAIL);
+ }
+ return;
+ }
+ }
+ if (PR_Interrupt(thread) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tcannot interrupt unjoinable thread\n");
+ }
+ else {
+ Test_Result(FAIL);
+ }
+ return;
+ } else {
+ if (debug_mode) {
+ printf("\tinterrupted unjoinable thread\n");
+ }
+ }
+}
+
+static PRIntn PR_CALLBACK RealMain(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+ printf("User-User test\n");
+ runTest(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+ printf("User-Kernel test\n");
+ runTest(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+ printf("Kernel-User test\n");
+ runTest(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
+ printf("Kernel-Kernel test\n");
+ runTest(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+ printf("Join with unjoinable thread\n");
+ joinWithUnjoinable();
+
+ printf("PASSED\n");
+
+ return 0;
+}
+
+
+
+
+int main(int argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/nsprpub/pr/tests/joinkk.c b/nsprpub/pr/tests/joinkk.c
new file mode 100644
index 0000000000..4e7b2b5e14
--- /dev/null
+++ b/nsprpub/pr/tests/joinkk.c
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: dbmalloc1.c
+**
+** Description: Tests PR_SetMallocCountdown PR_ClearMallocCountdown functions.
+**
+** Modification History:
+**
+** 19-May-97 AGarcia - separate the four join tests into different unit test modules.
+** AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+/*
+ Program to test joining of threads. Two threads are created. One
+ to be waited upon until it has started. The other to join after it has
+ completed.
+*/
+
+
+static void lowPriority(void *arg)
+{
+}
+
+static void highPriority(void *arg)
+{
+}
+
+void runTest(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *low,*high;
+
+ /* create the low and high priority threads */
+
+ low = PR_CreateThread(PR_USER_THREAD,
+ lowPriority, 0,
+ PR_PRIORITY_LOW,
+ scope1,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!low) {
+ if (debug_mode) {
+ printf("\tcannot create low priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ }
+
+ high = PR_CreateThread(PR_USER_THREAD,
+ highPriority, 0,
+ PR_PRIORITY_HIGH,
+ scope2,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!high) {
+ if (debug_mode) {
+ printf("\tcannot create high priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ }
+
+ /* Do the joining for both threads */
+ if (PR_JoinThread(low) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tcannot join low priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ } else {
+ if (debug_mode) {
+ printf("\tjoined low priority thread\n");
+ }
+ }
+ if (PR_JoinThread(high) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tcannot join high priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ } else {
+ if (debug_mode) {
+ printf("\tjoined high priority thread\n");
+ }
+ }
+}
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ if (debug_mode) {
+ printf("Kernel-Kernel test\n");
+ }
+ runTest(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+
+ if(failed_already)
+ {
+ printf("FAIL\n");
+ return 1;
+ }
+ else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+
+}
+
+int main(int argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/nsprpub/pr/tests/joinku.c b/nsprpub/pr/tests/joinku.c
new file mode 100644
index 0000000000..cd83b6cb42
--- /dev/null
+++ b/nsprpub/pr/tests/joinku.c
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: dbmalloc1.c
+**
+** Description: Tests PR_SetMallocCountdown PR_ClearMallocCountdown functions.
+**
+** Modification History:
+**
+** 19-May-97 AGarcia - separate the four join tests into different unit test modules.
+** AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+
+/*
+ Program to test joining of threads. Two threads are created. One
+ to be waited upon until it has started. The other to join after it has
+ completed.
+*/
+
+
+static void lowPriority(void *arg)
+{
+}
+
+static void highPriority(void *arg)
+{
+}
+
+void runTest(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *low,*high;
+
+ /* create the low and high priority threads */
+
+ low = PR_CreateThread(PR_USER_THREAD,
+ lowPriority, 0,
+ PR_PRIORITY_LOW,
+ scope1,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!low) {
+ if (debug_mode) {
+ printf("\tcannot create low priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ }
+
+ high = PR_CreateThread(PR_USER_THREAD,
+ highPriority, 0,
+ PR_PRIORITY_HIGH,
+ scope2,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!high) {
+ if (debug_mode) {
+ printf("\tcannot create high priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ }
+
+ /* Do the joining for both threads */
+ if (PR_JoinThread(low) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tcannot join low priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ } else {
+ if (debug_mode) {
+ printf("\tjoined low priority thread\n");
+ }
+ }
+ if (PR_JoinThread(high) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tcannot join high priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ } else {
+ if (debug_mode) {
+ printf("\tjoined high priority thread\n");
+ }
+ }
+}
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ /* main test */
+
+ if (debug_mode) {
+ printf("Kernel-User test\n");
+ }
+ runTest(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
+
+
+ if(failed_already)
+ {
+ printf("FAIL\n");
+ return 1;
+ }
+ else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+
+}
+
+
+int main(int argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/nsprpub/pr/tests/joinuk.c b/nsprpub/pr/tests/joinuk.c
new file mode 100644
index 0000000000..418c5e0e91
--- /dev/null
+++ b/nsprpub/pr/tests/joinuk.c
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: joinuk.c
+**
+** Description: Join kernel - user
+**
+** Modification History:
+**
+** 19-May-97 AGarcia - separate the four join tests into different unit test modules.
+** AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+/*
+ Program to test joining of threads. Two threads are created. One
+ to be waited upon until it has started. The other to join after it has
+ completed.
+*/
+
+
+static void lowPriority(void *arg)
+{
+}
+
+static void highPriority(void *arg)
+{
+}
+
+void runTest(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *low,*high;
+
+ /* create the low and high priority threads */
+
+ low = PR_CreateThread(PR_USER_THREAD,
+ lowPriority, 0,
+ PR_PRIORITY_LOW,
+ scope1,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!low) {
+ if (debug_mode) {
+ printf("\tcannot create low priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ }
+
+ high = PR_CreateThread(PR_USER_THREAD,
+ highPriority, 0,
+ PR_PRIORITY_HIGH,
+ scope2,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!high) {
+ if (debug_mode) {
+ printf("\tcannot create high priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ }
+
+ /* Do the joining for both threads */
+ if (PR_JoinThread(low) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tcannot join low priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ } else {
+ if (debug_mode) {
+ printf("\tjoined low priority thread\n");
+ }
+ }
+ if (PR_JoinThread(high) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tcannot join high priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ } else {
+ if (debug_mode) {
+ printf("\tjoined high priority thread\n");
+ }
+ }
+}
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ /* main test */
+
+ if (debug_mode) {
+ printf("User-Kernel test\n");
+ }
+ runTest(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+
+
+ if(failed_already)
+ {
+ printf("FAIL\n");
+ return 1;
+ } else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/nsprpub/pr/tests/joinuu.c b/nsprpub/pr/tests/joinuu.c
new file mode 100644
index 0000000000..4d30bfa7ce
--- /dev/null
+++ b/nsprpub/pr/tests/joinuu.c
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: dbmalloc1.c
+**
+** Description: Join tests user - user
+**
+** Modification History:
+**
+** 19-May-97 AGarcia - separate the four join tests into different unit test modules.
+** AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+
+/*
+ Program to test joining of threads. Two threads are created. One
+ to be waited upon until it has started. The other to join after it has
+ completed.
+*/
+
+
+static void lowPriority(void *arg)
+{
+}
+
+static void highPriority(void *arg)
+{
+}
+
+void runTest(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *low,*high;
+
+ /* create the low and high priority threads */
+
+ low = PR_CreateThread(PR_USER_THREAD,
+ lowPriority, 0,
+ PR_PRIORITY_LOW,
+ scope1,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!low) {
+ if (debug_mode) {
+ printf("\tcannot create low priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ }
+
+ high = PR_CreateThread(PR_USER_THREAD,
+ highPriority, 0,
+ PR_PRIORITY_HIGH,
+ scope2,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!high) {
+ if (debug_mode) {
+ printf("\tcannot create high priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ }
+
+ /* Do the joining for both threads */
+ if (PR_JoinThread(low) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tcannot join low priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ } else {
+ if (debug_mode) {
+ printf("\tjoined low priority thread\n");
+ }
+ }
+ if (PR_JoinThread(high) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tcannot join high priority thread\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return;
+ } else {
+ if (debug_mode) {
+ printf("\tjoined high priority thread\n");
+ }
+ }
+}
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ /* main test */
+ if (debug_mode) {
+ printf("User-User test\n");
+ }
+ runTest(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+
+ if(failed_already)
+ {
+ printf("FAIL\n");
+ return 1;
+ } else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+
+
+}
+
+
+int main(int argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/nsprpub/pr/tests/layer.c b/nsprpub/pr/tests/layer.c
new file mode 100644
index 0000000000..8ad32eeaa8
--- /dev/null
+++ b/nsprpub/pr/tests/layer.c
@@ -0,0 +1,471 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prio.h"
+#include "prprf.h"
+#include "prlog.h"
+#include "prnetdb.h"
+#include "prthread.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+#include "prwin16.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+** Testing layering of I/O
+**
+** The layered server
+** A thread that acts as a server. It creates a TCP listener with a dummy
+** layer pushed on top. Then listens for incoming connections. Each connection
+** request for connection will be layered as well, accept one request, echo
+** it back and close.
+**
+** The layered client
+** Pretty much what you'd expect.
+*/
+
+static PRFileDesc *logFile;
+static PRDescIdentity identity;
+static PRNetAddr server_address;
+
+static PRIOMethods myMethods;
+
+typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity;
+
+static PRIntn minor_iterations = 5;
+static PRIntn major_iterations = 1;
+static Verbosity verbosity = quiet;
+
+#ifdef DEBUG
+#define PORT_INC_DO +100
+#else
+#define PORT_INC_DO
+#endif
+#ifdef IS_64
+#define PORT_INC_3264 +200
+#else
+#define PORT_INC_3264
+#endif
+
+static PRUint16 default_port = 12273 PORT_INC_DO PORT_INC_3264;
+
+static PRFileDesc *PushLayer(PRFileDesc *stack)
+{
+ PRFileDesc *layer = PR_CreateIOLayerStub(identity, &myMethods);
+ PRStatus rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer);
+ if (verbosity > quiet) {
+ PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, stack);
+ }
+ PR_ASSERT(PR_SUCCESS == rv);
+ return stack;
+} /* PushLayer */
+
+static PRFileDesc *PushNewLayers(PRFileDesc *stack)
+{
+ PRDescIdentity tmp_identity;
+ PRFileDesc *layer;
+ PRStatus rv;
+
+ /* push a dummy layer */
+ tmp_identity = PR_GetUniqueIdentity("Dummy 1");
+ layer = PR_CreateIOLayerStub(tmp_identity, PR_GetDefaultIOMethods());
+ rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer);
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer,
+ stack);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ /* push a data processing layer */
+ layer = PR_CreateIOLayerStub(identity, &myMethods);
+ rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer);
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer,
+ stack);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ /* push another dummy layer */
+ tmp_identity = PR_GetUniqueIdentity("Dummy 2");
+ layer = PR_CreateIOLayerStub(tmp_identity, PR_GetDefaultIOMethods());
+ rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer);
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer,
+ stack);
+ PR_ASSERT(PR_SUCCESS == rv);
+ return stack;
+} /* PushLayer */
+
+#if 0
+static PRFileDesc *PopLayer(PRFileDesc *stack)
+{
+ PRFileDesc *popped = PR_PopIOLayer(stack, identity);
+ if (verbosity > quiet) {
+ PR_fprintf(logFile, "Popped layer(0x%x) from stack(0x%x)\n", popped, stack);
+ }
+ popped->dtor(popped);
+
+ return stack;
+} /* PopLayer */
+#endif
+
+static void PR_CALLBACK Client(void *arg)
+{
+ PRStatus rv;
+ PRUint8 buffer[100];
+ PRIntn empty_flags = 0;
+ PRIntn bytes_read, bytes_sent;
+ PRFileDesc *stack = (PRFileDesc*)arg;
+
+ /* Initialize the buffer so that Purify won't complain */
+ memset(buffer, 0, sizeof(buffer));
+
+ rv = PR_Connect(stack, &server_address, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(PR_SUCCESS == rv);
+ while (minor_iterations-- > 0)
+ {
+ bytes_sent = PR_Send(
+ stack, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(sizeof(buffer) == bytes_sent);
+ if (verbosity > chatty) {
+ PR_fprintf(logFile, "Client sending %d bytes\n", bytes_sent);
+ }
+ bytes_read = PR_Recv(
+ stack, buffer, bytes_sent, empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty) {
+ PR_fprintf(logFile, "Client receiving %d bytes\n", bytes_read);
+ }
+ PR_ASSERT(bytes_read == bytes_sent);
+ }
+
+ if (verbosity > quiet) {
+ PR_fprintf(logFile, "Client shutting down stack\n");
+ }
+
+ rv = PR_Shutdown(stack, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv);
+} /* Client */
+
+static void PR_CALLBACK Server(void *arg)
+{
+ PRStatus rv;
+ PRUint8 buffer[100];
+ PRFileDesc *service;
+ PRUintn empty_flags = 0;
+ PRIntn bytes_read, bytes_sent;
+ PRFileDesc *stack = (PRFileDesc*)arg;
+ PRNetAddr client_address;
+
+ service = PR_Accept(stack, &client_address, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > quiet) {
+ PR_fprintf(logFile, "Server accepting connection\n");
+ }
+
+ do
+ {
+ bytes_read = PR_Recv(
+ service, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (0 != bytes_read)
+ {
+ if (verbosity > chatty) {
+ PR_fprintf(logFile, "Server receiving %d bytes\n", bytes_read);
+ }
+ PR_ASSERT(bytes_read > 0);
+ bytes_sent = PR_Send(
+ service, buffer, bytes_read, empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty) {
+ PR_fprintf(logFile, "Server sending %d bytes\n", bytes_sent);
+ }
+ PR_ASSERT(bytes_read == bytes_sent);
+ }
+
+ } while (0 != bytes_read);
+
+ if (verbosity > quiet) {
+ PR_fprintf(logFile, "Server shutting down and closing stack\n");
+ }
+ rv = PR_Shutdown(service, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
+
+} /* Server */
+
+static PRInt32 PR_CALLBACK MyRecv(
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ char *b = (char*)buf;
+ PRFileDesc *lo = fd->lower;
+ PRInt32 rv, readin = 0, request = 0;
+ rv = lo->methods->recv(lo, &request, sizeof(request), flags, timeout);
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MyRecv sending permission for %d bytes\n", request);
+ if (0 < rv)
+ {
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MyRecv received permission request for %d bytes\n", request);
+ rv = lo->methods->send(
+ lo, &request, sizeof(request), flags, timeout);
+ if (0 < rv)
+ {
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MyRecv sending permission for %d bytes\n", request);
+ while (readin < request)
+ {
+ rv = lo->methods->recv(
+ lo, b + readin, amount - readin, flags, timeout);
+ if (rv <= 0) {
+ break;
+ }
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MyRecv received %d bytes\n", rv);
+ readin += rv;
+ }
+ rv = readin;
+ }
+ }
+ return rv;
+} /* MyRecv */
+
+static PRInt32 PR_CALLBACK MySend(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ PRFileDesc *lo = fd->lower;
+ const char *b = (const char*)buf;
+ PRInt32 rv, wroteout = 0, request;
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MySend asking permission to send %d bytes\n", amount);
+ rv = lo->methods->send(lo, &amount, sizeof(amount), flags, timeout);
+ if (0 < rv)
+ {
+ rv = lo->methods->recv(
+ lo, &request, sizeof(request), flags, timeout);
+ if (0 < rv)
+ {
+ PR_ASSERT(request == amount);
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MySend got permission to send %d bytes\n", request);
+ while (wroteout < request)
+ {
+ rv = lo->methods->send(
+ lo, b + wroteout, request - wroteout, flags, timeout);
+ if (rv <= 0) {
+ break;
+ }
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MySend wrote %d bytes\n", rv);
+ wroteout += rv;
+ }
+ rv = amount;
+ }
+ }
+ return rv;
+} /* MySend */
+
+static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta)
+{
+ PRIntn verbage = (PRIntn)verbosity + delta;
+ if (verbage < (PRIntn)silent) {
+ verbage = (PRIntn)silent;
+ }
+ else if (verbage > (PRIntn)noisy) {
+ verbage = (PRIntn)noisy;
+ }
+ return (Verbosity)verbage;
+} /* ChangeVerbosity */
+
+int main(int argc, char **argv)
+{
+ PRStatus rv;
+ PRIntn mits;
+ PLOptStatus os;
+ PRFileDesc *client, *service;
+ PRFileDesc *client_stack, *service_stack;
+ PRNetAddr any_address;
+ const char *server_name = NULL;
+ const PRIOMethods *stubMethods;
+ PRThread *client_thread, *server_thread;
+ PRThreadScope thread_scope = PR_LOCAL_THREAD;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dqGC:c:p:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 0:
+ server_name = opt->value;
+ break;
+ case 'd': /* debug mode */
+ if (verbosity < noisy) {
+ verbosity = ChangeVerbosity(verbosity, 1);
+ }
+ break;
+ case 'q': /* debug mode */
+ if (verbosity > silent) {
+ verbosity = ChangeVerbosity(verbosity, -1);
+ }
+ break;
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'C': /* number of threads waiting */
+ major_iterations = atoi(opt->value);
+ break;
+ case 'c': /* number of client threads */
+ minor_iterations = atoi(opt->value);
+ break;
+ case 'p': /* default port */
+ default_port = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ PR_STDIO_INIT();
+
+ logFile = PR_GetSpecialFD(PR_StandardError);
+
+ identity = PR_GetUniqueIdentity("Dummy");
+ stubMethods = PR_GetDefaultIOMethods();
+
+ /*
+ ** The protocol we're going to implement is one where in order to initiate
+ ** a send, the sender must first solicit permission. Therefore, every
+ ** send is really a send - receive - send sequence.
+ */
+ myMethods = *stubMethods; /* first get the entire batch */
+ myMethods.recv = MyRecv; /* then override the ones we care about */
+ myMethods.send = MySend; /* then override the ones we care about */
+
+ if (NULL == server_name)
+ rv = PR_InitializeNetAddr(
+ PR_IpAddrLoopback, default_port, &server_address);
+ else
+ {
+ rv = PR_StringToNetAddr(server_name, &server_address);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_InitializeNetAddr(
+ PR_IpAddrNull, default_port, &server_address);
+ }
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ /* one type w/o layering */
+
+ mits = minor_iterations;
+ while (major_iterations-- > 0)
+ {
+ if (verbosity > silent) {
+ PR_fprintf(logFile, "Beginning non-layered test\n");
+ }
+ client = PR_NewTCPSocket(); PR_ASSERT(NULL != client);
+ service = PR_NewTCPSocket(); PR_ASSERT(NULL != service);
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv);
+
+ minor_iterations = mits;
+ server_thread = PR_CreateThread(
+ PR_USER_THREAD, Server, service,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != server_thread);
+
+ client_thread = PR_CreateThread(
+ PR_USER_THREAD, Client, client,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != client_thread);
+
+ rv = PR_JoinThread(client_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(server_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
+ if (verbosity > silent) {
+ PR_fprintf(logFile, "Ending non-layered test\n");
+ }
+
+ /* with layering */
+ if (verbosity > silent) {
+ PR_fprintf(logFile, "Beginning layered test\n");
+ }
+ client = PR_NewTCPSocket(); PR_ASSERT(NULL != client);
+ PushLayer(client);
+ service = PR_NewTCPSocket(); PR_ASSERT(NULL != service);
+ PushLayer(service);
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv);
+
+ minor_iterations = mits;
+ server_thread = PR_CreateThread(
+ PR_USER_THREAD, Server, service,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != server_thread);
+
+ client_thread = PR_CreateThread(
+ PR_USER_THREAD, Client, client,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != client_thread);
+
+ rv = PR_JoinThread(client_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(server_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
+ /* with layering, using new style stack */
+ if (verbosity > silent)
+ PR_fprintf(logFile,
+ "Beginning layered test with new style stack\n");
+ client = PR_NewTCPSocket(); PR_ASSERT(NULL != client);
+ client_stack = PR_CreateIOLayer(client);
+ PushNewLayers(client_stack);
+ service = PR_NewTCPSocket(); PR_ASSERT(NULL != service);
+ service_stack = PR_CreateIOLayer(service);
+ PushNewLayers(service_stack);
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv);
+
+ minor_iterations = mits;
+ server_thread = PR_CreateThread(
+ PR_USER_THREAD, Server, service_stack,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != server_thread);
+
+ client_thread = PR_CreateThread(
+ PR_USER_THREAD, Client, client_stack,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != client_thread);
+
+ rv = PR_JoinThread(client_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(server_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Close(client_stack); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Close(service_stack); PR_ASSERT(PR_SUCCESS == rv);
+ if (verbosity > silent) {
+ PR_fprintf(logFile, "Ending layered test\n");
+ }
+ }
+ return 0;
+} /* main */
+
+/* layer.c */
diff --git a/nsprpub/pr/tests/lazyinit.c b/nsprpub/pr/tests/lazyinit.c
new file mode 100644
index 0000000000..2a910f2051
--- /dev/null
+++ b/nsprpub/pr/tests/lazyinit.c
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: lazyinit.c
+** Description: Testing lazy initialization
+**
+** Since you only get to initialize once, you have to rerun the test
+** for each test case. The test cases are numbered. If you want to
+** add more tests, take the next number and add it to the switch
+** statement.
+**
+** This test is problematic on systems that don't support the notion
+** of console output. The workarounds to emulate that feature include
+** initializations themselves, which defeats the purpose here.
+*/
+
+#include "prcvar.h"
+#include "prenv.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prio.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prthread.h"
+#include "prtypes.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void PR_CALLBACK lazyEntry(void *arg)
+{
+ PR_ASSERT(NULL == arg);
+} /* lazyEntry */
+
+
+int main(int argc, char **argv)
+{
+ PRUintn pdkey;
+ PRStatus status;
+ char *path = NULL;
+ PRDir *dir = NULL;
+ PRLock *ml = NULL;
+ PRCondVar *cv = NULL;
+ PRThread *thread = NULL;
+ PRIntervalTime interval = 0;
+ PRFileDesc *file, *udp, *tcp, *pair[2];
+ PRIntn test;
+
+ if ( argc < 2)
+ {
+ test = 0;
+ }
+ else {
+ test = atoi(argv[1]);
+ }
+
+ switch (test)
+ {
+ case 0: ml = PR_NewLock();
+ break;
+
+ case 1: interval = PR_SecondsToInterval(1);
+ break;
+
+ case 2: thread = PR_CreateThread(
+ PR_USER_THREAD, lazyEntry, NULL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ break;
+
+ case 3: file = PR_Open("./tmp-", PR_RDONLY, 0);
+ break;
+
+ case 4: udp = PR_NewUDPSocket();
+ break;
+
+ case 5: tcp = PR_NewTCPSocket();
+ break;
+
+ case 6: dir = PR_OpenDir("./tmp-");
+ break;
+
+ case 7: (void)PR_NewThreadPrivateIndex(&pdkey, NULL);
+ break;
+
+ case 8: path = PR_GetEnv("PATH");
+ break;
+
+ case 9: status = PR_NewTCPSocketPair(pair);
+ break;
+
+ case 10: PR_SetConcurrency(2);
+ break;
+
+ default:
+ printf(
+ "lazyinit: unrecognized command line argument: %s\n",
+ argv[1] );
+ printf( "FAIL\n" );
+ exit( 1 );
+ break;
+ } /* switch() */
+ return 0;
+} /* Lazy */
+
+/* lazyinit.c */
diff --git a/nsprpub/pr/tests/libfilename.c b/nsprpub/pr/tests/libfilename.c
new file mode 100644
index 0000000000..09d0980de3
--- /dev/null
+++ b/nsprpub/pr/tests/libfilename.c
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: libfilename.c
+**
+** Description: test PR_GetLibraryFilePathname.
+**
+***********************************************************************/
+
+#include "nspr.h"
+#include "pprio.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRBool debug_mode = PR_FALSE;
+
+static PRStatus RunTest(const char *name, PRFuncPtr addr)
+{
+ char *pathname;
+ PRFileDesc *fd;
+
+ pathname = PR_GetLibraryFilePathname(name, addr);
+ if (pathname == NULL) {
+ fprintf(stderr, "PR_GetLibraryFilePathname failed\n");
+ /* we let this test pass if this function is not implemented */
+ if (PR_GetError() == PR_NOT_IMPLEMENTED_ERROR) {
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+ }
+
+ if (debug_mode) {
+ printf("Pathname is %s\n", pathname);
+ }
+ fd = PR_OpenFile(pathname, PR_RDONLY, 0);
+ if (fd == NULL) {
+ fprintf(stderr, "PR_Open failed: %d\n", (int)PR_GetError());
+ return PR_FAILURE;
+ }
+ if (PR_Close(fd) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed: %d\n", (int)PR_GetError());
+ return PR_FAILURE;
+ }
+ PR_Free(pathname);
+ return PR_SUCCESS;
+}
+
+int main(int argc, char **argv)
+{
+ char *name;
+ PRFuncPtr addr;
+ PRLibrary *lib;
+ PRBool failed = PR_FALSE;
+
+ if (argc >= 2 && strcmp(argv[1], "-d") == 0) {
+ debug_mode = PR_TRUE;
+ }
+
+ /* First test a library that is implicitly linked. */
+#ifdef WINNT
+ name = PR_Malloc(strlen("libnspr4.dll")+1);
+ strcpy(name, "libnspr4.dll");
+#else
+ name = PR_GetLibraryName(NULL, "nspr4");
+#endif
+ addr = (PRFuncPtr)PR_GetTCPMethods()->close;
+ if (RunTest(name, addr) == PR_FAILURE) {
+ failed = PR_TRUE;
+ }
+ PR_FreeLibraryName(name);
+
+ /* Next test a library that is dynamically loaded. */
+ name = PR_GetLibraryName("dll", "my");
+ if (debug_mode) {
+ printf("Loading library %s\n", name);
+ }
+ lib = PR_LoadLibrary(name);
+ if (!lib) {
+ fprintf(stderr, "PR_LoadLibrary failed\n");
+ exit(1);
+ }
+ PR_FreeLibraryName(name);
+ name = PR_GetLibraryName(NULL, "my");
+ addr = PR_FindFunctionSymbol(lib, "My_GetValue");
+ if (RunTest(name, addr) == PR_FAILURE) {
+ failed = PR_TRUE;
+ }
+ PR_FreeLibraryName(name);
+ PR_UnloadLibrary(lib);
+ if (failed) {
+ printf("FAIL\n");
+ return 1;
+ }
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/lltest.c b/nsprpub/pr/tests/lltest.c
new file mode 100644
index 0000000000..26f7d8eadf
--- /dev/null
+++ b/nsprpub/pr/tests/lltest.c
@@ -0,0 +1,969 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** testll.c -- test suite for 64bit integer (longlong) operations
+**
+** Summary: testll [-d] | [-h]
+**
+** Where:
+** -d set debug mode on; displays individual test failures
+** -v verbose mode; displays progress in test, plus -d
+** -h gives usage message.
+**
+** Description:
+** lltest.c tests the functions defined in NSPR 2.0's prlong.h.
+**
+** Successive tests begin to depend on other LL functions working
+** correctly. So, ... Do not change the order of the tests as run
+** from main().
+**
+** Caveats:
+** Do not even begin to think that this is an exhaustive test!
+**
+** These tests try a little of everything, but not all boundary
+** conditions and limits are tested.
+** You want better coverage? ... Add it.
+**
+** ---
+** Author: Lawrence Hardiman <larryh@netscape.com>.
+** ---
+** Revision History:
+** 01-Oct-1997. Original implementation.
+**
+*/
+
+#include "nspr.h"
+#include "plgetopt.h"
+
+/* --- Local Definitions --- */
+#define ReportProgress(m) if (verboseMode) PR_fprintf(output, (m));
+
+
+/* --- Global variables --- */
+static PRIntn failedAlready = 0;
+static PRFileDesc* output = NULL;
+static PRBool debugMode = PR_FALSE;
+static PRBool verboseMode = PR_FALSE;
+
+/*
+** Constants used in tests.
+*/
+const PRInt64 bigZero = LL_INIT( 0, 0 );
+const PRInt64 bigOne = LL_INIT( 0, 1 );
+const PRInt64 bigTwo = LL_INIT( 0, 2 );
+const PRInt64 bigSixTeen = LL_INIT( 0, 16 );
+const PRInt64 bigThirtyTwo = LL_INIT( 0, 32 );
+const PRInt64 bigMinusOne = LL_INIT( 0xffffffff, 0xffffffff );
+const PRInt64 bigMinusTwo = LL_INIT( 0xffffffff, 0xfffffffe );
+const PRInt64 bigNumber = LL_INIT( 0x7fffffff, 0xffffffff );
+const PRInt64 bigMinusNumber = LL_INIT( 0x80000000, 0x00000001 );
+const PRInt64 bigMaxInt32 = LL_INIT( 0x00000000, 0x7fffffff );
+const PRInt64 big2To31 = LL_INIT( 0x00000000, 0x80000000 );
+const PRUint64 bigZeroFox = LL_INIT( 0x00000000, 0xffffffff );
+const PRUint64 bigFoxFox = LL_INIT( 0xffffffff, 0xffffffff );
+const PRUint64 bigFoxZero = LL_INIT( 0xffffffff, 0x00000000 );
+const PRUint64 bigEightZero = LL_INIT( 0x80000000, 0x00000000 );
+const PRUint64 big64K = LL_INIT( 0x00000000, 0x00010000 );
+const PRInt64 bigInt0 = LL_INIT( 0x01a00000, 0x00001000 );
+const PRInt64 bigInt1 = LL_INIT( 0x01a00000, 0x00001100 );
+const PRInt64 bigInt2 = LL_INIT( 0x01a00000, 0x00000100 );
+const PRInt64 bigInt3 = LL_INIT( 0x01a00001, 0x00001000 );
+const PRInt64 bigInt4 = LL_INIT( 0x01a00001, 0x00001100 );
+const PRInt64 bigInt5 = LL_INIT( 0x01a00001, 0x00000100 );
+const PRInt64 bigInt6 = LL_INIT( 0xb1a00000, 0x00001000 );
+const PRInt64 bigInt7 = LL_INIT( 0xb1a00000, 0x00001100 );
+const PRInt64 bigInt8 = LL_INIT( 0xb1a00000, 0x00000100 );
+const PRInt64 bigInt9 = LL_INIT( 0xb1a00001, 0x00001000 );
+const PRInt64 bigInt10 = LL_INIT( 0xb1a00001, 0x00001100 );
+const PRInt64 bigInt11 = LL_INIT( 0xb1a00001, 0x00000100 );
+const PRInt32 one = 1l;
+const PRInt32 minusOne = -1l;
+const PRInt32 sixteen = 16l;
+const PRInt32 thirtyTwo = 32l;
+const PRInt32 sixtyThree = 63l;
+
+/*
+** SetFailed() -- Report individual test failure
+**
+*/
+static void
+SetFailed( char *what, char *how )
+{
+ failedAlready = 1;
+ if ( debugMode ) {
+ PR_fprintf(output, "%s: failed: %s\n", what, how );
+ }
+ return;
+}
+
+static void
+ResultFailed( char *what, char *how, PRInt64 expected, PRInt64 got)
+{
+ if ( debugMode)
+ {
+ SetFailed( what, how );
+ PR_fprintf(output, "Expected: 0x%llx Got: 0x%llx\n", expected, got );
+ }
+ return;
+}
+
+
+/*
+** TestAssignment() -- Test the assignment
+*/
+static void TestAssignment( void )
+{
+ PRInt64 zero = LL_Zero();
+ PRInt64 min = LL_MinInt();
+ PRInt64 max = LL_MaxInt();
+ if (!LL_EQ(zero, bigZero)) {
+ SetFailed("LL_EQ(zero, bigZero)", "!=");
+ }
+ if (!LL_CMP(max, >, min)) {
+ SetFailed("LL_CMP(max, >, min)", "!>");
+ }
+}
+
+/*
+** TestComparisons() -- Test the longlong comparison operations
+*/
+static void
+TestComparisons( void )
+{
+ ReportProgress("Testing Comparisons Operations\n");
+
+ /* test for zero */
+ if ( !LL_IS_ZERO( bigZero )) {
+ SetFailed( "LL_IS_ZERO", "Zero is not zero" );
+ }
+
+ if ( LL_IS_ZERO( bigOne )) {
+ SetFailed( "LL_IS_ZERO", "One tests as zero" );
+ }
+
+ if ( LL_IS_ZERO( bigMinusOne )) {
+ SetFailed( "LL_IS_ZERO", "Minus One tests as zero" );
+ }
+
+ /* test equal */
+ if ( !LL_EQ( bigZero, bigZero )) {
+ SetFailed( "LL_EQ", "zero EQ zero");
+ }
+
+ if ( !LL_EQ( bigOne, bigOne )) {
+ SetFailed( "LL_EQ", "one EQ one" );
+ }
+
+ if ( !LL_EQ( bigNumber, bigNumber )) {
+ SetFailed( "LL_EQ", "bigNumber EQ bigNumber" );
+ }
+
+ if ( !LL_EQ( bigMinusOne, bigMinusOne )) {
+ SetFailed( "LL_EQ", "minus one EQ minus one");
+ }
+
+ if ( LL_EQ( bigZero, bigOne )) {
+ SetFailed( "LL_EQ", "zero EQ one");
+ }
+
+ if ( LL_EQ( bigOne, bigZero )) {
+ SetFailed( "LL_EQ", "one EQ zero" );
+ }
+
+ if ( LL_EQ( bigMinusOne, bigOne )) {
+ SetFailed( "LL_EQ", "minus one EQ one");
+ }
+
+ if ( LL_EQ( bigNumber, bigOne )) {
+ SetFailed( "LL_EQ", "bigNumber EQ one");
+ }
+
+ /* test not equal */
+ if ( LL_NE( bigZero, bigZero )) {
+ SetFailed( "LL_NE", "0 NE 0");
+ }
+
+ if ( LL_NE( bigOne, bigOne )) {
+ SetFailed( "LL_NE", "1 NE 1");
+ }
+
+ if ( LL_NE( bigMinusOne, bigMinusOne )) {
+ SetFailed( "LL_NE", "-1 NE -1");
+ }
+
+ if ( LL_NE( bigNumber, bigNumber )) {
+ SetFailed( "LL_NE", "n NE n");
+ }
+
+ if ( LL_NE( bigMinusNumber, bigMinusNumber )) {
+ SetFailed( "LL_NE", "-n NE -n");
+ }
+
+ if ( !LL_NE( bigZero, bigOne)) {
+ SetFailed( "LL_NE", "0 NE 1");
+ }
+
+ if ( !LL_NE( bigOne, bigMinusNumber)) {
+ SetFailed( "LL_NE", "1 NE -n");
+ }
+
+ /* Greater than or equal to zero */
+ if ( !LL_GE_ZERO( bigZero )) {
+ SetFailed( "LL_GE_ZERO", "0");
+ }
+
+ if ( !LL_GE_ZERO( bigOne )) {
+ SetFailed( "LL_GE_ZERO", "1");
+ }
+
+ if ( !LL_GE_ZERO( bigNumber )) {
+ SetFailed( "LL_GE_ZERO", "n");
+ }
+
+ if ( LL_GE_ZERO( bigMinusOne )) {
+ SetFailed( "LL_GE_ZERO", "-1");
+ }
+
+ if ( LL_GE_ZERO( bigMinusNumber )) {
+ SetFailed( "LL_GE_ZERO", "-n");
+ }
+
+ /* Algebraic Compare two values */
+ if ( !LL_CMP( bigZero, ==, bigZero )) {
+ SetFailed( "LL_CMP", "0 == 0");
+ }
+
+ if ( LL_CMP( bigZero, >, bigZero )) {
+ SetFailed( "LL_CMP", "0 > 0");
+ }
+
+ if ( LL_CMP( bigZero, <, bigZero )) {
+ SetFailed( "LL_CMP", "0 < 0");
+ }
+
+ if ( LL_CMP( bigNumber, <, bigOne )) {
+ SetFailed( "LL_CMP", "n < 1");
+ }
+
+ if ( !LL_CMP( bigNumber, >, bigOne )) {
+ SetFailed( "LL_CMP", "n <= 1");
+ }
+
+ if ( LL_CMP( bigOne, >, bigNumber )) {
+ SetFailed( "LL_CMP", "1 > n");
+ }
+
+ if ( LL_CMP( bigMinusNumber, >, bigNumber )) {
+ SetFailed( "LL_CMP", "-n > n");
+ }
+
+ if ( LL_CMP( bigNumber, !=, bigNumber)) {
+ SetFailed( "LL_CMP", "n != n");
+ }
+
+ if ( !LL_CMP( bigMinusOne, >, bigMinusTwo )) {
+ SetFailed( "LL_CMP", "-1 <= -2");
+ }
+
+ if ( !LL_CMP( bigMaxInt32, <, big2To31 )) {
+ SetFailed( "LL_CMP", "Max 32-bit signed int >= 2^31");
+ }
+
+ /* Two positive numbers */
+ if ( !LL_CMP( bigInt0, <=, bigInt0 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ if ( !LL_CMP( bigInt0, <=, bigInt1 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ if ( LL_CMP( bigInt0, <=, bigInt2 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ if ( !LL_CMP( bigInt0, <=, bigInt3 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ if ( !LL_CMP( bigInt0, <=, bigInt4 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ if ( !LL_CMP( bigInt0, <=, bigInt5 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ /* Two negative numbers */
+ if ( !LL_CMP( bigInt6, <=, bigInt6 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ if ( !LL_CMP( bigInt6, <=, bigInt7 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ if ( LL_CMP( bigInt6, <=, bigInt8 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ if ( !LL_CMP( bigInt6, <=, bigInt9 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ if ( !LL_CMP( bigInt6, <=, bigInt10 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ if ( !LL_CMP( bigInt6, <=, bigInt11 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ /* One positive, one negative */
+ if ( LL_CMP( bigInt0, <=, bigInt6 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ if ( LL_CMP( bigInt0, <=, bigInt7 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ if ( LL_CMP( bigInt0, <=, bigInt8 )) {
+ SetFailed( "LL_CMP", "LL_CMP(<=) failed");
+ }
+
+ /* Bitwise Compare two numbers */
+ if ( !LL_UCMP( bigZero, ==, bigZero )) {
+ SetFailed( "LL_UCMP", "0 == 0");
+ }
+
+ if ( LL_UCMP( bigZero, >, bigZero )) {
+ SetFailed( "LL_UCMP", "0 > 0");
+ }
+
+ if ( LL_UCMP( bigZero, <, bigZero )) {
+ SetFailed( "LL_UCMP", "0 < 0");
+ }
+
+ if ( LL_UCMP( bigNumber, <, bigOne )) {
+ SetFailed( "LL_UCMP", "n < 1");
+ }
+
+ if ( !LL_UCMP( bigNumber, >, bigOne )) {
+ SetFailed( "LL_UCMP", "n < 1");
+ }
+
+ if ( LL_UCMP( bigOne, >, bigNumber )) {
+ SetFailed( "LL_UCMP", "1 > n");
+ }
+
+ if ( LL_UCMP( bigMinusNumber, <, bigNumber )) {
+ SetFailed( "LL_UCMP", "-n < n");
+ }
+
+ /* Two positive numbers */
+ if ( !LL_UCMP( bigInt0, <=, bigInt0 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ if ( !LL_UCMP( bigInt0, <=, bigInt1 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ if ( LL_UCMP( bigInt0, <=, bigInt2 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ if ( !LL_UCMP( bigInt0, <=, bigInt3 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ if ( !LL_UCMP( bigInt0, <=, bigInt4 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ if ( !LL_UCMP( bigInt0, <=, bigInt5 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ /* Two negative numbers */
+ if ( !LL_UCMP( bigInt6, <=, bigInt6 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ if ( !LL_UCMP( bigInt6, <=, bigInt7 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ if ( LL_UCMP( bigInt6, <=, bigInt8 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ if ( !LL_UCMP( bigInt6, <=, bigInt9 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ if ( !LL_UCMP( bigInt6, <=, bigInt10 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ if ( !LL_UCMP( bigInt6, <=, bigInt11 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ /* One positive, one negative */
+ if ( !LL_UCMP( bigInt0, <=, bigInt6 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ if ( !LL_UCMP( bigInt0, <=, bigInt7 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ if ( !LL_UCMP( bigInt0, <=, bigInt8 )) {
+ SetFailed( "LL_UCMP", "LL_UCMP(<=) failed");
+ }
+
+ return;
+}
+
+/*
+** TestLogicalOperations() -- Tests for AND, OR, ...
+**
+*/
+static void
+TestLogicalOperations( void )
+{
+ PRUint64 result, result2;
+
+ ReportProgress("Testing Logical Operations\n");
+
+ /* Test AND */
+ LL_AND( result, bigZero, bigZero );
+ if ( !LL_IS_ZERO( result )) {
+ ResultFailed( "LL_AND", "0 & 0", bigZero, result );
+ }
+
+ LL_AND( result, bigOne, bigOne );
+ if ( LL_IS_ZERO( result )) {
+ ResultFailed( "LL_AND", "1 & 1", bigOne, result );
+ }
+
+ LL_AND( result, bigZero, bigOne );
+ if ( !LL_IS_ZERO( result )) {
+ ResultFailed( "LL_AND", "1 & 1", bigZero, result );
+ }
+
+ LL_AND( result, bigMinusOne, bigMinusOne );
+ if ( !LL_UCMP( result, ==, bigMinusOne )) {
+ ResultFailed( "LL_AND", "-1 & -1", bigMinusOne, result );
+ }
+
+ /* test OR */
+ LL_OR( result, bigZero, bigZero );
+ if ( !LL_IS_ZERO( result )) {
+ ResultFailed( "LL_OR", "0 | 1", bigZero, result);
+ }
+
+ LL_OR( result, bigZero, bigOne );
+ if ( LL_IS_ZERO( result )) {
+ ResultFailed( "LL_OR", "0 | 1", bigOne, result );
+ }
+
+ LL_OR( result, bigZero, bigMinusNumber );
+ if ( !LL_UCMP( result, ==, bigMinusNumber )) {
+ ResultFailed( "LL_OR", "0 | -n", bigMinusNumber, result);
+ }
+
+ LL_OR( result, bigMinusNumber, bigZero );
+ if ( !LL_UCMP( result, ==, bigMinusNumber )) {
+ ResultFailed( "LL_OR", "-n | 0", bigMinusNumber, result );
+ }
+
+ /* test XOR */
+ LL_XOR( result, bigZero, bigZero );
+ if ( LL_UCMP( result, !=, bigZero )) {
+ ResultFailed( "LL_XOR", "0 ^ 0", bigZero, result);
+ }
+
+ LL_XOR( result, bigOne, bigZero );
+ if ( LL_UCMP( result, !=, bigOne )) {
+ ResultFailed( "LL_XOR", "1 ^ 0", bigZero, result );
+ }
+
+ LL_XOR( result, bigMinusNumber, bigZero );
+ if ( LL_UCMP( result, !=, bigMinusNumber )) {
+ ResultFailed( "LL_XOR", "-n ^ 0", bigMinusNumber, result );
+ }
+
+ LL_XOR( result, bigMinusNumber, bigMinusNumber );
+ if ( LL_UCMP( result, !=, bigZero )) {
+ ResultFailed( "LL_XOR", "-n ^ -n", bigMinusNumber, result);
+ }
+
+ /* test OR2. */
+ result = bigZero;
+ LL_OR2( result, bigOne );
+ if ( LL_UCMP( result, !=, bigOne )) {
+ ResultFailed( "LL_OR2", "(r=0) |= 1", bigOne, result);
+ }
+
+ result = bigOne;
+ LL_OR2( result, bigNumber );
+ if ( LL_UCMP( result, !=, bigNumber )) {
+ ResultFailed( "LL_OR2", "(r=1) |= n", bigNumber, result);
+ }
+
+ result = bigMinusNumber;
+ LL_OR2( result, bigMinusNumber );
+ if ( LL_UCMP( result, !=, bigMinusNumber )) {
+ ResultFailed( "LL_OR2", "(r=-n) |= -n", bigMinusNumber, result);
+ }
+
+ /* test NOT */
+ LL_NOT( result, bigMinusNumber);
+ LL_NOT( result2, result);
+ if ( LL_UCMP( result2, !=, bigMinusNumber )) {
+ ResultFailed( "LL_NOT", "r != ~(~-n)", bigMinusNumber, result);
+ }
+
+ /* test Negation */
+ LL_NEG( result, bigMinusNumber );
+ LL_NEG( result2, result );
+ if ( LL_CMP( result2, !=, bigMinusNumber )) {
+ ResultFailed( "LL_NEG", "r != -(-(-n))", bigMinusNumber, result);
+ }
+
+ return;
+}
+
+
+
+/*
+** TestConversion() -- Test Conversion Operations
+**
+*/
+static void
+TestConversion( void )
+{
+ PRInt64 result;
+ PRInt64 resultU;
+ PRInt32 result32;
+ PRUint32 resultU32;
+ float resultF;
+ PRFloat64 resultD;
+
+ ReportProgress("Testing Conversion Operations\n");
+
+ /* LL_L2I -- Convert to signed 32bit */
+ LL_L2I(result32, bigOne );
+ if ( result32 != one ) {
+ SetFailed( "LL_L2I", "r != 1");
+ }
+
+ LL_L2I(result32, bigMinusOne );
+ if ( result32 != minusOne ) {
+ SetFailed( "LL_L2I", "r != -1");
+ }
+
+ /* LL_L2UI -- Convert 64bit to unsigned 32bit */
+ LL_L2UI( resultU32, bigMinusOne );
+ if ( resultU32 != (PRUint32) minusOne ) {
+ SetFailed( "LL_L2UI", "r != -1");
+ }
+
+ LL_L2UI( resultU32, bigOne );
+ if ( resultU32 != (PRUint32) one ) {
+ SetFailed( "LL_L2UI", "r != 1");
+ }
+
+ /* LL_L2F -- Convert to 32bit floating point */
+ LL_L2F( resultF, bigOne );
+ if ( resultF != 1.0 ) {
+ SetFailed( "LL_L2F", "r != 1.0");
+ }
+
+ LL_L2F( resultF, bigMinusOne );
+ if ( resultF != -1.0 ) {
+ SetFailed( "LL_L2F", "r != 1.0");
+ }
+
+ /* LL_L2D -- Convert to 64bit floating point */
+ LL_L2D( resultD, bigOne );
+ if ( resultD != 1.0L ) {
+ SetFailed( "LL_L2D", "r != 1.0");
+ }
+
+ LL_L2D( resultD, bigMinusOne );
+ if ( resultD != -1.0L ) {
+ SetFailed( "LL_L2D", "r != -1.0");
+ }
+
+ /* LL_I2L -- Convert 32bit signed to 64bit signed */
+ LL_I2L( result, one );
+ if ( LL_CMP(result, !=, bigOne )) {
+ SetFailed( "LL_I2L", "r != 1");
+ }
+
+ LL_I2L( result, minusOne );
+ if ( LL_CMP(result, !=, bigMinusOne )) {
+ SetFailed( "LL_I2L", "r != -1");
+ }
+
+ /* LL_UI2L -- Convert 32bit unsigned to 64bit unsigned */
+ LL_UI2L( resultU, (PRUint32) one );
+ if ( LL_CMP(resultU, !=, bigOne )) {
+ SetFailed( "LL_UI2L", "r != 1");
+ }
+
+ /* [lth.] This did not behave as expected, but it is correct
+ */
+ LL_UI2L( resultU, (PRUint32) minusOne );
+ if ( LL_CMP(resultU, !=, bigZeroFox )) {
+ ResultFailed( "LL_UI2L", "r != -1", bigZeroFox, resultU);
+ }
+
+ /* LL_F2L -- Convert 32bit float to 64bit signed */
+ LL_F2L( result, 1.0 );
+ if ( LL_CMP(result, !=, bigOne )) {
+ SetFailed( "LL_F2L", "r != 1");
+ }
+
+ LL_F2L( result, -1.0 );
+ if ( LL_CMP(result, !=, bigMinusOne )) {
+ SetFailed( "LL_F2L", "r != -1");
+ }
+
+ /* LL_D2L -- Convert 64bit Float to 64bit signed */
+ LL_D2L( result, 1.0L );
+ if ( LL_CMP(result, !=, bigOne )) {
+ SetFailed( "LL_D2L", "r != 1");
+ }
+
+ LL_D2L( result, -1.0L );
+ if ( LL_CMP(result, !=, bigMinusOne )) {
+ SetFailed( "LL_D2L", "r != -1");
+ }
+
+ return;
+}
+
+static void ShiftCompileOnly()
+{
+ /*
+ ** This function is only compiled, never called.
+ ** The real test is to see if it compiles w/o
+ ** warnings. This is no small feat, by the way.
+ */
+ PRInt64 ia, ib;
+ PRUint64 ua, ub;
+ LL_SHR(ia, ib, 32);
+ LL_SHL(ia, ib, 32);
+
+ LL_USHR(ua, ub, 32);
+ LL_ISHL(ia, 49, 32);
+
+} /* ShiftCompileOnly */
+
+
+/*
+** TestShift() -- Test Shifting Operations
+**
+*/
+static void
+TestShift( void )
+{
+ static const PRInt64 largeTwoZero = LL_INIT( 0x00000002, 0x00000000 );
+ PRInt64 result;
+ PRUint64 resultU;
+
+ ReportProgress("Testing Shifting Operations\n");
+
+ /* LL_SHL -- Shift left algebraic */
+ LL_SHL( result, bigOne, one );
+ if ( LL_CMP( result, !=, bigTwo )) {
+ ResultFailed( "LL_SHL", "r != 2", bigOne, result );
+ }
+
+ LL_SHL( result, bigTwo, thirtyTwo );
+ if ( LL_CMP( result, !=, largeTwoZero )) {
+ ResultFailed( "LL_SHL", "r != twoZero", largeTwoZero, result);
+ }
+
+ /* LL_SHR -- Shift right algebraic */
+ LL_SHR( result, bigFoxZero, thirtyTwo );
+ if ( LL_CMP( result, !=, bigMinusOne )) {
+ ResultFailed( "LL_SHR", "r != -1", bigMinusOne, result);
+ }
+
+ LL_SHR( result, bigTwo, one );
+ if ( LL_CMP( result, !=, bigOne )) {
+ ResultFailed( "LL_SHR", "r != 1", bigOne, result);
+ }
+
+ LL_SHR( result, bigFoxFox, thirtyTwo );
+ if ( LL_CMP( result, !=, bigMinusOne )) {
+ ResultFailed( "LL_SHR", "r != -1 (was ff,ff)", bigMinusOne, result);
+ }
+
+ /* LL_USHR -- Logical shift right */
+ LL_USHR( resultU, bigZeroFox, thirtyTwo );
+ if ( LL_UCMP( resultU, !=, bigZero )) {
+ ResultFailed( "LL_USHR", "r != 0 ", bigZero, result);
+ }
+
+ LL_USHR( resultU, bigFoxFox, thirtyTwo );
+ if ( LL_UCMP( resultU, !=, bigZeroFox )) {
+ ResultFailed( "LL_USHR", "r != 0 ", bigZeroFox, result);
+ }
+
+ /* LL_ISHL -- Shift a 32bit integer into a 64bit result */
+ LL_ISHL( resultU, minusOne, thirtyTwo );
+ if ( LL_UCMP( resultU, !=, bigFoxZero )) {
+ ResultFailed( "LL_ISHL", "r != ff,00 ", bigFoxZero, result);
+ }
+
+ LL_ISHL( resultU, one, sixtyThree );
+ if ( LL_UCMP( resultU, !=, bigEightZero )) {
+ ResultFailed( "LL_ISHL", "r != 80,00 ", bigEightZero, result);
+ }
+
+ LL_ISHL( resultU, one, sixteen );
+ if ( LL_UCMP( resultU, !=, big64K )) {
+ ResultFailed( "LL_ISHL", "r != 64K ", big64K, resultU);
+ }
+
+ return;
+}
+
+
+/*
+** TestArithmetic() -- Test arithmetic operations.
+**
+*/
+static void
+TestArithmetic( void )
+{
+ PRInt64 largeVal = LL_INIT( 0x00000001, 0xffffffff );
+ PRInt64 largeValPlusOne = LL_INIT( 0x00000002, 0x00000000 );
+ PRInt64 largeValTimesTwo = LL_INIT( 0x00000003, 0xfffffffe );
+ PRInt64 largeMultCand = LL_INIT( 0x00000000, 0x7fffffff );
+ PRInt64 largeMinusMultCand = LL_INIT( 0xffffffff, 0x10000001 );
+ PRInt64 largeMultCandx64K = LL_INIT( 0x00007fff, 0xffff0000 );
+ PRInt64 largeNumSHL5 = LL_INIT( 0x0000001f, 0xffffffe0 );
+ PRInt64 result, result2;
+
+ /* Addition */
+ LL_ADD( result, bigOne, bigOne );
+ if ( LL_CMP( result, !=, bigTwo )) {
+ ResultFailed( "LL_ADD", "r != 1 + 1", bigTwo, result);
+ }
+
+ LL_ADD( result, bigMinusOne, bigOne );
+ if ( LL_CMP( result, !=, bigZero )) {
+ ResultFailed( "LL_ADD", "r != -1 + 1", bigOne, result);
+ }
+
+ LL_ADD( result, largeVal, bigOne );
+ if ( LL_CMP( result, !=, largeValPlusOne )) {
+ ResultFailed( "LL_ADD", "lVP1 != lV + 1", largeValPlusOne, result);
+ }
+
+ /* Subtraction */
+ LL_SUB( result, bigOne, bigOne );
+ if ( LL_CMP( result, !=, bigZero )) {
+ ResultFailed( "LL_SUB", "r != 1 - 1", bigZero, result);
+ }
+
+ LL_SUB( result, bigTwo, bigOne );
+ if ( LL_CMP( result, !=, bigOne )) {
+ ResultFailed( "LL_SUB", "r != 2 - 1", bigOne, result);
+ }
+
+ LL_SUB( result, largeValPlusOne, bigOne );
+ if ( LL_CMP( result, !=, largeVal )) {
+ ResultFailed( "LL_SUB", "r != lVP1 - 1", largeVal, result);
+ }
+
+
+ /* Multiply */
+ LL_MUL( result, largeVal, bigTwo );
+ if ( LL_CMP( result, !=, largeValTimesTwo )) {
+ ResultFailed( "LL_MUL", "r != lV*2", largeValTimesTwo, result);
+ }
+
+ LL_MUL( result, largeMultCand, big64K );
+ if ( LL_CMP( result, !=, largeMultCandx64K )) {
+ ResultFailed( "LL_MUL", "r != lV*64K", largeMultCandx64K, result);
+ }
+
+ LL_NEG( result2, largeMultCand );
+ LL_MUL( result, largeMultCand, bigMinusOne );
+ if ( LL_CMP( result, !=, result2 )) {
+ ResultFailed( "LL_MUL", "r != -lMC", result2, result);
+ }
+
+ LL_SHL( result2, bigZeroFox, 5);
+ LL_MUL( result, bigZeroFox, bigThirtyTwo );
+ if ( LL_CMP( result, !=, largeNumSHL5 )) {
+ ResultFailed( "LL_MUL", "r != 0f<<5", largeNumSHL5, result );
+ }
+
+
+
+ /* LL_DIV() Division */
+ LL_DIV( result, bigOne, bigOne);
+ if ( LL_CMP( result, !=, bigOne )) {
+ ResultFailed( "LL_DIV", "1 != 1", bigOne, result);
+ }
+
+ LL_DIV( result, bigNumber, bigOne );
+ if ( LL_CMP( result, !=, bigNumber )) {
+ ResultFailed( "LL_DIV", "r != n / 1", bigNumber, result);
+ }
+
+ LL_DIV( result, bigNumber, bigMinusOne );
+ if ( LL_CMP( result, !=, bigMinusNumber )) {
+ ResultFailed( "LL_DIV", "r != n / -1", bigMinusNumber, result);
+ }
+
+ LL_DIV( result, bigMinusNumber, bigMinusOne );
+ if ( LL_CMP( result, !=, bigNumber )) {
+ ResultFailed( "LL_DIV", "r != -n / -1", bigNumber, result);
+ }
+
+ LL_SHL( result2, bigZeroFox, 5 );
+ LL_DIV( result, result2, bigOne );
+ if ( LL_CMP( result, !=, result2 )) {
+ ResultFailed( "LL_DIV", "0f<<5 != 0f<<5", result2, result);
+ }
+
+ LL_SHL( result2, bigZeroFox, 5 );
+ LL_NEG( result2, result2 );
+ LL_DIV( result, result2, bigOne );
+ if ( LL_CMP( result, !=, result2 )) {
+ ResultFailed( "LL_DIV", "-0f<<5 != -0f<<5", result2, result);
+ }
+
+ LL_SHL( result2, bigZeroFox, 17 );
+ LL_DIV( result, result2, bigMinusOne );
+ LL_NEG( result2, result2 );
+ if ( LL_CMP( result, !=, result2 )) {
+ ResultFailed( "LL_DIV", "-0f<<17 != -0f<<17", result2, result);
+ }
+
+
+ /* LL_MOD() Modulo Division */
+ LL_ADD( result2, bigThirtyTwo, bigOne );
+ LL_MOD( result, result2, bigSixTeen );
+ if ( LL_CMP( result, !=, bigOne )) {
+ ResultFailed( "LL_MOD", "r != 1", bigSixTeen, result);
+ }
+
+
+ LL_MUL( result2, bigZeroFox, bigThirtyTwo );
+ LL_ADD( result2, result2, bigSixTeen);
+ LL_MOD( result, result2, bigThirtyTwo );
+ if ( LL_CMP( result, !=, bigSixTeen )) {
+ ResultFailed( "LL_MOD", "r != 16", bigSixTeen, result);
+ }
+
+ /* LL_UDIVMOD */
+ LL_DIV( result, bigOne, bigOne);
+ if ( LL_CMP( result, !=, bigOne )) {
+ ResultFailed( "LL_DIV", "r != 16", bigSixTeen, result);
+ }
+
+
+ return;
+}
+
+static void TestWellknowns(void)
+{
+ PRInt64 max = LL_MAXINT, min = LL_MININT, zero = LL_ZERO;
+ PRInt64 mmax = LL_MaxInt(), mmin = LL_MinInt(), mzero = LL_Zero();
+ if (LL_NE(max, mmax)) {
+ ResultFailed( "max, mmax", "max != mmax", max, mmax);
+ }
+ if (LL_NE(min, mmin)) {
+ ResultFailed( "min, mmin", "min != mmin", max, mmin);
+ }
+ if (LL_NE(zero, mzero)) {
+ ResultFailed( "zero, mzero", "zero != mzero", zero, mzero);
+ }
+} /* TestWellknowns */
+
+/*
+** Initialize() -- Initialize the test case
+**
+** Parse command line options
+**
+*/
+static PRIntn
+Initialize( PRIntn argc, char **argv )
+{
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dvh");
+ PLOptStatus os;
+
+ /*
+ ** Parse command line options
+ */
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* set debug mode */
+ debugMode = PR_TRUE;
+ break;
+
+ case 'v': /* set verbose mode */
+ verboseMode = PR_TRUE;
+ debugMode = PR_TRUE;
+ break;
+
+ case 'h': /* user wants some guidance */
+ default:
+ PR_fprintf(output, "You get help.\n");
+ return(1);
+ }
+ }
+ PL_DestroyOptState(opt);
+ return(0);
+}
+
+int main(int argc, char **argv)
+{
+ PR_STDIO_INIT();
+ output = PR_GetSpecialFD(PR_StandardError);
+
+ if ( Initialize( argc, argv )) {
+ return(1);
+ }
+
+ TestAssignment();
+ TestComparisons();
+ TestLogicalOperations();
+ TestConversion();
+ TestShift();
+ TestArithmetic();
+ TestWellknowns();
+
+ /*
+ ** That's all folks!
+ */
+ if ( failedAlready )
+ {
+ PR_fprintf(output, "FAIL\n"); \
+ }
+ else
+ {
+ PR_fprintf(output, "PASS\n"); \
+ }
+ return failedAlready;
+} /* end main() */
diff --git a/nsprpub/pr/tests/lock.c b/nsprpub/pr/tests/lock.c
new file mode 100644
index 0000000000..83a4f41801
--- /dev/null
+++ b/nsprpub/pr/tests/lock.c
@@ -0,0 +1,548 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: lock.c
+** Purpose: test basic locking functions
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+**
+** 11-Aug-97 LarryH. Win16 port of NSPR.
+** - Added "PASS", "FAIL" messages on completion.
+** - Change stack variables to static scope variables
+** because of shadow-stack use by Win16
+** - Added PR_CALLBACK attribute to functions called by NSPR
+** - Added command line arguments:
+** - l <num> to control the number of loops
+** - c <num> to control the number of CPUs.
+** (was positional argv).
+**
+**
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prio.h"
+#include "prcmon.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prprf.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prmon.h"
+#include "prmem.h"
+#include "prthread.h"
+#include "prtypes.h"
+
+#include "plstr.h"
+
+#include <stdlib.h>
+
+#if defined(XP_UNIX)
+#include <string.h>
+#endif
+
+static PRIntn failed_already=0;
+static PRFileDesc *std_err = NULL;
+static PRBool verbosity = PR_FALSE;
+static PRBool debug_mode = PR_FALSE;
+
+const static PRIntervalTime contention_interval = 50;
+
+typedef struct LockContentious_s {
+ PRLock *ml;
+ PRInt32 loops;
+ PRUint32 contender;
+ PRUint32 contentious;
+ PRIntervalTime overhead;
+ PRIntervalTime interval;
+} LockContentious_t;
+
+typedef struct MonitorContentious_s {
+ PRMonitor *ml;
+ PRInt32 loops;
+ PRUint32 contender;
+ PRUint32 contentious;
+ PRIntervalTime overhead;
+ PRIntervalTime interval;
+} MonitorContentious_t;
+
+
+static PRIntervalTime Sleeper(PRUint32 loops)
+{
+ PRIntervalTime predicted = 0;
+ while (loops-- > 0)
+ {
+ predicted += contention_interval;
+ (void)PR_Sleep(contention_interval);
+ }
+ return predicted;
+} /* Sleeper */
+
+/*
+** BASIC LOCKS
+*/
+static PRIntervalTime MakeLock(PRUint32 loops)
+{
+ PRLock *ml = NULL;
+ while (loops-- > 0)
+ {
+ ml = PR_NewLock();
+ PR_DestroyLock(ml);
+ ml = NULL;
+ }
+ return 0;
+} /* MakeLock */
+
+static PRIntervalTime NonContentiousLock(PRUint32 loops)
+{
+ PRLock *ml = NULL;
+ ml = PR_NewLock();
+ while (loops-- > 0)
+ {
+ PR_Lock(ml);
+ PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(ml);
+ PR_Unlock(ml);
+ }
+ PR_DestroyLock(ml);
+ return 0;
+} /* NonContentiousLock */
+
+static void PR_CALLBACK LockContender(void *arg)
+{
+ LockContentious_t *contention = (LockContentious_t*)arg;
+ while (contention->loops-- > 0)
+ {
+ PR_Lock(contention->ml);
+ PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
+ contention->contender+= 1;
+ contention->overhead += contention->interval;
+ PR_Sleep(contention->interval);
+ PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
+ PR_Unlock(contention->ml);
+ }
+} /* LockContender */
+
+static PRIntervalTime ContentiousLock(PRUint32 loops)
+{
+ PRStatus status;
+ PRThread *thread = NULL;
+ LockContentious_t * contention;
+ PRIntervalTime rv, overhead, timein = PR_IntervalNow();
+
+ contention = PR_NEWZAP(LockContentious_t);
+ contention->loops = loops;
+ contention->overhead = 0;
+ contention->ml = PR_NewLock();
+ contention->interval = contention_interval;
+ thread = PR_CreateThread(
+ PR_USER_THREAD, LockContender, contention,
+ PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(thread != NULL);
+
+ overhead = PR_IntervalNow() - timein;
+
+ while (contention->loops-- > 0)
+ {
+ PR_Lock(contention->ml);
+ PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
+ contention->contentious+= 1;
+ contention->overhead += contention->interval;
+ PR_Sleep(contention->interval);
+ PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
+ PR_Unlock(contention->ml);
+ }
+
+ timein = PR_IntervalNow();
+ status = PR_JoinThread(thread);
+ PR_DestroyLock(contention->ml);
+ overhead += (PR_IntervalNow() - timein);
+ rv = overhead + contention->overhead;
+ if (verbosity)
+ PR_fprintf(
+ std_err, "Access ratio: %u to %u\n",
+ contention->contentious, contention->contender);
+ PR_Free(contention);
+ return rv;
+} /* ContentiousLock */
+
+/*
+** MONITORS
+*/
+static PRIntervalTime MakeMonitor(PRUint32 loops)
+{
+ PRMonitor *ml = NULL;
+ while (loops-- > 0)
+ {
+ ml = PR_NewMonitor();
+ PR_DestroyMonitor(ml);
+ ml = NULL;
+ }
+ return 0;
+} /* MakeMonitor */
+
+static PRIntervalTime NonContentiousMonitor(PRUint32 loops)
+{
+ PRMonitor *ml = NULL;
+ ml = PR_NewMonitor();
+ while (loops-- > 0)
+ {
+ PR_EnterMonitor(ml);
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
+ PR_ExitMonitor(ml);
+ }
+ PR_DestroyMonitor(ml);
+ return 0;
+} /* NonContentiousMonitor */
+
+static void PR_CALLBACK TryEntry(void *arg)
+{
+ PRMonitor *ml = (PRMonitor*)arg;
+ if (debug_mode) {
+ PR_fprintf(std_err, "Reentrant thread created\n");
+ }
+ PR_EnterMonitor(ml);
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
+ if (debug_mode) {
+ PR_fprintf(std_err, "Reentrant thread acquired monitor\n");
+ }
+ PR_ExitMonitor(ml);
+ if (debug_mode) {
+ PR_fprintf(std_err, "Reentrant thread released monitor\n");
+ }
+} /* TryEntry */
+
+static PRIntervalTime ReentrantMonitor(PRUint32 loops)
+{
+ PRStatus status;
+ PRThread *thread;
+ PRMonitor *ml = PR_NewMonitor();
+ if (debug_mode) {
+ PR_fprintf(std_err, "\nMonitor created for reentrant test\n");
+ }
+
+ PR_EnterMonitor(ml);
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
+ PR_EnterMonitor(ml);
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
+ if (debug_mode) {
+ PR_fprintf(std_err, "Monitor acquired twice\n");
+ }
+
+ thread = PR_CreateThread(
+ PR_USER_THREAD, TryEntry, ml,
+ PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(thread != NULL);
+ PR_Sleep(PR_SecondsToInterval(1));
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
+
+ PR_ExitMonitor(ml);
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
+ if (debug_mode) {
+ PR_fprintf(std_err, "Monitor released first time\n");
+ }
+
+ PR_ExitMonitor(ml);
+ if (debug_mode) {
+ PR_fprintf(std_err, "Monitor released second time\n");
+ }
+
+ status = PR_JoinThread(thread);
+ if (debug_mode) PR_fprintf(std_err,
+ "Reentrant thread joined %s\n",
+ (status == PR_SUCCESS) ? "successfully" : "in error");
+
+ PR_DestroyMonitor(ml);
+ return 0;
+} /* ReentrantMonitor */
+
+static void PR_CALLBACK MonitorContender(void *arg)
+{
+ MonitorContentious_t *contention = (MonitorContentious_t*)arg;
+ while (contention->loops-- > 0)
+ {
+ PR_EnterMonitor(contention->ml);
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
+ contention->contender+= 1;
+ contention->overhead += contention->interval;
+ PR_Sleep(contention->interval);
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
+ PR_ExitMonitor(contention->ml);
+ }
+} /* MonitorContender */
+
+static PRUint32 ContentiousMonitor(PRUint32 loops)
+{
+ PRStatus status;
+ PRThread *thread = NULL;
+ MonitorContentious_t * contention;
+ PRIntervalTime rv, overhead, timein = PR_IntervalNow();
+
+ contention = PR_NEWZAP(MonitorContentious_t);
+ contention->loops = loops;
+ contention->overhead = 0;
+ contention->ml = PR_NewMonitor();
+ contention->interval = contention_interval;
+ thread = PR_CreateThread(
+ PR_USER_THREAD, MonitorContender, contention,
+ PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(thread != NULL);
+
+ overhead = PR_IntervalNow() - timein;
+
+ while (contention->loops-- > 0)
+ {
+ PR_EnterMonitor(contention->ml);
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
+ contention->contentious+= 1;
+ contention->overhead += contention->interval;
+ PR_Sleep(contention->interval);
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
+ PR_ExitMonitor(contention->ml);
+ }
+
+ timein = PR_IntervalNow();
+ status = PR_JoinThread(thread);
+ PR_DestroyMonitor(contention->ml);
+ overhead += (PR_IntervalNow() - timein);
+ rv = overhead + contention->overhead;
+ if (verbosity)
+ PR_fprintf(
+ std_err, "Access ratio: %u to %u\n",
+ contention->contentious, contention->contender);
+ PR_Free(contention);
+ return rv;
+} /* ContentiousMonitor */
+
+/*
+** CACHED MONITORS
+*/
+static PRIntervalTime NonContentiousCMonitor(PRUint32 loops)
+{
+ MonitorContentious_t contention;
+ while (loops-- > 0)
+ {
+ PR_CEnterMonitor(&contention);
+ PR_CExitMonitor(&contention);
+ }
+ return 0;
+} /* NonContentiousCMonitor */
+
+static void PR_CALLBACK Contender(void *arg)
+{
+ MonitorContentious_t *contention = (MonitorContentious_t*)arg;
+ while (contention->loops-- > 0)
+ {
+ PR_CEnterMonitor(contention);
+ contention->contender+= 1;
+ contention->overhead += contention->interval;
+ PR_Sleep(contention->interval);
+ PR_CExitMonitor(contention);
+ }
+} /* Contender */
+
+static PRIntervalTime ContentiousCMonitor(PRUint32 loops)
+{
+ PRStatus status;
+ PRThread *thread = NULL;
+ MonitorContentious_t * contention;
+ PRIntervalTime overhead, timein = PR_IntervalNow();
+
+ contention = PR_NEWZAP(MonitorContentious_t);
+ contention->ml = NULL;
+ contention->loops = loops;
+ contention->interval = contention_interval;
+ thread = PR_CreateThread(
+ PR_USER_THREAD, Contender, contention,
+ PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(thread != NULL);
+
+ overhead = PR_IntervalNow() - timein;
+
+ while (contention->loops-- > 0)
+ {
+ PR_CEnterMonitor(contention);
+ contention->contentious+= 1;
+ contention->overhead += contention->interval;
+ PR_Sleep(contention->interval);
+ PR_CExitMonitor(contention);
+ }
+
+ timein = PR_IntervalNow();
+ status = PR_JoinThread(thread);
+ overhead += (PR_IntervalNow() - timein);
+ overhead += overhead + contention->overhead;
+ if (verbosity)
+ PR_fprintf(
+ std_err, "Access ratio: %u to %u\n",
+ contention->contentious, contention->contender);
+ PR_Free(contention);
+ return overhead;
+} /* ContentiousCMonitor */
+
+static PRIntervalTime Test(
+ const char* msg, PRUint32 (*test)(PRUint32 loops),
+ PRUint32 loops, PRIntervalTime overhead)
+{
+ /*
+ * overhead - overhead not measured by the test.
+ * duration - wall clock time it took to perform test.
+ * predicted - extra time test says should not be counted
+ *
+ * Time accountable to the test is duration - overhead - predicted
+ * All times are Intervals and accumulated for all iterations.
+ */
+ PRFloat64 elapsed;
+ PRIntervalTime accountable, duration;
+ PRUintn spaces = PL_strlen(msg);
+ PRIntervalTime timeout, timein = PR_IntervalNow();
+ PRIntervalTime predicted = test(loops);
+ timeout = PR_IntervalNow();
+ duration = timeout - timein;
+
+ if (debug_mode)
+ {
+ accountable = duration - predicted;
+ accountable -= overhead;
+ elapsed = (PRFloat64)PR_IntervalToMicroseconds(accountable);
+ PR_fprintf(PR_STDOUT, "%s:", msg);
+ while (spaces++ < 50) {
+ PR_fprintf(PR_STDOUT, " ");
+ }
+ if ((PRInt32)accountable < 0) {
+ PR_fprintf(PR_STDOUT, "*****.** usecs/iteration\n");
+ }
+ else {
+ PR_fprintf(PR_STDOUT, "%8.2f usecs/iteration\n", elapsed/loops);
+ }
+ }
+ return duration;
+} /* Test */
+
+int main(int argc, char **argv)
+{
+ PRBool rv = PR_TRUE;
+ PRIntervalTime duration;
+ PRUint32 cpu, cpus = 2, loops = 100;
+
+
+ PR_STDIO_INIT();
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ {
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Command line argument -l <num> sets the number of loops.
+ Command line argument -c <num> sets the number of cpus.
+ Usage: lock [-d] [-l <num>] [-c <num>]
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dvl:c:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'v': /* debug mode */
+ verbosity = PR_TRUE;
+ break;
+ case 'l': /* number of loops */
+ loops = atoi(opt->value);
+ break;
+ case 'c': /* number of cpus */
+ cpus = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ }
+
+ /* main test */
+ PR_SetConcurrency(8);
+
+ if (loops == 0) {
+ loops = 100;
+ }
+ if (debug_mode)
+ {
+ std_err = PR_STDERR;
+ PR_fprintf(std_err, "Lock: Using %d loops\n", loops);
+ }
+
+ if (cpus == 0) {
+ cpus = 2;
+ }
+ if (debug_mode) {
+ PR_fprintf(std_err, "Lock: Using %d cpu(s)\n", cpus);
+ }
+
+ (void)Sleeper(10); /* try filling in the caches */
+
+ for (cpu = 1; cpu <= cpus; ++cpu)
+ {
+ if (debug_mode) {
+ PR_fprintf(std_err, "\nLock: Using %d CPU(s)\n", cpu);
+ }
+ PR_SetConcurrency(cpu);
+
+ duration = Test("Overhead of PR_Sleep", Sleeper, loops, 0);
+ duration = 0;
+
+ (void)Test("Lock creation/deletion", MakeLock, loops, 0);
+ (void)Test("Lock non-contentious locking/unlocking", NonContentiousLock, loops, 0);
+ (void)Test("Lock contentious locking/unlocking", ContentiousLock, loops, duration);
+ (void)Test("Monitor creation/deletion", MakeMonitor, loops, 0);
+ (void)Test("Monitor non-contentious locking/unlocking", NonContentiousMonitor, loops, 0);
+ (void)Test("Monitor contentious locking/unlocking", ContentiousMonitor, loops, duration);
+
+ (void)Test("Cached monitor non-contentious locking/unlocking", NonContentiousCMonitor, loops, 0);
+ (void)Test("Cached monitor contentious locking/unlocking", ContentiousCMonitor, loops, duration);
+
+ (void)ReentrantMonitor(loops);
+ }
+
+ if (debug_mode)
+ PR_fprintf(
+ std_err, "%s: test %s\n", "Lock(mutex) test",
+ ((rv) ? "passed" : "failed"));
+ else {
+ if (!rv) {
+ failed_already=1;
+ }
+ }
+
+ if(failed_already)
+ {
+ PR_fprintf(PR_STDOUT, "FAIL\n");
+ return 1;
+ }
+ else
+ {
+ PR_fprintf(PR_STDOUT, "PASS\n");
+ return 0;
+ }
+
+} /* main */
+
+/* testlock.c */
diff --git a/nsprpub/pr/tests/lockfile.c b/nsprpub/pr/tests/lockfile.c
new file mode 100644
index 0000000000..883ef0d615
--- /dev/null
+++ b/nsprpub/pr/tests/lockfile.c
@@ -0,0 +1,255 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: lockfile.c
+** Purpose: test basic locking functions
+** Just because this times stuff, don't think its a perforamnce
+** test!!!
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prcmon.h"
+#include "prerror.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prmon.h"
+#include "prthread.h"
+#include "prtypes.h"
+
+#include "private/pprio.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+const static PRIntervalTime contention_interval = 50;
+
+typedef struct LockContentious_s {
+ PRLock *ml;
+ PRInt32 loops;
+ PRIntervalTime overhead;
+ PRIntervalTime interval;
+} LockContentious_t;
+
+#define LOCKFILE "prlock.fil"
+
+
+
+static PRIntervalTime NonContentiousLock(PRInt32 loops)
+{
+ PRFileDesc *_lockfile;
+ while (loops-- > 0)
+ {
+ _lockfile = PR_Open(LOCKFILE, PR_CREATE_FILE|PR_RDWR, 0666);
+ if (!_lockfile) {
+ if (debug_mode) printf(
+ "could not create lockfile: %d [%d]\n",
+ PR_GetError(), PR_GetOSError());
+ return PR_INTERVAL_NO_TIMEOUT;
+ }
+ PR_LockFile(_lockfile);
+ PR_UnlockFile(_lockfile);
+ PR_Close(_lockfile);
+ }
+ return 0;
+} /* NonContentiousLock */
+
+static void PR_CALLBACK LockContender(void *arg)
+{
+ LockContentious_t *contention = (LockContentious_t*)arg;
+ PRFileDesc *_lockfile;
+ while (contention->loops-- > 0)
+ {
+ _lockfile = PR_Open(LOCKFILE, PR_CREATE_FILE|PR_RDWR, 0666);
+ if (!_lockfile) {
+ if (debug_mode) printf(
+ "could not create lockfile: %d [%d]\n",
+ PR_GetError(), PR_GetOSError());
+ break;
+ }
+ PR_LockFile(_lockfile);
+ PR_Sleep(contention->interval);
+ PR_UnlockFile(_lockfile);
+ PR_Close(_lockfile);
+ }
+
+} /* LockContender */
+
+/*
+** Win16 requires things passed to Threads not be on the stack
+*/
+static LockContentious_t contention;
+
+static PRIntervalTime ContentiousLock(PRInt32 loops)
+{
+ PRStatus status;
+ PRThread *thread = NULL;
+ PRIntervalTime overhead, timein = PR_IntervalNow();
+
+ contention.loops = loops;
+ contention.overhead = 0;
+ contention.ml = PR_NewLock();
+ contention.interval = contention_interval;
+ thread = PR_CreateThread(
+ PR_USER_THREAD, LockContender, &contention,
+ PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(thread != NULL);
+
+ overhead = PR_IntervalNow() - timein;
+
+ while (contention.loops > 0)
+ {
+ PR_Lock(contention.ml);
+ contention.overhead += contention.interval;
+ PR_Sleep(contention.interval);
+ PR_Unlock(contention.ml);
+ }
+
+ timein = PR_IntervalNow();
+ status = PR_JoinThread(thread);
+ PR_DestroyLock(contention.ml);
+ overhead += (PR_IntervalNow() - timein);
+ return overhead + contention.overhead;
+} /* ContentiousLock */
+
+static PRIntervalTime Test(
+ const char* msg, PRIntervalTime (*test)(PRInt32 loops),
+ PRInt32 loops, PRIntervalTime overhead)
+{
+ /*
+ * overhead - overhead not measured by the test.
+ * duration - wall clock time it took to perform test.
+ * predicted - extra time test says should not be counted
+ *
+ * Time accountable to the test is duration - overhead - predicted
+ * All times are Intervals and accumulated for all iterations.
+ */
+ PRFloat64 elapsed;
+ PRIntervalTime accountable, duration;
+ PRUintn spaces = strlen(msg);
+ PRIntervalTime timeout, timein = PR_IntervalNow();
+ PRIntervalTime predicted = test(loops);
+ timeout = PR_IntervalNow();
+ duration = timeout - timein;
+ accountable = duration - predicted;
+ accountable -= overhead;
+ elapsed = (PRFloat64)PR_IntervalToMicroseconds(accountable);
+ if (debug_mode) {
+ printf("%s:", msg);
+ }
+ while (spaces++ < 50) if (debug_mode) {
+ printf(" ");
+ }
+ if ((PRInt32)accountable < 0) {
+ if (debug_mode) {
+ printf("*****.** usecs/iteration\n");
+ }
+ } else {
+ if (debug_mode) {
+ printf("%8.2f usecs/iteration\n", elapsed/loops);
+ }
+ }
+ return duration;
+} /* Test */
+
+int main(int argc, char **argv)
+{
+ PRIntervalTime duration;
+ PRUint32 cpu, cpus = 2;
+ PRInt32 loops = 100;
+
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (argc > 1) {
+ loops = atoi(argv[1]);
+ }
+ if (loops == 0) {
+ loops = 100;
+ }
+ if (debug_mode) {
+ printf("Lock: Using %d loops\n", loops);
+ }
+
+ cpus = (argc < 3) ? 2 : atoi(argv[2]);
+ if (cpus == 0) {
+ cpus = 2;
+ }
+ if (debug_mode) {
+ printf("Lock: Using %d cpu(s)\n", cpus);
+ }
+
+
+ for (cpu = 1; cpu <= cpus; ++cpu)
+ {
+ if (debug_mode) {
+ printf("\nLockFile: Using %d CPU(s)\n", cpu);
+ }
+ PR_SetConcurrency(cpu);
+
+ duration = Test("LockFile non-contentious locking/unlocking", NonContentiousLock, loops, 0);
+ (void)Test("LockFile contentious locking/unlocking", ContentiousLock, loops, duration);
+ }
+
+ PR_Delete(LOCKFILE); /* try to get rid of evidence */
+
+ if (debug_mode) {
+ printf("%s: test %s\n", "Lock(mutex) test", ((failed_already) ? "failed" : "passed"));
+ }
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+} /* main */
+
+/* testlock.c */
diff --git a/nsprpub/pr/tests/logfile.c b/nsprpub/pr/tests/logfile.c
new file mode 100644
index 0000000000..d6997be1fe
--- /dev/null
+++ b/nsprpub/pr/tests/logfile.c
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * A regression test for bug 491441. NSPR should not crash on startup in
+ * PR_SetLogFile when the NSPR_LOG_MODULES and NSPR_LOG_FILE environment
+ * variables are set.
+ *
+ * This test could be extended to be a full-blown test for NSPR_LOG_FILE.
+ */
+
+#include "prinit.h"
+#include "prlog.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main()
+{
+ PRLogModuleInfo *test_lm;
+
+ if (putenv("NSPR_LOG_MODULES=all:5") != 0) {
+ fprintf(stderr, "putenv failed\n");
+ exit(1);
+ }
+ if (putenv("NSPR_LOG_FILE=logfile.log") != 0) {
+ fprintf(stderr, "putenv failed\n");
+ exit(1);
+ }
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ test_lm = PR_NewLogModule("test");
+ PR_LOG(test_lm, PR_LOG_MIN, ("logfile: test log message"));
+ PR_Cleanup();
+ return 0;
+}
diff --git a/nsprpub/pr/tests/logger.c b/nsprpub/pr/tests/logger.c
new file mode 100644
index 0000000000..ee28d41d7c
--- /dev/null
+++ b/nsprpub/pr/tests/logger.c
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: logger.c
+ * Description: test program for logging's basic functions
+ */
+
+#include "prinit.h"
+#include "prlog.h"
+#include "prlock.h"
+#include "prcvar.h"
+#include "prthread.h"
+#include "prinrval.h"
+
+#include <stdio.h>
+
+/* lth. re-define PR_LOG() */
+#if 0
+#undef PR_LOG_TEST
+#undef PR_LOG
+#define PR_LOG_TEST(_module,_level) ((_module)->level <= (_level))
+#define PR_LOG(_module,_level,_args) \
+ { \
+ if (PR_LOG_TEST(_module,_level)) \
+ PR_LogPrint _args ; \
+ }
+#endif
+
+
+static void Error(const char* msg)
+{
+ printf("\t%s\n", msg);
+} /* Error */
+
+static void PR_CALLBACK forked(void *arg)
+{
+ PRIntn i;
+ PRLock *ml;
+ PRCondVar *cv;
+
+ PR_LogPrint("%s logging creating mutex\n", (const char*)arg);
+ ml = PR_NewLock();
+ PR_LogPrint("%s logging creating condition variable\n", (const char*)arg);
+ cv = PR_NewCondVar(ml);
+
+ PR_LogPrint("%s waiting on condition timeout 10 times\n", (const char*)arg);
+ for (i = 0; i < 10; ++i)
+ {
+ PR_Lock(ml);
+ PR_WaitCondVar(cv, PR_SecondsToInterval(1));
+ PR_Unlock(ml);
+ }
+
+ PR_LogPrint("%s logging destroying condition variable\n", (const char*)arg);
+ PR_DestroyCondVar(cv);
+ PR_LogPrint("%s logging destroying mutex\n", (const char*)arg);
+ PR_DestroyLock(ml);
+ PR_LogPrint("%s forked thread exiting\n", (const char*)arg);
+}
+
+static void UserLogStuff( void )
+{
+ PRLogModuleInfo *myLM;
+ PRIntn i;
+
+ myLM = PR_NewLogModule( "userStuff" );
+ if (! myLM )
+ {
+ printf("UserLogStuff(): can't create new log module\n" );
+ return;
+ }
+
+ PR_LOG( myLM, PR_LOG_NOTICE, ("Log a Notice %d\n", 1 ));
+
+ for (i = 0; i < 10 ; i++ )
+ {
+ PR_LOG( myLM, PR_LOG_DEBUG, ("Log Debug number: %d\n", i));
+ PR_Sleep( 300 );
+ }
+
+} /* end UserLogStuff() */
+
+int main(int argc, char **argv)
+{
+ PRThread *thread;
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (argc > 1)
+ {
+ if (!PR_SetLogFile(argv[1]))
+ {
+ Error("Access: Cannot create log file");
+ goto exit;
+ }
+ }
+
+ /* Start logging something here */
+ PR_LogPrint("%s logging into %s\n", argv[0], argv[1]);
+
+ PR_LogPrint("%s creating new thread\n", argv[0]);
+
+ /*
+ ** Now change buffering.
+ */
+ PR_SetLogBuffering( 65500 );
+ thread = PR_CreateThread(
+ PR_USER_THREAD, forked, (void*)argv[0], PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ PR_LogPrint("%s joining thread\n", argv[0]);
+
+ UserLogStuff();
+
+ PR_JoinThread(thread);
+
+ PR_LogFlush();
+ return 0;
+
+exit:
+ return -1;
+}
+
+/* logger.c */
diff --git a/nsprpub/pr/tests/makedir.c b/nsprpub/pr/tests/makedir.c
new file mode 100644
index 0000000000..21b42c5a6f
--- /dev/null
+++ b/nsprpub/pr/tests/makedir.c
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This test calls PR_MakeDir to create a bunch of directories
+ * with various mode bits.
+ */
+
+#include "prio.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv)
+{
+ if (PR_MakeDir("tdir0400", 0400) == PR_FAILURE) {
+ fprintf(stderr, "PR_MakeDir failed\n");
+ exit(1);
+ }
+ if (PR_MakeDir("tdir0200", 0200) == PR_FAILURE) {
+ fprintf(stderr, "PR_MakeDir failed\n");
+ exit(1);
+ }
+ if (PR_MakeDir("tdir0100", 0100) == PR_FAILURE) {
+ fprintf(stderr, "PR_MakeDir failed\n");
+ exit(1);
+ }
+ if (PR_MakeDir("tdir0500", 0500) == PR_FAILURE) {
+ fprintf(stderr, "PR_MakeDir failed\n");
+ exit(1);
+ }
+ if (PR_MakeDir("tdir0600", 0600) == PR_FAILURE) {
+ fprintf(stderr, "PR_MakeDir failed\n");
+ exit(1);
+ }
+ if (PR_MakeDir("tdir0300", 0300) == PR_FAILURE) {
+ fprintf(stderr, "PR_MakeDir failed\n");
+ exit(1);
+ }
+ if (PR_MakeDir("tdir0700", 0700) == PR_FAILURE) {
+ fprintf(stderr, "PR_MakeDir failed\n");
+ exit(1);
+ }
+ if (PR_MakeDir("tdir0640", 0640) == PR_FAILURE) {
+ fprintf(stderr, "PR_MakeDir failed\n");
+ exit(1);
+ }
+ if (PR_MakeDir("tdir0660", 0660) == PR_FAILURE) {
+ fprintf(stderr, "PR_MakeDir failed\n");
+ exit(1);
+ }
+ if (PR_MakeDir("tdir0644", 0644) == PR_FAILURE) {
+ fprintf(stderr, "PR_MakeDir failed\n");
+ exit(1);
+ }
+ if (PR_MakeDir("tdir0664", 0664) == PR_FAILURE) {
+ fprintf(stderr, "PR_MakeDir failed\n");
+ exit(1);
+ }
+ if (PR_MakeDir("tdir0666", 0666) == PR_FAILURE) {
+ fprintf(stderr, "PR_MakeDir failed\n");
+ exit(1);
+ }
+ return 0;
+}
diff --git a/nsprpub/pr/tests/many_cv.c b/nsprpub/pr/tests/many_cv.c
new file mode 100644
index 0000000000..8444c99576
--- /dev/null
+++ b/nsprpub/pr/tests/many_cv.c
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prinit.h"
+#include "prprf.h"
+#include "prthread.h"
+#include "prcvar.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prmem.h"
+
+#include "primpl.h"
+
+#include "plgetopt.h"
+
+#include <stdlib.h>
+
+static PRInt32 RandomNum(void)
+{
+ PRInt32 ran = rand() >> 16;
+ return ran;
+} /* RandomNum */
+
+static void Help(void)
+{
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+ PR_fprintf(err, "many_cv usage: [-c n] [-l n] [-h]\n");
+ PR_fprintf(err, "\t-c n Number of conditions per lock (default: 10)\n");
+ PR_fprintf(err, "\t-l n Number of times to loop the test (default: 1)\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ PLOptStatus os;
+ PRIntn index, nl;
+ PRLock *ml = NULL;
+ PRCondVar **cv = NULL;
+ PRBool stats = PR_FALSE;
+ PRIntn nc, loops = 1, cvs = 10;
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hsc:l:");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 's': /* number of CVs to association with lock */
+ stats = PR_TRUE;
+ break;
+ case 'c': /* number of CVs to association with lock */
+ cvs = atoi(opt->value);
+ break;
+ case 'l': /* number of times to run the tests */
+ loops = atoi(opt->value);
+ break;
+ case 'h': /* user wants some guidance */
+ default:
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_fprintf(err, "Settings\n");
+ PR_fprintf(err, "\tConditions / lock: %d\n", cvs);
+ PR_fprintf(err, "\tLoops to run test: %d\n", loops);
+
+ ml = PR_NewLock();
+ PR_ASSERT(NULL != ml);
+
+ cv = (PRCondVar**)PR_CALLOC(sizeof(PRCondVar*) * cvs);
+ PR_ASSERT(NULL != cv);
+
+ for (index = 0; index < cvs; ++index)
+ {
+ cv[index] = PR_NewCondVar(ml);
+ PR_ASSERT(NULL != cv[index]);
+ }
+
+ for (index = 0; index < loops; ++index)
+ {
+ PR_Lock(ml);
+ for (nl = 0; nl < cvs; ++nl)
+ {
+ PRInt32 ran = RandomNum() % 8;
+ if (0 == ran) {
+ PR_NotifyAllCondVar(cv[nl]);
+ }
+ else for (nc = 0; nc < ran; ++nc) {
+ PR_NotifyCondVar(cv[nl]);
+ }
+ }
+ PR_Unlock(ml);
+ }
+
+ for (index = 0; index < cvs; ++index) {
+ PR_DestroyCondVar(cv[index]);
+ }
+
+ PR_DELETE(cv);
+
+ PR_DestroyLock(ml);
+
+ printf("PASS\n");
+
+ PT_FPrintStats(err, "\nPThread Statistics\n");
+ return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/nsprpub/pr/tests/mbcs.c b/nsprpub/pr/tests/mbcs.c
new file mode 100644
index 0000000000..1a0d08f840
--- /dev/null
+++ b/nsprpub/pr/tests/mbcs.c
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: mbcs.c
+**
+** Synopsis: mbcs {dirName}
+**
+** where dirName is the directory to be traversed. dirName is required.
+**
+** Description:
+** mbcs.c tests use of multi-byte characters, as would be passed to
+** NSPR funtions by internationalized applications.
+**
+** mbcs.c, when run on any single-byte platform, should run correctly.
+** In truth, running the mbcs test on a single-byte platform is
+** really meaningless. mbcs.c, nor any NSPR library or test is not
+** intended for use with any wide character set, including Unicode.
+** mbcs.c should not be included in runtests.ksh because it requires
+** extensive user intervention to set-up and run.
+**
+** mbcs.c should be run on a platform using some form of multi-byte
+** characters. The initial platform for this test is a Japanese
+** language Windows NT 4.0 machine. ... Thank you Noriko Hoshi.
+**
+** To run mbcs.c, the tester should create a directory tree containing
+** some files in the same directory from which the test is run; i.e.
+** the current working directory. The directory and files should be
+** named such that when represented in the local multi-byte character
+** set, one or more characters of the name is longer than a single
+** byte.
+**
+*/
+
+#include <plgetopt.h>
+#include <nspr.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+** Test harness infrastructure
+*/
+PRLogModuleInfo *lm;
+PRLogModuleLevel msgLevel = PR_LOG_NONE;
+PRIntn debug = 0;
+PRUint32 failed_already = 0;
+/* end Test harness infrastructure */
+
+char *dirName = NULL; /* directory name to traverse */
+
+/*
+** Traverse directory
+*/
+static void TraverseDirectory( unsigned char *dir )
+{
+ PRDir *cwd;
+ PRDirEntry *dirEntry;
+ PRFileInfo info;
+ PRStatus rc;
+ PRInt32 err;
+ PRFileDesc *fd;
+ char nextDir[256];
+ char file[256];
+
+ printf("Directory: %s\n", dir );
+ cwd = PR_OpenDir( dir );
+ if ( NULL == cwd ) {
+ printf("PR_OpenDir() failed on directory: %s, with error: %d, %d\n",
+ dir, PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ while( NULL != (dirEntry = PR_ReadDir( cwd, PR_SKIP_BOTH | PR_SKIP_HIDDEN ))) {
+ sprintf( file, "%s/%s", dir, dirEntry->name );
+ rc = PR_GetFileInfo( file, &info );
+ if ( PR_FAILURE == rc ) {
+ printf("PR_GetFileInfo() failed on file: %s, with error: %d, %d\n",
+ dirEntry->name, PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ if ( PR_FILE_FILE == info.type ) {
+ printf("File: %s \tsize: %ld\n", dirEntry->name, info.size );
+ fd = PR_Open( file, PR_RDONLY, 0 );
+ if ( NULL == fd ) {
+ printf("PR_Open() failed. Error: %ld, OSError: %ld\n",
+ PR_GetError(), PR_GetOSError());
+ }
+ rc = PR_Close( fd );
+ if ( PR_FAILURE == rc ) {
+ printf("PR_Close() failed. Error: %ld, OSError: %ld\n",
+ PR_GetError(), PR_GetOSError());
+ }
+ } else if ( PR_FILE_DIRECTORY == info.type ) {
+ sprintf( nextDir, "%s/%s", dir, dirEntry->name );
+ TraverseDirectory(nextDir);
+ } else {
+ printf("type is not interesting for file: %s\n", dirEntry->name );
+ /* keep going */
+ }
+ }
+ /* assume end-of-file, actually could be error */
+
+ rc = PR_CloseDir( cwd );
+ if ( PR_FAILURE == rc ) {
+ printf("PR_CloseDir() failed on directory: %s, with error: %d, %d\n",
+ dir, PR_GetError(), PR_GetOSError());
+ }
+
+} /* end TraverseDirectory() */
+
+int main(int argc, char **argv)
+{
+ { /* get command line options */
+ /*
+ ** Get command line options
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dv");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug */
+ debug = 1;
+ msgLevel = PR_LOG_ERROR;
+ break;
+ case 'v': /* verbose mode */
+ msgLevel = PR_LOG_DEBUG;
+ break;
+ default:
+ dirName = strdup(opt->value);
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ } /* end get command line options */
+
+ lm = PR_NewLogModule("Test"); /* Initialize logging */
+
+
+ if ( dirName == NULL ) {
+ printf("you gotta specify a directory as an operand!\n");
+ exit(1);
+ }
+
+ TraverseDirectory( dirName );
+
+ if (debug) {
+ printf("%s\n", (failed_already)? "FAIL" : "PASS");
+ }
+ return( (failed_already == PR_TRUE )? 1 : 0 );
+} /* main() */
+/* end template.c */
diff --git a/nsprpub/pr/tests/monref.c b/nsprpub/pr/tests/monref.c
new file mode 100644
index 0000000000..3e2ae637bf
--- /dev/null
+++ b/nsprpub/pr/tests/monref.c
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This test program demonstrates that PR_ExitMonitor needs to add a
+ * reference to the PRMonitor object before unlocking the internal
+ * mutex.
+ */
+
+#include "prlog.h"
+#include "prmon.h"
+#include "prthread.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Protected by the PRMonitor 'mon' in the main function. */
+static PRBool done = PR_FALSE;
+
+static void ThreadFunc(void *arg)
+{
+ PRMonitor *mon = (PRMonitor *)arg;
+ PRStatus rv;
+
+ PR_EnterMonitor(mon);
+ done = PR_TRUE;
+ rv = PR_Notify(mon);
+ PR_ASSERT(rv == PR_SUCCESS);
+ rv = PR_ExitMonitor(mon);
+ PR_ASSERT(rv == PR_SUCCESS);
+}
+
+int main()
+{
+ PRMonitor *mon;
+ PRThread *thread;
+ PRStatus rv;
+
+ mon = PR_NewMonitor();
+ if (!mon) {
+ fprintf(stderr, "PR_NewMonitor failed\n");
+ exit(1);
+ }
+
+ thread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, mon,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD, 0);
+ if (!thread) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+
+ PR_EnterMonitor(mon);
+ while (!done) {
+ rv = PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(rv == PR_SUCCESS);
+ }
+ rv = PR_ExitMonitor(mon);
+ PR_ASSERT(rv == PR_SUCCESS);
+
+ /*
+ * Do you agree it should be safe to destroy 'mon' now?
+ * See bug 844784 comment 27.
+ */
+ PR_DestroyMonitor(mon);
+
+ rv = PR_JoinThread(thread);
+ PR_ASSERT(rv == PR_SUCCESS);
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/multiacc.c b/nsprpub/pr/tests/multiacc.c
new file mode 100644
index 0000000000..ff8452d3da
--- /dev/null
+++ b/nsprpub/pr/tests/multiacc.c
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: multiacc.c
+ *
+ * Description:
+ * This test creates multiple threads that accept on the
+ * same listening socket.
+ */
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NUM_SERVER_THREADS 10
+
+static int num_server_threads = NUM_SERVER_THREADS;
+static PRThreadScope thread_scope = PR_GLOBAL_THREAD;
+static PRBool exit_flag = PR_FALSE;
+
+static void ServerThreadFunc(void *arg)
+{
+ PRFileDesc *listenSock = (PRFileDesc *) arg;
+ PRFileDesc *acceptSock;
+ PRErrorCode err;
+ PRStatus status;
+
+ while (!exit_flag) {
+ acceptSock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == acceptSock) {
+ err = PR_GetError();
+ if (PR_PENDING_INTERRUPT_ERROR == err) {
+ printf("server thread is interrupted\n");
+ fflush(stdout);
+ continue;
+ }
+ fprintf(stderr, "PR_Accept failed: %d\n", err);
+ exit(1);
+ }
+ status = PR_Close(acceptSock);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRNetAddr serverAddr;
+ PRFileDesc *dummySock;
+ PRFileDesc *listenSock;
+ PRFileDesc *clientSock;
+ PRThread *dummyThread;
+ PRThread **serverThreads;
+ PRStatus status;
+ PRUint16 port;
+ int idx;
+ PRInt32 nbytes;
+ char buf[1024];
+
+ serverThreads = (PRThread **)
+ PR_Malloc(num_server_threads * sizeof(PRThread *));
+ if (NULL == serverThreads) {
+ fprintf(stderr, "PR_Malloc failed\n");
+ exit(1);
+ }
+
+ /*
+ * Create a dummy listening socket and have the first
+ * (dummy) thread listen on it. This is to ensure that
+ * the first thread becomes the I/O continuation thread
+ * in the pthreads implementation (see ptio.c) and remains
+ * so throughout the test, so that we never have to
+ * recycle the I/O continuation thread.
+ */
+ dummySock = PR_NewTCPSocket();
+ if (NULL == dummySock) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ exit(1);
+ }
+ memset(&serverAddr, 0, sizeof(serverAddr));
+ status = PR_InitializeNetAddr(PR_IpAddrAny, 0, &serverAddr);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_InitializeNetAddr failed\n");
+ exit(1);
+ }
+ status = PR_Bind(dummySock, &serverAddr);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_Bind failed\n");
+ exit(1);
+ }
+ status = PR_Listen(dummySock, 5);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_Listen failed\n");
+ exit(1);
+ }
+
+ listenSock = PR_NewTCPSocket();
+ if (NULL == listenSock) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ exit(1);
+ }
+ memset(&serverAddr, 0, sizeof(serverAddr));
+ status = PR_InitializeNetAddr(PR_IpAddrAny, 0, &serverAddr);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_InitializeNetAddr failed\n");
+ exit(1);
+ }
+ status = PR_Bind(listenSock, &serverAddr);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_Bind failed\n");
+ exit(1);
+ }
+ status = PR_GetSockName(listenSock, &serverAddr);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ exit(1);
+ }
+ port = PR_ntohs(serverAddr.inet.port);
+ status = PR_Listen(listenSock, 5);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_Listen failed\n");
+ exit(1);
+ }
+
+ printf("creating dummy thread\n");
+ fflush(stdout);
+ dummyThread = PR_CreateThread(PR_USER_THREAD,
+ ServerThreadFunc, dummySock, PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ if (NULL == dummyThread) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+ printf("sleeping one second before creating server threads\n");
+ fflush(stdout);
+ PR_Sleep(PR_SecondsToInterval(1));
+ for (idx = 0; idx < num_server_threads; idx++) {
+ serverThreads[idx] = PR_CreateThread(PR_USER_THREAD,
+ ServerThreadFunc, listenSock, PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ if (NULL == serverThreads[idx]) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+ }
+
+ memset(&serverAddr, 0, sizeof(serverAddr));
+ PR_InitializeNetAddr(PR_IpAddrLoopback, port, &serverAddr);
+ clientSock = PR_NewTCPSocket();
+ if (NULL == clientSock) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ exit(1);
+ }
+ printf("sleeping one second before connecting\n");
+ fflush(stdout);
+ PR_Sleep(PR_SecondsToInterval(1));
+ status = PR_Connect(clientSock, &serverAddr, PR_INTERVAL_NO_TIMEOUT);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_Connect failed\n");
+ exit(1);
+ }
+ nbytes = PR_Read(clientSock, buf, sizeof(buf));
+ if (nbytes != 0) {
+ fprintf(stderr, "expected 0 bytes but got %d bytes\n", nbytes);
+ exit(1);
+ }
+ status = PR_Close(clientSock);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ printf("sleeping one second before shutting down server threads\n");
+ fflush(stdout);
+ PR_Sleep(PR_SecondsToInterval(1));
+
+ exit_flag = PR_TRUE;
+ status = PR_Interrupt(dummyThread);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_Interrupt failed\n");
+ exit(1);
+ }
+ status = PR_JoinThread(dummyThread);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+ for (idx = 0; idx < num_server_threads; idx++) {
+ status = PR_Interrupt(serverThreads[idx]);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_Interrupt failed\n");
+ exit(1);
+ }
+ status = PR_JoinThread(serverThreads[idx]);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+ }
+ PR_Free(serverThreads);
+ status = PR_Close(dummySock);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ status = PR_Close(listenSock);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/multiwait.c b/nsprpub/pr/tests/multiwait.c
new file mode 100644
index 0000000000..62a07f0428
--- /dev/null
+++ b/nsprpub/pr/tests/multiwait.c
@@ -0,0 +1,785 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prio.h"
+#include "prprf.h"
+#include "prlog.h"
+#include "prmem.h"
+#include "pratom.h"
+#include "prlock.h"
+#include "prmwait.h"
+#include "prclist.h"
+#include "prerror.h"
+#include "prinrval.h"
+#include "prnetdb.h"
+#include "prthread.h"
+
+#include "plstr.h"
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include <string.h>
+
+typedef struct Shared
+{
+ const char *title;
+ PRLock *list_lock;
+ PRWaitGroup *group;
+ PRIntervalTime timeout;
+} Shared;
+
+typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity;
+
+#ifdef DEBUG
+#define PORT_INC_DO +100
+#else
+#define PORT_INC_DO
+#endif
+#ifdef IS_64
+#define PORT_INC_3264 +200
+#else
+#define PORT_INC_3264
+#endif
+
+static PRFileDesc *debug = NULL;
+static PRInt32 desc_allocated = 0;
+static PRUint16 default_port = 12273 PORT_INC_DO PORT_INC_3264;
+static enum Verbosity verbosity = quiet;
+static PRInt32 ops_required = 1000, ops_done = 0;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+static PRIntn client_threads = 20, worker_threads = 2, wait_objects = 50;
+
+#if defined(DEBUG)
+#define MW_ASSERT(_expr) \
+ ((_expr)?((void)0):_MW_Assert(# _expr,__FILE__,__LINE__))
+static void _MW_Assert(const char *s, const char *file, PRIntn ln)
+{
+ if (NULL != debug) {
+ PL_FPrintError(debug, NULL);
+ }
+ PR_Assert(s, file, ln);
+} /* _MW_Assert */
+#else
+#define MW_ASSERT(_expr)
+#endif
+
+static void PrintRecvDesc(PRRecvWait *desc, const char *msg)
+{
+ const char *tag[] = {
+ "PR_MW_INTERRUPT", "PR_MW_TIMEOUT",
+ "PR_MW_FAILURE", "PR_MW_SUCCESS", "PR_MW_PENDING"
+ };
+ PR_fprintf(
+ debug, "%s: PRRecvWait(@0x%x): {fd: 0x%x, outcome: %s, tmo: %u}\n",
+ msg, desc, desc->fd, tag[desc->outcome + 3], desc->timeout);
+} /* PrintRecvDesc */
+
+static Shared *MakeShared(const char *title)
+{
+ Shared *shared = PR_NEWZAP(Shared);
+ shared->group = PR_CreateWaitGroup(1);
+ shared->timeout = PR_SecondsToInterval(1);
+ shared->list_lock = PR_NewLock();
+ shared->title = title;
+ return shared;
+} /* MakeShared */
+
+static void DestroyShared(Shared *shared)
+{
+ PRStatus rv;
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: destroying group\n", shared->title);
+ }
+ rv = PR_DestroyWaitGroup(shared->group);
+ MW_ASSERT(PR_SUCCESS == rv);
+ PR_DestroyLock(shared->list_lock);
+ PR_DELETE(shared);
+} /* DestroyShared */
+
+static PRRecvWait *CreateRecvWait(PRFileDesc *fd, PRIntervalTime timeout)
+{
+ PRRecvWait *desc_out = PR_NEWZAP(PRRecvWait);
+ MW_ASSERT(NULL != desc_out);
+
+ MW_ASSERT(NULL != fd);
+ desc_out->fd = fd;
+ desc_out->timeout = timeout;
+ desc_out->buffer.length = 120;
+ desc_out->buffer.start = PR_CALLOC(120);
+
+ PR_AtomicIncrement(&desc_allocated);
+
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc_out, "Allocated");
+ }
+ return desc_out;
+} /* CreateRecvWait */
+
+static void DestroyRecvWait(PRRecvWait *desc_out)
+{
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc_out, "Destroying");
+ }
+ PR_Close(desc_out->fd);
+ if (NULL != desc_out->buffer.start) {
+ PR_DELETE(desc_out->buffer.start);
+ }
+ PR_Free(desc_out);
+ (void)PR_AtomicDecrement(&desc_allocated);
+} /* DestroyRecvWait */
+
+static void CancelGroup(Shared *shared)
+{
+ PRRecvWait *desc_out;
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s Reclaiming wait descriptors\n", shared->title);
+ }
+
+ do
+ {
+ desc_out = PR_CancelWaitGroup(shared->group);
+ if (NULL != desc_out) {
+ DestroyRecvWait(desc_out);
+ }
+ } while (NULL != desc_out);
+
+ MW_ASSERT(0 == desc_allocated);
+ MW_ASSERT(PR_GROUP_EMPTY_ERROR == PR_GetError());
+} /* CancelGroup */
+
+static void PR_CALLBACK ClientThread(void* arg)
+{
+ PRStatus rv;
+ PRInt32 bytes;
+ PRIntn empty_flags = 0;
+ PRNetAddr server_address;
+ unsigned char buffer[100];
+ Shared *shared = (Shared*)arg;
+ PRFileDesc *server = PR_NewTCPSocket();
+ if ((NULL == server)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
+ return;
+ }
+ MW_ASSERT(NULL != server);
+
+ if (verbosity > chatty) {
+ PR_fprintf(debug, "%s: Server socket @0x%x\n", shared->title, server);
+ }
+
+ /* Initialize the buffer so that Purify won't complain */
+ memset(buffer, 0, sizeof(buffer));
+
+ rv = PR_InitializeNetAddr(PR_IpAddrLoopback, default_port, &server_address);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: Client opening connection\n", shared->title);
+ }
+ rv = PR_Connect(server, &server_address, PR_INTERVAL_NO_TIMEOUT);
+
+ if (PR_FAILURE == rv)
+ {
+ if (verbosity > silent) {
+ PL_FPrintError(debug, "Client connect failed");
+ }
+ return;
+ }
+
+ while (ops_done < ops_required)
+ {
+ bytes = PR_Send(
+ server, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if ((-1 == bytes) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
+ break;
+ }
+ MW_ASSERT(sizeof(buffer) == bytes);
+ if (verbosity > chatty)
+ PR_fprintf(
+ debug, "%s: Client sent %d bytes\n",
+ shared->title, sizeof(buffer));
+ bytes = PR_Recv(
+ server, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty)
+ PR_fprintf(
+ debug, "%s: Client received %d bytes\n",
+ shared->title, sizeof(buffer));
+ if ((-1 == bytes) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
+ break;
+ }
+ MW_ASSERT(sizeof(buffer) == bytes);
+ PR_Sleep(shared->timeout);
+ }
+ rv = PR_Close(server);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+} /* ClientThread */
+
+static void OneInThenCancelled(Shared *shared)
+{
+ PRStatus rv;
+ PRRecvWait *desc_out, *desc_in = PR_NEWZAP(PRRecvWait);
+
+ shared->timeout = PR_INTERVAL_NO_TIMEOUT;
+
+ desc_in->fd = PR_NewTCPSocket();
+ desc_in->timeout = shared->timeout;
+
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc_in, "Adding desc");
+ }
+
+ rv = PR_AddWaitFileDesc(shared->group, desc_in);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc_in, "Cancelling");
+ }
+ rv = PR_CancelWaitFileDesc(shared->group, desc_in);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ desc_out = PR_WaitRecvReady(shared->group);
+ MW_ASSERT(desc_out == desc_in);
+ MW_ASSERT(PR_MW_INTERRUPT == desc_out->outcome);
+ MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError());
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc_out, "Ready");
+ }
+
+ rv = PR_Close(desc_in->fd);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: destroying group\n", shared->title);
+ }
+
+ PR_DELETE(desc_in);
+} /* OneInThenCancelled */
+
+static void OneOpOneThread(Shared *shared)
+{
+ PRStatus rv;
+ PRRecvWait *desc_out, *desc_in = PR_NEWZAP(PRRecvWait);
+
+ desc_in->fd = PR_NewTCPSocket();
+ desc_in->timeout = shared->timeout;
+
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc_in, "Adding desc");
+ }
+
+ rv = PR_AddWaitFileDesc(shared->group, desc_in);
+ MW_ASSERT(PR_SUCCESS == rv);
+ desc_out = PR_WaitRecvReady(shared->group);
+ MW_ASSERT(desc_out == desc_in);
+ MW_ASSERT(PR_MW_TIMEOUT == desc_out->outcome);
+ MW_ASSERT(PR_IO_TIMEOUT_ERROR == PR_GetError());
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc_out, "Ready");
+ }
+
+ rv = PR_Close(desc_in->fd);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ PR_DELETE(desc_in);
+} /* OneOpOneThread */
+
+static void ManyOpOneThread(Shared *shared)
+{
+ PRStatus rv;
+ PRIntn index;
+ PRRecvWait *desc_in;
+ PRRecvWait *desc_out;
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: adding %d descs\n", shared->title, wait_objects);
+ }
+
+ for (index = 0; index < wait_objects; ++index)
+ {
+ desc_in = CreateRecvWait(PR_NewTCPSocket(), shared->timeout);
+
+ rv = PR_AddWaitFileDesc(shared->group, desc_in);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+
+ while (ops_done < ops_required)
+ {
+ desc_out = PR_WaitRecvReady(shared->group);
+ MW_ASSERT(PR_MW_TIMEOUT == desc_out->outcome);
+ MW_ASSERT(PR_IO_TIMEOUT_ERROR == PR_GetError());
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc_out, "Ready/readding");
+ }
+ rv = PR_AddWaitFileDesc(shared->group, desc_out);
+ MW_ASSERT(PR_SUCCESS == rv);
+ (void)PR_AtomicIncrement(&ops_done);
+ }
+
+ CancelGroup(shared);
+} /* ManyOpOneThread */
+
+static void PR_CALLBACK SomeOpsThread(void *arg)
+{
+ PRRecvWait *desc_out;
+ PRStatus rv = PR_SUCCESS;
+ Shared *shared = (Shared*)arg;
+ do /* until interrupted */
+ {
+ desc_out = PR_WaitRecvReady(shared->group);
+ if (NULL == desc_out)
+ {
+ MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError());
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "Aborted\n");
+ }
+ break;
+ }
+ MW_ASSERT(PR_MW_TIMEOUT == desc_out->outcome);
+ MW_ASSERT(PR_IO_TIMEOUT_ERROR == PR_GetError());
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc_out, "Ready");
+ }
+
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc_out, "Re-Adding");
+ }
+ desc_out->timeout = shared->timeout;
+ rv = PR_AddWaitFileDesc(shared->group, desc_out);
+ PR_AtomicIncrement(&ops_done);
+ if (ops_done > ops_required) {
+ break;
+ }
+ } while (PR_SUCCESS == rv);
+ MW_ASSERT(PR_SUCCESS == rv);
+} /* SomeOpsThread */
+
+static void SomeOpsSomeThreads(Shared *shared)
+{
+ PRStatus rv;
+ PRThread **thread;
+ PRIntn index;
+ PRRecvWait *desc_in;
+
+ thread = (PRThread**)PR_CALLOC(sizeof(PRThread*) * worker_threads);
+
+ /* Create some threads */
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: creating threads\n", shared->title);
+ }
+ for (index = 0; index < worker_threads; ++index)
+ {
+ thread[index] = PR_CreateThread(
+ PR_USER_THREAD, SomeOpsThread, shared,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ }
+
+ /* then create some operations */
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: creating desc\n", shared->title);
+ }
+ for (index = 0; index < wait_objects; ++index)
+ {
+ desc_in = CreateRecvWait(PR_NewTCPSocket(), shared->timeout);
+ rv = PR_AddWaitFileDesc(shared->group, desc_in);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: sleeping\n", shared->title);
+ }
+ while (ops_done < ops_required) {
+ PR_Sleep(shared->timeout);
+ }
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: interrupting/joining threads\n", shared->title);
+ }
+ for (index = 0; index < worker_threads; ++index)
+ {
+ rv = PR_Interrupt(thread[index]);
+ MW_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(thread[index]);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+ PR_DELETE(thread);
+
+ CancelGroup(shared);
+} /* SomeOpsSomeThreads */
+
+static PRStatus ServiceRequest(Shared *shared, PRRecvWait *desc)
+{
+ PRInt32 bytes_out;
+
+ if (verbosity > chatty)
+ PR_fprintf(
+ debug, "%s: Service received %d bytes\n",
+ shared->title, desc->bytesRecv);
+
+ if (0 == desc->bytesRecv) {
+ goto quitting;
+ }
+ if ((-1 == desc->bytesRecv)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
+ goto aborted;
+ }
+
+ bytes_out = PR_Send(
+ desc->fd, desc->buffer.start, desc->bytesRecv, 0, shared->timeout);
+ if (verbosity > chatty)
+ PR_fprintf(
+ debug, "%s: Service sent %d bytes\n",
+ shared->title, bytes_out);
+
+ if ((-1 == bytes_out)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
+ goto aborted;
+ }
+ MW_ASSERT(bytes_out == desc->bytesRecv);
+
+ return PR_SUCCESS;
+
+aborted:
+quitting:
+ return PR_FAILURE;
+} /* ServiceRequest */
+
+static void PR_CALLBACK ServiceThread(void *arg)
+{
+ PRStatus rv = PR_SUCCESS;
+ PRRecvWait *desc_out = NULL;
+ Shared *shared = (Shared*)arg;
+ do /* until interrupted */
+ {
+ if (NULL != desc_out)
+ {
+ desc_out->timeout = PR_INTERVAL_NO_TIMEOUT;
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc_out, "Service re-adding");
+ }
+ rv = PR_AddWaitFileDesc(shared->group, desc_out);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+
+ desc_out = PR_WaitRecvReady(shared->group);
+ if (NULL == desc_out)
+ {
+ MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError());
+ break;
+ }
+
+ switch (desc_out->outcome)
+ {
+ case PR_MW_SUCCESS:
+ {
+ PR_AtomicIncrement(&ops_done);
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc_out, "Service ready");
+ }
+ rv = ServiceRequest(shared, desc_out);
+ break;
+ }
+ case PR_MW_INTERRUPT:
+ MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError());
+ rv = PR_FAILURE; /* if interrupted, then exit */
+ break;
+ case PR_MW_TIMEOUT:
+ MW_ASSERT(PR_IO_TIMEOUT_ERROR == PR_GetError());
+ case PR_MW_FAILURE:
+ if (verbosity > silent) {
+ PL_FPrintError(debug, "RecvReady failure");
+ }
+ break;
+ default:
+ break;
+ }
+ } while (PR_SUCCESS == rv);
+
+ if (NULL != desc_out) {
+ DestroyRecvWait(desc_out);
+ }
+
+} /* ServiceThread */
+
+static void PR_CALLBACK EnumerationThread(void *arg)
+{
+ PRStatus rv;
+ PRIntn count;
+ PRRecvWait *desc;
+ Shared *shared = (Shared*)arg;
+ PRIntervalTime five_seconds = PR_SecondsToInterval(5);
+ PRMWaitEnumerator *enumerator = PR_CreateMWaitEnumerator(shared->group);
+ MW_ASSERT(NULL != enumerator);
+
+ while (PR_SUCCESS == PR_Sleep(five_seconds))
+ {
+ count = 0;
+ desc = NULL;
+ while (NULL != (desc = PR_EnumerateWaitGroup(enumerator, desc)))
+ {
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc, shared->title);
+ }
+ count += 1;
+ }
+ if (verbosity > silent)
+ PR_fprintf(debug,
+ "%s Enumerated %d objects\n", shared->title, count);
+ }
+
+ MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError());
+
+
+ rv = PR_DestroyMWaitEnumerator(enumerator);
+ MW_ASSERT(PR_SUCCESS == rv);
+} /* EnumerationThread */
+
+static void PR_CALLBACK ServerThread(void *arg)
+{
+ PRStatus rv;
+ PRIntn index;
+ PRRecvWait *desc_in;
+ PRThread **worker_thread;
+ Shared *shared = (Shared*)arg;
+ PRFileDesc *listener, *service;
+ PRNetAddr server_address, client_address;
+
+ worker_thread = (PRThread**)PR_CALLOC(sizeof(PRThread*) * worker_threads);
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: Server creating worker_threads\n", shared->title);
+ }
+ for (index = 0; index < worker_threads; ++index)
+ {
+ worker_thread[index] = PR_CreateThread(
+ PR_USER_THREAD, ServiceThread, shared,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ }
+
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &server_address);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ listener = PR_NewTCPSocket(); MW_ASSERT(NULL != listener);
+ if (verbosity > chatty)
+ PR_fprintf(
+ debug, "%s: Server listener socket @0x%x\n",
+ shared->title, listener);
+ rv = PR_Bind(listener, &server_address); MW_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Listen(listener, 10); MW_ASSERT(PR_SUCCESS == rv);
+ while (ops_done < ops_required)
+ {
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: Server accepting connection\n", shared->title);
+ }
+ service = PR_Accept(listener, &client_address, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == service)
+ {
+ if (PR_PENDING_INTERRUPT_ERROR == PR_GetError()) {
+ break;
+ }
+ PL_PrintError("Accept failed");
+ MW_ASSERT(PR_FALSE && "Accept failed");
+ }
+ else
+ {
+ desc_in = CreateRecvWait(service, shared->timeout);
+ desc_in->timeout = PR_INTERVAL_NO_TIMEOUT;
+ if (verbosity > chatty) {
+ PrintRecvDesc(desc_in, "Service adding");
+ }
+ rv = PR_AddWaitFileDesc(shared->group, desc_in);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+ }
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: Server interrupting worker_threads\n", shared->title);
+ }
+ for (index = 0; index < worker_threads; ++index)
+ {
+ rv = PR_Interrupt(worker_thread[index]);
+ MW_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(worker_thread[index]);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+ PR_DELETE(worker_thread);
+
+ PR_Close(listener);
+
+ CancelGroup(shared);
+
+} /* ServerThread */
+
+static void RealOneGroupIO(Shared *shared)
+{
+ /*
+ ** Create a server that listens for connections and then services
+ ** requests that come in over those connections. The server never
+ ** deletes a connection and assumes a basic RPC model of operation.
+ **
+ ** Use worker_threads threads to service how every many open ports
+ ** there might be.
+ **
+ ** Oh, ya. Almost forget. Create (some) clients as well.
+ */
+ PRStatus rv;
+ PRIntn index;
+ PRThread *server_thread, *enumeration_thread, **client_thread;
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: creating server_thread\n", shared->title);
+ }
+
+ server_thread = PR_CreateThread(
+ PR_USER_THREAD, ServerThread, shared,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: creating enumeration_thread\n", shared->title);
+ }
+
+ enumeration_thread = PR_CreateThread(
+ PR_USER_THREAD, EnumerationThread, shared,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: snoozing before creating clients\n", shared->title);
+ }
+ PR_Sleep(5 * shared->timeout);
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: creating client_threads\n", shared->title);
+ }
+ client_thread = (PRThread**)PR_CALLOC(sizeof(PRThread*) * client_threads);
+ for (index = 0; index < client_threads; ++index)
+ {
+ client_thread[index] = PR_CreateThread(
+ PR_USER_THREAD, ClientThread, shared,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ }
+
+ while (ops_done < ops_required) {
+ PR_Sleep(shared->timeout);
+ }
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: interrupting/joining client_threads\n", shared->title);
+ }
+ for (index = 0; index < client_threads; ++index)
+ {
+ rv = PR_Interrupt(client_thread[index]);
+ MW_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(client_thread[index]);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+ PR_DELETE(client_thread);
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: interrupting/joining enumeration_thread\n", shared->title);
+ }
+ rv = PR_Interrupt(enumeration_thread);
+ MW_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(enumeration_thread);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ if (verbosity > quiet) {
+ PR_fprintf(debug, "%s: interrupting/joining server_thread\n", shared->title);
+ }
+ rv = PR_Interrupt(server_thread);
+ MW_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(server_thread);
+ MW_ASSERT(PR_SUCCESS == rv);
+} /* RealOneGroupIO */
+
+static void RunThisOne(
+ void (*func)(Shared*), const char *name, const char *test_name)
+{
+ Shared *shared;
+ if ((NULL == test_name) || (0 == PL_strcmp(name, test_name)))
+ {
+ if (verbosity > silent) {
+ PR_fprintf(debug, "%s()\n", name);
+ }
+ shared = MakeShared(name);
+ ops_done = 0;
+ func(shared); /* run the test */
+ MW_ASSERT(0 == desc_allocated);
+ DestroyShared(shared);
+ }
+} /* RunThisOne */
+
+static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta)
+{
+ return (Verbosity)(((PRIntn)verbosity) + delta);
+} /* ChangeVerbosity */
+
+int main(int argc, char **argv)
+{
+ PLOptStatus os;
+ const char *test_name = NULL;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dqGc:o:p:t:w:");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 0:
+ test_name = opt->value;
+ break;
+ case 'd': /* debug mode */
+ if (verbosity < noisy) {
+ verbosity = ChangeVerbosity(verbosity, 1);
+ }
+ break;
+ case 'q': /* debug mode */
+ if (verbosity > silent) {
+ verbosity = ChangeVerbosity(verbosity, -1);
+ }
+ break;
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'c': /* number of client threads */
+ client_threads = atoi(opt->value);
+ break;
+ case 'o': /* operations to compelete */
+ ops_required = atoi(opt->value);
+ break;
+ case 'p': /* default port */
+ default_port = atoi(opt->value);
+ break;
+ case 't': /* number of threads waiting */
+ worker_threads = atoi(opt->value);
+ break;
+ case 'w': /* number of wait objects */
+ wait_objects = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (verbosity > 0) {
+ debug = PR_GetSpecialFD(PR_StandardError);
+ }
+
+ RunThisOne(OneInThenCancelled, "OneInThenCancelled", test_name);
+ RunThisOne(OneOpOneThread, "OneOpOneThread", test_name);
+ RunThisOne(ManyOpOneThread, "ManyOpOneThread", test_name);
+ RunThisOne(SomeOpsSomeThreads, "SomeOpsSomeThreads", test_name);
+ RunThisOne(RealOneGroupIO, "RealOneGroupIO", test_name);
+ return 0;
+} /* main */
+
+/* multwait.c */
diff --git a/nsprpub/pr/tests/nameshm1.c b/nsprpub/pr/tests/nameshm1.c
new file mode 100644
index 0000000000..ca24b4e813
--- /dev/null
+++ b/nsprpub/pr/tests/nameshm1.c
@@ -0,0 +1,586 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: nameshm1.c -- Test Named Shared Memory
+**
+** Description:
+** nameshm1 tests Named Shared Memory. nameshm1 performs two tests of
+** named shared memory.
+**
+** The first test is a basic test. The basic test operates as a single
+** process. The process exercises all the API elements of the facility.
+** This test also attempts to write to all locations in the shared
+** memory.
+**
+** The second test is a client-server test. The client-server test
+** creates a new instance of nameshm1, passing the -C argument to the
+** new process; this creates the client-side process. The server-side
+** (the instance of nameshm1 created from the command line) and the
+** client-side interact via inter-process semaphores to verify that the
+** shared memory segment can be read and written by both sides in a
+** synchronized maner.
+**
+** Note: Because this test runs in two processes, the log files created
+** by the test are not in chronological sequence; makes it hard to read.
+** As a temporary circumvention, I changed the definition(s) of the
+** _PUT_LOG() macro in prlog.c to force a flushall(), or equivalent.
+** This causes the log entries to be emitted in true chronological
+** order.
+**
+** Synopsis: nameshm1 [options] [name]
+**
+** Options:
+** -d Enables debug trace via PR_LOG()
+** -v Enables verbose mode debug trace via PR_LOG()
+** -w Causes the basic test to attempt to write to the segment
+** mapped as read-only. When this option is specified, the
+** test should crash with a seg-fault; this is a destructive
+** test and is considered successful when it seg-faults.
+**
+** -C Causes nameshm1 to start as the client-side of a
+** client-server pair of processes. Only the instance
+** of nameshm1 operating as the server-side process should
+** specify the -C option when creating the client-side process;
+** the -C option should not be specified at the command line.
+** The client-side uses the shared memory segment created by
+** the server-side to communicate with the server-side
+** process.
+**
+** -p <n> Specify the number of iterations the client-server tests
+** should perform. Default: 1000.
+**
+** -s <n> Size, in KBytes (1024), of the shared memory segment.
+** Default: (10 * 1024)
+**
+** -i <n> Number of client-side iterations. Default: 3
+**
+** name specifies the name of the shared memory segment to be used.
+** Default: /tmp/xxxNSPRshm
+**
+**
+** See also: prshm.h
+**
+** /lth. Aug-1999.
+*/
+
+#include <plgetopt.h>
+#include <nspr.h>
+#include <stdlib.h>
+#include <string.h>
+#include <private/primpl.h>
+
+#ifdef DEBUG
+#define SEM_D "D"
+#else
+#define SEM_D
+#endif
+#ifdef IS_64
+#define SEM_64 "64"
+#else
+#define SEM_64
+#endif
+
+#define SEM_NAME1 "/tmp/nameshmSEM1" SEM_D SEM_64
+#define SEM_NAME2 "/tmp/nameshmSEM2" SEM_D SEM_64
+#define OPT_NAME "/tmp/xxxNSPRshm" SEM_D SEM_64
+#define EXE_NAME "nameshm1"
+#define SEM_MODE 0666
+#define SHM_MODE 0666
+
+#define NameSize (1024)
+
+PRIntn debug = 0;
+PRIntn failed_already = 0;
+PRLogModuleLevel msgLevel = PR_LOG_NONE;
+PRLogModuleInfo *lm;
+
+/* command line options */
+PRIntn optDebug = 0;
+PRIntn optVerbose = 0;
+PRUint32 optWriteRO = 0; /* test write to read-only memory. should crash */
+PRUint32 optClient = 0;
+PRUint32 optCreate = 1;
+PRUint32 optAttachRW = 1;
+PRUint32 optAttachRO = 1;
+PRUint32 optClose = 1;
+PRUint32 optDelete = 1;
+PRInt32 optPing = 1000;
+PRUint32 optSize = (10 * 1024 );
+PRInt32 optClientIterations = 3;
+char optName[NameSize] = OPT_NAME;
+
+char buf[1024] = "";
+
+
+static void BasicTest( void )
+{
+ PRSharedMemory *shm;
+ char *addr; /* address of shared memory segment */
+ PRUint32 i;
+ PRInt32 rc;
+
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Begin BasicTest" ));
+
+ if ( PR_FAILURE == PR_DeleteSharedMemory( optName )) {
+ PR_LOG( lm, msgLevel,
+ ("nameshm1: Initial PR_DeleteSharedMemory() failed. No problem"));
+ } else
+ PR_LOG( lm, msgLevel,
+ ("nameshm1: Initial PR_DeleteSharedMemory() success"));
+
+
+ shm = PR_OpenSharedMemory( optName, optSize, (PR_SHM_CREATE | PR_SHM_EXCL), SHM_MODE );
+ if ( NULL == shm )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RW Create: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RW Create: success: %p", shm ));
+
+ addr = PR_AttachSharedMemory( shm, 0 );
+ if ( NULL == addr )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RW Attach: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RW Attach: success: %p", addr ));
+
+ /* fill memory with i */
+ for ( i = 0; i < optSize ; i++ )
+ {
+ *(addr + i) = i;
+ }
+
+ rc = PR_DetachSharedMemory( shm, addr );
+ if ( PR_FAILURE == rc )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RW Detach: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RW Detach: success: " ));
+
+ rc = PR_CloseSharedMemory( shm );
+ if ( PR_FAILURE == rc )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RW Close: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RW Close: success: " ));
+
+ rc = PR_DeleteSharedMemory( optName );
+ if ( PR_FAILURE == rc )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RW Delete: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RW Delete: success: " ));
+
+ PR_LOG( lm, msgLevel,
+ ("nameshm1: BasicTest(): Passed"));
+
+ return;
+} /* end BasicTest() */
+
+static void ReadOnlyTest( void )
+{
+ PRSharedMemory *shm;
+ char *roAddr; /* read-only address of shared memory segment */
+ PRInt32 rc;
+
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Begin ReadOnlyTest" ));
+
+ shm = PR_OpenSharedMemory( optName, optSize, (PR_SHM_CREATE | PR_SHM_EXCL), SHM_MODE);
+ if ( NULL == shm )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RO Create: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RO Create: success: %p", shm ));
+
+
+ roAddr = PR_AttachSharedMemory( shm, PR_SHM_READONLY );
+ if ( NULL == roAddr )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RO Attach: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RO Attach: success: %p", roAddr ));
+
+ if ( optWriteRO )
+ {
+ *roAddr = 0x00; /* write to read-only memory */
+ failed_already = 1;
+ PR_LOG( lm, msgLevel, ("nameshm1: Wrote to read-only memory segment!"));
+ return;
+ }
+
+ rc = PR_DetachSharedMemory( shm, roAddr );
+ if ( PR_FAILURE == rc )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RO Detach: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RO Detach: success: " ));
+
+ rc = PR_CloseSharedMemory( shm );
+ if ( PR_FAILURE == rc )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RO Close: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RO Close: success: " ));
+
+ rc = PR_DeleteSharedMemory( optName );
+ if ( PR_FAILURE == rc )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RO Destroy: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: RO Destroy: success: " ));
+
+ PR_LOG( lm, msgLevel,
+ ("nameshm1: ReadOnlyTest(): Passed"));
+
+ return;
+} /* end ReadOnlyTest() */
+
+static void DoClient( void )
+{
+ PRStatus rc;
+ PRSem *sem1, *sem2;
+ PRSharedMemory *shm;
+ PRUint32 *addr;
+ PRInt32 i;
+
+ PR_LOG( lm, msgLevel,
+ ("nameshm1: DoClient(): Starting"));
+
+ sem1 = PR_OpenSemaphore( SEM_NAME1, 0, 0, 0 );
+ PR_ASSERT( sem1 );
+
+ sem2 = PR_OpenSemaphore( SEM_NAME2, 0, 0, 0 );
+ PR_ASSERT( sem1 );
+
+ shm = PR_OpenSharedMemory( optName, optSize, 0, SHM_MODE );
+ if ( NULL == shm )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: DoClient(): Create: Error: %ld. OSError: %ld",
+ PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: DoClient(): Create: success: %p", shm ));
+
+ addr = PR_AttachSharedMemory( shm, 0 );
+ if ( NULL == addr )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: DoClient(): Attach: Error: %ld. OSError: %ld",
+ PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: DoClient(): Attach: success: %p", addr ));
+
+ PR_LOG( lm, msgLevel,
+ ( "Client found: %s", addr));
+
+ PR_Sleep(PR_SecondsToInterval(4));
+ for ( i = 0 ; i < optPing ; i++ )
+ {
+ rc = PR_WaitSemaphore( sem2 );
+ PR_ASSERT( PR_FAILURE != rc );
+
+ (*addr)++;
+ PR_ASSERT( (*addr % 2) == 0 );
+ if ( optVerbose )
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Client ping: %d, i: %d", *addr, i));
+
+ rc = PR_PostSemaphore( sem1 );
+ PR_ASSERT( PR_FAILURE != rc );
+ }
+
+ rc = PR_CloseSemaphore( sem1 );
+ PR_ASSERT( PR_FAILURE != rc );
+
+ rc = PR_CloseSemaphore( sem2 );
+ PR_ASSERT( PR_FAILURE != rc );
+
+ rc = PR_DetachSharedMemory( shm, addr );
+ if ( PR_FAILURE == rc )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: DoClient(): Detach: Error: %ld. OSError: %ld",
+ PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: DoClient(): Detach: success: " ));
+
+ rc = PR_CloseSharedMemory( shm );
+ if ( PR_FAILURE == rc )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: DoClient(): Close: Error: %ld. OSError: %ld",
+ PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: DoClient(): Close: success: " ));
+
+ return;
+} /* end DoClient() */
+
+static void ClientServerTest( void )
+{
+ PRStatus rc;
+ PRSem *sem1, *sem2;
+ PRProcess *proc;
+ PRInt32 exit_status;
+ PRSharedMemory *shm;
+ PRUint32 *addr;
+ PRInt32 i;
+ char *child_argv[8];
+ char buf[24];
+
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Begin ClientServerTest" ));
+
+ rc = PR_DeleteSharedMemory( optName );
+ if ( PR_FAILURE == rc )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Server: Destroy: failed. No problem"));
+ } else
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Server: Destroy: success" ));
+
+
+ shm = PR_OpenSharedMemory( optName, optSize, (PR_SHM_CREATE | PR_SHM_EXCL), SHM_MODE);
+ if ( NULL == shm )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Server: Create: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Server: Create: success: %p", shm ));
+
+ addr = PR_AttachSharedMemory( shm, 0 );
+ if ( NULL == addr )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Server: Attach: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Server: Attach: success: %p", addr ));
+
+ sem1 = PR_OpenSemaphore( SEM_NAME1, PR_SEM_CREATE, SEM_MODE, 0 );
+ PR_ASSERT( sem1 );
+
+ sem2 = PR_OpenSemaphore( SEM_NAME2, PR_SEM_CREATE, SEM_MODE, 1 );
+ PR_ASSERT( sem1 );
+
+ strcpy( (char*)addr, "FooBar" );
+
+ child_argv[0] = EXE_NAME;
+ child_argv[1] = "-C";
+ child_argv[2] = "-p";
+ sprintf( buf, "%d", optPing );
+ child_argv[3] = buf;
+ child_argv[4] = optName;
+ child_argv[5] = NULL;
+
+ proc = PR_CreateProcess(child_argv[0], child_argv, NULL, NULL);
+ PR_ASSERT( proc );
+
+ PR_Sleep( PR_SecondsToInterval(4));
+
+ *addr = 1;
+ for ( i = 0 ; i < optPing ; i++ )
+ {
+ rc = PR_WaitSemaphore( sem1 );
+ PR_ASSERT( PR_FAILURE != rc );
+
+ (*addr)++;
+ PR_ASSERT( (*addr % 2) == 1 );
+ if ( optVerbose )
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Server pong: %d, i: %d", *addr, i));
+
+
+ rc = PR_PostSemaphore( sem2 );
+ PR_ASSERT( PR_FAILURE != rc );
+ }
+
+ rc = PR_WaitProcess( proc, &exit_status );
+ PR_ASSERT( PR_FAILURE != rc );
+
+ rc = PR_CloseSemaphore( sem1 );
+ PR_ASSERT( PR_FAILURE != rc );
+
+ rc = PR_CloseSemaphore( sem2 );
+ PR_ASSERT( PR_FAILURE != rc );
+
+ rc = PR_DeleteSemaphore( SEM_NAME1 );
+ PR_ASSERT( PR_FAILURE != rc );
+
+ rc = PR_DeleteSemaphore( SEM_NAME2 );
+ PR_ASSERT( PR_FAILURE != rc );
+
+ rc = PR_DetachSharedMemory( shm, addr );
+ if ( PR_FAILURE == rc )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Server: Detach: Error: %ld. OSError: %ld",
+ PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Server: Detach: success: " ));
+
+ rc = PR_CloseSharedMemory( shm );
+ if ( PR_FAILURE == rc )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Server: Close: Error: %ld. OSError: %ld",
+ PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Server: Close: success: " ));
+
+ rc = PR_DeleteSharedMemory( optName );
+ if ( PR_FAILURE == rc )
+ {
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Server: Destroy: Error: %ld. OSError: %ld",
+ PR_GetError(), PR_GetOSError()));
+ failed_already = 1;
+ return;
+ }
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Server: Destroy: success" ));
+
+ return;
+} /* end ClientServerTest() */
+
+int main(int argc, char **argv)
+{
+ {
+ /*
+ ** Get command line options
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Cdvw:s:p:i:");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'v': /* debug mode */
+ optVerbose = 1;
+ /* no break! fall into debug option */
+ case 'd': /* debug mode */
+ debug = 1;
+ msgLevel = PR_LOG_DEBUG;
+ break;
+ case 'w': /* try writing to memory mapped read-only */
+ optWriteRO = 1;
+ break;
+ case 'C':
+ optClient = 1;
+ break;
+ case 's':
+ optSize = atol(opt->value) * 1024;
+ break;
+ case 'p':
+ optPing = atol(opt->value);
+ break;
+ case 'i':
+ optClientIterations = atol(opt->value);
+ break;
+ default:
+ strcpy( optName, opt->value );
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ }
+
+ lm = PR_NewLogModule("Test"); /* Initialize logging */
+
+ PR_LOG( lm, msgLevel,
+ ( "nameshm1: Starting" ));
+
+ if ( optClient )
+ {
+ DoClient();
+ } else {
+ BasicTest();
+ if ( failed_already != 0 ) {
+ goto Finished;
+ }
+ ReadOnlyTest();
+ if ( failed_already != 0 ) {
+ goto Finished;
+ }
+ ClientServerTest();
+ }
+
+Finished:
+ if ( debug ) {
+ printf("%s\n", (failed_already)? "FAIL" : "PASS" );
+ }
+ return( (failed_already)? 1 : 0 );
+} /* main() */
+/* end instrumt.c */
diff --git a/nsprpub/pr/tests/nbconn.c b/nsprpub/pr/tests/nbconn.c
new file mode 100644
index 0000000000..d1dd67c042
--- /dev/null
+++ b/nsprpub/pr/tests/nbconn.c
@@ -0,0 +1,531 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * A test for nonblocking connect. Functions tested include PR_Connect,
+ * PR_Poll, and PR_GetConnectStatus.
+ *
+ * The test should be invoked with a host name, for example:
+ * nbconn www.netscape.com
+ * It will do a nonblocking connect to port 80 (HTTP) on that host,
+ * and when connected, issue the "GET /" HTTP command.
+ *
+ * You should run this test in three ways:
+ * 1. To a known web site, such as www.netscape.com. The HTML of the
+ * top-level page at the web site should be printed.
+ * 2. To a machine not running a web server at port 80. This test should
+ * fail. Ideally the error code should be PR_CONNECT_REFUSED_ERROR.
+ * But it is possible to return PR_UNKNOWN_ERROR on certain platforms.
+ * 3. To an unreachable machine, for example, a machine that is off line.
+ * The test should fail after the connect times out. Ideally the
+ * error code should be PR_IO_TIMEOUT_ERROR, but it is possible to
+ * return PR_UNKNOWN_ERROR on certain platforms.
+ */
+
+#include "nspr.h"
+#include "plgetopt.h"
+#include <stdio.h>
+#include <string.h>
+
+#define SERVER_MAX_BIND_COUNT 100
+#define DATA_BUF_SIZE 256
+#define TCP_SERVER_PORT 10000
+#define TCP_UNUSED_PORT 211
+
+typedef struct Server_Param {
+ PRFileDesc *sp_fd; /* server port */
+} Server_Param;
+static void PR_CALLBACK TCP_Server(void *arg);
+
+int _debug_on;
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+static PRIntn connection_success_test();
+static PRIntn connection_failure_test();
+
+int main(int argc, char **argv)
+{
+ PRHostEnt he;
+ char buf[1024];
+ PRNetAddr addr;
+ PRPollDesc pd;
+ PRStatus rv;
+ PRSocketOptionData optData;
+ const char *hostname = NULL;
+ PRIntn default_case, n, bytes_read, bytes_sent;
+ PRInt32 failed_already = 0;
+
+ /*
+ * -d debug mode
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 0: /* debug mode */
+ hostname = opt->value;
+ break;
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_STDIO_INIT();
+ if (hostname) {
+ default_case = 0;
+ }
+ else {
+ default_case = 1;
+ }
+
+ if (default_case) {
+
+ /*
+ * In the default case the following tests are executed:
+ * 1. successful connection: a server thread accepts a connection
+ * from the main thread
+ * 2. unsuccessful connection: the main thread tries to connect to a
+ * nonexistent port and expects to get an error
+ */
+ rv = connection_success_test();
+ if (rv == 0) {
+ rv = connection_failure_test();
+ }
+ return rv;
+ } else {
+ PRFileDesc *sock;
+
+ if (PR_GetHostByName(argv[1], buf, sizeof(buf), &he) == PR_FAILURE) {
+ printf( "Unknown host: %s\n", argv[1]);
+ exit(1);
+ } else {
+ printf( "host: %s\n", buf);
+ }
+ PR_EnumerateHostEnt(0, &he, 80, &addr);
+
+ sock = PR_NewTCPSocket();
+ optData.option = PR_SockOpt_Nonblocking;
+ optData.value.non_blocking = PR_TRUE;
+ PR_SetSocketOption(sock, &optData);
+ rv = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
+ if (rv == PR_FAILURE && PR_GetError() == PR_IN_PROGRESS_ERROR) {
+ printf( "Connect in progress\n");
+ }
+
+ pd.fd = sock;
+ pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (n == -1) {
+ printf( "PR_Poll failed\n");
+ exit(1);
+ }
+ printf( "PR_Poll returns %d\n", n);
+ if (pd.out_flags & PR_POLL_READ) {
+ printf( "PR_POLL_READ\n");
+ }
+ if (pd.out_flags & PR_POLL_WRITE) {
+ printf( "PR_POLL_WRITE\n");
+ }
+ if (pd.out_flags & PR_POLL_EXCEPT) {
+ printf( "PR_POLL_EXCEPT\n");
+ }
+ if (pd.out_flags & PR_POLL_ERR) {
+ printf( "PR_POLL_ERR\n");
+ }
+ if (pd.out_flags & PR_POLL_NVAL) {
+ printf( "PR_POLL_NVAL\n");
+ }
+
+ if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
+ printf("PR_GetConnectStatus: connect succeeded\n");
+ PR_Write(sock, "GET /\r\n\r\n", 9);
+ PR_Shutdown(sock, PR_SHUTDOWN_SEND);
+ pd.in_flags = PR_POLL_READ;
+ while (1) {
+ n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ printf( "poll returns %d\n", n);
+ n = PR_Read(sock, buf, sizeof(buf));
+ printf( "read returns %d\n", n);
+ if (n <= 0) {
+ break;
+ }
+ PR_Write(PR_STDOUT, buf, n);
+ }
+ } else {
+ if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
+ printf( "PR_GetConnectStatus: connect still in progress\n");
+ exit(1);
+ }
+ printf( "PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ }
+ PR_Close(sock);
+ printf( "PASS\n");
+ return 0;
+
+ }
+}
+
+
+/*
+ * TCP Server
+ * Server Thread
+ * Accept a connection from the client and write some data
+ */
+static void PR_CALLBACK
+TCP_Server(void *arg)
+{
+ Server_Param *sp = (Server_Param *) arg;
+ PRFileDesc *sockfd, *newsockfd;
+ char data_buf[DATA_BUF_SIZE];
+ PRIntn rv, bytes_read;
+
+ sockfd = sp->sp_fd;
+ if ((newsockfd = PR_Accept(sockfd, NULL,
+ PR_INTERVAL_NO_TIMEOUT)) == NULL) {
+ fprintf(stderr,"ERROR - PR_Accept failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ return;
+ }
+ bytes_read = 0;
+ while (bytes_read != DATA_BUF_SIZE) {
+ rv = PR_Read(newsockfd, data_buf + bytes_read,
+ DATA_BUF_SIZE - bytes_read);
+ if (rv < 0) {
+ fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ PR_Close(newsockfd);
+ return;
+ }
+ PR_ASSERT(rv != 0);
+ bytes_read += rv;
+ }
+ DPRINTF(("Bytes read from client - %d\n",bytes_read));
+ rv = PR_Write(newsockfd, data_buf,DATA_BUF_SIZE);
+ if (rv < 0) {
+ fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ PR_Close(newsockfd);
+ return;
+ }
+ PR_ASSERT(rv == DATA_BUF_SIZE);
+ DPRINTF(("Bytes written to client - %d\n",rv));
+ PR_Close(newsockfd);
+}
+
+
+/*
+ * test for successful connection using a non-blocking socket
+ */
+static PRIntn
+connection_success_test()
+{
+ PRFileDesc *sockfd = NULL, *conn_fd = NULL;
+ PRNetAddr netaddr;
+ PRInt32 i, rv;
+ PRPollDesc pd;
+ PRSocketOptionData optData;
+ PRThread *thr = NULL;
+ Server_Param sp;
+ char send_buf[DATA_BUF_SIZE], recv_buf[DATA_BUF_SIZE];
+ PRIntn default_case, n, bytes_read, bytes_sent;
+ PRIntn failed_already = 0;
+
+ /*
+ * Create a tcp socket
+ */
+ if ((sockfd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
+ failed_already=1;
+ goto def_exit;
+ }
+ memset(&netaddr, 0, sizeof(netaddr));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
+ netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ /*
+ * try a few times to bind server's address, if addresses are in
+ * use
+ */
+ i = 0;
+ while (PR_Bind(sockfd, &netaddr) < 0) {
+ if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
+ netaddr.inet.port += 2;
+ if (i++ < SERVER_MAX_BIND_COUNT) {
+ continue;
+ }
+ }
+ fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+
+ if (PR_Listen(sockfd, 32) < 0) {
+ fprintf(stderr,"ERROR - PR_Listen failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+
+ if (PR_GetSockName(sockfd, &netaddr) < 0) {
+ fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ if ((conn_fd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
+ failed_already=1;
+ goto def_exit;
+ }
+ optData.option = PR_SockOpt_Nonblocking;
+ optData.value.non_blocking = PR_TRUE;
+ PR_SetSocketOption(conn_fd, &optData);
+ rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT);
+ if (rv == PR_FAILURE) {
+ if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
+ DPRINTF(("Connect in progress\n"));
+ } else {
+ fprintf(stderr,"Error - PR_Connect failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ }
+ /*
+ * Now create a thread to accept a connection
+ */
+ sp.sp_fd = sockfd;
+ thr = PR_CreateThread(PR_USER_THREAD, TCP_Server, (void *)&sp,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (thr == NULL) {
+ fprintf(stderr,"Error - PR_CreateThread failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ DPRINTF(("Created TCP_Server thread [0x%x]\n",thr));
+ pd.fd = conn_fd;
+ pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (n == -1) {
+ fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
+ PRInt32 rv;
+
+ DPRINTF(("Connection successful\n"));
+
+ /*
+ * Write some data, read it back and check data integrity to
+ * make sure the connection is good
+ */
+ pd.in_flags = PR_POLL_WRITE;
+ bytes_sent = 0;
+ memset(send_buf, 'a', DATA_BUF_SIZE);
+ while (bytes_sent != DATA_BUF_SIZE) {
+ rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (rv < 0) {
+ fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_WRITE));
+ rv = PR_Write(conn_fd, send_buf + bytes_sent,
+ DATA_BUF_SIZE - bytes_sent);
+ if (rv < 0) {
+ fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ PR_ASSERT(rv > 0);
+ bytes_sent += rv;
+ }
+ DPRINTF(("Bytes written to server - %d\n",bytes_sent));
+ PR_Shutdown(conn_fd, PR_SHUTDOWN_SEND);
+ pd.in_flags = PR_POLL_READ;
+ bytes_read = 0;
+ memset(recv_buf, 0, DATA_BUF_SIZE);
+ while (bytes_read != DATA_BUF_SIZE) {
+ rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (rv < 0) {
+ fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_READ));
+ rv = PR_Read(conn_fd, recv_buf + bytes_read,
+ DATA_BUF_SIZE - bytes_read);
+ if (rv < 0) {
+ fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ PR_ASSERT(rv != 0);
+ bytes_read += rv;
+ }
+ DPRINTF(("Bytes read from server - %d\n",bytes_read));
+ /*
+ * verify the data read
+ */
+ if (memcmp(send_buf, recv_buf, DATA_BUF_SIZE) != 0) {
+ fprintf(stderr,"ERROR - data corruption\n");
+ failed_already=1;
+ goto def_exit;
+ }
+ DPRINTF(("Data integrity verified\n"));
+ } else {
+ fprintf(stderr,"PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already = 1;
+ goto def_exit;
+ }
+def_exit:
+ if (thr) {
+ PR_JoinThread(thr);
+ thr = NULL;
+ }
+ if (sockfd) {
+ PR_Close(sockfd);
+ sockfd = NULL;
+ }
+ if (conn_fd) {
+ PR_Close(conn_fd);
+ conn_fd = NULL;
+ }
+ if (failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+}
+
+/*
+ * test for connection to a nonexistent port using a non-blocking socket
+ */
+static PRIntn
+connection_failure_test()
+{
+ PRFileDesc *sockfd = NULL, *conn_fd = NULL;
+ PRNetAddr netaddr;
+ PRInt32 i, rv;
+ PRPollDesc pd;
+ PRSocketOptionData optData;
+ PRIntn n, failed_already = 0;
+
+ /*
+ * Create a tcp socket
+ */
+ if ((sockfd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
+ failed_already=1;
+ goto def_exit;
+ }
+ memset(&netaddr, 0, sizeof(netaddr));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
+ netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ /*
+ * try a few times to bind server's address, if addresses are in
+ * use
+ */
+ i = 0;
+ while (PR_Bind(sockfd, &netaddr) < 0) {
+ if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
+ netaddr.inet.port += 2;
+ if (i++ < SERVER_MAX_BIND_COUNT) {
+ continue;
+ }
+ }
+ fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+
+ if (PR_GetSockName(sockfd, &netaddr) < 0) {
+ fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+#ifdef AIX
+ /*
+ * On AIX, set to unused/reserved port
+ */
+ netaddr.inet.port = PR_htons(TCP_UNUSED_PORT);
+#endif
+ if ((conn_fd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
+ failed_already=1;
+ goto def_exit;
+ }
+ optData.option = PR_SockOpt_Nonblocking;
+ optData.value.non_blocking = PR_TRUE;
+ PR_SetSocketOption(conn_fd, &optData);
+ rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT);
+ if (rv == PR_FAILURE) {
+ DPRINTF(("PR_Connect to a non-listen port failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError()));
+ } else {
+ PR_ASSERT(rv == PR_SUCCESS);
+ fprintf(stderr,"Error - PR_Connect succeeded, expected to fail\n");
+ failed_already=1;
+ goto def_exit;
+ }
+ pd.fd = conn_fd;
+ pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (n == -1) {
+ fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
+ PRInt32 rv;
+ fprintf(stderr,"PR_GetConnectStatus succeeded, expected to fail\n");
+ failed_already = 1;
+ goto def_exit;
+ }
+ rv = PR_GetError();
+ DPRINTF(("Connection failed, successfully with PR_Error %d\n",rv));
+def_exit:
+ if (sockfd) {
+ PR_Close(sockfd);
+ sockfd = NULL;
+ }
+ if (conn_fd) {
+ PR_Close(conn_fd);
+ conn_fd = NULL;
+ }
+ if (failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+}
diff --git a/nsprpub/pr/tests/nblayer.c b/nsprpub/pr/tests/nblayer.c
new file mode 100644
index 0000000000..9459866972
--- /dev/null
+++ b/nsprpub/pr/tests/nblayer.c
@@ -0,0 +1,778 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prio.h"
+#include "prmem.h"
+#include "prprf.h"
+#include "prlog.h"
+#include "prerror.h"
+#include "prnetdb.h"
+#include "prthread.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+#include "prwin16.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+** Testing layering of I/O
+**
+** The layered server
+** A thread that acts as a server. It creates a TCP listener with a dummy
+** layer pushed on top. Then listens for incoming connections. Each connection
+** request for connection will be layered as well, accept one request, echo
+** it back and close.
+**
+** The layered client
+** Pretty much what you'd expect.
+*/
+
+static PRFileDesc *logFile;
+static PRDescIdentity identity;
+static PRNetAddr server_address;
+
+static PRIOMethods myMethods;
+
+typedef enum {rcv_get_debit, rcv_send_credit, rcv_data} RcvState;
+typedef enum {xmt_send_debit, xmt_recv_credit, xmt_data} XmtState;
+
+struct PRFilePrivate
+{
+ RcvState rcvstate;
+ XmtState xmtstate;
+ PRInt32 rcvreq, rcvinprogress;
+ PRInt32 xmtreq, xmtinprogress;
+};
+
+typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity;
+
+static PRIntn minor_iterations = 5;
+static PRIntn major_iterations = 1;
+static Verbosity verbosity = quiet;
+
+#ifdef DEBUG
+#define PORT_INC_DO +100
+#else
+#define PORT_INC_DO
+#endif
+#ifdef IS_64
+#define PORT_INC_3264 +200
+#else
+#define PORT_INC_3264
+#endif
+
+static PRUint16 default_port = 12273 PORT_INC_DO PORT_INC_3264;
+
+static PRFileDesc *PushLayer(PRFileDesc *stack)
+{
+ PRStatus rv;
+ PRFileDesc *layer = PR_CreateIOLayerStub(identity, &myMethods);
+ layer->secret = PR_NEWZAP(PRFilePrivate);
+ rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if (verbosity > quiet) {
+ PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, stack);
+ }
+ return stack;
+} /* PushLayer */
+
+static PRFileDesc *PopLayer(PRFileDesc *stack)
+{
+ PRFileDesc *popped = PR_PopIOLayer(stack, identity);
+ if (verbosity > quiet) {
+ PR_fprintf(logFile, "Popped layer(0x%x) from stack(0x%x)\n", popped, stack);
+ }
+ PR_DELETE(popped->secret);
+ popped->dtor(popped);
+ return stack;
+} /* PopLayer */
+
+static void PR_CALLBACK Client(void *arg)
+{
+ PRStatus rv;
+ PRIntn mits;
+ PRInt32 ready;
+ PRUint8 buffer[100];
+ PRPollDesc polldesc;
+ PRIntn empty_flags = 0;
+ PRIntn bytes_read, bytes_sent;
+ PRFileDesc *stack = (PRFileDesc*)arg;
+
+ /* Initialize the buffer so that Purify won't complain */
+ memset(buffer, 0, sizeof(buffer));
+
+ rv = PR_Connect(stack, &server_address, PR_INTERVAL_NO_TIMEOUT);
+ if ((PR_FAILURE == rv) && (PR_IN_PROGRESS_ERROR == PR_GetError()))
+ {
+ if (verbosity > quiet) {
+ PR_fprintf(logFile, "Client connect 'in progress'\n");
+ }
+ do
+ {
+ polldesc.fd = stack;
+ polldesc.out_flags = 0;
+ polldesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
+ if ((1 != ready) /* if not 1, then we're dead */
+ || (0 == (polldesc.in_flags & polldesc.out_flags)))
+ {
+ PR_NOT_REACHED("Whoa!");
+ break;
+ }
+ if (verbosity > quiet)
+ PR_fprintf(
+ logFile, "Client connect 'in progress' [0x%x]\n",
+ polldesc.out_flags);
+ rv = PR_GetConnectStatus(&polldesc);
+ if ((PR_FAILURE == rv)
+ && (PR_IN_PROGRESS_ERROR != PR_GetError())) {
+ break;
+ }
+ } while (PR_FAILURE == rv);
+ }
+ PR_ASSERT(PR_SUCCESS == rv);
+ if (verbosity > chatty) {
+ PR_fprintf(logFile, "Client created connection\n");
+ }
+
+ for (mits = 0; mits < minor_iterations; ++mits)
+ {
+ bytes_sent = 0;
+ if (verbosity > quiet) {
+ PR_fprintf(logFile, "Client sending %d bytes\n", sizeof(buffer));
+ }
+ do
+ {
+ if (verbosity > chatty)
+ PR_fprintf(
+ logFile, "Client sending %d bytes\n",
+ sizeof(buffer) - bytes_sent);
+ ready = PR_Send(
+ stack, buffer + bytes_sent, sizeof(buffer) - bytes_sent,
+ empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty) {
+ PR_fprintf(logFile, "Client send status [%d]\n", ready);
+ }
+ if (0 < ready) {
+ bytes_sent += ready;
+ }
+ else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
+ {
+ polldesc.fd = stack;
+ polldesc.out_flags = 0;
+ polldesc.in_flags = PR_POLL_WRITE;
+ ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
+ if ((1 != ready) /* if not 1, then we're dead */
+ || (0 == (polldesc.in_flags & polldesc.out_flags)))
+ {
+ PR_NOT_REACHED("Whoa!");
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ } while (bytes_sent < sizeof(buffer));
+ PR_ASSERT(sizeof(buffer) == bytes_sent);
+
+ bytes_read = 0;
+ do
+ {
+ if (verbosity > chatty)
+ PR_fprintf(
+ logFile, "Client receiving %d bytes\n",
+ bytes_sent - bytes_read);
+ ready = PR_Recv(
+ stack, buffer + bytes_read, bytes_sent - bytes_read,
+ empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty)
+ PR_fprintf(
+ logFile, "Client receive status [%d]\n", ready);
+ if (0 < ready) {
+ bytes_read += ready;
+ }
+ else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
+ {
+ polldesc.fd = stack;
+ polldesc.out_flags = 0;
+ polldesc.in_flags = PR_POLL_READ;
+ ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
+ if ((1 != ready) /* if not 1, then we're dead */
+ || (0 == (polldesc.in_flags & polldesc.out_flags)))
+ {
+ PR_NOT_REACHED("Whoa!");
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ } while (bytes_read < bytes_sent);
+ if (verbosity > chatty) {
+ PR_fprintf(logFile, "Client received %d bytes\n", bytes_read);
+ }
+ PR_ASSERT(bytes_read == bytes_sent);
+ }
+
+ if (verbosity > quiet) {
+ PR_fprintf(logFile, "Client shutting down stack\n");
+ }
+
+ rv = PR_Shutdown(stack, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv);
+} /* Client */
+
+static void PR_CALLBACK Server(void *arg)
+{
+ PRStatus rv;
+ PRInt32 ready;
+ PRUint8 buffer[100];
+ PRFileDesc *service;
+ PRUintn empty_flags = 0;
+ struct PRPollDesc polldesc;
+ PRIntn bytes_read, bytes_sent;
+ PRFileDesc *stack = (PRFileDesc*)arg;
+ PRNetAddr client_address;
+
+ do
+ {
+ if (verbosity > chatty) {
+ PR_fprintf(logFile, "Server accepting connection\n");
+ }
+ service = PR_Accept(stack, &client_address, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty) {
+ PR_fprintf(logFile, "Server accept status [0x%p]\n", service);
+ }
+ if ((NULL == service) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
+ {
+ polldesc.fd = stack;
+ polldesc.out_flags = 0;
+ polldesc.in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
+ ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
+ if ((1 != ready) /* if not 1, then we're dead */
+ || (0 == (polldesc.in_flags & polldesc.out_flags)))
+ {
+ PR_NOT_REACHED("Whoa!");
+ break;
+ }
+ }
+ } while (NULL == service);
+ PR_ASSERT(NULL != service);
+
+ if (verbosity > quiet) {
+ PR_fprintf(logFile, "Server accepting connection\n");
+ }
+
+ do
+ {
+ bytes_read = 0;
+ do
+ {
+ if (verbosity > chatty)
+ PR_fprintf(
+ logFile, "Server receiving %d bytes\n",
+ sizeof(buffer) - bytes_read);
+ ready = PR_Recv(
+ service, buffer + bytes_read, sizeof(buffer) - bytes_read,
+ empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty) {
+ PR_fprintf(logFile, "Server receive status [%d]\n", ready);
+ }
+ if (0 < ready) {
+ bytes_read += ready;
+ }
+ else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
+ {
+ polldesc.fd = service;
+ polldesc.out_flags = 0;
+ polldesc.in_flags = PR_POLL_READ;
+ ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
+ if ((1 != ready) /* if not 1, then we're dead */
+ || (0 == (polldesc.in_flags & polldesc.out_flags)))
+ {
+ PR_NOT_REACHED("Whoa!");
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ } while (bytes_read < sizeof(buffer));
+
+ if (0 != bytes_read)
+ {
+ if (verbosity > chatty) {
+ PR_fprintf(logFile, "Server received %d bytes\n", bytes_read);
+ }
+ PR_ASSERT(bytes_read > 0);
+
+ bytes_sent = 0;
+ do
+ {
+ ready = PR_Send(
+ service, buffer + bytes_sent, bytes_read - bytes_sent,
+ empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (0 < ready)
+ {
+ bytes_sent += ready;
+ }
+ else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
+ {
+ polldesc.fd = service;
+ polldesc.out_flags = 0;
+ polldesc.in_flags = PR_POLL_WRITE;
+ ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
+ if ((1 != ready) /* if not 1, then we're dead */
+ || (0 == (polldesc.in_flags & polldesc.out_flags)))
+ {
+ PR_NOT_REACHED("Whoa!");
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ } while (bytes_sent < bytes_read);
+ PR_ASSERT(bytes_read == bytes_sent);
+ if (verbosity > chatty) {
+ PR_fprintf(logFile, "Server sent %d bytes\n", bytes_sent);
+ }
+ }
+ } while (0 != bytes_read);
+
+ if (verbosity > quiet) {
+ PR_fprintf(logFile, "Server shutting down stack\n");
+ }
+ rv = PR_Shutdown(service, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
+
+} /* Server */
+
+static PRStatus PR_CALLBACK MyClose(PRFileDesc *fd)
+{
+ PR_DELETE(fd->secret); /* manage my secret file object */
+ return (PR_GetDefaultIOMethods())->close(fd); /* let him do all the work */
+} /* MyClose */
+
+static PRInt16 PR_CALLBACK MyPoll(
+ PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
+{
+ PRInt16 my_flags, new_flags;
+ PRFilePrivate *mine = (PRFilePrivate*)fd->secret;
+ if (0 != (PR_POLL_READ & in_flags))
+ {
+ /* client thinks he's reading */
+ switch (mine->rcvstate)
+ {
+ case rcv_send_credit:
+ my_flags = (in_flags & ~PR_POLL_READ) | PR_POLL_WRITE;
+ break;
+ case rcv_data:
+ case rcv_get_debit:
+ my_flags = in_flags;
+ default: break;
+ }
+ }
+ else if (0 != (PR_POLL_WRITE & in_flags))
+ {
+ /* client thinks he's writing */
+ switch (mine->xmtstate)
+ {
+ case xmt_recv_credit:
+ my_flags = (in_flags & ~PR_POLL_WRITE) | PR_POLL_READ;
+ break;
+ case xmt_send_debit:
+ case xmt_data:
+ my_flags = in_flags;
+ default: break;
+ }
+ }
+ else {
+ PR_NOT_REACHED("How'd I get here?");
+ }
+ new_flags = (fd->lower->methods->poll)(fd->lower, my_flags, out_flags);
+ if (verbosity > chatty)
+ PR_fprintf(
+ logFile, "Poll [i: 0x%x, m: 0x%x, o: 0x%x, n: 0x%x]\n",
+ in_flags, my_flags, *out_flags, new_flags);
+ return new_flags;
+} /* MyPoll */
+
+static PRFileDesc * PR_CALLBACK MyAccept(
+ PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRStatus rv;
+ PRFileDesc *newfd, *layer = fd;
+ PRFileDesc *newstack;
+ PRFilePrivate *newsecret;
+
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ newstack = PR_NEW(PRFileDesc);
+ if (NULL == newstack)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ newsecret = PR_NEW(PRFilePrivate);
+ if (NULL == newsecret)
+ {
+ PR_DELETE(newstack);
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ *newstack = *fd; /* make a copy of the accepting layer */
+ *newsecret = *fd->secret;
+ newstack->secret = newsecret;
+
+ newfd = (fd->lower->methods->accept)(fd->lower, addr, timeout);
+ if (NULL == newfd)
+ {
+ PR_DELETE(newsecret);
+ PR_DELETE(newstack);
+ return NULL;
+ }
+
+ /* this PR_PushIOLayer call cannot fail */
+ rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack);
+ PR_ASSERT(PR_SUCCESS == rv);
+ return newfd; /* that's it */
+}
+
+static PRInt32 PR_CALLBACK MyRecv(
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ char *b;
+ PRInt32 rv;
+ PRFileDesc *lo = fd->lower;
+ PRFilePrivate *mine = (PRFilePrivate*)fd->secret;
+
+ do
+ {
+ switch (mine->rcvstate)
+ {
+ case rcv_get_debit:
+ b = (char*)&mine->rcvreq;
+ mine->rcvreq = amount;
+ rv = lo->methods->recv(
+ lo, b + mine->rcvinprogress,
+ sizeof(mine->rcvreq) - mine->rcvinprogress, flags, timeout);
+ if (0 == rv) {
+ goto closed;
+ }
+ if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) {
+ break;
+ }
+ mine->rcvinprogress += rv; /* accumulate the read */
+ if (mine->rcvinprogress < sizeof(mine->rcvreq)) {
+ break; /* loop */
+ }
+ mine->rcvstate = rcv_send_credit;
+ mine->rcvinprogress = 0;
+ case rcv_send_credit:
+ b = (char*)&mine->rcvreq;
+ rv = lo->methods->send(
+ lo, b + mine->rcvinprogress,
+ sizeof(mine->rcvreq) - mine->rcvinprogress, flags, timeout);
+ if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) {
+ break;
+ }
+ mine->rcvinprogress += rv; /* accumulate the read */
+ if (mine->rcvinprogress < sizeof(mine->rcvreq)) {
+ break; /* loop */
+ }
+ mine->rcvstate = rcv_data;
+ mine->rcvinprogress = 0;
+ case rcv_data:
+ b = (char*)buf;
+ rv = lo->methods->recv(
+ lo, b + mine->rcvinprogress,
+ mine->rcvreq - mine->rcvinprogress, flags, timeout);
+ if (0 == rv) {
+ goto closed;
+ }
+ if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) {
+ break;
+ }
+ mine->rcvinprogress += rv; /* accumulate the read */
+ if (mine->rcvinprogress < amount) {
+ break; /* loop */
+ }
+ mine->rcvstate = rcv_get_debit;
+ mine->rcvinprogress = 0;
+ return mine->rcvreq; /* << -- that's it! */
+ default:
+ break;
+ }
+ } while (-1 != rv);
+ return rv;
+closed:
+ mine->rcvinprogress = 0;
+ mine->rcvstate = rcv_get_debit;
+ return 0;
+} /* MyRecv */
+
+static PRInt32 PR_CALLBACK MySend(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ char *b;
+ PRInt32 rv;
+ PRFileDesc *lo = fd->lower;
+ PRFilePrivate *mine = (PRFilePrivate*)fd->secret;
+
+ do
+ {
+ switch (mine->xmtstate)
+ {
+ case xmt_send_debit:
+ b = (char*)&mine->xmtreq;
+ mine->xmtreq = amount;
+ rv = lo->methods->send(
+ lo, b - mine->xmtinprogress,
+ sizeof(mine->xmtreq) - mine->xmtinprogress, flags, timeout);
+ if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) {
+ break;
+ }
+ mine->xmtinprogress += rv;
+ if (mine->xmtinprogress < sizeof(mine->xmtreq)) {
+ break;
+ }
+ mine->xmtstate = xmt_recv_credit;
+ mine->xmtinprogress = 0;
+ case xmt_recv_credit:
+ b = (char*)&mine->xmtreq;
+ rv = lo->methods->recv(
+ lo, b + mine->xmtinprogress,
+ sizeof(mine->xmtreq) - mine->xmtinprogress, flags, timeout);
+ if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) {
+ break;
+ }
+ mine->xmtinprogress += rv;
+ if (mine->xmtinprogress < sizeof(mine->xmtreq)) {
+ break;
+ }
+ mine->xmtstate = xmt_data;
+ mine->xmtinprogress = 0;
+ case xmt_data:
+ b = (char*)buf;
+ rv = lo->methods->send(
+ lo, b + mine->xmtinprogress,
+ mine->xmtreq - mine->xmtinprogress, flags, timeout);
+ if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) {
+ break;
+ }
+ mine->xmtinprogress += rv;
+ if (mine->xmtinprogress < amount) {
+ break;
+ }
+ mine->xmtstate = xmt_send_debit;
+ mine->xmtinprogress = 0;
+ return mine->xmtreq; /* <<-- That's the one! */
+ default:
+ break;
+ }
+ } while (-1 != rv);
+ return rv;
+} /* MySend */
+
+static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta)
+{
+ PRIntn verbage = (PRIntn)verbosity + delta;
+ if (verbage < (PRIntn)silent) {
+ verbage = (PRIntn)silent;
+ }
+ else if (verbage > (PRIntn)noisy) {
+ verbage = (PRIntn)noisy;
+ }
+ return (Verbosity)verbage;
+} /* ChangeVerbosity */
+
+int main(int argc, char **argv)
+{
+ PRStatus rv;
+ PLOptStatus os;
+ PRFileDesc *client, *service;
+ PRNetAddr any_address;
+ const char *server_name = NULL;
+ const PRIOMethods *stubMethods;
+ PRThread *client_thread, *server_thread;
+ PRThreadScope thread_scope = PR_LOCAL_THREAD;
+ PRSocketOptionData socket_noblock, socket_nodelay;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dqGC:c:p:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 0:
+ server_name = opt->value;
+ break;
+ case 'd': /* debug mode */
+ if (verbosity < noisy) {
+ verbosity = ChangeVerbosity(verbosity, 1);
+ }
+ break;
+ case 'q': /* debug mode */
+ if (verbosity > silent) {
+ verbosity = ChangeVerbosity(verbosity, -1);
+ }
+ break;
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'C': /* number of threads waiting */
+ major_iterations = atoi(opt->value);
+ break;
+ case 'c': /* number of client threads */
+ minor_iterations = atoi(opt->value);
+ break;
+ case 'p': /* default port */
+ default_port = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ PR_STDIO_INIT();
+
+ logFile = PR_GetSpecialFD(PR_StandardError);
+ identity = PR_GetUniqueIdentity("Dummy");
+ stubMethods = PR_GetDefaultIOMethods();
+
+ /*
+ ** The protocol we're going to implement is one where in order to initiate
+ ** a send, the sender must first solicit permission. Therefore, every
+ ** send is really a send - receive - send sequence.
+ */
+ myMethods = *stubMethods; /* first get the entire batch */
+ myMethods.accept = MyAccept; /* then override the ones we care about */
+ myMethods.recv = MyRecv; /* then override the ones we care about */
+ myMethods.send = MySend; /* then override the ones we care about */
+ myMethods.close = MyClose; /* then override the ones we care about */
+ myMethods.poll = MyPoll; /* then override the ones we care about */
+
+ if (NULL == server_name)
+ rv = PR_InitializeNetAddr(
+ PR_IpAddrLoopback, default_port, &server_address);
+ else
+ {
+ rv = PR_StringToNetAddr(server_name, &server_address);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_InitializeNetAddr(
+ PR_IpAddrNull, default_port, &server_address);
+ }
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ socket_noblock.value.non_blocking = PR_TRUE;
+ socket_noblock.option = PR_SockOpt_Nonblocking;
+ socket_nodelay.value.no_delay = PR_TRUE;
+ socket_nodelay.option = PR_SockOpt_NoDelay;
+
+ /* one type w/o layering */
+
+ while (major_iterations-- > 0)
+ {
+ if (verbosity > silent) {
+ PR_fprintf(logFile, "Beginning non-layered test\n");
+ }
+
+ client = PR_NewTCPSocket(); PR_ASSERT(NULL != client);
+ service = PR_NewTCPSocket(); PR_ASSERT(NULL != service);
+
+ rv = PR_SetSocketOption(client, &socket_noblock);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetSocketOption(service, &socket_noblock);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetSocketOption(client, &socket_nodelay);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetSocketOption(service, &socket_nodelay);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv);
+
+ server_thread = PR_CreateThread(
+ PR_USER_THREAD, Server, service,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != server_thread);
+
+ client_thread = PR_CreateThread(
+ PR_USER_THREAD, Client, client,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != client_thread);
+
+ rv = PR_JoinThread(client_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(server_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
+ if (verbosity > silent) {
+ PR_fprintf(logFile, "Ending non-layered test\n");
+ }
+
+ /* with layering */
+ if (verbosity > silent) {
+ PR_fprintf(logFile, "Beginning layered test\n");
+ }
+ client = PR_NewTCPSocket(); PR_ASSERT(NULL != client);
+ service = PR_NewTCPSocket(); PR_ASSERT(NULL != service);
+
+ rv = PR_SetSocketOption(client, &socket_noblock);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetSocketOption(service, &socket_noblock);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetSocketOption(client, &socket_nodelay);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetSocketOption(service, &socket_nodelay);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ PushLayer(client);
+ PushLayer(service);
+
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv);
+
+ server_thread = PR_CreateThread(
+ PR_USER_THREAD, Server, service,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != server_thread);
+
+ client_thread = PR_CreateThread(
+ PR_USER_THREAD, Client, client,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != client_thread);
+
+ rv = PR_JoinThread(client_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(server_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Close(PopLayer(client)); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Close(PopLayer(service)); PR_ASSERT(PR_SUCCESS == rv);
+ if (verbosity > silent) {
+ PR_fprintf(logFile, "Ending layered test\n");
+ }
+ }
+ return 0;
+} /* main */
+
+/* nblayer.c */
diff --git a/nsprpub/pr/tests/nonblock.c b/nsprpub/pr/tests/nonblock.c
new file mode 100644
index 0000000000..f24ffb02d0
--- /dev/null
+++ b/nsprpub/pr/tests/nonblock.c
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+#include "prio.h"
+#include "prerror.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prnetdb.h"
+#include "plerror.h"
+#include "obsolete/probslet.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define NUMBER_ROUNDS 5
+
+#if defined(WIN16)
+/*
+** Make win16 unit_time interval 300 milliseconds, others get 100
+*/
+#define UNIT_TIME 200 /* unit time in milliseconds */
+#else
+#define UNIT_TIME 100 /* unit time in milliseconds */
+#endif
+#define CHUNK_SIZE 10
+#undef USE_PR_SELECT /* If defined, we use PR_Select.
+ * If not defined, use PR_Poll instead. */
+
+#if defined(USE_PR_SELECT)
+#include "pprio.h"
+#endif
+
+static void PR_CALLBACK
+clientThreadFunc(void *arg)
+{
+ PRUintn port = (PRUintn)arg;
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ char buf[CHUNK_SIZE];
+ int i;
+ PRIntervalTime unitTime = PR_MillisecondsToInterval(UNIT_TIME);
+ PRSocketOptionData optval;
+ PRStatus retVal;
+ PRInt32 nBytes;
+
+ /* Initialize the buffer so that Purify won't complain */
+ memset(buf, 0, sizeof(buf));
+
+ addr.inet.family = PR_AF_INET;
+ addr.inet.port = PR_htons((PRUint16)port);
+ addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+ PR_snprintf(buf, sizeof(buf), "%hu", addr.inet.ip);
+
+ /* time 1 */
+ PR_Sleep(unitTime);
+ sock = PR_NewTCPSocket();
+ optval.option = PR_SockOpt_Nonblocking;
+ optval.value.non_blocking = PR_TRUE;
+ PR_SetSocketOption(sock, &optval);
+ retVal = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
+ if (retVal == PR_FAILURE && PR_GetError() == PR_IN_PROGRESS_ERROR) {
+#if !defined(USE_PR_SELECT)
+ PRPollDesc pd;
+ PRInt32 n;
+ fprintf(stderr, "connect: EWOULDBLOCK, good\n");
+ pd.fd = sock;
+ pd.in_flags = PR_POLL_WRITE;
+ n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(n == 1);
+ PR_ASSERT(pd.out_flags == PR_POLL_WRITE);
+#else
+ PR_fd_set writeSet;
+ PRInt32 n;
+ fprintf(stderr, "connect: EWOULDBLOCK, good\n");
+ PR_FD_ZERO(&writeSet);
+ PR_FD_SET(sock, &writeSet);
+ n = PR_Select(0, NULL, &writeSet, NULL, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(n == 1);
+ PR_ASSERT(PR_FD_ISSET(sock, &writeSet));
+#endif
+ }
+ printf("client connected\n");
+ fflush(stdout);
+
+ /* time 4, 7, 11, etc. */
+ for (i = 0; i < NUMBER_ROUNDS; i++) {
+ PR_Sleep(3 * unitTime);
+ nBytes = PR_Write(sock, buf, sizeof(buf));
+ if (nBytes == -1) {
+ if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
+ fprintf(stderr, "write: EWOULDBLOCK\n");
+ exit(1);
+ } else {
+ fprintf(stderr, "write: failed\n");
+ }
+ }
+ printf("client sent %d bytes\n", nBytes);
+ fflush(stdout);
+ }
+
+ PR_Close(sock);
+}
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ PRFileDesc *listenSock, *sock;
+ PRUint16 listenPort;
+ PRNetAddr addr;
+ char buf[CHUNK_SIZE];
+ PRThread *clientThread;
+ PRInt32 retVal;
+ PRSocketOptionData optval;
+ PRIntn i;
+ PRIntervalTime unitTime = PR_MillisecondsToInterval(UNIT_TIME);
+
+ /* Create a listening socket */
+ if ((listenSock = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ exit(1);
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ exit(1);
+ }
+ if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ exit(1);
+ }
+ listenPort = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ exit(1);
+ }
+
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on port %hu\n\n",
+ listenPort);
+ printf("%s", buf);
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ exit(1);
+ }
+
+ printf("client thread created.\n");
+
+ optval.option = PR_SockOpt_Nonblocking;
+ optval.value.non_blocking = PR_TRUE;
+ PR_SetSocketOption(listenSock, &optval);
+ /* time 0 */
+ sock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (sock != NULL || PR_GetError() != PR_WOULD_BLOCK_ERROR) {
+ PL_PrintError("First Accept\n");
+ fprintf(stderr, "First PR_Accept() xxx\n" );
+ exit(1);
+ }
+ printf("accept: EWOULDBLOCK, good\n");
+ fflush(stdout);
+ /* time 2 */
+ PR_Sleep(2 * unitTime);
+ sock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (sock == NULL) {
+ PL_PrintError("Second Accept\n");
+ fprintf(stderr, "Second PR_Accept() failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ printf("accept: succeeded, good\n");
+ fflush(stdout);
+ PR_Close(listenSock);
+
+ PR_SetSocketOption(sock, &optval);
+
+ /* time 3, 5, 6, 8, etc. */
+ for (i = 0; i < NUMBER_ROUNDS; i++) {
+ PR_Sleep(unitTime);
+ retVal = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT);
+ if (retVal != -1 || PR_GetError() != PR_WOULD_BLOCK_ERROR) {
+ PL_PrintError("First Receive:\n");
+ fprintf(stderr, "First PR_Recv: retVal: %ld, Error: %ld\n",
+ retVal, PR_GetError());
+ exit(1);
+ }
+ printf("read: EWOULDBLOCK, good\n");
+ fflush(stdout);
+ PR_Sleep(2 * unitTime);
+ retVal = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT);
+ if (retVal != CHUNK_SIZE) {
+ PL_PrintError("Second Receive:\n");
+ fprintf(stderr, "Second PR_Recv: retVal: %ld, Error: %ld\n",
+ retVal, PR_GetError());
+ exit(1);
+ }
+ printf("read: %d bytes, good\n", retVal);
+ fflush(stdout);
+ }
+ PR_Close(sock);
+
+ printf("All tests finished\n");
+ printf("PASS\n");
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
+
diff --git a/nsprpub/pr/tests/ntioto.c b/nsprpub/pr/tests/ntioto.c
new file mode 100644
index 0000000000..809e0ae5c6
--- /dev/null
+++ b/nsprpub/pr/tests/ntioto.c
@@ -0,0 +1,319 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: ntioto.c
+** Description:
+** This test, ntioto.c, was designed to reproduce a bug reported by NES
+** on WindowsNT (fibers implementation). NSPR was asserting in ntio.c
+** after PR_AcceptRead() had timed out. I/O performed subsequent to the
+** call to PR_AcceptRead() could complete on a CPU other than the one
+** on which it was started. The assert in ntio.c detected this, then
+** asserted.
+**
+** Design:
+** This test will fail with an assert in ntio.c if the problem it was
+** designed to catch occurs. It returns 0 otherwise.
+**
+** The main() thread initializes and tears things down. A file is
+** opened for writing; this file will be written to by AcceptThread()
+** and JitterThread(). Main() creates a socket for reading, listens
+** and binds the socket.
+**
+** ConnectThread() connects to the socket created by main, then polls
+** the "state" variable. When state is AllDone, ConnectThread() exits.
+**
+** AcceptThread() calls PR_AcceptRead() on the socket. He fully expects
+** it to time out. After the timeout, AccpetThread() interacts with
+** JitterThread() via a common condition variable and the state
+** variable. The two threads ping-pong back and forth, each thread
+** writes the the file opened by main. This should provoke the
+** condition reported by NES (if we didn't fix it).
+**
+** The failure is not solid. It may fail within a few ping-pongs between
+** AcceptThread() and JitterThread() or may take a while. The default
+** iteration count, jitter, is set by DEFAULT_JITTER. This may be
+** modified at the command line with the -j option.
+**
+*/
+
+#include <plgetopt.h>
+#include <nspr.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+** Test harness infrastructure
+*/
+PRLogModuleInfo *lm;
+PRLogModuleLevel msgLevel = PR_LOG_NONE;
+PRIntn debug = 0;
+PRIntn verbose = 0;
+PRUint32 failed_already = 0;
+/* end Test harness infrastructure */
+
+/* JITTER_DEFAULT: the number of times AcceptThread() and JitterThread() ping-pong */
+#define JITTER_DEFAULT 100000
+
+#ifdef DEBUG
+#define PORT_INC_DO +100
+#else
+#define PORT_INC_DO
+#endif
+#ifdef IS_64
+#define PORT_INC_3264 +200
+#else
+#define PORT_INC_3264
+#endif
+
+#define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264
+
+PRIntervalTime timeout;
+PRNetAddr listenAddr;
+PRFileDesc *listenSock;
+PRLock *ml;
+PRCondVar *cv;
+volatile enum {
+ RunJitter,
+ RunAcceptRead,
+ AllDone
+} state = RunAcceptRead;
+PRFileDesc *file1;
+PRIntn iCounter = 0;
+PRIntn jitter = JITTER_DEFAULT;
+PRBool resume = PR_FALSE;
+
+/*
+** Emit help text for this test
+*/
+static void Help( void )
+{
+ printf("Template: Help(): display your help message(s) here");
+ exit(1);
+} /* end Help() */
+
+
+/*
+** static computation of PR_AcceptRead() buffer size.
+*/
+#define ACCEPT_READ_DATASIZE 10
+#define ACCEPT_READ_BUFSIZE (PR_ACCEPT_READ_BUF_OVERHEAD + ACCEPT_READ_DATASIZE)
+
+static void AcceptThread(void *arg)
+{
+ PRIntn bytesRead;
+ char dataBuf[ACCEPT_READ_BUFSIZE];
+ PRFileDesc *arSock;
+ PRNetAddr *arAddr;
+
+ bytesRead = PR_AcceptRead( listenSock,
+ &arSock,
+ &arAddr,
+ dataBuf,
+ ACCEPT_READ_DATASIZE,
+ PR_SecondsToInterval(1));
+
+ if ( bytesRead == -1 && PR_GetError() == PR_IO_TIMEOUT_ERROR ) {
+ if ( debug ) {
+ printf("AcceptRead timed out\n");
+ }
+ } else {
+ if ( debug ) {
+ printf("Oops! read: %d, error: %d\n", bytesRead, PR_GetError());
+ }
+ }
+
+ while( state != AllDone ) {
+ PR_Lock( ml );
+ while( state != RunAcceptRead ) {
+ PR_WaitCondVar( cv, PR_INTERVAL_NO_TIMEOUT );
+ }
+ if ( ++iCounter >= jitter ) {
+ state = AllDone;
+ }
+ else {
+ state = RunJitter;
+ }
+ if ( verbose ) {
+ printf(".");
+ }
+ PR_NotifyCondVar( cv );
+ PR_Unlock( ml );
+ PR_Write( file1, ".", 1 );
+ }
+
+ return;
+} /* end AcceptThread() */
+
+static void JitterThread(void *arg)
+{
+ while( state != AllDone ) {
+ PR_Lock( ml );
+ while( state != RunJitter && state != AllDone ) {
+ PR_WaitCondVar( cv, PR_INTERVAL_NO_TIMEOUT );
+ }
+ if ( state != AllDone) {
+ state = RunAcceptRead;
+ }
+ if ( verbose ) {
+ printf("+");
+ }
+ PR_NotifyCondVar( cv );
+ PR_Unlock( ml );
+ PR_Write( file1, "+", 1 );
+ }
+ return;
+} /* end Goofy() */
+
+static void ConnectThread( void *arg )
+{
+ PRStatus rv;
+ PRFileDesc *clientSock;
+ PRNetAddr serverAddress;
+ clientSock = PR_NewTCPSocket();
+
+ PR_ASSERT(clientSock);
+
+ if ( resume ) {
+ if ( debug ) {
+ printf("pausing 3 seconds before connect\n");
+ }
+ PR_Sleep( PR_SecondsToInterval(3));
+ }
+
+ memset(&serverAddress, 0, sizeof(serverAddress));
+ rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &serverAddress);
+ PR_ASSERT( PR_SUCCESS == rv );
+ rv = PR_Connect( clientSock,
+ &serverAddress,
+ PR_SecondsToInterval(1));
+ PR_ASSERT( PR_SUCCESS == rv );
+
+ /* that's all we do. ... Wait for the acceptread() to timeout */
+ while( state != AllDone ) {
+ PR_Sleep( PR_SecondsToInterval(1));
+ }
+ return;
+} /* end ConnectThread() */
+
+
+int main(int argc, char **argv)
+{
+ PRThread *tJitter;
+ PRThread *tAccept;
+ PRThread *tConnect;
+ PRStatus rv;
+ /* This test if valid for WinNT only! */
+
+#if !defined(WINNT)
+ return 0;
+#endif
+
+ {
+ /*
+ ** Get command line options
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hdrvj:");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug */
+ debug = 1;
+ msgLevel = PR_LOG_ERROR;
+ break;
+ case 'v': /* verbose mode */
+ verbose = 1;
+ msgLevel = PR_LOG_DEBUG;
+ break;
+ case 'j':
+ jitter = atoi(opt->value);
+ if ( jitter == 0) {
+ jitter = JITTER_DEFAULT;
+ }
+ break;
+ case 'r':
+ resume = PR_TRUE;
+ break;
+ case 'h': /* help message */
+ Help();
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ }
+
+ lm = PR_NewLogModule("Test"); /* Initialize logging */
+
+ /* set concurrency */
+ PR_SetConcurrency( 4 );
+
+ /* setup thread synchronization mechanics */
+ ml = PR_NewLock();
+ cv = PR_NewCondVar( ml );
+
+ /* setup a tcp socket */
+ memset(&listenAddr, 0, sizeof(listenAddr));
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, BASE_PORT, &listenAddr);
+ PR_ASSERT( PR_SUCCESS == rv );
+
+ listenSock = PR_NewTCPSocket();
+ PR_ASSERT( listenSock );
+
+ rv = PR_Bind( listenSock, &listenAddr);
+ PR_ASSERT( PR_SUCCESS == rv );
+
+ rv = PR_Listen( listenSock, 5 );
+ PR_ASSERT( PR_SUCCESS == rv );
+
+ /* open a file for writing, provoke bug */
+ file1 = PR_Open("xxxTestFile", PR_CREATE_FILE | PR_RDWR, 666);
+
+ /* create Connect thread */
+ tConnect = PR_CreateThread(
+ PR_USER_THREAD, ConnectThread, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD, 0 );
+ PR_ASSERT( tConnect );
+
+ /* create jitter off thread */
+ tJitter = PR_CreateThread(
+ PR_USER_THREAD, JitterThread, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD, 0 );
+ PR_ASSERT( tJitter );
+
+ /* create acceptread thread */
+ tAccept = PR_CreateThread(
+ PR_USER_THREAD, AcceptThread, NULL,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD, 0 );
+ PR_ASSERT( tAccept );
+
+ /* wait for all threads to quit, then terminate gracefully */
+ PR_JoinThread( tConnect );
+ PR_JoinThread( tAccept );
+ PR_JoinThread( tJitter );
+ PR_Close( listenSock );
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+ PR_Close( file1 );
+ PR_Delete( "xxxTestFile");
+
+ /* test return and exit */
+ if (debug) {
+ printf("%s\n", (failed_already)? "FAIL" : "PASS");
+ }
+ return( (failed_already == PR_TRUE )? 1 : 0 );
+} /* main() */
+/* end ntioto.c */
diff --git a/nsprpub/pr/tests/ntoh.c b/nsprpub/pr/tests/ntoh.c
new file mode 100644
index 0000000000..980a1b4983
--- /dev/null
+++ b/nsprpub/pr/tests/ntoh.c
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * A test program for PR_htons, PR_ntohs, PR_htonl, PR_ntohl,
+ * PR_htonll, and PR_ntohll.
+ */
+
+#include "prnetdb.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* Byte sequence in network byte order */
+static unsigned char bytes_n[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+/* Integers in host byte order */
+static PRUint16 s_h = 0x0102;
+static PRUint32 l_h = 0x01020304;
+static PRUint64 ll_h = LL_INIT(0x01020304, 0x05060708);
+
+int main(int argc, char **argv)
+{
+ union {
+ PRUint16 s;
+ PRUint32 l;
+ PRUint64 ll;
+ unsigned char bytes[8];
+ } un;
+
+ un.s = s_h;
+ printf("%u %u\n",
+ un.bytes[0], un.bytes[1]);
+ un.s = PR_htons(un.s);
+ printf("%u %u\n",
+ un.bytes[0], un.bytes[1]);
+ if (memcmp(un.bytes, bytes_n, 2)) {
+ fprintf(stderr, "PR_htons failed\n");
+ exit(1);
+ }
+ un.s = PR_ntohs(un.s);
+ printf("%u %u\n",
+ un.bytes[0], un.bytes[1]);
+ if (un.s != s_h) {
+ fprintf(stderr, "PR_ntohs failed\n");
+ exit(1);
+ }
+
+ un.l = l_h;
+ printf("%u %u %u %u\n",
+ un.bytes[0], un.bytes[1], un.bytes[2], un.bytes[3]);
+ un.l = PR_htonl(un.l);
+ printf("%u %u %u %u\n",
+ un.bytes[0], un.bytes[1], un.bytes[2], un.bytes[3]);
+ if (memcmp(un.bytes, bytes_n, 4)) {
+ fprintf(stderr, "PR_htonl failed\n");
+ exit(1);
+ }
+ un.l = PR_ntohl(un.l);
+ printf("%u %u %u %u\n",
+ un.bytes[0], un.bytes[1], un.bytes[2], un.bytes[3]);
+ if (un.l != l_h) {
+ fprintf(stderr, "PR_ntohl failed\n");
+ exit(1);
+ }
+
+ un.ll = ll_h;
+ printf("%u %u %u %u %u %u %u %u\n",
+ un.bytes[0], un.bytes[1], un.bytes[2], un.bytes[3],
+ un.bytes[4], un.bytes[5], un.bytes[6], un.bytes[7]);
+ un.ll = PR_htonll(un.ll);
+ printf("%u %u %u %u %u %u %u %u\n",
+ un.bytes[0], un.bytes[1], un.bytes[2], un.bytes[3],
+ un.bytes[4], un.bytes[5], un.bytes[6], un.bytes[7]);
+ if (memcmp(un.bytes, bytes_n, 8)) {
+ fprintf(stderr, "PR_htonll failed\n");
+ exit(1);
+ }
+ un.ll = PR_ntohll(un.ll);
+ printf("%u %u %u %u %u %u %u %u\n",
+ un.bytes[0], un.bytes[1], un.bytes[2], un.bytes[3],
+ un.bytes[4], un.bytes[5], un.bytes[6], un.bytes[7]);
+ if (LL_NE(un.ll, ll_h)) {
+ fprintf(stderr, "PR_ntohll failed\n");
+ exit(1);
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/obsints.c b/nsprpub/pr/tests/obsints.c
new file mode 100644
index 0000000000..30501ad746
--- /dev/null
+++ b/nsprpub/pr/tests/obsints.c
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Test: obsints.c
+ *
+ * Description: make sure that protypes.h defines the obsolete integer
+ * types intn, uintn, uint, int8, uint8, int16, uint16, int32, uint32,
+ * int64, and uint64.
+ */
+
+#include <stdio.h>
+
+#ifdef NO_NSPR_10_SUPPORT
+
+/* nothing to do */
+int main(int argc, char **argv)
+{
+ printf("PASS\n");
+ return 0;
+}
+
+#else /* NO_NSPR_10_SUPPORT */
+
+#include "prtypes.h" /* which includes protypes.h */
+
+int main(int argc, char **argv)
+{
+ /*
+ * Compilation fails if any of these integer types are not
+ * defined by protypes.h.
+ */
+ intn in;
+ uintn uin;
+ uint ui;
+ int8 i8;
+ uint8 ui8;
+ int16 i16;
+ uint16 ui16;
+ int32 i32;
+ uint32 ui32;
+ int64 i64;
+ uint64 ui64;
+
+ printf("PASS\n");
+ return 0;
+}
+
+#endif /* NO_NSPR_10_SUPPORT */
diff --git a/nsprpub/pr/tests/op_2long.c b/nsprpub/pr/tests/op_2long.c
new file mode 100644
index 0000000000..7a6b13122c
--- /dev/null
+++ b/nsprpub/pr/tests/op_2long.c
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: op_2long.c
+**
+** Description: Test Program to verify the PR_NAME_TOO_LONG_ERROR
+**
+** Modification History:
+** 03-June-97 AGarcia- Initial version
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "prinit.h"
+#include "prmem.h"
+#include "prio.h"
+#include "prerror.h"
+#include <stdio.h>
+#include "plerror.h"
+#include "plgetopt.h"
+
+static PRFileDesc *t1;
+PRIntn error_code;
+
+/*
+ * should exceed any system's maximum file name length
+ * Note: was set at 4096. This is legal on some unix (Linux 2.1+) platforms.
+ *
+ */
+#define TOO_LONG 5000
+
+int main(int argc, char **argv)
+{
+ char nameTooLong[TOO_LONG];
+ int i;
+
+ /* Generate a really long pathname */
+ for (i = 0; i < TOO_LONG - 1; i++) {
+ if (i % 10 == 0) {
+ nameTooLong[i] = '/';
+ } else {
+ nameTooLong[i] = 'a';
+ }
+ }
+ nameTooLong[TOO_LONG - 1] = 0;
+
+ PR_STDIO_INIT();
+ t1 = PR_Open(nameTooLong, PR_RDWR, 0666);
+ if (t1 == NULL) {
+ if (PR_GetError() == PR_NAME_TOO_LONG_ERROR) {
+ PL_PrintError("error code is");
+ printf ("PASS\n");
+ return 0;
+ }
+ else {
+ PL_PrintError("error code is");
+ printf ("FAIL\n");
+ return 1;
+ }
+ }
+
+ else {
+ printf ("Test passed\n");
+ return 0;
+ }
+
+
+
+}
diff --git a/nsprpub/pr/tests/op_excl.c b/nsprpub/pr/tests/op_excl.c
new file mode 100644
index 0000000000..e214af1257
--- /dev/null
+++ b/nsprpub/pr/tests/op_excl.c
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: op_excl.c
+**
+** Description: Test Program to verify function of PR_EXCL open flag
+**
+** Modification History:
+** 27-Oct-1999 lth. Initial version
+***********************************************************************/
+
+#include <plgetopt.h>
+#include <nspr.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+** Test harness infrastructure
+*/
+PRLogModuleInfo *lm;
+PRLogModuleLevel msgLevel = PR_LOG_NONE;
+PRIntn debug = 0;
+PRUint32 failed_already = 0;
+/* end Test harness infrastructure */
+/*
+** Emit help text for this test
+*/
+static void Help( void )
+{
+ printf("op_excl: Help");
+ printf("op_excl [-d]");
+ printf("-d enables debug messages");
+ exit(1);
+} /* end Help() */
+
+
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *fd;
+ PRStatus rv;
+ PRInt32 written;
+ char outBuf[] = "op_excl.c test file";
+#define OUT_SIZE sizeof(outBuf)
+#define NEW_FILENAME "xxxExclNewFile"
+
+ {
+ /*
+ ** Get command line options
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hd");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug */
+ debug = 1;
+ msgLevel = PR_LOG_ERROR;
+ break;
+ case 'h': /* help message */
+ Help();
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ }
+
+ lm = PR_NewLogModule("Test"); /* Initialize logging */
+
+ /*
+ ** First, open a file, PR_EXCL, we believe not to exist
+ */
+ fd = PR_Open( NEW_FILENAME, PR_CREATE_FILE | PR_EXCL | PR_WRONLY, 0666 );
+ if ( NULL == fd ) {
+ if (debug) {
+ fprintf( stderr, "Open exclusive. Expected success, got failure\n");
+ }
+ failed_already = 1;
+ goto Finished;
+ }
+
+ written = PR_Write( fd, outBuf, OUT_SIZE );
+ if ( OUT_SIZE != written ) {
+ if (debug) {
+ fprintf( stderr, "Write after open exclusive failed\n");
+ }
+ failed_already = 1;
+ goto Finished;
+ }
+
+ rv = PR_Close(fd);
+ if ( PR_FAILURE == rv ) {
+ if (debug) {
+ fprintf( stderr, "Close after open exclusive failed\n");
+ }
+ failed_already = 1;
+ goto Finished;
+ }
+
+ /*
+ ** Second, open the same file, PR_EXCL, expect it to fail
+ */
+ fd = PR_Open( NEW_FILENAME, PR_CREATE_FILE | PR_EXCL | PR_WRONLY, 0666 );
+ if ( NULL != fd ) {
+ if (debug) {
+ fprintf( stderr, "Open exclusive. Expected failure, got success\n");
+ }
+ failed_already = 1;
+ PR_Close(fd);
+ }
+
+ rv = PR_Delete( NEW_FILENAME );
+ if ( PR_FAILURE == rv ) {
+ if (debug) {
+ fprintf( stderr, "PR_Delete() failed\n");
+ }
+ failed_already = 1;
+ }
+
+Finished:
+ if (debug) {
+ printf("%s\n", (failed_already)? "FAIL" : "PASS");
+ }
+ return( (failed_already == PR_TRUE )? 1 : 0 );
+} /* main() */
+/* end op_excl.c */
+
diff --git a/nsprpub/pr/tests/op_filnf.c b/nsprpub/pr/tests/op_filnf.c
new file mode 100644
index 0000000000..b4ab2fb156
--- /dev/null
+++ b/nsprpub/pr/tests/op_filnf.c
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: op_filnf.c
+**
+** Description: Test Program to verify the PR_FILE_NOT_FOUND_ERROR
+** This test program also uses the TRUNCATE option
+**
+** Modification History:
+** 03-June-97 AGarcia- Initial version
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "prinit.h"
+#include "prmem.h"
+#include "prio.h"
+#include "prerror.h"
+#include <stdio.h>
+#include "plgetopt.h"
+
+static PRFileDesc *t1;
+PRIntn error_code;
+
+int main(int argc, char **argv)
+{
+ PR_STDIO_INIT();
+ t1 = PR_Open("./tmp-ttools/err03.tmp", PR_TRUNCATE | PR_RDWR, 0666);
+ if (t1 == NULL) {
+ if (PR_GetError() == PR_FILE_NOT_FOUND_ERROR) {
+ printf ("error code is %d \n", PR_GetError());
+ printf ("PASS\n");
+ return 0;
+ }
+ else {
+ printf ("error code is %d \n", PR_GetError());
+ printf ("FAIL\n");
+ return 1;
+ }
+ }
+ PR_Close(t1);
+ printf ("opened a file that should not exist\n");
+ printf ("FAIL\n");
+ return 1;
+}
diff --git a/nsprpub/pr/tests/op_filok.c b/nsprpub/pr/tests/op_filok.c
new file mode 100644
index 0000000000..4f8688b7f1
--- /dev/null
+++ b/nsprpub/pr/tests/op_filok.c
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: op_filok.c
+**
+** Description: Test Program to verify the PR_Open finding an existing file.
+**
+** Modification History:
+** 03-June-97 AGarcia- Initial version
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "prinit.h"
+#include "prmem.h"
+#include "prio.h"
+#include "prerror.h"
+#include <stdio.h>
+
+static PRFileDesc *t1;
+
+int main(int argc, char **argv)
+{
+ PR_STDIO_INIT();
+
+ t1 = PR_Open(argv[0], PR_RDONLY, 0666);
+
+ if (t1 == NULL) {
+ printf ("error code is %d \n", PR_GetError());
+ printf ("File %s should be found\n", argv[0]);
+ return 1;
+ } else {
+ if (PR_Close(t1) == PR_SUCCESS) {
+ printf ("Test passed \n");
+ return 0;
+ } else {
+ printf ("cannot close file\n");
+ printf ("error code is %d\n", PR_GetError());
+ return 1;
+ }
+ }
+}
diff --git a/nsprpub/pr/tests/op_noacc.c b/nsprpub/pr/tests/op_noacc.c
new file mode 100644
index 0000000000..21ad04be46
--- /dev/null
+++ b/nsprpub/pr/tests/op_noacc.c
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: op_noacc.c
+**
+** Description: Test Program to verify the PR_NO_ACCESS_RIGHTS_ERROR in PR_Open
+**
+** Modification History:
+** 03-June-97 AGarcia- Initial version
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "prinit.h"
+#include "prmem.h"
+#include "prio.h"
+#include "prerror.h"
+#include <stdio.h>
+#include "plgetopt.h"
+
+static PRFileDesc *err01;
+PRIntn error_code;
+
+int main(int argc, char **argv)
+{
+#ifdef XP_PC
+ printf("op_noacc: Test not valid on MS-Windows.\n\tNo concept of 'mode' on Open() call\n");
+ return(0);
+#endif
+
+
+ PR_STDIO_INIT();
+ err01 = PR_Open("err01.tmp", PR_CREATE_FILE | PR_RDWR, 0);
+ if (err01 == NULL) {
+ int error = PR_GetError();
+ printf ("error code is %d\n", error);
+ if (error == PR_NO_ACCESS_RIGHTS_ERROR) {
+ printf ("PASS\n");
+ return 0;
+ }
+ }
+ printf ("FAIL\n");
+ return 1;
+}
+
diff --git a/nsprpub/pr/tests/op_nofil.c b/nsprpub/pr/tests/op_nofil.c
new file mode 100644
index 0000000000..12d542017c
--- /dev/null
+++ b/nsprpub/pr/tests/op_nofil.c
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: op_nofil.c
+**
+** Description: Test Program to verify the PR_FILE_NOT_FOUND_ERROR
+**
+** Modification History:
+** 03-June-97 AGarcia- Initial version
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "prinit.h"
+#include "prmem.h"
+#include "prio.h"
+#include "prerror.h"
+#include <stdio.h>
+#include "plgetopt.h"
+
+/*
+ * A file name that cannot exist
+ */
+#define NO_SUCH_FILE "/no/such/file.tmp"
+
+static PRFileDesc *t1;
+
+int main(int argc, char **argv)
+{
+ PR_STDIO_INIT();
+ t1 = PR_Open(NO_SUCH_FILE, PR_RDONLY, 0666);
+ if (t1 == NULL) {
+ if (PR_GetError() == PR_FILE_NOT_FOUND_ERROR) {
+ printf ("error code is PR_FILE_NOT_FOUND_ERROR, as expected\n");
+ printf ("PASS\n");
+ return 0;
+ } else {
+ printf ("error code is %d \n", PR_GetError());
+ printf ("FAIL\n");
+ return 1;
+ }
+ }
+ printf ("File %s exists on this machine!?\n", NO_SUCH_FILE);
+ if (PR_Close(t1) == PR_FAILURE) {
+ printf ("cannot close file\n");
+ printf ("error code is %d \n", PR_GetError());
+ }
+ printf ("FAIL\n");
+ return 1;
+}
diff --git a/nsprpub/pr/tests/openfile.c b/nsprpub/pr/tests/openfile.c
new file mode 100644
index 0000000000..265e041895
--- /dev/null
+++ b/nsprpub/pr/tests/openfile.c
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This test calls PR_OpenFile to create a bunch of files
+ * with various file modes.
+ */
+
+#include "prio.h"
+#include "prerror.h"
+#include "prinit.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define TEMPLATE_FILE_NAME "template.txt"
+
+int main(int argc, char **argv)
+{
+ FILE *template;
+ char buf[32];
+ PRInt32 nbytes;
+ PRFileDesc *fd;
+
+
+ /* Write in text mode. Let stdio deal with line endings. */
+ template = fopen(TEMPLATE_FILE_NAME, "w");
+ fputs("line 1\nline 2\n", template);
+ fclose(template);
+
+ /* Read in binary mode */
+ fd = PR_OpenFile(TEMPLATE_FILE_NAME, PR_RDONLY, 0666);
+ nbytes = PR_Read(fd, buf, sizeof(buf));
+ PR_Close(fd);
+ PR_Delete(TEMPLATE_FILE_NAME);
+
+ fd = PR_OpenFile("tfil0700.txt", PR_RDWR | PR_CREATE_FILE, 0700);
+ if (NULL == fd) {
+ fprintf(stderr, "PR_OpenFile failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ PR_Write(fd, buf, nbytes);
+ PR_Close(fd);
+
+ fd = PR_OpenFile("tfil0500.txt", PR_RDWR | PR_CREATE_FILE, 0500);
+ if (NULL == fd) {
+ fprintf(stderr, "PR_OpenFile failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ PR_Write(fd, buf, nbytes);
+ PR_Close(fd);
+
+ fd = PR_OpenFile("tfil0400.txt", PR_RDWR | PR_CREATE_FILE, 0400);
+ if (NULL == fd) {
+ fprintf(stderr, "PR_OpenFile failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ PR_Write(fd, buf, nbytes);
+ PR_Close(fd);
+
+ fd = PR_OpenFile("tfil0644.txt", PR_RDWR | PR_CREATE_FILE, 0644);
+ if (NULL == fd) {
+ fprintf(stderr, "PR_OpenFile failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ PR_Write(fd, buf, nbytes);
+ PR_Close(fd);
+
+ fd = PR_OpenFile("tfil0664.txt", PR_RDWR | PR_CREATE_FILE, 0664);
+ if (NULL == fd) {
+ fprintf(stderr, "PR_OpenFile failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ PR_Write(fd, buf, nbytes);
+ PR_Close(fd);
+
+ fd = PR_OpenFile("tfil0660.txt", PR_RDWR | PR_CREATE_FILE, 0660);
+ if (NULL == fd) {
+ fprintf(stderr, "PR_OpenFile failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ PR_Write(fd, buf, nbytes);
+ PR_Close(fd);
+
+ fd = PR_OpenFile("tfil0666.txt", PR_RDWR | PR_CREATE_FILE, 0666);
+ if (NULL == fd) {
+ fprintf(stderr, "PR_OpenFile failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ PR_Write(fd, buf, nbytes);
+ PR_Close(fd);
+
+ fd = PR_OpenFile("tfil0640.txt", PR_RDWR | PR_CREATE_FILE, 0640);
+ if (NULL == fd) {
+ fprintf(stderr, "PR_OpenFile failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ PR_Write(fd, buf, nbytes);
+ PR_Close(fd);
+
+ PR_Cleanup();
+ return 0;
+}
diff --git a/nsprpub/pr/tests/parent.c b/nsprpub/pr/tests/parent.c
new file mode 100644
index 0000000000..f43ce0c3f0
--- /dev/null
+++ b/nsprpub/pr/tests/parent.c
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** file: parent.c
+** description: test the process machinery
+*/
+
+#include "prmem.h"
+#include "prprf.h"
+#include "prinit.h"
+#include "prproces.h"
+#include "prinrval.h"
+
+typedef struct Child
+{
+ const char *name;
+ char **argv;
+ PRProcess *process;
+ PRProcessAttr *attr;
+} Child;
+
+/* for the default test 'cvar -c 2000' */
+static char *default_argv[] = {"cvar", "-c", "2000", NULL};
+
+static void PrintUsage(void)
+{
+ PR_fprintf(PR_GetSpecialFD(PR_StandardError),
+ "Usage: parent [-d] child [options]\n");
+}
+
+int main(int argc, char **argv)
+{
+ PRStatus rv;
+ PRInt32 test_status = 1;
+ PRIntervalTime t_start, t_elapsed;
+ PRFileDesc *debug = NULL;
+ Child *child = PR_NEWZAP(Child);
+
+ if (1 == argc)
+ {
+ /* no command-line arguments: run the default test */
+ child->argv = default_argv;
+ }
+ else
+ {
+ argv += 1; /* don't care about our program name */
+ while (*argv != NULL && argv[0][0] == '-')
+ {
+ if (argv[0][1] == 'd') {
+ debug = PR_GetSpecialFD(PR_StandardError);
+ }
+ else
+ {
+ PrintUsage();
+ return 2; /* not sufficient */
+ }
+ argv += 1;
+ }
+ child->argv = argv;
+ }
+
+ if (NULL == *child->argv)
+ {
+ PrintUsage();
+ return 2;
+ }
+
+ child->name = *child->argv;
+ if (NULL != debug) {
+ PR_fprintf(debug, "Forking %s\n", child->name);
+ }
+
+ child->attr = PR_NewProcessAttr();
+ PR_ProcessAttrSetStdioRedirect(
+ child->attr, PR_StandardOutput,
+ PR_GetSpecialFD(PR_StandardOutput));
+ PR_ProcessAttrSetStdioRedirect(
+ child->attr, PR_StandardError,
+ PR_GetSpecialFD(PR_StandardError));
+
+ t_start = PR_IntervalNow();
+ child->process = PR_CreateProcess(
+ child->name, child->argv, NULL, child->attr);
+ t_elapsed = (PRIntervalTime) (PR_IntervalNow() - t_start);
+
+ PR_DestroyProcessAttr(child->attr);
+
+ test_status = (NULL == child->process) ? 1 : 0;
+ if (NULL != debug)
+ {
+ PR_fprintf(
+ debug, "Child was %sforked\n",
+ (0 == test_status) ? "" : "NOT ");
+ if (0 == test_status)
+ PR_fprintf(
+ debug, "PR_CreateProcess took %lu microseconds\n",
+ PR_IntervalToMicroseconds(t_elapsed));
+ }
+
+ if (0 == test_status)
+ {
+ if (NULL != debug) {
+ PR_fprintf(debug, "Waiting for child to exit\n");
+ }
+ rv = PR_WaitProcess(child->process, &test_status);
+ if (PR_SUCCESS == rv)
+ {
+ if (NULL != debug)
+ PR_fprintf(
+ debug, "Child exited %s\n",
+ (0 == test_status) ? "successfully" : "with error");
+ }
+ else
+ {
+ test_status = 1;
+ if (NULL != debug) {
+ PR_fprintf(debug, "PR_WaitProcess failed\n");
+ }
+ }
+ }
+ PR_DELETE(child);
+ PR_Cleanup();
+ return test_status;
+
+} /* main */
+
+/* parent.c */
+
diff --git a/nsprpub/pr/tests/parsetm.c b/nsprpub/pr/tests/parsetm.c
new file mode 100644
index 0000000000..0e25ffe68d
--- /dev/null
+++ b/nsprpub/pr/tests/parsetm.c
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This test program should eventually become a full-blown test for
+ * PR_ParseTimeString. Right now it just verifies that PR_ParseTimeString
+ * doesn't crash on an out-of-range time string (bug 480740).
+ */
+
+#include "prtime.h"
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRBool debug_mode = PR_TRUE;
+
+static char *dayOfWeek[] =
+{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "???" };
+static char *month[] =
+{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???"
+};
+
+static void PrintExplodedTime(const PRExplodedTime *et) {
+ PRInt32 totalOffset;
+ PRInt32 hourOffset, minOffset;
+ const char *sign;
+
+ /* Print day of the week, month, day, hour, minute, and second */
+ if (debug_mode) printf("%s %s %ld %02ld:%02ld:%02ld ",
+ dayOfWeek[et->tm_wday], month[et->tm_month], et->tm_mday,
+ et->tm_hour, et->tm_min, et->tm_sec);
+
+ /* Print time zone */
+ totalOffset = et->tm_params.tp_gmt_offset + et->tm_params.tp_dst_offset;
+ if (totalOffset == 0) {
+ if (debug_mode) {
+ printf("UTC ");
+ }
+ } else {
+ sign = "+";
+ if (totalOffset < 0) {
+ totalOffset = -totalOffset;
+ sign = "-";
+ }
+ hourOffset = totalOffset / 3600;
+ minOffset = (totalOffset % 3600) / 60;
+ if (debug_mode) {
+ printf("%s%02ld%02ld ", sign, hourOffset, minOffset);
+ }
+ }
+
+ /* Print year */
+ if (debug_mode) {
+ printf("%hd", et->tm_year);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRTime ct;
+ PRExplodedTime et;
+ PRStatus rv;
+ char *sp1 = "Sat, 1 Jan 3001 00:00:00"; /* no time zone */
+ char *sp2 = "Fri, 31 Dec 3000 23:59:60"; /* no time zone, not normalized */
+
+#if _MSC_VER >= 1400 && !defined(WINCE)
+ /* Run this test in the US Pacific Time timezone. */
+ _putenv_s("TZ", "PST8PDT");
+ _tzset();
+#endif
+
+ rv = PR_ParseTimeString(sp1, PR_FALSE, &ct);
+ printf("rv = %d\n", rv);
+ PR_ExplodeTime(ct, PR_GMTParameters, &et);
+ PrintExplodedTime(&et);
+ printf("\n");
+
+ rv = PR_ParseTimeString(sp2, PR_FALSE, &ct);
+ printf("rv = %d\n", rv);
+ PR_ExplodeTime(ct, PR_GMTParameters, &et);
+ PrintExplodedTime(&et);
+ printf("\n");
+
+ return 0;
+}
diff --git a/nsprpub/pr/tests/peek.c b/nsprpub/pr/tests/peek.c
new file mode 100644
index 0000000000..4dce8299dd
--- /dev/null
+++ b/nsprpub/pr/tests/peek.c
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * A test case for the PR_MSG_PEEK flag of PR_Recv().
+ *
+ * Test both blocking and non-blocking sockets.
+ */
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define BUFFER_SIZE 1024
+
+static int iterations = 10;
+
+/*
+ * In iteration i, recv_amount[i] is the number of bytes we
+ * wish to receive, and send_amount[i] is the number of bytes
+ * we actually send. Therefore, the number of elements in the
+ * recv_amount or send_amount array should equal to 'iterations'.
+ * For this test to pass we need to ensure that
+ * recv_amount[i] <= BUFFER_SIZE,
+ * send_amount[i] <= BUFFER_SIZE,
+ * send_amount[i] <= recv_amount[i].
+ */
+static PRInt32 recv_amount[10] = {
+ 16, 128, 256, 1024, 512, 512, 128, 256, 32, 32
+};
+static PRInt32 send_amount[10] = {
+ 16, 64, 128, 1024, 512, 256, 128, 64, 16, 32
+};
+
+/* Blocking I/O */
+static void ServerB(void *arg)
+{
+ PRFileDesc *listenSock = (PRFileDesc *) arg;
+ PRFileDesc *sock;
+ char buf[BUFFER_SIZE];
+ PRInt32 nbytes;
+ int i;
+ int j;
+
+ sock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == sock) {
+ fprintf(stderr, "PR_Accept failed\n");
+ exit(1);
+ }
+
+ for (i = 0; i < iterations; i++) {
+ memset(buf, 0, sizeof(buf));
+ nbytes = PR_Recv(sock, buf, recv_amount[i],
+ PR_MSG_PEEK, PR_INTERVAL_NO_TIMEOUT);
+ if (-1 == nbytes) {
+ fprintf(stderr, "PR_Recv failed\n");
+ exit(1);
+ }
+ if (send_amount[i] != nbytes) {
+ fprintf(stderr, "PR_Recv returned %d, absurd!\n", nbytes);
+ exit(1);
+ }
+ for (j = 0; j < nbytes; j++) {
+ if (buf[j] != 2*i) {
+ fprintf(stderr, "byte %d should be %d but is %d\n",
+ j, 2*i, buf[j]);
+ exit(1);
+ }
+ }
+ fprintf(stderr, "server: peeked expected data\n");
+
+ memset(buf, 0, sizeof(buf));
+ nbytes = PR_Recv(sock, buf, recv_amount[i],
+ PR_MSG_PEEK, PR_INTERVAL_NO_TIMEOUT);
+ if (-1 == nbytes) {
+ fprintf(stderr, "PR_Recv failed\n");
+ exit(1);
+ }
+ if (send_amount[i] != nbytes) {
+ fprintf(stderr, "PR_Recv returned %d, absurd!\n", nbytes);
+ exit(1);
+ }
+ for (j = 0; j < nbytes; j++) {
+ if (buf[j] != 2*i) {
+ fprintf(stderr, "byte %d should be %d but is %d\n",
+ j, 2*i, buf[j]);
+ exit(1);
+ }
+ }
+ fprintf(stderr, "server: peeked expected data\n");
+
+ memset(buf, 0, sizeof(buf));
+ nbytes = PR_Recv(sock, buf, recv_amount[i],
+ 0, PR_INTERVAL_NO_TIMEOUT);
+ if (-1 == nbytes) {
+ fprintf(stderr, "PR_Recv failed\n");
+ exit(1);
+ }
+ if (send_amount[i] != nbytes) {
+ fprintf(stderr, "PR_Recv returned %d, absurd!\n", nbytes);
+ exit(1);
+ }
+ for (j = 0; j < nbytes; j++) {
+ if (buf[j] != 2*i) {
+ fprintf(stderr, "byte %d should be %d but is %d\n",
+ j, 2*i, buf[j]);
+ exit(1);
+ }
+ }
+ fprintf(stderr, "server: received expected data\n");
+
+ PR_Sleep(PR_SecondsToInterval(1));
+ memset(buf, 2*i+1, send_amount[i]);
+ nbytes = PR_Send(sock, buf, send_amount[i],
+ 0, PR_INTERVAL_NO_TIMEOUT);
+ if (-1 == nbytes) {
+ fprintf(stderr, "PR_Send failed\n");
+ exit(1);
+ }
+ if (send_amount[i] != nbytes) {
+ fprintf(stderr, "PR_Send returned %d, absurd!\n", nbytes);
+ exit(1);
+ }
+ }
+ if (PR_Close(sock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+}
+
+/* Non-blocking I/O */
+static void ClientNB(void *arg)
+{
+ PRFileDesc *sock;
+ PRSocketOptionData opt;
+ PRUint16 port = (PRUint16) arg;
+ PRNetAddr addr;
+ char buf[BUFFER_SIZE];
+ PRPollDesc pd;
+ PRInt32 npds;
+ PRInt32 nbytes;
+ int i;
+ int j;
+
+ sock = PR_OpenTCPSocket(PR_AF_INET6);
+ if (NULL == sock) {
+ fprintf(stderr, "PR_OpenTCPSocket failed\n");
+ exit(1);
+ }
+ opt.option = PR_SockOpt_Nonblocking;
+ opt.value.non_blocking = PR_TRUE;
+ if (PR_SetSocketOption(sock, &opt) == PR_FAILURE) {
+ fprintf(stderr, "PR_SetSocketOption failed\n");
+ exit(1);
+ }
+ memset(&addr, 0, sizeof(addr));
+ if (PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET6, port, &addr)
+ == PR_FAILURE) {
+ fprintf(stderr, "PR_SetNetAddr failed\n");
+ exit(1);
+ }
+ if (PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ if (PR_GetError() != PR_IN_PROGRESS_ERROR) {
+ fprintf(stderr, "PR_Connect failed\n");
+ exit(1);
+ }
+ pd.fd = sock;
+ pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ npds = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (-1 == npds) {
+ fprintf(stderr, "PR_Poll failed\n");
+ exit(1);
+ }
+ if (1 != npds) {
+ fprintf(stderr, "PR_Poll returned %d, absurd!\n", npds);
+ exit(1);
+ }
+ if (PR_GetConnectStatus(&pd) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetConnectStatus failed\n");
+ exit(1);
+ }
+ }
+
+ for (i = 0; i < iterations; i++) {
+ PR_Sleep(PR_SecondsToInterval(1));
+ memset(buf, 2*i, send_amount[i]);
+ while ((nbytes = PR_Send(sock, buf, send_amount[i],
+ 0, PR_INTERVAL_NO_TIMEOUT)) == -1) {
+ if (PR_GetError() != PR_WOULD_BLOCK_ERROR) {
+ fprintf(stderr, "PR_Send failed\n");
+ exit(1);
+ }
+ pd.fd = sock;
+ pd.in_flags = PR_POLL_WRITE;
+ npds = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (-1 == npds) {
+ fprintf(stderr, "PR_Poll failed\n");
+ exit(1);
+ }
+ if (1 != npds) {
+ fprintf(stderr, "PR_Poll returned %d, absurd!\n", npds);
+ exit(1);
+ }
+ }
+ if (send_amount[i] != nbytes) {
+ fprintf(stderr, "PR_Send returned %d, absurd!\n", nbytes);
+ exit(1);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ while ((nbytes = PR_Recv(sock, buf, recv_amount[i],
+ PR_MSG_PEEK, PR_INTERVAL_NO_TIMEOUT)) == -1) {
+ if (PR_GetError() != PR_WOULD_BLOCK_ERROR) {
+ fprintf(stderr, "PR_Recv failed\n");
+ exit(1);
+ }
+ pd.fd = sock;
+ pd.in_flags = PR_POLL_READ;
+ npds = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (-1 == npds) {
+ fprintf(stderr, "PR_Poll failed\n");
+ exit(1);
+ }
+ if (1 != npds) {
+ fprintf(stderr, "PR_Poll returned %d, absurd!\n", npds);
+ exit(1);
+ }
+ }
+ if (send_amount[i] != nbytes) {
+ fprintf(stderr, "PR_Recv returned %d, absurd!\n", nbytes);
+ exit(1);
+ }
+ for (j = 0; j < nbytes; j++) {
+ if (buf[j] != 2*i+1) {
+ fprintf(stderr, "byte %d should be %d but is %d\n",
+ j, 2*i+1, buf[j]);
+ exit(1);
+ }
+ }
+ fprintf(stderr, "client: peeked expected data\n");
+
+ memset(buf, 0, sizeof(buf));
+ nbytes = PR_Recv(sock, buf, recv_amount[i],
+ PR_MSG_PEEK, PR_INTERVAL_NO_TIMEOUT);
+ if (-1 == nbytes) {
+ fprintf(stderr, "PR_Recv failed\n");
+ exit(1);
+ }
+ if (send_amount[i] != nbytes) {
+ fprintf(stderr, "PR_Recv returned %d, absurd!\n", nbytes);
+ exit(1);
+ }
+ for (j = 0; j < nbytes; j++) {
+ if (buf[j] != 2*i+1) {
+ fprintf(stderr, "byte %d should be %d but is %d\n",
+ j, 2*i+1, buf[j]);
+ exit(1);
+ }
+ }
+ fprintf(stderr, "client: peeked expected data\n");
+
+ memset(buf, 0, sizeof(buf));
+ nbytes = PR_Recv(sock, buf, recv_amount[i],
+ 0, PR_INTERVAL_NO_TIMEOUT);
+ if (-1 == nbytes) {
+ fprintf(stderr, "PR_Recv failed\n");
+ exit(1);
+ }
+ if (send_amount[i] != nbytes) {
+ fprintf(stderr, "PR_Recv returned %d, absurd!\n", nbytes);
+ exit(1);
+ }
+ for (j = 0; j < nbytes; j++) {
+ if (buf[j] != 2*i+1) {
+ fprintf(stderr, "byte %d should be %d but is %d\n",
+ j, 2*i+1, buf[j]);
+ exit(1);
+ }
+ }
+ fprintf(stderr, "client: received expected data\n");
+ }
+ if (PR_Close(sock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+}
+
+static void
+RunTest(PRThreadScope scope, PRFileDesc *listenSock, PRUint16 port)
+{
+ PRThread *server, *client;
+
+ server = PR_CreateThread(PR_USER_THREAD, ServerB, listenSock,
+ PR_PRIORITY_NORMAL, scope, PR_JOINABLE_THREAD, 0);
+ if (NULL == server) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+ client = PR_CreateThread(
+ PR_USER_THREAD, ClientNB, (void *) port,
+ PR_PRIORITY_NORMAL, scope, PR_JOINABLE_THREAD, 0);
+ if (NULL == client) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+
+ if (PR_JoinThread(server) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+ if (PR_JoinThread(client) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock;
+ PRNetAddr addr;
+ PRUint16 port;
+
+ listenSock = PR_OpenTCPSocket(PR_AF_INET6);
+ if (NULL == listenSock) {
+ fprintf(stderr, "PR_OpenTCPSocket failed\n");
+ exit(1);
+ }
+ memset(&addr, 0, sizeof(addr));
+ if (PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, 0, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_SetNetAddr failed\n");
+ exit(1);
+ }
+ if (PR_Bind(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_Bind failed\n");
+ exit(1);
+ }
+ if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ exit(1);
+ }
+ port = PR_ntohs(addr.ipv6.port);
+ if (PR_Listen(listenSock, 5) == PR_FAILURE) {
+ fprintf(stderr, "PR_Listen failed\n");
+ exit(1);
+ }
+
+ fprintf(stderr, "Running the test with local threads\n");
+ RunTest(PR_LOCAL_THREAD, listenSock, port);
+ fprintf(stderr, "Running the test with global threads\n");
+ RunTest(PR_GLOBAL_THREAD, listenSock, port);
+
+ if (PR_Close(listenSock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/perf.c b/nsprpub/pr/tests/perf.c
new file mode 100644
index 0000000000..cd8ac1439b
--- /dev/null
+++ b/nsprpub/pr/tests/perf.c
@@ -0,0 +1,447 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int _debug_on = 0;
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+#include "obsolete/prsem.h"
+
+PRLock *lock;
+PRMonitor *mon;
+PRMonitor *mon2;
+
+#define DEFAULT_COUNT 1000
+
+PRInt32 count;
+
+static void nop(int a, int b, int c)
+{
+}
+
+static void LocalProcedureCall(void)
+{
+ PRInt32 i;
+
+ for (i = 0; i < count; i++) {
+ nop(i, i, 5);
+ }
+}
+
+static void DLLProcedureCall(void)
+{
+ PRInt32 i;
+ PRThreadState state;
+ PRThread *self = PR_GetCurrentThread();
+
+ for (i = 0; i < count; i++) {
+ state = PR_GetThreadState(self);
+ }
+}
+
+static void Now(void)
+{
+ PRInt32 i;
+ PRTime time;
+
+ for (i = 0; i < count; i++) {
+ time = PR_Now();
+ }
+}
+
+static void Interval(void)
+{
+ PRInt32 i;
+ PRIntervalTime time;
+
+ for (i = 0; i < count; i++) {
+ time = PR_IntervalNow();
+ }
+}
+
+static void IdleLock(void)
+{
+ PRInt32 i;
+
+ for (i = 0; i < count; i++) {
+ PR_Lock(lock);
+ PR_Unlock(lock);
+ }
+}
+
+static void IdleMonitor(void)
+{
+ PRInt32 i;
+
+ for (i = 0; i < count; i++) {
+ PR_EnterMonitor(mon);
+ PR_ExitMonitor(mon);
+ }
+}
+
+static void IdleCMonitor(void)
+{
+ PRInt32 i;
+
+ for (i = 0; i < count; i++) {
+ PR_CEnterMonitor((void*)7);
+ PR_CExitMonitor((void*)7);
+ }
+}
+
+/************************************************************************/
+
+static void PR_CALLBACK dull(void *arg)
+{
+}
+
+static void CDThread(void)
+{
+ PRInt32 i;
+ int num_threads = count;
+
+ /*
+ * Cannot create too many threads
+ */
+ if (num_threads > 1000) {
+ num_threads = 1000;
+ }
+
+ for (i = 0; i < num_threads; i++) {
+ PRThread *t = PR_CreateThread(PR_USER_THREAD,
+ dull, 0,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (NULL == t) {
+ fprintf(stderr, "CDThread: cannot create thread %3d\n", i);
+ } else {
+ DPRINTF(("CDThread: created thread %3d \n",i));
+ }
+ PR_Sleep(0);
+ }
+}
+
+static int alive;
+static int cxq;
+
+static void PR_CALLBACK CXReader(void *arg)
+{
+ PRInt32 i, n;
+
+ PR_EnterMonitor(mon);
+ n = count / 2;
+ for (i = 0; i < n; i++) {
+ while (cxq == 0) {
+ DPRINTF(("CXReader: thread = 0x%lx waiting\n",
+ PR_GetCurrentThread()));
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+ --cxq;
+ PR_Notify(mon);
+ }
+ PR_ExitMonitor(mon);
+
+ PR_EnterMonitor(mon2);
+ --alive;
+ PR_Notify(mon2);
+ PR_ExitMonitor(mon2);
+ DPRINTF(("CXReader: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
+}
+
+static void PR_CALLBACK CXWriter(void *arg)
+{
+ PRInt32 i, n;
+
+ PR_EnterMonitor(mon);
+ n = count / 2;
+ for (i = 0; i < n; i++) {
+ while (cxq == 1) {
+ DPRINTF(("CXWriter: thread = 0x%lx waiting\n",
+ PR_GetCurrentThread()));
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+ ++cxq;
+ PR_Notify(mon);
+ }
+ PR_ExitMonitor(mon);
+
+ PR_EnterMonitor(mon2);
+ --alive;
+ PR_Notify(mon2);
+ PR_ExitMonitor(mon2);
+ DPRINTF(("CXWriter: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
+}
+
+static void ContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *t1, *t2;
+
+ PR_EnterMonitor(mon2);
+ alive = 2;
+ cxq = 0;
+
+ t1 = PR_CreateThread(PR_USER_THREAD,
+ CXReader, 0,
+ PR_PRIORITY_NORMAL,
+ scope1,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (NULL == t1) {
+ fprintf(stderr, "ContextSwitch: cannot create thread\n");
+ } else {
+ DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n",
+ (scope1 == PR_GLOBAL_THREAD ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
+ t1));
+ }
+ t2 = PR_CreateThread(PR_USER_THREAD,
+ CXWriter, 0,
+ PR_PRIORITY_NORMAL,
+ scope2,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (NULL == t2) {
+ fprintf(stderr, "ContextSwitch: cannot create thread\n");
+ } else {
+ DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n",
+ (scope2 == PR_GLOBAL_THREAD ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
+ t2));
+ }
+
+ /* Wait for both of the threads to exit */
+ while (alive) {
+ PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(mon2);
+}
+
+static void ContextSwitchUU(void)
+{
+ ContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void ContextSwitchUK(void)
+{
+ ContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+static void ContextSwitchKU(void)
+{
+ ContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void ContextSwitchKK(void)
+{
+ ContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+/************************************************************************/
+
+static void PR_CALLBACK SemaThread(void *argSema)
+{
+ PRSemaphore **sem = (PRSemaphore **)argSema;
+ PRInt32 i, n;
+
+ n = count / 2;
+ for (i = 0; i < n; i++) {
+ DPRINTF(("SemaThread: thread = 0x%lx waiting on sem = 0x%lx\n",
+ PR_GetCurrentThread(), sem[0]));
+ PR_WaitSem(sem[0]);
+ DPRINTF(("SemaThread: thread = 0x%lx posting on sem = 0x%lx\n",
+ PR_GetCurrentThread(), sem[1]));
+ PR_PostSem(sem[1]);
+ }
+
+ PR_EnterMonitor(mon2);
+ --alive;
+ PR_Notify(mon2);
+ PR_ExitMonitor(mon2);
+ DPRINTF(("SemaThread: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
+}
+
+static PRSemaphore *sem_set1[2];
+static PRSemaphore *sem_set2[2];
+
+static void SemaContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *t1, *t2;
+ sem_set1[0] = PR_NewSem(1);
+ sem_set1[1] = PR_NewSem(0);
+ sem_set2[0] = sem_set1[1];
+ sem_set2[1] = sem_set1[0];
+
+ PR_EnterMonitor(mon2);
+ alive = 2;
+ cxq = 0;
+
+ t1 = PR_CreateThread(PR_USER_THREAD,
+ SemaThread,
+ sem_set1,
+ PR_PRIORITY_NORMAL,
+ scope1,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (NULL == t1) {
+ fprintf(stderr, "SemaContextSwitch: cannot create thread\n");
+ } else {
+ DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n",
+ (scope1 == PR_GLOBAL_THREAD ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
+ t1));
+ }
+ t2 = PR_CreateThread(PR_USER_THREAD,
+ SemaThread,
+ sem_set2,
+ PR_PRIORITY_NORMAL,
+ scope2,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (NULL == t2) {
+ fprintf(stderr, "SemaContextSwitch: cannot create thread\n");
+ } else {
+ DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n",
+ (scope2 == PR_GLOBAL_THREAD ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
+ t2));
+ }
+
+ /* Wait for both of the threads to exit */
+ while (alive) {
+ PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(mon2);
+
+ PR_DestroySem(sem_set1[0]);
+ PR_DestroySem(sem_set1[1]);
+}
+
+static void SemaContextSwitchUU(void)
+{
+ SemaContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void SemaContextSwitchUK(void)
+{
+ SemaContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+static void SemaContextSwitchKU(void)
+{
+ SemaContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void SemaContextSwitchKK(void)
+{
+ SemaContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow() - start;
+ d = (double)PR_IntervalToMicroseconds(stop);
+
+ printf("%40s: %6.2f usec\n", msg, d / count);
+}
+
+int main(int argc, char **argv)
+{
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dc:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ case 'c': /* loop count */
+ count = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == count) {
+ count = DEFAULT_COUNT;
+ }
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_BlockClockInterrupts();
+ PR_UnblockClockInterrupts();
+ PR_STDIO_INIT();
+
+ lock = PR_NewLock();
+ mon = PR_NewMonitor();
+ mon2 = PR_NewMonitor();
+
+ Measure(LocalProcedureCall, "local procedure call overhead");
+ Measure(DLLProcedureCall, "DLL procedure call overhead");
+ Measure(Now, "current calendar time");
+ Measure(Interval, "interval time");
+ Measure(IdleLock, "idle lock lock/unlock pair");
+ Measure(IdleMonitor, "idle monitor entry/exit pair");
+ Measure(IdleCMonitor, "idle cache monitor entry/exit pair");
+ Measure(CDThread, "create/destroy thread pair");
+ Measure(ContextSwitchUU, "context switch - user/user");
+ Measure(ContextSwitchUK, "context switch - user/kernel");
+ Measure(ContextSwitchKU, "context switch - kernel/user");
+ Measure(ContextSwitchKK, "context switch - kernel/kernel");
+ Measure(SemaContextSwitchUU, "sema context switch - user/user");
+ Measure(SemaContextSwitchUK, "sema context switch - user/kernel");
+ Measure(SemaContextSwitchKU, "sema context switch - kernel/user");
+ Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel");
+
+ printf("--------------\n");
+ printf("Adding 7 additional CPUs\n");
+
+ PR_SetConcurrency(8);
+ printf("--------------\n");
+
+ Measure(LocalProcedureCall, "local procedure call overhead");
+ Measure(DLLProcedureCall, "DLL procedure call overhead");
+ Measure(Now, "current calendar time");
+ Measure(Interval, "interval time");
+ Measure(IdleLock, "idle lock lock/unlock pair");
+ Measure(IdleMonitor, "idle monitor entry/exit pair");
+ Measure(IdleCMonitor, "idle cache monitor entry/exit pair");
+ Measure(CDThread, "create/destroy thread pair");
+ Measure(ContextSwitchUU, "context switch - user/user");
+ Measure(ContextSwitchUK, "context switch - user/kernel");
+ Measure(ContextSwitchKU, "context switch - kernel/user");
+ Measure(ContextSwitchKK, "context switch - kernel/kernel");
+ Measure(SemaContextSwitchUU, "sema context switch - user/user");
+ Measure(SemaContextSwitchUK, "sema context switch - user/kernel");
+ Measure(SemaContextSwitchKU, "sema context switch - kernel/user");
+ Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel");
+
+ PR_DestroyLock(lock);
+ PR_DestroyMonitor(mon);
+ PR_DestroyMonitor(mon2);
+
+ PR_Cleanup();
+ return 0;
+}
diff --git a/nsprpub/pr/tests/pipeping.c b/nsprpub/pr/tests/pipeping.c
new file mode 100644
index 0000000000..26b63c659d
--- /dev/null
+++ b/nsprpub/pr/tests/pipeping.c
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: pipeping.c
+ *
+ * Description:
+ * This test runs in conjunction with the pipepong test.
+ * This test creates two pipes and redirects the stdin and
+ * stdout of the pipepong test to the pipes. Then this
+ * test writes "ping" to the pipepong test and the pipepong
+ * test writes "pong" back. To run this pair of tests,
+ * just invoke pipeping.
+ *
+ * Tested areas: process creation, pipes, file descriptor
+ * inheritance, standard I/O redirection.
+ */
+
+#include "prerror.h"
+#include "prio.h"
+#include "prproces.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef XP_OS2
+static char *child_argv[] = { "pipepong.exe", NULL };
+#else
+static char *child_argv[] = { "pipepong", NULL };
+#endif
+
+#define NUM_ITERATIONS 10
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *in_pipe[2];
+ PRFileDesc *out_pipe[2];
+ PRStatus status;
+ PRProcess *process;
+ PRProcessAttr *attr;
+ char buf[1024];
+ PRInt32 nBytes;
+ PRInt32 exitCode;
+ int idx;
+
+ status = PR_CreatePipe(&in_pipe[0], &in_pipe[1]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_CreatePipe failed\n");
+ exit(1);
+ }
+ status = PR_CreatePipe(&out_pipe[0], &out_pipe[1]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_CreatePipe failed\n");
+ exit(1);
+ }
+
+ status = PR_SetFDInheritable(in_pipe[0], PR_FALSE);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_SetFDInheritable failed\n");
+ exit(1);
+ }
+ status = PR_SetFDInheritable(in_pipe[1], PR_TRUE);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_SetFDInheritable failed\n");
+ exit(1);
+ }
+ status = PR_SetFDInheritable(out_pipe[0], PR_TRUE);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_SetFDInheritable failed\n");
+ exit(1);
+ }
+ status = PR_SetFDInheritable(out_pipe[1], PR_FALSE);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_SetFDInheritable failed\n");
+ exit(1);
+ }
+
+ attr = PR_NewProcessAttr();
+ if (attr == NULL) {
+ fprintf(stderr, "PR_NewProcessAttr failed\n");
+ exit(1);
+ }
+
+ PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, out_pipe[0]);
+ PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, in_pipe[1]);
+
+ process = PR_CreateProcess(child_argv[0], child_argv, NULL, attr);
+ if (process == NULL) {
+ fprintf(stderr, "PR_CreateProcess failed\n");
+ exit(1);
+ }
+ PR_DestroyProcessAttr(attr);
+ status = PR_Close(out_pipe[0]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ status = PR_Close(in_pipe[1]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+
+ for (idx = 0; idx < NUM_ITERATIONS; idx++) {
+ strcpy(buf, "ping");
+ printf("ping process: sending \"%s\"\n", buf);
+ nBytes = PR_Write(out_pipe[1], buf, 5);
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Write failed: (%d, %d)\n", PR_GetError(),
+ PR_GetOSError());
+ exit(1);
+ }
+ memset(buf, 0, sizeof(buf));
+ nBytes = PR_Read(in_pipe[0], buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ printf("ping process: received \"%s\"\n", buf);
+ if (nBytes != 5) {
+ fprintf(stderr, "ping process: expected 5 bytes but got %d bytes\n",
+ nBytes);
+ exit(1);
+ }
+ if (strcmp(buf, "pong") != 0) {
+ fprintf(stderr, "ping process: expected \"pong\" but got \"%s\"\n",
+ buf);
+ exit(1);
+ }
+ }
+
+ status = PR_Close(in_pipe[0]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ status = PR_Close(out_pipe[1]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ status = PR_WaitProcess(process, &exitCode);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_WaitProcess failed\n");
+ exit(1);
+ }
+ if (exitCode == 0) {
+ printf("PASS\n");
+ return 0;
+ } else {
+ printf("FAIL\n");
+ return 1;
+ }
+}
diff --git a/nsprpub/pr/tests/pipeping2.c b/nsprpub/pr/tests/pipeping2.c
new file mode 100644
index 0000000000..ed96109172
--- /dev/null
+++ b/nsprpub/pr/tests/pipeping2.c
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: pipeping2.c
+ *
+ * Description:
+ * This test runs in conjunction with the pipepong2 test.
+ * This test creates two pipes and passes two pipe fd's
+ * to the pipepong2 test. Then this test writes "ping" to
+ * to the pipepong2 test and the pipepong2 test writes "pong"
+ * back. To run this pair of tests, just invoke pipeping2.
+ *
+ * Tested areas: process creation, pipes, file descriptor
+ * inheritance.
+ */
+
+#include "prerror.h"
+#include "prio.h"
+#include "prproces.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define NUM_ITERATIONS 10
+
+static char *child_argv[] = { "pipepong2", NULL };
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *in_pipe[2];
+ PRFileDesc *out_pipe[2];
+ PRStatus status;
+ PRProcess *process;
+ PRProcessAttr *attr;
+ char buf[1024];
+ PRInt32 nBytes;
+ PRInt32 exitCode;
+ int idx;
+
+ status = PR_CreatePipe(&in_pipe[0], &in_pipe[1]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_CreatePipe failed\n");
+ exit(1);
+ }
+ status = PR_CreatePipe(&out_pipe[0], &out_pipe[1]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_CreatePipe failed\n");
+ exit(1);
+ }
+
+ status = PR_SetFDInheritable(in_pipe[0], PR_FALSE);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_SetFDInheritable failed\n");
+ exit(1);
+ }
+ status = PR_SetFDInheritable(in_pipe[1], PR_TRUE);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_SetFDInheritable failed\n");
+ exit(1);
+ }
+ status = PR_SetFDInheritable(out_pipe[0], PR_TRUE);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_SetFDInheritable failed\n");
+ exit(1);
+ }
+ status = PR_SetFDInheritable(out_pipe[1], PR_FALSE);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_SetFDInheritable failed\n");
+ exit(1);
+ }
+
+ attr = PR_NewProcessAttr();
+ if (attr == NULL) {
+ fprintf(stderr, "PR_NewProcessAttr failed\n");
+ exit(1);
+ }
+
+ status = PR_ProcessAttrSetInheritableFD(attr, out_pipe[0], "PIPE_READ");
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_ProcessAttrSetInheritableFD failed\n");
+ exit(1);
+ }
+ status = PR_ProcessAttrSetInheritableFD(attr, in_pipe[1], "PIPE_WRITE");
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_ProcessAttrSetInheritableFD failed\n");
+ exit(1);
+ }
+
+ process = PR_CreateProcess(child_argv[0], child_argv, NULL, attr);
+ if (process == NULL) {
+ fprintf(stderr, "PR_CreateProcess failed\n");
+ exit(1);
+ }
+ PR_DestroyProcessAttr(attr);
+ status = PR_Close(out_pipe[0]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ status = PR_Close(in_pipe[1]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+
+ for (idx = 0; idx < NUM_ITERATIONS; idx++) {
+ strcpy(buf, "ping");
+ printf("ping process: sending \"%s\"\n", buf);
+ nBytes = PR_Write(out_pipe[1], buf, 5);
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Write failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ memset(buf, 0, sizeof(buf));
+ nBytes = PR_Read(in_pipe[0], buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read failed\n");
+ exit(1);
+ }
+ printf("ping process: received \"%s\"\n", buf);
+ if (nBytes != 5) {
+ fprintf(stderr, "ping process: expected 5 bytes but got %d bytes\n",
+ nBytes);
+ exit(1);
+ }
+ if (strcmp(buf, "pong") != 0) {
+ fprintf(stderr, "ping process: expected \"pong\" but got \"%s\"\n",
+ buf);
+ exit(1);
+ }
+ }
+
+ status = PR_Close(in_pipe[0]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ status = PR_Close(out_pipe[1]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ status = PR_WaitProcess(process, &exitCode);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_WaitProcess failed\n");
+ exit(1);
+ }
+ if (exitCode == 0) {
+ printf("PASS\n");
+ return 0;
+ } else {
+ printf("FAIL\n");
+ return 1;
+ }
+}
diff --git a/nsprpub/pr/tests/pipepong.c b/nsprpub/pr/tests/pipepong.c
new file mode 100644
index 0000000000..965aec32de
--- /dev/null
+++ b/nsprpub/pr/tests/pipepong.c
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: pipepong.c
+ *
+ * Description:
+ * This test runs in conjunction with the pipeping test.
+ * The pipeping test creates two pipes and redirects the
+ * stdin and stdout of this test to the pipes. Then the
+ * pipeping test writes "ping" to this test and this test
+ * writes "pong" back. Note that this test does not depend
+ * on NSPR at all. To run this pair of tests, just invoke
+ * pipeping.
+ *
+ * Tested areas: process creation, pipes, file descriptor
+ * inheritance, standard I/O redirection.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define NUM_ITERATIONS 10
+
+int main(int argc, char **argv)
+{
+ char buf[1024];
+ size_t nBytes;
+ int idx;
+
+ for (idx = 0; idx < NUM_ITERATIONS; idx++) {
+ memset(buf, 0, sizeof(buf));
+ nBytes = fread(buf, 1, 5, stdin);
+ fprintf(stderr, "pong process: received \"%s\"\n", buf);
+ if (nBytes != 5) {
+ fprintf(stderr, "pong process: expected 5 bytes but got %d bytes\n",
+ nBytes);
+ exit(1);
+ }
+ if (strcmp(buf, "ping") != 0) {
+ fprintf(stderr, "pong process: expected \"ping\" but got \"%s\"\n",
+ buf);
+ exit(1);
+ }
+
+ strcpy(buf, "pong");
+ fprintf(stderr, "pong process: sending \"%s\"\n", buf);
+ nBytes = fwrite(buf, 1, 5, stdout);
+ if (nBytes != 5) {
+ fprintf(stderr, "pong process: fwrite failed\n");
+ exit(1);
+ }
+ fflush(stdout);
+ }
+
+ return 0;
+}
diff --git a/nsprpub/pr/tests/pipepong2.c b/nsprpub/pr/tests/pipepong2.c
new file mode 100644
index 0000000000..839f252a08
--- /dev/null
+++ b/nsprpub/pr/tests/pipepong2.c
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: pipepong2.c
+ *
+ * Description:
+ * This test runs in conjunction with the pipeping2 test.
+ * The pipeping2 test creates two pipes and passes two
+ * pipe fd's to this test. Then the pipeping2 test writes
+ * "ping" to this test and this test writes "pong" back.
+ * To run this pair of tests, just invoke pipeping2.
+ *
+ * Tested areas: process creation, pipes, file descriptor
+ * inheritance.
+ */
+
+#include "prerror.h"
+#include "prio.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define NUM_ITERATIONS 10
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *pipe_read, *pipe_write;
+ PRStatus status;
+ char buf[1024];
+ PRInt32 nBytes;
+ int idx;
+
+ pipe_read = PR_GetInheritedFD("PIPE_READ");
+ if (pipe_read == NULL) {
+ fprintf(stderr, "PR_GetInheritedFD failed\n");
+ exit(1);
+ }
+ status = PR_SetFDInheritable(pipe_read, PR_FALSE);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_SetFDInheritable failed\n");
+ exit(1);
+ }
+ pipe_write = PR_GetInheritedFD("PIPE_WRITE");
+ if (pipe_write == NULL) {
+ fprintf(stderr, "PR_GetInheritedFD failed\n");
+ exit(1);
+ }
+ status = PR_SetFDInheritable(pipe_write, PR_FALSE);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_SetFDInheritable failed\n");
+ exit(1);
+ }
+
+ for (idx = 0; idx < NUM_ITERATIONS; idx++) {
+ memset(buf, 0, sizeof(buf));
+ nBytes = PR_Read(pipe_read, buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ printf("pong process: received \"%s\"\n", buf);
+ if (nBytes != 5) {
+ fprintf(stderr, "pong process: expected 5 bytes but got %d bytes\n",
+ nBytes);
+ exit(1);
+ }
+ if (strcmp(buf, "ping") != 0) {
+ fprintf(stderr, "pong process: expected \"ping\" but got \"%s\"\n",
+ buf);
+ exit(1);
+ }
+
+ strcpy(buf, "pong");
+ printf("pong process: sending \"%s\"\n", buf);
+ nBytes = PR_Write(pipe_write, buf, 5);
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Write failed\n");
+ exit(1);
+ }
+ }
+
+ status = PR_Close(pipe_read);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ status = PR_Close(pipe_write);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ return 0;
+}
diff --git a/nsprpub/pr/tests/pipeself.c b/nsprpub/pr/tests/pipeself.c
new file mode 100644
index 0000000000..7066c58d1c
--- /dev/null
+++ b/nsprpub/pr/tests/pipeself.c
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: pipeself.c
+ *
+ * Description:
+ * This test has two threads communicating with each other using
+ * two unidirectional pipes. The primordial thread is the ping
+ * thread and the other thread is the pong thread. The ping
+ * thread writes "ping" to the pong thread and the pong thread
+ * writes "pong" back.
+ */
+
+#include "prio.h"
+#include "prerror.h"
+#include "prthread.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NUM_ITERATIONS 10
+
+static PRFileDesc *ping_in, *ping_out;
+static PRFileDesc *pong_in, *pong_out;
+
+static void PongThreadFunc(void *arg)
+{
+ char buf[1024];
+ int idx;
+ PRInt32 nBytes;
+ PRStatus status;
+
+ for (idx = 0; idx < NUM_ITERATIONS; idx++) {
+ memset(buf, 0, sizeof(buf));
+ nBytes = PR_Read(pong_in, buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read failed\n");
+ exit(1);
+ }
+ printf("pong thread: received \"%s\"\n", buf);
+ if (nBytes != 5) {
+ fprintf(stderr, "pong thread: expected 5 bytes but got %d bytes\n",
+ nBytes);
+ exit(1);
+ }
+ if (strcmp(buf, "ping") != 0) {
+ fprintf(stderr, "pong thread: expected \"ping\" but got \"%s\"\n",
+ buf);
+ exit(1);
+ }
+ strcpy(buf, "pong");
+ printf("pong thread: sending \"%s\"\n", buf);
+ nBytes = PR_Write(pong_out, buf, 5);
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Write failed: (%d, %d)\n", PR_GetError(),
+ PR_GetOSError());
+ exit(1);
+ }
+ }
+
+ status = PR_Close(pong_in);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ status = PR_Close(pong_out);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRStatus status;
+ PRThread *pongThread;
+ char buf[1024];
+ PRInt32 nBytes;
+ int idx;
+
+ status = PR_CreatePipe(&ping_in, &pong_out);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_CreatePipe failed\n");
+ exit(1);
+ }
+ status = PR_CreatePipe(&pong_in, &ping_out);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_CreatePipe failed\n");
+ exit(1);
+ }
+
+ pongThread = PR_CreateThread(PR_USER_THREAD, PongThreadFunc, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (pongThread == NULL) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+
+ for (idx = 0; idx < NUM_ITERATIONS; idx++) {
+ strcpy(buf, "ping");
+ printf("ping thread: sending \"%s\"\n", buf);
+ nBytes = PR_Write(ping_out, buf, 5);
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Write failed: (%d, %d)\n", PR_GetError(),
+ PR_GetOSError());
+ exit(1);
+ }
+ memset(buf, 0, sizeof(buf));
+ nBytes = PR_Read(ping_in, buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read failed\n");
+ exit(1);
+ }
+ printf("ping thread: received \"%s\"\n", buf);
+ if (nBytes != 5) {
+ fprintf(stderr, "ping thread: expected 5 bytes but got %d bytes\n",
+ nBytes);
+ exit(1);
+ }
+ if (strcmp(buf, "pong") != 0) {
+ fprintf(stderr, "ping thread: expected \"pong\" but got \"%s\"\n",
+ buf);
+ exit(1);
+ }
+ }
+
+ status = PR_Close(ping_in);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ status = PR_Close(ping_out);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ status = PR_JoinThread(pongThread);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+
+#if defined(XP_UNIX)
+ /*
+ * Test PR_Available for pipes
+ */
+ status = PR_CreatePipe(&ping_in, &ping_out);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_CreatePipe failed\n");
+ exit(1);
+ }
+ nBytes = PR_Write(ping_out, buf, 250);
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Write failed: (%d, %d)\n", PR_GetError(),
+ PR_GetOSError());
+ exit(1);
+ }
+ nBytes = PR_Available(ping_in);
+ if (nBytes < 0) {
+ fprintf(stderr, "PR_Available failed: (%d, %d)\n", PR_GetError(),
+ PR_GetOSError());
+ exit(1);
+ } else if (nBytes != 250) {
+ fprintf(stderr, "PR_Available: expected 250 bytes but got %d bytes\n",
+ nBytes);
+ exit(1);
+ }
+ printf("PR_Available: expected %d, got %d bytes\n",250, nBytes);
+ /* read some data */
+ nBytes = PR_Read(ping_in, buf, 7);
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read failed\n");
+ exit(1);
+ }
+ /* check available data */
+ nBytes = PR_Available(ping_in);
+ if (nBytes < 0) {
+ fprintf(stderr, "PR_Available failed: (%d, %d)\n", PR_GetError(),
+ PR_GetOSError());
+ exit(1);
+ } else if (nBytes != (250 - 7)) {
+ fprintf(stderr, "PR_Available: expected 243 bytes but got %d bytes\n",
+ nBytes);
+ exit(1);
+ }
+ printf("PR_Available: expected %d, got %d bytes\n",243, nBytes);
+ /* read all data */
+ nBytes = PR_Read(ping_in, buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read failed\n");
+ exit(1);
+ } else if (nBytes != 243) {
+ fprintf(stderr, "PR_Read failed: expected %d, got %d bytes\n",
+ 243, nBytes);
+ exit(1);
+ }
+ /* check available data */
+ nBytes = PR_Available(ping_in);
+ if (nBytes < 0) {
+ fprintf(stderr, "PR_Available failed: (%d, %d)\n", PR_GetError(),
+ PR_GetOSError());
+ exit(1);
+ } else if (nBytes != 0) {
+ fprintf(stderr, "PR_Available: expected 0 bytes but got %d bytes\n",
+ nBytes);
+ exit(1);
+ }
+ printf("PR_Available: expected %d, got %d bytes\n", 0, nBytes);
+
+ status = PR_Close(ping_in);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ status = PR_Close(ping_out);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+#endif /* XP_UNIX */
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/poll_er.c b/nsprpub/pr/tests/poll_er.c
new file mode 100644
index 0000000000..b3a751d83e
--- /dev/null
+++ b/nsprpub/pr/tests/poll_er.c
@@ -0,0 +1,210 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: prpoll_err.c
+**
+** Description: This program tests PR_Poll with sockets.
+** error reporting operation is tested
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "primpl.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+static void
+ClientThreadFunc(void *arg)
+{
+ PRFileDesc *badFD = (PRFileDesc *) arg;
+ /*
+ * Make the fd invalid
+ */
+#if defined(XP_UNIX)
+ close(PR_FileDesc2NativeHandle(badFD));
+#elif defined(XP_OS2)
+ soclose(PR_FileDesc2NativeHandle(badFD));
+#elif defined(WIN32) || defined(WIN16)
+ closesocket(PR_FileDesc2NativeHandle(badFD));
+#else
+#error "Unknown architecture"
+#endif
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1, *listenSock2;
+ PRFileDesc *badFD;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ char buf[128];
+ PRPollDesc pds0[10], pds1[10], *pds, *other_pds;
+ PRIntn npds;
+ PRInt32 retVal;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("This program tests PR_Poll with sockets.\n");
+ printf("error reporting is tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ if (debug_mode) {
+ printf("%s", buf);
+ }
+
+ /* Set up the poll descriptor array */
+ pds = pds0;
+ other_pds = pds1;
+ memset(pds, 0, sizeof(pds));
+ pds[0].fd = listenSock1;
+ pds[0].in_flags = PR_POLL_READ;
+ pds[1].fd = listenSock2;
+ pds[1].in_flags = PR_POLL_READ;
+ npds = 2;
+
+
+ /* Testing bad fd */
+ if (debug_mode) {
+ printf("PR_Poll should detect a bad file descriptor\n");
+ }
+ if ((badFD = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a TCP socket\n");
+ goto exit_now;
+ }
+
+ pds[2].fd = badFD;
+ pds[2].in_flags = PR_POLL_READ;
+ npds = 3;
+
+ if (PR_CreateThread(PR_USER_THREAD, ClientThreadFunc,
+ badFD, PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0) == NULL) {
+ fprintf(stderr, "cannot create thread\n");
+ exit(1);
+ }
+
+ retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT);
+ if (retVal != 1 || (unsigned short) pds[2].out_flags != PR_POLL_NVAL) {
+ fprintf(stderr, "Failed to detect the bad fd: "
+ "PR_Poll returns %d, out_flags is 0x%hx\n",
+ retVal, pds[2].out_flags);
+ failed_already=1;
+ goto exit_now;
+ }
+ if (debug_mode) {
+ printf("PR_Poll detected the bad fd. Test passed.\n\n");
+ }
+ PR_Cleanup();
+ goto exit_now;
+exit_now:
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
diff --git a/nsprpub/pr/tests/poll_nm.c b/nsprpub/pr/tests/poll_nm.c
new file mode 100644
index 0000000000..f356d141ac
--- /dev/null
+++ b/nsprpub/pr/tests/poll_nm.c
@@ -0,0 +1,355 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: prpoll_norm.c
+**
+** Description: This program tests PR_Poll with sockets.
+** Normal operation are tested
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prio.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prnetdb.h"
+#include "obsolete/probslet.h"
+
+#include "private/pprio.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+#define NUM_ITERATIONS 5
+
+static void PR_CALLBACK
+clientThreadFunc(void *arg)
+{
+ PRUintn port = (PRUintn) arg;
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ char buf[128];
+ int i;
+ PRStatus sts;
+ PRInt32 n;
+
+ addr.inet.family = PR_AF_INET;
+ addr.inet.port = PR_htons((PRUint16)port);
+ addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+ memset(buf, 0, sizeof(buf));
+ PR_snprintf(buf, sizeof(buf), "%hu", port);
+
+ for (i = 0; i < NUM_ITERATIONS; i++) {
+ sock = PR_NewTCPSocket();
+ PR_ASSERT(sock != NULL);
+
+ sts = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(sts == PR_SUCCESS);
+
+ n = PR_Write(sock, buf, sizeof(buf));
+ PR_ASSERT(n >= 0);
+
+ sts = PR_Close(sock);
+ PR_ASSERT(sts == PR_SUCCESS);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1 = NULL, *listenSock2 = NULL;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ char buf[128];
+ PRThread *clientThread;
+ PRPollDesc pds0[20], pds1[20], *pds, *other_pds;
+ PRIntn npds;
+ PRInt32 retVal;
+ PRIntn i, j;
+ PRSocketOptionData optval;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("This program tests PR_Poll with sockets.\n");
+ printf("Normal operation are tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ memset(&addr, 0, sizeof(addr));
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ optval.option = PR_SockOpt_Nonblocking;
+ optval.value.non_blocking = PR_TRUE;
+ PR_SetSocketOption(listenSock1, &optval);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ PR_SetSocketOption(listenSock2, &optval);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ if (debug_mode) {
+ printf("%s", buf);
+ }
+
+ /* Set up the poll descriptor array */
+ pds = pds0;
+ other_pds = pds1;
+ memset(pds, 0, sizeof(pds));
+ pds[0].fd = listenSock1;
+ pds[0].in_flags = PR_POLL_READ;
+ pds[1].fd = listenSock2;
+ pds[1].in_flags = PR_POLL_READ;
+ /* Add some unused entries to test if they are ignored by PR_Poll() */
+ memset(&pds[2], 0, sizeof(pds[2]));
+ memset(&pds[3], 0, sizeof(pds[3]));
+ memset(&pds[4], 0, sizeof(pds[4]));
+ npds = 5;
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort1,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort2,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if (debug_mode) {
+ printf("Two client threads are created. Each of them will\n");
+ printf("send data to one of the two ports the server is listening on.\n");
+ printf("The data they send is the port number. Each of them send\n");
+ printf("the data five times, so you should see ten lines below,\n");
+ printf("interleaved in an arbitrary order.\n");
+ }
+
+ /* two clients, three events per iteration: accept, read, close */
+ i = 0;
+ while (i < 2 * 3 * NUM_ITERATIONS) {
+ PRPollDesc *tmp;
+ int nextIndex;
+ int nEvents = 0;
+
+ retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(retVal != 0); /* no timeout */
+ if (retVal == -1) {
+ fprintf(stderr, "PR_Poll failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ nextIndex = 2;
+ /* the two listening sockets */
+ for (j = 0; j < 2; j++) {
+ other_pds[j] = pds[j];
+ PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0
+ && (pds[j].out_flags & PR_POLL_EXCEPT) == 0);
+ if (pds[j].out_flags & PR_POLL_READ) {
+ PRFileDesc *sock;
+
+ nEvents++;
+ sock = PR_Accept(pds[j].fd, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (sock == NULL) {
+ fprintf(stderr, "PR_Accept() failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ other_pds[nextIndex].fd = sock;
+ other_pds[nextIndex].in_flags = PR_POLL_READ;
+ nextIndex++;
+ } else if (pds[j].out_flags & PR_POLL_ERR) {
+ fprintf(stderr, "PR_Poll() indicates that an fd has error\n");
+ failed_already=1;
+ goto exit_now;
+ } else if (pds[j].out_flags & PR_POLL_NVAL) {
+ fprintf(stderr, "PR_Poll() indicates that fd %d is invalid\n",
+ PR_FileDesc2NativeHandle(pds[j].fd));
+ failed_already=1;
+ goto exit_now;
+ }
+ }
+
+ for (j = 2; j < npds; j++) {
+ if (NULL == pds[j].fd) {
+ /*
+ * Keep the unused entries in the poll descriptor array
+ * for testing purposes.
+ */
+ other_pds[nextIndex] = pds[j];
+ nextIndex++;
+ continue;
+ }
+
+ PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0
+ && (pds[j].out_flags & PR_POLL_EXCEPT) == 0);
+ if (pds[j].out_flags & PR_POLL_READ) {
+ PRInt32 nAvail;
+ PRInt32 nRead;
+
+ nEvents++;
+ nAvail = PR_Available(pds[j].fd);
+ nRead = PR_Read(pds[j].fd, buf, sizeof(buf));
+ PR_ASSERT(nAvail == nRead);
+ if (nRead == -1) {
+ fprintf(stderr, "PR_Read() failed\n");
+ failed_already=1;
+ goto exit_now;
+ } else if (nRead == 0) {
+ PR_Close(pds[j].fd);
+ continue;
+ } else {
+ /* Just to be safe */
+ buf[127] = '\0';
+ if (debug_mode) {
+ printf("The server received \"%s\" from a client\n", buf);
+ }
+ }
+ } else if (pds[j].out_flags & PR_POLL_ERR) {
+ fprintf(stderr, "PR_Poll() indicates that an fd has error\n");
+ failed_already=1;
+ goto exit_now;
+ } else if (pds[j].out_flags & PR_POLL_NVAL) {
+ fprintf(stderr, "PR_Poll() indicates that an fd is invalid\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ other_pds[nextIndex] = pds[j];
+ nextIndex++;
+ }
+
+ PR_ASSERT(retVal == nEvents);
+ /* swap */
+ tmp = pds;
+ pds = other_pds;
+ other_pds = tmp;
+ npds = nextIndex;
+ i += nEvents;
+ }
+
+ if (debug_mode) {
+ printf("Tests passed\n");
+ }
+
+exit_now:
+
+ if (listenSock1) {
+ PR_Close(listenSock1);
+ }
+ if (listenSock2) {
+ PR_Close(listenSock2);
+ }
+
+ PR_Cleanup();
+
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+}
diff --git a/nsprpub/pr/tests/poll_to.c b/nsprpub/pr/tests/poll_to.c
new file mode 100644
index 0000000000..91291bcb11
--- /dev/null
+++ b/nsprpub/pr/tests/poll_to.c
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: prpoll_to.c
+**
+** Description: This program tests PR_Poll with sockets.
+** Timeout operation is tested
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prio.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prnetdb.h"
+
+#include "private/pprio.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1 = NULL, *listenSock2 = NULL;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ char buf[128];
+ PRPollDesc pds0[10], pds1[10], *pds, *other_pds;
+ PRIntn npds;
+ PRInt32 retVal;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("This program tests PR_Poll with sockets.\n");
+ printf("Timeout is tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ memset(&addr, 0, sizeof(addr));
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ if (debug_mode) {
+ printf("%s", buf);
+ }
+
+ /* Set up the poll descriptor array */
+ pds = pds0;
+ other_pds = pds1;
+ memset(pds, 0, sizeof(pds));
+ pds[0].fd = listenSock1;
+ pds[0].in_flags = PR_POLL_READ;
+ pds[1].fd = listenSock2;
+ pds[1].in_flags = PR_POLL_READ;
+ npds = 2;
+
+ /* Testing timeout */
+ if (debug_mode) {
+ printf("PR_Poll should time out in 5 seconds\n");
+ }
+ retVal = PR_Poll(pds, npds, PR_SecondsToInterval(5));
+ if (retVal != 0) {
+ PR_snprintf(buf, sizeof(buf),
+ "PR_Poll should time out and return 0, but it returns %ld\n",
+ retVal);
+ fprintf(stderr, "%s", buf);
+ if (!debug_mode) {
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ if (debug_mode) {
+ printf("PR_Poll timed out. Test passed.\n\n");
+ }
+
+exit_now:
+
+ if (listenSock1) {
+ PR_Close(listenSock1);
+ }
+ if (listenSock2) {
+ PR_Close(listenSock2);
+ }
+
+ PR_Cleanup();
+
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+}
diff --git a/nsprpub/pr/tests/pollable.c b/nsprpub/pr/tests/pollable.c
new file mode 100644
index 0000000000..7d0b51cac3
--- /dev/null
+++ b/nsprpub/pr/tests/pollable.c
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * A test for the pollable events.
+ *
+ * A number of threads are in a ring configuration, each waiting on
+ * a pollable event that is set by its upstream neighbor.
+ */
+
+#include "prinit.h"
+#include "prio.h"
+#include "prthread.h"
+#include "prerror.h"
+#include "prmem.h"
+#include "prlog.h"
+#include "prprf.h"
+
+#include "plgetopt.h"
+
+#include <stdlib.h>
+
+#define DEFAULT_THREADS 10
+#define DEFAULT_LOOPS 100
+
+PRIntn numThreads = DEFAULT_THREADS;
+PRIntn numIterations = DEFAULT_LOOPS;
+PRIntervalTime dally = PR_INTERVAL_NO_WAIT;
+PRFileDesc *debug_out = NULL;
+PRBool debug_mode = PR_FALSE;
+PRBool verbosity = PR_FALSE;
+
+typedef struct ThreadData {
+ PRFileDesc *event;
+ int index;
+ struct ThreadData *next;
+} ThreadData;
+
+void ThreadRoutine(void *arg)
+{
+ ThreadData *data = (ThreadData *) arg;
+ PRIntn i;
+ PRPollDesc pd;
+ PRInt32 rv;
+
+ pd.fd = data->event;
+ pd.in_flags = PR_POLL_READ;
+
+ for (i = 0; i < numIterations; i++) {
+ rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (rv == -1) {
+ PR_fprintf(PR_STDERR, "PR_Poll failed\n");
+ exit(1);
+ }
+ if (verbosity) {
+ PR_fprintf(debug_out, "thread %d awakened\n", data->index);
+ }
+ PR_ASSERT(rv != 0);
+ PR_ASSERT(pd.out_flags & PR_POLL_READ);
+ if (PR_WaitForPollableEvent(data->event) == PR_FAILURE) {
+ PR_fprintf(PR_STDERR, "consume event failed\n");
+ exit(1);
+ }
+ if (dally != PR_INTERVAL_NO_WAIT) {
+ PR_Sleep(dally);
+ }
+ if (verbosity) {
+ PR_fprintf(debug_out, "thread %d posting event\n", data->index);
+ }
+ if (PR_SetPollableEvent(data->next->event) == PR_FAILURE) {
+ PR_fprintf(PR_STDERR, "post event failed\n");
+ exit(1);
+ }
+ }
+}
+
+static void Help(void)
+{
+ debug_out = PR_STDOUT;
+
+ PR_fprintf(
+ debug_out, "Usage: pollable [-c n] [-t n] [-d] [-v] [-G] [-C n] [-D n]\n");
+ PR_fprintf(
+ debug_out, "-c n\tloops at thread level (default: %d)\n", DEFAULT_LOOPS);
+ PR_fprintf(
+ debug_out, "-t n\tnumber of threads (default: %d)\n", DEFAULT_THREADS);
+ PR_fprintf(debug_out, "-d\tturn on debugging output (default: FALSE)\n");
+ PR_fprintf(debug_out, "-v\tturn on verbose output (default: FALSE)\n");
+ PR_fprintf(debug_out, "-G\tglobal threads only (default: FALSE)\n");
+ PR_fprintf(debug_out, "-C n\tconcurrency setting (default: 1)\n");
+ PR_fprintf(debug_out, "-D n\tdally setting (msecs) (default: 0)\n");
+} /* Help */
+
+int main(int argc, char **argv)
+{
+ ThreadData selfData;
+ ThreadData *data;
+ PRThread **thread;
+ void *block;
+ PRIntn i;
+ PRIntervalTime timeStart, timeEnd;
+ PRPollDesc pd;
+ PRInt32 rv;
+ PRThreadScope thread_scope = PR_LOCAL_THREAD;
+ PRBool help = PR_FALSE;
+ PRUintn concurrency = 1;
+ PRUintn average;
+ PLOptStatus os;
+ PLOptState *opt;
+
+ PR_STDIO_INIT();
+
+ opt = PL_CreateOptState(argc, argv, "hdvc:t:C:GD:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option) {
+ case 'v': /* verbose mode */
+ verbosity = PR_TRUE;
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'c': /* loop counter */
+ numIterations = atoi(opt->value);
+ break;
+ case 't': /* thread limit */
+ numThreads = atoi(opt->value);
+ break;
+ case 'C': /* Concurrency limit */
+ concurrency = atoi(opt->value);
+ break;
+ case 'G': /* global threads only */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'D': /* dally */
+ dally = PR_MillisecondsToInterval(atoi(opt->value));
+ break;
+ case 'h': /* help message */
+ Help();
+ help = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (help) {
+ return 1;
+ }
+
+ if (concurrency > 1) {
+ PR_SetConcurrency(concurrency);
+ }
+
+ if (PR_TRUE == debug_mode) {
+ debug_out = PR_STDOUT;
+ PR_fprintf(debug_out, "Test parameters\n");
+ PR_fprintf(debug_out, "\tThreads involved: %d\n", numThreads);
+ PR_fprintf(debug_out, "\tIteration limit: %d\n", numIterations);
+ PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency);
+ PR_fprintf(debug_out, "\tThread type: %s\n",
+ (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL");
+ }
+
+ /*
+ * Malloc a block of memory and divide it into data and thread.
+ */
+ block = PR_MALLOC(numThreads * (sizeof(ThreadData) + sizeof(PRThread *)));
+ if (block == NULL) {
+ PR_fprintf(PR_STDERR, "cannot malloc, failed\n");
+ exit(1);
+ }
+ data = (ThreadData *) block;
+ thread = (PRThread **) &data[numThreads];
+
+ /* Pollable event */
+ selfData.event = PR_NewPollableEvent();
+ if (selfData.event == NULL) {
+ PR_fprintf(PR_STDERR, "cannot create event: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ selfData.next = &data[0];
+ for (i = 0; i < numThreads; i++) {
+ data[i].event = PR_NewPollableEvent();
+ if (data[i].event == NULL) {
+ PR_fprintf(PR_STDERR, "cannot create event: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ data[i].index = i;
+ if (i != numThreads - 1) {
+ data[i].next = &data[i + 1];
+ } else {
+ data[i].next = &selfData;
+ }
+
+ thread[i] = PR_CreateThread(PR_USER_THREAD,
+ ThreadRoutine, &data[i], PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ if (thread[i] == NULL) {
+ PR_fprintf(PR_STDERR, "cannot create thread\n");
+ exit(1);
+ }
+ }
+
+ timeStart = PR_IntervalNow();
+ pd.fd = selfData.event;
+ pd.in_flags = PR_POLL_READ;
+ for (i = 0; i < numIterations; i++) {
+ if (dally != PR_INTERVAL_NO_WAIT) {
+ PR_Sleep(dally);
+ }
+ if (verbosity) {
+ PR_fprintf(debug_out, "main thread posting event\n");
+ }
+ if (PR_SetPollableEvent(selfData.next->event) == PR_FAILURE) {
+ PR_fprintf(PR_STDERR, "set event failed\n");
+ exit(1);
+ }
+ rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (rv == -1) {
+ PR_fprintf(PR_STDERR, "wait failed\n");
+ exit(1);
+ }
+ PR_ASSERT(rv != 0);
+ PR_ASSERT(pd.out_flags & PR_POLL_READ);
+ if (verbosity) {
+ PR_fprintf(debug_out, "main thread awakened\n");
+ }
+ if (PR_WaitForPollableEvent(selfData.event) == PR_FAILURE) {
+ PR_fprintf(PR_STDERR, "consume event failed\n");
+ exit(1);
+ }
+ }
+ timeEnd = PR_IntervalNow();
+
+ if (debug_mode) {
+ average = PR_IntervalToMicroseconds(timeEnd - timeStart)
+ / (numIterations * numThreads);
+ PR_fprintf(debug_out, "Average switch times %d usecs for %d threads\n",
+ average, numThreads);
+ }
+
+ for (i = 0; i < numThreads; i++) {
+ if (PR_JoinThread(thread[i]) == PR_FAILURE) {
+ PR_fprintf(PR_STDERR, "join thread failed\n");
+ exit(1);
+ }
+ PR_DestroyPollableEvent(data[i].event);
+ }
+ PR_DELETE(block);
+ PR_DestroyPollableEvent(selfData.event);
+
+ PR_fprintf(PR_STDOUT, "PASSED\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/prfdbl.c b/nsprpub/pr/tests/prfdbl.c
new file mode 100644
index 0000000000..3bb0650b2e
--- /dev/null
+++ b/nsprpub/pr/tests/prfdbl.c
@@ -0,0 +1,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This is a simple test of the PR_fprintf() function for doubles.
+ */
+
+#include "prprf.h"
+
+int main()
+{
+ double pi = 3.1415926;
+ double e = 2.71828;
+ double root2 = 1.414;
+ double zero = 0.0;
+ double nan = zero / zero;
+
+ PR_fprintf(PR_STDOUT, "pi is %f.\n", pi);
+ PR_fprintf(PR_STDOUT, "e is %f.\n", e);
+ PR_fprintf(PR_STDOUT, "The square root of 2 is %f.\n", root2);
+ PR_fprintf(PR_STDOUT, "NaN is %f.\n", nan);
+
+ PR_fprintf(PR_STDOUT, "pi is %301f.\n", pi);
+ PR_fprintf(PR_STDOUT, "e is %65416.123f.\n", e);
+ PR_fprintf(PR_STDOUT, "e is %0000000000000000000065416.123f.\n", e);
+ PR_fprintf(PR_STDOUT, "NaN is %1024.1f.\n", nan);
+ return 0;
+}
diff --git a/nsprpub/pr/tests/prftest.c b/nsprpub/pr/tests/prftest.c
new file mode 100644
index 0000000000..091c990a79
--- /dev/null
+++ b/nsprpub/pr/tests/prftest.c
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: prftest.c
+ * Description:
+ * This is a simple test of the PR_snprintf() function defined
+ * in prprf.c.
+ */
+
+#include "prlong.h"
+#include "prprf.h"
+
+#include <string.h>
+
+#define BUF_SIZE 128
+
+int main(int argc, char **argv)
+{
+ PRInt16 i16;
+ PRIntn n;
+ PRInt32 i32;
+ PRInt64 i64;
+ char buf[BUF_SIZE];
+ char answer[BUF_SIZE];
+ int i, rv = 0;
+
+ i16 = -1;
+ n = -1;
+ i32 = -1;
+ LL_I2L(i64, i32);
+
+ PR_snprintf(buf, BUF_SIZE, "%hx %x %lx %llx", i16, n, i32, i64);
+ strcpy(answer, "ffff ");
+ for (i = PR_BYTES_PER_INT * 2; i; i--) {
+ strcat(answer, "f");
+ }
+ strcat(answer, " ffffffff ffffffffffffffff");
+
+ if (!strcmp(buf, answer)) {
+ printf("PR_snprintf test 1 passed\n");
+ } else {
+ printf("PR_snprintf test 1 failed\n");
+ printf("Converted string is %s\n", buf);
+ printf("Should be %s\n", answer);
+ rv = 1;
+ }
+
+ i16 = -32;
+ n = 30;
+ i32 = 64;
+ LL_I2L(i64, 333);
+ PR_snprintf(buf, BUF_SIZE, "%d %hd %lld %ld", n, i16, i64, i32);
+ if (!strcmp(buf, "30 -32 333 64")) {
+ printf("PR_snprintf test 2 passed\n");
+ } else {
+ printf("PR_snprintf test 2 failed\n");
+ printf("Converted string is %s\n", buf);
+ printf("Should be 30 -32 333 64\n");
+ rv = 1;
+ }
+
+ return rv;
+}
diff --git a/nsprpub/pr/tests/prftest1.c b/nsprpub/pr/tests/prftest1.c
new file mode 100644
index 0000000000..5d6374a455
--- /dev/null
+++ b/nsprpub/pr/tests/prftest1.c
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: prftest1.c
+** Description:
+** This is a simple test of the PR_snprintf() function defined
+** in prprf.c.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+#include "prttools.h"
+
+#include "prinit.h"
+#include "prlong.h"
+#include "prprf.h"
+
+#include <string.h>
+
+#define BUF_SIZE 128
+
+/***********************************************************************
+** PRIVATE FUNCTION: Test_Result
+** DESCRIPTION: Used in conjunction with the regress tool, prints out the
+** status of the test case.
+** INPUTS: PASS/FAIL
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+**
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM: Determine what the status is and print accordingly.
+**
+***********************************************************************/
+
+
+static void Test_Result (int result)
+{
+ if (result == PASS) {
+ printf ("PASS\n");
+ }
+ else {
+ printf ("FAIL\n");
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRInt16 i16;
+ PRIntn n;
+ PRInt32 i32;
+ PRInt64 i64;
+ char buf[BUF_SIZE];
+ char answer[BUF_SIZE];
+ int i;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+ PR_STDIO_INIT();
+
+ i16 = -1;
+ n = -1;
+ i32 = -1;
+ LL_I2L(i64, i32);
+
+ PR_snprintf(buf, BUF_SIZE, "%hx %x %lx %llx", i16, n, i32, i64);
+ strcpy(answer, "ffff ");
+ for (i = PR_BYTES_PER_INT * 2; i; i--) {
+ strcat(answer, "f");
+ }
+ strcat(answer, " ffffffff ffffffffffffffff");
+
+ if (!strcmp(buf, answer)) {
+ if (debug_mode) {
+ printf("PR_snprintf test 1 passed\n");
+ }
+ else {
+ Test_Result (PASS);
+ }
+ } else {
+ if (debug_mode) {
+ printf("PR_snprintf test 1 failed\n");
+ printf("Converted string is %s\n", buf);
+ printf("Should be %s\n", answer);
+ }
+ else {
+ Test_Result (FAIL);
+ }
+ }
+
+ return 0;
+}
diff --git a/nsprpub/pr/tests/prftest2.c b/nsprpub/pr/tests/prftest2.c
new file mode 100644
index 0000000000..6376fd2ffd
--- /dev/null
+++ b/nsprpub/pr/tests/prftest2.c
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: prftest2.c
+** Description:
+** This is a simple test of the PR_snprintf() function defined
+** in prprf.c.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prlong.h"
+#include "prinit.h"
+#include "prprf.h"
+
+#include <string.h>
+
+#define BUF_SIZE 128
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+int main(int argc, char **argv)
+{
+ PRInt16 i16;
+ PRIntn n;
+ PRInt32 i32;
+ PRInt64 i64;
+ char buf[BUF_SIZE];
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+
+ PR_STDIO_INIT();
+ i16 = -32;
+ n = 30;
+ i32 = 64;
+ LL_I2L(i64, 333);
+ PR_snprintf(buf, BUF_SIZE, "%d %hd %lld %ld", n, i16, i64, i32);
+ if (!strcmp(buf, "30 -32 333 64")) {
+ if (debug_mode) {
+ printf("PR_snprintf test 2 passed\n");
+ }
+ } else {
+ if (debug_mode) {
+ printf("PR_snprintf test 2 failed\n");
+ printf("Converted string is %s\n", buf);
+ printf("Should be 30 -32 333 64\n");
+ }
+ else {
+ failed_already=1;
+ }
+ }
+ if(failed_already)
+ {
+ printf("FAILED\n");
+ return 1;
+ }
+ else
+ {
+ printf("PASSED\n");
+ return 0;
+ }
+}
diff --git a/nsprpub/pr/tests/prfz.c b/nsprpub/pr/tests/prfz.c
new file mode 100644
index 0000000000..7179dbecdb
--- /dev/null
+++ b/nsprpub/pr/tests/prfz.c
@@ -0,0 +1,85 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This is a simple test of the PR_fprintf() function for size_t formats.
+ */
+
+#include "prprf.h"
+#include <sys/types.h>
+#include <limits.h>
+#include <string.h>
+#include <stdint.h>
+
+int
+main(int argc, char **argv)
+{
+ char buffer[128];
+
+ size_t unsigned_small = 266;
+#ifdef XP_UNIX
+ ssize_t signed_small_p = 943;
+ ssize_t signed_small_n = -1;
+#endif
+ size_t unsigned_max = SIZE_MAX;
+ size_t unsigned_min = 0;
+#ifdef XP_UNIX
+ ssize_t signed_max = SSIZE_MAX;
+#endif
+
+ printf("Test: unsigned small '%%zu' : ");
+ PR_snprintf(buffer, sizeof(buffer), "%zu", unsigned_small);
+ if (strncmp(buffer, "266", sizeof(buffer)) != 0) {
+ printf("Failed, got '%s'\n", buffer);
+ return -1;
+ }
+ printf("OK\n");
+
+#ifdef XP_UNIX
+ printf("Test: signed small positive '%%zd' : ");
+ PR_snprintf(buffer, sizeof(buffer), "%zd", signed_small_p);
+ if (strncmp(buffer, "943", sizeof(buffer)) != 0) {
+ printf("Failed, got '%s'\n", buffer);
+ return -1;
+ }
+ printf("OK\n");
+
+ printf("Test: signed small negative '%%zd' : ");
+ PR_snprintf(buffer, sizeof(buffer), "%zd", signed_small_n);
+ if (strncmp(buffer, "-1", sizeof(buffer)) != 0) {
+ printf("Failed, got '%s'\n", buffer);
+ return -1;
+ }
+ printf("OK\n");
+#endif
+
+ printf("Test: 0 '%%zu' : ");
+ PR_snprintf(buffer, sizeof(buffer), "%zu", unsigned_min);
+ if (strncmp(buffer, "0", sizeof(buffer)) != 0) {
+ printf("Failed, got '%s'\n", buffer);
+ return -1;
+ }
+ printf("OK\n");
+
+ printf("Test: SIZE_MAX '%%zx' : ");
+ PR_snprintf(buffer, sizeof(buffer), "%zx", unsigned_max);
+ if (strspn(buffer, "f") != sizeof(size_t) * 2) {
+ printf("Failed, got '%s'\n", buffer);
+ return -1;
+ }
+ printf("OK\n");
+
+#ifdef XP_UNIX
+ printf("Test: SSIZE_MAX '%%zx' : ");
+ PR_snprintf(buffer, sizeof(buffer), "%zx", signed_max);
+ if (*buffer != '7' ||
+ strspn(buffer + 1, "f") != sizeof(ssize_t) * 2 - 1) {
+ printf("Failed, got '%s'\n", buffer);
+ return -1;
+ }
+ printf("OK\n");
+#endif
+
+ return 0;
+}
diff --git a/nsprpub/pr/tests/primblok.c b/nsprpub/pr/tests/primblok.c
new file mode 100644
index 0000000000..1182d3ed73
--- /dev/null
+++ b/nsprpub/pr/tests/primblok.c
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: primblok.c
+ * Purpose: testing whether the primordial thread can block in a
+ * native blocking function without affecting the correct
+ * functioning of NSPR I/O functions (Bugzilla bug #30746)
+ */
+
+#if !defined(WINNT)
+
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ printf("This test is not relevant on this platform\n");
+ return 0;
+}
+
+#else /* WINNT */
+
+#include "nspr.h"
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define TEST_FILE_NAME "primblok.dat"
+
+/* use InterlockedExchange to update this variable */
+static LONG iothread_done;
+
+static void PR_CALLBACK IOThread(void *arg)
+{
+ PRFileDesc *fd;
+ char buf[32];
+ PRInt32 nbytes;
+
+ /* Give the primordial thread one second to block */
+ Sleep(1000);
+
+ /*
+ * See if our PR_Write call will hang when the primordial
+ * thread is blocking in a native blocking function.
+ */
+ fd = PR_Open(TEST_FILE_NAME, PR_WRONLY|PR_CREATE_FILE, 0666);
+ if (NULL == fd) {
+ fprintf(stderr, "PR_Open failed\n");
+ exit(1);
+ }
+ memset(buf, 0xaf, sizeof(buf));
+ fprintf(stderr, "iothread: calling PR_Write\n");
+ nbytes = PR_Write(fd, buf, sizeof(buf));
+ fprintf(stderr, "iothread: PR_Write returned\n");
+ if (nbytes != sizeof(buf)) {
+ fprintf(stderr, "PR_Write returned %d\n", nbytes);
+ exit(1);
+ }
+ if (PR_Close(fd) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ if (PR_Delete(TEST_FILE_NAME) == PR_FAILURE) {
+ fprintf(stderr, "PR_Delete failed\n");
+ exit(1);
+ }
+
+ /* Tell the main thread that we are done */
+ InterlockedExchange(&iothread_done, 1);
+}
+
+int main(int argc, char **argv)
+{
+ PRThread *iothread;
+
+ /* Must be a global thread */
+ iothread = PR_CreateThread(
+ PR_USER_THREAD, IOThread, NULL, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (iothread == NULL) {
+ fprintf(stderr, "cannot create thread\n");
+ exit(1);
+ }
+
+ /*
+ * Block in a native blocking function.
+ * Give iothread 5 seconds to finish its task.
+ */
+ Sleep(5000);
+
+ /*
+ * Is iothread done or is it hung?
+ *
+ * I'm actually only interested in reading the value
+ * of iothread_done. I'm using InterlockedExchange as
+ * a thread-safe way to read iothread_done.
+ */
+ if (InterlockedExchange(&iothread_done, 1) == 0) {
+ fprintf(stderr, "iothread is hung\n");
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ if (PR_JoinThread(iothread) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+ printf("PASSED\n");
+ return 0;
+} /* main */
+
+#endif /* WINNT */
diff --git a/nsprpub/pr/tests/priotest.c b/nsprpub/pr/tests/priotest.c
new file mode 100644
index 0000000000..1e2249de63
--- /dev/null
+++ b/nsprpub/pr/tests/priotest.c
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: priotest.c
+ * Purpose: testing priorities
+ */
+
+#include "prcmon.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prmon.h"
+#include "prprf.h"
+#include "prthread.h"
+#include "prtypes.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define DEFAULT_DURATION 5
+
+static PRBool failed = PR_FALSE;
+static PRIntervalTime oneSecond;
+static PRFileDesc *debug_out = NULL;
+static PRBool debug_mode = PR_FALSE;
+
+static PRUint32 PerSecond(PRIntervalTime timein)
+{
+ PRUint32 loop = 0;
+ while (((PRIntervalTime)(PR_IntervalNow()) - timein) < oneSecond) {
+ loop += 1;
+ }
+ return loop;
+} /* PerSecond */
+
+static void PR_CALLBACK Low(void *arg)
+{
+ PRUint32 t3 = 0, t2 = 0, t1 = 0, t0, *tn = (PRUint32*)arg;
+ while (1)
+ {
+ t0 = PerSecond(PR_IntervalNow());
+ *tn = (t3 + 3 * t2 + 3 * t1 + t0) / 8;
+ t3 = t2; t2 = t1; t1 = t0;
+ }
+} /* Low */
+
+static void PR_CALLBACK High(void *arg)
+{
+ PRUint32 t3 = 0, t2 = 0, t1 = 0, t0, *tn = (PRUint32*)arg;
+ while (1)
+ {
+ PRIntervalTime timein = PR_IntervalNow();
+ PR_Sleep(oneSecond >> 2); /* 0.25 seconds */
+ t0 = PerSecond(timein);
+ *tn = (t3 + 3 * t2 + 3 * t1 + t0) / 8;
+ t3 = t2; t2 = t1; t1 = t0;
+ }
+} /* High */
+
+static void Help(void)
+{
+ PR_fprintf(
+ debug_out, "Usage: priotest [-d] [-c n]\n");
+ PR_fprintf(
+ debug_out, "-c n\tduration of test in seconds (default: %d)\n", DEFAULT_DURATION);
+ PR_fprintf(
+ debug_out, "-d\tturn on debugging output (default: FALSE)\n");
+} /* Help */
+
+static void RudimentaryTests(void)
+{
+ /*
+ ** Try some rudimentary tests like setting valid priority and
+ ** getting it back, or setting invalid priorities and getting
+ ** back a valid answer.
+ */
+ PRThreadPriority priority;
+ PR_SetThreadPriority(PR_GetCurrentThread(), PR_PRIORITY_URGENT);
+ priority = PR_GetThreadPriority(PR_GetCurrentThread());
+ failed = ((PR_TRUE == failed) || (PR_PRIORITY_URGENT != priority))
+ ? PR_TRUE : PR_FALSE;
+ if (debug_mode && (PR_PRIORITY_URGENT != priority))
+ {
+ PR_fprintf(debug_out, "PR_[S/G]etThreadPriority() failed\n");
+ }
+
+
+ PR_SetThreadPriority(
+ PR_GetCurrentThread(), (PRThreadPriority)(PR_PRIORITY_FIRST - 1));
+ priority = PR_GetThreadPriority(PR_GetCurrentThread());
+ failed = ((PR_TRUE == failed) || (PR_PRIORITY_FIRST != priority))
+ ? PR_TRUE : PR_FALSE;
+ if (debug_mode && (PR_PRIORITY_FIRST != priority))
+ {
+ PR_fprintf(debug_out, "PR_SetThreadPriority(-1) failed\n");
+ }
+
+ PR_SetThreadPriority(
+ PR_GetCurrentThread(), (PRThreadPriority)(PR_PRIORITY_LAST + 1));
+ priority = PR_GetThreadPriority(PR_GetCurrentThread());
+ failed = ((PR_TRUE == failed) || (PR_PRIORITY_LAST != priority))
+ ? PR_TRUE : PR_FALSE;
+ if (debug_mode && (PR_PRIORITY_LAST != priority))
+ {
+ PR_fprintf(debug_out, "PR_SetThreadPriority(+1) failed\n");
+ }
+
+} /* RudimentataryTests */
+
+static void CreateThreads(PRUint32 *lowCount, PRUint32 *highCount)
+{
+ (void)PR_CreateThread(
+ PR_USER_THREAD, Low, lowCount, PR_PRIORITY_LOW,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ (void)PR_CreateThread(
+ PR_USER_THREAD, High, highCount, PR_PRIORITY_HIGH,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+} /* CreateThreads */
+
+int main(int argc, char **argv)
+{
+ PLOptStatus os;
+ PRIntn duration = DEFAULT_DURATION;
+ PRUint32 totalCount, highCount = 0, lowCount = 0;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hdc:");
+
+ debug_out = PR_STDOUT;
+ oneSecond = PR_SecondsToInterval(1);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'c': /* test duration */
+ duration = atoi(opt->value);
+ break;
+ case 'h': /* help message */
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+ PR_STDIO_INIT();
+
+ if (duration == 0) {
+ duration = DEFAULT_DURATION;
+ }
+
+ RudimentaryTests();
+
+ printf("Priority test: running for %d seconds\n\n", duration);
+
+ (void)PerSecond(PR_IntervalNow());
+ totalCount = PerSecond(PR_IntervalNow());
+
+ PR_SetThreadPriority(PR_GetCurrentThread(), PR_PRIORITY_URGENT);
+
+ if (debug_mode)
+ {
+ PR_fprintf(debug_out,
+ "The high priority thread should get approximately three\n");
+ PR_fprintf( debug_out,
+ "times what the low priority thread manages. A maximum of \n");
+ PR_fprintf( debug_out, "%d cycles are available.\n\n", totalCount);
+ }
+
+ duration = (duration + 4) / 5;
+ CreateThreads(&lowCount, &highCount);
+ while (duration--)
+ {
+ PRIntn loop = 5;
+ while (loop--) {
+ PR_Sleep(oneSecond);
+ }
+ if (debug_mode) {
+ PR_fprintf(debug_out, "high : low :: %d : %d\n", highCount, lowCount);
+ }
+ }
+
+
+ PR_ProcessExit((failed) ? 1 : 0);
+
+ PR_NOT_REACHED("You can't get here -- but you did!");
+ return 1; /* or here */
+
+} /* main */
+
+/* priotest.c */
diff --git a/nsprpub/pr/tests/provider.c b/nsprpub/pr/tests/provider.c
new file mode 100644
index 0000000000..3450b20aea
--- /dev/null
+++ b/nsprpub/pr/tests/provider.c
@@ -0,0 +1,1412 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *
+ * Notes:
+ * [1] lth. The call to Sleep() is a hack to get the test case to run
+ * on Windows 95. Without it, the test case fails with an error
+ * WSAECONNRESET following a recv() call. The error is caused by the
+ * server side thread termination without a shutdown() or closesocket()
+ * call. Windows docmunentation suggests that this is predicted
+ * behavior; that other platforms get away with it is ... serindipity.
+ * The test case should shutdown() or closesocket() before
+ * thread termination. I didn't have time to figure out where or how
+ * to do it. The Sleep() call inserts enough delay to allow the
+ * client side to recv() all his data before the server side thread
+ * terminates. Whew! ...
+ *
+ ** Modification History:
+ * 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+ * The debug mode will print all of the printfs associated with this test.
+ * The regress mode will be the default mode. Since the regress tool limits
+ * the output to a one line status:PASS or FAIL,all of the printf statements
+ * have been handled with an if (debug_mode) statement.
+ */
+
+#include "prclist.h"
+#include "prcvar.h"
+#include "prerror.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prio.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prtime.h"
+#include "prmem.h"
+#include "prnetdb.h"
+#include "prprf.h"
+#include "prthread.h"
+
+#include "pprio.h"
+#include "primpl.h"
+
+#include "plstr.h"
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(XP_UNIX)
+#include <math.h>
+#endif
+
+/*
+** This is the beginning of the test
+*/
+
+#ifdef DEBUG
+#define PORT_INC_DO +100
+#else
+#define PORT_INC_DO
+#endif
+#ifdef IS_64
+#define PORT_INC_3264 +200
+#else
+#define PORT_INC_3264
+#endif
+
+#define RECV_FLAGS 0
+#define SEND_FLAGS 0
+#define BUFFER_SIZE 1024
+#define DEFAULT_BACKLOG 5
+#define DEFAULT_PORT 13000 PORT_INC_DO PORT_INC_3264
+#define DEFAULT_CLIENTS 1
+#define ALLOWED_IN_ACCEPT 1
+#define DEFAULT_CLIPPING 1000
+#define DEFAULT_WORKERS_MIN 1
+#define DEFAULT_WORKERS_MAX 1
+#define DEFAULT_SERVER "localhost"
+#define DEFAULT_EXECUTION_TIME 10
+#define DEFAULT_CLIENT_TIMEOUT 4000
+#define DEFAULT_SERVER_TIMEOUT 4000
+#define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH
+
+typedef enum CSState_e {cs_init, cs_run, cs_stop, cs_exit} CSState_t;
+
+static void PR_CALLBACK Worker(void *arg);
+typedef struct CSPool_s CSPool_t;
+typedef struct CSWorker_s CSWorker_t;
+typedef struct CSServer_s CSServer_t;
+typedef enum Verbosity
+{
+ TEST_LOG_ALWAYS,
+ TEST_LOG_ERROR,
+ TEST_LOG_WARNING,
+ TEST_LOG_NOTICE,
+ TEST_LOG_INFO,
+ TEST_LOG_STATUS,
+ TEST_LOG_VERBOSE
+} Verbosity;
+
+static enum {
+ thread_nspr, thread_pthread, thread_sproc, thread_win32
+} thread_provider;
+
+static PRInt32 domain = AF_INET;
+static PRInt32 protocol = 6; /* TCP */
+static PRFileDesc *debug_out = NULL;
+static PRBool debug_mode = PR_FALSE;
+static PRBool pthread_stats = PR_FALSE;
+static Verbosity verbosity = TEST_LOG_ALWAYS;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+struct CSWorker_s
+{
+ PRCList element; /* list of the server's workers */
+
+ PRThread *thread; /* this worker objects thread */
+ CSServer_t *server; /* back pointer to server structure */
+};
+
+struct CSPool_s
+{
+ PRCondVar *exiting;
+ PRCondVar *acceptComplete;
+ PRUint32 accepting, active, workers;
+};
+
+struct CSServer_s
+{
+ PRCList list; /* head of worker list */
+
+ PRLock *ml;
+ PRThread *thread; /* the main server thread */
+ PRCondVar *stateChange;
+
+ PRUint16 port; /* port we're listening on */
+ PRUint32 backlog; /* size of our listener backlog */
+ PRFileDesc *listener; /* the fd accepting connections */
+
+ CSPool_t pool; /* statistics on worker threads */
+ CSState_t state; /* the server's state */
+ struct /* controlling worker counts */
+ {
+ PRUint32 minimum, maximum, accepting;
+ } workers;
+
+ /* statistics */
+ PRIntervalTime started, stopped;
+ PRUint32 operations, bytesTransferred;
+};
+
+typedef struct CSDescriptor_s
+{
+ PRInt32 size; /* size of transfer */
+ char filename[60]; /* filename, null padded */
+} CSDescriptor_t;
+
+typedef struct CSClient_s
+{
+ PRLock *ml;
+ PRThread *thread;
+ PRCondVar *stateChange;
+ PRNetAddr serverAddress;
+
+ CSState_t state;
+
+ /* statistics */
+ PRIntervalTime started, stopped;
+ PRUint32 operations, bytesTransferred;
+} CSClient_t;
+
+#define TEST_LOG(l, p, a) \
+ do { \
+ if (debug_mode || (p <= verbosity)) printf a; \
+ } while (0)
+
+PRLogModuleInfo *cltsrv_log_file = NULL;
+
+#define MY_ASSERT(_expr) \
+ ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
+
+#define TEST_ASSERT(_expr) \
+ ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
+
+static void _MY_Assert(const char *s, const char *file, PRIntn ln)
+{
+ PL_PrintError(NULL);
+ PR_Assert(s, file, ln);
+} /* _MY_Assert */
+
+static PRBool Aborted(PRStatus rv)
+{
+ return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) ?
+ PR_TRUE : PR_FALSE;
+}
+
+static void TimeOfDayMessage(const char *msg, PRThread* me)
+{
+ char buffer[100];
+ PRExplodedTime tod;
+ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod);
+ (void)PR_FormatTime(buffer, sizeof(buffer), "%H:%M:%S", &tod);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("%s(0x%p): %s\n", msg, me, buffer));
+} /* TimeOfDayMessage */
+
+
+static void PR_CALLBACK Client(void *arg)
+{
+ PRStatus rv;
+ PRIntn index;
+ char buffer[1024];
+ PRFileDesc *fd = NULL;
+ PRUintn clipping = DEFAULT_CLIPPING;
+ CSClient_t *client = (CSClient_t*)arg;
+ PRThread *me = client->thread = PR_GetCurrentThread();
+ CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
+ PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT);
+
+
+ for (index = 0; index < sizeof(buffer); ++index) {
+ buffer[index] = (char)index;
+ }
+
+ client->started = PR_IntervalNow();
+
+ PR_Lock(client->ml);
+ client->state = cs_run;
+ PR_NotifyCondVar(client->stateChange);
+ PR_Unlock(client->ml);
+
+ TimeOfDayMessage("Client started at", me);
+
+ while (cs_run == client->state)
+ {
+ PRInt32 bytes, descbytes, filebytes, netbytes;
+
+ (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer));
+ TEST_LOG(cltsrv_log_file, TEST_LOG_INFO,
+ ("\tClient(0x%p): connecting to server at %s\n", me, buffer));
+
+ fd = PR_Socket(domain, SOCK_STREAM, protocol);
+ TEST_ASSERT(NULL != fd);
+ rv = PR_Connect(fd, &client->serverAddress, timeout);
+ if (PR_FAILURE == rv)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): conection failed\n", me));
+ goto aborted;
+ }
+
+ memset(descriptor, 0, sizeof(*descriptor));
+ descriptor->size = PR_htonl(descbytes = rand() % clipping);
+ PR_snprintf(
+ descriptor->filename, sizeof(descriptor->filename),
+ "CS%p%p-%p.dat", client->started, me, client->operations);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes));
+ bytes = PR_Send(
+ fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout);
+ if (sizeof(CSDescriptor_t) != bytes)
+ {
+ if (Aborted(PR_FAILURE)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): send descriptor timeout\n", me));
+ goto retry;
+ }
+ }
+ TEST_ASSERT(sizeof(*descriptor) == bytes);
+
+ netbytes = 0;
+ while (netbytes < descbytes)
+ {
+ filebytes = sizeof(buffer);
+ if ((descbytes - netbytes) < filebytes) {
+ filebytes = descbytes - netbytes;
+ }
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tClient(0x%p): sending %d bytes\n", me, filebytes));
+ bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
+ if (filebytes != bytes)
+ {
+ if (Aborted(PR_FAILURE)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): send data timeout\n", me));
+ goto retry;
+ }
+ }
+ TEST_ASSERT(bytes == filebytes);
+ netbytes += bytes;
+ }
+ filebytes = 0;
+ while (filebytes < descbytes)
+ {
+ netbytes = sizeof(buffer);
+ if ((descbytes - filebytes) < netbytes) {
+ netbytes = descbytes - filebytes;
+ }
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tClient(0x%p): receiving %d bytes\n", me, netbytes));
+ bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
+ if (-1 == bytes)
+ {
+ if (Aborted(PR_FAILURE))
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): receive data aborted\n", me));
+ goto aborted;
+ }
+ else if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): receive data timeout\n", me));
+ else
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): receive error (%d, %d)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto retry;
+ }
+ if (0 == bytes)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tClient(0x%p): unexpected end of stream\n",
+ PR_GetCurrentThread()));
+ break;
+ }
+ filebytes += bytes;
+ }
+
+ rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ TEST_ASSERT(PR_SUCCESS == rv);
+retry:
+ (void)PR_Close(fd); fd = NULL;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("\tClient(0x%p): disconnected from server\n", me));
+
+ PR_Lock(client->ml);
+ client->operations += 1;
+ client->bytesTransferred += 2 * descbytes;
+ rv = PR_WaitCondVar(client->stateChange, rand() % clipping);
+ PR_Unlock(client->ml);
+ if (Aborted(rv)) {
+ break;
+ }
+ }
+
+aborted:
+ client->stopped = PR_IntervalNow();
+
+ PR_ClearInterrupt();
+ if (NULL != fd) {
+ rv = PR_Close(fd);
+ }
+
+ PR_Lock(client->ml);
+ client->state = cs_exit;
+ PR_NotifyCondVar(client->stateChange);
+ PR_Unlock(client->ml);
+ PR_DELETE(descriptor);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("\tClient(0x%p): stopped after %u operations and %u bytes\n",
+ PR_GetCurrentThread(), client->operations, client->bytesTransferred));
+
+} /* Client */
+
+static PRStatus ProcessRequest(PRFileDesc *fd, CSServer_t *server)
+{
+ PRStatus drv, rv;
+ char buffer[1024];
+ PRFileDesc *file = NULL;
+ PRThread * me = PR_GetCurrentThread();
+ PRInt32 bytes, descbytes, netbytes, filebytes = 0;
+ CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
+ PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): receiving desciptor\n", me));
+ bytes = PR_Recv(
+ fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout);
+ if (-1 == bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto exit;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tProcessRequest(0x%p): receive timeout\n", me));
+ }
+ goto exit;
+ }
+ if (0 == bytes)
+ {
+ rv = PR_FAILURE;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tProcessRequest(0x%p): unexpected end of file\n", me));
+ goto exit;
+ }
+ descbytes = PR_ntohl(descriptor->size);
+ TEST_ASSERT(sizeof(*descriptor) == bytes);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n",
+ me, descbytes, descriptor->filename));
+
+ file = PR_Open(
+ descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666);
+ if (NULL == file)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tProcessRequest(0x%p): open file timeout\n", me));
+ goto aborted;
+ }
+ }
+ TEST_ASSERT(NULL != file);
+
+ filebytes = 0;
+ while (filebytes < descbytes)
+ {
+ netbytes = sizeof(buffer);
+ if ((descbytes - filebytes) < netbytes) {
+ netbytes = descbytes - filebytes;
+ }
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes));
+ bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
+ if (-1 == bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): receive data timeout\n", me));
+ goto aborted;
+ }
+ /*
+ * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED)
+ * on NT here. This is equivalent to ECONNRESET on Unix.
+ * -wtc
+ */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_WARNING,
+ ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto aborted;
+ }
+ if(0 == bytes)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_WARNING,
+ ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me));
+ rv = PR_FAILURE;
+ goto aborted;
+ }
+ filebytes += bytes;
+ netbytes = bytes;
+ /* The byte count for PR_Write should be positive */
+ MY_ASSERT(netbytes > 0);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes));
+ bytes = PR_Write(file, buffer, netbytes);
+ if (netbytes != bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): write file timeout\n", me));
+ goto aborted;
+ }
+ }
+ TEST_ASSERT(bytes > 0);
+ }
+
+ PR_Lock(server->ml);
+ server->operations += 1;
+ server->bytesTransferred += filebytes;
+ PR_Unlock(server->ml);
+
+ rv = PR_Close(file); file = NULL;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): opening %s\n", me, descriptor->filename));
+ file = PR_Open(descriptor->filename, PR_RDONLY, 0);
+ if (NULL == file)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): open file timeout\n",
+ PR_GetCurrentThread()));
+ goto aborted;
+ }
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): other file open error (%u, %u)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto aborted;
+ }
+ TEST_ASSERT(NULL != file);
+
+ netbytes = 0;
+ while (netbytes < descbytes)
+ {
+ filebytes = sizeof(buffer);
+ if ((descbytes - netbytes) < filebytes) {
+ filebytes = descbytes - netbytes;
+ }
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): read %d bytes from file\n", me, filebytes));
+ bytes = PR_Read(file, buffer, filebytes);
+ if (filebytes != bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): read file timeout\n", me));
+ else
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): other file error (%d, %d)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto aborted;
+ }
+ TEST_ASSERT(bytes > 0);
+ netbytes += bytes;
+ filebytes = bytes;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): sending %d bytes\n", me, filebytes));
+ bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
+ if (filebytes != bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): send data timeout\n", me));
+ goto aborted;
+ }
+ break;
+ }
+ TEST_ASSERT(bytes > 0);
+ }
+
+ PR_Lock(server->ml);
+ server->bytesTransferred += filebytes;
+ PR_Unlock(server->ml);
+
+ rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+
+ rv = PR_Close(file); file = NULL;
+ if (Aborted(rv)) {
+ goto aborted;
+ }
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+aborted:
+ PR_ClearInterrupt();
+ if (NULL != file) {
+ PR_Close(file);
+ }
+ drv = PR_Delete(descriptor->filename);
+ TEST_ASSERT(PR_SUCCESS == drv);
+exit:
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): Finished\n", me));
+
+ PR_DELETE(descriptor);
+
+#if defined(WIN95)
+ PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */
+#endif
+ return rv;
+} /* ProcessRequest */
+
+typedef void (*StartFn)(void*);
+typedef struct StartObject
+{
+ StartFn start;
+ void *arg;
+} StartObject;
+
+#if defined(_PR_PTHREADS)
+#include "md/_pth.h"
+#include <pthread.h>
+
+static void *pthread_start(void *arg)
+{
+ StartObject *so = (StartObject*)arg;
+ StartFn start = so->start;
+ void *data = so->arg;
+ PR_Free(so);
+ start(data);
+ return NULL;
+} /* pthread_start */
+#endif /* defined(_PR_PTHREADS) */
+
+#if defined(WIN32)
+#include <process.h> /* for _beginthreadex() */
+
+static PRUintn __stdcall windows_start(void *arg)
+{
+ StartObject *so = (StartObject*)arg;
+ StartFn start = so->start;
+ void *data = so->arg;
+ PR_Free(so);
+ start(data);
+ return 0;
+} /* windows_start */
+#endif /* defined(WIN32) */
+
+static PRStatus JoinThread(PRThread *thread)
+{
+ PRStatus rv;
+ switch (thread_provider)
+ {
+ case thread_nspr:
+ rv = PR_JoinThread(thread);
+ break;
+ case thread_pthread:
+#if defined(_PR_PTHREADS)
+ rv = PR_SUCCESS;
+ break;
+#endif /* defined(_PR_PTHREADS) */
+ case thread_win32:
+#if defined(WIN32)
+ rv = PR_SUCCESS;
+ break;
+#endif
+ default:
+ rv = PR_FAILURE;
+ break;
+ }
+ return rv;
+} /* JoinThread */
+
+static PRStatus NewThread(
+ StartFn start, void *arg, PRThreadPriority prio, PRThreadState state)
+{
+ PRStatus rv;
+
+ switch (thread_provider)
+ {
+ case thread_nspr:
+ {
+ PRThread *thread = PR_CreateThread(
+ PR_USER_THREAD, start, arg,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD, 0);
+ rv = (NULL == thread) ? PR_FAILURE : PR_SUCCESS;
+ }
+ break;
+ case thread_pthread:
+#if defined(_PR_PTHREADS)
+ {
+ int rv;
+ pthread_t id;
+ pthread_attr_t tattr;
+ StartObject *start_object;
+ start_object = PR_NEW(StartObject);
+ PR_ASSERT(NULL != start_object);
+ start_object->start = start;
+ start_object->arg = arg;
+
+ rv = _PT_PTHREAD_ATTR_INIT(&tattr);
+ PR_ASSERT(0 == rv);
+
+ rv = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
+ PR_ASSERT(0 == rv);
+
+ rv = pthread_attr_setstacksize(&tattr, 64 * 1024);
+ PR_ASSERT(0 == rv);
+
+ rv = _PT_PTHREAD_CREATE(&id, tattr, pthread_start, start_object);
+ (void)_PT_PTHREAD_ATTR_DESTROY(&tattr);
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+ }
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+#endif /* defined(_PR_PTHREADS) */
+ break;
+
+ case thread_sproc:
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+ break;
+ case thread_win32:
+#if defined(WIN32)
+ {
+ void *th;
+ PRUintn id;
+ StartObject *start_object;
+ start_object = PR_NEW(StartObject);
+ PR_ASSERT(NULL != start_object);
+ start_object->start = start;
+ start_object->arg = arg;
+ th = (void*)_beginthreadex(
+ NULL, /* LPSECURITY_ATTRIBUTES - pointer to thread security attributes */
+ 0U, /* DWORD - initial thread stack size, in bytes */
+ windows_start, /* LPTHREAD_START_ROUTINE - pointer to thread function */
+ start_object, /* LPVOID - argument for new thread */
+ STACK_SIZE_PARAM_IS_A_RESERVATION, /*DWORD dwCreationFlags - creation flags */
+ &id /* LPDWORD - pointer to returned thread identifier */ );
+
+ rv = (NULL == th) ? PR_FAILURE : PR_SUCCESS;
+ }
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+#endif
+ break;
+ default:
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ return rv;
+} /* NewThread */
+
+static PRStatus CreateWorker(CSServer_t *server, CSPool_t *pool)
+{
+ PRStatus rv;
+ CSWorker_t *worker = PR_NEWZAP(CSWorker_t);
+ worker->server = server;
+ PR_INIT_CLIST(&worker->element);
+ rv = NewThread(
+ Worker, worker, DEFAULT_SERVER_PRIORITY, PR_UNJOINABLE_THREAD);
+ if (PR_FAILURE == rv) {
+ PR_DELETE(worker);
+ }
+
+ TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
+ ("\tCreateWorker(0x%p): create new worker (0x%p)\n",
+ PR_GetCurrentThread(), worker->thread));
+
+ return rv;
+} /* CreateWorker */
+
+static void PR_CALLBACK Worker(void *arg)
+{
+ PRStatus rv;
+ PRNetAddr from;
+ PRFileDesc *fd = NULL;
+ CSWorker_t *worker = (CSWorker_t*)arg;
+ CSServer_t *server = worker->server;
+ CSPool_t *pool = &server->pool;
+
+ PRThread *me = worker->thread = PR_GetCurrentThread();
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1));
+
+ PR_Lock(server->ml);
+ PR_APPEND_LINK(&worker->element, &server->list);
+ pool->workers += 1; /* define our existance */
+
+ while (cs_run == server->state)
+ {
+ while (pool->accepting >= server->workers.accepting)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tWorker(0x%p): waiting for accept slot[%d]\n",
+ me, pool->accepting));
+ rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT);
+ if (Aborted(rv) || (cs_run != server->state))
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\tWorker(0x%p): has been %s\n",
+ me, (Aborted(rv) ? "interrupted" : "stopped")));
+ goto exit;
+ }
+ }
+ pool->accepting += 1; /* how many are really in accept */
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tWorker(0x%p): calling accept\n", me));
+ fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT);
+
+ PR_Lock(server->ml);
+ pool->accepting -= 1;
+ PR_NotifyCondVar(pool->acceptComplete);
+
+ if ((NULL == fd) && Aborted(PR_FAILURE))
+ {
+ if (NULL != server->listener)
+ {
+ PR_Close(server->listener);
+ server->listener = NULL;
+ }
+ goto exit;
+ }
+
+ if (NULL != fd)
+ {
+ /*
+ ** Create another worker of the total number of workers is
+ ** less than the minimum specified or we have none left in
+ ** accept() AND we're not over the maximum.
+ ** This sort of presumes that the number allowed in accept
+ ** is at least as many as the minimum. Otherwise we'll keep
+ ** creating new threads and deleting them soon after.
+ */
+ PRBool another =
+ ((pool->workers < server->workers.minimum) ||
+ ((0 == pool->accepting)
+ && (pool->workers < server->workers.maximum))) ?
+ PR_TRUE : PR_FALSE;
+ pool->active += 1;
+ PR_Unlock(server->ml);
+
+ if (another) {
+ (void)CreateWorker(server, pool);
+ }
+
+ rv = ProcessRequest(fd, server);
+ if (PR_SUCCESS != rv)
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tWorker(0x%p): server process ended abnormally\n", me));
+ (void)PR_Close(fd); fd = NULL;
+
+ PR_Lock(server->ml);
+ pool->active -= 1;
+ }
+ }
+
+exit:
+ PR_ClearInterrupt();
+ PR_Unlock(server->ml);
+
+ if (NULL != fd)
+ {
+ (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
+ (void)PR_Close(fd);
+ }
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\t\tWorker(0x%p): exiting [%u]\n", PR_GetCurrentThread(), pool->workers));
+
+ PR_Lock(server->ml);
+ pool->workers -= 1; /* undefine our existance */
+ PR_REMOVE_AND_INIT_LINK(&worker->element);
+ PR_NotifyCondVar(pool->exiting);
+ PR_Unlock(server->ml);
+
+ PR_DELETE(worker); /* destruction of the "worker" object */
+
+} /* Worker */
+
+static void PR_CALLBACK Server(void *arg)
+{
+ PRStatus rv;
+ PRNetAddr serverAddress;
+ CSServer_t *server = (CSServer_t*)arg;
+ PRThread *me = server->thread = PR_GetCurrentThread();
+ PRSocketOptionData sockOpt;
+
+ server->listener = PR_Socket(domain, SOCK_STREAM, protocol);
+
+ sockOpt.option = PR_SockOpt_Reuseaddr;
+ sockOpt.value.reuse_addr = PR_TRUE;
+ rv = PR_SetSocketOption(server->listener, &sockOpt);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ memset(&serverAddress, 0, sizeof(serverAddress));
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress);
+
+ rv = PR_Bind(server->listener, &serverAddress);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Listen(server->listener, server->backlog);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ server->started = PR_IntervalNow();
+ TimeOfDayMessage("Server started at", me);
+
+ PR_Lock(server->ml);
+ server->state = cs_run;
+ PR_NotifyCondVar(server->stateChange);
+ PR_Unlock(server->ml);
+
+ /*
+ ** Create the first worker (actually, a thread that accepts
+ ** connections and then processes the work load as needed).
+ ** From this point on, additional worker threads are created
+ ** as they are needed by existing worker threads.
+ */
+ rv = CreateWorker(server, &server->pool);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ /*
+ ** From here on this thread is merely hanging around as the contact
+ ** point for the main test driver. It's just waiting for the driver
+ ** to declare the test complete.
+ */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tServer(0x%p): waiting for state change\n", me));
+
+ PR_Lock(server->ml);
+ while ((cs_run == server->state) && !Aborted(rv))
+ {
+ rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(server->ml);
+ PR_ClearInterrupt();
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("\tServer(0x%p): shutting down workers\n", me));
+
+ /*
+ ** Get all the worker threads to exit. They know how to
+ ** clean up after themselves, so this is just a matter of
+ ** waiting for clorine in the pool to take effect. During
+ ** this stage we're ignoring interrupts.
+ */
+ server->workers.minimum = server->workers.maximum = 0;
+
+ PR_Lock(server->ml);
+ while (!PR_CLIST_IS_EMPTY(&server->list))
+ {
+ PRCList *head = PR_LIST_HEAD(&server->list);
+ CSWorker_t *worker = (CSWorker_t*)head;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker));
+ rv = PR_Interrupt(worker->thread);
+ TEST_ASSERT(PR_SUCCESS == rv);
+ PR_REMOVE_AND_INIT_LINK(head);
+ }
+
+ while (server->pool.workers > 0)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\tServer(0x%p): waiting for %u workers to exit\n",
+ me, server->pool.workers));
+ (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ server->state = cs_exit;
+ PR_NotifyCondVar(server->stateChange);
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("\tServer(0x%p): stopped after %u operations and %u bytes\n",
+ me, server->operations, server->bytesTransferred));
+
+ if (NULL != server->listener) {
+ PR_Close(server->listener);
+ }
+ server->stopped = PR_IntervalNow();
+
+} /* Server */
+
+static void WaitForCompletion(PRIntn execution)
+{
+ while (execution > 0)
+ {
+ PRIntn dally = (execution > 30) ? 30 : execution;
+ PR_Sleep(PR_SecondsToInterval(dally));
+ if (pthread_stats) {
+ PT_FPrintStats(debug_out, "\nPThread Statistics\n");
+ }
+ execution -= dally;
+ }
+} /* WaitForCompletion */
+
+static void Help(void)
+{
+ PR_fprintf(debug_out, "cltsrv test program usage:\n");
+ PR_fprintf(debug_out, "\t-a <n> threads allowed in accept (5)\n");
+ PR_fprintf(debug_out, "\t-b <n> backlock for listen (5)\n");
+ PR_fprintf(debug_out, "\t-c <threads> number of clients to create (1)\n");
+ PR_fprintf(debug_out, "\t-w <threads> minimal number of server threads (1)\n");
+ PR_fprintf(debug_out, "\t-W <threads> maximum number of server threads (1)\n");
+ PR_fprintf(debug_out, "\t-e <seconds> duration of the test in seconds (10)\n");
+ PR_fprintf(debug_out, "\t-s <string> dsn name of server (localhost)\n");
+ PR_fprintf(debug_out, "\t-G use GLOBAL threads (LOCAL)\n");
+ PR_fprintf(debug_out, "\t-T <string> thread provider ('n' | 'p' | 'w')(n)\n");
+ PR_fprintf(debug_out, "\t-X use XTP as transport (TCP)\n");
+ PR_fprintf(debug_out, "\t-6 Use IPv6 (IPv4)\n");
+ PR_fprintf(debug_out, "\t-v verbosity (accumulative) (0)\n");
+ PR_fprintf(debug_out, "\t-p pthread statistics (FALSE)\n");
+ PR_fprintf(debug_out, "\t-d debug mode (FALSE)\n");
+ PR_fprintf(debug_out, "\t-h this message\n");
+} /* Help */
+
+static Verbosity IncrementVerbosity(void)
+{
+ PRIntn verboge = (PRIntn)verbosity + 1;
+ return (Verbosity)verboge;
+} /* IncrementVerbosity */
+
+int main(int argc, char **argv)
+{
+ PRUintn index;
+ PRBool boolean;
+ CSClient_t *client;
+ PRStatus rv, joinStatus;
+ CSServer_t *server = NULL;
+ char *thread_type;
+
+ PRUintn backlog = DEFAULT_BACKLOG;
+ PRUintn clients = DEFAULT_CLIENTS;
+ const char *serverName = DEFAULT_SERVER;
+ PRBool serverIsLocal = PR_TRUE;
+ PRUintn accepting = ALLOWED_IN_ACCEPT;
+ PRUintn workersMin = DEFAULT_WORKERS_MIN;
+ PRUintn workersMax = DEFAULT_WORKERS_MAX;
+ PRIntn execution = DEFAULT_EXECUTION_TIME;
+
+ /*
+ * -G use global threads
+ * -a <n> threads allowed in accept
+ * -b <n> backlock for listen
+ * -c <threads> number of clients to create
+ * -w <threads> minimal number of server threads
+ * -W <threads> maximum number of server threads
+ * -e <seconds> duration of the test in seconds
+ * -s <string> dsn name of server (implies no server here)
+ * -v verbosity
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:w:W:e:s:T:vdhp");
+
+#if defined(WIN32)
+ thread_provider = thread_win32;
+#elif defined(_PR_PTHREADS)
+ thread_provider = thread_pthread;
+#else
+ thread_provider = thread_nspr;
+#endif
+
+ debug_out = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'X': /* use XTP as transport */
+ protocol = 36;
+ break;
+ case '6': /* Use IPv6 */
+ domain = PR_AF_INET6;
+ break;
+ case 'a': /* the value for accepting */
+ accepting = atoi(opt->value);
+ break;
+ case 'b': /* the value for backlock */
+ backlog = atoi(opt->value);
+ break;
+ case 'T': /* the thread provider */
+ if ('n' == *opt->value) {
+ thread_provider = thread_nspr;
+ }
+ else if ('p' == *opt->value) {
+ thread_provider = thread_pthread;
+ }
+ else if ('w' == *opt->value) {
+ thread_provider = thread_win32;
+ }
+ else {
+ Help();
+ return 2;
+ }
+ break;
+ case 'c': /* number of client threads */
+ clients = atoi(opt->value);
+ break;
+ case 'w': /* minimum server worker threads */
+ workersMin = atoi(opt->value);
+ break;
+ case 'W': /* maximum server worker threads */
+ workersMax = atoi(opt->value);
+ break;
+ case 'e': /* program execution time in seconds */
+ execution = atoi(opt->value);
+ break;
+ case 's': /* server's address */
+ serverName = opt->value;
+ break;
+ case 'v': /* verbosity */
+ verbosity = IncrementVerbosity();
+ break;
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'p': /* pthread mode */
+ pthread_stats = PR_TRUE;
+ break;
+ case 'h':
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) {
+ serverIsLocal = PR_FALSE;
+ }
+ if (0 == execution) {
+ execution = DEFAULT_EXECUTION_TIME;
+ }
+ if (0 == workersMax) {
+ workersMax = DEFAULT_WORKERS_MAX;
+ }
+ if (0 == workersMin) {
+ workersMin = DEFAULT_WORKERS_MIN;
+ }
+ if (0 == accepting) {
+ accepting = ALLOWED_IN_ACCEPT;
+ }
+ if (0 == backlog) {
+ backlog = DEFAULT_BACKLOG;
+ }
+
+ if (workersMin > accepting) {
+ accepting = workersMin;
+ }
+
+ PR_STDIO_INIT();
+ TimeOfDayMessage("Client/Server started at", PR_GetCurrentThread());
+
+ cltsrv_log_file = PR_NewLogModule("cltsrv_log");
+ MY_ASSERT(NULL != cltsrv_log_file);
+ boolean = PR_SetLogFile("cltsrv.log");
+ MY_ASSERT(boolean);
+
+ if (serverIsLocal)
+ {
+ /* Establish the server */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("main(0x%p): starting server\n", PR_GetCurrentThread()));
+
+ server = PR_NEWZAP(CSServer_t);
+ PR_INIT_CLIST(&server->list);
+ server->state = cs_init;
+ server->ml = PR_NewLock();
+ server->backlog = backlog;
+ server->port = DEFAULT_PORT;
+ server->workers.minimum = workersMin;
+ server->workers.maximum = workersMax;
+ server->workers.accepting = accepting;
+ server->stateChange = PR_NewCondVar(server->ml);
+ server->pool.exiting = PR_NewCondVar(server->ml);
+ server->pool.acceptComplete = PR_NewCondVar(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("main(0x%p): creating server thread\n", PR_GetCurrentThread()));
+
+ rv = NewThread(
+ Server, server, PR_PRIORITY_HIGH, PR_JOINABLE_THREAD);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): waiting for server init\n", PR_GetCurrentThread()));
+
+ PR_Lock(server->ml);
+ while (server->state == cs_init) {
+ PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): server init complete (port #%d)\n",
+ PR_GetCurrentThread(), server->port));
+ }
+
+ if (clients != 0)
+ {
+ /* Create all of the clients */
+ PRHostEnt host;
+ char buffer[BUFFER_SIZE];
+ client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t));
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): creating %d client threads\n",
+ PR_GetCurrentThread(), clients));
+
+ if (!serverIsLocal)
+ {
+ rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host);
+ if (PR_SUCCESS != rv)
+ {
+ PL_FPrintError(PR_STDERR, "PR_GetHostByName");
+ return 2;
+ }
+ }
+
+ for (index = 0; index < clients; ++index)
+ {
+ client[index].state = cs_init;
+ client[index].ml = PR_NewLock();
+ if (serverIsLocal)
+ {
+ (void)PR_InitializeNetAddr(
+ PR_IpAddrLoopback, DEFAULT_PORT,
+ &client[index].serverAddress);
+ }
+ else
+ {
+ (void)PR_EnumerateHostEnt(
+ 0, &host, DEFAULT_PORT, &client[index].serverAddress);
+ }
+ client[index].stateChange = PR_NewCondVar(client[index].ml);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("main(0x%p): creating client threads\n", PR_GetCurrentThread()));
+ rv = NewThread(
+ Client, &client[index], PR_PRIORITY_NORMAL, PR_JOINABLE_THREAD);
+ TEST_ASSERT(PR_SUCCESS == rv);
+ PR_Lock(client[index].ml);
+ while (cs_init == client[index].state) {
+ PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(client[index].ml);
+ }
+ }
+
+ /* Then just let them go at it for a bit */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("main(0x%p): waiting for execution interval (%d seconds)\n",
+ PR_GetCurrentThread(), execution));
+
+ WaitForCompletion(execution);
+
+ TimeOfDayMessage("Shutting down", PR_GetCurrentThread());
+
+ if (clients != 0)
+ {
+ for (index = 0; index < clients; ++index)
+ {
+ TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
+ ("main(0x%p): notifying client(0x%p) to stop\n",
+ PR_GetCurrentThread(), client[index].thread));
+
+ PR_Lock(client[index].ml);
+ if (cs_run == client[index].state)
+ {
+ client[index].state = cs_stop;
+ PR_Interrupt(client[index].thread);
+ while (cs_stop == client[index].state)
+ PR_WaitCondVar(
+ client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(client[index].ml);
+
+ TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): joining client(0x%p)\n",
+ PR_GetCurrentThread(), client[index].thread));
+
+ joinStatus = JoinThread(client[index].thread);
+ TEST_ASSERT(PR_SUCCESS == joinStatus);
+ PR_DestroyCondVar(client[index].stateChange);
+ PR_DestroyLock(client[index].ml);
+ }
+ PR_DELETE(client);
+ }
+
+ if (NULL != server)
+ {
+ /* All clients joined - retrieve the server */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("main(0x%p): notifying server(0x%p) to stop\n",
+ PR_GetCurrentThread(), server->thread));
+
+ PR_Lock(server->ml);
+ server->state = cs_stop;
+ PR_Interrupt(server->thread);
+ while (cs_exit != server->state) {
+ PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("main(0x%p): joining server(0x%p)\n",
+ PR_GetCurrentThread(), server->thread));
+ joinStatus = JoinThread(server->thread);
+ TEST_ASSERT(PR_SUCCESS == joinStatus);
+
+ PR_DestroyCondVar(server->stateChange);
+ PR_DestroyCondVar(server->pool.exiting);
+ PR_DestroyCondVar(server->pool.acceptComplete);
+ PR_DestroyLock(server->ml);
+ PR_DELETE(server);
+ }
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("main(0x%p): test complete\n", PR_GetCurrentThread()));
+
+ if (thread_provider == thread_win32) {
+ thread_type = "\nWin32 Thread Statistics\n";
+ }
+ else if (thread_provider == thread_pthread) {
+ thread_type = "\npthread Statistics\n";
+ }
+ else if (thread_provider == thread_sproc) {
+ thread_type = "\nsproc Statistics\n";
+ }
+ else {
+ PR_ASSERT(thread_provider == thread_nspr);
+ thread_type = "\nPRThread Statistics\nn";
+ }
+
+ PT_FPrintStats(debug_out, thread_type);
+
+ TimeOfDayMessage("Test exiting at", PR_GetCurrentThread());
+ PR_Cleanup();
+ return 0;
+} /* main */
+
+/* cltsrv.c */
diff --git a/nsprpub/pr/tests/prpoll.c b/nsprpub/pr/tests/prpoll.c
new file mode 100644
index 0000000000..5c87a57d17
--- /dev/null
+++ b/nsprpub/pr/tests/prpoll.c
@@ -0,0 +1,351 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#ifdef XP_UNIX
+#include <unistd.h> /* for close() */
+#endif
+
+#include "prinit.h"
+#include "prio.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prnetdb.h"
+
+#include "private/pprio.h"
+
+#define CLIENT_LOOPS 5
+#define BUF_SIZE 128
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef WINCE
+
+int main(int argc, char **argv)
+{
+ fprintf(stderr, "Invalid/Broken Test for WinCE/WinMobile\n");
+ exit(1);
+}
+
+#else
+
+static void
+clientThreadFunc(void *arg)
+{
+ PRUint16 port = (PRUint16) arg;
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ char buf[BUF_SIZE];
+ int i;
+
+ addr.inet.family = PR_AF_INET;
+ addr.inet.port = PR_htons(port);
+ addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+ PR_snprintf(buf, sizeof(buf), "%hu", port);
+
+ for (i = 0; i < 5; i++) {
+ sock = PR_NewTCPSocket();
+ PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
+
+ PR_Write(sock, buf, sizeof(buf));
+ PR_Close(sock);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1, *listenSock2;
+ PRFileDesc *badFD;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ char buf[BUF_SIZE];
+ PRThread *clientThread;
+ PRPollDesc pds0[10], pds1[10], *pds, *other_pds;
+ PRIntn npds;
+ PRInt32 retVal;
+ PRInt32 rv;
+ PROsfd sd;
+ struct sockaddr_in saddr;
+ PRIntn saddr_len;
+ PRUint16 listenPort3;
+ PRFileDesc *socket_poll_fd;
+ PRIntn i, j;
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ printf("This program tests PR_Poll with sockets.\n");
+ printf("Timeout, error reporting, and normal operation are tested.\n\n");
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ exit(1);
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ exit(1);
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ exit(1);
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ exit(1);
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ exit(1);
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ exit(1);
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ exit(1);
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ exit(1);
+ }
+ /* Set up the poll descriptor array */
+ pds = pds0;
+ other_pds = pds1;
+ memset(pds, 0, sizeof(pds));
+ npds = 0;
+ pds[npds].fd = listenSock1;
+ pds[npds].in_flags = PR_POLL_READ;
+ npds++;
+ pds[npds].fd = listenSock2;
+ pds[npds].in_flags = PR_POLL_READ;
+ npds++;
+
+ sd = socket(AF_INET, SOCK_STREAM, 0);
+ PR_ASSERT(sd >= 0);
+ memset((char *) &saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr.sin_port = htons(0);
+
+ rv = bind(sd, (struct sockaddr *)&saddr, sizeof(saddr));
+ PR_ASSERT(rv == 0);
+ saddr_len = sizeof(saddr);
+ rv = getsockname(sd, (struct sockaddr *) &saddr, &saddr_len);
+ PR_ASSERT(rv == 0);
+ listenPort3 = ntohs(saddr.sin_port);
+
+ rv = listen(sd, 5);
+ PR_ASSERT(rv == 0);
+ pds[npds].fd = socket_poll_fd = PR_CreateSocketPollFd(sd);
+ PR_ASSERT(pds[npds].fd);
+ pds[npds].in_flags = PR_POLL_READ;
+ npds++;
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu, %hu and %hu\n\n",
+ listenPort1, listenPort2, listenPort3);
+ printf("%s", buf);
+
+ /* Testing timeout */
+ printf("PR_Poll should time out in 5 seconds\n");
+ retVal = PR_Poll(pds, npds, PR_SecondsToInterval(5));
+ if (retVal != 0) {
+ PR_snprintf(buf, sizeof(buf),
+ "PR_Poll should time out and return 0, but it returns %ld\n",
+ retVal);
+ fprintf(stderr, "%s", buf);
+ exit(1);
+ }
+ printf("PR_Poll timed out. Test passed.\n\n");
+
+ /* Testing bad fd */
+ printf("PR_Poll should detect a bad file descriptor\n");
+ if ((badFD = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a TCP socket\n");
+ exit(1);
+ }
+
+ pds[npds].fd = badFD;
+ pds[npds].in_flags = PR_POLL_READ;
+ npds++;
+ PR_Close(badFD); /* make the fd bad */
+#if 0
+ retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT);
+ if (retVal != 1 || (unsigned short) pds[2].out_flags != PR_POLL_NVAL) {
+ fprintf(stderr, "Failed to detect the bad fd: "
+ "PR_Poll returns %d, out_flags is 0x%hx\n",
+ retVal, pds[npds - 1].out_flags);
+ exit(1);
+ }
+ printf("PR_Poll detected the bad fd. Test passed.\n\n");
+#endif
+ npds--;
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort1,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ exit(1);
+ }
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort2,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ exit(1);
+ }
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort3,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_BOUND_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ exit(1);
+ }
+
+
+ printf("Three client threads are created. Each of them will\n");
+ printf("send data to one of the three ports the server is listening on.\n");
+ printf("The data they send is the port number. Each of them send\n");
+ printf("the data five times, so you should see ten lines below,\n");
+ printf("interleaved in an arbitrary order.\n");
+
+ /* 30 events total */
+ i = 0;
+ while (i < 30) {
+ PRPollDesc *tmp;
+ int nextIndex;
+ int nEvents = 0;
+
+ retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(retVal != 0); /* no timeout */
+ if (retVal == -1) {
+ fprintf(stderr, "PR_Poll failed\n");
+ exit(1);
+ }
+
+ nextIndex = 3;
+ /* the three listening sockets */
+ for (j = 0; j < 3; j++) {
+ other_pds[j] = pds[j];
+ PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0
+ && (pds[j].out_flags & PR_POLL_EXCEPT) == 0);
+ if (pds[j].out_flags & PR_POLL_READ) {
+ PRFileDesc *sock;
+
+ nEvents++;
+ if (j == 2) {
+ PROsfd newsd;
+ newsd = accept(PR_FileDesc2NativeHandle(pds[j].fd), NULL, 0);
+ if (newsd == -1) {
+ fprintf(stderr, "accept() failed\n");
+ exit(1);
+ }
+ other_pds[nextIndex].fd = PR_CreateSocketPollFd(newsd);
+ PR_ASSERT(other_pds[nextIndex].fd);
+ other_pds[nextIndex].in_flags = PR_POLL_READ;
+ } else {
+ sock = PR_Accept(pds[j].fd, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (sock == NULL) {
+ fprintf(stderr, "PR_Accept() failed\n");
+ exit(1);
+ }
+ other_pds[nextIndex].fd = sock;
+ other_pds[nextIndex].in_flags = PR_POLL_READ;
+ }
+ nextIndex++;
+ } else if (pds[j].out_flags & PR_POLL_ERR) {
+ fprintf(stderr, "PR_Poll() indicates that an fd has error\n");
+ exit(1);
+ } else if (pds[j].out_flags & PR_POLL_NVAL) {
+ fprintf(stderr, "PR_Poll() indicates that fd %d is invalid\n",
+ PR_FileDesc2NativeHandle(pds[j].fd));
+ exit(1);
+ }
+ }
+
+ for (j = 3; j < npds; j++) {
+ PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0
+ && (pds[j].out_flags & PR_POLL_EXCEPT) == 0);
+ if (pds[j].out_flags & PR_POLL_READ) {
+ PRInt32 nBytes;
+
+ nEvents++;
+ /* XXX: This call is a hack and should be fixed */
+ if (PR_GetDescType(pds[j].fd) == (PRDescType) 0) {
+ nBytes = recv(PR_FileDesc2NativeHandle(pds[j].fd), buf,
+ sizeof(buf), 0);
+ if (nBytes == -1) {
+ fprintf(stderr, "recv() failed\n");
+ exit(1);
+ }
+ printf("Server read %d bytes from native fd %d\n",nBytes,
+ PR_FileDesc2NativeHandle(pds[j].fd));
+#ifdef WIN32
+ closesocket((SOCKET)PR_FileDesc2NativeHandle(pds[j].fd));
+#else
+ close(PR_FileDesc2NativeHandle(pds[j].fd));
+#endif
+ PR_DestroySocketPollFd(pds[j].fd);
+ } else {
+ nBytes = PR_Read(pds[j].fd, buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read() failed\n");
+ exit(1);
+ }
+ PR_Close(pds[j].fd);
+ }
+ /* Just to be safe */
+ buf[BUF_SIZE - 1] = '\0';
+ printf("The server received \"%s\" from a client\n", buf);
+ } else if (pds[j].out_flags & PR_POLL_ERR) {
+ fprintf(stderr, "PR_Poll() indicates that an fd has error\n");
+ exit(1);
+ } else if (pds[j].out_flags & PR_POLL_NVAL) {
+ fprintf(stderr, "PR_Poll() indicates that an fd is invalid\n");
+ exit(1);
+ } else {
+ other_pds[nextIndex] = pds[j];
+ nextIndex++;
+ }
+ }
+
+ PR_ASSERT(retVal == nEvents);
+ /* swap */
+ tmp = pds;
+ pds = other_pds;
+ other_pds = tmp;
+ npds = nextIndex;
+ i += nEvents;
+ }
+ PR_DestroySocketPollFd(socket_poll_fd);
+
+ printf("All tests finished\n");
+ PR_Cleanup();
+ return 0;
+}
+
+
+#endif /* ifdef WINCE */
diff --git a/nsprpub/pr/tests/prpollml.c b/nsprpub/pr/tests/prpollml.c
new file mode 100644
index 0000000000..118d3674df
--- /dev/null
+++ b/nsprpub/pr/tests/prpollml.c
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This test exercises the code that allocates and frees the syspoll_list
+ * array of PRThread in the pthreads version. This test is intended to be
+ * run under Purify to verify that there is no memory leak.
+ */
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define POLL_DESC_COUNT 256 /* This should be greater than the
+ * STACK_POLL_DESC_COUNT macro in
+ * ptio.c to cause syspoll_list to
+ * be created. */
+
+static PRPollDesc pd[POLL_DESC_COUNT];
+
+static void Test(void)
+{
+ int i;
+ PRInt32 rv;
+ PRIntervalTime timeout;
+
+ timeout = PR_MillisecondsToInterval(10);
+ /* cause syspoll_list to grow */
+ for (i = 1; i <= POLL_DESC_COUNT; i++) {
+ rv = PR_Poll(pd, i, timeout);
+ if (rv != 0) {
+ fprintf(stderr,
+ "PR_Poll should time out but returns %d (%d, %d)\n",
+ (int) rv, (int) PR_GetError(), (int) PR_GetOSError());
+ exit(1);
+ }
+ }
+ /* syspoll_list should be large enough for all these */
+ for (i = POLL_DESC_COUNT; i >= 1; i--) {
+ rv = PR_Poll(pd, i, timeout);
+ if (rv != 0) {
+ fprintf(stderr, "PR_Poll should time out but returns %d\n",
+ (int) rv);
+ exit(1);
+ }
+ }
+}
+
+static void ThreadFunc(void *arg)
+{
+ Test();
+}
+
+int main(int argc, char **argv)
+{
+ int i;
+ PRThread *thread;
+ PRFileDesc *sock;
+ PRNetAddr addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.inet.family = PR_AF_INET;
+ addr.inet.port = PR_htons(0);
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ for (i = 0; i < POLL_DESC_COUNT; i++) {
+ sock = PR_NewTCPSocket();
+ if (sock == NULL) {
+ fprintf(stderr, "PR_NewTCPSocket failed (%d, %d)\n",
+ (int) PR_GetError(), (int) PR_GetOSError());
+ fprintf(stderr, "Ensure the per process file descriptor limit "
+ "is greater than %d.", POLL_DESC_COUNT);
+ exit(1);
+ }
+ if (PR_Bind(sock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_Bind failed (%d, %d)\n",
+ (int) PR_GetError(), (int) PR_GetOSError());
+ exit(1);
+ }
+ if (PR_Listen(sock, 5) == PR_FAILURE) {
+ fprintf(stderr, "PR_Listen failed (%d, %d)\n",
+ (int) PR_GetError(), (int) PR_GetOSError());
+ exit(1);
+ }
+
+ pd[i].fd = sock;
+ pd[i].in_flags = PR_POLL_READ;
+ }
+
+ /* first run the test on the primordial thread */
+ Test();
+
+ /* then run the test on all three kinds of threads */
+ thread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, NULL,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (NULL == thread) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+ if (PR_JoinThread(thread) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+ thread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (NULL == thread) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+ if (PR_JoinThread(thread) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+ thread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_BOUND_THREAD, PR_JOINABLE_THREAD, 0);
+ if (NULL == thread) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+ if (PR_JoinThread(thread) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+ for (i = 0; i < POLL_DESC_COUNT; i++) {
+ if (PR_Close(pd[i].fd) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ }
+ PR_Cleanup();
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/prselect.c b/nsprpub/pr/tests/prselect.c
new file mode 100644
index 0000000000..2a81100e87
--- /dev/null
+++ b/nsprpub/pr/tests/prselect.c
@@ -0,0 +1,368 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+** 1997 - Netscape Communications Corporation
+**
+** Name: prselect_err.c
+**
+** Description: tests PR_Select with sockets Error condition functions.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+#include "prttools.h"
+
+
+#include "prinit.h"
+#include "prio.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prerror.h"
+#include "prnetdb.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/***********************************************************************
+** PRIVATE FUNCTION: Test_Result
+** DESCRIPTION: Used in conjunction with the regress tool, prints out the
+** status of the test case.
+** INPUTS: PASS/FAIL
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+**
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM: Determine what the status is and print accordingly.
+**
+***********************************************************************/
+
+
+static Test_Result (int result)
+{
+ if (result == PASS) {
+ printf ("PASS\n");
+ }
+ else {
+ printf ("FAIL\n");
+ }
+}
+
+static void
+clientThreadFunc(void *arg)
+{
+ PRUint16 port = (PRUint16) arg;
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ char buf[128];
+ int i;
+
+ addr.inet.family = AF_INET;
+ addr.inet.port = PR_htons(port);
+ addr.inet.ip = PR_htonl(INADDR_LOOPBACK);
+ PR_snprintf(buf, sizeof(buf), "%hu", port);
+
+ for (i = 0; i < 5; i++) {
+ sock = PR_NewTCPSocket();
+ PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
+ PR_Write(sock, buf, sizeof(buf));
+ PR_Close(sock);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1, *listenSock2;
+ PRFileDesc *badFD;
+ PRFileDesc *fds0[10], *fds1[10], **fds, **other_fds;
+ PRIntn nfds;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ PR_fd_set readFdSet;
+ char buf[128];
+ PRThread *clientThread;
+ PRInt32 retVal;
+ PRIntn i, j;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("This program tests PR_Select with sockets. Timeout, error\n");
+ printf("reporting, and normal operation are tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ if (!debug_mode) {
+ Test_Result(FAIL);
+ }
+ exit(1);
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ if (!debug_mode) {
+ Test_Result(FAIL);
+ }
+ exit(1);
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ if (!debug_mode) {
+ Test_Result(FAIL);
+ }
+ exit(1);
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ if (!debug_mode) {
+ Test_Result(FAIL);
+ }
+ exit(1);
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ if (!debug_mode) {
+ Test_Result(FAIL);
+ }
+ exit(1);
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ if (!debug_mode) {
+ Test_Result(FAIL);
+ }
+ exit(1);
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ if (!debug_mode) {
+ Test_Result(FAIL);
+ }
+ exit(1);
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ if (!debug_mode) {
+ Test_Result(FAIL);
+ }
+ exit(1);
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ printf("%s", buf);
+
+ /* Set up the fd set */
+ PR_FD_ZERO(&readFdSet);
+ PR_FD_SET(listenSock1, &readFdSet);
+ PR_FD_SET(listenSock2, &readFdSet);
+
+ /* Testing timeout */
+ if (debug_mode) {
+ printf("PR_Select should time out in 5 seconds\n");
+ }
+ retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL,
+ PR_SecondsToInterval(5));
+ if (retVal != 0) {
+ PR_snprintf(buf, sizeof(buf),
+ "PR_Select should time out and return 0, but it returns %ld\n",
+ retVal);
+ fprintf(stderr, "%s", buf);
+ if (retVal == -1) {
+ fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(),
+ PR_GetOSError());
+ if (!debug_mode) {
+ Test_Result(FAIL);
+ }
+ }
+ exit(1);
+ }
+ if (debug_mode) {
+ printf("PR_Select timed out. Test passed.\n\n");
+ }
+ else {
+ Test_Result(PASS);
+ }
+
+ /* Testing bad fd */
+ printf("PR_Select should detect a bad file descriptor\n");
+ if ((badFD = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a TCP socket\n");
+ exit(1);
+ }
+
+ PR_FD_SET(listenSock1, &readFdSet);
+ PR_FD_SET(listenSock2, &readFdSet);
+ PR_FD_SET(badFD, &readFdSet);
+ PR_Close(badFD); /* make the fd bad */
+ retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (retVal != -1 || PR_GetError() != PR_BAD_DESCRIPTOR_ERROR) {
+ fprintf(stderr, "Failed to detect the bad fd: "
+ "PR_Select returns %d\n", retVal);
+ if (retVal == -1) {
+ fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(),
+ PR_GetOSError());
+ }
+ exit(1);
+ }
+ printf("PR_Select detected a bad fd. Test passed.\n\n");
+ PR_FD_CLR(badFD, &readFdSet);
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort1,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ exit(1);
+ }
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort2,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ exit(1);
+ }
+
+ printf("Two client threads are created. Each of them will\n");
+ printf("send data to one of the two ports the server is listening on.\n");
+ printf("The data they send is the port number. Each of them send\n");
+ printf("the data five times, so you should see ten lines below,\n");
+ printf("interleaved in an arbitrary order.\n");
+
+ /* set up the fd array */
+ fds = fds0;
+ other_fds = fds1;
+ fds[0] = listenSock1;
+ fds[1] = listenSock2;
+ nfds = 2;
+ PR_FD_SET(listenSock1, &readFdSet);
+ PR_FD_SET(listenSock2, &readFdSet);
+
+ /* 20 events total */
+ i = 0;
+ while (i < 20) {
+ PRFileDesc **tmp;
+ int nextIndex;
+ int nEvents = 0;
+
+ retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL,
+ PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(retVal != 0); /* no timeout */
+ if (retVal == -1) {
+ fprintf(stderr, "PR_Select failed (%d, %d)\n", PR_GetError(),
+ PR_GetOSError());
+ exit(1);
+ }
+
+ nextIndex = 2;
+ /* the two listening sockets */
+ for (j = 0; j < 2; j++) {
+ other_fds[j] = fds[j];
+ if (PR_FD_ISSET(fds[j], &readFdSet)) {
+ PRFileDesc *sock;
+
+ nEvents++;
+ sock = PR_Accept(fds[j], NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (sock == NULL) {
+ fprintf(stderr, "PR_Accept() failed\n");
+ exit(1);
+ }
+ other_fds[nextIndex] = sock;
+ PR_FD_SET(sock, &readFdSet);
+ nextIndex++;
+ }
+ PR_FD_SET(fds[j], &readFdSet);
+ }
+
+ for (j = 2; j < nfds; j++) {
+ if (PR_FD_ISSET(fds[j], &readFdSet)) {
+ PRInt32 nBytes;
+
+ PR_FD_CLR(fds[j], &readFdSet);
+ nEvents++;
+ nBytes = PR_Read(fds[j], buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read() failed\n");
+ exit(1);
+ }
+ /* Just to be safe */
+ buf[127] = '\0';
+ PR_Close(fds[j]);
+ printf("The server received \"%s\" from a client\n", buf);
+ } else {
+ PR_FD_SET(fds[j], &readFdSet);
+ other_fds[nextIndex] = fds[j];
+ nextIndex++;
+ }
+ }
+
+ PR_ASSERT(retVal == nEvents);
+ /* swap */
+ tmp = fds;
+ fds = other_fds;
+ other_fds = tmp;
+ nfds = nextIndex;
+ i += nEvents;
+ }
+
+ printf("All tests finished\n");
+ PR_Cleanup();
+ return 0;
+}
diff --git a/nsprpub/pr/tests/prttools.h b/nsprpub/pr/tests/prttools.h
new file mode 100644
index 0000000000..e3cac5daa9
--- /dev/null
+++ b/nsprpub/pr/tests/prttools.h
@@ -0,0 +1,11 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Used in Regress Tool */
+#define NOSTATUS 2
+#define PASS 1
+#define FAIL 0
+
+PRIntn debug_mode=0;
diff --git a/nsprpub/pr/tests/pushtop.c b/nsprpub/pr/tests/pushtop.c
new file mode 100644
index 0000000000..621a7c7248
--- /dev/null
+++ b/nsprpub/pr/tests/pushtop.c
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* A regression test for bug 794316 */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "prio.h"
+
+static PRIOMethods dummyMethods;
+
+int main()
+{
+ PRDescIdentity topId, middleId, bottomId;
+ PRFileDesc *top, *middle, *bottom;
+ PRFileDesc *fd;
+
+ topId = PR_GetUniqueIdentity("top");
+ middleId = PR_GetUniqueIdentity("middle");
+ bottomId = PR_GetUniqueIdentity("bottom");
+
+ top = PR_CreateIOLayerStub(topId, &dummyMethods);
+ middle = PR_CreateIOLayerStub(middleId, &dummyMethods);
+ bottom = PR_CreateIOLayerStub(bottomId, &dummyMethods);
+
+ fd = bottom;
+ PR_PushIOLayer(fd, PR_TOP_IO_LAYER, middle);
+ PR_PushIOLayer(fd, PR_TOP_IO_LAYER, top);
+
+ top = fd;
+ middle = top->lower;
+ bottom = middle->lower;
+
+ /* Verify that the higher pointers are correct. */
+ if (middle->higher != top) {
+ fprintf(stderr, "middle->higher is wrong\n");
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+ if (bottom->higher != middle) {
+ fprintf(stderr, "bottom->higher is wrong\n");
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ top = PR_PopIOLayer(fd, topId);
+ top->dtor(top);
+
+ middle = fd;
+ bottom = middle->lower;
+
+ /* Verify that the higher pointer is correct. */
+ if (bottom->higher != middle) {
+ fprintf(stderr, "bottom->higher is wrong\n");
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ middle = PR_PopIOLayer(fd, middleId);
+ middle->dtor(middle);
+ if (fd->identity != bottomId) {
+ fprintf(stderr, "The bottom layer has the wrong identity\n");
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+ fd->dtor(fd);
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/randseed.c b/nsprpub/pr/tests/randseed.c
new file mode 100644
index 0000000000..2ca14c7af0
--- /dev/null
+++ b/nsprpub/pr/tests/randseed.c
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: rngseed.c
+** Description:
+** Test NSPR's Random Number Seed generator
+**
+** Initial test: Just make sure it outputs some data.
+**
+** ... more? ... check some iterations to ensure it is random (no dupes)
+** ... more? ... histogram distribution of random numbers
+*/
+
+#include "plgetopt.h"
+#include "nspr.h"
+#include "prrng.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+** Test harness infrastructure
+*/
+PRLogModuleInfo *lm;
+PRLogModuleLevel msgLevel = PR_LOG_NONE;
+PRIntn debug = 0;
+PRUint32 failed_already = 0;
+/* end Test harness infrastructure */
+
+PRIntn optRandCount = 30;
+char buf[40];
+PRSize bufSize = sizeof(buf);
+PRSize rSize;
+PRIntn i;
+
+/*
+** Emit help text for this test
+*/
+static void Help( void )
+{
+ printf("Template: Help(): display your help message(s) here");
+ exit(1);
+} /* end Help() */
+
+static void PrintRand( void *buf, PRIntn size )
+{
+ PRUint32 *rp = buf;
+ PRIntn i;
+
+ printf("%4.4d--\n", size );
+ while (size > 0 ) {
+ switch( size ) {
+ case 1 :
+ printf("%2.2X\n", *(rp++) );
+ size -= 4;
+ break;
+ case 2 :
+ printf("%4.4X\n", *(rp++) );
+ size -= 4;
+ break;
+ case 3 :
+ printf("%6.6X\n", *(rp++) );
+ size -= 4;
+ break;
+ default:
+ while ( size >= 4) {
+ PRIntn i = 3;
+ do {
+ printf("%8.8X ", *(rp++) );
+ size -= 4;
+ } while( i-- );
+ i = 3;
+ printf("\n");
+ }
+ break;
+ } /* end switch() */
+ } /* end while() */
+} /* end PrintRand() */
+
+
+int main(int argc, char **argv)
+{
+ {
+ /*
+ ** Get command line options
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hdv");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug */
+ debug = 1;
+ msgLevel = PR_LOG_ERROR;
+ break;
+ case 'v': /* verbose mode */
+ msgLevel = PR_LOG_DEBUG;
+ break;
+ case 'h': /* help message */
+ Help();
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ }
+
+ lm = PR_NewLogModule("Test"); /* Initialize logging */
+ for ( i = 0; i < optRandCount ; i++ ) {
+ memset( buf, 0, bufSize );
+ rSize = PR_GetRandomNoise( buf, bufSize );
+ if (!rSize) {
+ fprintf(stderr, "Not implemented\n" );
+ failed_already = PR_TRUE;
+ break;
+ }
+ if (debug) {
+ PrintRand( buf, rSize );
+ }
+ }
+
+ if (debug) {
+ printf("%s\n", (failed_already)? "FAIL" : "PASS");
+ }
+ return( (failed_already == PR_TRUE )? 1 : 0 );
+} /* main() */
+/* end template.c */
+
diff --git a/nsprpub/pr/tests/ranfile.c b/nsprpub/pr/tests/ranfile.c
new file mode 100644
index 0000000000..a6dcc4ac41
--- /dev/null
+++ b/nsprpub/pr/tests/ranfile.c
@@ -0,0 +1,433 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Contact: AOF<freier@netscape.com>
+**
+** Name: ranfile.c
+**
+** Description: Test to hammer on various components of NSPR
+** Modification History:
+** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prthread.h"
+#include "prlock.h"
+#include "prcvar.h"
+#include "prmem.h"
+#include "prinrval.h"
+#include "prio.h"
+
+#include <string.h>
+#include <stdio.h>
+
+static PRIntn debug_mode = 0;
+static PRIntn failed_already=0;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+typedef enum {sg_go, sg_stop, sg_done} Action;
+typedef enum {sg_okay, sg_open, sg_close, sg_delete, sg_write, sg_seek} Problem;
+
+typedef struct Hammer_s {
+ PRLock *ml;
+ PRCondVar *cv;
+ PRUint32 id;
+ PRUint32 limit;
+ PRUint32 writes;
+ PRThread *thread;
+ PRIntervalTime timein;
+ Action action;
+ Problem problem;
+} Hammer_t;
+
+#define DEFAULT_LIMIT 10
+#define DEFAULT_THREADS 2
+#define DEFAULT_LOOPS 1
+
+static PRInt32 pageSize = 1024;
+static const char* baseName = "./";
+static const char *programName = "Random File";
+
+/***********************************************************************
+** PRIVATE FUNCTION: RandomNum
+** DESCRIPTION:
+** Generate a pseudo-random number
+** INPUTS: None
+** OUTPUTS: None
+** RETURN: A pseudo-random unsigned number, 32-bits wide
+** SIDE EFFECTS:
+** Updates random seed (a static)
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM:
+** Uses the current interval timer value, promoted to a 64 bit
+** float as a multiplier for a static residue (which begins
+** as an uninitialized variable). The result is bits [16..48)
+** of the product. Seed is then updated with the return value
+** promoted to a float-64.
+***********************************************************************/
+static PRUint32 RandomNum(void)
+{
+ PRUint32 rv;
+ PRUint64 shift;
+ static PRFloat64 seed = 0x58a9382; /* Just make sure it isn't 0! */
+ PRFloat64 random = seed * (PRFloat64)PR_IntervalNow();
+ LL_USHR(shift, *((PRUint64*)&random), 16);
+ LL_L2UI(rv, shift);
+ seed = (PRFloat64)rv;
+ return rv;
+} /* RandomNum */
+
+/***********************************************************************
+** PRIVATE FUNCTION: Thread
+** DESCRIPTION:
+** Hammer on the file I/O system
+** INPUTS: A pointer to the thread's private data
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+** Creates, accesses and deletes a file
+** RESTRICTIONS:
+** (Currently) must have file create permission in "/usr/tmp".
+** MEMORY: NA
+** ALGORITHM:
+** This function is a root of a thread
+** 1) Creates a (hopefully) unique file in /usr/tmp/
+** 2) Writes a zero to a random number of sequential pages
+** 3) Closes the file
+** 4) Reopens the file
+** 5) Seeks to a random page within the file
+** 6) Writes a one byte on that page
+** 7) Repeat steps [5..6] for each page in the file
+** 8) Close and delete the file
+** 9) Repeat steps [1..8] until told to stop
+** 10) Notify complete and return
+***********************************************************************/
+static void PR_CALLBACK Thread(void *arg)
+{
+ PRUint32 index;
+ char filename[30];
+ const char zero = 0;
+ PRFileDesc *file = NULL;
+ PRStatus rv = PR_SUCCESS;
+ Hammer_t *cd = (Hammer_t*)arg;
+
+ (void)sprintf(filename, "%ssg%04ld.dat", baseName, cd->id);
+
+ if (debug_mode) {
+ printf("Starting work on %s\n", filename);
+ }
+
+ while (PR_TRUE)
+ {
+ PRUint32 bytes;
+ PRUint32 minor = (RandomNum() % cd->limit) + 1;
+ PRUint32 random = (RandomNum() % cd->limit) + 1;
+ PRUint32 pages = (RandomNum() % cd->limit) + 10;
+ while (minor-- > 0)
+ {
+ cd->problem = sg_okay;
+ if (cd->action != sg_go) {
+ goto finished;
+ }
+ cd->problem = sg_open;
+ file = PR_Open(filename, PR_RDWR|PR_CREATE_FILE, 0666);
+ if (file == NULL) {
+ goto finished;
+ }
+ for (index = 0; index < pages; index++)
+ {
+ cd->problem = sg_okay;
+ if (cd->action != sg_go) {
+ goto close;
+ }
+ cd->problem = sg_seek;
+ bytes = PR_Seek(file, pageSize * index, PR_SEEK_SET);
+ if (bytes != pageSize * index) {
+ goto close;
+ }
+ cd->problem = sg_write;
+ bytes = PR_Write(file, &zero, sizeof(zero));
+ if (bytes <= 0) {
+ goto close;
+ }
+ cd->writes += 1;
+ }
+ cd->problem = sg_close;
+ rv = PR_Close(file);
+ if (rv != PR_SUCCESS) {
+ goto purge;
+ }
+
+ cd->problem = sg_okay;
+ if (cd->action != sg_go) {
+ goto purge;
+ }
+
+ cd->problem = sg_open;
+ file = PR_Open(filename, PR_RDWR, 0666);
+ for (index = 0; index < pages; index++)
+ {
+ cd->problem = sg_okay;
+ if (cd->action != sg_go) {
+ goto close;
+ }
+ cd->problem = sg_seek;
+ bytes = PR_Seek(file, pageSize * index, PR_SEEK_SET);
+ if (bytes != pageSize * index) {
+ goto close;
+ }
+ cd->problem = sg_write;
+ bytes = PR_Write(file, &zero, sizeof(zero));
+ if (bytes <= 0) {
+ goto close;
+ }
+ cd->writes += 1;
+ random = (random + 511) % pages;
+ }
+ cd->problem = sg_close;
+ rv = PR_Close(file);
+ if (rv != PR_SUCCESS) {
+ goto purge;
+ }
+ cd->problem = sg_delete;
+ rv = PR_Delete(filename);
+ if (rv != PR_SUCCESS) {
+ goto finished;
+ }
+ }
+ }
+
+close:
+ (void)PR_Close(file);
+purge:
+ (void)PR_Delete(filename);
+finished:
+ PR_Lock(cd->ml);
+ cd->action = sg_done;
+ PR_NotifyCondVar(cd->cv);
+ PR_Unlock(cd->ml);
+
+ if (debug_mode) {
+ printf("Ending work on %s\n", filename);
+ }
+
+ return;
+} /* Thread */
+
+static Hammer_t hammer[100];
+static PRCondVar *cv;
+/***********************************************************************
+** PRIVATE FUNCTION: main
+** DESCRIPTION:
+** Hammer on the file I/O system
+** INPUTS: The usual argc and argv
+** argv[0] - program name (not used)
+** argv[1] - the number of times to execute the major loop
+** argv[2] - the number of threads to toss into the batch
+** argv[3] - the clipping number applied to randoms
+** default values: loops = 2, threads = 10, limit = 57
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+** Creates, accesses and deletes lots of files
+** RESTRICTIONS:
+** (Currently) must have file create permission in "/usr/tmp".
+** MEMORY: NA
+** ALGORITHM:
+** 1) Fork a "Thread()"
+** 2) Wait for 'interleave' seconds
+** 3) For [0..'threads') repeat [1..2]
+** 4) Mark all objects to stop
+** 5) Collect the threads, accumulating the results
+** 6) For [0..'loops') repeat [1..5]
+** 7) Print accumulated results and exit
+**
+** Characteristic output (from IRIX)
+** Random File: Using loops = 2, threads = 10, limit = 57
+** Random File: [min [avg] max] writes/sec average
+***********************************************************************/
+int main(int argc, char **argv)
+{
+ PRLock *ml;
+ PRUint32 id = 0;
+ int active, poll;
+ PRIntervalTime interleave;
+ PRIntervalTime duration = 0;
+ int limit = 0, loops = 0, threads = 0, times;
+ PRUint32 writes, writesMin = 0x7fffffff, writesTot = 0, durationTot = 0, writesMax = 0;
+
+ const char *where[] = {"okay", "open", "close", "delete", "write", "seek"};
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:t:i:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'G': /* global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'l': /* limiting number */
+ limit = atoi(opt->value);
+ break;
+ case 't': /* number of threads */
+ threads = atoi(opt->value);
+ break;
+ case 'i': /* iteration counter */
+ loops = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ interleave = PR_SecondsToInterval(10);
+
+ ml = PR_NewLock();
+ cv = PR_NewCondVar(ml);
+
+ if (loops == 0) {
+ loops = DEFAULT_LOOPS;
+ }
+ if (limit == 0) {
+ limit = DEFAULT_LIMIT;
+ }
+ if (threads == 0) {
+ threads = DEFAULT_THREADS;
+ }
+
+ if (debug_mode) printf(
+ "%s: Using loops = %d, threads = %d, limit = %d and %s threads\n",
+ programName, loops, threads, limit,
+ (thread_scope == PR_LOCAL_THREAD) ? "LOCAL" : "GLOBAL");
+
+ for (times = 0; times < loops; ++times)
+ {
+ if (debug_mode) {
+ printf("%s: Setting concurrency level to %d\n", programName, times + 1);
+ }
+ PR_SetConcurrency(times + 1);
+ for (active = 0; active < threads; active++)
+ {
+ hammer[active].ml = ml;
+ hammer[active].cv = cv;
+ hammer[active].id = id++;
+ hammer[active].writes = 0;
+ hammer[active].action = sg_go;
+ hammer[active].problem = sg_okay;
+ hammer[active].limit = (RandomNum() % limit) + 1;
+ hammer[active].timein = PR_IntervalNow();
+ hammer[active].thread = PR_CreateThread(
+ PR_USER_THREAD, Thread, &hammer[active],
+ PR_GetThreadPriority(PR_GetCurrentThread()),
+ thread_scope, PR_JOINABLE_THREAD, 0);
+
+ PR_Lock(ml);
+ PR_WaitCondVar(cv, interleave); /* start new ones slowly */
+ PR_Unlock(ml);
+ }
+
+ /*
+ * The last thread started has had the opportunity to run for
+ * 'interleave' seconds. Now gather them all back in.
+ */
+ PR_Lock(ml);
+ for (poll = 0; poll < threads; poll++)
+ {
+ if (hammer[poll].action == sg_go) { /* don't overwrite done */
+ hammer[poll].action = sg_stop; /* ask him to stop */
+ }
+ }
+ PR_Unlock(ml);
+
+ while (active > 0)
+ {
+ for (poll = 0; poll < threads; poll++)
+ {
+ PR_Lock(ml);
+ while (hammer[poll].action < sg_done) {
+ PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(ml);
+
+ active -= 1; /* this is another one down */
+ (void)PR_JoinThread(hammer[poll].thread);
+ hammer[poll].thread = NULL;
+ if (hammer[poll].problem == sg_okay)
+ {
+ duration = PR_IntervalToMilliseconds(
+ PR_IntervalNow() - hammer[poll].timein);
+ writes = hammer[poll].writes * 1000 / duration;
+ if (writes < writesMin) {
+ writesMin = writes;
+ }
+ if (writes > writesMax) {
+ writesMax = writes;
+ }
+ writesTot += hammer[poll].writes;
+ durationTot += duration;
+ }
+ else if (debug_mode) printf(
+ "%s: test failed %s after %ld seconds\n",
+ programName, where[hammer[poll].problem], duration);
+ else {
+ failed_already=1;
+ }
+ }
+ }
+ }
+ if (debug_mode) printf(
+ "%s: [%ld [%ld] %ld] writes/sec average\n",
+ programName, writesMin, writesTot * 1000 / durationTot, writesMax);
+
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+
+ if (failed_already)
+ {
+ printf("FAIL\n");
+ return 1;
+ }
+ else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+} /* main */
diff --git a/nsprpub/pr/tests/reinit.c b/nsprpub/pr/tests/reinit.c
new file mode 100644
index 0000000000..b4dfa4d9b9
--- /dev/null
+++ b/nsprpub/pr/tests/reinit.c
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* This test verifies that NSPR can be cleaned up and reinitialized. */
+
+#include "nspr.h"
+#include <stdio.h>
+
+int main()
+{
+ PRStatus rv;
+
+ fprintf(stderr, "Init 1\n");
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ fprintf(stderr, "Cleanup 1\n");
+ rv = PR_Cleanup();
+ if (rv != PR_SUCCESS) {
+ fprintf(stderr, "FAIL\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Init 2\n");
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ fprintf(stderr, "Cleanup 2\n");
+ rv = PR_Cleanup();
+ if (rv != PR_SUCCESS) {
+ fprintf(stderr, "FAIL\n");
+ return 1;
+ }
+
+ fprintf(stderr, "PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/rmdir.c b/nsprpub/pr/tests/rmdir.c
new file mode 100644
index 0000000000..c1400d7287
--- /dev/null
+++ b/nsprpub/pr/tests/rmdir.c
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: rmdir.c
+** Description: Demonstrate bugzilla 80884.
+**
+** after fix to unix_errors.c, message should report correct
+** failure of PR_Rmdir().
+**
+**
+**
+*/
+
+#include <prio.h>
+#include <stdio.h>
+#include <prerror.h>
+#include <prlog.h>
+#include "plgetopt.h"
+
+#define DIRNAME "xxxBug80884/"
+#define FILENAME "file80883"
+
+PRBool failed_already = PR_FALSE;
+PRBool debug_mode = PR_FALSE;
+
+PRLogModuleInfo *lm;
+
+/*
+** Help() -- print Usage information
+*/
+static void Help( void ) {
+ fprintf(stderr, "template usage:\n"
+ "\t-d debug mode\n"
+ );
+} /* --- end Help() */
+
+int main(int argc, char **argv)
+{
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dh");
+ PRFileDesc* fd;
+ PRErrorCode err;
+
+ /* parse command line options */
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option) {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'h':
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ lm = PR_NewLogModule( "testcase" );
+
+ (void) PR_MkDir( DIRNAME, 0777);
+ fd = PR_Open( DIRNAME FILENAME, PR_CREATE_FILE|PR_RDWR, 0666);
+ if (fd == 0) {
+ PRErrorCode err = PR_GetError();
+ fprintf(stderr, "create file fails: %d: %s\n", err,
+ PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT));
+ failed_already = PR_TRUE;
+ goto Finished;
+ }
+
+ PR_Close(fd);
+
+ if (PR_RmDir( DIRNAME ) == PR_SUCCESS) {
+ fprintf(stderr, "remove directory succeeds\n");
+ failed_already = PR_TRUE;
+ goto Finished;
+ }
+
+ err = PR_GetError();
+ fprintf(stderr, "remove directory fails with: %d: %s\n", err,
+ PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT));
+
+ (void) PR_Delete( DIRNAME FILENAME);
+ (void) PR_RmDir( DIRNAME );
+
+ return 0;
+
+Finished:
+ if ( debug_mode ) {
+ printf("%s\n", ( failed_already ) ? "FAILED" : "PASS" );
+ }
+ return( (failed_already)? 1 : 0 );
+} /* --- end main() */
+/* --- end template.c */
diff --git a/nsprpub/pr/tests/runtests.pl b/nsprpub/pr/tests/runtests.pl
new file mode 100755
index 0000000000..326c08f862
--- /dev/null
+++ b/nsprpub/pr/tests/runtests.pl
@@ -0,0 +1,371 @@
+#!/usr/bin/perl
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+use POSIX qw(:sys_wait_h);
+use POSIX qw(setsid);
+use FileHandle;
+
+# Constants
+$WINOS = "MSWin32";
+
+$osname = $^O;
+
+use Cwd;
+if ($osname =~ $WINOS) {
+ # Windows
+ require Win32::Process;
+ require Win32;
+}
+
+# Get environment variables.
+$output_file = $ENV{NSPR_TEST_LOGFILE};
+$timeout = $ENV{TEST_TIMEOUT};
+
+$timeout = 0 if (!defined($timeout));
+
+sub getTime {
+ ($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear, $daylightSavings) = localtime();
+
+ $year = 1900 + $yearOffset;
+
+ $theTime = sprintf("%04d-%02d-%02d %02d:%02d:%02d",$year,$month,$dayOfMonth,$hour,$minute,$second);
+ return $theTime;
+}
+
+sub open_log {
+
+ if (!defined($output_file)) {
+ print "No output file.\n";
+ # null device
+ if ($osname =~ $WINOS) {
+ $output_file = "nul";
+ } else {
+ $output_file = "/dev/null";
+ }
+ }
+
+ # use STDOUT for OF (to print summary of test results)
+ open(OF, ">&STDOUT") or die "Can't reuse STDOUT for OF\n";
+ OF->autoflush;
+ # reassign STDOUT to $output_file (to print details of test results)
+ open(STDOUT, ">$output_file") or die "Can't open file $output_file for STDOUT\n";
+ STDOUT->autoflush;
+ # redirect STDERR to STDOUT
+ open(STDERR, ">&STDOUT") or die "Can't redirect STDERR to STDOUT\n";
+ STDERR->autoflush;
+
+ # Print header test in summary
+ $now = getTime;
+ print OF "\nNSPR Test Results - tests\n";
+ print OF "\nBEGIN\t\t\t$now\n";
+ print OF "NSPR_TEST_LOGFILE\t$output_file\n";
+ print OF "TEST_TIMEOUT\t$timeout\n\n";
+ print OF "\nTest\t\t\tResult\n\n";
+}
+
+sub close_log {
+ # end of test marker in summary
+ $now = getTime;
+ print OF "END\t\t\t$now\n";
+
+ close(OF) or die "Can't close file OF\n";
+ close(STDERR) or die "Can't close STDERR\n";
+ close(STDOUT) or die "Can't close STDOUT\n";
+}
+
+sub print_begin {
+$lprog = shift;
+
+ # Summary output
+ print OF "$prog";
+ # Full output
+ $now = getTime;
+ print "BEGIN TEST: $lprog ($now)\n\n";
+}
+
+sub print_end {
+($lprog, $exit_status, $exit_signal, $exit_core) = @_;
+
+ if (($exit_status == 0) && ($exit_signal == 0) && ($exit_core == 0)) {
+ $str_status = "Passed";
+ } else {
+ $str_status = "FAILED";
+ }
+ if ($exit_signal != 0) {
+ $str_signal = " - signal $exit_signal";
+ } else {
+ $str_signal = "";
+ }
+ if ($exit_core != 0) {
+ $str_core = " - core dumped";
+ } else {
+ $str_core = "";
+ }
+ $now = getTime;
+ # Full output
+ print "\nEND TEST: $lprog ($now)\n";
+ print "TEST STATUS: $lprog = $str_status (exit status " . $exit_status . $str_signal . $str_core . ")\n";
+ print "--------------------------------------------------\n\n";
+ # Summary output
+ print OF "\t\t\t$str_status\n";
+}
+
+sub ux_start_prog {
+# parameters:
+$lprog = shift; # command to run
+
+ # Create a process group for the child
+ # so we can kill all of it if needed
+ setsid or die "setsid failed: $!";
+ # Start test program
+ exec("./$lprog");
+ # We should not be here unless exec failed.
+ print "Faild to exec $lprog";
+ exit 1 << 8;
+}
+
+sub ux_wait_timeout {
+# parameters:
+$lpid = shift; # child process id
+$ltimeout = shift; # timeout
+
+ if ($ltimeout == 0) {
+ # No timeout: use blocking wait
+ $ret = waitpid($lpid,0);
+ # Exit and don't kill
+ $lstatus = $?;
+ $ltimeout = -1;
+ } else {
+ while ($ltimeout > 0) {
+ # Check status of child using non blocking wait
+ $ret = waitpid($lpid, WNOHANG);
+ if ($ret == 0) {
+ # Child still running
+ # print "Time left=$ltimeout\n";
+ sleep 1;
+ $ltimeout--;
+ } else {
+ # Child has ended
+ $lstatus = $?;
+ # Exit the wait loop and don't kill
+ $ltimeout = -1;
+ }
+ }
+ }
+
+ if ($ltimeout == 0) {
+ # we ran all the timeout: it's time to kill the child
+ print "Timeout ! Kill child process $lpid\n";
+ # Kill the child process and group
+ kill(-9,$lpid);
+ $lstatus = 9;
+ }
+
+ return $lstatus;
+}
+
+sub ux_test_prog {
+# parameters:
+$prog = shift; # Program to test
+
+ $child_pid = fork;
+ if ($child_pid == 0) {
+ # we are in the child process
+ print_begin($prog);
+ ux_start_prog($prog);
+ } else {
+ # we are in the parent process
+ $status = ux_wait_timeout($child_pid,$timeout);
+ # See Perlvar for documentation of $?
+ # exit status = $status >> 8
+ # exit signal = $status & 127 (no signal = 0)
+ # core dump = $status & 128 (no core = 0)
+ print_end($prog, $status >> 8, $status & 127, $status & 128);
+ }
+
+ return $status;
+}
+
+sub win_path {
+$lpath = shift;
+
+ # MSYS drive letter = /c/ -> c:/
+ $lpath =~ s/^\/(\w)\//$1:\//;
+ # Cygwin drive letter = /cygdrive/c/ -> c:/
+ $lpath =~ s/^\/cygdrive\/(\w)\//$1:\//;
+ # replace / with \\
+ $lpath =~ s/\//\\\\/g;
+
+ return $lpath;
+}
+
+sub win_ErrorReport{
+ print Win32::FormatMessage( Win32::GetLastError() );
+}
+
+sub win_test_prog {
+# parameters:
+$prog = shift; # Program to test
+
+ $status = 1;
+ $curdir = getcwd;
+ $curdir = win_path($curdir);
+ $prog_path = "$curdir\\$prog.exe";
+
+ print_begin($prog);
+
+ Win32::Process::Create($ProcessObj,
+ "$prog_path",
+ "$prog",
+ 0,
+ NORMAL_PRIORITY_CLASS,
+ ".")|| die win_ErrorReport();
+ $retwait = $ProcessObj->Wait($timeout * 1000);
+
+ if ( $retwait == 0) {
+ # the prog didn't finish after the timeout: kill
+ $ProcessObj->Kill($status);
+ print "Timeout ! Process killed with exit status $status\n";
+ } else {
+ # the prog finished before the timeout: get exit status
+ $ProcessObj->GetExitCode($status);
+ }
+ # There is no signal, no core on Windows
+ print_end($prog, $status, 0, 0);
+
+ return $status
+}
+
+# MAIN ---------------
+@progs = (
+"abstract",
+"accept",
+"acceptread",
+"acceptreademu",
+"affinity",
+"alarm",
+"anonfm",
+"atomic",
+"attach",
+"bigfile",
+"cleanup",
+"cltsrv",
+"concur",
+"cvar",
+"cvar2",
+"dlltest",
+"dtoa",
+"errcodes",
+"exit",
+"fdcach",
+"fileio",
+"foreign",
+"formattm",
+"fsync",
+"gethost",
+"getproto",
+"i2l",
+"initclk",
+"inrval",
+"instrumt",
+"intrio",
+"intrupt",
+"io_timeout",
+"ioconthr",
+"join",
+"joinkk",
+"joinku",
+"joinuk",
+"joinuu",
+"layer",
+"lazyinit",
+"libfilename",
+"lltest",
+"lock",
+"lockfile",
+"logfile",
+"logger",
+"many_cv",
+"nameshm1",
+"nblayer",
+"nonblock",
+"ntioto",
+"ntoh",
+"op_2long",
+"op_excl",
+"op_filnf",
+"op_filok",
+"op_nofil",
+"parent",
+"parsetm",
+"peek",
+"perf",
+"pipeping",
+"pipeping2",
+"pipeself",
+"poll_nm",
+"poll_to",
+"pollable",
+"prftest",
+"prfz",
+"primblok",
+"provider",
+"prpollml",
+"pushtop",
+"ranfile",
+"randseed",
+"reinit",
+"rwlocktest",
+"sel_spd",
+"selct_er",
+"selct_nm",
+"selct_to",
+"selintr",
+"sema",
+"semaerr",
+"semaping",
+"sendzlf",
+"server_test",
+"servr_kk",
+"servr_uk",
+"servr_ku",
+"servr_uu",
+"short_thread",
+"sigpipe",
+"socket",
+"sockopt",
+"sockping",
+"sprintf",
+"stack",
+"stdio",
+"str2addr",
+"strod",
+"switch",
+"system",
+"testbit",
+"testfile",
+"threads",
+"timemac",
+"timetest",
+"tpd",
+"udpsrv",
+"vercheck",
+"version",
+"writev",
+"xnotify",
+"zerolen");
+
+open_log;
+
+foreach $current_prog (@progs) {
+ if ($osname =~ $WINOS) {
+ win_test_prog($current_prog);
+ } else {
+ ux_test_prog($current_prog);
+ }
+}
+
+close_log;
diff --git a/nsprpub/pr/tests/runtests.sh b/nsprpub/pr/tests/runtests.sh
new file mode 100755
index 0000000000..8d3144019d
--- /dev/null
+++ b/nsprpub/pr/tests/runtests.sh
@@ -0,0 +1,277 @@
+#!/bin/sh
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if test -z $1
+then
+ echo "usage: $0 <path-to-dist>"
+ exit 1
+fi
+
+cd $1/lib
+ABS_LIB=$PWD
+cd -
+
+export DYLD_LIBRARY_PATH=${ABS_LIB}:${DYLD_LIBRARY_PATH}
+export LD_LIBRARY_PATH=${ABS_LIB}:${LD_LIBRARY_PATH}
+export PATH=${ABS_LIB}:${PATH}
+
+#
+# runtests.sh
+# Bourne shell script for nspr tests
+#
+
+SYSTEM_INFO=`uname -a`
+OS_ARCH=`uname -s`
+
+if [ $OS_ARCH = "Windows_NT" ] || [ $OS_ARCH = "OS/2" ]
+then
+ NULL_DEVICE=nul
+else
+ NULL_DEVICE=/dev/null
+ FILE_D=`ulimit -n`
+ if [ $FILE_D -lt 512 ]
+ then
+ ulimit -n 512
+ fi
+fi
+
+#
+# Irrevelant tests
+#
+#bug1test - used to demonstrate a bug on NT
+#bigfile2 - requires 4Gig file creation. See BugZilla #5451
+#bigfile3 - requires 4Gig file creation. See BugZilla #5451
+#dbmalloc - obsolete; originally for testing debug version of nspr's malloc
+#dbmalloc1 - obsolete; originally for testing debug version of nspr's malloc
+#depend - obsolete; used to test a initial spec for library dependencies
+#dceemu - used to tests special functions in NSPR for DCE emulation
+#ipv6 - IPV6 not in use by NSPR clients
+#mbcs - tests use of multi-byte charset for filenames. See BugZilla #25140
+#io_timeoutk - obsolete; subsumed in io_timeout
+#io_timeoutu - obsolete; subsumed in io_timeout
+#prftest1 - obsolete; subsumed by prftest
+#prftest2 - obsolete; subsumed by prftest
+#prselect - obsolete; PR_Select is obsolete
+#select2 - obsolete; PR_Select is obsolete
+#sem - obsolete; PRSemaphore is obsolete
+#stat - for OS2?
+#suspend - private interfaces PR_SuspendAll, PR_ResumeAll, etc..
+#thruput - needs to be run manually as client/server
+#time - used to measure time with native calls and nspr calls
+#tmoacc - should be run with tmocon
+#tmocon - should be run with tmoacc
+#op_noacc - limited use
+#yield - limited use for PR_Yield
+
+#
+# Tests not run (but should)
+#
+
+#forktest (failed on IRIX)
+#multiwait - fails on Linux 64bit since NSPR v 4.4 from 2004.
+#nbconn - fails on some platforms
+#poll_er - fails on some platforms? limited use?
+#prpoll - the bad-FD test needs to be moved to a different test
+#sleep - specific to OS/2
+#
+# all of the following were disabled in 2019 when reenabling CI tests,
+# because they failed on at least one of the platforms:
+#
+# cltsrv
+# cvar
+# gethost
+# getproto
+# layer
+# logfile
+# nameshm1
+# nblayer
+# nonblock
+# ntioto
+# op_2long
+# parent
+# provider
+# ranfile
+# socket
+# sockopt
+# vercheck
+
+#LOGFILE=${NSPR_TEST_LOGFILE:-$NULL_DEVICE}
+LOGFILE=nspr-test.log
+
+#
+# Tests run on all platforms
+#
+
+TESTS="
+abstract
+accept
+acceptread
+acceptreademu
+affinity
+alarm
+anonfm
+atomic
+attach
+bigfile
+cleanup
+concur
+cvar2
+dlltest
+dtoa
+errcodes
+exit
+fdcach
+fileio
+foreign
+formattm
+fsync
+i2l
+initclk
+inrval
+instrumt
+intrio
+intrupt
+io_timeout
+ioconthr
+join
+joinkk
+joinku
+joinuk
+joinuu
+lazyinit
+libfilename
+lltest
+lock
+lockfile
+logger
+many_cv
+ntoh
+op_excl
+op_filnf
+op_filok
+op_nofil
+parsetm
+peek
+perf
+pipeping
+pipeping2
+pipeself
+poll_nm
+poll_to
+pollable
+prftest
+prfz
+primblok
+prpollml
+pushtop
+randseed
+reinit
+rwlocktest
+sel_spd
+selct_er
+selct_nm
+selct_to
+selintr
+sema
+semaerr
+semaping
+sendzlf
+server_test
+servr_kk
+servr_uk
+servr_ku
+servr_uu
+short_thread
+sigpipe
+sockping
+sprintf
+stack
+stdio
+str2addr
+strod
+switch
+system
+testbit
+testfile
+threads
+timemac
+timetest
+tpd
+udpsrv
+version
+writev
+xnotify
+zerolen"
+
+rval=0
+
+
+#
+# When set, value of the environment variable TEST_TIMEOUT is the maximum
+# time (secs) allowed for a test program beyond which it is terminated.
+# If TEST_TIMEOUT is not set or if it's value is 0, then test programs
+# don't timeout.
+#
+# Running runtests.ksh under MKS toolkit on NT, 95, 98 does not cause
+# timeout detection correctly. For these platforms, do not attempt timeout
+# test. (lth).
+#
+#
+
+OS_PLATFORM=`uname`
+OBJDIR=`basename $PWD`
+printf "\nNSPR Test Results - $OBJDIR\n\n"
+printf "BEGIN\t\t\t`date`\n"
+printf "NSPR_TEST_LOGFILE\t${LOGFILE}\n\n"
+printf "Test\t\t\tResult\n\n"
+if [ $OS_PLATFORM = "Windows_95" ] || [ $OS_PLATFORM = "Windows_98" ] || [ $OS_PLATFORM = "Windows_NT" ] || [ $OS_PLATFORM = "OS/2" ] ; then
+ for prog in $TESTS
+ do
+ printf "$prog (`date +%T`)"
+ printf "\nBEGIN TEST: $prog\n\n" >> ${LOGFILE} 2>&1
+ ./$prog >> ${LOGFILE} 2>&1
+ if [ 0 = $? ] ; then
+ printf "\t\t\tPassed\n";
+ else
+ printf "\t\t\tFAILED\n";
+ rval=1
+ fi;
+ printf "\nEND TEST: $prog\n\n" >> ${LOGFILE} 2>&1
+ done
+else
+ for prog in $TESTS
+ do
+ printf "$prog (`date +%T`)"
+ printf "\nBEGIN TEST: $prog\n\n" >> ${LOGFILE} 2>&1
+ export test_rval
+ ./$prog >> ${LOGFILE} 2>&1 &
+ test_pid=$!
+ sleep_pid=0
+ if test -n "$TEST_TIMEOUT" && test "$TEST_TIMEOUT" -gt 0
+ then
+ (sleep $TEST_TIMEOUT; kill $test_pid >/dev/null 2>&1 ) &
+ sleep_pid=$!
+ fi
+ wait $test_pid
+ test_rval=$?
+ [ $sleep_pid -eq 0 ] || kill $sleep_pid >/dev/null 2>&1
+ if [ 0 = $test_rval ] ; then
+ printf "\t\t\tPassed\n";
+ else
+ printf "\t\t\tFAILED\n";
+ rval=1
+ fi;
+ printf "\nEND TEST: $prog\n\n" >> ${LOGFILE} 2>&1
+ done
+fi;
+
+if [ $rval -ne 0 ]; then
+ cat ${LOGFILE}
+fi
+
+printf "END\t\t\t`date`\n"
+exit $rval
+
diff --git a/nsprpub/pr/tests/runy2ktests.ksh b/nsprpub/pr/tests/runy2ktests.ksh
new file mode 100644
index 0000000000..56f7d3c5e3
--- /dev/null
+++ b/nsprpub/pr/tests/runy2ktests.ksh
@@ -0,0 +1,237 @@
+#!/bin/ksh
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#
+# runy2ktests.ksh
+# Set system clock to Y2K dates of interest and run the Y2K tests.
+# Needs root/administrator privilege
+#
+# WARNING: Because this script needs to be run with root/administrator
+# privilege, thorough understanding of the script and extreme
+# caution are urged.
+#
+
+#
+# SECTION I
+# Define variables
+#
+
+SYSTEM_INFO=`uname -a`
+OS_ARCH=`uname -s`
+if [ $OS_ARCH = "Windows_NT" ] || [ $OS_ARCH = "Windows_95" ]
+then
+ NULL_DEVICE=nul
+else
+ NULL_DEVICE=/dev/null
+fi
+
+#
+# Test dates for NSPR Y2K tests
+#
+Y2KDATES=" 123123591998.55
+ 090923591999.55
+ 123123591999.55
+ 022823592000.55
+ 022923592000.55
+ 123123592000.55"
+
+Y2KDATES_AIX=" 12312359.5598
+ 09092359.5599
+ 12312359.5599
+ 02282359.5500
+ 02292359.5500
+ 12312359.5500"
+
+Y2KDATES_HPUX=" 123123591998
+ 090923591999
+ 123123591999
+ 022823592000
+ 022923592000
+ 123123592000"
+
+Y2KDATES_MKS=" 1231235998.55
+ 0909235999.55
+ 1231235999.55
+ 0228235900.55
+ 0229235900.55
+ 1231235900.55"
+
+#
+# NSPR Y2K tests
+#
+Y2KTESTS="
+y2k \n
+y2ktmo \n
+y2k \n
+../runtests.ksh"
+
+Y2KTESTS_HPUX="
+y2k \n
+y2ktmo -l 60\n
+y2k \n
+../runtests.ksh"
+
+#
+# SECTION II
+# Define functions
+#
+
+save_date()
+{
+ case $OS_ARCH in
+ AIX)
+ SAVED_DATE=`date "+%m%d%H%M.%S%y"`
+ ;;
+ HP-UX)
+ SAVED_DATE=`date "+%m%d%H%M%Y"`
+ ;;
+ Windows_NT)
+ SAVED_DATE=`date "+%m%d%H%M%y.%S"`
+ ;;
+ Windows_95)
+ SAVED_DATE=`date "+%m%d%H%M%y.%S"`
+ ;;
+ *)
+ SAVED_DATE=`date "+%m%d%H%M%Y.%S"`
+ ;;
+ esac
+}
+
+set_date()
+{
+ case $OS_ARCH in
+ Windows_NT)
+#
+# The date command in MKS Toolkit releases 5.1 and 5.2
+# uses the current DST status for the date we want to
+# set the system clock to. However, the DST status for
+# that date may be different from the current DST status.
+# We can work around this problem by invoking the date
+# command with the same date twice.
+#
+ date "$1" > $NULL_DEVICE
+ date "$1" > $NULL_DEVICE
+ ;;
+ *)
+ date "$1" > $NULL_DEVICE
+ ;;
+ esac
+}
+
+restore_date()
+{
+ set_date "$SAVED_DATE"
+}
+
+savedate()
+{
+ case $OS_ARCH in
+ AIX)
+ SAVED_DATE=`date "+%m%d%H%M.%S%y"`
+ ;;
+ HP-UX)
+ SAVED_DATE=`date "+%m%d%H%M%Y"`
+ ;;
+ Windows_NT)
+ SAVED_DATE=`date "+%m%d%H%M%y.%S"`
+ ;;
+ Windows_95)
+ SAVED_DATE=`date "+%m%d%H%M%y.%S"`
+ ;;
+ *)
+ SAVED_DATE=`date "+%m%d%H%M%Y.%S"`
+ ;;
+ esac
+}
+
+set_y2k_test_parameters()
+{
+#
+# set dates
+#
+ case $OS_ARCH in
+ AIX)
+ DATES=$Y2KDATES_AIX
+ ;;
+ HP-UX)
+ DATES=$Y2KDATES_HPUX
+ ;;
+ Windows_NT)
+ DATES=$Y2KDATES_MKS
+ ;;
+ Windows_95)
+ DATES=$Y2KDATES_MKS
+ ;;
+ *)
+ DATES=$Y2KDATES
+ ;;
+ esac
+
+#
+# set tests
+#
+ case $OS_ARCH in
+ HP-UX)
+ TESTS=$Y2KTESTS_HPUX
+ ;;
+ *)
+ TESTS=$Y2KTESTS
+ ;;
+ esac
+}
+
+#
+# runtests:
+# - runs each test in $TESTS after setting the
+# system clock to each date in $DATES
+#
+
+runtests()
+{
+for newdate in ${DATES}
+do
+ set_date $newdate
+ echo $newdate
+ echo "BEGIN\t\t\t`date`"
+ echo "Date\t\t\t\t\tTest\t\t\tResult"
+ echo $TESTS | while read prog
+ do
+ echo "`date`\t\t\c"
+ echo "$prog\c"
+ ./$prog >> ${LOGFILE} 2>&1
+ if [ 0 = $? ] ; then
+ echo "\t\t\tPassed";
+ else
+ echo "\t\t\tFAILED";
+ fi;
+ done
+ echo "END\t\t\t`date`\n"
+done
+
+}
+
+#
+# SECTION III
+# Run tests
+#
+
+LOGFILE=${NSPR_TEST_LOGFILE:-$NULL_DEVICE}
+OBJDIR=`basename $PWD`
+echo "\nNSPR Year 2000 Test Results - $OBJDIR\n"
+echo "SYSTEM:\t\t\t${SYSTEM_INFO}"
+echo "NSPR_TEST_LOGFILE:\t${LOGFILE}\n"
+
+
+save_date
+
+#
+# Run NSPR Y2k and standard tests
+#
+
+set_y2k_test_parameters
+runtests
+
+restore_date
diff --git a/nsprpub/pr/tests/rwlockrank.c b/nsprpub/pr/tests/rwlockrank.c
new file mode 100644
index 0000000000..d77eb9680a
--- /dev/null
+++ b/nsprpub/pr/tests/rwlockrank.c
@@ -0,0 +1,122 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * RWLock rank tests
+ */
+
+#include "nspr.h"
+#include "plgetopt.h"
+
+static int _debug_on;
+static PRRWLock *rwlock0;
+static PRRWLock *rwlock1;
+static PRRWLock *rwlock2;
+
+static void rwtest(void *args)
+{
+ PR_RWLock_Rlock(rwlock1);
+ PR_RWLock_Unlock(rwlock1);
+
+ PR_RWLock_Rlock(rwlock1);
+ PR_RWLock_Unlock(rwlock1);
+
+ /* Test correct lock rank. */
+ PR_RWLock_Rlock(rwlock1);
+ PR_RWLock_Rlock(rwlock2);
+ PR_RWLock_Unlock(rwlock2);
+ PR_RWLock_Unlock(rwlock1);
+
+ PR_RWLock_Rlock(rwlock1);
+ PR_RWLock_Rlock(rwlock2);
+ PR_RWLock_Unlock(rwlock1);
+ PR_RWLock_Unlock(rwlock2);
+
+ PR_RWLock_Rlock(rwlock1);
+ PR_RWLock_Rlock(rwlock0);
+ PR_RWLock_Rlock(rwlock2);
+ PR_RWLock_Unlock(rwlock2);
+ PR_RWLock_Unlock(rwlock0);
+ PR_RWLock_Unlock(rwlock1);
+
+#if 0
+ /* Test incorrect lock rank. */
+ PR_RWLock_Rlock(rwlock2);
+ PR_RWLock_Rlock(rwlock1);
+ PR_RWLock_Unlock(rwlock1);
+ PR_RWLock_Unlock(rwlock2);
+
+ PR_RWLock_Rlock(rwlock2);
+ PR_RWLock_Rlock(rwlock0);
+ PR_RWLock_Rlock(rwlock1);
+ PR_RWLock_Unlock(rwlock1);
+ PR_RWLock_Unlock(rwlock0);
+ PR_RWLock_Unlock(rwlock2);
+#endif
+}
+
+int main(int argc, char **argv)
+{
+ PRStatus rc;
+ PRThread *thread;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option) {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ rwlock0 = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "Lock 0");
+ if (rwlock0 == NULL) {
+ fprintf(stderr, "PR_NewRWLock failed - error %d\n",
+ (int)PR_GetError());
+ return 1;
+ }
+ rwlock1 = PR_NewRWLock(1, "Lock 1");
+ if (rwlock1 == NULL) {
+ fprintf(stderr, "PR_NewRWLock failed - error %d\n",
+ (int)PR_GetError());
+ return 1;
+ }
+ rwlock2 = PR_NewRWLock(2, "Lock 2");
+ if (rwlock2 == NULL) {
+ fprintf(stderr, "PR_NewRWLock failed - error %d\n",
+ (int)PR_GetError());
+ return 1;
+ }
+
+ thread = PR_CreateThread(PR_USER_THREAD, rwtest, NULL, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (thread == NULL) {
+ fprintf(stderr, "PR_CreateThread failed - error %d\n",
+ (int)PR_GetError());
+ PR_ProcessExit(2);
+ }
+ if (_debug_on) {
+ printf("%s: created thread = %p\n", argv[0], thread);
+ }
+
+ rc = PR_JoinThread(thread);
+ PR_ASSERT(rc == PR_SUCCESS);
+
+ PR_DestroyRWLock(rwlock0);
+ rwlock0 = NULL;
+ PR_DestroyRWLock(rwlock1);
+ rwlock1 = NULL;
+ PR_DestroyRWLock(rwlock2);
+ rwlock2 = NULL;
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/rwlocktest.c b/nsprpub/pr/tests/rwlocktest.c
new file mode 100644
index 0000000000..396103c341
--- /dev/null
+++ b/nsprpub/pr/tests/rwlocktest.c
@@ -0,0 +1,213 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+/*
+ *
+ * RWLock tests
+ *
+ * Several threads are created to access and modify data arrays using
+ * PRRWLocks for synchronization. Two data arrays, array_A and array_B, are
+ * initialized with random data and a third array, array_C, is initialized
+ * with the sum of the first 2 arrays.
+ *
+ * Each one of the threads acquires a read lock to verify that the sum of
+ * the arrays A and B is equal to array C, and acquires a write lock to
+ * consistently update arrays A and B so that their is equal to array C.
+ *
+ */
+
+#include "nspr.h"
+#include "plgetopt.h"
+#include "prrwlock.h"
+
+static int _debug_on;
+static void rwtest(void *args);
+static PRInt32 *array_A,*array_B,*array_C;
+static void update_array(void);
+static void check_array(void);
+
+typedef struct thread_args {
+ PRRWLock *rwlock;
+ PRInt32 loop_cnt;
+} thread_args;
+
+PRFileDesc *output;
+PRFileDesc *errhandle;
+
+#define DEFAULT_THREAD_CNT 4
+#define DEFAULT_LOOP_CNT 100
+#define TEST_ARRAY_SIZE 100
+
+int main(int argc, char **argv)
+{
+ PRInt32 cnt;
+ PRStatus rc;
+ PRInt32 i;
+
+ PRInt32 thread_cnt = DEFAULT_THREAD_CNT;
+ PRInt32 loop_cnt = DEFAULT_LOOP_CNT;
+ PRThread **threads;
+ thread_args *params;
+ PRRWLock *rwlock1;
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dt:c:");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ case 't': /* thread count */
+ thread_cnt = atoi(opt->value);
+ break;
+ case 'c': /* loop count */
+ loop_cnt = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_SetConcurrency(4);
+
+ output = PR_GetSpecialFD(PR_StandardOutput);
+ errhandle = PR_GetSpecialFD(PR_StandardError);
+
+ rwlock1 = PR_NewRWLock(0,"Lock 1");
+ if (rwlock1 == NULL) {
+ PR_fprintf(errhandle, "PR_NewRWLock failed - error %d\n",
+ PR_GetError());
+ return 1;
+ }
+
+ threads = (PRThread**) PR_CALLOC(sizeof(PRThread*) * thread_cnt);
+ params = (thread_args *) PR_CALLOC(sizeof(thread_args) * thread_cnt);
+
+ /*
+ * allocate and initialize data arrays
+ */
+ array_A =(PRInt32 *) PR_MALLOC(sizeof(PRInt32) * TEST_ARRAY_SIZE);
+ array_B =(PRInt32 *) PR_MALLOC(sizeof(PRInt32) * TEST_ARRAY_SIZE);
+ array_C =(PRInt32 *) PR_MALLOC(sizeof(PRInt32) * TEST_ARRAY_SIZE);
+ cnt = 0;
+ for (i=0; i < TEST_ARRAY_SIZE; i++) {
+ array_A[i] = cnt++;
+ array_B[i] = cnt++;
+ array_C[i] = array_A[i] + array_B[i];
+ }
+
+ if (_debug_on)
+ PR_fprintf(output,"%s: thread_cnt = %d loop_cnt = %d\n", argv[0],
+ thread_cnt, loop_cnt);
+ for(cnt = 0; cnt < thread_cnt; cnt++) {
+ PRThreadScope scope;
+
+ params[cnt].rwlock = rwlock1;
+ params[cnt].loop_cnt = loop_cnt;
+
+ /*
+ * create LOCAL and GLOBAL threads alternately
+ */
+ if (cnt & 1) {
+ scope = PR_LOCAL_THREAD;
+ }
+ else {
+ scope = PR_GLOBAL_THREAD;
+ }
+
+ threads[cnt] = PR_CreateThread(PR_USER_THREAD,
+ rwtest, &params[cnt],
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (threads[cnt] == NULL) {
+ PR_fprintf(errhandle, "PR_CreateThread failed - error %d\n",
+ PR_GetError());
+ PR_ProcessExit(2);
+ }
+ if (_debug_on)
+ PR_fprintf(output,"%s: created thread = %p\n", argv[0],
+ threads[cnt]);
+ }
+
+ for(cnt = 0; cnt < thread_cnt; cnt++) {
+ rc = PR_JoinThread(threads[cnt]);
+ PR_ASSERT(rc == PR_SUCCESS);
+
+ }
+
+ PR_DELETE(threads);
+ PR_DELETE(params);
+
+ PR_DELETE(array_A);
+ PR_DELETE(array_B);
+ PR_DELETE(array_C);
+
+ PR_DestroyRWLock(rwlock1);
+
+
+ printf("PASS\n");
+ return 0;
+}
+
+static void rwtest(void *args)
+{
+ PRInt32 index;
+ thread_args *arg = (thread_args *) args;
+
+
+ for (index = 0; index < arg->loop_cnt; index++) {
+
+ /*
+ * verify sum, update arrays and verify sum again
+ */
+
+ PR_RWLock_Rlock(arg->rwlock);
+ check_array();
+ PR_RWLock_Unlock(arg->rwlock);
+
+ PR_RWLock_Wlock(arg->rwlock);
+ update_array();
+ PR_RWLock_Unlock(arg->rwlock);
+
+ PR_RWLock_Rlock(arg->rwlock);
+ check_array();
+ PR_RWLock_Unlock(arg->rwlock);
+ }
+ if (_debug_on)
+ PR_fprintf(output,
+ "Thread[0x%x] lock = 0x%x exiting\n",
+ PR_GetCurrentThread(), arg->rwlock);
+
+}
+
+static void check_array(void)
+{
+ PRInt32 i;
+
+ for (i=0; i < TEST_ARRAY_SIZE; i++)
+ if (array_C[i] != (array_A[i] + array_B[i])) {
+ PR_fprintf(output, "Error - data check failed\n");
+ PR_ProcessExit(1);
+ }
+}
+
+static void update_array(void)
+{
+ PRInt32 i;
+
+ for (i=0; i < TEST_ARRAY_SIZE; i++) {
+ array_A[i] += i;
+ array_B[i] -= i;
+ }
+}
diff --git a/nsprpub/pr/tests/sel_spd.c b/nsprpub/pr/tests/sel_spd.c
new file mode 100644
index 0000000000..6e3f66fedd
--- /dev/null
+++ b/nsprpub/pr/tests/sel_spd.c
@@ -0,0 +1,534 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Test the speed of select within NSPR
+ *
+ */
+
+#include "nspr.h"
+#include "prpriv.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#if defined(XP_UNIX)
+#include <unistd.h>
+#endif
+
+#ifdef DEBUG
+#define PORT_INC_DO +100
+#else
+#define PORT_INC_DO
+#endif
+#ifdef IS_64
+#define PORT_INC_3264 +200
+#else
+#define PORT_INC_3264
+#endif
+
+#define PORT_BASE 19000 PORT_INC_DO PORT_INC_3264
+
+typedef struct timer_slot_t {
+ unsigned long d_connect;
+ unsigned long d_cl_data;
+ unsigned long d_sv_data;
+ unsigned long d_close;
+ unsigned long d_total;
+ unsigned long requests;
+} timer_slot_t;
+
+static long _iterations = 5;
+static long _client_data = 8192;
+
+static long _server_data = (128*1024);
+static long _threads_max = 10, _threads = 10;
+
+static int verbose=0;
+static PRMonitor *exit_cv;
+static long _thread_exit_count;
+static timer_slot_t *timer_data;
+static PRThreadScope scope1, scope2;
+
+void tally_results(int);
+
+/* return the diff in microseconds */
+unsigned long _delta(PRIntervalTime *start, PRIntervalTime *stop)
+{
+ /*
+ * Will C do the right thing with unsigned arithemtic?
+ */
+ return PR_IntervalToMicroseconds(*stop - *start);
+}
+
+int _readn(PRFileDesc *sock, char *buf, int len)
+{
+ int rem;
+ int bytes;
+
+ for (rem=len; rem; rem -= bytes) {
+ bytes = PR_Recv(sock, buf+len-rem, rem, 0, PR_INTERVAL_NO_TIMEOUT);
+ if (bytes <= 0) {
+ return -1;
+ }
+ }
+ return len;
+}
+
+void
+_thread_exit(int id)
+{
+ PR_EnterMonitor(exit_cv);
+#ifdef DEBUG
+ fprintf(stdout, "Thread %d EXIT\n", id);
+#endif
+
+ _thread_exit_count--;
+ if (_thread_exit_count == 0) {
+#ifdef DEBUG
+ fprintf(stdout, "Thread %d EXIT triggered notify\n", id);
+#endif
+ PR_Notify(exit_cv);
+ }
+ PR_ExitMonitor(exit_cv);
+}
+
+void
+_server_thread(void *arg_id)
+{
+ void _client_thread(void *);
+ int *id = (int *)arg_id;
+ PRFileDesc *sock;
+ PRSocketOptionData sockopt;
+ PRNetAddr sa;
+ PRFileDesc * newsock;
+ char *data_buffer = NULL;
+ int data_buffer_size;
+ int index;
+ PRIntervalTime start,
+ connect_done,
+ read_done,
+ write_done,
+ close_done;
+
+
+#ifdef DEBUG
+ fprintf(stdout, "server thread %d alive\n", *id);
+#endif
+
+ data_buffer_size = (_client_data>_server_data?_client_data:_server_data);
+
+ if ( (data_buffer = (char *)PR_Malloc(data_buffer_size * sizeof(char))) == NULL ) {
+ fprintf(stderr, "Error creating buffer in server thread %d\n", *id);
+ goto done;
+ }
+
+
+ if ( (sock = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Error creating socket in server thread %d\n", *id);
+ goto done;
+ }
+
+ sockopt.option = PR_SockOpt_Reuseaddr;
+ sockopt.value.reuse_addr = PR_TRUE;
+ if ( PR_SetSocketOption(sock, &sockopt) == PR_FAILURE) {
+ fprintf(stderr, "Error setting socket option in server thread %d\n", *id);
+ goto done;
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.inet.family = PR_AF_INET;
+ sa.inet.port = PR_htons(PORT_BASE + *id);
+ sa.inet.ip = PR_htonl(PR_INADDR_ANY);
+
+ if ( PR_Bind(sock, &sa) < 0) {
+ fprintf(stderr, "Error binding socket in server thread %d errno = %d\n", *id, errno);
+ goto done;
+ }
+
+ if ( PR_Listen(sock, 32) < 0 ) {
+ fprintf(stderr, "Error listening to socket in server thread %d\n", *id);
+ goto done;
+ }
+
+ /* Tell the client to start */
+ if ( PR_CreateThread(PR_USER_THREAD,
+ _client_thread,
+ id,
+ PR_PRIORITY_NORMAL,
+ scope2,
+ PR_UNJOINABLE_THREAD,
+ 0) == NULL) {
+ fprintf(stderr, "Error creating client thread %d\n", *id);
+ }
+
+ for (index = 0; index< _iterations; index++) {
+
+#ifdef DEBUG
+ fprintf(stdout, "server thread %d loop %d\n", *id, index);
+#endif
+
+ start = PR_IntervalNow();
+
+ if ( (newsock = PR_Accept(sock, &sa,
+ PR_INTERVAL_NO_TIMEOUT)) == NULL) {
+ fprintf(stderr, "Error accepting connection %d in server thread %d\n",
+ index, *id);
+ goto done;
+ }
+#ifdef DEBUG
+ fprintf(stdout, "server thread %d got connection %d\n", *id, newsock);
+#endif
+
+
+ connect_done = PR_IntervalNow();
+
+ if ( _readn(newsock, data_buffer, _client_data) < _client_data) {
+ fprintf(stderr, "Error reading client data for iteration %d in server thread %d\n", index, *id );
+ goto done;
+ }
+
+#ifdef DEBUG
+ fprintf(stdout, "server thread %d read %d bytes\n", *id, _client_data);
+#endif
+ read_done = PR_IntervalNow();
+
+ if ( PR_Send(newsock, data_buffer, _server_data, 0,
+ PR_INTERVAL_NO_TIMEOUT) < _server_data) {
+ fprintf(stderr, "Error sending client data for iteration %d in server thread %d\n", index, *id );
+ goto done;
+ }
+
+#ifdef DEBUG
+ fprintf(stdout, "server thread %d write %d bytes\n", *id, _server_data);
+#endif
+
+ write_done = PR_IntervalNow();
+
+ PR_Close(newsock);
+
+ close_done = PR_IntervalNow();
+
+ timer_data[2*(*id)].d_connect += _delta(&start, &connect_done);
+ timer_data[2*(*id)].d_cl_data += _delta(&connect_done, &read_done);
+ timer_data[2*(*id)].d_sv_data += _delta(&read_done, &write_done);
+ timer_data[2*(*id)].d_close += _delta(&write_done, &close_done);
+ timer_data[2*(*id)].d_total += _delta(&start, &close_done);
+ timer_data[2*(*id)].requests++;
+
+
+#ifdef DEBUG
+ fprintf(stdout, "server: %d %d %d %d %d\n",
+ _delta(&start, &connect_done), _delta(&connect_done, &read_done),
+ _delta(&read_done, &write_done), _delta(&write_done, &close_done),
+ _delta(&start, &close_done));
+#endif
+ }
+
+done:
+ if (data_buffer != NULL) {
+ PR_Free (data_buffer);
+ }
+ if (sock) {
+ PR_Close(sock);
+ }
+ _thread_exit(*id);
+ return;
+}
+
+void
+_client_thread(void *arg_id)
+{
+ int *id = (int *)arg_id;
+ int index;
+ PRNetAddr sa;
+ PRFileDesc *sock_h;
+ char *data_buffer = NULL;
+ int data_buffer_size;
+ int bytes;
+ PRIntervalTime start,
+ connect_done,
+ read_done,
+ write_done,
+ close_done;
+ PRStatus rv;
+
+#ifdef DEBUG
+ fprintf(stdout, "client thread %d alive\n", *id);
+#endif
+
+ data_buffer_size = (_client_data>_server_data?_client_data:_server_data);
+
+ if ( (data_buffer = (char *)PR_Malloc(data_buffer_size * sizeof(char))) == NULL) {
+ fprintf(stderr, "Error creating buffer in server thread %d\n", *id);
+ goto done;
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ rv = PR_InitializeNetAddr(PR_IpAddrLoopback, PORT_BASE + *id, &sa);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ for (index = 0; index< _iterations; index++) {
+
+#ifdef DEBUG
+ fprintf(stdout, "client thread %d loop %d\n", *id, index);
+#endif
+
+ start = PR_IntervalNow();
+ if ( (sock_h = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Error creating socket %d in client thread %d\n",
+ index, *id);
+ goto done;
+ }
+
+#ifdef DEBUG
+ fprintf(stdout, "client thread %d socket created %d\n", *id, sock_h);
+#endif
+
+ if ( PR_Connect(sock_h, &sa,
+ PR_INTERVAL_NO_TIMEOUT) < 0) {
+ fprintf(stderr, "Error accepting connection %d in client thread %d\n",
+ index, *id);
+ goto done;
+ }
+
+#ifdef DEBUG
+ fprintf(stdout, "client thread %d socket connected %d\n", *id, sock_h);
+#endif
+
+ connect_done = PR_IntervalNow();
+ if ( PR_Send(sock_h, data_buffer, _client_data, 0,
+ PR_INTERVAL_NO_TIMEOUT) < _client_data) {
+ fprintf(stderr, "Error sending client data for iteration %d in client thread %d\n", index, *id );
+ goto done;
+ }
+
+#ifdef DEBUG
+ fprintf(stdout, "client thread %d socket wrote %d\n", *id, _client_data);
+#endif
+
+ write_done = PR_IntervalNow();
+ if ( (bytes = _readn(sock_h, data_buffer, _server_data)) < _server_data) {
+ fprintf(stderr, "Error reading server data for iteration %d in client thread %d (read %d bytes)\n", index, *id, bytes );
+ goto done;
+ }
+
+#ifdef DEBUG
+ fprintf(stdout, "client thread %d socket read %d\n", *id, _server_data);
+#endif
+
+ read_done = PR_IntervalNow();
+ PR_Close(sock_h);
+ close_done = PR_IntervalNow();
+
+ timer_data[2*(*id)+1].d_connect += _delta(&start, &connect_done);
+ timer_data[2*(*id)+1].d_cl_data += _delta(&connect_done, &write_done);
+ timer_data[2*(*id)+1].d_sv_data += _delta(&write_done, &read_done);
+ timer_data[2*(*id)+1].d_close += _delta(&read_done, &close_done);
+ timer_data[2*(*id)+1].d_total += _delta(&start, &close_done);
+ timer_data[2*(*id)+1].requests++;
+ }
+done:
+ if (data_buffer != NULL) {
+ PR_Free (data_buffer);
+ }
+ _thread_exit(*id);
+
+ return;
+}
+
+static
+void do_work(void)
+{
+ int index;
+
+ _thread_exit_count = _threads * 2;
+ for (index=0; index<_threads; index++) {
+ int *id = (int *)PR_Malloc(sizeof(int));
+
+ *id = index;
+
+ if ( PR_CreateThread(PR_USER_THREAD,
+ _server_thread,
+ id,
+ PR_PRIORITY_NORMAL,
+ scope1,
+ PR_UNJOINABLE_THREAD,
+ 0) == NULL) {
+ fprintf(stderr, "Error creating server thread %d\n", index);
+ }
+ }
+
+ PR_EnterMonitor(exit_cv);
+ while (_thread_exit_count > 0) {
+ PR_Wait(exit_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(exit_cv);
+
+ fprintf(stdout, "TEST COMPLETE!\n");
+
+ tally_results(verbose);
+
+}
+
+static void do_workUU(void)
+{
+ scope1 = PR_LOCAL_THREAD;
+ scope2 = PR_LOCAL_THREAD;
+ do_work();
+}
+
+static void do_workUK(void)
+{
+ scope1 = PR_LOCAL_THREAD;
+ scope2 = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+static void do_workKU(void)
+{
+ scope1 = PR_GLOBAL_THREAD;
+ scope2 = PR_LOCAL_THREAD;
+ do_work();
+}
+
+static void do_workKK(void)
+{
+ scope1 = PR_GLOBAL_THREAD;
+ scope2 = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ printf("%40s: %6.2f usec\n", msg, d / _iterations);
+}
+
+
+int main(int argc, char **argv)
+{
+#if defined(XP_UNIX) || defined(XP_OS2)
+ int opt;
+ PR_IMPORT_DATA(char *) optarg;
+#endif
+
+#if defined(XP_UNIX) || defined(XP_OS2)
+ while ( (opt = getopt(argc, argv, "c:s:i:t:v")) != EOF) {
+ switch(opt) {
+ case 'i':
+ _iterations = atoi(optarg);
+ break;
+ case 't':
+ _threads_max = _threads = atoi(optarg);
+ break;
+ case 'c':
+ _client_data = atoi(optarg);
+ break;
+ case 's':
+ _server_data = atoi(optarg);
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ break;
+ }
+ }
+#endif
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ fprintf(stdout, "Running test for %d iterations with %d simultaneous threads.\n",
+ _iterations, _threads);
+ fprintf(stdout, "\tWill send %d bytes of client data and %d bytes of server data\n",
+ _client_data, _server_data);
+
+ if ( (exit_cv = PR_NewMonitor()) == NULL) {
+ fprintf(stderr, "Error creating monitor for exit cv\n");
+ }
+ if ( (timer_data = (timer_slot_t *)PR_Malloc(2*_threads * sizeof(timer_slot_t))) == NULL) {
+ fprintf(stderr, "error allocating thread time results array\n");
+ }
+ memset(timer_data, 0, 2*_threads*sizeof(timer_slot_t));
+
+ Measure(do_workUU, "select loop user/user");
+ Measure(do_workUK, "select loop user/kernel");
+ Measure(do_workKU, "select loop kernel/user");
+ Measure(do_workKK, "select loop kernel/kernel");
+
+
+ return 0;
+}
+
+void
+tally_results(int verbose)
+{
+ int index;
+ unsigned long tot_connect = 0;
+ unsigned long tot_cl_data = 0;
+ unsigned long tot_sv_data = 0;
+ unsigned long tot_close = 0;
+ unsigned long tot_all = 0;
+ unsigned long tot_requests = 0;
+
+ fprintf(stdout, "Server results:\n\n");
+ for (index=0; index<_threads_max*2; index+=2) {
+
+ if (verbose)
+ fprintf(stdout, "server thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n",
+ index, timer_data[index].requests, timer_data[index].d_connect,
+ timer_data[index].d_cl_data, timer_data[index].d_sv_data,
+ timer_data[index].d_close, timer_data[index].d_total);
+
+ tot_connect += timer_data[index].d_connect / _threads;
+ tot_cl_data += timer_data[index].d_cl_data / _threads;
+ tot_sv_data += timer_data[index].d_sv_data / _threads;
+ tot_close += timer_data[index].d_close / _threads;
+ tot_all += timer_data[index].d_total / _threads;
+ tot_requests += timer_data[index].requests / _threads;
+ }
+ fprintf(stdout, "----------\n");
+ fprintf(stdout, "server per thread totals %u\t%u\t%u\t%u\t%u\n",
+ tot_requests, tot_connect, tot_cl_data, tot_sv_data, tot_close);
+ fprintf(stdout, "server per thread elapsed time %u\n", tot_all);
+ fprintf(stdout, "----------\n");
+
+ tot_connect = tot_cl_data = tot_sv_data = tot_close = tot_all = tot_requests = 0;
+ fprintf(stdout, "Client results:\n\n");
+ for (index=1; index<_threads_max*2; index+=2) {
+
+ if (verbose)
+ fprintf(stdout, "client thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n",
+ index, timer_data[index].requests, timer_data[index].d_connect,
+ timer_data[index].d_cl_data, timer_data[index].d_sv_data,
+ timer_data[index].d_close, timer_data[index].d_total);
+
+ tot_connect += timer_data[index].d_connect / _threads;
+ tot_cl_data += timer_data[index].d_cl_data / _threads;
+ tot_sv_data += timer_data[index].d_sv_data / _threads;
+ tot_close += timer_data[index].d_close / _threads;
+ tot_all += timer_data[index].d_total / _threads;
+ tot_requests += timer_data[index].requests / _threads;
+ }
+ fprintf(stdout, "----------\n");
+ fprintf(stdout, "client per thread totals %u\t%u\t%u\t%u\t%u\n",
+ tot_requests, tot_connect, tot_cl_data, tot_sv_data, tot_close);
+ fprintf(stdout, "client per thread elapsed time %u\n", tot_all);
+}
+
diff --git a/nsprpub/pr/tests/selct_er.c b/nsprpub/pr/tests/selct_er.c
new file mode 100644
index 0000000000..0c5c46e08d
--- /dev/null
+++ b/nsprpub/pr/tests/selct_er.c
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+** 1997 - Netscape Communications Corporation
+**
+** Name: prselect_err.c
+**
+** Description: tests PR_Select with sockets Error condition functions.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "primpl.h"
+#include "pprio.h"
+#include "prnetdb.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1, *listenSock2;
+ PRFileDesc *badFD;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ PR_fd_set readFdSet;
+ char buf[128];
+ PRInt32 retVal;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("This program tests PR_Select with sockets. Error\n");
+ printf("reporting operations are tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ if (debug_mode) {
+ printf("%s", buf);
+ }
+
+ /* Set up the fd set */
+ PR_FD_ZERO(&readFdSet);
+ PR_FD_SET(listenSock1, &readFdSet);
+ PR_FD_SET(listenSock2, &readFdSet);
+
+
+ /* Testing bad fd */
+ if (debug_mode) {
+ printf("PR_Select should detect a bad file descriptor\n");
+ }
+ if ((badFD = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ PR_FD_SET(badFD, &readFdSet);
+ /*
+ * Make the fd invalid
+ */
+#if defined(XP_UNIX)
+ close(PR_FileDesc2NativeHandle(badFD));
+#elif defined(XP_OS2)
+ soclose(PR_FileDesc2NativeHandle(badFD));
+#elif defined(WIN32) || defined(WIN16)
+ closesocket(PR_FileDesc2NativeHandle(badFD));
+#else
+#error "Unknown architecture"
+#endif
+
+ retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (retVal != -1 || PR_GetError() != PR_BAD_DESCRIPTOR_ERROR) {
+ fprintf(stderr, "Failed to detect the bad fd: "
+ "PR_Select returns %d\n", retVal);
+ if (retVal == -1) {
+ fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(),
+ PR_GetOSError());
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ if (debug_mode) {
+ printf("PR_Select detected a bad fd. Test passed.\n\n");
+ }
+ PR_FD_CLR(badFD, &readFdSet);
+
+ PR_Cleanup();
+ goto exit_now;
+exit_now:
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+}
+
diff --git a/nsprpub/pr/tests/selct_nm.c b/nsprpub/pr/tests/selct_nm.c
new file mode 100644
index 0000000000..a698f0fa42
--- /dev/null
+++ b/nsprpub/pr/tests/selct_nm.c
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+** 1997 - Netscape Communications Corporation
+**
+** Name: prselect_norm.c
+**
+** Description: tests PR_Select with sockets - Normal operations.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prio.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prerror.h"
+#include "prnetdb.h"
+
+#include "obsolete/probslet.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+static void
+clientThreadFunc(void *arg)
+{
+ PRUintn port = (PRUintn) arg;
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ char buf[128];
+ int i;
+
+ addr.inet.family = PR_AF_INET;
+ addr.inet.port = PR_htons((PRUint16)port);
+ addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+ PR_snprintf(buf, sizeof(buf), "%hu", addr.inet.port);
+
+ for (i = 0; i < 5; i++) {
+ sock = PR_NewTCPSocket();
+ PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
+ PR_Write(sock, buf, sizeof(buf));
+ PR_Close(sock);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1, *listenSock2;
+ PRFileDesc *fds0[10], *fds1[10], **fds, **other_fds;
+ PRIntn nfds;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ PR_fd_set readFdSet;
+ char buf[128];
+ PRThread *clientThread;
+ PRInt32 retVal;
+ PRIntn i, j;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("This program tests PR_Select with sockets. \n");
+ printf(" Normal operation are tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ if (debug_mode) {
+ printf("%s", buf);
+ }
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort1,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort2,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if (debug_mode) {
+ printf("Two client threads are created. Each of them will\n");
+ printf("send data to one of the two ports the server is listening on.\n");
+ printf("The data they send is the port number. Each of them send\n");
+ printf("the data five times, so you should see ten lines below,\n");
+ printf("interleaved in an arbitrary order.\n");
+ }
+ /* set up the fd array */
+ fds = fds0;
+ other_fds = fds1;
+ fds[0] = listenSock1;
+ fds[1] = listenSock2;
+ nfds = 2;
+ /* Set up the fd set */
+ PR_FD_ZERO(&readFdSet);
+ PR_FD_SET(listenSock1, &readFdSet);
+ PR_FD_SET(listenSock2, &readFdSet);
+
+ /* 20 events total */
+ i = 0;
+ while (i < 20) {
+ PRFileDesc **tmp;
+ int nextIndex;
+ int nEvents = 0;
+
+ retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL,
+ PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(retVal != 0); /* no timeout */
+ if (retVal == -1) {
+ fprintf(stderr, "PR_Select failed (%d, %d)\n", PR_GetError(),
+ PR_GetOSError());
+ failed_already=1;
+ goto exit_now;
+ }
+
+ nextIndex = 2;
+ /* the two listening sockets */
+ for (j = 0; j < 2; j++) {
+ other_fds[j] = fds[j];
+ if (PR_FD_ISSET(fds[j], &readFdSet)) {
+ PRFileDesc *sock;
+
+ nEvents++;
+ sock = PR_Accept(fds[j], NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (sock == NULL) {
+ fprintf(stderr, "PR_Accept() failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ other_fds[nextIndex] = sock;
+ PR_FD_SET(sock, &readFdSet);
+ nextIndex++;
+ }
+ PR_FD_SET(fds[j], &readFdSet);
+ }
+
+ for (j = 2; j < nfds; j++) {
+ if (PR_FD_ISSET(fds[j], &readFdSet)) {
+ PRInt32 nBytes;
+
+ PR_FD_CLR(fds[j], &readFdSet);
+ nEvents++;
+ nBytes = PR_Read(fds[j], buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read() failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ /* Just to be safe */
+ buf[127] = '\0';
+ PR_Close(fds[j]);
+ if (debug_mode) {
+ printf("The server received \"%s\" from a client\n", buf);
+ }
+ } else {
+ PR_FD_SET(fds[j], &readFdSet);
+ other_fds[nextIndex] = fds[j];
+ nextIndex++;
+ }
+ }
+
+ PR_ASSERT(retVal == nEvents);
+ /* swap */
+ tmp = fds;
+ fds = other_fds;
+ other_fds = tmp;
+ nfds = nextIndex;
+ i += nEvents;
+ }
+
+ if (debug_mode) {
+ printf("Test passed\n");
+ }
+
+ PR_Cleanup();
+ goto exit_now;
+exit_now:
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
diff --git a/nsprpub/pr/tests/selct_to.c b/nsprpub/pr/tests/selct_to.c
new file mode 100644
index 0000000000..76ac99b2e1
--- /dev/null
+++ b/nsprpub/pr/tests/selct_to.c
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+** 1997 - Netscape Communications Corporation
+**
+** Name: prselect_to.c
+**
+** Description: tests PR_Select with sockets. Time out functions
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prio.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prnetdb.h"
+
+#include "obsolete/probslet.h"
+
+#include "prerror.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1, *listenSock2;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ PR_fd_set readFdSet;
+ char buf[128];
+ PRInt32 retVal;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("This program tests PR_Select with sockets. Timeout \n");
+ printf("operations are tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ if (debug_mode) {
+ printf("%s", buf);
+ }
+
+ /* Set up the fd set */
+ PR_FD_ZERO(&readFdSet);
+ PR_FD_SET(listenSock1, &readFdSet);
+ PR_FD_SET(listenSock2, &readFdSet);
+
+ /* Testing timeout */
+ if (debug_mode) {
+ printf("PR_Select should time out in 5 seconds\n");
+ }
+ retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL,
+ PR_SecondsToInterval(5));
+ if (retVal != 0) {
+ PR_snprintf(buf, sizeof(buf),
+ "PR_Select should time out and return 0, but it returns %ld\n",
+ retVal);
+ fprintf(stderr, "%s", buf);
+ if (retVal == -1) {
+ fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(),
+ PR_GetOSError());
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ if (debug_mode) {
+ printf("PR_Select timed out. Test passed.\n\n");
+ }
+
+ PR_Cleanup();
+
+exit_now:
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
diff --git a/nsprpub/pr/tests/select2.c b/nsprpub/pr/tests/select2.c
new file mode 100644
index 0000000000..da7360b025
--- /dev/null
+++ b/nsprpub/pr/tests/select2.c
@@ -0,0 +1,385 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: select2.c
+**
+** Description: Measure PR_Select and Empty_Select performance.
+**
+** Modification History:
+** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+#include "prttools.h"
+#include "primpl.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(OS2)
+#include <sys/time.h>
+#endif
+
+#define PORT 8000
+#define DEFAULT_COUNT 10
+PRInt32 count;
+
+
+/***********************************************************************
+** PRIVATE FUNCTION: Test_Result
+** DESCRIPTION: Used in conjunction with the regress tool, prints out the
+** status of the test case.
+** INPUTS: PASS/FAIL
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+**
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM: Determine what the status is and print accordingly.
+**
+***********************************************************************/
+
+
+static void Test_Result (int result)
+{
+ switch (result)
+ {
+ case PASS:
+ printf ("PASS\n");
+ break;
+ case FAIL:
+ printf ("FAIL\n");
+ break;
+ default:
+ printf ("NOSTATUS\n");
+ break;
+ }
+}
+
+static void EmptyPRSelect(void)
+{
+ PRInt32 index = count;
+ PRInt32 rv;
+
+ for (; index--;) {
+ rv = PR_Select(0, NULL, NULL, NULL, PR_INTERVAL_NO_WAIT);
+ }
+}
+
+static void EmptyNativeSelect(void)
+{
+ PRInt32 rv;
+ PRInt32 index = count;
+ struct timeval timeout;
+
+ timeout.tv_sec = timeout.tv_usec = 0;
+ for (; index--;) {
+ rv = select(0, NULL, NULL, NULL, &timeout);
+ }
+}
+
+static void PRSelectTest(void)
+{
+ PRFileDesc *listenSocket;
+ PRNetAddr serverAddr;
+
+ if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) {
+ printf("\tServer error creating listen socket\n");
+ }
+ return;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(INADDR_ANY);
+
+ if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tServer error binding to server address\n");
+ }
+ PR_Close(listenSocket);
+ return;
+ }
+
+ if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tServer error listening to server socket\n");
+ }
+ PR_Close(listenSocket);
+ return;
+ }
+ if (debug_mode) {
+ printf("Listening on port %d\n", PORT);
+ }
+
+ {
+ PRFileDesc *newSock;
+ PRNetAddr rAddr;
+ PRInt32 loops = 0;
+ PR_fd_set rdset;
+ PRInt32 rv;
+ PRInt32 bytesRead;
+ char buf[11];
+
+ loops++;
+
+ if (debug_mode) {
+ printf("Going into accept\n");
+ }
+
+ newSock = PR_Accept(listenSocket,
+ &rAddr,
+ PR_INTERVAL_NO_TIMEOUT);
+
+ if (newSock) {
+ if (debug_mode) {
+ printf("Got connection!\n");
+ }
+ } else {
+ if (debug_mode) {
+ printf("PR_Accept failed: error code %d\n", PR_GetError());
+ }
+ else {
+ Test_Result (FAIL);
+ }
+ }
+
+ PR_FD_ZERO(&rdset);
+ PR_FD_SET(newSock, &rdset);
+
+ if (debug_mode) {
+ printf("Going into select \n");
+ }
+
+ rv = PR_Select(0, &rdset, 0, 0, PR_INTERVAL_NO_TIMEOUT);
+
+ if (debug_mode) {
+ printf("return from select is %d\n", rv);
+ }
+
+ if (PR_FD_ISSET(newSock, &rdset)) {
+ if (debug_mode) {
+ printf("I can't believe it- the socket is ready okay!\n");
+ }
+ } else {
+ if (debug_mode) {
+ printf("Damn; the select test failed...\n");
+ }
+ else {
+ Test_Result (FAIL);
+ }
+ }
+
+ strcpy(buf, "XXXXXXXXXX");
+ bytesRead = PR_Recv(newSock, buf, 10, 0, PR_INTERVAL_NO_TIMEOUT);
+ buf[10] = '\0';
+
+ if (debug_mode) {
+ printf("Recv completed with %d bytes, %s\n", bytesRead, buf);
+ }
+
+ PR_Close(newSock);
+ }
+
+}
+
+#if defined(XP_UNIX)
+
+static void NativeSelectTest(void)
+{
+ PRFileDesc *listenSocket;
+ PRNetAddr serverAddr;
+
+ if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) {
+ printf("\tServer error creating listen socket\n");
+ }
+ return;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(INADDR_ANY);
+
+ if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tServer error binding to server address\n");
+ }
+ PR_Close(listenSocket);
+ return;
+ }
+
+ if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tServer error listening to server socket\n");
+ }
+ PR_Close(listenSocket);
+ return;
+ }
+ if (debug_mode) {
+ printf("Listening on port %d\n", PORT);
+ }
+
+ {
+ PRIntn osfd;
+ char buf[11];
+ fd_set rdset;
+ PRNetAddr rAddr;
+ PRFileDesc *newSock;
+ struct timeval timeout;
+ PRInt32 bytesRead, rv, loops = 0;
+
+ loops++;
+
+ if (debug_mode) {
+ printf("Going into accept\n");
+ }
+
+ newSock = PR_Accept(listenSocket, &rAddr, PR_INTERVAL_NO_TIMEOUT);
+
+ if (newSock) {
+ if (debug_mode) {
+ printf("Got connection!\n");
+ }
+ } else {
+ if (debug_mode) {
+ printf("PR_Accept failed: error code %d\n", PR_GetError());
+ }
+ else {
+ Test_Result (FAIL);
+ }
+ }
+
+ osfd = PR_FileDesc2NativeHandle(newSock);
+ FD_ZERO(&rdset);
+ FD_SET(osfd, &rdset);
+
+ if (debug_mode) {
+ printf("Going into select \n");
+ }
+
+
+ timeout.tv_sec = 2; timeout.tv_usec = 0;
+ rv = select(osfd + 1, &rdset, NULL, NULL, &timeout);
+
+ if (debug_mode) {
+ printf("return from select is %d\n", rv);
+ }
+
+ if (FD_ISSET(osfd, &rdset)) {
+ if (debug_mode) {
+ printf("I can't believe it- the socket is ready okay!\n");
+ }
+ } else {
+ if (debug_mode) {
+ printf("Damn; the select test failed...\n");
+ }
+ else {
+ Test_Result (FAIL);
+ }
+ }
+
+ strcpy(buf, "XXXXXXXXXX");
+ bytesRead = PR_Recv(newSock, buf, 10, 0, PR_INTERVAL_NO_TIMEOUT);
+ buf[10] = '\0';
+
+ if (debug_mode) {
+ printf("Recv completed with %d bytes, %s\n", bytesRead, buf);
+ }
+
+ PR_Close(newSock);
+ }
+
+} /* NativeSelectTest */
+
+#endif /* defined(XP_UNIX) */
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+ PRInt32 tot;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+ tot = PR_IntervalToMilliseconds(stop-start);
+
+ if (debug_mode) {
+ printf("%40s: %6.2f usec avg, %d msec total\n", msg, d / count, tot);
+ }
+}
+
+int main(int argc, char **argv)
+{
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (argc > 2) {
+ count = atoi(argv[2]);
+ } else {
+ count = DEFAULT_COUNT;
+ }
+
+#if defined(XP_UNIX)
+ Measure(NativeSelectTest, "time to call 1 element select()");
+#endif
+ Measure(EmptyPRSelect, "time to call Empty PR_select()");
+ Measure(EmptyNativeSelect, "time to call Empty select()");
+ Measure(PRSelectTest, "time to call 1 element PR_select()");
+
+ if (!debug_mode) {
+ Test_Result (NOSTATUS);
+ }
+ PR_Cleanup();
+
+
+}
diff --git a/nsprpub/pr/tests/selintr.c b/nsprpub/pr/tests/selintr.c
new file mode 100644
index 0000000000..84513f6887
--- /dev/null
+++ b/nsprpub/pr/tests/selintr.c
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Test whether classic NSPR's select() wrapper properly blocks
+ * the periodic SIGALRM clocks. On some platforms (such as
+ * HP-UX and SINIX) an interrupted select() system call is
+ * restarted with the originally specified timeout, ignoring
+ * the time that has elapsed. If a select() call is interrupted
+ * repeatedly, it will never time out. (See Bugzilla bug #39674.)
+ */
+
+#if !defined(XP_UNIX)
+
+/*
+ * This test is applicable to Unix only.
+ */
+
+int main()
+{
+ return 0;
+}
+
+#else /* XP_UNIX */
+
+#include "nspr.h"
+
+#include <sys/time.h>
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ struct timeval timeout;
+ int rv;
+
+ PR_SetError(0, 0); /* force NSPR to initialize */
+ PR_EnableClockInterrupts();
+
+ /* 2 seconds timeout */
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+ rv = select(1, NULL, NULL, NULL, &timeout);
+ printf("select returned %d\n", rv);
+ return 0;
+}
+
+#endif /* XP_UNIX */
diff --git a/nsprpub/pr/tests/sem.c b/nsprpub/pr/tests/sem.c
new file mode 100644
index 0000000000..80db76d2e9
--- /dev/null
+++ b/nsprpub/pr/tests/sem.c
@@ -0,0 +1,222 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: sem.c
+**
+** Description: Tests Semaphonre functions.
+**
+** Modification History:
+** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "prpriv.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+/*
+ Since we don't have stdin, stdout everywhere, we will fake
+ it with our in-memory buffers called stdin and stdout.
+*/
+
+#define SBSIZE 1024
+
+#include "obsolete/prsem.h"
+
+static char stdinBuf[SBSIZE];
+static char stdoutBuf[SBSIZE];
+
+static PRUintn stdinBufIdx = 0;
+static PRUintn stdoutBufIdx = 0;
+static PRStatus finalResult = PR_SUCCESS;
+
+
+static size_t dread (PRUintn device, char *buf, size_t bufSize)
+{
+ PRUintn i;
+
+ /* during first read call, initialize the stdinBuf buffer*/
+ if (stdinBufIdx == 0) {
+ for (i=0; i<SBSIZE; i++) {
+ stdinBuf[i] = i;
+ }
+ }
+
+ /* now copy data from stdinBuf to the given buffer upto bufSize */
+ for (i=0; i<bufSize; i++) {
+ if (stdinBufIdx == SBSIZE) {
+ break;
+ }
+ buf[i] = stdinBuf[stdinBufIdx++];
+ }
+
+ return i;
+}
+
+static size_t dwrite (PRUintn device, char *buf, size_t bufSize)
+{
+ PRUintn i, j;
+
+ /* copy data from the given buffer upto bufSize to stdoutBuf */
+ for (i=0; i<bufSize; i++) {
+ if (stdoutBufIdx == SBSIZE) {
+ break;
+ }
+ stdoutBuf[stdoutBufIdx++] = buf[i];
+ }
+
+ /* during last write call, compare the two buffers */
+ if (stdoutBufIdx == SBSIZE)
+ for (j=0; j<SBSIZE; j++)
+ if (stdinBuf[j] != stdoutBuf[j]) {
+ if (debug_mode) {
+ printf("data mismatch for index= %d \n", j);
+ }
+ finalResult = PR_FAILURE;
+ }
+
+ return i;
+}
+
+/*------------------ Following is the real test program ---------*/
+/*
+ Program to copy standard input to standard output. The program
+ uses two threads. One reads the input and puts the data in a
+ double buffer. The other reads the buffer contents and writes
+ it to standard output.
+*/
+
+PRSemaphore *emptyBufs; /* number of empty buffers */
+PRSemaphore *fullBufs; /* number of buffers that are full */
+
+#define BSIZE 100
+
+struct {
+ char data[BSIZE];
+ PRUintn nbytes; /* number of bytes in this buffer */
+} buf[2];
+
+static void PR_CALLBACK reader(void *arg)
+{
+ PRUintn i = 0;
+ size_t nbytes;
+
+ do {
+ (void) PR_WaitSem(emptyBufs);
+ nbytes = dread(0, buf[i].data, BSIZE);
+ buf[i].nbytes = nbytes;
+ PR_PostSem(fullBufs);
+ i = (i + 1) % 2;
+ } while (nbytes > 0);
+}
+
+static void writer(void)
+{
+ PRUintn i = 0;
+ size_t nbytes;
+
+ do {
+ (void) PR_WaitSem(fullBufs);
+ nbytes = buf[i].nbytes;
+ if (nbytes > 0) {
+ nbytes = dwrite(1, buf[i].data, nbytes);
+ PR_PostSem(emptyBufs);
+ i = (i + 1) % 2;
+ }
+ } while (nbytes > 0);
+}
+
+int main(int argc, char **argv)
+{
+ PRThread *r;
+
+ PR_STDIO_INIT();
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+
+ {
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ }
+
+ /* main test */
+
+ emptyBufs = PR_NewSem(2); /* two empty buffers */
+
+ fullBufs = PR_NewSem(0); /* zero full buffers */
+
+ /* create the reader thread */
+
+ r = PR_CreateThread(PR_USER_THREAD,
+ reader, 0,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+
+ /* Do the writer operation in this thread */
+ writer();
+
+ PR_DestroySem(emptyBufs);
+ PR_DestroySem(fullBufs);
+
+ if (finalResult == PR_SUCCESS) {
+ if (debug_mode) {
+ printf("sem Test Passed.\n");
+ }
+ }
+ else {
+ if (debug_mode) {
+ printf("sem Test Failed.\n");
+ }
+ failed_already=1;
+ }
+ PR_Cleanup();
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
diff --git a/nsprpub/pr/tests/sema.c b/nsprpub/pr/tests/sema.c
new file mode 100644
index 0000000000..81ccd01b7a
--- /dev/null
+++ b/nsprpub/pr/tests/sema.c
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+
+#ifdef DEBUG
+#define SEM_D "D"
+#else
+#define SEM_D
+#endif
+#ifdef IS_64
+#define SEM_64 "64"
+#else
+#define SEM_64
+#endif
+
+#define SEM_NAME1 "/tmp/foo.sem" SEM_D SEM_64
+#define SEM_NAME2 "/tmp/bar.sem" SEM_D SEM_64
+#define SEM_MODE 0666
+#define ITERATIONS 1000
+
+static PRBool debug_mode = PR_FALSE;
+static PRIntn iterations = ITERATIONS;
+static PRIntn counter;
+static PRSem *sem1, *sem2;
+
+/*
+ * Thread 2 waits on semaphore 2 and posts to semaphore 1.
+ */
+void ThreadFunc(void *arg)
+{
+ PRIntn i;
+
+ for (i = 0; i < iterations; i++) {
+ if (PR_WaitSemaphore(sem2) == PR_FAILURE) {
+ fprintf(stderr, "PR_WaitSemaphore failed\n");
+ exit(1);
+ }
+ if (counter == 2*i+1) {
+ if (debug_mode) {
+ printf("thread 2: counter = %d\n", counter);
+ }
+ } else {
+ fprintf(stderr, "thread 2: counter should be %d but is %d\n",
+ 2*i+1, counter);
+ exit(1);
+ }
+ counter++;
+ if (PR_PostSemaphore(sem1) == PR_FAILURE) {
+ fprintf(stderr, "PR_PostSemaphore failed\n");
+ exit(1);
+ }
+ }
+}
+
+static void Help(void)
+{
+ fprintf(stderr, "sema test program usage:\n");
+ fprintf(stderr, "\t-d debug mode (FALSE)\n");
+ fprintf(stderr, "\t-c <count> loop count (%d)\n", ITERATIONS);
+ fprintf(stderr, "\t-h this message\n");
+} /* Help */
+
+int main(int argc, char **argv)
+{
+ PRThread *thred;
+ PRIntn i;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dc:h");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option) {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'c': /* loop count */
+ iterations = atoi(opt->value);
+ break;
+ case 'h':
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (PR_DeleteSemaphore(SEM_NAME1) == PR_SUCCESS) {
+ fprintf(stderr, "warning: removed semaphore %s left over "
+ "from previous run\n", SEM_NAME1);
+ }
+ if (PR_DeleteSemaphore(SEM_NAME2) == PR_SUCCESS) {
+ fprintf(stderr, "warning: removed semaphore %s left over "
+ "from previous run\n", SEM_NAME2);
+ }
+
+ sem1 = PR_OpenSemaphore(SEM_NAME1, PR_SEM_CREATE, SEM_MODE, 1);
+ if (NULL == sem1) {
+ fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ sem2 = PR_OpenSemaphore(SEM_NAME2, PR_SEM_CREATE, SEM_MODE, 0);
+ if (NULL == sem2) {
+ fprintf(stderr, "PR_OpenSemaphore failed\n");
+ exit(1);
+ }
+ thred = PR_CreateThread(PR_USER_THREAD, ThreadFunc, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (NULL == thred) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+
+ /*
+ * Thread 1 waits on semaphore 1 and posts to semaphore 2.
+ */
+ for (i = 0; i < iterations; i++) {
+ if (PR_WaitSemaphore(sem1) == PR_FAILURE) {
+ fprintf(stderr, "PR_WaitSemaphore failed\n");
+ exit(1);
+ }
+ if (counter == 2*i) {
+ if (debug_mode) {
+ printf("thread 1: counter = %d\n", counter);
+ }
+ } else {
+ fprintf(stderr, "thread 1: counter should be %d but is %d\n",
+ 2*i, counter);
+ exit(1);
+ }
+ counter++;
+ if (PR_PostSemaphore(sem2) == PR_FAILURE) {
+ fprintf(stderr, "PR_PostSemaphore failed\n");
+ exit(1);
+ }
+ }
+
+ if (PR_JoinThread(thred) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+
+ if (PR_CloseSemaphore(sem1) == PR_FAILURE) {
+ fprintf(stderr, "PR_CloseSemaphore failed\n");
+ }
+ if (PR_CloseSemaphore(sem2) == PR_FAILURE) {
+ fprintf(stderr, "PR_CloseSemaphore failed\n");
+ }
+ if (PR_DeleteSemaphore(SEM_NAME1) == PR_FAILURE) {
+ fprintf(stderr, "PR_DeleteSemaphore failed\n");
+ }
+ if (PR_DeleteSemaphore(SEM_NAME2) == PR_FAILURE) {
+ fprintf(stderr, "PR_DeleteSemaphore failed\n");
+ }
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/semaerr.c b/nsprpub/pr/tests/semaerr.c
new file mode 100644
index 0000000000..f3c253b4f4
--- /dev/null
+++ b/nsprpub/pr/tests/semaerr.c
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+
+#ifdef DEBUG
+#define SEM_D "D"
+#else
+#define SEM_D
+#endif
+#ifdef IS_64
+#define SEM_64 "64"
+#else
+#define SEM_64
+#endif
+
+#define NO_SUCH_SEM_NAME "/tmp/nosuchsem.sem" SEM_D SEM_64
+#define SEM_NAME1 "/tmp/foo.sem" SEM_D SEM_64
+#define EXE_NAME "semaerr1"
+#define SEM_MODE 0666
+
+static PRBool debug_mode = PR_FALSE;
+
+static void Help(void)
+{
+ fprintf(stderr, "semaerr test program usage:\n");
+ fprintf(stderr, "\t-d debug mode (FALSE)\n");
+ fprintf(stderr, "\t-h this message\n");
+} /* Help */
+
+int main(int argc, char **argv)
+{
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dh");
+ PRSem *sem;
+ char *child_argv[32];
+ char **child_arg;
+ PRProcess *proc;
+ PRInt32 exit_code;
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option) {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'h':
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /*
+ * Open a nonexistent semaphore without the PR_SEM_CREATE
+ * flag should fail with PR_FILE_NOT_FOUND_ERROR.
+ */
+ (void) PR_DeleteSemaphore(NO_SUCH_SEM_NAME);
+ sem = PR_OpenSemaphore(NO_SUCH_SEM_NAME, 0, 0, 0);
+ if (NULL != sem) {
+ fprintf(stderr, "Opening nonexistent semaphore %s "
+ "without the PR_SEM_CREATE flag should fail "
+ "but succeeded\n", NO_SUCH_SEM_NAME);
+ exit(1);
+ }
+ if (PR_GetError() != PR_FILE_NOT_FOUND_ERROR) {
+ fprintf(stderr, "Expected error is %d but got (%d, %d)\n",
+ PR_FILE_NOT_FOUND_ERROR, PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+
+ /*
+ * Create a semaphore and let the another process
+ * try PR_SEM_CREATE and PR_SEM_CREATE|PR_SEM_EXCL.
+ */
+ if (PR_DeleteSemaphore(SEM_NAME1) == PR_SUCCESS) {
+ fprintf(stderr, "warning: deleted semaphore %s from previous "
+ "run of the test\n", SEM_NAME1);
+ }
+ sem = PR_OpenSemaphore(SEM_NAME1, PR_SEM_CREATE, SEM_MODE, 0);
+ if (sem == NULL) {
+ fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ child_arg = child_argv;
+ *child_arg++ = EXE_NAME;
+ if (debug_mode) {
+ *child_arg++ = "-d";
+ }
+ *child_arg = NULL;
+ proc = PR_CreateProcess(child_argv[0], child_argv, NULL, NULL);
+ if (proc == NULL) {
+ fprintf(stderr, "PR_CreateProcess failed\n");
+ exit(1);
+ }
+ if (PR_WaitProcess(proc, &exit_code) == PR_FAILURE) {
+ fprintf(stderr, "PR_WaitProcess failed\n");
+ exit(1);
+ }
+ if (exit_code != 0) {
+ fprintf(stderr, "process semaerr1 failed\n");
+ exit(1);
+ }
+ if (PR_CloseSemaphore(sem) == PR_FAILURE) {
+ fprintf(stderr, "PR_CloseSemaphore failed\n");
+ exit(1);
+ }
+ if (PR_DeleteSemaphore(SEM_NAME1) == PR_FAILURE) {
+ fprintf(stderr, "PR_DeleteSemaphore failed\n");
+ exit(1);
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/semaerr1.c b/nsprpub/pr/tests/semaerr1.c
new file mode 100644
index 0000000000..9271663d69
--- /dev/null
+++ b/nsprpub/pr/tests/semaerr1.c
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+
+#ifdef DEBUG
+#define SEM_D "D"
+#else
+#define SEM_D
+#endif
+#ifdef IS_64
+#define SEM_64 "64"
+#else
+#define SEM_64
+#endif
+
+#define SEM_NAME1 "/tmp/foo.sem" SEM_D SEM_64
+#define SEM_NAME2 "/tmp/bar.sem" SEM_D SEM_64
+#define SEM_MODE 0666
+
+static PRBool debug_mode = PR_FALSE;
+
+static void Help(void)
+{
+ fprintf(stderr, "semaerr1 test program usage:\n");
+ fprintf(stderr, "\t-d debug mode (FALSE)\n");
+ fprintf(stderr, "\t-h this message\n");
+} /* Help */
+
+int main(int argc, char **argv)
+{
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dh");
+ PRSem *sem;
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option) {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'h':
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /*
+ * PR_SEM_CREATE|PR_SEM_EXCL should be able to
+ * create a nonexistent semaphore.
+ */
+ (void) PR_DeleteSemaphore(SEM_NAME2);
+ sem = PR_OpenSemaphore(SEM_NAME2, PR_SEM_CREATE|PR_SEM_EXCL, SEM_MODE, 0);
+ if (sem == NULL) {
+ fprintf(stderr, "PR_OpenSemaphore failed\n");
+ exit(1);
+ }
+ if (PR_CloseSemaphore(sem) == PR_FAILURE) {
+ fprintf(stderr, "PR_CloseSemaphore failed\n");
+ exit(1);
+ }
+ if (PR_DeleteSemaphore(SEM_NAME2) == PR_FAILURE) {
+ fprintf(stderr, "PR_DeleteSemaphore failed\n");
+ exit(1);
+ }
+
+ /*
+ * Opening an existing semaphore with PR_SEM_CREATE|PR_SEM_EXCL.
+ * should fail with PR_FILE_EXISTS_ERROR.
+ */
+ sem = PR_OpenSemaphore(SEM_NAME1, PR_SEM_CREATE|PR_SEM_EXCL, SEM_MODE, 0);
+ if (sem != NULL) {
+ fprintf(stderr, "PR_OpenSemaphore should fail but succeeded\n");
+ exit(1);
+ }
+ if (PR_GetError() != PR_FILE_EXISTS_ERROR) {
+ fprintf(stderr, "Expect %d but got %d\n", PR_FILE_EXISTS_ERROR,
+ PR_GetError());
+ exit(1);
+ }
+
+ /*
+ * Try again, with just PR_SEM_CREATE. This should succeed.
+ */
+ sem = PR_OpenSemaphore(SEM_NAME1, PR_SEM_CREATE, SEM_MODE, 0);
+ if (sem == NULL) {
+ fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ if (PR_CloseSemaphore(sem) == PR_FAILURE) {
+ fprintf(stderr, "PR_CloseSemaphore failed\n");
+ exit(1);
+ }
+
+ sem = PR_OpenSemaphore(SEM_NAME2, PR_SEM_CREATE|PR_SEM_EXCL, SEM_MODE, 0);
+ if (sem == NULL) {
+ fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ if (PR_CloseSemaphore(sem) == PR_FAILURE) {
+ fprintf(stderr, "PR_CloseSemaphore failed\n");
+ exit(1);
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/semaping.c b/nsprpub/pr/tests/semaping.c
new file mode 100644
index 0000000000..3b653d524e
--- /dev/null
+++ b/nsprpub/pr/tests/semaping.c
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+
+#ifdef DEBUG
+#define SEM_D "D"
+#else
+#define SEM_D
+#endif
+#ifdef IS_64
+#define SEM_64 "64"
+#else
+#define SEM_64
+#endif
+
+#define SHM_NAME "/tmp/counter" SEM_D SEM_64
+#define SEM_NAME1 "/tmp/foo.sem" SEM_D SEM_64
+#define SEM_NAME2 "/tmp/bar.sem" SEM_D SEM_64
+#define EXE_NAME "semapong"
+#define SEM_MODE 0666
+#define SHM_MODE 0666
+#define ITERATIONS 1000
+
+static PRBool debug_mode = PR_FALSE;
+static PRIntn iterations = ITERATIONS;
+static PRSem *sem1, *sem2;
+
+static void Help(void)
+{
+ fprintf(stderr, "semaping test program usage:\n");
+ fprintf(stderr, "\t-d debug mode (FALSE)\n");
+ fprintf(stderr, "\t-c <count> loop count (%d)\n", ITERATIONS);
+ fprintf(stderr, "\t-h this message\n");
+} /* Help */
+
+int main(int argc, char **argv)
+{
+ PRProcess *proc;
+ PRIntn i;
+ char *child_argv[32];
+ char **child_arg;
+ char iterations_buf[32];
+ PRSharedMemory *shm;
+ PRIntn *counter_addr;
+ PRInt32 exit_code;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dc:h");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option) {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'c': /* loop count */
+ iterations = atoi(opt->value);
+ break;
+ case 'h':
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (PR_DeleteSharedMemory(SHM_NAME) == PR_SUCCESS) {
+ fprintf(stderr, "warning: removed shared memory %s left over "
+ "from previous run\n", SHM_NAME);
+ }
+ if (PR_DeleteSemaphore(SEM_NAME1) == PR_SUCCESS) {
+ fprintf(stderr, "warning: removed semaphore %s left over "
+ "from previous run\n", SEM_NAME1);
+ }
+ if (PR_DeleteSemaphore(SEM_NAME2) == PR_SUCCESS) {
+ fprintf(stderr, "warning: removed semaphore %s left over "
+ "from previous run\n", SEM_NAME2);
+ }
+
+ shm = PR_OpenSharedMemory(SHM_NAME, sizeof(*counter_addr), PR_SHM_CREATE, SHM_MODE);
+ if (NULL == shm) {
+ fprintf(stderr, "PR_OpenSharedMemory failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ counter_addr = PR_AttachSharedMemory(shm, 0);
+ if (NULL == counter_addr) {
+ fprintf(stderr, "PR_AttachSharedMemory failed\n");
+ exit(1);
+ }
+ *counter_addr = 0;
+ sem1 = PR_OpenSemaphore(SEM_NAME1, PR_SEM_CREATE, SEM_MODE, 1);
+ if (NULL == sem1) {
+ fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ sem2 = PR_OpenSemaphore(SEM_NAME2, PR_SEM_CREATE, SEM_MODE, 0);
+ if (NULL == sem2) {
+ fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+
+ child_arg = &child_argv[0];
+ *child_arg++ = EXE_NAME;
+ if (debug_mode != PR_FALSE) {
+ *child_arg++ = "-d";
+ }
+ if (iterations != ITERATIONS) {
+ *child_arg++ = "-c";
+ PR_snprintf(iterations_buf, sizeof(iterations_buf), "%d", iterations);
+ *child_arg++ = iterations_buf;
+ }
+ *child_arg = NULL;
+ proc = PR_CreateProcess(child_argv[0], child_argv, NULL, NULL);
+ if (NULL == proc) {
+ fprintf(stderr, "PR_CreateProcess failed\n");
+ exit(1);
+ }
+
+ /*
+ * Process 1 waits on semaphore 1 and posts to semaphore 2.
+ */
+ for (i = 0; i < iterations; i++) {
+ if (PR_WaitSemaphore(sem1) == PR_FAILURE) {
+ fprintf(stderr, "PR_WaitSemaphore failed\n");
+ exit(1);
+ }
+ if (*counter_addr == 2*i) {
+ if (debug_mode) {
+ printf("process 1: counter = %d\n", *counter_addr);
+ }
+ } else {
+ fprintf(stderr, "process 1: counter should be %d but is %d\n",
+ 2*i, *counter_addr);
+ exit(1);
+ }
+ (*counter_addr)++;
+ if (PR_PostSemaphore(sem2) == PR_FAILURE) {
+ fprintf(stderr, "PR_PostSemaphore failed\n");
+ exit(1);
+ }
+ }
+ if (PR_DetachSharedMemory(shm, counter_addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_DetachSharedMemory failed\n");
+ exit(1);
+ }
+ if (PR_CloseSharedMemory(shm) == PR_FAILURE) {
+ fprintf(stderr, "PR_CloseSharedMemory failed\n");
+ exit(1);
+ }
+ if (PR_CloseSemaphore(sem1) == PR_FAILURE) {
+ fprintf(stderr, "PR_CloseSemaphore failed\n");
+ }
+ if (PR_CloseSemaphore(sem2) == PR_FAILURE) {
+ fprintf(stderr, "PR_CloseSemaphore failed\n");
+ }
+
+ if (PR_WaitProcess(proc, &exit_code) == PR_FAILURE) {
+ fprintf(stderr, "PR_WaitProcess failed\n");
+ exit(1);
+ }
+ if (exit_code != 0) {
+ fprintf(stderr, "process 2 failed with exit code %d\n", exit_code);
+ exit(1);
+ }
+
+ if (PR_DeleteSharedMemory(SHM_NAME) == PR_FAILURE) {
+ fprintf(stderr, "PR_DeleteSharedMemory failed\n");
+ }
+ if (PR_DeleteSemaphore(SEM_NAME1) == PR_FAILURE) {
+ fprintf(stderr, "PR_DeleteSemaphore failed\n");
+ }
+ if (PR_DeleteSemaphore(SEM_NAME2) == PR_FAILURE) {
+ fprintf(stderr, "PR_DeleteSemaphore failed\n");
+ }
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/semapong.c b/nsprpub/pr/tests/semapong.c
new file mode 100644
index 0000000000..fd24824e31
--- /dev/null
+++ b/nsprpub/pr/tests/semapong.c
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+
+#ifdef DEBUG
+#define SEM_D "D"
+#else
+#define SEM_D
+#endif
+#ifdef IS_64
+#define SEM_64 "64"
+#else
+#define SEM_64
+#endif
+
+#define SHM_NAME "/tmp/counter" SEM_D SEM_64
+#define SEM_NAME1 "/tmp/foo.sem" SEM_D SEM_64
+#define SEM_NAME2 "/tmp/bar.sem" SEM_D SEM_64
+#define ITERATIONS 1000
+
+static PRBool debug_mode = PR_FALSE;
+static PRIntn iterations = ITERATIONS;
+static PRSem *sem1, *sem2;
+
+static void Help(void)
+{
+ fprintf(stderr, "semapong test program usage:\n");
+ fprintf(stderr, "\t-d debug mode (FALSE)\n");
+ fprintf(stderr, "\t-c <count> loop count (%d)\n", ITERATIONS);
+ fprintf(stderr, "\t-h this message\n");
+} /* Help */
+
+int main(int argc, char **argv)
+{
+ PRIntn i;
+ PRSharedMemory *shm;
+ PRIntn *counter_addr;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dc:h");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option) {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'c': /* loop count */
+ iterations = atoi(opt->value);
+ break;
+ case 'h':
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ shm = PR_OpenSharedMemory(SHM_NAME, sizeof(*counter_addr), 0, 0666);
+ if (NULL == shm) {
+ fprintf(stderr, "PR_OpenSharedMemory failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ sem1 = PR_OpenSemaphore(SEM_NAME1, 0, 0, 0);
+ if (NULL == sem1) {
+ fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ sem2 = PR_OpenSemaphore(SEM_NAME2, 0, 0, 0);
+ if (NULL == sem2) {
+ fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+
+ counter_addr = PR_AttachSharedMemory(shm, 0);
+ if (NULL == counter_addr) {
+ fprintf(stderr, "PR_AttachSharedMemory failed\n");
+ exit(1);
+ }
+
+ /*
+ * Process 2 waits on semaphore 2 and posts to semaphore 1.
+ */
+ for (i = 0; i < iterations; i++) {
+ if (PR_WaitSemaphore(sem2) == PR_FAILURE) {
+ fprintf(stderr, "PR_WaitSemaphore failed\n");
+ exit(1);
+ }
+ if (*counter_addr == 2*i+1) {
+ if (debug_mode) {
+ printf("process 2: counter = %d\n", *counter_addr);
+ }
+ } else {
+ fprintf(stderr, "process 2: counter should be %d but is %d\n",
+ 2*i+1, *counter_addr);
+ exit(1);
+ }
+ (*counter_addr)++;
+ if (PR_PostSemaphore(sem1) == PR_FAILURE) {
+ fprintf(stderr, "PR_PostSemaphore failed\n");
+ exit(1);
+ }
+ }
+ if (PR_DetachSharedMemory(shm, counter_addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_DetachSharedMemory failed\n");
+ exit(1);
+ }
+ if (PR_CloseSharedMemory(shm) == PR_FAILURE) {
+ fprintf(stderr, "PR_CloseSharedMemory failed\n");
+ exit(1);
+ }
+ if (PR_CloseSemaphore(sem1) == PR_FAILURE) {
+ fprintf(stderr, "PR_CloseSemaphore failed\n");
+ }
+ if (PR_CloseSemaphore(sem2) == PR_FAILURE) {
+ fprintf(stderr, "PR_CloseSemaphore failed\n");
+ }
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/sendzlf.c b/nsprpub/pr/tests/sendzlf.c
new file mode 100644
index 0000000000..a65f6cd6b4
--- /dev/null
+++ b/nsprpub/pr/tests/sendzlf.c
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Test: sendzlf.c
+ *
+ * Description: send a zero-length file with PR_SendFile and
+ * PR_TransmitFile.
+ */
+
+#define ZERO_LEN_FILE_NAME "zerolen.tmp"
+#define HEADER_STR "Header"
+#define HEADER_LEN 6 /* length of HEADER_STR, not counting the null byte */
+#define TRAILER_STR "Trailer"
+#define TRAILER_LEN 7 /* length of TRAILER_STR, not counting the null byte */
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void ClientThread(void *arg)
+{
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ PRUint16 port = (PRUint16) arg;
+ char buf[1024];
+ char *bufPtr;
+ PRInt32 nbytes;
+ PRInt32 ntotal;
+ PRInt32 nexpected;
+
+ sock = PR_NewTCPSocket();
+ if (NULL == sock) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ exit(1);
+ }
+ if (PR_InitializeNetAddr(PR_IpAddrLoopback, port, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_InitializeNetAddr failed\n");
+ exit(1);
+ }
+ if (PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ fprintf(stderr, "PR_Connect failed\n");
+ exit(1);
+ }
+ ntotal = 0;
+ bufPtr = buf;
+ while ((nbytes = PR_Read(sock, bufPtr, sizeof(buf)-ntotal)) > 0) {
+ ntotal += nbytes;
+ bufPtr += nbytes;
+ }
+ if (-1 == nbytes) {
+ fprintf(stderr, "PR_Read failed\n");
+ exit(1);
+ }
+ nexpected = HEADER_LEN+TRAILER_LEN+TRAILER_LEN+HEADER_LEN+HEADER_LEN;
+ if (ntotal != nexpected) {
+ fprintf(stderr, "total bytes read should be %d but is %d\n",
+ nexpected, ntotal);
+ exit(1);
+ }
+ if (memcmp(buf, HEADER_STR TRAILER_STR TRAILER_STR HEADER_STR HEADER_STR,
+ nexpected) != 0) {
+ fprintf(stderr, "wrong data is received\n");
+ exit(1);
+ }
+ if (PR_Close(sock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+}
+
+static void ServerThread(void *arg)
+{
+ PRFileDesc *listenSock = (PRFileDesc *) arg;
+ PRFileDesc *acceptSock;
+ PRFileDesc *file;
+ PRSendFileData sfd;
+ char header[1024], trailer[1024];
+ PRInt32 nbytes;
+
+ /* Create a zero-length file */
+ file = PR_Open(ZERO_LEN_FILE_NAME,
+ PR_CREATE_FILE|PR_TRUNCATE|PR_RDWR, 0666);
+ if (NULL == file) {
+ fprintf(stderr, "PR_Open failed\n");
+ exit(1);
+ }
+ sfd.fd = file;
+ sfd.file_offset = 0;
+ sfd.file_nbytes = 0;
+ memcpy(header, HEADER_STR, HEADER_LEN);
+ memcpy(trailer, TRAILER_STR, TRAILER_LEN);
+ sfd.header = header;
+ sfd.hlen = HEADER_LEN;
+ sfd.trailer = trailer;
+ sfd.tlen = TRAILER_LEN;
+ acceptSock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == acceptSock) {
+ fprintf(stderr, "PR_Accept failed\n");
+ exit(1);
+ }
+ /* Send both header and trailer */
+ nbytes = PR_SendFile(acceptSock, &sfd, PR_TRANSMITFILE_KEEP_OPEN,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (HEADER_LEN+TRAILER_LEN != nbytes) {
+ fprintf(stderr, "PR_SendFile should return %d but returned %d\n",
+ HEADER_LEN+TRAILER_LEN, nbytes);
+ exit(1);
+ }
+ /* Trailer only, no header */
+ sfd.hlen = 0;
+ nbytes = PR_SendFile(acceptSock, &sfd, PR_TRANSMITFILE_KEEP_OPEN,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (TRAILER_LEN != nbytes) {
+ fprintf(stderr, "PR_SendFile should return %d but returned %d\n",
+ TRAILER_LEN, nbytes);
+ exit(1);
+ }
+ /* Header only, no trailer */
+ sfd.hlen = HEADER_LEN;
+ sfd.tlen = 0;
+ nbytes = PR_SendFile(acceptSock, &sfd, PR_TRANSMITFILE_KEEP_OPEN,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (HEADER_LEN != nbytes) {
+ fprintf(stderr, "PR_SendFile should return %d but returned %d\n",
+ HEADER_LEN, nbytes);
+ exit(1);
+ }
+ /* Try PR_TransmitFile */
+ nbytes = PR_TransmitFile(acceptSock, file, header, HEADER_LEN,
+ PR_TRANSMITFILE_KEEP_OPEN, PR_INTERVAL_NO_TIMEOUT);
+ if (HEADER_LEN != nbytes) {
+ fprintf(stderr, "PR_TransmitFile should return %d but returned %d\n",
+ HEADER_LEN, nbytes);
+ exit(1);
+ }
+ if (PR_Close(acceptSock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ if (PR_Close(file) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ if (PR_Delete(ZERO_LEN_FILE_NAME) == PR_FAILURE) {
+ fprintf(stderr, "PR_Delete failed\n");
+ exit(1);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock;
+ PRThread *clientThread;
+ PRThread *serverThread;
+ PRNetAddr addr;
+ PRThreadScope scope = PR_GLOBAL_THREAD;
+
+ listenSock = PR_NewTCPSocket();
+ if (NULL == listenSock) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ exit(1);
+ }
+ if (PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_InitializeNetAddr failed\n");
+ exit(1);
+ }
+ if (PR_Bind(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_Bind failed\n");
+ exit(1);
+ }
+ /* Find out what port number we are bound to. */
+ if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ exit(1);
+ }
+ if (PR_Listen(listenSock, 5) == PR_FAILURE) {
+ fprintf(stderr, "PR_Listen failed\n");
+ exit(1);
+ }
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ ClientThread, (void *) PR_ntohs(PR_NetAddrInetPort(&addr)),
+ PR_PRIORITY_NORMAL, scope, PR_JOINABLE_THREAD, 0);
+ if (NULL == clientThread) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+ serverThread = PR_CreateThread(PR_USER_THREAD,
+ ServerThread, listenSock,
+ PR_PRIORITY_NORMAL, scope, PR_JOINABLE_THREAD, 0);
+ if (NULL == serverThread) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+ if (PR_JoinThread(clientThread) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+ if (PR_JoinThread(serverThread) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+ if (PR_Close(listenSock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/server_test.c b/nsprpub/pr/tests/server_test.c
new file mode 100644
index 0000000000..93ffe4aff2
--- /dev/null
+++ b/nsprpub/pr/tests/server_test.c
@@ -0,0 +1,692 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** This server simulates a server running in loopback mode.
+**
+** The idea is that a single server is created. The server initially creates
+** a number of worker threads. Then, with the server running, a number of
+** clients are created which start requesting service from the server.
+**
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "pprthred.h"
+
+#include <string.h>
+
+#define PORT 15004
+#define THREAD_STACKSIZE 0
+
+#define PASS 0
+#define FAIL 1
+static int debug_mode = 0;
+static int failed_already = 0;
+
+static int _iterations = 1000;
+static int _clients = 1;
+static int _client_data = 250;
+static int _server_data = (8*1024);
+
+static PRThreadScope ServerScope, ClientScope;
+
+#define SERVER "Server"
+#define MAIN "Main"
+
+#define SERVER_STATE_STARTUP 0
+#define SERVER_STATE_READY 1
+#define SERVER_STATE_DYING 2
+#define SERVER_STATE_DEAD 4
+int ServerState;
+PRLock *ServerStateCVLock;
+PRCondVar *ServerStateCV;
+
+#undef DEBUGPRINTS
+#ifdef DEBUGPRINTS
+#define DPRINTF printf
+#else
+#define DPRINTF
+#endif
+
+
+/***********************************************************************
+** PRIVATE FUNCTION: Test_Result
+** DESCRIPTION: Used in conjunction with the regress tool, prints out the
+** status of the test case.
+** INPUTS: PASS/FAIL
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+**
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM: Determine what the status is and print accordingly.
+**
+***********************************************************************/
+
+
+static void Test_Result (int result)
+{
+ switch (result)
+ {
+ case PASS:
+ printf ("PASS\n");
+ break;
+ case FAIL:
+ printf ("FAIL\n");
+ failed_already = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static void do_work(void);
+
+/* --- Server state functions --------------------------------------------- */
+void
+SetServerState(char *waiter, PRInt32 state)
+{
+ PR_Lock(ServerStateCVLock);
+ ServerState = state;
+ PR_NotifyCondVar(ServerStateCV);
+
+ if (debug_mode) {
+ DPRINTF("\t%s changed state to %d\n", waiter, state);
+ }
+
+ PR_Unlock(ServerStateCVLock);
+}
+
+int
+WaitServerState(char *waiter, PRInt32 state)
+{
+ PRInt32 rv;
+
+ PR_Lock(ServerStateCVLock);
+
+ if (debug_mode) {
+ DPRINTF("\t%s waiting for state %d\n", waiter, state);
+ }
+
+ while(!(ServerState & state)) {
+ PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT);
+ }
+ rv = ServerState;
+
+ if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n",
+ waiter, state, ServerState);
+ PR_Unlock(ServerStateCVLock);
+
+ return rv;
+}
+
+/* --- Server Functions ------------------------------------------- */
+
+PRLock *workerThreadsLock;
+PRInt32 workerThreads;
+PRInt32 workerThreadsBusy;
+
+void
+WorkerThreadFunc(void *_listenSock)
+{
+ PRFileDesc *listenSock = (PRFileDesc *)_listenSock;
+ PRInt32 bytesRead;
+ PRInt32 bytesWritten;
+ char *dataBuf;
+ char *sendBuf;
+
+ if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n",
+ _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32);
+ dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32);
+ if (!dataBuf)
+ if (debug_mode) {
+ printf("\tServer could not malloc space!?\n");
+ }
+ sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) {
+ printf("\tServer could not malloc space!?\n");
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tServer worker thread running\n");
+ }
+
+ while(1) {
+ PRInt32 bytesToRead = _client_data;
+ PRInt32 bytesToWrite = _server_data;
+ PRFileDesc *newSock;
+ PRNetAddr *rAddr;
+ PRInt32 loops = 0;
+
+ loops++;
+
+ if (debug_mode) {
+ DPRINTF("\tServer thread going into accept\n");
+ }
+
+ bytesRead = PR_AcceptRead(listenSock,
+ &newSock,
+ &rAddr,
+ dataBuf,
+ bytesToRead,
+ PR_INTERVAL_NO_TIMEOUT);
+
+ if (bytesRead < 0) {
+ if (debug_mode) {
+ printf("\tServer error in accept (%d)\n", bytesRead);
+ }
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead);
+ }
+
+ PR_AtomicIncrement(&workerThreadsBusy);
+ if (workerThreadsBusy == workerThreads) {
+ PR_Lock(workerThreadsLock);
+ if (workerThreadsBusy == workerThreads) {
+ PRThread *WorkerThread;
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSock,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) {
+ printf("Error creating client thread %d\n", workerThreads);
+ }
+ } else {
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) {
+ DPRINTF("\tServer creates worker (%d)\n", workerThreads);
+ }
+ }
+ }
+ PR_Unlock(workerThreadsLock);
+ }
+
+ bytesToRead -= bytesRead;
+ while (bytesToRead) {
+ bytesRead = PR_Recv(newSock,
+ dataBuf,
+ bytesToRead,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesRead < 0) {
+ if (debug_mode) {
+ printf("\tServer error receiving data (%d)\n", bytesRead);
+ }
+ continue;
+ }
+ if (debug_mode) {
+ DPRINTF("\tServer received %d bytes\n", bytesRead);
+ }
+ }
+
+ bytesWritten = PR_Send(newSock,
+ sendBuf,
+ bytesToWrite,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesWritten != _server_data) {
+ if (debug_mode) printf("\tError sending data to client (%d, %d)\n",
+ bytesWritten, PR_GetOSError());
+ } else {
+ if (debug_mode) {
+ DPRINTF("\tServer sent %d bytes\n", bytesWritten);
+ }
+ }
+
+ PR_Close(newSock);
+ PR_AtomicDecrement(&workerThreadsBusy);
+ }
+}
+
+PRFileDesc *
+ServerSetup(void)
+{
+ PRFileDesc *listenSocket;
+ PRSocketOptionData sockOpt;
+ PRNetAddr serverAddr;
+ PRThread *WorkerThread;
+
+ if ((listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) {
+ printf("\tServer error creating listen socket\n");
+ }
+ else {
+ Test_Result(FAIL);
+ }
+ return NULL;
+ }
+
+ sockOpt.option = PR_SockOpt_Reuseaddr;
+ sockOpt.value.reuse_addr = PR_TRUE;
+ if (PR_SetSocketOption(listenSocket, &sockOpt) != PR_SUCCESS) {
+ if (debug_mode) printf("\tServer error setting socket option: OS error %d\n",
+ PR_GetOSError());
+ else {
+ Test_Result(FAIL);
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+
+ if (PR_Bind(listenSocket, &serverAddr) != PR_SUCCESS) {
+ if (debug_mode) printf("\tServer error binding to server address: OS error %d\n",
+ PR_GetOSError());
+ else {
+ Test_Result(FAIL);
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ if (PR_Listen(listenSocket, 128) != PR_SUCCESS) {
+ if (debug_mode) {
+ printf("\tServer error listening to server socket\n");
+ }
+ else {
+ Test_Result(FAIL);
+ }
+ PR_Close(listenSocket);
+
+ return NULL;
+ }
+
+ /* Create Clients */
+ workerThreads = 0;
+ workerThreadsBusy = 0;
+
+ workerThreadsLock = PR_NewLock();
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSocket,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) {
+ printf("error creating working thread\n");
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) {
+ DPRINTF("\tServer created primordial worker thread\n");
+ }
+
+ return listenSocket;
+}
+
+/* The main server loop */
+void
+ServerThreadFunc(void *unused)
+{
+ PRFileDesc *listenSocket;
+
+ /* Do setup */
+ listenSocket = ServerSetup();
+
+ if (!listenSocket) {
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ } else {
+
+ if (debug_mode) {
+ DPRINTF("\tServer up\n");
+ }
+
+ /* Tell clients they can start now. */
+ SetServerState(SERVER, SERVER_STATE_READY);
+
+ /* Now wait for server death signal */
+ WaitServerState(SERVER, SERVER_STATE_DYING);
+
+ /* Cleanup */
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ }
+}
+
+/* --- Client Functions ------------------------------------------- */
+
+PRInt32 numRequests;
+PRInt32 numClients;
+PRMonitor *clientMonitor;
+
+void
+ClientThreadFunc(void *unused)
+{
+ PRNetAddr serverAddr;
+ PRFileDesc *clientSocket;
+ char *sendBuf;
+ char *recvBuf;
+ PRInt32 rv;
+ PRInt32 bytesNeeded;
+
+ sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) {
+ printf("\tClient could not malloc space!?\n");
+ }
+ recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char));
+ if (!recvBuf)
+ if (debug_mode) {
+ printf("\tClient could not malloc space!?\n");
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+
+ while(numRequests > 0) {
+
+ if ( (numRequests % 10) == 0 )
+ if (debug_mode) {
+ printf(".");
+ }
+ if (debug_mode) {
+ DPRINTF("\tClient starting request %d\n", numRequests);
+ }
+
+ clientSocket = PR_NewTCPSocket();
+ if (!clientSocket) {
+ if (debug_mode) printf("Client error creating socket: OS error %d\n",
+ PR_GetOSError());
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient connecting\n");
+ }
+
+ rv = PR_Connect(clientSocket,
+ &serverAddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (!clientSocket) {
+ if (debug_mode) {
+ printf("\tClient error connecting\n");
+ }
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient connected\n");
+ }
+
+ rv = PR_Send(clientSocket,
+ sendBuf,
+ _client_data,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != _client_data) {
+ if (debug_mode) {
+ printf("Client error sending data (%d)\n", rv);
+ }
+ PR_Close(clientSocket);
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient sent %d bytes\n", rv);
+ }
+
+ bytesNeeded = _server_data;
+ while(bytesNeeded) {
+ rv = PR_Recv(clientSocket,
+ recvBuf,
+ bytesNeeded,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv <= 0) {
+ if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n",
+ rv, (_server_data - bytesNeeded), _server_data);
+ break;
+ }
+ if (debug_mode) {
+ DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv);
+ }
+ bytesNeeded -= rv;
+ }
+
+ PR_Close(clientSocket);
+
+ PR_AtomicDecrement(&numRequests);
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ --numClients;
+ PR_Notify(clientMonitor);
+ PR_ExitMonitor(clientMonitor);
+
+ PR_DELETE(sendBuf);
+ PR_DELETE(recvBuf);
+}
+
+void
+RunClients(void)
+{
+ PRInt32 index;
+
+ numRequests = _iterations;
+ numClients = _clients;
+ clientMonitor = PR_NewMonitor();
+
+ for (index=0; index<_clients; index++) {
+ PRThread *clientThread;
+
+
+ clientThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ClientThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ClientScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!clientThread) {
+ if (debug_mode) {
+ printf("\terror creating client thread %d\n", index);
+ }
+ } else if (debug_mode) {
+ DPRINTF("\tMain created client %d/%d\n", index+1, _clients);
+ }
+
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ while(numClients) {
+ PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(clientMonitor);
+}
+
+/* --- Main Function ---------------------------------------------- */
+
+static
+void do_work()
+{
+ PRThread *ServerThread;
+ PRInt32 state;
+
+ SetServerState(MAIN, SERVER_STATE_STARTUP);
+ ServerThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ServerThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_JOINABLE_THREAD,
+ THREAD_STACKSIZE);
+ if (!ServerThread) {
+ if (debug_mode) {
+ printf("error creating main server thread\n");
+ }
+ return;
+ }
+
+ /* Wait for server to be ready */
+ state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD);
+
+ if (!(state & SERVER_STATE_DEAD)) {
+ /* Run Test Clients */
+ RunClients();
+
+ /* Send death signal to server */
+ SetServerState(MAIN, SERVER_STATE_DYING);
+ }
+
+ PR_JoinThread(ServerThread);
+}
+
+static void do_workUU(void)
+{
+ ServerScope = PR_LOCAL_THREAD;
+ ClientScope = PR_LOCAL_THREAD;
+ do_work();
+}
+
+static void do_workUK(void)
+{
+ ServerScope = PR_LOCAL_THREAD;
+ ClientScope = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+static void do_workKU(void)
+{
+ ServerScope = PR_GLOBAL_THREAD;
+ ClientScope = PR_LOCAL_THREAD;
+ do_work();
+}
+
+static void do_workKK(void)
+{
+ ServerScope = PR_GLOBAL_THREAD;
+ ClientScope = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ if (debug_mode) {
+ printf("\n%40s: %6.2f usec\n", msg, d / _iterations);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+ if (debug_mode) {
+ printf("Enter number of iterations: \n");
+ scanf("%d", &_iterations);
+ printf("Enter number of clients : \n");
+ scanf("%d", &_clients);
+ printf("Enter size of client data : \n");
+ scanf("%d", &_client_data);
+ printf("Enter size of server data : \n");
+ scanf("%d", &_server_data);
+ }
+ else
+ {
+
+ _iterations = 10;
+ _clients = 1;
+ _client_data = 10;
+ _server_data = 10;
+ }
+
+ if (debug_mode) {
+ printf("\n\n%d iterations with %d client threads.\n",
+ _iterations, _clients);
+ printf("Sending %d bytes of client data and %d bytes of server data\n",
+ _client_data, _server_data);
+ }
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ ServerStateCVLock = PR_NewLock();
+ ServerStateCV = PR_NewCondVar(ServerStateCVLock);
+
+ Measure(do_workUU, "server loop user/user");
+#if 0
+ Measure(do_workUK, "server loop user/kernel");
+ Measure(do_workKU, "server loop kernel/user");
+ Measure(do_workKK, "server loop kernel/kernel");
+#endif
+
+ PR_Cleanup();
+
+ return failed_already;
+}
diff --git a/nsprpub/pr/tests/servr_kk.c b/nsprpub/pr/tests/servr_kk.c
new file mode 100644
index 0000000000..06f4fae117
--- /dev/null
+++ b/nsprpub/pr/tests/servr_kk.c
@@ -0,0 +1,659 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** This server simulates a server running in loopback mode.
+**
+** The idea is that a single server is created. The server initially creates
+** a number of worker threads. Then, with the server running, a number of
+** clients are created which start requesting service from the server.
+**
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "pprthred.h"
+
+#include <string.h>
+
+#define PORT 15004
+#define THREAD_STACKSIZE 0
+
+static int _iterations = 1000;
+static int _clients = 1;
+static int _client_data = 250;
+static int _server_data = (8*1024);
+
+static PRThreadScope ServerScope, ClientScope;
+
+#define SERVER "Server"
+#define MAIN "Main"
+
+#define SERVER_STATE_STARTUP 0
+#define SERVER_STATE_READY 1
+#define SERVER_STATE_DYING 2
+#define SERVER_STATE_DEAD 4
+int ServerState;
+PRLock *ServerStateCVLock;
+PRCondVar *ServerStateCV;
+
+#ifdef DEBUGPRINTS
+#define DPRINTF printf
+#else
+#define DPRINTF
+#endif
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+static void do_work(void);
+
+/* --- Server state functions --------------------------------------------- */
+void
+SetServerState(char *waiter, PRInt32 state)
+{
+ PR_Lock(ServerStateCVLock);
+ ServerState = state;
+ PR_NotifyCondVar(ServerStateCV);
+
+ if (debug_mode) {
+ DPRINTF("\t%s changed state to %d\n", waiter, state);
+ }
+
+ PR_Unlock(ServerStateCVLock);
+}
+
+int
+WaitServerState(char *waiter, PRInt32 state)
+{
+ PRInt32 rv;
+
+ PR_Lock(ServerStateCVLock);
+
+ if (debug_mode) {
+ DPRINTF("\t%s waiting for state %d\n", waiter, state);
+ }
+
+ while(!(ServerState & state)) {
+ PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT);
+ }
+ rv = ServerState;
+
+ if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n",
+ waiter, state, ServerState);
+ PR_Unlock(ServerStateCVLock);
+
+ return rv;
+}
+
+/* --- Server Functions ------------------------------------------- */
+
+PRLock *workerThreadsLock;
+PRInt32 workerThreads;
+PRInt32 workerThreadsBusy;
+
+void
+WorkerThreadFunc(void *_listenSock)
+{
+ PRFileDesc *listenSock = (PRFileDesc *)_listenSock;
+ PRInt32 bytesRead;
+ PRInt32 bytesWritten;
+ char *dataBuf;
+ char *sendBuf;
+
+ if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n",
+ _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32);
+ dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32);
+ if (!dataBuf)
+ if (debug_mode) {
+ printf("\tServer could not malloc space!?\n");
+ }
+ sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) {
+ printf("\tServer could not malloc space!?\n");
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tServer worker thread running\n");
+ }
+
+ while(1) {
+ PRInt32 bytesToRead = _client_data;
+ PRInt32 bytesToWrite = _server_data;
+ PRFileDesc *newSock;
+ PRNetAddr *rAddr;
+ PRInt32 loops = 0;
+
+ loops++;
+
+ if (debug_mode) {
+ DPRINTF("\tServer thread going into accept\n");
+ }
+
+ bytesRead = PR_AcceptRead(listenSock,
+ &newSock,
+ &rAddr,
+ dataBuf,
+ bytesToRead,
+ PR_INTERVAL_NO_TIMEOUT);
+
+ if (bytesRead < 0) {
+ if (debug_mode) {
+ printf("\tServer error in accept (%d)\n", bytesRead);
+ }
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead);
+ }
+
+ PR_AtomicIncrement(&workerThreadsBusy);
+ if (workerThreadsBusy == workerThreads) {
+ PR_Lock(workerThreadsLock);
+ if (workerThreadsBusy == workerThreads) {
+ PRThread *WorkerThread;
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSock,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) {
+ printf("Error creating client thread %d\n", workerThreads);
+ }
+ } else {
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) {
+ DPRINTF("\tServer creates worker (%d)\n", workerThreads);
+ }
+ }
+ }
+ PR_Unlock(workerThreadsLock);
+ }
+
+ bytesToRead -= bytesRead;
+ while (bytesToRead) {
+ bytesRead = PR_Recv(newSock,
+ dataBuf,
+ bytesToRead,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesRead < 0) {
+ if (debug_mode) {
+ printf("\tServer error receiving data (%d)\n", bytesRead);
+ }
+ continue;
+ }
+ if (debug_mode) {
+ DPRINTF("\tServer received %d bytes\n", bytesRead);
+ }
+ }
+
+ bytesWritten = PR_Send(newSock,
+ sendBuf,
+ bytesToWrite,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesWritten != _server_data) {
+ if (debug_mode) printf("\tError sending data to client (%d, %d)\n",
+ bytesWritten, PR_GetOSError());
+ } else {
+ if (debug_mode) {
+ DPRINTF("\tServer sent %d bytes\n", bytesWritten);
+ }
+ }
+
+ PR_Close(newSock);
+ PR_AtomicDecrement(&workerThreadsBusy);
+ }
+}
+
+PRFileDesc *
+ServerSetup(void)
+{
+ PRFileDesc *listenSocket;
+ PRSocketOptionData sockOpt;
+ PRNetAddr serverAddr;
+ PRThread *WorkerThread;
+
+ if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) {
+ printf("\tServer error creating listen socket\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return NULL;
+ }
+
+ sockOpt.option = PR_SockOpt_Reuseaddr;
+ sockOpt.value.reuse_addr = PR_TRUE;
+ if ( PR_SetSocketOption(listenSocket, &sockOpt) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error setting socket option: OS error %d\n",
+ PR_GetOSError());
+ else {
+ failed_already=1;
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+
+ if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error binding to server address: OS error %d\n",
+ PR_GetOSError());
+ else {
+ failed_already=1;
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tServer error listening to server socket\n");
+ }
+ else {
+ failed_already=1;
+ }
+ PR_Close(listenSocket);
+
+ return NULL;
+ }
+
+ /* Create Clients */
+ workerThreads = 0;
+ workerThreadsBusy = 0;
+
+ workerThreadsLock = PR_NewLock();
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSocket,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) {
+ printf("error creating working thread\n");
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) {
+ DPRINTF("\tServer created primordial worker thread\n");
+ }
+
+ return listenSocket;
+}
+
+/* The main server loop */
+void
+ServerThreadFunc(void *unused)
+{
+ PRFileDesc *listenSocket;
+
+ /* Do setup */
+ listenSocket = ServerSetup();
+
+ if (!listenSocket) {
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ } else {
+
+ if (debug_mode) {
+ DPRINTF("\tServer up\n");
+ }
+
+ /* Tell clients they can start now. */
+ SetServerState(SERVER, SERVER_STATE_READY);
+
+ /* Now wait for server death signal */
+ WaitServerState(SERVER, SERVER_STATE_DYING);
+
+ /* Cleanup */
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ }
+}
+
+/* --- Client Functions ------------------------------------------- */
+
+PRInt32 numRequests;
+PRInt32 numClients;
+PRMonitor *clientMonitor;
+
+void
+ClientThreadFunc(void *unused)
+{
+ PRNetAddr serverAddr;
+ PRFileDesc *clientSocket;
+ char *sendBuf;
+ char *recvBuf;
+ PRInt32 rv;
+ PRInt32 bytesNeeded;
+
+ sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) {
+ printf("\tClient could not malloc space!?\n");
+ }
+ recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char));
+ if (!recvBuf)
+ if (debug_mode) {
+ printf("\tClient could not malloc space!?\n");
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+
+ while(numRequests > 0) {
+
+ if ( (numRequests % 10) == 0 )
+ if (debug_mode) {
+ printf(".");
+ }
+ if (debug_mode) {
+ DPRINTF("\tClient starting request %d\n", numRequests);
+ }
+
+ clientSocket = PR_NewTCPSocket();
+ if (!clientSocket) {
+ if (debug_mode) printf("Client error creating socket: OS error %d\n",
+ PR_GetOSError());
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient connecting\n");
+ }
+
+ rv = PR_Connect(clientSocket,
+ &serverAddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (!clientSocket) {
+ if (debug_mode) {
+ printf("\tClient error connecting\n");
+ }
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient connected\n");
+ }
+
+ rv = PR_Send(clientSocket,
+ sendBuf,
+ _client_data,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != _client_data) {
+ if (debug_mode) {
+ printf("Client error sending data (%d)\n", rv);
+ }
+ PR_Close(clientSocket);
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient sent %d bytes\n", rv);
+ }
+
+ bytesNeeded = _server_data;
+ while(bytesNeeded) {
+ rv = PR_Recv(clientSocket,
+ recvBuf,
+ bytesNeeded,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv <= 0) {
+ if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n",
+ rv, (_server_data - bytesNeeded), _server_data);
+ break;
+ }
+ if (debug_mode) {
+ DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv);
+ }
+ bytesNeeded -= rv;
+ }
+
+ PR_Close(clientSocket);
+
+ PR_AtomicDecrement(&numRequests);
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ --numClients;
+ PR_Notify(clientMonitor);
+ PR_ExitMonitor(clientMonitor);
+
+ PR_DELETE(sendBuf);
+ PR_DELETE(recvBuf);
+}
+
+void
+RunClients(void)
+{
+ PRInt32 index;
+
+ numRequests = _iterations;
+ numClients = _clients;
+ clientMonitor = PR_NewMonitor();
+
+ for (index=0; index<_clients; index++) {
+ PRThread *clientThread;
+
+
+ clientThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ClientThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ClientScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!clientThread) {
+ if (debug_mode) {
+ printf("\terror creating client thread %d\n", index);
+ }
+ } else if (debug_mode) {
+ DPRINTF("\tMain created client %d/%d\n", index+1, _clients);
+ }
+
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ while(numClients) {
+ PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(clientMonitor);
+}
+
+/* --- Main Function ---------------------------------------------- */
+
+static
+void do_work()
+{
+ PRThread *ServerThread;
+ PRInt32 state;
+
+ SetServerState(MAIN, SERVER_STATE_STARTUP);
+ ServerThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ServerThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_JOINABLE_THREAD,
+ THREAD_STACKSIZE);
+ if (!ServerThread) {
+ if (debug_mode) {
+ printf("error creating main server thread\n");
+ }
+ return;
+ }
+
+ /* Wait for server to be ready */
+ state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD);
+
+ if (!(state & SERVER_STATE_DEAD)) {
+ /* Run Test Clients */
+ RunClients();
+
+ /* Send death signal to server */
+ SetServerState(MAIN, SERVER_STATE_DYING);
+ }
+
+ PR_JoinThread(ServerThread);
+}
+
+static void do_workUU(void)
+{
+ ServerScope = PR_LOCAL_THREAD;
+ ClientScope = PR_LOCAL_THREAD;
+ do_work();
+}
+
+static void do_workUK(void)
+{
+ ServerScope = PR_LOCAL_THREAD;
+ ClientScope = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+static void do_workKU(void)
+{
+ ServerScope = PR_GLOBAL_THREAD;
+ ClientScope = PR_LOCAL_THREAD;
+ do_work();
+}
+
+static void do_workKK(void)
+{
+ ServerScope = PR_GLOBAL_THREAD;
+ ClientScope = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ if (debug_mode) {
+ printf("\n%40s: %6.2f usec\n", msg, d / _iterations);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+ if (debug_mode) {
+ printf("Enter number of iterations: \n");
+ scanf("%d", &_iterations);
+ printf("Enter number of clients : \n");
+ scanf("%d", &_clients);
+ printf("Enter size of client data : \n");
+ scanf("%d", &_client_data);
+ printf("Enter size of server data : \n");
+ scanf("%d", &_server_data);
+ }
+ else
+ {
+ _iterations = 7;
+ _clients = 7;
+ _client_data = 100;
+ _server_data = 100;
+ }
+
+ if (debug_mode) {
+ printf("\n\n%d iterations with %d client threads.\n",
+ _iterations, _clients);
+ printf("Sending %d bytes of client data and %d bytes of server data\n",
+ _client_data, _server_data);
+ }
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ PR_SetThreadRecycleMode(64);
+
+ ServerStateCVLock = PR_NewLock();
+ ServerStateCV = PR_NewCondVar(ServerStateCVLock);
+
+
+ Measure(do_workKK, "server loop kernel/kernel");
+
+ PR_Cleanup();
+
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+}
diff --git a/nsprpub/pr/tests/servr_ku.c b/nsprpub/pr/tests/servr_ku.c
new file mode 100644
index 0000000000..d0c446f52f
--- /dev/null
+++ b/nsprpub/pr/tests/servr_ku.c
@@ -0,0 +1,639 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** This server simulates a server running in loopback mode.
+**
+** The idea is that a single server is created. The server initially creates
+** a number of worker threads. Then, with the server running, a number of
+** clients are created which start requesting service from the server.
+**
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "pprthred.h"
+
+#include <string.h>
+
+#define PORT 15004
+#define THREAD_STACKSIZE 0
+
+static int _iterations = 1000;
+static int _clients = 1;
+static int _client_data = 250;
+static int _server_data = (8*1024);
+
+static PRThreadScope ServerScope, ClientScope;
+
+#define SERVER "Server"
+#define MAIN "Main"
+
+#define SERVER_STATE_STARTUP 0
+#define SERVER_STATE_READY 1
+#define SERVER_STATE_DYING 2
+#define SERVER_STATE_DEAD 4
+int ServerState;
+PRLock *ServerStateCVLock;
+PRCondVar *ServerStateCV;
+
+#ifdef DEBUGPRINTS
+#define DPRINTF printf
+#else
+#define DPRINTF
+#endif
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+static void do_work(void);
+
+/* --- Server state functions --------------------------------------------- */
+void
+SetServerState(char *waiter, PRInt32 state)
+{
+ PR_Lock(ServerStateCVLock);
+ ServerState = state;
+ PR_NotifyCondVar(ServerStateCV);
+
+ if (debug_mode) {
+ DPRINTF("\t%s changed state to %d\n", waiter, state);
+ }
+
+ PR_Unlock(ServerStateCVLock);
+}
+
+int
+WaitServerState(char *waiter, PRInt32 state)
+{
+ PRInt32 rv;
+
+ PR_Lock(ServerStateCVLock);
+
+ if (debug_mode) {
+ DPRINTF("\t%s waiting for state %d\n", waiter, state);
+ }
+
+ while(!(ServerState & state)) {
+ PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT);
+ }
+ rv = ServerState;
+
+ if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n",
+ waiter, state, ServerState);
+ PR_Unlock(ServerStateCVLock);
+
+ return rv;
+}
+
+/* --- Server Functions ------------------------------------------- */
+
+PRLock *workerThreadsLock;
+PRInt32 workerThreads;
+PRInt32 workerThreadsBusy;
+
+void
+WorkerThreadFunc(void *_listenSock)
+{
+ PRFileDesc *listenSock = (PRFileDesc *)_listenSock;
+ PRInt32 bytesRead;
+ PRInt32 bytesWritten;
+ char *dataBuf;
+ char *sendBuf;
+
+ if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n",
+ _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32);
+ dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32);
+ if (!dataBuf)
+ if (debug_mode) {
+ printf("\tServer could not malloc space!?\n");
+ }
+ sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) {
+ printf("\tServer could not malloc space!?\n");
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tServer worker thread running\n");
+ }
+
+ while(1) {
+ PRInt32 bytesToRead = _client_data;
+ PRInt32 bytesToWrite = _server_data;
+ PRFileDesc *newSock;
+ PRNetAddr *rAddr;
+ PRInt32 loops = 0;
+
+ loops++;
+
+ if (debug_mode) {
+ DPRINTF("\tServer thread going into accept\n");
+ }
+
+ bytesRead = PR_AcceptRead(listenSock,
+ &newSock,
+ &rAddr,
+ dataBuf,
+ bytesToRead,
+ PR_INTERVAL_NO_TIMEOUT);
+
+ if (bytesRead < 0) {
+ if (debug_mode) {
+ printf("\tServer error in accept (%d)\n", bytesRead);
+ }
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead);
+ }
+
+ PR_AtomicIncrement(&workerThreadsBusy);
+ if (workerThreadsBusy == workerThreads) {
+ PR_Lock(workerThreadsLock);
+ if (workerThreadsBusy == workerThreads) {
+ PRThread *WorkerThread;
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSock,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) {
+ printf("Error creating client thread %d\n", workerThreads);
+ }
+ } else {
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) {
+ DPRINTF("\tServer creates worker (%d)\n", workerThreads);
+ }
+ }
+ }
+ PR_Unlock(workerThreadsLock);
+ }
+
+ bytesToRead -= bytesRead;
+ while (bytesToRead) {
+ bytesRead = PR_Recv(newSock,
+ dataBuf,
+ bytesToRead,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesRead < 0) {
+ if (debug_mode) {
+ printf("\tServer error receiving data (%d)\n", bytesRead);
+ }
+ continue;
+ }
+ if (debug_mode) {
+ DPRINTF("\tServer received %d bytes\n", bytesRead);
+ }
+ }
+
+ bytesWritten = PR_Send(newSock,
+ sendBuf,
+ bytesToWrite,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesWritten != _server_data) {
+ if (debug_mode) printf("\tError sending data to client (%d, %d)\n",
+ bytesWritten, PR_GetOSError());
+ } else {
+ if (debug_mode) {
+ DPRINTF("\tServer sent %d bytes\n", bytesWritten);
+ }
+ }
+
+ PR_Close(newSock);
+ PR_AtomicDecrement(&workerThreadsBusy);
+ }
+}
+
+PRFileDesc *
+ServerSetup(void)
+{
+ PRFileDesc *listenSocket;
+ PRSocketOptionData sockOpt;
+ PRNetAddr serverAddr;
+ PRThread *WorkerThread;
+
+ if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) {
+ printf("\tServer error creating listen socket\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return NULL;
+ }
+
+ sockOpt.option = PR_SockOpt_Reuseaddr;
+ sockOpt.value.reuse_addr = PR_TRUE;
+ if ( PR_SetSocketOption(listenSocket, &sockOpt) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error setting socket option: OS error %d\n",
+ PR_GetOSError());
+ else {
+ failed_already=1;
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+
+ if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error binding to server address: OS error %d\n",
+ PR_GetOSError());
+ else {
+ failed_already=1;
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tServer error listening to server socket\n");
+ }
+ else {
+ failed_already=1;
+ }
+ PR_Close(listenSocket);
+
+ return NULL;
+ }
+
+ /* Create Clients */
+ workerThreads = 0;
+ workerThreadsBusy = 0;
+
+ workerThreadsLock = PR_NewLock();
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSocket,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) {
+ printf("error creating working thread\n");
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) {
+ DPRINTF("\tServer created primordial worker thread\n");
+ }
+
+ return listenSocket;
+}
+
+/* The main server loop */
+void
+ServerThreadFunc(void *unused)
+{
+ PRFileDesc *listenSocket;
+
+ /* Do setup */
+ listenSocket = ServerSetup();
+
+ if (!listenSocket) {
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ } else {
+
+ if (debug_mode) {
+ DPRINTF("\tServer up\n");
+ }
+
+ /* Tell clients they can start now. */
+ SetServerState(SERVER, SERVER_STATE_READY);
+
+ /* Now wait for server death signal */
+ WaitServerState(SERVER, SERVER_STATE_DYING);
+
+ /* Cleanup */
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ }
+}
+
+/* --- Client Functions ------------------------------------------- */
+
+PRInt32 numRequests;
+PRInt32 numClients;
+PRMonitor *clientMonitor;
+
+void
+ClientThreadFunc(void *unused)
+{
+ PRNetAddr serverAddr;
+ PRFileDesc *clientSocket;
+ char *sendBuf;
+ char *recvBuf;
+ PRInt32 rv;
+ PRInt32 bytesNeeded;
+
+ sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) {
+ printf("\tClient could not malloc space!?\n");
+ }
+ recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char));
+ if (!recvBuf)
+ if (debug_mode) {
+ printf("\tClient could not malloc space!?\n");
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+
+ while(numRequests > 0) {
+
+ if ( (numRequests % 10) == 0 )
+ if (debug_mode) {
+ printf(".");
+ }
+ if (debug_mode) {
+ DPRINTF("\tClient starting request %d\n", numRequests);
+ }
+
+ clientSocket = PR_NewTCPSocket();
+ if (!clientSocket) {
+ if (debug_mode) printf("Client error creating socket: OS error %d\n",
+ PR_GetOSError());
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient connecting\n");
+ }
+
+ rv = PR_Connect(clientSocket,
+ &serverAddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (!clientSocket) {
+ if (debug_mode) {
+ printf("\tClient error connecting\n");
+ }
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient connected\n");
+ }
+
+ rv = PR_Send(clientSocket,
+ sendBuf,
+ _client_data,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != _client_data) {
+ if (debug_mode) {
+ printf("Client error sending data (%d)\n", rv);
+ }
+ PR_Close(clientSocket);
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient sent %d bytes\n", rv);
+ }
+
+ bytesNeeded = _server_data;
+ while(bytesNeeded) {
+ rv = PR_Recv(clientSocket,
+ recvBuf,
+ bytesNeeded,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv <= 0) {
+ if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n",
+ rv, (_server_data - bytesNeeded), _server_data);
+ break;
+ }
+ if (debug_mode) {
+ DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv);
+ }
+ bytesNeeded -= rv;
+ }
+
+ PR_Close(clientSocket);
+
+ PR_AtomicDecrement(&numRequests);
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ --numClients;
+ PR_Notify(clientMonitor);
+ PR_ExitMonitor(clientMonitor);
+
+ PR_DELETE(sendBuf);
+ PR_DELETE(recvBuf);
+}
+
+void
+RunClients(void)
+{
+ PRInt32 index;
+
+ numRequests = _iterations;
+ numClients = _clients;
+ clientMonitor = PR_NewMonitor();
+
+ for (index=0; index<_clients; index++) {
+ PRThread *clientThread;
+
+
+ clientThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ClientThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ClientScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!clientThread) {
+ if (debug_mode) {
+ printf("\terror creating client thread %d\n", index);
+ }
+ } else if (debug_mode) {
+ DPRINTF("\tMain created client %d/%d\n", index+1, _clients);
+ }
+
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ while(numClients) {
+ PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(clientMonitor);
+}
+
+/* --- Main Function ---------------------------------------------- */
+
+static
+void do_work()
+{
+ PRThread *ServerThread;
+ PRInt32 state;
+
+ SetServerState(MAIN, SERVER_STATE_STARTUP);
+ ServerThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ServerThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_JOINABLE_THREAD,
+ THREAD_STACKSIZE);
+ if (!ServerThread) {
+ if (debug_mode) {
+ printf("error creating main server thread\n");
+ }
+ return;
+ }
+
+ /* Wait for server to be ready */
+ state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD);
+
+ if (!(state & SERVER_STATE_DEAD)) {
+ /* Run Test Clients */
+ RunClients();
+
+ /* Send death signal to server */
+ SetServerState(MAIN, SERVER_STATE_DYING);
+ }
+
+ PR_JoinThread(ServerThread);
+}
+
+
+static void do_workKU(void)
+{
+ ServerScope = PR_GLOBAL_THREAD;
+ ClientScope = PR_LOCAL_THREAD;
+ do_work();
+}
+
+
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ if (debug_mode) {
+ printf("\n%40s: %6.2f usec\n", msg, d / _iterations);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+ if (debug_mode) {
+ printf("Enter number of iterations: \n");
+ scanf("%d", &_iterations);
+ printf("Enter number of clients : \n");
+ scanf("%d", &_clients);
+ printf("Enter size of client data : \n");
+ scanf("%d", &_client_data);
+ printf("Enter size of server data : \n");
+ scanf("%d", &_server_data);
+ }
+ else
+ {
+ _iterations = 7;
+ _clients = 7;
+ _client_data = 100;
+ _server_data = 100;
+ }
+
+ if (debug_mode) {
+ printf("\n\n%d iterations with %d client threads.\n",
+ _iterations, _clients);
+ printf("Sending %d bytes of client data and %d bytes of server data\n",
+ _client_data, _server_data);
+ }
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ PR_SetThreadRecycleMode(64);
+
+ ServerStateCVLock = PR_NewLock();
+ ServerStateCV = PR_NewCondVar(ServerStateCVLock);
+
+ Measure(do_workKU, "server loop kernel/user");
+
+ PR_Cleanup();
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+}
diff --git a/nsprpub/pr/tests/servr_uk.c b/nsprpub/pr/tests/servr_uk.c
new file mode 100644
index 0000000000..bd97c8f35a
--- /dev/null
+++ b/nsprpub/pr/tests/servr_uk.c
@@ -0,0 +1,640 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** This server simulates a server running in loopback mode.
+**
+** The idea is that a single server is created. The server initially creates
+** a number of worker threads. Then, with the server running, a number of
+** clients are created which start requesting service from the server.
+**
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "pprthred.h"
+
+#include <string.h>
+
+#define PORT 15004
+#define THREAD_STACKSIZE 0
+
+static int _iterations = 1000;
+static int _clients = 1;
+static int _client_data = 250;
+static int _server_data = (8*1024);
+
+static PRThreadScope ServerScope, ClientScope;
+
+#define SERVER "Server"
+#define MAIN "Main"
+
+#define SERVER_STATE_STARTUP 0
+#define SERVER_STATE_READY 1
+#define SERVER_STATE_DYING 2
+#define SERVER_STATE_DEAD 4
+int ServerState;
+PRLock *ServerStateCVLock;
+PRCondVar *ServerStateCV;
+
+#ifdef DEBUGPRINTS
+#define DPRINTF printf
+#else
+#define DPRINTF
+#endif
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+
+
+static void do_work(void);
+
+/* --- Server state functions --------------------------------------------- */
+void
+SetServerState(char *waiter, PRInt32 state)
+{
+ PR_Lock(ServerStateCVLock);
+ ServerState = state;
+ PR_NotifyCondVar(ServerStateCV);
+
+ if (debug_mode) {
+ DPRINTF("\t%s changed state to %d\n", waiter, state);
+ }
+
+ PR_Unlock(ServerStateCVLock);
+}
+
+int
+WaitServerState(char *waiter, PRInt32 state)
+{
+ PRInt32 rv;
+
+ PR_Lock(ServerStateCVLock);
+
+ if (debug_mode) {
+ DPRINTF("\t%s waiting for state %d\n", waiter, state);
+ }
+
+ while(!(ServerState & state)) {
+ PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT);
+ }
+ rv = ServerState;
+
+ if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n",
+ waiter, state, ServerState);
+ PR_Unlock(ServerStateCVLock);
+
+ return rv;
+}
+
+/* --- Server Functions ------------------------------------------- */
+
+PRLock *workerThreadsLock;
+PRInt32 workerThreads;
+PRInt32 workerThreadsBusy;
+
+void
+WorkerThreadFunc(void *_listenSock)
+{
+ PRFileDesc *listenSock = (PRFileDesc *)_listenSock;
+ PRInt32 bytesRead;
+ PRInt32 bytesWritten;
+ char *dataBuf;
+ char *sendBuf;
+
+ if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n",
+ _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32);
+ dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32);
+ if (!dataBuf)
+ if (debug_mode) {
+ printf("\tServer could not malloc space!?\n");
+ }
+ sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) {
+ printf("\tServer could not malloc space!?\n");
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tServer worker thread running\n");
+ }
+
+ while(1) {
+ PRInt32 bytesToRead = _client_data;
+ PRInt32 bytesToWrite = _server_data;
+ PRFileDesc *newSock;
+ PRNetAddr *rAddr;
+ PRInt32 loops = 0;
+
+ loops++;
+
+ if (debug_mode) {
+ DPRINTF("\tServer thread going into accept\n");
+ }
+
+ bytesRead = PR_AcceptRead(listenSock,
+ &newSock,
+ &rAddr,
+ dataBuf,
+ bytesToRead,
+ PR_INTERVAL_NO_TIMEOUT);
+
+ if (bytesRead < 0) {
+ if (debug_mode) {
+ printf("\tServer error in accept (%d)\n", bytesRead);
+ }
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead);
+ }
+
+ PR_AtomicIncrement(&workerThreadsBusy);
+ if (workerThreadsBusy == workerThreads) {
+ PR_Lock(workerThreadsLock);
+ if (workerThreadsBusy == workerThreads) {
+ PRThread *WorkerThread;
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSock,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) {
+ printf("Error creating client thread %d\n", workerThreads);
+ }
+ } else {
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) {
+ DPRINTF("\tServer creates worker (%d)\n", workerThreads);
+ }
+ }
+ }
+ PR_Unlock(workerThreadsLock);
+ }
+
+ bytesToRead -= bytesRead;
+ while (bytesToRead) {
+ bytesRead = PR_Recv(newSock,
+ dataBuf,
+ bytesToRead,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesRead < 0) {
+ if (debug_mode) {
+ printf("\tServer error receiving data (%d)\n", bytesRead);
+ }
+ continue;
+ }
+ if (debug_mode) {
+ DPRINTF("\tServer received %d bytes\n", bytesRead);
+ }
+ }
+
+ bytesWritten = PR_Send(newSock,
+ sendBuf,
+ bytesToWrite,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesWritten != _server_data) {
+ if (debug_mode) printf("\tError sending data to client (%d, %d)\n",
+ bytesWritten, PR_GetOSError());
+ } else {
+ if (debug_mode) {
+ DPRINTF("\tServer sent %d bytes\n", bytesWritten);
+ }
+ }
+
+ PR_Close(newSock);
+ PR_AtomicDecrement(&workerThreadsBusy);
+ }
+}
+
+PRFileDesc *
+ServerSetup(void)
+{
+ PRFileDesc *listenSocket;
+ PRSocketOptionData sockOpt;
+ PRNetAddr serverAddr;
+ PRThread *WorkerThread;
+
+ if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) {
+ printf("\tServer error creating listen socket\n");
+ }
+ else {
+ return NULL;
+ }
+ }
+
+ sockOpt.option = PR_SockOpt_Reuseaddr;
+ sockOpt.value.reuse_addr = PR_TRUE;
+ if ( PR_SetSocketOption(listenSocket, &sockOpt) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error setting socket option: OS error %d\n",
+ PR_GetOSError());
+ else {
+ failed_already=1;
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+
+ if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error binding to server address: OS error %d\n",
+ PR_GetOSError());
+ else {
+ failed_already=1;
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tServer error listening to server socket\n");
+ }
+ else {
+ failed_already=1;
+ }
+ PR_Close(listenSocket);
+
+ return NULL;
+ }
+
+ /* Create Clients */
+ workerThreads = 0;
+ workerThreadsBusy = 0;
+
+ workerThreadsLock = PR_NewLock();
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSocket,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) {
+ printf("error creating working thread\n");
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) {
+ DPRINTF("\tServer created primordial worker thread\n");
+ }
+
+ return listenSocket;
+}
+
+/* The main server loop */
+void
+ServerThreadFunc(void *unused)
+{
+ PRFileDesc *listenSocket;
+
+ /* Do setup */
+ listenSocket = ServerSetup();
+
+ if (!listenSocket) {
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ } else {
+
+ if (debug_mode) {
+ DPRINTF("\tServer up\n");
+ }
+
+ /* Tell clients they can start now. */
+ SetServerState(SERVER, SERVER_STATE_READY);
+
+ /* Now wait for server death signal */
+ WaitServerState(SERVER, SERVER_STATE_DYING);
+
+ /* Cleanup */
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ }
+}
+
+/* --- Client Functions ------------------------------------------- */
+
+PRInt32 numRequests;
+PRInt32 numClients;
+PRMonitor *clientMonitor;
+
+void
+ClientThreadFunc(void *unused)
+{
+ PRNetAddr serverAddr;
+ PRFileDesc *clientSocket;
+ char *sendBuf;
+ char *recvBuf;
+ PRInt32 rv;
+ PRInt32 bytesNeeded;
+
+ sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) {
+ printf("\tClient could not malloc space!?\n");
+ }
+ recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char));
+ if (!recvBuf)
+ if (debug_mode) {
+ printf("\tClient could not malloc space!?\n");
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+
+ while(numRequests > 0) {
+
+ if ( (numRequests % 10) == 0 )
+ if (debug_mode) {
+ printf(".");
+ }
+ if (debug_mode) {
+ DPRINTF("\tClient starting request %d\n", numRequests);
+ }
+
+ clientSocket = PR_NewTCPSocket();
+ if (!clientSocket) {
+ if (debug_mode) printf("Client error creating socket: OS error %d\n",
+ PR_GetOSError());
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient connecting\n");
+ }
+
+ rv = PR_Connect(clientSocket,
+ &serverAddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (!clientSocket) {
+ if (debug_mode) {
+ printf("\tClient error connecting\n");
+ }
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient connected\n");
+ }
+
+ rv = PR_Send(clientSocket,
+ sendBuf,
+ _client_data,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != _client_data) {
+ if (debug_mode) {
+ printf("Client error sending data (%d)\n", rv);
+ }
+ PR_Close(clientSocket);
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient sent %d bytes\n", rv);
+ }
+
+ bytesNeeded = _server_data;
+ while(bytesNeeded) {
+ rv = PR_Recv(clientSocket,
+ recvBuf,
+ bytesNeeded,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv <= 0) {
+ if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n",
+ rv, (_server_data - bytesNeeded), _server_data);
+ break;
+ }
+ if (debug_mode) {
+ DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv);
+ }
+ bytesNeeded -= rv;
+ }
+
+ PR_Close(clientSocket);
+
+ PR_AtomicDecrement(&numRequests);
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ --numClients;
+ PR_Notify(clientMonitor);
+ PR_ExitMonitor(clientMonitor);
+
+ PR_DELETE(sendBuf);
+ PR_DELETE(recvBuf);
+}
+
+void
+RunClients(void)
+{
+ PRInt32 index;
+
+ numRequests = _iterations;
+ numClients = _clients;
+ clientMonitor = PR_NewMonitor();
+
+ for (index=0; index<_clients; index++) {
+ PRThread *clientThread;
+
+
+ clientThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ClientThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ClientScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!clientThread) {
+ if (debug_mode) {
+ printf("\terror creating client thread %d\n", index);
+ }
+ } else if (debug_mode) {
+ DPRINTF("\tMain created client %d/%d\n", index+1, _clients);
+ }
+
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ while(numClients) {
+ PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(clientMonitor);
+}
+
+/* --- Main Function ---------------------------------------------- */
+
+static
+void do_work()
+{
+ PRThread *ServerThread;
+ PRInt32 state;
+
+ SetServerState(MAIN, SERVER_STATE_STARTUP);
+ ServerThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ServerThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_JOINABLE_THREAD,
+ THREAD_STACKSIZE);
+ if (!ServerThread) {
+ if (debug_mode) {
+ printf("error creating main server thread\n");
+ }
+ return;
+ }
+
+ /* Wait for server to be ready */
+ state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD);
+
+ if (!(state & SERVER_STATE_DEAD)) {
+ /* Run Test Clients */
+ RunClients();
+
+ /* Send death signal to server */
+ SetServerState(MAIN, SERVER_STATE_DYING);
+ }
+
+ PR_JoinThread(ServerThread);
+}
+
+
+static void do_workUK(void)
+{
+ ServerScope = PR_LOCAL_THREAD;
+ ClientScope = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ if (debug_mode) {
+ printf("\n%40s: %6.2f usec\n", msg, d / _iterations);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+ if (debug_mode) {
+ printf("Enter number of iterations: \n");
+ scanf("%d", &_iterations);
+ printf("Enter number of clients : \n");
+ scanf("%d", &_clients);
+ printf("Enter size of client data : \n");
+ scanf("%d", &_client_data);
+ printf("Enter size of server data : \n");
+ scanf("%d", &_server_data);
+ }
+ else
+ {
+ _iterations = 7;
+ _clients = 7;
+ _client_data = 100;
+ _server_data = 100;
+ }
+
+ if (debug_mode) {
+ printf("\n\n%d iterations with %d client threads.\n",
+ _iterations, _clients);
+ printf("Sending %d bytes of client data and %d bytes of server data\n",
+ _client_data, _server_data);
+ }
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ PR_SetThreadRecycleMode(64);
+
+ ServerStateCVLock = PR_NewLock();
+ ServerStateCV = PR_NewCondVar(ServerStateCVLock);
+
+ Measure(do_workUK, "server loop user/kernel");
+
+ PR_Cleanup();
+
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
diff --git a/nsprpub/pr/tests/servr_uu.c b/nsprpub/pr/tests/servr_uu.c
new file mode 100644
index 0000000000..8e77d8fa82
--- /dev/null
+++ b/nsprpub/pr/tests/servr_uu.c
@@ -0,0 +1,640 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** This server simulates a server running in loopback mode.
+**
+** The idea is that a single server is created. The server initially creates
+** a number of worker threads. Then, with the server running, a number of
+** clients are created which start requesting service from the server.
+**
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "pprthred.h"
+
+#include <string.h>
+
+#define PORT 15004
+#define THREAD_STACKSIZE 0
+
+static int _iterations = 1000;
+static int _clients = 1;
+static int _client_data = 250;
+static int _server_data = (8*1024);
+
+static PRThreadScope ServerScope, ClientScope;
+
+#define SERVER "Server"
+#define MAIN "Main"
+
+#define SERVER_STATE_STARTUP 0
+#define SERVER_STATE_READY 1
+#define SERVER_STATE_DYING 2
+#define SERVER_STATE_DEAD 4
+int ServerState;
+PRLock *ServerStateCVLock;
+PRCondVar *ServerStateCV;
+
+#ifdef DEBUGPRINTS
+#define DPRINTF printf
+#else
+#define DPRINTF
+#endif
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+static void do_work(void);
+
+/* --- Server state functions --------------------------------------------- */
+void
+SetServerState(char *waiter, PRInt32 state)
+{
+ PR_Lock(ServerStateCVLock);
+ ServerState = state;
+ PR_NotifyCondVar(ServerStateCV);
+
+ if (debug_mode) {
+ DPRINTF("\t%s changed state to %d\n", waiter, state);
+ }
+
+ PR_Unlock(ServerStateCVLock);
+}
+
+int
+WaitServerState(char *waiter, PRInt32 state)
+{
+ PRInt32 rv;
+
+ PR_Lock(ServerStateCVLock);
+
+ if (debug_mode) {
+ DPRINTF("\t%s waiting for state %d\n", waiter, state);
+ }
+
+ while(!(ServerState & state)) {
+ PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT);
+ }
+ rv = ServerState;
+
+ if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n",
+ waiter, state, ServerState);
+ PR_Unlock(ServerStateCVLock);
+
+ return rv;
+}
+
+/* --- Server Functions ------------------------------------------- */
+
+PRLock *workerThreadsLock;
+PRInt32 workerThreads;
+PRInt32 workerThreadsBusy;
+
+void
+WorkerThreadFunc(void *_listenSock)
+{
+ PRFileDesc *listenSock = (PRFileDesc *)_listenSock;
+ PRInt32 bytesRead;
+ PRInt32 bytesWritten;
+ char *dataBuf;
+ char *sendBuf;
+
+ if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n",
+ _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32);
+ dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32);
+ if (!dataBuf)
+ if (debug_mode) {
+ printf("\tServer could not malloc space!?\n");
+ }
+ sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) {
+ printf("\tServer could not malloc space!?\n");
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tServer worker thread running\n");
+ }
+
+ while(1) {
+ PRInt32 bytesToRead = _client_data;
+ PRInt32 bytesToWrite = _server_data;
+ PRFileDesc *newSock;
+ PRNetAddr *rAddr;
+ PRInt32 loops = 0;
+
+ loops++;
+
+ if (debug_mode) {
+ DPRINTF("\tServer thread going into accept\n");
+ }
+
+ bytesRead = PR_AcceptRead(listenSock,
+ &newSock,
+ &rAddr,
+ dataBuf,
+ bytesToRead,
+ PR_INTERVAL_NO_TIMEOUT);
+
+ if (bytesRead < 0) {
+ if (debug_mode) {
+ printf("\tServer error in accept (%d)\n", bytesRead);
+ }
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead);
+ }
+
+ PR_AtomicIncrement(&workerThreadsBusy);
+ if (workerThreadsBusy == workerThreads) {
+
+ PR_Lock(workerThreadsLock);
+ if (workerThreadsBusy == workerThreads) {
+ PRThread *WorkerThread;
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSock,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) {
+ printf("Error creating client thread %d\n", workerThreads);
+ }
+ } else {
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) {
+ DPRINTF("\tServer creates worker (%d)\n", workerThreads);
+ }
+ }
+ }
+ PR_Unlock(workerThreadsLock);
+ }
+
+ bytesToRead -= bytesRead;
+ while (bytesToRead) {
+ bytesRead = PR_Recv(newSock,
+ dataBuf,
+ bytesToRead,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesRead < 0) {
+ if (debug_mode) {
+ printf("\tServer error receiving data (%d)\n", bytesRead);
+ }
+ continue;
+ }
+ if (debug_mode) {
+ DPRINTF("\tServer received %d bytes\n", bytesRead);
+ }
+ }
+
+ bytesWritten = PR_Send(newSock,
+ sendBuf,
+ bytesToWrite,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesWritten != _server_data) {
+ if (debug_mode) printf("\tError sending data to client (%d, %d)\n",
+ bytesWritten, PR_GetOSError());
+ } else {
+ if (debug_mode) {
+ DPRINTF("\tServer sent %d bytes\n", bytesWritten);
+ }
+ }
+
+ PR_Close(newSock);
+ PR_AtomicDecrement(&workerThreadsBusy);
+ }
+}
+
+PRFileDesc *
+ServerSetup(void)
+{
+ PRFileDesc *listenSocket;
+ PRSocketOptionData sockOpt;
+ PRNetAddr serverAddr;
+ PRThread *WorkerThread;
+
+ if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) {
+ printf("\tServer error creating listen socket\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return NULL;
+ }
+
+ sockOpt.option = PR_SockOpt_Reuseaddr;
+ sockOpt.value.reuse_addr = PR_TRUE;
+ if ( PR_SetSocketOption(listenSocket, &sockOpt) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error setting socket option: OS error %d\n",
+ PR_GetOSError());
+ else {
+ failed_already=1;
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+
+ if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error binding to server address: OS error %d\n",
+ PR_GetOSError());
+ else {
+ failed_already=1;
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
+ if (debug_mode) {
+ printf("\tServer error listening to server socket\n");
+ }
+ else {
+ failed_already=1;
+ }
+ PR_Close(listenSocket);
+
+ return NULL;
+ }
+
+ /* Create Clients */
+ workerThreads = 0;
+ workerThreadsBusy = 0;
+
+ workerThreadsLock = PR_NewLock();
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSocket,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) {
+ printf("error creating working thread\n");
+ }
+ PR_Close(listenSocket);
+ return NULL;
+ }
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) {
+ DPRINTF("\tServer created primordial worker thread\n");
+ }
+
+ return listenSocket;
+}
+
+/* The main server loop */
+void
+ServerThreadFunc(void *unused)
+{
+ PRFileDesc *listenSocket;
+
+ /* Do setup */
+ listenSocket = ServerSetup();
+
+ if (!listenSocket) {
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ } else {
+
+ if (debug_mode) {
+ DPRINTF("\tServer up\n");
+ }
+
+ /* Tell clients they can start now. */
+ SetServerState(SERVER, SERVER_STATE_READY);
+
+ /* Now wait for server death signal */
+ WaitServerState(SERVER, SERVER_STATE_DYING);
+
+ /* Cleanup */
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ }
+}
+
+/* --- Client Functions ------------------------------------------- */
+
+PRInt32 numRequests;
+PRInt32 numClients;
+PRMonitor *clientMonitor;
+
+void
+ClientThreadFunc(void *unused)
+{
+ PRNetAddr serverAddr;
+ PRFileDesc *clientSocket;
+ char *sendBuf;
+ char *recvBuf;
+ PRInt32 rv;
+ PRInt32 bytesNeeded;
+
+ sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) {
+ printf("\tClient could not malloc space!?\n");
+ }
+ recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char));
+ if (!recvBuf)
+ if (debug_mode) {
+ printf("\tClient could not malloc space!?\n");
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+
+ while(numRequests > 0) {
+
+ if ( (numRequests % 10) == 0 )
+ if (debug_mode) {
+ printf(".");
+ }
+ if (debug_mode) {
+ DPRINTF("\tClient starting request %d\n", numRequests);
+ }
+
+ clientSocket = PR_NewTCPSocket();
+ if (!clientSocket) {
+ if (debug_mode) printf("Client error creating socket: OS error %d\n",
+ PR_GetOSError());
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient connecting\n");
+ }
+
+ rv = PR_Connect(clientSocket,
+ &serverAddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (!clientSocket) {
+ if (debug_mode) {
+ printf("\tClient error connecting\n");
+ }
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient connected\n");
+ }
+
+ rv = PR_Send(clientSocket,
+ sendBuf,
+ _client_data,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != _client_data) {
+ if (debug_mode) {
+ printf("Client error sending data (%d)\n", rv);
+ }
+ PR_Close(clientSocket);
+ continue;
+ }
+
+ if (debug_mode) {
+ DPRINTF("\tClient sent %d bytes\n", rv);
+ }
+
+ bytesNeeded = _server_data;
+ while(bytesNeeded) {
+ rv = PR_Recv(clientSocket,
+ recvBuf,
+ bytesNeeded,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv <= 0) {
+ if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n",
+ rv, (_server_data - bytesNeeded), _server_data);
+ break;
+ }
+ if (debug_mode) {
+ DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv);
+ }
+ bytesNeeded -= rv;
+ }
+
+ PR_Close(clientSocket);
+
+ PR_AtomicDecrement(&numRequests);
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ --numClients;
+ PR_Notify(clientMonitor);
+ PR_ExitMonitor(clientMonitor);
+
+ PR_DELETE(sendBuf);
+ PR_DELETE(recvBuf);
+}
+
+void
+RunClients(void)
+{
+ PRInt32 index;
+
+ numRequests = _iterations;
+ numClients = _clients;
+ clientMonitor = PR_NewMonitor();
+
+ for (index=0; index<_clients; index++) {
+ PRThread *clientThread;
+
+
+ clientThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ClientThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ClientScope,
+ PR_UNJOINABLE_THREAD,
+ THREAD_STACKSIZE);
+
+ if (!clientThread) {
+ if (debug_mode) {
+ printf("\terror creating client thread %d\n", index);
+ }
+ } else if (debug_mode) {
+ DPRINTF("\tMain created client %d/%d\n", index+1, _clients);
+ }
+
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ while(numClients) {
+ PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(clientMonitor);
+}
+
+/* --- Main Function ---------------------------------------------- */
+
+static
+void do_work()
+{
+ PRThread *ServerThread;
+ PRInt32 state;
+
+ SetServerState(MAIN, SERVER_STATE_STARTUP);
+ ServerThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ServerThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_JOINABLE_THREAD,
+ THREAD_STACKSIZE);
+ if (!ServerThread) {
+ if (debug_mode) {
+ printf("error creating main server thread\n");
+ }
+ return;
+ }
+
+ /* Wait for server to be ready */
+ state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD);
+
+ if (!(state & SERVER_STATE_DEAD)) {
+ /* Run Test Clients */
+ RunClients();
+
+ /* Send death signal to server */
+ SetServerState(MAIN, SERVER_STATE_DYING);
+ }
+
+ PR_JoinThread(ServerThread);
+}
+
+static void do_workUU(void)
+{
+ ServerScope = PR_LOCAL_THREAD;
+ ClientScope = PR_LOCAL_THREAD;
+ do_work();
+}
+
+
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ if (debug_mode) {
+ printf("\n%40s: %6.2f usec\n", msg, d / _iterations);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+ if (debug_mode) {
+ printf("Enter number of iterations: \n");
+ scanf("%d", &_iterations);
+ printf("Enter number of clients : \n");
+ scanf("%d", &_clients);
+ printf("Enter size of client data : \n");
+ scanf("%d", &_client_data);
+ printf("Enter size of server data : \n");
+ scanf("%d", &_server_data);
+ }
+ else
+ {
+ _iterations = 7;
+ _clients = 7;
+ _client_data = 100;
+ _server_data = 100;
+ }
+
+ if (debug_mode) {
+ printf("\n\n%d iterations with %d client threads.\n",
+ _iterations, _clients);
+ printf("Sending %d bytes of client data and %d bytes of server data\n",
+ _client_data, _server_data);
+ }
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ PR_SetThreadRecycleMode(64);
+
+ ServerStateCVLock = PR_NewLock();
+ ServerStateCV = PR_NewCondVar(ServerStateCVLock);
+
+ Measure(do_workUU, "server loop user/user");
+
+ PR_Cleanup();
+
+ if(failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+}
diff --git a/nsprpub/pr/tests/short_thread.c b/nsprpub/pr/tests/short_thread.c
new file mode 100644
index 0000000000..de069e8967
--- /dev/null
+++ b/nsprpub/pr/tests/short_thread.c
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include "nspr.h"
+#include "plgetopt.h"
+
+/*
+ * Create a thread that exits right away; useful for testing race conditions in thread
+ * creation
+ */
+
+int _debug_on = 0;
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+static void housecleaning(void *cur_time);
+
+int main (int argc, char **argv)
+{
+ static PRIntervalTime thread_start_time;
+ static PRThread *housekeeping_tid = NULL;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (( housekeeping_tid =
+ PR_CreateThread (PR_USER_THREAD, housecleaning, (void*)&thread_start_time,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0))
+ == NULL ) {
+ fprintf(stderr,
+ "simple_test: Error - PR_CreateThread failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ exit( 1 );
+ }
+ PR_Cleanup();
+ return(0);
+}
+
+static void
+housecleaning (void *cur_time)
+{
+ DPRINTF(("Child Thread exiting\n"));
+}
diff --git a/nsprpub/pr/tests/sigpipe.c b/nsprpub/pr/tests/sigpipe.c
new file mode 100644
index 0000000000..02ba9114f2
--- /dev/null
+++ b/nsprpub/pr/tests/sigpipe.c
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *************************************************************************
+ *
+ * Test: sigpipe.c
+ *
+ * Test the SIGPIPE handler in NSPR. This test applies to Unix only.
+ *
+ *************************************************************************
+ */
+
+#if !defined(XP_UNIX) && !defined(XP_OS2)
+
+int main(void)
+{
+ /* This test applies to Unix and OS/2. */
+ return 0;
+}
+
+#else /* XP_UNIX && OS/2 */
+
+#include "nspr.h"
+
+#ifdef XP_OS2
+#define INCL_DOSQUEUES
+#define INCL_DOSERRORS
+#include <os2.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+static void Test(void *arg)
+{
+#ifdef XP_OS2
+ HFILE pipefd[2];
+#else
+ int pipefd[2];
+#endif
+ int rv;
+ char c = '\0';
+
+#ifdef XP_OS2
+ if (DosCreatePipe(&pipefd[0], &pipefd[1], 4096) != 0) {
+#else
+ if (pipe(pipefd) == -1) {
+#endif
+ fprintf(stderr, "cannot create pipe: %d\n", errno);
+ exit(1);
+ }
+ close(pipefd[0]);
+
+ rv = write(pipefd[1], &c, 1);
+ if (rv != -1) {
+ fprintf(stderr, "write to broken pipe should have failed with EPIPE but returned %d\n", rv);
+ exit(1);
+ }
+ if (errno != EPIPE) {
+ fprintf(stderr, "write to broken pipe failed but with wrong errno: %d\n", errno);
+ exit(1);
+ }
+ close(pipefd[1]);
+ printf("write to broken pipe failed with EPIPE, as expected\n");
+}
+
+int main(int argc, char **argv)
+{
+ PRThread *thread;
+
+ /* This initializes NSPR. */
+ PR_SetError(0, 0);
+
+ thread = PR_CreateThread(PR_USER_THREAD, Test, NULL, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (thread == NULL) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+ if (PR_JoinThread(thread) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+ Test(NULL);
+
+ printf("PASSED\n");
+ return 0;
+}
+
+#endif /* XP_UNIX */
diff --git a/nsprpub/pr/tests/sleep.c b/nsprpub/pr/tests/sleep.c
new file mode 100644
index 0000000000..3e00acab01
--- /dev/null
+++ b/nsprpub/pr/tests/sleep.c
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+
+#if defined(XP_UNIX) || defined(XP_OS2)
+
+#include <stdio.h>
+
+#ifndef XP_OS2
+#include <unistd.h>
+#endif
+#include <sys/time.h>
+
+#if defined(HAVE_SVID_GETTOD)
+#define GTOD(_a) gettimeofday(_a)
+#else
+#define GTOD(_a) gettimeofday((_a), NULL)
+#endif
+
+static PRIntn rv = 0;
+
+static void Other(void *unused)
+{
+ PRIntn didit = 0;
+ while (PR_SUCCESS == PR_Sleep(PR_MillisecondsToInterval(250)))
+ {
+ fprintf(stderr, ".");
+ didit += 1;
+ }
+ if (didit < 5) {
+ rv = 1;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRUint32 elapsed;
+ PRThread *thread;
+ struct timeval timein, timeout;
+ PRInt32 onePercent = 3000000UL / 100UL;
+
+ fprintf (stderr, "First sleep will sleep 3 seconds.\n");
+ fprintf (stderr, " sleep 1 begin\n");
+ (void)GTOD(&timein);
+ sleep (3);
+ (void)GTOD(&timeout);
+ fprintf (stderr, " sleep 1 end\n");
+ elapsed = 1000000UL * (timeout.tv_sec - timein.tv_sec);
+ elapsed += (timeout.tv_usec - timein.tv_usec);
+ fprintf(stderr, "elapsed %u usecs\n", elapsed);
+ if (labs(elapsed - 3000000UL) > onePercent) {
+ rv = 1;
+ }
+
+ PR_Init (PR_USER_THREAD, PR_PRIORITY_NORMAL, 100);
+ PR_STDIO_INIT();
+
+ fprintf (stderr, "Second sleep should do the same (does it?).\n");
+ fprintf (stderr, " sleep 2 begin\n");
+ (void)GTOD(&timein);
+ sleep (3);
+ (void)GTOD(&timeout);
+ fprintf (stderr, " sleep 2 end\n");
+ elapsed = 1000000UL * (timeout.tv_sec - timein.tv_sec);
+ elapsed += (timeout.tv_usec - timein.tv_usec);
+ fprintf(stderr, "elapsed %u usecs\n", elapsed);
+ if (labs(elapsed - 3000000UL) > onePercent) {
+ rv = 1;
+ }
+
+ fprintf (stderr, "What happens to other threads?\n");
+ fprintf (stderr, "You should see dots every quarter second.\n");
+ fprintf (stderr, "If you don't, you're probably running on classic NSPR.\n");
+ thread = PR_CreateThread(
+ PR_USER_THREAD, Other, NULL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ fprintf (stderr, " sleep 2 begin\n");
+ (void)GTOD(&timein);
+ sleep (3);
+ (void)GTOD(&timeout);
+ fprintf (stderr, " sleep 2 end\n");
+ PR_Interrupt(thread);
+ PR_JoinThread(thread);
+ elapsed = 1000000UL * (timeout.tv_sec - timein.tv_sec);
+ elapsed += (timeout.tv_usec - timein.tv_usec);
+ fprintf(stderr, "elapsed %u usecs\n", elapsed);
+ if (labs(elapsed - 3000000UL) > onePercent) {
+ rv = 1;
+ }
+ fprintf(stderr, "%s\n", (0 == rv) ? "PASSED" : "FAILED");
+ return rv;
+}
+
+#else /* defined(XP_UNIX) */
+
+PRIntn main()
+{
+ return 2;
+}
+
+#endif /* defined(XP_UNIX) */
+
diff --git a/nsprpub/pr/tests/socket.c b/nsprpub/pr/tests/socket.c
new file mode 100644
index 0000000000..232898f69e
--- /dev/null
+++ b/nsprpub/pr/tests/socket.c
@@ -0,0 +1,2286 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: socket.c
+**
+** Description: Test socket functionality.
+**
+** Modification History:
+*/
+#include "primpl.h"
+
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#ifdef XP_UNIX
+#include <sys/mman.h>
+#endif
+#if defined(_PR_PTHREADS)
+#include <pthread.h>
+#endif
+
+#ifdef WIN32
+#include <process.h>
+#endif
+
+static int _debug_on = 0;
+static int test_cancelio = 0;
+
+#include "obsolete/prsem.h"
+
+#ifdef XP_PC
+#define mode_t int
+#endif
+
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+#ifdef XP_PC
+char *TEST_DIR = "prdir";
+char *SMALL_FILE_NAME = "prsmallf";
+char *LARGE_FILE_NAME = "prlargef";
+#else
+char *TEST_DIR = "./tmp-prsocket_test_dir";
+char *SMALL_FILE_NAME = "./tmp-prsocket_test_dir/small_file";
+char *LARGE_FILE_NAME = "./tmp-prsocket_test_dir/large_file";
+#endif
+#define SMALL_FILE_SIZE (3 * 1024) /* 3 KB */
+#define SMALL_FILE_OFFSET_1 (512)
+#define SMALL_FILE_LEN_1 (1 * 1024) /* 1 KB */
+#define SMALL_FILE_OFFSET_2 (75)
+#define SMALL_FILE_LEN_2 (758)
+#define SMALL_FILE_OFFSET_3 (1024)
+#define SMALL_FILE_LEN_3 (SMALL_FILE_SIZE - SMALL_FILE_OFFSET_3)
+#define SMALL_FILE_HEADER_SIZE (64) /* 64 bytes */
+#define SMALL_FILE_TRAILER_SIZE (128) /* 128 bytes */
+
+#define LARGE_FILE_SIZE (3 * 1024 * 1024) /* 3 MB */
+#define LARGE_FILE_OFFSET_1 (0)
+#define LARGE_FILE_LEN_1 (2 * 1024 * 1024) /* 2 MB */
+#define LARGE_FILE_OFFSET_2 (64)
+#define LARGE_FILE_LEN_2 (1 * 1024 * 1024 + 75)
+#define LARGE_FILE_OFFSET_3 (2 * 1024 * 1024 - 128)
+#define LARGE_FILE_LEN_3 (LARGE_FILE_SIZE - LARGE_FILE_OFFSET_3)
+#define LARGE_FILE_OFFSET_4 PR_GetPageSize()
+#define LARGE_FILE_LEN_4 769
+#define LARGE_FILE_HEADER_SIZE (512)
+#define LARGE_FILE_TRAILER_SIZE (64)
+
+#define BUF_DATA_SIZE (2 * 1024)
+#define TCP_MESG_SIZE 1024
+/*
+ * set UDP datagram size small enough that datagrams sent to a port on the
+ * local host will not be lost
+ */
+#define UDP_DGRAM_SIZE 128
+#define NUM_TCP_CLIENTS 5 /* for a listen queue depth of 5 */
+#define NUM_UDP_CLIENTS 10
+
+#define NUM_TRANSMITFILE_CLIENTS 4
+
+#define NUM_TCP_CONNECTIONS_PER_CLIENT 5
+#define NUM_TCP_MESGS_PER_CONNECTION 10
+#define NUM_UDP_DATAGRAMS_PER_CLIENT 5
+#define TCP_SERVER_PORT 10000
+#define UDP_SERVER_PORT TCP_SERVER_PORT
+#define SERVER_MAX_BIND_COUNT 100
+
+#ifdef WINCE
+#define perror(s)
+#endif
+
+static PRInt32 num_tcp_clients = NUM_TCP_CLIENTS;
+static PRInt32 num_udp_clients = NUM_UDP_CLIENTS;
+static PRInt32 num_transmitfile_clients = NUM_TRANSMITFILE_CLIENTS;
+static PRInt32 num_tcp_connections_per_client = NUM_TCP_CONNECTIONS_PER_CLIENT;
+static PRInt32 tcp_mesg_size = TCP_MESG_SIZE;
+static PRInt32 num_tcp_mesgs_per_connection = NUM_TCP_MESGS_PER_CONNECTION;
+static PRInt32 num_udp_datagrams_per_client = NUM_UDP_DATAGRAMS_PER_CLIENT;
+static PRInt32 udp_datagram_size = UDP_DGRAM_SIZE;
+
+static PRInt32 thread_count;
+PRUint16 server_domain = PR_AF_INET, client_domain = PR_AF_INET;
+
+/* an I/O layer that uses the emulated senfile method */
+static PRDescIdentity emuSendFileIdentity;
+static PRIOMethods emuSendFileMethods;
+
+int failed_already=0;
+typedef struct buffer {
+ char data[BUF_DATA_SIZE];
+} buffer;
+
+PRNetAddr tcp_server_addr, udp_server_addr;
+
+typedef struct Serve_Client_Param {
+ PRFileDesc *sockfd; /* socket to read from/write to */
+ PRInt32 datalen; /* bytes of data transfered in each read/write */
+} Serve_Client_Param;
+
+typedef struct Server_Param {
+ PRSemaphore *addr_sem; /* sem to post on, after setting up the address */
+ PRMonitor *exit_mon; /* monitor to signal on exit */
+ PRInt32 *exit_counter; /* counter to decrement, before exit */
+ PRInt32 datalen; /* bytes of data transfered in each read/write */
+} Server_Param;
+
+
+typedef struct Client_Param {
+ PRNetAddr server_addr;
+ PRMonitor *exit_mon; /* monitor to signal on exit */
+ PRInt32 *exit_counter; /* counter to decrement, before exit */
+ PRInt32 datalen;
+ PRInt32 udp_connect; /* if set clients connect udp sockets */
+} Client_Param;
+
+/* the sendfile method in emuSendFileMethods */
+static PRInt32 PR_CALLBACK
+emu_SendFile(PRFileDesc *sd, PRSendFileData *sfd,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ return PR_EmulateSendFile(sd, sfd, flags, timeout);
+}
+
+/* the transmitfile method in emuSendFileMethods */
+static PRInt32 PR_CALLBACK
+emu_TransmitFile(PRFileDesc *sd, PRFileDesc *fd, const void *headers,
+ PRInt32 hlen, PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ PRSendFileData sfd;
+
+ sfd.fd = fd;
+ sfd.file_offset = 0;
+ sfd.file_nbytes = 0;
+ sfd.header = headers;
+ sfd.hlen = hlen;
+ sfd.trailer = NULL;
+ sfd.tlen = 0;
+ return emu_SendFile(sd, &sfd, flags, timeout);
+}
+
+/*
+ * readn
+ * read data from sockfd into buf
+ */
+static PRInt32
+readn(PRFileDesc *sockfd, char *buf, int len)
+{
+ int rem;
+ int bytes;
+ int offset = 0;
+ int err;
+ PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
+
+ if (test_cancelio) {
+ timeout = PR_SecondsToInterval(2);
+ }
+
+ for (rem=len; rem; offset += bytes, rem -= bytes) {
+ DPRINTF(("thread = 0x%lx: calling PR_Recv, bytes = %d\n",
+ PR_GetCurrentThread(), rem));
+retry:
+ bytes = PR_Recv(sockfd, buf + offset, rem, 0,
+ timeout);
+ DPRINTF(("thread = 0x%lx: returning from PR_Recv, bytes = %d\n",
+ PR_GetCurrentThread(), bytes));
+ if (bytes < 0) {
+#ifdef WINNT
+ printf("PR_Recv: error = %d oserr = %d\n",(err = PR_GetError()),
+ PR_GetOSError());
+ if ((test_cancelio) && (err == PR_IO_TIMEOUT_ERROR)) {
+ if (PR_NT_CancelIo(sockfd) != PR_SUCCESS) {
+ printf("PR_NT_CancelIO: error = %d\n",PR_GetError());
+ }
+ timeout = PR_INTERVAL_NO_TIMEOUT;
+ goto retry;
+ }
+#endif
+ return -1;
+ }
+ }
+ return len;
+}
+
+/*
+ * writen
+ * write data from buf to sockfd
+ */
+static PRInt32
+writen(PRFileDesc *sockfd, char *buf, int len)
+{
+ int rem;
+ int bytes;
+ int offset = 0;
+
+ for (rem=len; rem; offset += bytes, rem -= bytes) {
+ DPRINTF(("thread = 0x%lx: calling PR_Send, bytes = %d\n",
+ PR_GetCurrentThread(), rem));
+ bytes = PR_Send(sockfd, buf + offset, rem, 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("thread = 0x%lx: returning from PR_Send, bytes = %d\n",
+ PR_GetCurrentThread(), bytes));
+ if (bytes <= 0) {
+ return -1;
+ }
+ }
+ return len;
+}
+
+/*
+ * Serve_Client
+ * Thread, started by the server, for serving a client connection.
+ * Reads data from socket and writes it back, unmodified, and
+ * closes the socket
+ */
+static void PR_CALLBACK
+Serve_Client(void *arg)
+{
+ Serve_Client_Param *scp = (Serve_Client_Param *) arg;
+ PRFileDesc *sockfd;
+ buffer *in_buf;
+ PRInt32 bytes, j;
+
+ sockfd = scp->sockfd;
+ bytes = scp->datalen;
+ in_buf = PR_NEW(buffer);
+ if (in_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n");
+ failed_already=1;
+ goto exit;
+ }
+
+
+ for (j = 0; j < num_tcp_mesgs_per_connection; j++) {
+ /*
+ * Read data from client and send it back to the client unmodified
+ */
+ if (readn(sockfd, in_buf->data, bytes) < bytes) {
+ fprintf(stderr,"prsocket_test: ERROR - Serve_Client:readn\n");
+ failed_already=1;
+ goto exit;
+ }
+ /* Shutdown only RCV will cause error on Symbian OS */
+ /*
+ * shutdown reads, after the last read
+ */
+ if (j == num_tcp_mesgs_per_connection - 1)
+ if (PR_Shutdown(sockfd, PR_SHUTDOWN_RCV) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Shutdown\n");
+ }
+ DPRINTF(("Serve_Client [0x%lx]: inbuf[0] = 0x%lx\n",PR_GetCurrentThread(),
+ (*((int *) in_buf->data))));
+ if (writen(sockfd, in_buf->data, bytes) < bytes) {
+ fprintf(stderr,"prsocket_test: ERROR - Serve_Client:writen\n");
+ failed_already=1;
+ goto exit;
+ }
+ }
+ /*
+ * shutdown reads and writes
+ */
+ if (PR_Shutdown(sockfd, PR_SHUTDOWN_BOTH) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Shutdown\n");
+ failed_already=1;
+ }
+
+exit:
+ PR_Close(sockfd);
+ if (in_buf) {
+ PR_DELETE(in_buf);
+ }
+}
+
+PRThread* create_new_thread(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize, PRInt32 index)
+{
+ PRInt32 native_thread = 0;
+
+ PR_ASSERT(state == PR_UNJOINABLE_THREAD);
+#if defined(_PR_PTHREADS) || defined(WIN32)
+ switch(index % 4) {
+ case 0:
+ scope = (PR_LOCAL_THREAD);
+ break;
+ case 1:
+ scope = (PR_GLOBAL_THREAD);
+ break;
+ case 2:
+ scope = (PR_GLOBAL_BOUND_THREAD);
+ break;
+ case 3:
+ native_thread = 1;
+ break;
+ default:
+ PR_NOT_REACHED("Invalid scope");
+ break;
+ }
+ if (native_thread) {
+#if defined(_PR_PTHREADS)
+ pthread_t tid;
+ if (!pthread_create(&tid, NULL, (void * (*)(void *)) start, arg)) {
+ return((PRThread *) tid);
+ }
+ else {
+ return (NULL);
+ }
+#else
+ HANDLE thandle;
+ unsigned tid;
+
+ thandle = (HANDLE) _beginthreadex(
+ NULL,
+ stackSize,
+ (unsigned (__stdcall *)(void *))start,
+ arg,
+ STACK_SIZE_PARAM_IS_A_RESERVATION,
+ &tid);
+ return((PRThread *) thandle);
+#endif
+ } else {
+ return(PR_CreateThread(type,start,arg,priority,scope,state,stackSize));
+ }
+#else
+ return(PR_CreateThread(type,start,arg,priority,scope,state,stackSize));
+#endif
+}
+
+/*
+ * TCP Server
+ * Server Thread
+ * Bind an address to a socket and listen for incoming connections
+ * Start a Serve_Client thread for each incoming connection.
+ */
+static void PR_CALLBACK
+TCP_Server(void *arg)
+{
+ PRThread *t;
+ Server_Param *sp = (Server_Param *) arg;
+ Serve_Client_Param *scp;
+ PRFileDesc *sockfd, *newsockfd;
+ PRNetAddr netaddr;
+ PRInt32 i;
+ /*
+ * Create a tcp socket
+ */
+ if ((sockfd = PR_OpenTCPSocket(server_domain)) == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewTCPSocket failed\n");
+ goto exit;
+ }
+ memset(&netaddr, 0, sizeof(netaddr));
+
+ if (PR_SetNetAddr(PR_IpAddrAny, server_domain, TCP_SERVER_PORT,
+ &netaddr) == PR_FAILURE) {
+ fprintf(stderr,"prsocket_test: PR_SetNetAddr failed\n");
+ goto exit;
+ }
+ /*
+ * try a few times to bind server's address, if addresses are in
+ * use
+ */
+ i = 0;
+
+ while (PR_Bind(sockfd, &netaddr) < 0) {
+ if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
+ netaddr.inet.port += 2;
+ if (i++ < SERVER_MAX_BIND_COUNT) {
+ continue;
+ }
+ }
+ fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n");
+ perror("PR_Bind");
+ failed_already=1;
+ goto exit;
+ }
+
+ if (PR_Listen(sockfd, 32) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Listen failed\n");
+ failed_already=1;
+ goto exit;
+ }
+
+ if (PR_GetSockName(sockfd, &netaddr) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit;
+ }
+
+ DPRINTF(("TCP_Server: PR_BIND netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n",
+ netaddr.inet.ip, netaddr.inet.port));
+ if (PR_SetNetAddr(PR_IpAddrLoopback, client_domain,
+ PR_ntohs(PR_NetAddrInetPort(&netaddr)),
+ &tcp_server_addr) == PR_FAILURE) {
+ fprintf(stderr,"prsocket_test: PR_SetNetAddr failed\n");
+ goto exit;
+ }
+ if ((client_domain == PR_AF_INET6) && (server_domain == PR_AF_INET))
+ PR_ConvertIPv4AddrToIPv6(PR_htonl(INADDR_LOOPBACK),
+ &tcp_server_addr.ipv6.ip);
+
+ /*
+ * Wake up parent thread because server address is bound and made
+ * available in the global variable 'tcp_server_addr'
+ */
+ PR_PostSem(sp->addr_sem);
+
+ for (i = 0; i < (num_tcp_clients * num_tcp_connections_per_client); i++) {
+ /* test both null and non-null 'addr' argument to PR_Accept */
+ PRNetAddr *addrp = (i%2 ? &netaddr: NULL);
+
+ DPRINTF(("TCP_Server: Accepting connection\n"));
+ if ((newsockfd = PR_Accept(sockfd, addrp,
+ PR_INTERVAL_NO_TIMEOUT)) == NULL) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Accept failed\n");
+ goto exit;
+ }
+ DPRINTF(("TCP_Server: Accepted connection\n"));
+ scp = PR_NEW(Serve_Client_Param);
+ if (scp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ goto exit;
+ }
+
+ /*
+ * Start a Serve_Client thread for each incoming connection
+ */
+ scp->sockfd = newsockfd;
+ scp->datalen = sp->datalen;
+
+ t = create_new_thread(PR_USER_THREAD,
+ Serve_Client, (void *)scp,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0, i);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ goto exit;
+ }
+ DPRINTF(("TCP_Server: Created Serve_Client = 0x%lx\n", t));
+ }
+
+exit:
+ if (sockfd) {
+ PR_Close(sockfd);
+ }
+
+ /*
+ * Decrement exit_counter and notify parent thread
+ */
+
+ PR_EnterMonitor(sp->exit_mon);
+ --(*sp->exit_counter);
+ PR_Notify(sp->exit_mon);
+ PR_ExitMonitor(sp->exit_mon);
+ DPRINTF(("TCP_Server [0x%lx] exiting\n", PR_GetCurrentThread()));
+}
+
+/*
+ * UDP Server
+ * Server Thread
+ * Bind an address to a socket, read data from clients and send data
+ * back to clients
+ */
+static void PR_CALLBACK
+UDP_Server(void *arg)
+{
+ Server_Param *sp = (Server_Param *) arg;
+ PRFileDesc *sockfd;
+ buffer *in_buf;
+ PRNetAddr netaddr;
+ PRInt32 bytes, i, rv = 0;
+
+
+ bytes = sp->datalen;
+ /*
+ * Create a udp socket
+ */
+ if ((sockfd = PR_OpenUDPSocket(server_domain)) == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewUDPSocket failed\n");
+ failed_already=1;
+ return;
+ }
+ memset(&netaddr, 0, sizeof(netaddr));
+ if (PR_SetNetAddr(PR_IpAddrAny, server_domain, UDP_SERVER_PORT,
+ &netaddr) == PR_FAILURE) {
+ fprintf(stderr,"prsocket_test: PR_SetNetAddr failed\n");
+ failed_already=1;
+ return;
+ }
+ /*
+ * try a few times to bind server's address, if addresses are in
+ * use
+ */
+ i = 0;
+ while (PR_Bind(sockfd, &netaddr) < 0) {
+ if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
+ netaddr.inet.port += 2;
+ if (i++ < SERVER_MAX_BIND_COUNT) {
+ continue;
+ }
+ }
+ fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n");
+ perror("PR_Bind");
+ failed_already=1;
+ return;
+ }
+
+ if (PR_GetSockName(sockfd, &netaddr) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_GetSockName failed\n");
+ failed_already=1;
+ return;
+ }
+
+ DPRINTF(("PR_Bind: UDP Server netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n",
+ netaddr.inet.ip, netaddr.inet.port));
+ /*
+ * We can't use the IP address returned by PR_GetSockName in
+ * netaddr.inet.ip because netaddr.inet.ip is returned
+ * as 0 (= PR_INADDR_ANY).
+ */
+
+ if (PR_SetNetAddr(PR_IpAddrLoopback, client_domain,
+ PR_ntohs(PR_NetAddrInetPort(&netaddr)),
+ &udp_server_addr) == PR_FAILURE) {
+ fprintf(stderr,"prsocket_test: PR_SetNetAddr failed\n");
+ failed_already=1;
+ return;
+ }
+ if ((client_domain == PR_AF_INET6) && (server_domain == PR_AF_INET))
+ PR_ConvertIPv4AddrToIPv6(PR_htonl(INADDR_LOOPBACK),
+ &udp_server_addr.ipv6.ip);
+
+ /*
+ * Wake up parent thread because server address is bound and made
+ * available in the global variable 'udp_server_addr'
+ */
+ PR_PostSem(sp->addr_sem);
+
+ bytes = sp->datalen;
+ in_buf = PR_NEW(buffer);
+ if (in_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n");
+ failed_already=1;
+ return;
+ }
+ /*
+ * Receive datagrams from clients and send them back, unmodified, to the
+ * clients
+ */
+ memset(&netaddr, 0, sizeof(netaddr));
+ for (i = 0; i < (num_udp_clients * num_udp_datagrams_per_client); i++) {
+ DPRINTF(("UDP_Server: calling PR_RecvFrom client - ip = 0x%lx, port = %d bytes = %d inbuf = 0x%lx, inbuf[0] = 0x%lx\n",
+ netaddr.inet.ip, netaddr.inet.port, bytes, in_buf->data,
+ in_buf->data[0]));
+
+ rv = PR_RecvFrom(sockfd, in_buf->data, bytes, 0, &netaddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("UDP_Server: PR_RecvFrom client - ip = 0x%lx, port = %d bytes = %d inbuf = 0x%lx, inbuf[0] = 0x%lx\n",
+ netaddr.inet.ip, netaddr.inet.port, rv, in_buf->data,
+ in_buf->data[0]));
+ if (rv != bytes) {
+ return;
+ }
+ rv = PR_SendTo(sockfd, in_buf->data, bytes, 0, &netaddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != bytes) {
+ return;
+ }
+ }
+
+ PR_DELETE(in_buf);
+ PR_Close(sockfd);
+
+ /*
+ * Decrement exit_counter and notify parent thread
+ */
+ PR_EnterMonitor(sp->exit_mon);
+ --(*sp->exit_counter);
+ PR_Notify(sp->exit_mon);
+ PR_ExitMonitor(sp->exit_mon);
+ DPRINTF(("UDP_Server [0x%x] exiting\n", PR_GetCurrentThread()));
+}
+
+/*
+ * TCP_Client
+ * Client Thread
+ * Connect to the server at the address specified in the argument.
+ * Fill in a buffer, write data to server, read it back and check
+ * for data corruption.
+ * Close the socket for server connection
+ */
+static void PR_CALLBACK
+TCP_Client(void *arg)
+{
+ Client_Param *cp = (Client_Param *) arg;
+ PRFileDesc *sockfd;
+ buffer *in_buf, *out_buf;
+ union PRNetAddr netaddr;
+ PRInt32 bytes, i, j;
+
+
+ bytes = cp->datalen;
+ out_buf = PR_NEW(buffer);
+ if (out_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n");
+ failed_already=1;
+ return;
+ }
+ in_buf = PR_NEW(buffer);
+ if (in_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n");
+ failed_already=1;
+ return;
+ }
+ netaddr = cp->server_addr;
+
+ for (i = 0; i < num_tcp_connections_per_client; i++) {
+ if ((sockfd = PR_OpenTCPSocket(client_domain)) == NULL) {
+ fprintf(stderr,"prsocket_test: PR_OpenTCPSocket failed\n");
+ failed_already=1;
+ return;
+ }
+ if (PR_Connect(sockfd, &netaddr,PR_INTERVAL_NO_TIMEOUT) < 0) {
+ fprintf(stderr, "PR_Connect failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ return;
+ }
+ for (j = 0; j < num_tcp_mesgs_per_connection; j++) {
+ /*
+ * fill in random data
+ */
+ memset(out_buf->data, ((PRInt32) (&netaddr)) + i + j, bytes);
+ /*
+ * write to server
+ */
+#ifdef WINNT
+ if (test_cancelio && (j == 0)) {
+ PR_Sleep(PR_SecondsToInterval(12));
+ }
+#endif
+ if (writen(sockfd, out_buf->data, bytes) < bytes) {
+ fprintf(stderr,"prsocket_test: ERROR - TCP_Client:writen\n");
+ failed_already=1;
+ return;
+ }
+ DPRINTF(("TCP Client [0x%lx]: out_buf = 0x%lx out_buf[0] = 0x%lx\n",
+ PR_GetCurrentThread(), out_buf, (*((int *) out_buf->data))));
+ if (readn(sockfd, in_buf->data, bytes) < bytes) {
+ fprintf(stderr,"prsocket_test: ERROR - TCP_Client:readn\n");
+ failed_already=1;
+ return;
+ }
+ /*
+ * verify the data read
+ */
+ if (memcmp(in_buf->data, out_buf->data, bytes) != 0) {
+ fprintf(stderr,"prsocket_test: ERROR - data corruption\n");
+ failed_already=1;
+ return;
+ }
+ }
+ /*
+ * shutdown reads and writes
+ */
+ if (PR_Shutdown(sockfd, PR_SHUTDOWN_BOTH) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Shutdown\n");
+ failed_already=1;
+ }
+ PR_Close(sockfd);
+ }
+
+ PR_DELETE(out_buf);
+ PR_DELETE(in_buf);
+
+ /*
+ * Decrement exit_counter and notify parent thread
+ */
+
+ PR_EnterMonitor(cp->exit_mon);
+ --(*cp->exit_counter);
+ PR_Notify(cp->exit_mon);
+ PR_ExitMonitor(cp->exit_mon);
+ DPRINTF(("TCP_Client [0x%x] exiting\n", PR_GetCurrentThread()));
+}
+
+/*
+ * UDP_Client
+ * Client Thread
+ * Create a socket and bind an address
+ * Communicate with the server at the address specified in the argument.
+ * Fill in a buffer, write data to server, read it back and check
+ * for data corruption.
+ * Close the socket
+ */
+static void PR_CALLBACK
+UDP_Client(void *arg)
+{
+ Client_Param *cp = (Client_Param *) arg;
+ PRFileDesc *sockfd;
+ buffer *in_buf, *out_buf;
+ union PRNetAddr netaddr;
+ PRInt32 bytes, i, rv;
+
+
+ bytes = cp->datalen;
+ out_buf = PR_NEW(buffer);
+ if (out_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n");
+ failed_already=1;
+ return;
+ }
+ in_buf = PR_NEW(buffer);
+ if (in_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n");
+ failed_already=1;
+ return;
+ }
+ if ((sockfd = PR_OpenUDPSocket(client_domain)) == NULL) {
+ fprintf(stderr,"prsocket_test: PR_OpenUDPSocket failed\n");
+ failed_already=1;
+ return;
+ }
+
+ /*
+ * bind an address for the client, let the system chose the port
+ * number
+ */
+ memset(&netaddr, 0, sizeof(netaddr));
+ if (PR_SetNetAddr(PR_IpAddrAny, client_domain, 0,
+ &netaddr) == PR_FAILURE) {
+ fprintf(stderr,"prsocket_test: PR_SetNetAddr failed\n");
+ failed_already=1;
+ return;
+ }
+ if (PR_Bind(sockfd, &netaddr) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n");
+ perror("PR_Bind");
+ return;
+ }
+
+ if (PR_GetSockName(sockfd, &netaddr) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_GetSockName failed\n");
+ failed_already=1;
+ return;
+ }
+
+ DPRINTF(("PR_Bind: UDP Client netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n",
+ netaddr.inet.ip, netaddr.inet.port));
+
+ netaddr = cp->server_addr;
+
+ if (cp->udp_connect) {
+ if (PR_Connect(sockfd, &netaddr,PR_INTERVAL_NO_TIMEOUT) < 0) {
+ fprintf(stderr,"prsocket_test: PR_Connect failed\n");
+ failed_already=1;
+ return;
+ }
+ }
+
+ for (i = 0; i < num_udp_datagrams_per_client; i++) {
+ /*
+ * fill in random data
+ */
+ DPRINTF(("UDP_Client [0x%lx]: out_buf = 0x%lx bytes = 0x%lx\n",
+ PR_GetCurrentThread(), out_buf->data, bytes));
+ memset(out_buf->data, ((PRInt32) (&netaddr)) + i, bytes);
+ /*
+ * write to server
+ */
+ if (cp->udp_connect)
+ rv = PR_Send(sockfd, out_buf->data, bytes, 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ else
+ rv = PR_SendTo(sockfd, out_buf->data, bytes, 0, &netaddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != bytes) {
+ return;
+ }
+ DPRINTF(("UDP_Client [0x%lx]: out_buf = 0x%lx out_buf[0] = 0x%lx\n",
+ PR_GetCurrentThread(), out_buf, (*((int *) out_buf->data))));
+ if (cp->udp_connect)
+ rv = PR_Recv(sockfd, in_buf->data, bytes, 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ else
+ rv = PR_RecvFrom(sockfd, in_buf->data, bytes, 0, &netaddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != bytes) {
+ return;
+ }
+ DPRINTF(("UDP_Client [0x%lx]: in_buf = 0x%lx in_buf[0] = 0x%lx\n",
+ PR_GetCurrentThread(), in_buf, (*((int *) in_buf->data))));
+ /*
+ * verify the data read
+ */
+ if (memcmp(in_buf->data, out_buf->data, bytes) != 0) {
+ fprintf(stderr,"prsocket_test: ERROR - UDP data corruption\n");
+ failed_already=1;
+ return;
+ }
+ }
+ PR_Close(sockfd);
+
+ PR_DELETE(in_buf);
+ PR_DELETE(out_buf);
+
+ /*
+ * Decrement exit_counter and notify parent thread
+ */
+
+ PR_EnterMonitor(cp->exit_mon);
+ --(*cp->exit_counter);
+ PR_Notify(cp->exit_mon);
+ PR_ExitMonitor(cp->exit_mon);
+ PR_DELETE(cp);
+ DPRINTF(("UDP_Client [0x%x] exiting\n", PR_GetCurrentThread()));
+}
+
+/*
+ * TCP_Socket_Client_Server_Test - concurrent server test
+ *
+ * One server and several clients are started
+ * Each client connects to the server and sends a chunk of data
+ * For each connection, server starts another thread to read the data
+ * from the client and send it back to the client, unmodified.
+ * Each client checks that data received from server is same as the
+ * data it sent to the server.
+ *
+ */
+
+static PRInt32
+TCP_Socket_Client_Server_Test(void)
+{
+ int i;
+ PRThread *t;
+ PRSemaphore *server_sem;
+ Server_Param *sparamp;
+ Client_Param *cparamp;
+ PRMonitor *mon2;
+ PRInt32 datalen;
+
+
+ datalen = tcp_mesg_size;
+ thread_count = 0;
+ /*
+ * start the server thread
+ */
+ sparamp = PR_NEW(Server_Param);
+ if (sparamp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ return -1;
+ }
+ server_sem = PR_NewSem(0);
+ if (server_sem == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewSem failed\n");
+ failed_already=1;
+ return -1;
+ }
+ mon2 = PR_NewMonitor();
+ if (mon2 == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewMonitor failed\n");
+ failed_already=1;
+ return -1;
+ }
+ PR_EnterMonitor(mon2);
+
+ sparamp->addr_sem = server_sem;
+ sparamp->exit_mon = mon2;
+ sparamp->exit_counter = &thread_count;
+ sparamp->datalen = datalen;
+ t = PR_CreateThread(PR_USER_THREAD,
+ TCP_Server, (void *)sparamp,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ return -1;
+ }
+ DPRINTF(("Created TCP server = 0x%lx\n", t));
+ thread_count++;
+
+ /*
+ * wait till the server address is setup
+ */
+ PR_WaitSem(server_sem);
+
+ /*
+ * Now start a bunch of client threads
+ */
+
+ cparamp = PR_NEW(Client_Param);
+ if (cparamp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ return -1;
+ }
+ cparamp->server_addr = tcp_server_addr;
+ cparamp->exit_mon = mon2;
+ cparamp->exit_counter = &thread_count;
+ cparamp->datalen = datalen;
+ for (i = 0; i < num_tcp_clients; i++) {
+ t = create_new_thread(PR_USER_THREAD,
+ TCP_Client, (void *) cparamp,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0, i);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ return -1;
+ }
+ DPRINTF(("Created TCP client = 0x%lx\n", t));
+ thread_count++;
+ }
+ /* Wait for server and client threads to exit */
+ while (thread_count) {
+ PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("TCP Server - thread_count = %d\n", thread_count));
+ }
+ PR_ExitMonitor(mon2);
+ printf("%30s","TCP_Socket_Client_Server_Test:");
+ printf("%2ld Server %2ld Clients %2ld connections_per_client\n",1l,
+ num_tcp_clients, num_tcp_connections_per_client);
+ printf("%30s %2ld messages_per_connection %4ld bytes_per_message\n",":",
+ num_tcp_mesgs_per_connection, tcp_mesg_size);
+
+ return 0;
+}
+
+/*
+ * UDP_Socket_Client_Server_Test - iterative server test
+ *
+ * One server and several clients are started
+ * Each client connects to the server and sends a chunk of data
+ * For each connection, server starts another thread to read the data
+ * from the client and send it back to the client, unmodified.
+ * Each client checks that data received from server is same as the
+ * data it sent to the server.
+ *
+ */
+
+static PRInt32
+UDP_Socket_Client_Server_Test(void)
+{
+ int i;
+ PRThread *t;
+ PRSemaphore *server_sem;
+ Server_Param *sparamp;
+ Client_Param *cparamp;
+ PRMonitor *mon2;
+ PRInt32 datalen;
+ PRInt32 udp_connect = 1;
+
+
+ datalen = udp_datagram_size;
+ thread_count = 0;
+ /*
+ * start the server thread
+ */
+ sparamp = PR_NEW(Server_Param);
+ if (sparamp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ return -1;
+ }
+ server_sem = PR_NewSem(0);
+ if (server_sem == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewSem failed\n");
+ failed_already=1;
+ return -1;
+ }
+ mon2 = PR_NewMonitor();
+ if (mon2 == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewMonitor failed\n");
+ failed_already=1;
+ return -1;
+ }
+ PR_EnterMonitor(mon2);
+
+ sparamp->addr_sem = server_sem;
+ sparamp->exit_mon = mon2;
+ sparamp->exit_counter = &thread_count;
+ sparamp->datalen = datalen;
+ DPRINTF(("Creating UDP server"));
+ t = PR_CreateThread(PR_USER_THREAD,
+ UDP_Server, (void *)sparamp,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ return -1;
+ }
+ thread_count++;
+
+ /*
+ * wait till the server address is setup
+ */
+ PR_WaitSem(server_sem);
+
+ /*
+ * Now start a bunch of client threads
+ */
+
+ for (i = 0; i < num_udp_clients; i++) {
+ cparamp = PR_NEW(Client_Param);
+ if (cparamp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ return -1;
+ }
+ cparamp->server_addr = udp_server_addr;
+ cparamp->exit_mon = mon2;
+ cparamp->exit_counter = &thread_count;
+ cparamp->datalen = datalen;
+ /*
+ * Cause every other client thread to connect udp sockets
+ */
+ cparamp->udp_connect = udp_connect;
+ if (udp_connect) {
+ udp_connect = 0;
+ }
+ else {
+ udp_connect = 1;
+ }
+ DPRINTF(("Creating UDP client %d\n", i));
+ t = PR_CreateThread(PR_USER_THREAD,
+ UDP_Client, (void *) cparamp,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ return -1;
+ }
+ thread_count++;
+ }
+ /* Wait for server and client threads to exit */
+ while (thread_count) {
+ PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("UDP Server - thread_count = %d\n", thread_count));
+ }
+ PR_ExitMonitor(mon2);
+ printf("%30s","UDP_Socket_Client_Server_Test: ");
+ printf("%2ld Server %2ld Clients\n",1l, num_udp_clients);
+ printf("%30s %2ld datagrams_per_client %4ld bytes_per_datagram\n",":",
+ num_udp_datagrams_per_client, udp_datagram_size);
+
+ return 0;
+}
+
+static PRFileDesc *small_file_fd, *large_file_fd;
+static void *small_file_addr, *small_file_header, *large_file_addr;
+static void *small_file_trailer, *large_file_header, *large_file_trailer;
+/*
+ * TransmitFile_Client
+ * Client Thread
+ */
+static void
+TransmitFile_Client(void *arg)
+{
+ PRFileDesc *sockfd;
+ union PRNetAddr netaddr;
+ char *small_buf, *large_buf;
+ Client_Param *cp = (Client_Param *) arg;
+ PRInt32 rlen;
+
+ small_buf = (char*)PR_Malloc(SMALL_FILE_SIZE + SMALL_FILE_HEADER_SIZE +
+ SMALL_FILE_TRAILER_SIZE);
+ if (small_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer\n");
+ failed_already=1;
+ return;
+ }
+ large_buf = (char*)PR_Malloc(LARGE_FILE_SIZE + LARGE_FILE_HEADER_SIZE +
+ LARGE_FILE_TRAILER_SIZE);
+ if (large_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer\n");
+ failed_already=1;
+ return;
+ }
+ netaddr.inet.family = cp->server_addr.inet.family;
+ netaddr.inet.port = cp->server_addr.inet.port;
+ netaddr.inet.ip = cp->server_addr.inet.ip;
+
+ if ((sockfd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewTCPSocket failed\n");
+ failed_already=1;
+ return;
+ }
+
+ if (PR_Connect(sockfd, &netaddr,PR_INTERVAL_NO_TIMEOUT) < 0) {
+ fprintf(stderr,"prsocket_test: PR_Connect failed\n");
+ failed_already=1;
+ return;
+ }
+ /*
+ * read the small file and verify the data
+ */
+ if (readn(sockfd, small_buf, SMALL_FILE_SIZE + SMALL_FILE_HEADER_SIZE)
+ != (SMALL_FILE_SIZE + SMALL_FILE_HEADER_SIZE)) {
+ fprintf(stderr,
+ "prsocket_test: TransmitFile_Client failed to receive file\n");
+ failed_already=1;
+ return;
+ }
+#if defined(XP_UNIX)
+ /* File transmission test can not be done because of large file's size */
+ if (memcmp(small_file_header, small_buf, SMALL_FILE_HEADER_SIZE) != 0) {
+ fprintf(stderr,
+ "prsocket_test: TransmitFile_Client ERROR - small file header data corruption\n");
+ failed_already=1;
+ return;
+ }
+ if (memcmp(small_file_addr, small_buf + SMALL_FILE_HEADER_SIZE,
+ SMALL_FILE_SIZE) != 0) {
+ fprintf(stderr,
+ "prsocket_test: TransmitFile_Client ERROR - small file data corruption\n");
+ failed_already=1;
+ return;
+ }
+#endif
+ /*
+ * read the large file and verify the data
+ */
+ if (readn(sockfd, large_buf, LARGE_FILE_SIZE) != LARGE_FILE_SIZE) {
+ fprintf(stderr,
+ "prsocket_test: TransmitFile_Client failed to receive file\n");
+ failed_already=1;
+ return;
+ }
+#if defined(XP_UNIX)
+ if (memcmp(large_file_addr, large_buf, LARGE_FILE_SIZE) != 0) {
+ fprintf(stderr,
+ "prsocket_test: TransmitFile_Client ERROR - large file data corruption\n");
+ failed_already=1;
+ }
+#endif
+
+
+ /*
+ * receive data from PR_SendFile
+ */
+ /*
+ * case 1: small file with header and trailer
+ */
+ rlen = SMALL_FILE_SIZE + SMALL_FILE_HEADER_SIZE +
+ SMALL_FILE_TRAILER_SIZE;
+ if (readn(sockfd, small_buf, rlen) != rlen) {
+ fprintf(stderr,
+ "prsocket_test: SendFile_Client failed to receive file\n");
+ failed_already=1;
+ return;
+ }
+#if defined(XP_UNIX)
+ if (memcmp(small_file_header, small_buf, SMALL_FILE_HEADER_SIZE) != 0) {
+ fprintf(stderr,
+ "SendFile 1. ERROR - small file header corruption\n");
+ failed_already=1;
+ return;
+ }
+ if (memcmp(small_file_addr, small_buf + SMALL_FILE_HEADER_SIZE,
+ SMALL_FILE_SIZE) != 0) {
+ fprintf(stderr,
+ "SendFile 1. ERROR - small file data corruption\n");
+ failed_already=1;
+ return;
+ }
+ if (memcmp(small_file_trailer,
+ small_buf + SMALL_FILE_HEADER_SIZE + SMALL_FILE_SIZE,
+ SMALL_FILE_TRAILER_SIZE) != 0) {
+ fprintf(stderr,
+ "SendFile 1. ERROR - small file trailer corruption\n");
+ failed_already=1;
+ return;
+ }
+#endif
+ /*
+ * case 2: partial large file at zero offset, file with header and trailer
+ */
+ rlen = LARGE_FILE_LEN_1 + LARGE_FILE_HEADER_SIZE +
+ LARGE_FILE_TRAILER_SIZE;
+ if (readn(sockfd, large_buf, rlen) != rlen) {
+ fprintf(stderr,
+ "prsocket_test: SendFile_Client failed to receive file\n");
+ failed_already=1;
+ return;
+ }
+#if defined(XP_UNIX)
+ if (memcmp(large_file_header, large_buf, LARGE_FILE_HEADER_SIZE) != 0) {
+ fprintf(stderr,
+ "SendFile 2. ERROR - large file header corruption\n");
+ failed_already=1;
+ return;
+ }
+ if (memcmp(large_file_addr, large_buf + LARGE_FILE_HEADER_SIZE,
+ LARGE_FILE_LEN_1) != 0) {
+ fprintf(stderr,
+ "SendFile 2. ERROR - large file data corruption\n");
+ failed_already=1;
+ return;
+ }
+ if (memcmp(large_file_trailer,
+ large_buf + LARGE_FILE_HEADER_SIZE + LARGE_FILE_LEN_1,
+ LARGE_FILE_TRAILER_SIZE) != 0) {
+ fprintf(stderr,
+ "SendFile 2. ERROR - large file trailer corruption\n");
+ failed_already=1;
+ return;
+ }
+#endif
+ /*
+ * case 3: partial small file at non-zero offset, with header
+ */
+ rlen = SMALL_FILE_LEN_1 + SMALL_FILE_HEADER_SIZE;
+ if (readn(sockfd, small_buf, rlen) != rlen) {
+ fprintf(stderr,
+ "prsocket_test: SendFile_Client failed to receive file\n");
+ failed_already=1;
+ return;
+ }
+#if defined(XP_UNIX)
+ if (memcmp(small_file_header, small_buf, SMALL_FILE_HEADER_SIZE) != 0) {
+ fprintf(stderr,
+ "SendFile 3. ERROR - small file header corruption\n");
+ failed_already=1;
+ return;
+ }
+ if (memcmp((char *) small_file_addr + SMALL_FILE_OFFSET_1,
+ small_buf + SMALL_FILE_HEADER_SIZE, SMALL_FILE_LEN_1) != 0) {
+ fprintf(stderr,
+ "SendFile 3. ERROR - small file data corruption\n");
+ failed_already=1;
+ return;
+ }
+#endif
+ /*
+ * case 4: partial small file at non-zero offset, with trailer
+ */
+ rlen = SMALL_FILE_LEN_2 + SMALL_FILE_TRAILER_SIZE;
+ if (readn(sockfd, small_buf, rlen) != rlen) {
+ fprintf(stderr,
+ "prsocket_test: SendFile_Client failed to receive file\n");
+ failed_already=1;
+ return;
+ }
+#if defined(XP_UNIX)
+ if (memcmp((char *) small_file_addr + SMALL_FILE_OFFSET_2, small_buf,
+ SMALL_FILE_LEN_2) != 0) {
+ fprintf(stderr,
+ "SendFile 4. ERROR - small file data corruption\n");
+ failed_already=1;
+ return;
+ }
+ if (memcmp(small_file_trailer, small_buf + SMALL_FILE_LEN_2,
+ SMALL_FILE_TRAILER_SIZE) != 0) {
+ fprintf(stderr,
+ "SendFile 4. ERROR - small file trailer corruption\n");
+ failed_already=1;
+ return;
+ }
+#endif
+ /*
+ * case 5: partial large file at non-zero offset, file with header
+ */
+ rlen = LARGE_FILE_LEN_2 + LARGE_FILE_HEADER_SIZE;
+ if (readn(sockfd, large_buf, rlen) != rlen) {
+ fprintf(stderr,
+ "prsocket_test: SendFile_Client failed to receive file\n");
+ failed_already=1;
+ return;
+ }
+#if defined(XP_UNIX)
+ if (memcmp(large_file_header, large_buf, LARGE_FILE_HEADER_SIZE) != 0) {
+ fprintf(stderr,
+ "SendFile 5. ERROR - large file header corruption\n");
+ failed_already=1;
+ return;
+ }
+ if (memcmp((char *)large_file_addr + LARGE_FILE_OFFSET_2,
+ large_buf + LARGE_FILE_HEADER_SIZE,
+ LARGE_FILE_LEN_2) != 0) {
+ fprintf(stderr,
+ "SendFile 5. ERROR - large file data corruption\n");
+ failed_already=1;
+ return;
+ }
+#endif
+ /*
+ * case 6: partial small file at non-zero offset, with header
+ */
+ rlen = SMALL_FILE_LEN_3 + SMALL_FILE_HEADER_SIZE;
+ if (readn(sockfd, small_buf, rlen) != rlen) {
+ fprintf(stderr,
+ "prsocket_test: SendFile_Client failed to receive file\n");
+ failed_already=1;
+ return;
+ }
+#if defined(XP_UNIX)
+ if (memcmp(small_file_header, small_buf, SMALL_FILE_HEADER_SIZE) != 0) {
+ fprintf(stderr,
+ "SendFile 6. ERROR - small file header corruption\n");
+ return;
+ }
+ if (memcmp((char *) small_file_addr + SMALL_FILE_OFFSET_3,
+ small_buf + SMALL_FILE_HEADER_SIZE, SMALL_FILE_LEN_3) != 0) {
+#if 0
+ char *i, *j;
+ int k;
+
+ i = (char *) small_file_addr + SMALL_FILE_OFFSET_3;
+ j = small_buf + SMALL_FILE_HEADER_SIZE;
+ k = SMALL_FILE_LEN_3;
+ while (k-- > 0) {
+ if (*i++ != *j++)
+ printf("i = %d j = %d\n",
+ (int) (i - ((char *) small_file_addr + SMALL_FILE_OFFSET_3)),
+ (int) (j - (small_buf + SMALL_FILE_HEADER_SIZE)));
+ }
+#endif
+ fprintf(stderr,
+ "SendFile 6. ERROR - small file data corruption\n");
+ failed_already=1;
+ return;
+ }
+#endif
+ /*
+ * case 7: partial large file at non-zero offset, with header
+ */
+ rlen = LARGE_FILE_LEN_3 + LARGE_FILE_HEADER_SIZE;
+ if (readn(sockfd, large_buf, rlen) != rlen) {
+ fprintf(stderr,
+ "prsocket_test: SendFile_Client failed to receive file\n");
+ failed_already=1;
+ return;
+ }
+#if defined(XP_UNIX)
+ if (memcmp(large_file_header, large_buf, LARGE_FILE_HEADER_SIZE) != 0) {
+ fprintf(stderr,
+ "SendFile 7. ERROR - large file header corruption\n");
+ failed_already=1;
+ return;
+ }
+ if (memcmp((char *)large_file_addr + LARGE_FILE_OFFSET_3,
+ large_buf + LARGE_FILE_HEADER_SIZE,
+ LARGE_FILE_LEN_3) != 0) {
+ fprintf(stderr,
+ "SendFile 7. ERROR - large file data corruption\n");
+ failed_already=1;
+ return;
+ }
+#endif
+ /*
+ * case 8: partial large file at non-zero, page-aligned offset, with
+ * header and trailer
+ */
+ rlen = LARGE_FILE_LEN_4 + LARGE_FILE_HEADER_SIZE +
+ LARGE_FILE_TRAILER_SIZE;
+ if (readn(sockfd, large_buf, rlen) != rlen) {
+ fprintf(stderr,
+ "prsocket_test: SendFile_Client failed to receive file\n");
+ failed_already=1;
+ return;
+ }
+#if defined(XP_UNIX)
+ if (memcmp(large_file_header, large_buf, LARGE_FILE_HEADER_SIZE) != 0) {
+ fprintf(stderr,
+ "SendFile 2. ERROR - large file header corruption\n");
+ failed_already=1;
+ return;
+ }
+ if (memcmp((char *)large_file_addr + LARGE_FILE_OFFSET_4,
+ large_buf + LARGE_FILE_HEADER_SIZE,
+ LARGE_FILE_LEN_4) != 0) {
+ fprintf(stderr,
+ "SendFile 2. ERROR - large file data corruption\n");
+ failed_already=1;
+ return;
+ }
+ if (memcmp(large_file_trailer,
+ large_buf + LARGE_FILE_HEADER_SIZE + LARGE_FILE_LEN_4,
+ LARGE_FILE_TRAILER_SIZE) != 0) {
+ fprintf(stderr,
+ "SendFile 2. ERROR - large file trailer corruption\n");
+ failed_already=1;
+ return;
+ }
+#endif
+ PR_DELETE(small_buf);
+ PR_DELETE(large_buf);
+ PR_Close(sockfd);
+
+
+ /*
+ * Decrement exit_counter and notify parent thread
+ */
+
+ PR_EnterMonitor(cp->exit_mon);
+ --(*cp->exit_counter);
+ PR_Notify(cp->exit_mon);
+ PR_ExitMonitor(cp->exit_mon);
+ DPRINTF(("TransmitFile_Client [0x%lx] exiting\n", PR_GetCurrentThread()));
+}
+
+/*
+ * Serve_TransmitFile_Client
+ * Thread, started by the server, for serving a client connection.
+ * Trasmits a small file, with a header, and a large file, without
+ * a header
+ */
+static void
+Serve_TransmitFile_Client(void *arg)
+{
+ Serve_Client_Param *scp = (Serve_Client_Param *) arg;
+ PRFileDesc *sockfd;
+ PRInt32 bytes;
+ PRFileDesc *local_small_file_fd=NULL;
+ PRFileDesc *local_large_file_fd=NULL;
+ PRSendFileData sfd;
+ PRInt32 slen;
+
+ sockfd = scp->sockfd;
+ local_small_file_fd = PR_Open(SMALL_FILE_NAME, PR_RDONLY,0);
+
+ if (local_small_file_fd == NULL) {
+ fprintf(stderr,"prsocket_test failed to open file for transmitting %s\n",
+ SMALL_FILE_NAME);
+ failed_already=1;
+ goto done;
+ }
+ local_large_file_fd = PR_Open(LARGE_FILE_NAME, PR_RDONLY,0);
+
+ if (local_large_file_fd == NULL) {
+ fprintf(stderr,"prsocket_test failed to open file for transmitting %s\n",
+ LARGE_FILE_NAME);
+ failed_already=1;
+ goto done;
+ }
+ bytes = PR_TransmitFile(sockfd, local_small_file_fd, small_file_header,
+ SMALL_FILE_HEADER_SIZE, PR_TRANSMITFILE_KEEP_OPEN,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytes != (SMALL_FILE_SIZE+ SMALL_FILE_HEADER_SIZE)) {
+ fprintf(stderr,
+ "prsocet_test: PR_TransmitFile failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+ bytes = PR_TransmitFile(sockfd, local_large_file_fd, NULL, 0,
+ PR_TRANSMITFILE_KEEP_OPEN, PR_INTERVAL_NO_TIMEOUT);
+ if (bytes != LARGE_FILE_SIZE) {
+ fprintf(stderr,
+ "prsocket_test: PR_TransmitFile failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+
+ /*
+ * PR_SendFile test cases
+ */
+
+ /*
+ * case 1: small file with header and trailer
+ */
+ sfd.fd = local_small_file_fd;
+ sfd.file_offset = 0;
+ sfd.file_nbytes = 0;
+ sfd.header = small_file_header;
+ sfd.hlen = SMALL_FILE_HEADER_SIZE;
+ sfd.trailer = small_file_trailer;
+ sfd.tlen = SMALL_FILE_TRAILER_SIZE;
+ bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_KEEP_OPEN,
+ PR_INTERVAL_NO_TIMEOUT);
+ slen = SMALL_FILE_SIZE+ SMALL_FILE_HEADER_SIZE +
+ SMALL_FILE_TRAILER_SIZE;
+ if (bytes != slen) {
+ fprintf(stderr,
+ "socket: Error - 1. PR_SendFile send_size = %d, bytes sent = %d\n",
+ slen, bytes);
+ fprintf(stderr,
+ "prsocket_test: PR_SendFile failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+
+ /*
+ * case 2: partial large file at zero offset, file with header and trailer
+ */
+ sfd.fd = local_large_file_fd;
+ sfd.file_offset = 0;
+ sfd.file_nbytes = LARGE_FILE_LEN_1;
+ sfd.header = large_file_header;
+ sfd.hlen = LARGE_FILE_HEADER_SIZE;
+ sfd.trailer = large_file_trailer;
+ sfd.tlen = LARGE_FILE_TRAILER_SIZE;
+ bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_KEEP_OPEN,
+ PR_INTERVAL_NO_TIMEOUT);
+ slen = LARGE_FILE_LEN_1 + LARGE_FILE_HEADER_SIZE +
+ LARGE_FILE_TRAILER_SIZE;
+ if (bytes != slen) {
+ fprintf(stderr,
+ "socket: Error - 2. PR_SendFile send_size = %d, bytes sent = %d\n",
+ slen, bytes);
+ fprintf(stderr,
+ "prsocket_test: PR_SendFile failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+ /*
+ * case 3: partial small file at non-zero offset, with header
+ */
+ sfd.fd = local_small_file_fd;
+ sfd.file_offset = SMALL_FILE_OFFSET_1;
+ sfd.file_nbytes = SMALL_FILE_LEN_1;
+ sfd.header = small_file_header;
+ sfd.hlen = SMALL_FILE_HEADER_SIZE;
+ sfd.trailer = NULL;
+ sfd.tlen = 0;
+ bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_KEEP_OPEN,
+ PR_INTERVAL_NO_TIMEOUT);
+ slen = SMALL_FILE_LEN_1 + SMALL_FILE_HEADER_SIZE;
+ if (bytes != slen) {
+ fprintf(stderr,
+ "socket: Error - 3. PR_SendFile send_size = %d, bytes sent = %d\n",
+ slen, bytes);
+ fprintf(stderr,
+ "prsocket_test: PR_SendFile failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+ /*
+ * case 4: partial small file at non-zero offset, with trailer
+ */
+ sfd.fd = local_small_file_fd;
+ sfd.file_offset = SMALL_FILE_OFFSET_2;
+ sfd.file_nbytes = SMALL_FILE_LEN_2;
+ sfd.header = NULL;
+ sfd.hlen = 0;
+ sfd.trailer = small_file_trailer;
+ sfd.tlen = SMALL_FILE_TRAILER_SIZE;
+ bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_KEEP_OPEN,
+ PR_INTERVAL_NO_TIMEOUT);
+ slen = SMALL_FILE_LEN_2 + SMALL_FILE_TRAILER_SIZE;
+ if (bytes != slen) {
+ fprintf(stderr,
+ "socket: Error - 4. PR_SendFile send_size = %d, bytes sent = %d\n",
+ slen, bytes);
+ fprintf(stderr,
+ "prsocket_test: PR_SendFile failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+ /*
+ * case 5: partial large file at non-zero offset, file with header
+ */
+ sfd.fd = local_large_file_fd;
+ sfd.file_offset = LARGE_FILE_OFFSET_2;
+ sfd.file_nbytes = LARGE_FILE_LEN_2;
+ sfd.header = large_file_header;
+ sfd.hlen = LARGE_FILE_HEADER_SIZE;
+ sfd.trailer = NULL;
+ sfd.tlen = 0;
+ bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_KEEP_OPEN,
+ PR_INTERVAL_NO_TIMEOUT);
+ slen = LARGE_FILE_LEN_2 + LARGE_FILE_HEADER_SIZE;
+ if (bytes != slen) {
+ fprintf(stderr,
+ "socket: Error - 5. PR_SendFile send_size = %d, bytes sent = %d\n",
+ slen, bytes);
+ fprintf(stderr,
+ "prsocket_test: PR_SendFile failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+ /*
+ * case 6: partial small file from non-zero offset till end of file, with header
+ */
+ sfd.fd = local_small_file_fd;
+ sfd.file_offset = SMALL_FILE_OFFSET_3;
+ sfd.file_nbytes = 0; /* data from offset to end-of-file */
+ sfd.header = small_file_header;
+ sfd.hlen = SMALL_FILE_HEADER_SIZE;
+ sfd.trailer = NULL;
+ sfd.tlen = 0;
+ bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_KEEP_OPEN,
+ PR_INTERVAL_NO_TIMEOUT);
+ slen = SMALL_FILE_LEN_3 + SMALL_FILE_HEADER_SIZE;
+ if (bytes != slen) {
+ fprintf(stderr,
+ "socket: Error - 6. PR_SendFile send_size = %d, bytes sent = %d\n",
+ slen, bytes);
+ fprintf(stderr,
+ "prsocket_test: PR_SendFile failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+ /*
+ * case 7: partial large file at non-zero offset till end-of-file, with header
+ */
+ sfd.fd = local_large_file_fd;
+ sfd.file_offset = LARGE_FILE_OFFSET_3;
+ sfd.file_nbytes = 0; /* data until end-of-file */
+ sfd.header = large_file_header;
+ sfd.hlen = LARGE_FILE_HEADER_SIZE;
+ sfd.trailer = NULL;
+ sfd.tlen = 0;
+ bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_KEEP_OPEN,
+ PR_INTERVAL_NO_TIMEOUT);
+ slen = LARGE_FILE_LEN_3 + LARGE_FILE_HEADER_SIZE;
+ if (bytes != slen) {
+ fprintf(stderr,
+ "socket: Error - 7. PR_SendFile send_size = %d, bytes sent = %d\n",
+ slen, bytes);
+ fprintf(stderr,
+ "prsocket_test: PR_SendFile failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+ /*
+ * case 8: partial large file at non-zero page-aligned offset,
+ * with header and trailer
+ */
+ sfd.fd = local_large_file_fd;
+ sfd.file_offset = LARGE_FILE_OFFSET_4;
+ sfd.file_nbytes = LARGE_FILE_LEN_4;
+ sfd.header = large_file_header;
+ sfd.hlen = LARGE_FILE_HEADER_SIZE;
+ sfd.trailer = large_file_trailer;
+ sfd.tlen = LARGE_FILE_TRAILER_SIZE;
+ bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_CLOSE_SOCKET,
+ PR_INTERVAL_NO_TIMEOUT);
+ slen = LARGE_FILE_LEN_4 + LARGE_FILE_HEADER_SIZE +
+ LARGE_FILE_TRAILER_SIZE;
+ if (bytes != slen) {
+ fprintf(stderr,
+ "socket: Error - 2. PR_SendFile send_size = %d, bytes sent = %d\n",
+ slen, bytes);
+ fprintf(stderr,
+ "prsocket_test: PR_SendFile failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+done:
+ if (local_small_file_fd != NULL) {
+ PR_Close(local_small_file_fd);
+ }
+ if (local_large_file_fd != NULL) {
+ PR_Close(local_large_file_fd);
+ }
+}
+
+/*
+ * TransmitFile Server
+ * Server Thread
+ * Bind an address to a socket and listen for incoming connections
+ * Create worker threads to service clients
+ */
+static void
+TransmitFile_Server(void *arg)
+{
+ PRThread **t = NULL; /* an array of PRThread pointers */
+ Server_Param *sp = (Server_Param *) arg;
+ Serve_Client_Param *scp;
+ PRFileDesc *sockfd = NULL, *newsockfd;
+ PRNetAddr netaddr;
+ PRInt32 i;
+
+ t = (PRThread**)PR_MALLOC(num_transmitfile_clients * sizeof(PRThread *));
+ if (t == NULL) {
+ fprintf(stderr, "prsocket_test: run out of memory\n");
+ failed_already=1;
+ goto exit;
+ }
+ /*
+ * Create a tcp socket
+ */
+ if ((sockfd = PR_OpenTCPSocket(PR_AF_INET)) == NULL) {
+ fprintf(stderr,"prsocket_test: PR_OpenTCPSocket failed\n");
+ failed_already=1;
+ goto exit;
+ }
+ memset(&netaddr, 0, sizeof(netaddr));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
+ netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ /*
+ * try a few times to bind server's address, if addresses are in
+ * use
+ */
+ i = 0;
+ while (PR_Bind(sockfd, &netaddr) < 0) {
+ if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
+ netaddr.inet.port += 2;
+ if (i++ < SERVER_MAX_BIND_COUNT) {
+ continue;
+ }
+ }
+ fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n");
+ failed_already=1;
+ perror("PR_Bind");
+ goto exit;
+ }
+
+ if (PR_Listen(sockfd, 32) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Listen failed\n");
+ failed_already=1;
+ goto exit;
+ }
+
+ if (PR_GetSockName(sockfd, &netaddr) < 0) {
+ fprintf(stderr,
+ "prsocket_test: ERROR - PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit;
+ }
+
+ DPRINTF(("TCP_Server: PR_BIND netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n",
+ netaddr.inet.ip, netaddr.inet.port));
+ tcp_server_addr.inet.family = netaddr.inet.family;
+ tcp_server_addr.inet.port = netaddr.inet.port;
+ tcp_server_addr.inet.ip = netaddr.inet.ip;
+
+ /*
+ * Wake up parent thread because server address is bound and made
+ * available in the global variable 'tcp_server_addr'
+ */
+ PR_PostSem(sp->addr_sem);
+
+ for (i = 0; i < num_transmitfile_clients ; i++) {
+ /* test both null and non-null 'addr' argument to PR_Accept */
+ PRNetAddr *addrp = (i%2 ? &netaddr: NULL);
+
+ if ((newsockfd = PR_Accept(sockfd, addrp,
+ PR_INTERVAL_NO_TIMEOUT)) == NULL) {
+ fprintf(stderr,
+ "prsocket_test: ERROR - PR_Accept failed\n");
+ failed_already=1;
+ goto exit;
+ }
+ /* test both regular and emulated PR_SendFile */
+ if (i%2) {
+ PRFileDesc *layer = PR_CreateIOLayerStub(
+ emuSendFileIdentity, &emuSendFileMethods);
+ if (layer == NULL) {
+ fprintf(stderr,
+ "prsocket_test: ERROR - PR_CreateIOLayerStub failed\n");
+ failed_already=1;
+ goto exit;
+ }
+ if (PR_PushIOLayer(newsockfd, PR_TOP_IO_LAYER, layer)
+ == PR_FAILURE) {
+ fprintf(stderr,
+ "prsocket_test: ERROR - PR_PushIOLayer failed\n");
+ failed_already=1;
+ goto exit;
+ }
+ }
+ scp = PR_NEW(Serve_Client_Param);
+ if (scp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ goto exit;
+ }
+
+ /*
+ * Start a Serve_Client thread for each incoming connection
+ */
+ scp->sockfd = newsockfd;
+ scp->datalen = sp->datalen;
+
+ t[i] = PR_CreateThread(PR_USER_THREAD,
+ Serve_TransmitFile_Client, (void *)scp,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (t[i] == NULL) {
+ fprintf(stderr,
+ "prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ goto exit;
+ }
+ DPRINTF(("TransmitFile_Server: Created Serve_TransmitFile_Client = 0x%lx\n", t));
+ }
+
+ /*
+ * Wait for all the worker threads to end, so that we know
+ * they are no longer using the small and large file fd's.
+ */
+
+ for (i = 0; i < num_transmitfile_clients; i++) {
+ PR_JoinThread(t[i]);
+ }
+
+exit:
+ if (t) {
+ PR_DELETE(t);
+ }
+ if (sockfd) {
+ PR_Close(sockfd);
+ }
+
+ /*
+ * Decrement exit_counter and notify parent thread
+ */
+
+ PR_EnterMonitor(sp->exit_mon);
+ --(*sp->exit_counter);
+ PR_Notify(sp->exit_mon);
+ PR_ExitMonitor(sp->exit_mon);
+ DPRINTF(("TransmitFile_Server [0x%lx] exiting\n", PR_GetCurrentThread()));
+}
+
+/*
+ * Socket_Misc_Test - test miscellaneous functions
+ *
+ */
+static PRInt32
+Socket_Misc_Test(void)
+{
+ PRIntn i, rv = 0, bytes, count, len;
+ PRThread *t;
+ PRSemaphore *server_sem;
+ Server_Param *sparamp;
+ Client_Param *cparamp;
+ PRMonitor *mon2;
+ PRInt32 datalen;
+
+ /*
+ * We deliberately pick a buffer size that is not a nice multiple
+ * of 1024.
+ */
+#define TRANSMITFILE_BUF_SIZE (4 * 1024 - 11)
+
+ typedef struct {
+ char data[TRANSMITFILE_BUF_SIZE];
+ } file_buf;
+ file_buf *buf = NULL;
+
+ /*
+ * create file(s) to be transmitted
+ */
+ if ((PR_MkDir(TEST_DIR, 0777)) < 0) {
+ printf("prsocket_test failed to create dir %s\n",TEST_DIR);
+ failed_already=1;
+ return -1;
+ }
+
+ small_file_fd = PR_Open(SMALL_FILE_NAME, PR_RDWR | PR_CREATE_FILE,0777);
+
+ if (small_file_fd == NULL) {
+ fprintf(stderr,"prsocket_test failed to create/open file %s\n",
+ SMALL_FILE_NAME);
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ buf = PR_NEW(file_buf);
+ if (buf == NULL) {
+ fprintf(stderr,"prsocket_test failed to allocate buffer\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ /*
+ * fill in random data
+ */
+ for (i = 0; i < TRANSMITFILE_BUF_SIZE; i++) {
+ buf->data[i] = i;
+ }
+ count = 0;
+ do {
+ len = (SMALL_FILE_SIZE - count) > TRANSMITFILE_BUF_SIZE ?
+ TRANSMITFILE_BUF_SIZE : (SMALL_FILE_SIZE - count);
+ bytes = PR_Write(small_file_fd, buf->data, len);
+ if (bytes <= 0) {
+ fprintf(stderr,
+ "prsocket_test failed to write to file %s\n",
+ SMALL_FILE_NAME);
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ count += bytes;
+ } while (count < SMALL_FILE_SIZE);
+#ifdef XP_UNIX
+ /*
+ * map the small file; used in checking for data corruption
+ */
+ small_file_addr = mmap(0, SMALL_FILE_SIZE, PROT_READ,
+ MAP_SHARED, small_file_fd->secret->md.osfd, 0);
+ if (small_file_addr == (void *) -1) {
+ fprintf(stderr,"prsocket_test failed to mmap file %s\n",
+ SMALL_FILE_NAME);
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+#endif
+ /*
+ * header for small file
+ */
+ small_file_header = PR_MALLOC(SMALL_FILE_HEADER_SIZE);
+ if (small_file_header == NULL) {
+ fprintf(stderr,"prsocket_test failed to malloc header file\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ memset(small_file_header, (int) PR_IntervalNow(),
+ SMALL_FILE_HEADER_SIZE);
+ /*
+ * trailer for small file
+ */
+ small_file_trailer = PR_MALLOC(SMALL_FILE_TRAILER_SIZE);
+ if (small_file_trailer == NULL) {
+ fprintf(stderr,"prsocket_test failed to malloc header trailer\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ memset(small_file_trailer, (int) PR_IntervalNow(),
+ SMALL_FILE_TRAILER_SIZE);
+ /*
+ * setup large file
+ */
+ large_file_fd = PR_Open(LARGE_FILE_NAME, PR_RDWR | PR_CREATE_FILE,0777);
+
+ if (large_file_fd == NULL) {
+ fprintf(stderr,"prsocket_test failed to create/open file %s\n",
+ LARGE_FILE_NAME);
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ /*
+ * fill in random data
+ */
+ for (i = 0; i < TRANSMITFILE_BUF_SIZE; i++) {
+ buf->data[i] = i;
+ }
+ count = 0;
+ do {
+ len = (LARGE_FILE_SIZE - count) > TRANSMITFILE_BUF_SIZE ?
+ TRANSMITFILE_BUF_SIZE : (LARGE_FILE_SIZE - count);
+ bytes = PR_Write(large_file_fd, buf->data, len);
+ if (bytes <= 0) {
+ fprintf(stderr,
+ "prsocket_test failed to write to file %s: (%ld, %ld)\n",
+ LARGE_FILE_NAME,
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ count += bytes;
+ } while (count < LARGE_FILE_SIZE);
+#if defined(XP_UNIX)
+ /*
+ * map the large file; used in checking for data corruption
+ */
+ large_file_addr = mmap(0, LARGE_FILE_SIZE, PROT_READ,
+ MAP_SHARED, large_file_fd->secret->md.osfd, 0);
+ if (large_file_addr == (void *) -1) {
+ fprintf(stderr,"prsocket_test failed to mmap file %s\n",
+ LARGE_FILE_NAME);
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+#endif
+ /*
+ * header for large file
+ */
+ large_file_header = PR_MALLOC(LARGE_FILE_HEADER_SIZE);
+ if (large_file_header == NULL) {
+ fprintf(stderr,"prsocket_test failed to malloc header file\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ memset(large_file_header, (int) PR_IntervalNow(),
+ LARGE_FILE_HEADER_SIZE);
+ /*
+ * trailer for large file
+ */
+ large_file_trailer = PR_MALLOC(LARGE_FILE_TRAILER_SIZE);
+ if (large_file_trailer == NULL) {
+ fprintf(stderr,"prsocket_test failed to malloc header trailer\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ memset(large_file_trailer, (int) PR_IntervalNow(),
+ LARGE_FILE_TRAILER_SIZE);
+
+ datalen = tcp_mesg_size;
+ thread_count = 0;
+ /*
+ * start the server thread
+ */
+ sparamp = PR_NEW(Server_Param);
+ if (sparamp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ server_sem = PR_NewSem(0);
+ if (server_sem == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewSem failed\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ mon2 = PR_NewMonitor();
+ if (mon2 == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewMonitor failed\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ PR_EnterMonitor(mon2);
+
+ sparamp->addr_sem = server_sem;
+ sparamp->exit_mon = mon2;
+ sparamp->exit_counter = &thread_count;
+ sparamp->datalen = datalen;
+ t = PR_CreateThread(PR_USER_THREAD,
+ TransmitFile_Server, (void *)sparamp,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ DPRINTF(("Created TCP server = 0x%x\n", t));
+ thread_count++;
+
+ /*
+ * wait till the server address is setup
+ */
+ PR_WaitSem(server_sem);
+
+ /*
+ * Now start a bunch of client threads
+ */
+
+ cparamp = PR_NEW(Client_Param);
+ if (cparamp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ cparamp->server_addr = tcp_server_addr;
+ cparamp->server_addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+ cparamp->exit_mon = mon2;
+ cparamp->exit_counter = &thread_count;
+ cparamp->datalen = datalen;
+ for (i = 0; i < num_transmitfile_clients; i++) {
+ t = create_new_thread(PR_USER_THREAD,
+ TransmitFile_Client, (void *) cparamp,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0, i);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ rv = -1;
+ failed_already=1;
+ goto done;
+ }
+ DPRINTF(("Created TransmitFile client = 0x%lx\n", t));
+ thread_count++;
+ }
+ /* Wait for server and client threads to exit */
+ while (thread_count) {
+ PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("Socket_Misc_Test - thread_count = %d\n", thread_count));
+ }
+ PR_ExitMonitor(mon2);
+done:
+ if (buf) {
+ PR_DELETE(buf);
+ }
+#if defined(XP_UNIX)
+ munmap((char*)small_file_addr, SMALL_FILE_SIZE);
+ munmap((char*)large_file_addr, LARGE_FILE_SIZE);
+#endif
+ PR_Close(small_file_fd);
+ PR_Close(large_file_fd);
+ if ((PR_Delete(SMALL_FILE_NAME)) == PR_FAILURE) {
+ fprintf(stderr,"prsocket_test: failed to unlink file %s\n",
+ SMALL_FILE_NAME);
+ failed_already=1;
+ }
+ if ((PR_Delete(LARGE_FILE_NAME)) == PR_FAILURE) {
+ fprintf(stderr,"prsocket_test: failed to unlink file %s\n",
+ LARGE_FILE_NAME);
+ failed_already=1;
+ }
+ if ((PR_RmDir(TEST_DIR)) == PR_FAILURE) {
+ fprintf(stderr,"prsocket_test failed to rmdir %s: (%ld, %ld)\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+
+ printf("%-29s%s","Socket_Misc_Test",":");
+ printf("%2d Server %2d Clients\n",1, num_transmitfile_clients);
+ printf("%30s Sizes of Transmitted Files - %4d KB, %2d MB \n",":",
+ SMALL_FILE_SIZE/1024, LARGE_FILE_SIZE/(1024 * 1024));
+
+
+ return rv;
+}
+/************************************************************************/
+
+/*
+ * Test Socket NSPR APIs
+ */
+
+int main(int argc, char **argv)
+{
+ /*
+ * -d debug mode
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ PR_SetConcurrency(4);
+
+ emuSendFileIdentity = PR_GetUniqueIdentity("Emulated SendFile");
+ emuSendFileMethods = *PR_GetDefaultIOMethods();
+ emuSendFileMethods.transmitfile = emu_TransmitFile;
+ emuSendFileMethods.sendfile = emu_SendFile;
+
+ /*
+ * run client-server test with TCP, Ipv4-Ipv4
+ */
+ printf("TCP Client/Server Test - IPv4/Ipv4\n");
+ if (TCP_Socket_Client_Server_Test() < 0) {
+ printf("TCP_Socket_Client_Server_Test failed\n");
+ goto done;
+ } else {
+ printf("TCP_Socket_Client_Server_Test Passed\n");
+ }
+ /*
+ * client-server test, Ipv6-Ipv4
+ */
+ client_domain = PR_AF_INET6;
+ printf("TCP Client/Server Test - IPv6/Ipv4\n");
+ if (TCP_Socket_Client_Server_Test() < 0) {
+ printf("TCP_Socket_Client_Server_Test failed\n");
+ goto done;
+ } else {
+ printf("TCP_Socket_Client_Server_Test Passed\n");
+ }
+ /*
+ * client-server test, Ipv4-Ipv6
+ */
+ client_domain = PR_AF_INET;
+ server_domain = PR_AF_INET6;
+ printf("TCP Client/Server Test - IPv4/Ipv6\n");
+ if (TCP_Socket_Client_Server_Test() < 0) {
+ printf("TCP_Socket_Client_Server_Test failed\n");
+ goto done;
+ } else {
+ printf("TCP_Socket_Client_Server_Test Passed\n");
+ }
+ /*
+ * client-server test, Ipv6-Ipv6
+ */
+ client_domain = PR_AF_INET6;
+ server_domain = PR_AF_INET6;
+ printf("TCP Client/Server Test - IPv6/Ipv6\n");
+ if (TCP_Socket_Client_Server_Test() < 0) {
+ printf("TCP_Socket_Client_Server_Test failed\n");
+ goto done;
+ } else {
+ printf("TCP_Socket_Client_Server_Test Passed\n");
+ }
+ test_cancelio = 0;
+
+ /*
+ * Misc socket tests - including transmitfile, etc.
+ */
+
+ /* File transmission test can not be done in Symbian OS because of
+ * large file's size and the incomplete mmap() implementation. */
+#if !defined(WIN16)
+ /*
+ ** The 'transmit file' test does not run because
+ ** transmit file is not implemented in NSPR yet.
+ **
+ */
+ if (Socket_Misc_Test() < 0) {
+ printf("Socket_Misc_Test failed\n");
+ failed_already=1;
+ goto done;
+ } else {
+ printf("Socket_Misc_Test passed\n");
+ }
+
+ /*
+ * run client-server test with TCP again to test
+ * recycling used sockets from PR_TransmitFile().
+ */
+ if (TCP_Socket_Client_Server_Test() < 0) {
+ printf("TCP_Socket_Client_Server_Test failed\n");
+ goto done;
+ } else {
+ printf("TCP_Socket_Client_Server_Test Passed\n");
+ }
+#endif
+
+done:
+ PR_Cleanup();
+ if (failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
diff --git a/nsprpub/pr/tests/sockopt.c b/nsprpub/pr/tests/sockopt.c
new file mode 100644
index 0000000000..d1a04451ab
--- /dev/null
+++ b/nsprpub/pr/tests/sockopt.c
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+#include "prio.h"
+#include "prinit.h"
+#include "prprf.h"
+#include "obsolete/probslet.h"
+
+#include "plerror.h"
+
+#ifdef XP_UNIX
+#include <sys/socket.h> /* SO_REUSEPORT */
+#endif
+
+static PRFileDesc *err = NULL;
+static PRBool failed = PR_FALSE;
+
+static void Failed(const char *msg1, const char *msg2)
+{
+ if (NULL != msg1) {
+ PR_fprintf(err, "%s ", msg1);
+ }
+ PL_FPrintError(err, msg2);
+ failed = PR_TRUE;
+} /* Failed */
+
+static PRSockOption Incr(PRSockOption *option)
+{
+ PRIntn val = ((PRIntn)*option) + 1;
+ *option = (PRSockOption)val;
+ return (PRSockOption)val;
+} /* Incr */
+
+int main(int argc, char **argv)
+{
+ PRStatus rv;
+ PRFileDesc *udp = PR_NewUDPSocket();
+ PRFileDesc *tcp = PR_NewTCPSocket();
+ const char *tag[] =
+ {
+ "PR_SockOpt_Nonblocking", /* nonblocking io */
+ "PR_SockOpt_Linger", /* linger on close if data present */
+ "PR_SockOpt_Reuseaddr", /* allow local address reuse */
+ "PR_SockOpt_Keepalive", /* keep connections alive */
+ "PR_SockOpt_RecvBufferSize", /* receive buffer size */
+ "PR_SockOpt_SendBufferSize", /* send buffer size */
+
+ "PR_SockOpt_IpTimeToLive", /* time to live */
+ "PR_SockOpt_IpTypeOfService", /* type of service and precedence */
+
+ "PR_SockOpt_AddMember", /* add an IP group membership */
+ "PR_SockOpt_DropMember", /* drop an IP group membership */
+ "PR_SockOpt_McastInterface", /* multicast interface address */
+ "PR_SockOpt_McastTimeToLive", /* multicast timetolive */
+ "PR_SockOpt_McastLoopback", /* multicast loopback */
+
+ "PR_SockOpt_NoDelay", /* don't delay send to coalesce packets */
+ "PR_SockOpt_MaxSegment", /* maximum segment size */
+ "PR_SockOpt_Broadcast", /* Enable broadcast */
+ "PR_SockOpt_Reuseport", /* allow local address & port reuse */
+ "PR_SockOpt_DontFrag", /* do not fragment */
+ "PR_SockOpt_Last"
+ };
+
+ err = PR_GetSpecialFD(PR_StandardError);
+ PR_STDIO_INIT();
+
+ if (NULL == udp) {
+ Failed("PR_NewUDPSocket()", NULL);
+ }
+ else if (NULL == tcp) {
+ Failed("PR_NewTCPSocket()", NULL);
+ }
+ else
+ {
+ PRSockOption option;
+ PRUint32 segment = 1024;
+ PRNetAddr addr;
+
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr);
+ if (PR_FAILURE == rv) {
+ Failed("PR_InitializeNetAddr()", NULL);
+ }
+ rv = PR_Bind(udp, &addr);
+ if (PR_FAILURE == rv) {
+ Failed("PR_Bind()", NULL);
+ }
+ for(option = PR_SockOpt_Linger; option < PR_SockOpt_Last; Incr(&option))
+ {
+ PRSocketOptionData data;
+ PRFileDesc *fd = tcp;
+ data.option = option;
+ switch (option)
+ {
+ case PR_SockOpt_Nonblocking:
+ data.value.non_blocking = PR_TRUE;
+ break;
+ case PR_SockOpt_Linger:
+ data.value.linger.polarity = PR_TRUE;
+ data.value.linger.linger = PR_SecondsToInterval(2);
+ break;
+ case PR_SockOpt_Reuseaddr:
+ data.value.reuse_addr = PR_TRUE;
+ break;
+ case PR_SockOpt_Keepalive:
+ data.value.keep_alive = PR_TRUE;
+ break;
+ case PR_SockOpt_RecvBufferSize:
+ data.value.recv_buffer_size = segment;
+ break;
+ case PR_SockOpt_SendBufferSize:
+ data.value.send_buffer_size = segment;
+ break;
+ case PR_SockOpt_IpTimeToLive:
+ data.value.ip_ttl = 64;
+ break;
+ case PR_SockOpt_IpTypeOfService:
+ data.value.tos = 0;
+ break;
+ case PR_SockOpt_McastTimeToLive:
+ fd = udp;
+ data.value.mcast_ttl = 4;
+ break;
+ case PR_SockOpt_McastLoopback:
+ fd = udp;
+ data.value.mcast_loopback = PR_TRUE;
+ break;
+ case PR_SockOpt_NoDelay:
+ data.value.no_delay = PR_TRUE;
+ break;
+#ifndef WIN32
+ case PR_SockOpt_MaxSegment:
+ data.value.max_segment = segment;
+ break;
+#endif
+ case PR_SockOpt_Broadcast:
+ fd = udp;
+ data.value.broadcast = PR_TRUE;
+ break;
+#ifdef SO_REUSEPORT
+ case PR_SockOpt_Reuseport:
+ data.value.reuse_port = PR_TRUE;
+ break;
+#endif
+ case PR_SockOpt_DontFrag:
+ data.value.dont_fragment = PR_TRUE;
+ break;
+
+ default: continue;
+ }
+
+ /*
+ * TCP_MAXSEG can only be read, not set
+ */
+ if (option != PR_SockOpt_MaxSegment) {
+#ifdef WIN32
+ if (option != PR_SockOpt_McastLoopback)
+#endif
+ {
+ rv = PR_SetSocketOption(fd, &data);
+ if (PR_FAILURE == rv) {
+ Failed("PR_SetSocketOption()", tag[option]);
+ }
+ }
+ }
+
+ rv = PR_GetSocketOption(fd, &data);
+ if (PR_FAILURE == rv) {
+ Failed("PR_GetSocketOption()", tag[option]);
+ }
+ }
+ PR_Close(udp);
+ PR_Close(tcp);
+ }
+ PR_fprintf(err, "%s\n", (failed) ? "FAILED" : "PASSED");
+ return (failed) ? 1 : 0;
+} /* main */
+
+/* sockopt.c */
+
diff --git a/nsprpub/pr/tests/sockping.c b/nsprpub/pr/tests/sockping.c
new file mode 100644
index 0000000000..327d1d8e1a
--- /dev/null
+++ b/nsprpub/pr/tests/sockping.c
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: sockping.c
+ *
+ * Description:
+ * This test runs in conjunction with the sockpong test.
+ * This test creates a socket pair and passes one socket
+ * to the sockpong test. Then this test writes "ping" to
+ * to the sockpong test and the sockpong test writes "pong"
+ * back. To run this pair of tests, just invoke sockping.
+ *
+ * Tested areas: process creation, socket pairs, file
+ * descriptor inheritance.
+ */
+
+#include "prerror.h"
+#include "prio.h"
+#include "prproces.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define NUM_ITERATIONS 10
+
+static char *child_argv[] = { "sockpong", NULL };
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *sock[2];
+ PRStatus status;
+ PRProcess *process;
+ PRProcessAttr *attr;
+ char buf[1024];
+ PRInt32 nBytes;
+ PRInt32 exitCode;
+ int idx;
+
+ status = PR_NewTCPSocketPair(sock);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_NewTCPSocketPair failed\n");
+ exit(1);
+ }
+
+ status = PR_SetFDInheritable(sock[0], PR_FALSE);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_SetFDInheritable failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ status = PR_SetFDInheritable(sock[1], PR_TRUE);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_SetFDInheritable failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+
+ attr = PR_NewProcessAttr();
+ if (attr == NULL) {
+ fprintf(stderr, "PR_NewProcessAttr failed\n");
+ exit(1);
+ }
+
+ status = PR_ProcessAttrSetInheritableFD(attr, sock[1], "SOCKET");
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_ProcessAttrSetInheritableFD failed\n");
+ exit(1);
+ }
+
+ process = PR_CreateProcess(child_argv[0], child_argv, NULL, attr);
+ if (process == NULL) {
+ fprintf(stderr, "PR_CreateProcess failed\n");
+ exit(1);
+ }
+ PR_DestroyProcessAttr(attr);
+ status = PR_Close(sock[1]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+
+ for (idx = 0; idx < NUM_ITERATIONS; idx++) {
+ strcpy(buf, "ping");
+ printf("ping process: sending \"%s\"\n", buf);
+ nBytes = PR_Write(sock[0], buf, 5);
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Write failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ memset(buf, 0, sizeof(buf));
+ nBytes = PR_Read(sock[0], buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ printf("ping process: received \"%s\"\n", buf);
+ if (nBytes != 5) {
+ fprintf(stderr, "ping process: expected 5 bytes but got %d bytes\n",
+ nBytes);
+ exit(1);
+ }
+ if (strcmp(buf, "pong") != 0) {
+ fprintf(stderr, "ping process: expected \"pong\" but got \"%s\"\n",
+ buf);
+ exit(1);
+ }
+ }
+
+ status = PR_Close(sock[0]);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ status = PR_WaitProcess(process, &exitCode);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_WaitProcess failed\n");
+ exit(1);
+ }
+ if (exitCode == 0) {
+ printf("PASS\n");
+ return 0;
+ } else {
+ printf("FAIL\n");
+ return 1;
+ }
+}
diff --git a/nsprpub/pr/tests/sockpong.c b/nsprpub/pr/tests/sockpong.c
new file mode 100644
index 0000000000..31cfd3ee8c
--- /dev/null
+++ b/nsprpub/pr/tests/sockpong.c
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: sockpong.c
+ *
+ * Description:
+ * This test runs in conjunction with the sockping test.
+ * The sockping test creates a socket pair and passes one
+ * socket to this test. Then the sockping test writes
+ * "ping" to this test and this test writes "pong" back.
+ * To run this pair of tests, just invoke sockping.
+ *
+ * Tested areas: process creation, socket pairs, file
+ * descriptor inheritance.
+ */
+
+#include "prerror.h"
+#include "prio.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define NUM_ITERATIONS 10
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *sock;
+ PRStatus status;
+ char buf[1024];
+ PRInt32 nBytes;
+ int idx;
+
+ sock = PR_GetInheritedFD("SOCKET");
+ if (sock == NULL) {
+ fprintf(stderr, "PR_GetInheritedFD failed\n");
+ exit(1);
+ }
+ status = PR_SetFDInheritable(sock, PR_FALSE);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_SetFDInheritable failed\n");
+ exit(1);
+ }
+
+ for (idx = 0; idx < NUM_ITERATIONS; idx++) {
+ memset(buf, 0, sizeof(buf));
+ nBytes = PR_Read(sock, buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ printf("pong process: received \"%s\"\n", buf);
+ if (nBytes != 5) {
+ fprintf(stderr, "pong process: expected 5 bytes but got %d bytes\n",
+ nBytes);
+ exit(1);
+ }
+ if (strcmp(buf, "ping") != 0) {
+ fprintf(stderr, "pong process: expected \"ping\" but got \"%s\"\n",
+ buf);
+ exit(1);
+ }
+
+ strcpy(buf, "pong");
+ printf("pong process: sending \"%s\"\n", buf);
+ nBytes = PR_Write(sock, buf, 5);
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Write failed\n");
+ exit(1);
+ }
+ }
+
+ status = PR_Close(sock);
+ if (status == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ return 0;
+}
diff --git a/nsprpub/pr/tests/sprintf.c b/nsprpub/pr/tests/sprintf.c
new file mode 100644
index 0000000000..51db026d1c
--- /dev/null
+++ b/nsprpub/pr/tests/sprintf.c
@@ -0,0 +1,459 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: sprintf.c
+ * Description:
+ * This is a test program for the PR_snprintf() functions defined
+ * in prprf.c. This test program is based on ns/nspr/tests/sprintf.c,
+ * revision 1.10.
+ * Modification History:
+ * 20-May-1997 AGarcia replaced printf statment to return PASS\n. This is to be used by the
+ * regress tool parsing routine.
+ ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+ * recognize the return code from tha main program.
+ */
+
+#include "prinit.h"
+#include "prprf.h"
+#include "prlog.h"
+#include "prlong.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static char sbuf[20000];
+
+
+/*
+** Perform a three way test against PR_smprintf, PR_snprintf, and sprintf.
+** Make sure the results are identical
+*/
+static void test_i(char *pattern, int i)
+{
+ char *s;
+ char buf[200];
+ int n;
+
+ /* try all three routines */
+ s = PR_smprintf(pattern, i);
+ PR_ASSERT(s != 0);
+ n = PR_snprintf(buf, sizeof(buf), pattern, i);
+ PR_ASSERT(n <= sizeof(buf));
+ sprintf(sbuf, pattern, i);
+
+ /* compare results */
+ if ((strncmp(s, buf, sizeof(buf)) != 0) ||
+ (strncmp(s, sbuf, sizeof(sbuf)) != 0)) {
+ fprintf(stderr,
+ "pattern='%s' i=%d\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n",
+ pattern, i, s, buf, sbuf);
+ PR_smprintf_free(s);
+ exit(-1);
+ }
+ PR_smprintf_free(s);
+}
+
+static void TestI(void)
+{
+ static int nums[] = {
+ 0, 1, -1, 10, -10,
+ 32767, -32768,
+ };
+ static char *signs[] = {
+ "",
+ "0", "-", "+", " ",
+ "0-", "0+", "0 ", "-0", "-+", "- ",
+ "+0", "+-", "+ ", " 0", " -", " +",
+ "0-+", "0- ", "0+-", "0+ ", "0 -", "0 +",
+ "-0+", "-0 ", "-+0", "-+ ", "- 0", "- +",
+ "+0-", "+0 ", "+-0", "+- ", "+ 0", "+ -",
+ " 0-", " 0+", " -0", " -+", " +0", " +-",
+ "0-+ ", "0- +", "0+- ", "0+ -", "0 -+", "0 +-",
+ "-0+ ", "-0 +", "-+0 ", "-+ 0", "- 0+", "- +0",
+ "+0- ", "+0 -", "+-0 ", "+- 0", "+ 0-", "+ -0",
+ " 0-+", " 0+-", " -0+", " -+0", " +0-", " +-0",
+ };
+ static char *precs[] = {
+ "", "3", "5", "43",
+ "7.3", "7.5", "7.11", "7.43",
+ };
+ static char *formats[] = {
+ "d", "o", "x", "u",
+ "hd", "ho", "hx", "hu"
+ };
+ int f, s, n, p;
+ char fmt[20];
+
+ for (f = 0; f < PR_ARRAY_SIZE(formats); f++) {
+ for (s = 0; s < PR_ARRAY_SIZE(signs); s++) {
+ for (p = 0; p < PR_ARRAY_SIZE(precs); p++) {
+ fmt[0] = '%';
+ fmt[1] = 0;
+ if (signs[s]) {
+ strcat(fmt, signs[s]);
+ }
+ if (precs[p]) {
+ strcat(fmt, precs[p]);
+ }
+ if (formats[f]) {
+ strcat(fmt, formats[f]);
+ }
+ for (n = 0; n < PR_ARRAY_SIZE(nums); n++) {
+ test_i(fmt, nums[n]);
+ }
+ }
+ }
+ }
+}
+
+/************************************************************************/
+
+/*
+** Perform a three way test against PR_smprintf, PR_snprintf, and sprintf.
+** Make sure the results are identical
+*/
+static void test_l(char *pattern, char *spattern, PRInt32 l)
+{
+ char *s;
+ char buf[200];
+ int n;
+
+ /* try all three routines */
+ s = PR_smprintf(pattern, l);
+ PR_ASSERT(s != 0);
+ n = PR_snprintf(buf, sizeof(buf), pattern, l);
+ PR_ASSERT(n <= sizeof(buf));
+ sprintf(sbuf, spattern, l);
+
+ /* compare results */
+ if ((strncmp(s, buf, sizeof(buf)) != 0) ||
+ (strncmp(s, sbuf, sizeof(sbuf)) != 0)) {
+ fprintf(stderr,
+ "pattern='%s' l=%ld\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n",
+ pattern, l, s, buf, sbuf);
+ PR_smprintf_free(s);
+ exit(-1);
+ }
+ PR_smprintf_free(s);
+}
+
+static void TestL(void)
+{
+ static PRInt32 nums[] = {
+ 0,
+ 1,
+ -1,
+ 10,
+ -10,
+ 32767,
+ -32768,
+ PR_INT32(0x7fffffff), /* 2147483647L */
+ -1 - PR_INT32(0x7fffffff) /* -2147483648L */
+ };
+ static char *signs[] = {
+ "",
+ "0", "-", "+", " ",
+ "0-", "0+", "0 ", "-0", "-+", "- ",
+ "+0", "+-", "+ ", " 0", " -", " +",
+ "0-+", "0- ", "0+-", "0+ ", "0 -", "0 +",
+ "-0+", "-0 ", "-+0", "-+ ", "- 0", "- +",
+ "+0-", "+0 ", "+-0", "+- ", "+ 0", "+ -",
+ " 0-", " 0+", " -0", " -+", " +0", " +-",
+ "0-+ ", "0- +", "0+- ", "0+ -", "0 -+", "0 +-",
+ "-0+ ", "-0 +", "-+0 ", "-+ 0", "- 0+", "- +0",
+ "+0- ", "+0 -", "+-0 ", "+- 0", "+ 0-", "+ -0",
+ " 0-+", " 0+-", " -0+", " -+0", " +0-", " +-0",
+ };
+ static char *precs[] = {
+ "", "3", "5", "43",
+ ".3", ".43",
+ "7.3", "7.5", "7.11", "7.43",
+ };
+ static char *formats[] = { "ld", "lo", "lx", "lu" };
+
+#if PR_BYTES_PER_INT == 4
+ static char *sformats[] = { "d", "o", "x", "u" };
+#elif PR_BYTES_PER_LONG == 4
+ static char *sformats[] = { "ld", "lo", "lx", "lu" };
+#else
+#error Neither int nor long is 4 bytes on this platform
+#endif
+
+ int f, s, n, p;
+ char fmt[40], sfmt[40];
+
+ for (f = 0; f < PR_ARRAY_SIZE(formats); f++) {
+ for (s = 0; s < PR_ARRAY_SIZE(signs); s++) {
+ for (p = 0; p < PR_ARRAY_SIZE(precs); p++) {
+ fmt[0] = '%';
+ fmt[1] = 0;
+ if (signs[s]) {
+ strcat(fmt, signs[s]);
+ }
+ if (precs[p]) {
+ strcat(fmt, precs[p]);
+ }
+ strcpy(sfmt, fmt);
+ if (formats[f]) {
+ strcat(fmt, formats[f]);
+ }
+ if (sformats[f]) {
+ strcat(sfmt, sformats[f]);
+ }
+ for (n = 0; n < PR_ARRAY_SIZE(nums); n++) {
+ test_l(fmt, sfmt, nums[n]);
+ }
+ }
+ }
+ }
+}
+
+/************************************************************************/
+
+/*
+** Perform a three way test against PR_smprintf, PR_snprintf, and sprintf.
+** Make sure the results are identical
+*/
+static void test_ll(char *pattern, char *spattern, PRInt64 l)
+{
+ char *s;
+ char buf[200];
+ int n;
+
+ /* try all three routines */
+ s = PR_smprintf(pattern, l);
+ PR_ASSERT(s != 0);
+ n = PR_snprintf(buf, sizeof(buf), pattern, l);
+ PR_ASSERT(n <= sizeof(buf));
+#if defined(HAVE_LONG_LONG)
+ sprintf(sbuf, spattern, l);
+
+ /* compare results */
+ if ((strncmp(s, buf, sizeof(buf)) != 0) ||
+ (strncmp(s, sbuf, sizeof(sbuf)) != 0)) {
+#if PR_BYTES_PER_LONG == 8
+#define FORMAT_SPEC "%ld"
+#elif defined(WIN16)
+#define FORMAT_SPEC "%Ld"
+#elif defined(WIN32)
+#define FORMAT_SPEC "%I64d"
+#else
+#define FORMAT_SPEC "%lld"
+#endif
+ fprintf(stderr,
+ "pattern='%s' ll=" FORMAT_SPEC "\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n",
+ pattern, l, s, buf, sbuf);
+ printf("FAIL\n");
+ PR_smprintf_free(s);
+ exit(-1);
+ }
+ PR_smprintf_free(s);
+#else
+ /* compare results */
+ if ((strncmp(s, buf, sizeof(buf)) != 0)) {
+ fprintf(stderr,
+ "pattern='%s'\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n",
+ pattern, s, buf, sbuf);
+ printf("FAIL\n");
+ PR_smprintf_free(s);
+ exit(-1);
+ }
+ PR_smprintf_free(s);
+#endif
+}
+
+static void TestLL(void)
+{
+ static PRInt64 nums[] = {
+ LL_INIT(0, 0),
+ LL_INIT(0, 1),
+ LL_INIT(0xffffffff, 0xffffffff), /* -1 */
+ LL_INIT(0, 10),
+ LL_INIT(0xffffffff, 0xfffffff6), /* -10 */
+ LL_INIT(0, 32767),
+ LL_INIT(0xffffffff, 0xffff8000), /* -32768 */
+ LL_INIT(0, 0x7fffffff), /* 2147483647 */
+ LL_INIT(0xffffffff, 0x80000000), /* -2147483648 */
+ LL_INIT(0x7fffffff, 0xffffffff), /* 9223372036854775807 */
+ LL_INIT(0x80000000, 0), /* -9223372036854775808 */
+ PR_INT64(0),
+ PR_INT64(1),
+ PR_INT64(-1),
+ PR_INT64(10),
+ PR_INT64(-10),
+ PR_INT64(32767),
+ PR_INT64(-32768),
+ PR_INT64(2147483647),
+ PR_INT64(-2147483648),
+ PR_INT64(9223372036854775807),
+ PR_INT64(-9223372036854775808)
+ };
+
+ static char *signs[] = {
+ "",
+ "0", "-", "+", " ",
+ "0-", "0+", "0 ", "-0", "-+", "- ",
+ "+0", "+-", "+ ", " 0", " -", " +",
+ "0-+", "0- ", "0+-", "0+ ", "0 -", "0 +",
+ "-0+", "-0 ", "-+0", "-+ ", "- 0", "- +",
+ "+0-", "+0 ", "+-0", "+- ", "+ 0", "+ -",
+ " 0-", " 0+", " -0", " -+", " +0", " +-",
+ "0-+ ", "0- +", "0+- ", "0+ -", "0 -+", "0 +-",
+ "-0+ ", "-0 +", "-+0 ", "-+ 0", "- 0+", "- +0",
+ "+0- ", "+0 -", "+-0 ", "+- 0", "+ 0-", "+ -0",
+ " 0-+", " 0+-", " -0+", " -+0", " +0-", " +-0",
+ };
+ static char *precs[] = {
+ "", "3", "5", "43",
+ ".3", ".43",
+ "7.3", "7.5", "7.11", "7.43",
+ };
+ static char *formats[] = { "lld", "llo", "llx", "llu" };
+
+#if PR_BYTES_PER_LONG == 8
+ static char *sformats[] = { "ld", "lo", "lx", "lu" };
+#elif defined(WIN16)
+ /* Watcom uses the format string "%Ld" instead of "%lld". */
+ static char *sformats[] = { "Ld", "Lo", "Lx", "Lu" };
+#elif defined(WIN32)
+ static char *sformats[] = { "I64d", "I64o", "I64x", "I64u" };
+#else
+ static char *sformats[] = { "lld", "llo", "llx", "llu" };
+#endif
+
+ int f, s, n, p;
+ char fmt[40], sfmt[40];
+
+ for (f = 0; f < PR_ARRAY_SIZE(formats); f++) {
+ for (s = 0; s < PR_ARRAY_SIZE(signs); s++) {
+ for (p = 0; p < PR_ARRAY_SIZE(precs); p++) {
+ fmt[0] = '%';
+ fmt[1] = 0;
+ if (signs[s]) {
+ strcat(fmt, signs[s]);
+ }
+ if (precs[p]) {
+ strcat(fmt, precs[p]);
+ }
+ strcpy(sfmt, fmt);
+ if (formats[f]) {
+ strcat(fmt, formats[f]);
+ }
+ if (sformats[f]) {
+ strcat(sfmt, sformats[f]);
+ }
+ for (n = 0; n < PR_ARRAY_SIZE(nums); n++) {
+ test_ll(fmt, sfmt, nums[n]);
+ }
+ }
+ }
+ }
+}
+
+/************************************************************************/
+
+/*
+** Perform a three way test against PR_smprintf, PR_snprintf, and sprintf.
+** Make sure the results are identical
+*/
+static void test_s(char *pattern, char *ss)
+{
+ char *s;
+ unsigned char before[8];
+ char buf[200];
+ unsigned char after[8];
+ int n;
+
+ memset(before, 0xBB, 8);
+ memset(after, 0xAA, 8);
+
+ /* try all three routines */
+ s = PR_smprintf(pattern, ss);
+ PR_ASSERT(s != 0);
+ n = PR_snprintf(buf, sizeof(buf), pattern, ss);
+ PR_ASSERT(n <= sizeof(buf));
+ sprintf(sbuf, pattern, ss);
+
+ for (n = 0; n < 8; n++) {
+ PR_ASSERT(before[n] == 0xBB);
+ PR_ASSERT(after[n] == 0xAA);
+ }
+
+ /* compare results */
+ if ((strncmp(s, buf, sizeof(buf)) != 0) ||
+ (strncmp(s, sbuf, sizeof(sbuf)) != 0)) {
+ fprintf(stderr,
+ "pattern='%s' ss=%.20s\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n",
+ pattern, ss, s, buf, sbuf);
+ printf("FAIL\n");
+ PR_smprintf_free(s);
+ exit(-1);
+ }
+ PR_smprintf_free(s);
+}
+
+static void TestS(void)
+{
+ static char *strs[] = {
+ "",
+ "a",
+ "abc",
+ "abcde",
+ "abcdefABCDEF",
+ "abcdefghijklmnopqrstuvwxyz0123456789!@#$"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$"
+ "abcdefghijklmnopqrstuvwxyz0123456789!@#$",
+ };
+ /* '0' is not relevant to printing strings */
+ static char *signs[] = {
+ "",
+ "-", "+", " ",
+ "-+", "- ", "+-", "+ ", " -", " +",
+ "-+ ", "- +", "+- ", "+ -", " -+", " +-",
+ };
+ static char *precs[] = {
+ "", "3", "5", "43",
+ ".3", ".43",
+ "7.3", "7.5", "7.11", "7.43",
+ };
+ static char *formats[] = { "s" };
+ int f, s, n, p;
+ char fmt[40];
+
+ for (f = 0; f < PR_ARRAY_SIZE(formats); f++) {
+ for (s = 0; s < PR_ARRAY_SIZE(signs); s++) {
+ for (p = 0; p < PR_ARRAY_SIZE(precs); p++) {
+ fmt[0] = '%';
+ fmt[1] = 0;
+ if (signs[s]) {
+ strcat(fmt+strlen(fmt), signs[s]);
+ }
+ if (precs[p]) {
+ strcat(fmt+strlen(fmt), precs[p]);
+ }
+ if (formats[f]) {
+ strcat(fmt+strlen(fmt), formats[f]);
+ }
+ for (n = 0; n < PR_ARRAY_SIZE(strs); n++) {
+ test_s(fmt, strs[n]);
+ }
+ }
+ }
+ }
+}
+
+/************************************************************************/
+
+int main(int argc, char **argv)
+{
+ PR_STDIO_INIT();
+ TestI();
+ TestL();
+ TestLL();
+ TestS();
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/stack.c b/nsprpub/pr/tests/stack.c
new file mode 100644
index 0000000000..5526a27445
--- /dev/null
+++ b/nsprpub/pr/tests/stack.c
@@ -0,0 +1,282 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+/*
+ *
+ * Test atomic stack operations
+ *
+ * Two stacks are created and threads add data items (each containing
+ * one of the first n integers) to the first stack, remove data items
+ * from the first stack and add them to the second stack. The primordial
+ * thread compares the sum of the first n integers to the sum of the
+ * integers in the data items in the second stack. The test succeeds if
+ * they are equal.
+ */
+
+#include "nspr.h"
+#include "plgetopt.h"
+
+typedef struct _DataRecord {
+ PRInt32 data;
+ PRStackElem link;
+} DataRecord;
+
+#define RECORD_LINK_PTR(lp) ((DataRecord*) ((char*) (lp) - offsetof(DataRecord,link)))
+
+#define MAX_THREAD_CNT 100
+#define DEFAULT_THREAD_CNT 4
+#define DEFAULT_DATA_CNT 100
+#define DEFAULT_LOOP_CNT 10000
+
+/*
+ * sum of the first n numbers using the formula n*(n+1)/2
+ */
+#define SUM_OF_NUMBERS(n) ((n & 1) ? (((n + 1)/2) * n) : ((n/2) * (n+1)))
+
+typedef struct stack_data {
+ PRStack *list1;
+ PRStack *list2;
+ PRInt32 initial_data_value;
+ PRInt32 data_cnt;
+ PRInt32 loops;
+} stack_data;
+
+static void stackop(void *arg);
+
+static int _debug_on;
+
+PRFileDesc *output;
+PRFileDesc *errhandle;
+
+int main(int argc, char **argv)
+{
+ PRInt32 rv, cnt, sum;
+ DataRecord *Item;
+ PRStack *list1, *list2;
+ PRStackElem *node;
+ PRStatus rc;
+
+ PRInt32 thread_cnt = DEFAULT_THREAD_CNT;
+ PRInt32 data_cnt = DEFAULT_DATA_CNT;
+ PRInt32 loops = DEFAULT_LOOP_CNT;
+ PRThread **threads;
+ stack_data *thread_args;
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dt:c:l:");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ case 't': /* thread count */
+ thread_cnt = atoi(opt->value);
+ break;
+ case 'c': /* data count */
+ data_cnt = atoi(opt->value);
+ break;
+ case 'l': /* loop count */
+ loops = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_SetConcurrency(4);
+
+ output = PR_GetSpecialFD(PR_StandardOutput);
+ errhandle = PR_GetSpecialFD(PR_StandardError);
+ list1 = PR_CreateStack("Stack_1");
+ if (list1 == NULL) {
+ PR_fprintf(errhandle, "PR_CreateStack failed - error %d\n",
+ PR_GetError());
+ return 1;
+ }
+
+ list2 = PR_CreateStack("Stack_2");
+ if (list2 == NULL) {
+ PR_fprintf(errhandle, "PR_CreateStack failed - error %d\n",
+ PR_GetError());
+ return 1;
+ }
+
+
+ threads = (PRThread**) PR_CALLOC(sizeof(PRThread*) * thread_cnt);
+ thread_args = (stack_data *) PR_CALLOC(sizeof(stack_data) * thread_cnt);
+
+ if (_debug_on)
+ PR_fprintf(output,"%s: thread_cnt = %d data_cnt = %d\n", argv[0],
+ thread_cnt, data_cnt);
+ for(cnt = 0; cnt < thread_cnt; cnt++) {
+ PRThreadScope scope;
+
+ thread_args[cnt].list1 = list1;
+ thread_args[cnt].list2 = list2;
+ thread_args[cnt].loops = loops;
+ thread_args[cnt].data_cnt = data_cnt;
+ thread_args[cnt].initial_data_value = 1 + cnt * data_cnt;
+
+ if (cnt & 1) {
+ scope = PR_GLOBAL_THREAD;
+ }
+ else {
+ scope = PR_LOCAL_THREAD;
+ }
+
+
+ threads[cnt] = PR_CreateThread(PR_USER_THREAD,
+ stackop, &thread_args[cnt],
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (threads[cnt] == NULL) {
+ PR_fprintf(errhandle, "PR_CreateThread failed - error %d\n",
+ PR_GetError());
+ PR_ProcessExit(2);
+ }
+ if (_debug_on)
+ PR_fprintf(output,"%s: created thread = 0x%x\n", argv[0],
+ threads[cnt]);
+ }
+
+ for(cnt = 0; cnt < thread_cnt; cnt++) {
+ rc = PR_JoinThread(threads[cnt]);
+ PR_ASSERT(rc == PR_SUCCESS);
+ }
+
+ node = PR_StackPop(list1);
+ /*
+ * list1 should be empty
+ */
+ if (node != NULL) {
+ PR_fprintf(errhandle, "Error - Stack 1 not empty\n");
+ PR_ASSERT(node == NULL);
+ PR_ProcessExit(4);
+ }
+
+ cnt = data_cnt * thread_cnt;
+ sum = 0;
+ while (cnt-- > 0) {
+ node = PR_StackPop(list2);
+ /*
+ * There should be at least 'cnt' number of records
+ */
+ if (node == NULL) {
+ PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n");
+ PR_ProcessExit(3);
+ }
+ Item = RECORD_LINK_PTR(node);
+ sum += Item->data;
+ }
+ node = PR_StackPop(list2);
+ /*
+ * there should be exactly 'cnt' number of records
+ */
+ if (node != NULL) {
+ PR_fprintf(errhandle, "Error - Stack 2 not empty\n");
+ PR_ASSERT(node == NULL);
+ PR_ProcessExit(4);
+ }
+ PR_DELETE(threads);
+ PR_DELETE(thread_args);
+
+ PR_DestroyStack(list1);
+ PR_DestroyStack(list2);
+
+ if (sum == SUM_OF_NUMBERS(data_cnt * thread_cnt)) {
+ PR_fprintf(output, "%s successful\n", argv[0]);
+ PR_fprintf(output, "\t\tsum = 0x%x, expected = 0x%x\n", sum,
+ SUM_OF_NUMBERS(thread_cnt * data_cnt));
+ return 0;
+ } else {
+ PR_fprintf(output, "%s failed: sum = 0x%x, expected = 0x%x\n",
+ argv[0], sum,
+ SUM_OF_NUMBERS(data_cnt * thread_cnt));
+ return 2;
+ }
+}
+
+static void stackop(void *thread_arg)
+{
+ PRInt32 val, cnt, index, loops;
+ DataRecord *Items, *Item;
+ PRStack *list1, *list2;
+ PRStackElem *node;
+ stack_data *arg = (stack_data *) thread_arg;
+
+ val = arg->initial_data_value;
+ cnt = arg->data_cnt;
+ loops = arg->loops;
+ list1 = arg->list1;
+ list2 = arg->list2;
+
+ /*
+ * allocate memory for the data records
+ */
+ Items = (DataRecord *) PR_CALLOC(sizeof(DataRecord) * cnt);
+ PR_ASSERT(Items != NULL);
+ index = 0;
+
+ if (_debug_on)
+ PR_fprintf(output,
+ "Thread[0x%x] init_val = %d cnt = %d data1 = 0x%x datan = 0x%x\n",
+ PR_GetCurrentThread(), val, cnt, &Items[0], &Items[cnt-1]);
+
+
+ /*
+ * add the data records to list1
+ */
+ while (cnt-- > 0) {
+ Items[index].data = val++;
+ PR_StackPush(list1, &Items[index].link);
+ index++;
+ }
+
+ /*
+ * pop data records from list1 and add them back to list1
+ * generates contention for the stack accesses
+ */
+ while (loops-- > 0) {
+ cnt = arg->data_cnt;
+ while (cnt-- > 0) {
+ node = PR_StackPop(list1);
+ if (node == NULL) {
+ PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n");
+ PR_ASSERT(node != NULL);
+ PR_ProcessExit(3);
+ }
+ PR_StackPush(list1, node);
+ }
+ }
+ /*
+ * remove the data records from list1 and add them to list2
+ */
+ cnt = arg->data_cnt;
+ while (cnt-- > 0) {
+ node = PR_StackPop(list1);
+ if (node == NULL) {
+ PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n");
+ PR_ASSERT(node != NULL);
+ PR_ProcessExit(3);
+ }
+ PR_StackPush(list2, node);
+ }
+ if (_debug_on)
+ PR_fprintf(output,
+ "Thread[0x%x] init_val = %d cnt = %d exiting\n",
+ PR_GetCurrentThread(), val, cnt);
+
+}
+
diff --git a/nsprpub/pr/tests/stat.c b/nsprpub/pr/tests/stat.c
new file mode 100644
index 0000000000..1f870a0d42
--- /dev/null
+++ b/nsprpub/pr/tests/stat.c
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Program to test different ways to get file info; right now it
+ * only works for solaris and OS/2.
+ *
+ */
+#include "nspr.h"
+#include "prpriv.h"
+#include "prinrval.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef XP_OS2
+#include <io.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#define DEFAULT_COUNT 100000
+PRInt32 count;
+
+#ifndef XP_PC
+char *filename = "/etc/passwd";
+#else
+char *filename = "..\\stat.c";
+#endif
+
+static void statPRStat(void)
+{
+ PRFileInfo finfo;
+ PRInt32 index = count;
+
+ for (; index--;) {
+ PR_GetFileInfo(filename, &finfo);
+ }
+}
+
+static void statStat(void)
+{
+ struct stat finfo;
+ PRInt32 index = count;
+
+ for (; index--;) {
+ stat(filename, &finfo);
+ }
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+ PRInt32 tot;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+ tot = PR_IntervalToMilliseconds(stop-start);
+
+ printf("%40s: %6.2f usec avg, %d msec total\n", msg, d / count, tot);
+}
+
+int main(int argc, char **argv)
+{
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (argc > 1) {
+ count = atoi(argv[1]);
+ } else {
+ count = DEFAULT_COUNT;
+ }
+
+ Measure(statPRStat, "time to call PR_GetFileInfo()");
+ Measure(statStat, "time to call stat()");
+
+ PR_Cleanup();
+}
diff --git a/nsprpub/pr/tests/stdio.c b/nsprpub/pr/tests/stdio.c
new file mode 100644
index 0000000000..d8a12389b2
--- /dev/null
+++ b/nsprpub/pr/tests/stdio.c
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: stdio.c
+ * Description: testing the "special" fds
+ * Modification History:
+ * 20-May-1997 AGarcia - Replace Test succeeded status with PASS. This is used by the
+ * regress tool parsing code.
+ ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+ */
+
+
+#include "prlog.h"
+#include "prinit.h"
+#include "prio.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static PRIntn PR_CALLBACK stdio(PRIntn argc, char **argv)
+{
+ PRInt32 rv;
+
+ PRFileDesc *out = PR_GetSpecialFD(PR_StandardOutput);
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+
+ rv = PR_Write(
+ out, "This to standard out\n",
+ strlen("This to standard out\n"));
+ PR_ASSERT((PRInt32)strlen("This to standard out\n") == rv);
+ rv = PR_Write(
+ err, "This to standard err\n",
+ strlen("This to standard err\n"));
+ PR_ASSERT((PRInt32)strlen("This to standard err\n") == rv);
+
+ return 0;
+
+} /* stdio */
+
+int main(int argc, char **argv)
+{
+ PR_STDIO_INIT();
+ return PR_Initialize(stdio, argc, argv, 0);
+} /* main */
+
+
+/* stdio.c */
diff --git a/nsprpub/pr/tests/str2addr.c b/nsprpub/pr/tests/str2addr.c
new file mode 100644
index 0000000000..ad80137d4d
--- /dev/null
+++ b/nsprpub/pr/tests/str2addr.c
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: str2addr.c
+ * Description: a test for PR_StringToNetAddr
+ */
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Address string to convert */
+#define DEFAULT_IPV4_ADDR_STR "207.200.73.41"
+
+/* Expected conversion result, in network byte order */
+static unsigned char default_ipv4_addr[] = {207, 200, 73, 41};
+
+int main(int argc, char **argv)
+{
+ PRNetAddr addr;
+ const char *addrStr;
+ unsigned char *bytes;
+ int idx;
+
+ addrStr = DEFAULT_IPV4_ADDR_STR;
+ if (PR_StringToNetAddr(addrStr, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_StringToNetAddr failed\n");
+ exit(1);
+ }
+ if (addr.inet.family != PR_AF_INET) {
+ fprintf(stderr, "addr.inet.family should be %d but is %d\n",
+ PR_AF_INET, addr.inet.family);
+ exit(1);
+ }
+ bytes = (unsigned char *) &addr.inet.ip;
+ for (idx = 0; idx < 4; idx++) {
+ if (bytes[idx] != default_ipv4_addr[idx]) {
+ fprintf(stderr, "byte %d of IPv4 addr should be %d but is %d\n",
+ idx, default_ipv4_addr[idx], bytes[idx]);
+ exit(1);
+ }
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/strod.c b/nsprpub/pr/tests/strod.c
new file mode 100644
index 0000000000..014192fa6b
--- /dev/null
+++ b/nsprpub/pr/tests/strod.c
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prinit.h"
+#include "prio.h"
+#include "prprf.h"
+#include "prdtoa.h"
+#include "plgetopt.h"
+
+#include <stdlib.h>
+
+static void Help(void)
+{
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+ PR_fprintf(err, "Usage: /.strod [-n n] [-l n] [-h]\n");
+ PR_fprintf(err, "\t-n n Number to translate (default: 1234567890123456789)\n");
+ PR_fprintf(err, "\t-l n Times to loop the test (default: 1)\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+static PRIntn PR_CALLBACK RealMain(PRIntn argc, char **argv)
+{
+ PLOptStatus os;
+ PRIntn loops = 1;
+ PRFloat64 answer;
+ const char *number = "1234567890123456789";
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hc:l:");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'n': /* number to translate */
+ number = opt->value;
+ break;
+ case 'l': /* number of times to run the tests */
+ loops = atoi(opt->value);
+ break;
+ case 'h': /* user wants some guidance */
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_fprintf(err, "Settings\n");
+ PR_fprintf(err, "\tNumber to translate %s\n", number);
+ PR_fprintf(err, "\tLoops to run test: %d\n", loops);
+
+ while (loops--)
+ {
+ answer = PR_strtod(number, NULL);
+ PR_fprintf(err, "Translation = %20.0f\n", answer);
+ }
+ return 0;
+}
+
+
+
+int main(int argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/nsprpub/pr/tests/suspend.c b/nsprpub/pr/tests/suspend.c
new file mode 100644
index 0000000000..1976b9d8c0
--- /dev/null
+++ b/nsprpub/pr/tests/suspend.c
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+#include "prpriv.h"
+#include "prinrval.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRMonitor *mon;
+PRInt32 count;
+PRInt32 alive;
+
+#define SLEEP_TIME 4 /* secs */
+
+void PR_CALLBACK
+Level_2_Thread(void *arg)
+{
+ PR_Sleep(PR_MillisecondsToInterval(4 * 1000));
+ printf("Level_2_Thread[0x%lx] exiting\n",PR_GetCurrentThread());
+ return;
+}
+
+void PR_CALLBACK
+Level_1_Thread(void *arg)
+{
+ PRUint32 tmp = (PRUint32)arg;
+ PRThreadScope scope = (PRThreadScope) tmp;
+ PRThread *thr;
+
+ thr = PR_CreateThreadGCAble(PR_USER_THREAD,
+ Level_2_Thread,
+ NULL,
+ PR_PRIORITY_HIGH,
+ scope,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ if (!thr) {
+ printf("Could not create thread!\n");
+ } else {
+ printf("Level_1_Thread[0x%lx] created %15s thread 0x%lx\n",
+ PR_GetCurrentThread(),
+ (scope == PR_GLOBAL_THREAD) ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD",
+ thr);
+ PR_JoinThread(thr);
+ }
+ PR_EnterMonitor(mon);
+ alive--;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+ printf("Thread[0x%lx] exiting\n",PR_GetCurrentThread());
+}
+
+static PRStatus PR_CALLBACK print_thread(PRThread *thread, int i, void *arg)
+{
+ PRInt32 words;
+ PRWord *registers;
+
+ printf(
+ "\nprint_thread[0x%lx]: %-20s - i = %ld\n",thread,
+ (PR_GLOBAL_THREAD == PR_GetThreadScope(thread)) ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD", i);
+ registers = PR_GetGCRegisters(thread, 0, (int *)&words);
+ if (registers)
+ printf("Registers R0 = 0x%x R1 = 0x%x R2 = 0x%x R3 = 0x%x\n",
+ registers[0],registers[1],registers[2],registers[3]);
+ printf("Stack Pointer = 0x%lx\n", PR_GetSP(thread));
+ return PR_SUCCESS;
+}
+
+static void Level_0_Thread(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *thr;
+ PRThread *me = PR_GetCurrentThread();
+ int n;
+ PRInt32 words;
+ PRWord *registers;
+
+ alive = 0;
+ mon = PR_NewMonitor();
+
+ alive = count;
+ for (n=0; n<count; n++) {
+ thr = PR_CreateThreadGCAble(PR_USER_THREAD,
+ Level_1_Thread,
+ (void *)scope2,
+ PR_PRIORITY_NORMAL,
+ scope1,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (!thr) {
+ printf("Could not create thread!\n");
+ alive--;
+ }
+ printf("Level_0_Thread[0x%lx] created %15s thread 0x%lx\n",
+ PR_GetCurrentThread(),
+ (scope1 == PR_GLOBAL_THREAD) ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD",
+ thr);
+
+ PR_Sleep(0);
+ }
+ PR_SuspendAll();
+ PR_EnumerateThreads(print_thread, NULL);
+ registers = PR_GetGCRegisters(me, 1, (int *)&words);
+ if (registers)
+ printf("My Registers: R0 = 0x%x R1 = 0x%x R2 = 0x%x R3 = 0x%x\n",
+ registers[0],registers[1],registers[2],registers[3]);
+ printf("My Stack Pointer = 0x%lx\n", PR_GetSP(me));
+ PR_ResumeAll();
+
+ /* Wait for all threads to exit */
+ PR_EnterMonitor(mon);
+ while (alive) {
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ PR_ExitMonitor(mon);
+ PR_DestroyMonitor(mon);
+}
+
+static void CreateThreadsUU(void)
+{
+ Level_0_Thread(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void CreateThreadsUK(void)
+{
+ Level_0_Thread(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+static void CreateThreadsKU(void)
+{
+ Level_0_Thread(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void CreateThreadsKK(void)
+{
+ Level_0_Thread(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+
+int main(int argc, char **argv)
+{
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (argc > 1) {
+ count = atoi(argv[1]);
+ } else {
+ count = 5;
+ }
+
+ printf("\n\n%20s%30s\n\n"," ","Suspend_Resume Test");
+ CreateThreadsUU();
+ CreateThreadsUK();
+ CreateThreadsKU();
+ CreateThreadsKK();
+ PR_SetConcurrency(2);
+
+ printf("\n%20s%30s\n\n"," ","Added 2nd CPU\n");
+
+ CreateThreadsUK();
+ CreateThreadsKK();
+ CreateThreadsUU();
+ CreateThreadsKU();
+ PR_Cleanup();
+
+ return 0;
+}
+
diff --git a/nsprpub/pr/tests/switch.c b/nsprpub/pr/tests/switch.c
new file mode 100644
index 0000000000..43db7888fb
--- /dev/null
+++ b/nsprpub/pr/tests/switch.c
@@ -0,0 +1,252 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: switch.c
+** Description: trying to time context switches
+*/
+
+#include "prinit.h"
+#include "prcvar.h"
+#include "prmem.h"
+#include "prinrval.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prthread.h"
+#include "prprf.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include "private/pprio.h"
+
+#include <stdlib.h>
+
+#define INNER_LOOPS 100
+#define DEFAULT_LOOPS 100
+#define DEFAULT_THREADS 10
+
+static PRFileDesc *debug_out = NULL;
+static PRBool debug_mode = PR_FALSE, verbosity = PR_FALSE, failed = PR_FALSE;
+
+typedef struct Shared
+{
+ PRLock *ml;
+ PRCondVar *cv;
+ PRBool twiddle;
+ PRThread *thread;
+ struct Shared *next;
+} Shared;
+
+static void Help(void)
+{
+ debug_out = PR_STDOUT;
+
+ PR_fprintf(
+ debug_out, "Usage: >./switch [-c n] [-t n] [-d] [-v] [-G] [-C n]\n");
+ PR_fprintf(
+ debug_out, "-c n\tloops at thread level (default: %d)\n", DEFAULT_LOOPS);
+ PR_fprintf(
+ debug_out, "-t n\tnumber of threads (default: %d)\n", DEFAULT_THREADS);
+ PR_fprintf(debug_out, "-d\tturn on debugging output (default: FALSE)\n");
+ PR_fprintf(debug_out, "-v\tturn on verbose output (default: FALSE)\n");
+ PR_fprintf(debug_out, "-G\tglobal threads only (default: FALSE)\n");
+ PR_fprintf(debug_out, "-C n\tconcurrency setting (default: 1)\n");
+} /* Help */
+
+static void PR_CALLBACK Notified(void *arg)
+{
+ Shared *shared = (Shared*)arg;
+ PRStatus status = PR_SUCCESS;
+ while (PR_SUCCESS == status)
+ {
+ PR_Lock(shared->ml);
+ while (shared->twiddle && (PR_SUCCESS == status)) {
+ status = PR_WaitCondVar(shared->cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ if (verbosity) {
+ PR_fprintf(debug_out, "+");
+ }
+ shared->twiddle = PR_TRUE;
+ shared->next->twiddle = PR_FALSE;
+ PR_NotifyCondVar(shared->next->cv);
+ PR_Unlock(shared->ml);
+ }
+} /* Notified */
+
+static Shared home;
+PRIntn PR_CALLBACK Switch(PRIntn argc, char **argv)
+{
+ PLOptStatus os;
+ PRStatus status;
+ PRBool help = PR_FALSE;
+ PRUintn concurrency = 1;
+ Shared *shared, *link;
+ PRIntervalTime timein, timeout;
+ PRThreadScope thread_scope = PR_LOCAL_THREAD;
+ PRUintn thread_count, inner_count, loop_count, average;
+ PRUintn thread_limit = DEFAULT_THREADS, loop_limit = DEFAULT_LOOPS;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hdvc:t:C:G");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'v': /* verbose mode */
+ verbosity = PR_TRUE;
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'c': /* loop counter */
+ loop_limit = atoi(opt->value);
+ break;
+ case 't': /* thread limit */
+ thread_limit = atoi(opt->value);
+ break;
+ case 'C': /* Concurrency limit */
+ concurrency = atoi(opt->value);
+ break;
+ case 'G': /* global threads only */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'h': /* help message */
+ Help();
+ help = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (help) {
+ return -1;
+ }
+
+ if (PR_TRUE == debug_mode)
+ {
+ debug_out = PR_STDOUT;
+ PR_fprintf(debug_out, "Test parameters\n");
+ PR_fprintf(debug_out, "\tThreads involved: %d\n", thread_limit);
+ PR_fprintf(debug_out, "\tIteration limit: %d\n", loop_limit);
+ PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency);
+ PR_fprintf(
+ debug_out, "\tThread type: %s\n",
+ (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL");
+ }
+
+ PR_SetConcurrency(concurrency);
+
+ link = &home;
+ home.ml = PR_NewLock();
+ home.cv = PR_NewCondVar(home.ml);
+ home.twiddle = PR_FALSE;
+ home.next = NULL;
+
+ timeout = 0;
+
+ for (thread_count = 1; thread_count <= thread_limit; ++thread_count)
+ {
+ shared = PR_NEWZAP(Shared);
+
+ shared->ml = home.ml;
+ shared->cv = PR_NewCondVar(home.ml);
+ shared->twiddle = PR_TRUE;
+ shared->next = link;
+ link = shared;
+
+ shared->thread = PR_CreateThread(
+ PR_USER_THREAD, Notified, shared,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(shared->thread != NULL);
+ if (NULL == shared->thread) {
+ failed = PR_TRUE;
+ }
+ }
+
+ for (loop_count = 1; loop_count <= loop_limit; ++loop_count)
+ {
+ timein = PR_IntervalNow();
+ for (inner_count = 0; inner_count < INNER_LOOPS; ++inner_count)
+ {
+ PR_Lock(home.ml);
+ home.twiddle = PR_TRUE;
+ shared->twiddle = PR_FALSE;
+ PR_NotifyCondVar(shared->cv);
+ while (home.twiddle)
+ {
+ status = PR_WaitCondVar(home.cv, PR_INTERVAL_NO_TIMEOUT);
+ if (PR_FAILURE == status) {
+ failed = PR_TRUE;
+ }
+ }
+ PR_Unlock(home.ml);
+ }
+ timeout += (PR_IntervalNow() - timein);
+ }
+
+ if (debug_mode)
+ {
+ average = PR_IntervalToMicroseconds(timeout)
+ / (INNER_LOOPS * loop_limit * thread_count);
+ PR_fprintf(
+ debug_out, "Average switch times %d usecs for %d threads\n",
+ average, thread_limit);
+ }
+
+ link = shared;
+ for (thread_count = 1; thread_count <= thread_limit; ++thread_count)
+ {
+ if (&home == link) {
+ break;
+ }
+ status = PR_Interrupt(link->thread);
+ if (PR_SUCCESS != status)
+ {
+ failed = PR_TRUE;
+ if (debug_mode) {
+ PL_FPrintError(debug_out, "Failed to interrupt");
+ }
+ }
+ link = link->next;
+ }
+
+ for (thread_count = 1; thread_count <= thread_limit; ++thread_count)
+ {
+ link = shared->next;
+ status = PR_JoinThread(shared->thread);
+ if (PR_SUCCESS != status)
+ {
+ failed = PR_TRUE;
+ if (debug_mode) {
+ PL_FPrintError(debug_out, "Failed to join");
+ }
+ }
+ PR_DestroyCondVar(shared->cv);
+ PR_DELETE(shared);
+ if (&home == link) {
+ break;
+ }
+ shared = link;
+ }
+ PR_DestroyCondVar(home.cv);
+ PR_DestroyLock(home.ml);
+
+ PR_fprintf(PR_STDOUT, ((failed) ? "FAILED\n" : "PASSED\n"));
+ return ((failed) ? 1 : 0);
+} /* Switch */
+
+int main(int argc, char **argv)
+{
+ PRIntn result;
+ PR_STDIO_INIT();
+ result = PR_Initialize(Switch, argc, argv, 0);
+ return result;
+} /* main */
+
+/* switch.c */
diff --git a/nsprpub/pr/tests/system.c b/nsprpub/pr/tests/system.c
new file mode 100644
index 0000000000..85b5e2e502
--- /dev/null
+++ b/nsprpub/pr/tests/system.c
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prio.h"
+#include "prmem.h"
+#include "prprf.h"
+#include "prsystem.h"
+
+#include "plerror.h"
+
+static char *tag[] =
+{
+ "PR_SI_HOSTNAME",
+ "PR_SI_SYSNAME",
+ "PR_SI_RELEASE",
+ "PR_SI_ARCHITECTURE"
+};
+
+static PRSysInfo Incr(PRSysInfo *cmd)
+{
+ PRIntn tmp = (PRIntn)*cmd + 1;
+ *cmd = (PRSysInfo)tmp;
+ return (PRSysInfo)tmp;
+} /* Incr */
+
+int main(int argc, char **argv)
+{
+ PRStatus rv;
+ PRSysInfo cmd;
+ PRFileDesc *output = PR_GetSpecialFD(PR_StandardOutput);
+
+ char *info = (char*)PR_Calloc(SYS_INFO_BUFFER_LENGTH, 1);
+ for (cmd = PR_SI_HOSTNAME; cmd <= PR_SI_ARCHITECTURE; Incr(&cmd))
+ {
+ rv = PR_GetSystemInfo(cmd, info, SYS_INFO_BUFFER_LENGTH);
+ if (PR_SUCCESS == rv) {
+ PR_fprintf(output, "%s: %s\n", tag[cmd], info);
+ }
+ else {
+ PL_FPrintError(output, tag[cmd]);
+ }
+ }
+ PR_DELETE(info);
+
+ PR_fprintf(output, "Host page size is %d\n", PR_GetPageSize());
+ PR_fprintf(output, "Page shift is %d\n", PR_GetPageShift());
+ PR_fprintf(output, "Memory map alignment is %ld\n", PR_GetMemMapAlignment());
+ PR_fprintf(output, "Number of processors is: %d\n", PR_GetNumberOfProcessors());
+ PR_fprintf(output, "Physical memory size is: %llu\n", PR_GetPhysicalMemorySize());
+
+ return 0;
+} /* main */
+
+/* system.c */
diff --git a/nsprpub/pr/tests/testbit.c b/nsprpub/pr/tests/testbit.c
new file mode 100644
index 0000000000..889c637e22
--- /dev/null
+++ b/nsprpub/pr/tests/testbit.c
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: lazyinit.c
+** Description: Test the functions and macros declared in prbit.h
+**
+*/
+
+#include "nspr.h"
+
+#define ErrorReport(x) { printf((x)); failed = 1; }
+
+prbitmap_t myMap[512/32] = { 0 };
+
+PRInt32 rc;
+PRInt32 i;
+PRIntn failed = 0;
+
+int main(int argc, char **argv)
+{
+ /*
+ ** Test bitmap things.
+ */
+ if ( PR_TEST_BIT( myMap, 0 )) {
+ ErrorReport("Test 0.0: Failed\n");
+ }
+
+ if ( PR_TEST_BIT( myMap, 31 )) {
+ ErrorReport("Test 0.1: Failed\n");
+ }
+
+ if ( PR_TEST_BIT( myMap, 128 )) {
+ ErrorReport("Test 0.2: Failed\n");
+ }
+
+ if ( PR_TEST_BIT( myMap, 129 )) {
+ ErrorReport("Test 0.3: Failed\n");
+ }
+
+
+ PR_SET_BIT( myMap, 0 );
+ if ( !PR_TEST_BIT( myMap, 0 )) {
+ ErrorReport("Test 1.0: Failed\n");
+ }
+
+ PR_CLEAR_BIT( myMap, 0 );
+ if ( PR_TEST_BIT( myMap, 0 )) {
+ ErrorReport("Test 1.0.1: Failed\n");
+ }
+
+ PR_SET_BIT( myMap, 31 );
+ if ( !PR_TEST_BIT( myMap, 31 )) {
+ ErrorReport("Test 1.1: Failed\n");
+ }
+
+ PR_CLEAR_BIT( myMap, 31 );
+ if ( PR_TEST_BIT( myMap, 31 )) {
+ ErrorReport("Test 1.1.1: Failed\n");
+ }
+
+ PR_SET_BIT( myMap, 128 );
+ if ( !PR_TEST_BIT( myMap, 128 )) {
+ ErrorReport("Test 1.2: Failed\n");
+ }
+
+ PR_CLEAR_BIT( myMap, 128 );
+ if ( PR_TEST_BIT( myMap, 128 )) {
+ ErrorReport("Test 1.2.1: Failed\n");
+ }
+
+ PR_SET_BIT( myMap, 129 );
+ if ( !PR_TEST_BIT( myMap, 129 )) {
+ ErrorReport("Test 1.3: Failed\n");
+ }
+
+ PR_CLEAR_BIT( myMap, 129 );
+ if ( PR_TEST_BIT( myMap, 129 )) {
+ ErrorReport("Test 1.3.1: Failed\n");
+ }
+
+
+ /*
+ ** Test Ceiling and Floor functions and macros
+ */
+ if ((rc = PR_CeilingLog2(32)) != 5 ) {
+ ErrorReport("Test 10.0: Failed\n");
+ }
+
+ if ((rc = PR_FloorLog2(32)) != 5 ) {
+ ErrorReport("Test 10.1: Failed\n");
+ }
+
+
+ /*
+ ** Evaluate results and exit
+ */
+ if (failed)
+ {
+ printf("FAILED\n");
+ return(1);
+ }
+ else
+ {
+ printf("PASSED\n");
+ return(0);
+ }
+} /* end main() */
+/* end testbit.c */
diff --git a/nsprpub/pr/tests/testfile.c b/nsprpub/pr/tests/testfile.c
new file mode 100644
index 0000000000..79d8a05f92
--- /dev/null
+++ b/nsprpub/pr/tests/testfile.c
@@ -0,0 +1,963 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+#include "prpriv.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef WIN32
+#include <windows.h>
+#include <process.h>
+#endif
+#if defined(_PR_PTHREADS)
+#include <pthread.h>
+#endif
+
+#if defined(XP_UNIX)
+#include <unistd.h>
+#endif
+
+#if defined(XP_OS2)
+#define INCL_DOSFILEMGR
+#include <os2.h>
+#include <getopt.h>
+#include <errno.h>
+#endif /* XP_OS2 */
+
+static int _debug_on = 0;
+
+#ifdef WINCE
+#define setbuf(x,y)
+#endif
+
+#ifdef XP_WIN
+#define mode_t int
+#endif
+
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+PRLock *lock;
+PRMonitor *mon;
+PRInt32 count;
+int thread_count;
+
+#ifdef WIN16
+#define BUF_DATA_SIZE 256 * 120
+#else
+#define BUF_DATA_SIZE 256 * 1024
+#endif
+
+#define NUM_RDWR_THREADS 10
+#define NUM_DIRTEST_THREADS 4
+#define CHUNK_SIZE 512
+
+typedef struct buffer {
+ char data[BUF_DATA_SIZE];
+} buffer;
+
+typedef struct File_Rdwr_Param {
+ char *pathname;
+ char *buf;
+ int offset;
+ int len;
+} File_Rdwr_Param;
+
+#ifdef XP_PC
+#ifdef XP_OS2
+char *TEST_DIR = "prdir";
+#else
+char *TEST_DIR = "C:\\temp\\prdir";
+#endif
+char *FILE_NAME = "pr_testfile";
+char *HIDDEN_FILE_NAME = "hidden_pr_testfile";
+#else
+char *TEST_DIR = "./tmp-testfile_dir";
+char *FILE_NAME = "pr_testfile";
+char *HIDDEN_FILE_NAME = ".hidden_pr_testfile";
+#endif
+buffer *in_buf, *out_buf;
+char pathname[256], renamename[256];
+#ifdef WINCE
+WCHAR wPathname[256];
+#endif
+#define TMPDIR_LEN 64
+char testdir[TMPDIR_LEN];
+static PRInt32 PR_CALLBACK DirTest(void *argunused);
+PRInt32 dirtest_failed = 0;
+
+PRThread* create_new_thread(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize, PRInt32 index)
+{
+ PRInt32 native_thread = 0;
+
+ PR_ASSERT(state == PR_UNJOINABLE_THREAD);
+
+#if defined(_PR_PTHREADS) || defined(WIN32) || defined(XP_OS2)
+
+ switch(index % 4) {
+ case 0:
+ scope = (PR_LOCAL_THREAD);
+ break;
+ case 1:
+ scope = (PR_GLOBAL_THREAD);
+ break;
+ case 2:
+ scope = (PR_GLOBAL_BOUND_THREAD);
+ break;
+ case 3:
+ native_thread = 1;
+ break;
+ default:
+ PR_NOT_REACHED("Invalid scope");
+ break;
+ }
+ if (native_thread) {
+#if defined(_PR_PTHREADS)
+ pthread_t tid;
+ if (!pthread_create(&tid, NULL, start, arg)) {
+ return((PRThread *) tid);
+ }
+ else {
+ return (NULL);
+ }
+#elif defined(XP_OS2)
+ TID tid;
+
+ tid = (TID)_beginthread((void(* _Optlink)(void*))start,
+ NULL, 32768, arg);
+ if (tid == -1) {
+ printf("_beginthread failed. errno %d\n", errno);
+ return (NULL);
+ }
+ else {
+ return((PRThread *) tid);
+ }
+#else
+ HANDLE thandle;
+ unsigned tid;
+
+ thandle = (HANDLE) _beginthreadex(
+ NULL,
+ stackSize,
+ (unsigned (__stdcall *)(void *))start,
+ arg,
+ STACK_SIZE_PARAM_IS_A_RESERVATION,
+ &tid);
+ return((PRThread *) thandle);
+#endif
+ } else {
+ return(PR_CreateThread(type,start,arg,priority,scope,state,stackSize));
+ }
+#else
+ return(PR_CreateThread(type,start,arg,priority,scope,state,stackSize));
+#endif
+}
+
+static void PR_CALLBACK File_Write(void *arg)
+{
+ PRFileDesc *fd_file;
+ File_Rdwr_Param *fp = (File_Rdwr_Param *) arg;
+ char *name, *buf;
+ int offset, len;
+
+ setbuf(stdout, NULL);
+ name = fp->pathname;
+ buf = fp->buf;
+ offset = fp->offset;
+ len = fp->len;
+
+ fd_file = PR_Open(name, PR_RDWR | PR_CREATE_FILE, 0777);
+ if (fd_file == NULL) {
+ printf("testfile failed to create/open file %s\n",name);
+ return;
+ }
+ if (PR_Seek(fd_file, offset, PR_SEEK_SET) < 0) {
+ printf("testfile failed to seek in file %s\n",name);
+ return;
+ }
+ if ((PR_Write(fd_file, buf, len)) < 0) {
+ printf("testfile failed to write to file %s\n",name);
+ return;
+ }
+ DPRINTF(("Write out_buf[0] = 0x%x\n",(*((int *) buf))));
+ PR_Close(fd_file);
+ PR_DELETE(fp);
+
+ PR_EnterMonitor(mon);
+ --thread_count;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+}
+
+static void PR_CALLBACK File_Read(void *arg)
+{
+ PRFileDesc *fd_file;
+ File_Rdwr_Param *fp = (File_Rdwr_Param *) arg;
+ char *name, *buf;
+ int offset, len;
+
+ setbuf(stdout, NULL);
+ name = fp->pathname;
+ buf = fp->buf;
+ offset = fp->offset;
+ len = fp->len;
+
+ fd_file = PR_Open(name, PR_RDONLY, 0);
+ if (fd_file == NULL) {
+ printf("testfile failed to open file %s\n",name);
+ return;
+ }
+ if (PR_Seek(fd_file, offset, PR_SEEK_SET) < 0) {
+ printf("testfile failed to seek in file %s\n",name);
+ return;
+ }
+ if ((PR_Read(fd_file, buf, len)) < 0) {
+ printf("testfile failed to read to file %s\n",name);
+ return;
+ }
+ DPRINTF(("Read in_buf[0] = 0x%x\n",(*((int *) buf))));
+ PR_Close(fd_file);
+ PR_DELETE(fp);
+
+ PR_EnterMonitor(mon);
+ --thread_count;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+}
+
+
+static PRInt32 Misc_File_Tests(char *pathname)
+{
+ PRFileDesc *fd_file;
+ int len, rv = 0;
+ PRFileInfo file_info, file_info1;
+ char tmpname[1024];
+
+ setbuf(stdout, NULL);
+ /*
+ * Test PR_Available, PR_Seek, PR_GetFileInfo, PR_Rename, PR_Access
+ */
+
+ fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777);
+
+ if (fd_file == NULL) {
+ printf("testfile failed to create/open file %s\n",pathname);
+ return -1;
+ }
+ if (PR_GetOpenFileInfo(fd_file, &file_info) < 0) {
+ printf("testfile PR_GetFileInfo failed on file %s\n",pathname);
+ rv = -1;
+ goto cleanup;
+ }
+ if (PR_Access(pathname, PR_ACCESS_EXISTS) != 0) {
+ printf("testfile PR_Access failed on file %s\n",pathname);
+ rv = -1;
+ goto cleanup;
+ }
+ if (PR_Access(pathname, PR_ACCESS_WRITE_OK) != 0) {
+ printf("testfile PR_Access failed on file %s\n",pathname);
+ rv = -1;
+ goto cleanup;
+ }
+ if (PR_Access(pathname, PR_ACCESS_READ_OK) != 0) {
+ printf("testfile PR_Access failed on file %s\n",pathname);
+ rv = -1;
+ goto cleanup;
+ }
+
+
+ if (PR_GetFileInfo(pathname, &file_info) < 0) {
+ printf("testfile PR_GetFileInfo failed on file %s\n",pathname);
+ rv = -1;
+ goto cleanup;
+ }
+ if (file_info.type != PR_FILE_FILE) {
+ printf(
+ "testfile: Error - PR_GetFileInfo returned incorrect type for file %s\n",
+ pathname);
+ rv = -1;
+ goto cleanup;
+ }
+ if (file_info.size != 0) {
+ printf(
+ "testfile PR_GetFileInfo returned incorrect size (%d should be 0) for file %s\n",
+ file_info.size, pathname);
+ rv = -1;
+ goto cleanup;
+ }
+ file_info1 = file_info;
+
+ len = PR_Available(fd_file);
+ if (len < 0) {
+ printf("testfile PR_Available failed on file %s\n",pathname);
+ rv = -1;
+ goto cleanup;
+ } else if (len != 0) {
+ printf(
+ "testfile PR_Available failed: expected/returned = %d/%d bytes\n",
+ 0, len);
+ rv = -1;
+ goto cleanup;
+ }
+ if (PR_GetOpenFileInfo(fd_file, &file_info) < 0) {
+ printf("testfile PR_GetFileInfo failed on file %s\n",pathname);
+ goto cleanup;
+ }
+ if (LL_NE(file_info.creationTime, file_info1.creationTime)) {
+ printf(
+ "testfile PR_GetFileInfo returned incorrect status-change time: %s\n",
+ pathname);
+ printf("ft = %lld, ft1 = %lld\n",file_info.creationTime,
+ file_info1.creationTime);
+ rv = -1;
+ goto cleanup;
+ }
+ len = PR_Write(fd_file, out_buf->data, CHUNK_SIZE);
+ if (len < 0) {
+ printf("testfile failed to write to file %s\n",pathname);
+ rv = -1;
+ goto cleanup;
+ }
+ if (PR_GetOpenFileInfo(fd_file, &file_info) < 0) {
+ printf("testfile PR_GetFileInfo failed on file %s\n",pathname);
+ goto cleanup;
+ }
+ if (file_info.size != CHUNK_SIZE) {
+ printf(
+ "testfile PR_GetFileInfo returned incorrect size (%d should be %d) for file %s\n",
+ file_info.size, CHUNK_SIZE, pathname);
+ rv = -1;
+ goto cleanup;
+ }
+ if (LL_CMP(file_info.modifyTime, <, file_info1.modifyTime)) {
+ printf(
+ "testfile PR_GetFileInfo returned incorrect modify time: %s\n",
+ pathname);
+ printf("ft = %lld, ft1 = %lld\n",file_info.modifyTime,
+ file_info1.modifyTime);
+ rv = -1;
+ goto cleanup;
+ }
+
+ len = PR_Available(fd_file);
+ if (len < 0) {
+ printf("testfile PR_Available failed on file %s\n",pathname);
+ rv = -1;
+ goto cleanup;
+ } else if (len != 0) {
+ printf(
+ "testfile PR_Available failed: expected/returned = %d/%d bytes\n",
+ 0, len);
+ rv = -1;
+ goto cleanup;
+ }
+
+ PR_Seek(fd_file, 0, PR_SEEK_SET);
+ len = PR_Available(fd_file);
+ if (len < 0) {
+ printf("testfile PR_Available failed on file %s\n",pathname);
+ rv = -1;
+ goto cleanup;
+ } else if (len != CHUNK_SIZE) {
+ printf(
+ "testfile PR_Available failed: expected/returned = %d/%d bytes\n",
+ CHUNK_SIZE, len);
+ rv = -1;
+ goto cleanup;
+ }
+ PR_Close(fd_file);
+
+ strcpy(tmpname,pathname);
+ strcat(tmpname,".RENAMED");
+ if (PR_FAILURE == PR_Rename(pathname, tmpname)) {
+ printf("testfile failed to rename file %s\n",pathname);
+ rv = -1;
+ goto cleanup;
+ }
+
+ fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777);
+ len = PR_Write(fd_file, out_buf->data, CHUNK_SIZE);
+ PR_Close(fd_file);
+ if (PR_SUCCESS == PR_Rename(pathname, tmpname)) {
+ printf("testfile renamed to existing file %s\n",pathname);
+ }
+
+ if ((PR_Delete(tmpname)) < 0) {
+ printf("testfile failed to unlink file %s\n",tmpname);
+ rv = -1;
+ }
+
+cleanup:
+ if ((PR_Delete(pathname)) < 0) {
+ printf("testfile failed to unlink file %s\n",pathname);
+ rv = -1;
+ }
+ return rv;
+}
+
+
+static PRInt32 PR_CALLBACK FileTest(void)
+{
+ PRDir *fd_dir;
+ int i, offset, len, rv = 0;
+ PRThread *t;
+ PRThreadScope scope = PR_GLOBAL_THREAD;
+ File_Rdwr_Param *fparamp;
+
+ /*
+ * Create Test dir
+ */
+ if ((PR_MkDir(TEST_DIR, 0777)) < 0) {
+ printf("testfile failed to create dir %s\n",TEST_DIR);
+ return -1;
+ }
+ fd_dir = PR_OpenDir(TEST_DIR);
+ if (fd_dir == NULL) {
+ printf("testfile failed to open dir %s\n",TEST_DIR);
+ rv = -1;
+ goto cleanup;
+ }
+
+ PR_CloseDir(fd_dir);
+
+ strcat(pathname, TEST_DIR);
+ strcat(pathname, "/");
+ strcat(pathname, FILE_NAME);
+
+ in_buf = PR_NEW(buffer);
+ if (in_buf == NULL) {
+ printf(
+ "testfile failed to alloc buffer struct\n");
+ rv = -1;
+ goto cleanup;
+ }
+ out_buf = PR_NEW(buffer);
+ if (out_buf == NULL) {
+ printf(
+ "testfile failed to alloc buffer struct\n");
+ rv = -1;
+ goto cleanup;
+ }
+
+ /*
+ * Start a bunch of writer threads
+ */
+ offset = 0;
+ len = CHUNK_SIZE;
+ PR_EnterMonitor(mon);
+ for (i = 0; i < NUM_RDWR_THREADS; i++) {
+ fparamp = PR_NEW(File_Rdwr_Param);
+ if (fparamp == NULL) {
+ printf(
+ "testfile failed to alloc File_Rdwr_Param struct\n");
+ rv = -1;
+ goto cleanup;
+ }
+ fparamp->pathname = pathname;
+ fparamp->buf = out_buf->data + offset;
+ fparamp->offset = offset;
+ fparamp->len = len;
+ memset(fparamp->buf, i, len);
+
+ t = create_new_thread(PR_USER_THREAD,
+ File_Write, (void *)fparamp,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_UNJOINABLE_THREAD,
+ 0, i);
+ offset += len;
+ }
+ thread_count = i;
+ /* Wait for writer threads to exit */
+ while (thread_count) {
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(mon);
+
+
+ /*
+ * Start a bunch of reader threads
+ */
+ offset = 0;
+ len = CHUNK_SIZE;
+ PR_EnterMonitor(mon);
+ for (i = 0; i < NUM_RDWR_THREADS; i++) {
+ fparamp = PR_NEW(File_Rdwr_Param);
+ if (fparamp == NULL) {
+ printf(
+ "testfile failed to alloc File_Rdwr_Param struct\n");
+ rv = -1;
+ goto cleanup;
+ }
+ fparamp->pathname = pathname;
+ fparamp->buf = in_buf->data + offset;
+ fparamp->offset = offset;
+ fparamp->len = len;
+
+ t = create_new_thread(PR_USER_THREAD,
+ File_Read, (void *)fparamp,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_UNJOINABLE_THREAD,
+ 0, i);
+ offset += len;
+ if ((offset + len) > BUF_DATA_SIZE) {
+ break;
+ }
+ }
+ thread_count = i;
+
+ /* Wait for reader threads to exit */
+ while (thread_count) {
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(mon);
+
+ if (memcmp(in_buf->data, out_buf->data, offset) != 0) {
+ printf("File Test failed: file data corrupted\n");
+ rv = -1;
+ goto cleanup;
+ }
+
+ if ((PR_Delete(pathname)) < 0) {
+ printf("testfile failed to unlink file %s\n",pathname);
+ rv = -1;
+ goto cleanup;
+ }
+
+ /*
+ * Test PR_Available, PR_Seek, PR_GetFileInfo, PR_Rename, PR_Access
+ */
+ if (Misc_File_Tests(pathname) < 0) {
+ rv = -1;
+ }
+
+cleanup:
+ if ((PR_RmDir(TEST_DIR)) < 0) {
+ printf("testfile failed to rmdir %s\n", TEST_DIR);
+ rv = -1;
+ }
+ return rv;
+}
+
+struct dirtest_arg {
+ PRMonitor *mon;
+ PRInt32 done;
+};
+
+static PRInt32 RunDirTest(void)
+{
+ int i;
+ PRThread *t;
+ PRMonitor *mon;
+ struct dirtest_arg thrarg;
+
+ mon = PR_NewMonitor();
+ if (!mon) {
+ printf("RunDirTest: Error - failed to create monitor\n");
+ dirtest_failed = 1;
+ return -1;
+ }
+ thrarg.mon = mon;
+
+ for (i = 0; i < NUM_DIRTEST_THREADS; i++) {
+
+ thrarg.done= 0;
+ t = create_new_thread(PR_USER_THREAD,
+ DirTest, &thrarg,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0, i);
+ if (!t) {
+ printf("RunDirTest: Error - failed to create thread\n");
+ dirtest_failed = 1;
+ return -1;
+ }
+ PR_EnterMonitor(mon);
+ while (!thrarg.done) {
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(mon);
+
+ }
+ PR_DestroyMonitor(mon);
+ return 0;
+}
+
+static PRInt32 PR_CALLBACK DirTest(void *arg)
+{
+ struct dirtest_arg *tinfo = (struct dirtest_arg *) arg;
+ PRFileDesc *fd_file;
+ PRDir *fd_dir;
+ int i;
+ int path_len;
+ PRDirEntry *dirEntry;
+ PRFileInfo info;
+ PRInt32 num_files = 0;
+#if defined(XP_PC) && defined(WIN32)
+ HANDLE hfile;
+#endif
+
+#define FILES_IN_DIR 20
+
+ /*
+ * Create Test dir
+ */
+ DPRINTF(("Creating test dir %s\n",TEST_DIR));
+ if ((PR_MkDir(TEST_DIR, 0777)) < 0) {
+ printf(
+ "testfile failed to create dir %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ fd_dir = PR_OpenDir(TEST_DIR);
+ if (fd_dir == NULL) {
+ printf(
+ "testfile failed to open dirctory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ strcpy(pathname, TEST_DIR);
+ strcat(pathname, "/");
+ strcat(pathname, FILE_NAME);
+ path_len = strlen(pathname);
+
+ for (i = 0; i < FILES_IN_DIR; i++) {
+
+ sprintf(pathname + path_len,"%d%s",i,"");
+
+ DPRINTF(("Creating test file %s\n",pathname));
+
+ fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777);
+
+ if (fd_file == NULL) {
+ printf(
+ "testfile failed to create/open file %s [%d, %d]\n",
+ pathname, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ PR_Close(fd_file);
+ }
+#if defined(XP_UNIX) || (defined(XP_PC) && defined(WIN32)) || defined(XP_OS2)
+ /*
+ * Create a hidden file - a platform-dependent operation
+ */
+ strcpy(pathname, TEST_DIR);
+ strcat(pathname, "/");
+ strcat(pathname, HIDDEN_FILE_NAME);
+#if defined(XP_UNIX)
+ DPRINTF(("Creating hidden test file %s\n",pathname));
+ fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777);
+
+ if (fd_file == NULL) {
+ printf(
+ "testfile failed to create/open hidden file %s [%d, %d]\n",
+ pathname, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ PR_Close(fd_file);
+
+#elif defined(WINCE)
+ DPRINTF(("Creating hidden test file %s\n",pathname));
+ MultiByteToWideChar(CP_ACP, 0, pathname, -1, wPathname, 256);
+ hfile = CreateFile(wPathname, GENERIC_READ,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ CREATE_NEW,
+ FILE_ATTRIBUTE_HIDDEN,
+ NULL);
+ if (hfile == INVALID_HANDLE_VALUE) {
+ printf("testfile failed to create/open hidden file %s [0, %d]\n",
+ pathname, GetLastError());
+ return -1;
+ }
+ CloseHandle(hfile);
+
+#elif defined(XP_PC) && defined(WIN32)
+ DPRINTF(("Creating hidden test file %s\n",pathname));
+ hfile = CreateFile(pathname, GENERIC_READ,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ CREATE_NEW,
+ FILE_ATTRIBUTE_HIDDEN,
+ NULL);
+ if (hfile == INVALID_HANDLE_VALUE) {
+ printf("testfile failed to create/open hidden file %s [0, %d]\n",
+ pathname, GetLastError());
+ return -1;
+ }
+ CloseHandle(hfile);
+
+#elif defined(OS2)
+ DPRINTF(("Creating hidden test file %s\n",pathname));
+ fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, (int)FILE_HIDDEN);
+
+ if (fd_file == NULL) {
+ printf("testfile failed to create/open hidden file %s [%d, %d]\n",
+ pathname, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ PR_Close(fd_file);
+#endif /* XP_UNIX */
+
+#endif /* XP_UNIX || (XP_PC && WIN32) */
+
+
+ if (PR_FAILURE == PR_CloseDir(fd_dir))
+ {
+ printf(
+ "testfile failed to close dirctory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ fd_dir = PR_OpenDir(TEST_DIR);
+ if (fd_dir == NULL) {
+ printf(
+ "testfile failed to reopen dirctory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ /*
+ * List all files, including hidden files
+ */
+ DPRINTF(("Listing all files in directory %s\n",TEST_DIR));
+#if defined(XP_UNIX) || (defined(XP_PC) && defined(WIN32)) || defined(XP_OS2)
+ num_files = FILES_IN_DIR + 1;
+#else
+ num_files = FILES_IN_DIR;
+#endif
+ while ((dirEntry = PR_ReadDir(fd_dir, PR_SKIP_BOTH)) != NULL) {
+ num_files--;
+ strcpy(pathname, TEST_DIR);
+ strcat(pathname, "/");
+ strcat(pathname, dirEntry->name);
+ DPRINTF(("\t%s\n",dirEntry->name));
+
+ if ((PR_GetFileInfo(pathname, &info)) < 0) {
+ printf(
+ "testfile failed to GetFileInfo file %s [%d, %d]\n",
+ pathname, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ if (info.type != PR_FILE_FILE) {
+ printf(
+ "testfile incorrect fileinfo for file %s [%d, %d]\n",
+ pathname, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ }
+ if (num_files != 0)
+ {
+ printf(
+ "testfile failed to find all files in directory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ PR_CloseDir(fd_dir);
+
+#if defined(XP_UNIX) || (defined(XP_PC) && defined(WIN32)) || defined(XP_OS2)
+
+ /*
+ * List all files, except hidden files
+ */
+
+ fd_dir = PR_OpenDir(TEST_DIR);
+ if (fd_dir == NULL) {
+ printf(
+ "testfile failed to reopen dirctory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ DPRINTF(("Listing non-hidden files in directory %s\n",TEST_DIR));
+ while ((dirEntry = PR_ReadDir(fd_dir, PR_SKIP_HIDDEN)) != NULL) {
+ DPRINTF(("\t%s\n",dirEntry->name));
+ if (!strcmp(HIDDEN_FILE_NAME, dirEntry->name)) {
+ printf("testfile found hidden file %s\n", pathname);
+ return -1;
+ }
+
+ }
+ /*
+ * Delete hidden file
+ */
+ strcpy(pathname, TEST_DIR);
+ strcat(pathname, "/");
+ strcat(pathname, HIDDEN_FILE_NAME);
+ if (PR_FAILURE == PR_Delete(pathname)) {
+ printf(
+ "testfile failed to delete hidden file %s [%d, %d]\n",
+ pathname, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ PR_CloseDir(fd_dir);
+#endif /* XP_UNIX || (XP_PC && WIN32) */
+
+ strcpy(renamename, TEST_DIR);
+ strcat(renamename, ".RENAMED");
+ if (PR_FAILURE == PR_Rename(TEST_DIR, renamename)) {
+ printf(
+ "testfile failed to rename directory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ if (PR_FAILURE == PR_MkDir(TEST_DIR, 0777)) {
+ printf(
+ "testfile failed to recreate dir %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ if (PR_SUCCESS == PR_Rename(renamename, TEST_DIR)) {
+ printf(
+ "testfile renamed directory to existing name %s\n",
+ renamename);
+ return -1;
+ }
+
+ if (PR_FAILURE == PR_RmDir(TEST_DIR)) {
+ printf(
+ "testfile failed to rmdir %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ if (PR_FAILURE == PR_Rename(renamename, TEST_DIR)) {
+ printf(
+ "testfile failed to rename directory %s [%d, %d]\n",
+ renamename, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ fd_dir = PR_OpenDir(TEST_DIR);
+ if (fd_dir == NULL) {
+ printf(
+ "testfile failed to reopen directory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ strcpy(pathname, TEST_DIR);
+ strcat(pathname, "/");
+ strcat(pathname, FILE_NAME);
+ path_len = strlen(pathname);
+
+ for (i = 0; i < FILES_IN_DIR; i++) {
+
+ sprintf(pathname + path_len,"%d%s",i,"");
+
+ if (PR_FAILURE == PR_Delete(pathname)) {
+ printf(
+ "testfile failed to delete file %s [%d, %d]\n",
+ pathname, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ }
+
+ PR_CloseDir(fd_dir);
+
+ if (PR_FAILURE == PR_RmDir(TEST_DIR)) {
+ printf(
+ "testfile failed to rmdir %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ PR_EnterMonitor(tinfo->mon);
+ tinfo->done = 1;
+ PR_Notify(tinfo->mon);
+ PR_ExitMonitor(tinfo->mon);
+
+ return 0;
+}
+/************************************************************************/
+
+/*
+ * Test file and directory NSPR APIs
+ */
+
+int main(int argc, char **argv)
+{
+#ifdef WIN32
+ PRUint32 len;
+#endif
+#if defined(XP_UNIX) || defined(XP_OS2)
+ int opt;
+ extern char *optarg;
+ extern int optind;
+#endif
+#if defined(XP_UNIX) || defined(XP_OS2)
+ while ((opt = getopt(argc, argv, "d")) != EOF) {
+ switch(opt) {
+ case 'd':
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+#endif
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ mon = PR_NewMonitor();
+ if (mon == NULL) {
+ printf("testfile: PR_NewMonitor failed\n");
+ exit(2);
+ }
+#ifdef WIN32
+
+#ifdef WINCE
+ {
+ WCHAR tdir[TMPDIR_LEN];
+ len = GetTempPath(TMPDIR_LEN, tdir);
+ if ((len > 0) && (len < (TMPDIR_LEN - 6))) {
+ /*
+ * enough space for prdir
+ */
+ WideCharToMultiByte(CP_ACP, 0, tdir, -1, testdir, TMPDIR_LEN, 0, 0);
+ }
+ }
+#else
+ len = GetTempPath(TMPDIR_LEN, testdir);
+#endif /* WINCE */
+
+ if ((len > 0) && (len < (TMPDIR_LEN - 6))) {
+ /*
+ * enough space for prdir
+ */
+ strcpy((testdir + len),"prdir");
+ TEST_DIR = testdir;
+ printf("TEST_DIR = %s\n",TEST_DIR);
+ }
+#endif /* WIN32 */
+
+ if (FileTest() < 0) {
+ printf("File Test failed\n");
+ exit(2);
+ }
+ printf("File Test passed\n");
+ if ((RunDirTest() < 0) || dirtest_failed) {
+ printf("Dir Test failed\n");
+ exit(2);
+ }
+ printf("Dir Test passed\n");
+
+ PR_DestroyMonitor(mon);
+ PR_Cleanup();
+ return 0;
+}
diff --git a/nsprpub/pr/tests/threads.c b/nsprpub/pr/tests/threads.c
new file mode 100644
index 0000000000..4a28dc6e58
--- /dev/null
+++ b/nsprpub/pr/tests/threads.c
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+#include "prinrval.h"
+#include "plgetopt.h"
+#include "pprthred.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRMonitor *mon;
+PRInt32 count, iterations, alive;
+
+PRBool debug_mode = PR_FALSE, passed = PR_TRUE;
+
+void
+PR_CALLBACK
+ReallyDumbThread(void *arg)
+{
+ return;
+}
+
+void
+PR_CALLBACK
+DumbThread(void *arg)
+{
+ PRInt32 tmp = (PRInt32)arg;
+ PRThreadScope scope = (PRThreadScope)tmp;
+ PRThread *thr;
+
+ thr = PR_CreateThread(PR_USER_THREAD,
+ ReallyDumbThread,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ if (!thr) {
+ if (debug_mode) {
+ printf("Could not create really dumb thread (%d, %d)!\n",
+ PR_GetError(), PR_GetOSError());
+ }
+ passed = PR_FALSE;
+ } else {
+ PR_JoinThread(thr);
+ }
+ PR_EnterMonitor(mon);
+ alive--;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+}
+
+static void CreateThreads(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *thr;
+ int n;
+
+ alive = 0;
+ mon = PR_NewMonitor();
+
+ alive = count;
+ for (n=0; n<count; n++) {
+ thr = PR_CreateThread(PR_USER_THREAD,
+ DumbThread,
+ (void *)scope2,
+ PR_PRIORITY_NORMAL,
+ scope1,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (!thr) {
+ if (debug_mode) {
+ printf("Could not create dumb thread (%d, %d)!\n",
+ PR_GetError(), PR_GetOSError());
+ }
+ passed = PR_FALSE;
+ alive--;
+ }
+
+ PR_Sleep(0);
+ }
+
+ /* Wait for all threads to exit */
+ PR_EnterMonitor(mon);
+ while (alive) {
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ PR_ExitMonitor(mon);
+ PR_DestroyMonitor(mon);
+}
+
+static void CreateThreadsUU(void)
+{
+ CreateThreads(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void CreateThreadsUK(void)
+{
+ CreateThreads(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+static void CreateThreadsKU(void)
+{
+ CreateThreads(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void CreateThreadsKK(void)
+{
+ CreateThreads(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ if (debug_mode)
+ {
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+ printf("%40s: %6.2f usec\n", msg, d / count);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int index;
+
+ PR_STDIO_INIT();
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_HIGH, 0);
+
+ {
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dc:i:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'c': /* loop counter */
+ count = atoi(opt->value);
+ break;
+ case 'i': /* loop counter */
+ iterations = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ }
+
+ if (0 == count) {
+ count = 50;
+ }
+ if (0 == iterations) {
+ iterations = 10;
+ }
+
+ if (debug_mode)
+ {
+ printf("\
+** Tests lots of thread creations. \n\
+** Create %ld native threads %ld times. \n\
+** Create %ld user threads %ld times \n", iterations,count,iterations,count);
+ }
+
+ for (index=0; index<iterations; index++) {
+ Measure(CreateThreadsUU, "Create user/user threads");
+ Measure(CreateThreadsUK, "Create user/native threads");
+ Measure(CreateThreadsKU, "Create native/user threads");
+ Measure(CreateThreadsKK, "Create native/native threads");
+ }
+
+ if (debug_mode) {
+ printf("\nNow switch to recycling threads \n\n");
+ }
+ PR_SetThreadRecycleMode(1);
+
+ for (index=0; index<iterations; index++) {
+ Measure(CreateThreadsUU, "Create user/user threads");
+ Measure(CreateThreadsUK, "Create user/native threads");
+ Measure(CreateThreadsKU, "Create native/user threads");
+ Measure(CreateThreadsKK, "Create native/native threads");
+ }
+
+
+ printf("%s\n", ((passed) ? "PASS" : "FAIL"));
+
+ PR_Cleanup();
+
+ if (passed) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
diff --git a/nsprpub/pr/tests/thrpool_client.c b/nsprpub/pr/tests/thrpool_client.c
new file mode 100644
index 0000000000..2c01a3c51d
--- /dev/null
+++ b/nsprpub/pr/tests/thrpool_client.c
@@ -0,0 +1,349 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: thrpool_client.c
+**
+** Description: Test threadpool functionality.
+**
+** Modification History:
+*/
+#include "primpl.h"
+
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#ifdef XP_UNIX
+#include <sys/mman.h>
+#endif
+#if defined(_PR_PTHREADS)
+#include <pthread.h>
+#endif
+
+#ifdef WIN32
+#include <process.h>
+#endif
+
+static int _debug_on = 0;
+static int server_port = -1;
+static char *program_name = NULL;
+
+#include "obsolete/prsem.h"
+
+#ifdef XP_PC
+#define mode_t int
+#endif
+
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+#define BUF_DATA_SIZE (2 * 1024)
+#define TCP_MESG_SIZE 1024
+#define NUM_TCP_CLIENTS 10 /* for a listen queue depth of 5 */
+
+#define NUM_TCP_CONNECTIONS_PER_CLIENT 10
+#define NUM_TCP_MESGS_PER_CONNECTION 10
+#define TCP_SERVER_PORT 10000
+
+static PRInt32 num_tcp_clients = NUM_TCP_CLIENTS;
+static PRInt32 num_tcp_connections_per_client = NUM_TCP_CONNECTIONS_PER_CLIENT;
+static PRInt32 tcp_mesg_size = TCP_MESG_SIZE;
+static PRInt32 num_tcp_mesgs_per_connection = NUM_TCP_MESGS_PER_CONNECTION;
+
+int failed_already=0;
+
+typedef struct buffer {
+ char data[BUF_DATA_SIZE];
+} buffer;
+
+PRNetAddr tcp_server_addr, udp_server_addr;
+
+typedef struct Client_Param {
+ PRNetAddr server_addr;
+ PRMonitor *exit_mon; /* monitor to signal on exit */
+ PRInt32 *exit_counter; /* counter to decrement, before exit */
+ PRInt32 datalen;
+} Client_Param;
+
+/*
+ * readn
+ * read data from sockfd into buf
+ */
+static PRInt32
+readn(PRFileDesc *sockfd, char *buf, int len)
+{
+ int rem;
+ int bytes;
+ int offset = 0;
+ PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
+
+ for (rem=len; rem; offset += bytes, rem -= bytes) {
+ DPRINTF(("thread = 0x%lx: calling PR_Recv, bytes = %d\n",
+ PR_GetCurrentThread(), rem));
+ bytes = PR_Recv(sockfd, buf + offset, rem, 0,
+ timeout);
+ DPRINTF(("thread = 0x%lx: returning from PR_Recv, bytes = %d\n",
+ PR_GetCurrentThread(), bytes));
+ if (bytes < 0) {
+ return -1;
+ }
+ }
+ return len;
+}
+
+/*
+ * writen
+ * write data from buf to sockfd
+ */
+static PRInt32
+writen(PRFileDesc *sockfd, char *buf, int len)
+{
+ int rem;
+ int bytes;
+ int offset = 0;
+
+ for (rem=len; rem; offset += bytes, rem -= bytes) {
+ DPRINTF(("thread = 0x%lx: calling PR_Send, bytes = %d\n",
+ PR_GetCurrentThread(), rem));
+ bytes = PR_Send(sockfd, buf + offset, rem, 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("thread = 0x%lx: returning from PR_Send, bytes = %d\n",
+ PR_GetCurrentThread(), bytes));
+ if (bytes <= 0) {
+ return -1;
+ }
+ }
+ return len;
+}
+
+/*
+ * TCP_Client
+ * Client job
+ * Connect to the server at the address specified in the argument.
+ * Fill in a buffer, write data to server, read it back and check
+ * for data corruption.
+ * Close the socket for server connection
+ */
+static void PR_CALLBACK
+TCP_Client(void *arg)
+{
+ Client_Param *cp = (Client_Param *) arg;
+ PRFileDesc *sockfd;
+ buffer *in_buf, *out_buf;
+ union PRNetAddr netaddr;
+ PRInt32 bytes, i, j;
+
+
+ DPRINTF(("TCP client started\n"));
+ bytes = cp->datalen;
+ out_buf = PR_NEW(buffer);
+ if (out_buf == NULL) {
+ fprintf(stderr,"%s: failed to alloc buffer struct\n", program_name);
+ failed_already=1;
+ return;
+ }
+ in_buf = PR_NEW(buffer);
+ if (in_buf == NULL) {
+ fprintf(stderr,"%s: failed to alloc buffer struct\n", program_name);
+ failed_already=1;
+ return;
+ }
+ netaddr.inet.family = cp->server_addr.inet.family;
+ netaddr.inet.port = cp->server_addr.inet.port;
+ netaddr.inet.ip = cp->server_addr.inet.ip;
+
+ for (i = 0; i < num_tcp_connections_per_client; i++) {
+ if ((sockfd = PR_OpenTCPSocket(PR_AF_INET)) == NULL) {
+ fprintf(stderr,"%s: PR_OpenTCPSocket failed\n", program_name);
+ failed_already=1;
+ return;
+ }
+
+ DPRINTF(("TCP client connecting to server:%d\n", server_port));
+ if (PR_Connect(sockfd, &netaddr,PR_INTERVAL_NO_TIMEOUT) < 0) {
+ fprintf(stderr, "PR_Connect failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ return;
+ }
+ for (j = 0; j < num_tcp_mesgs_per_connection; j++) {
+ /*
+ * fill in random data
+ */
+ memset(out_buf->data, ((PRInt32) (&netaddr)) + i + j, bytes);
+ /*
+ * write to server
+ */
+ if (writen(sockfd, out_buf->data, bytes) < bytes) {
+ fprintf(stderr,"%s: ERROR - TCP_Client:writen\n", program_name);
+ failed_already=1;
+ return;
+ }
+ /*
+ DPRINTF(("TCP Client [0x%lx]: out_buf = 0x%lx out_buf[0] = 0x%lx\n",
+ PR_GetCurrentThread(), out_buf, (*((int *) out_buf->data))));
+ */
+ if (readn(sockfd, in_buf->data, bytes) < bytes) {
+ fprintf(stderr,"%s: ERROR - TCP_Client:readn\n", program_name);
+ failed_already=1;
+ return;
+ }
+ /*
+ * verify the data read
+ */
+ if (memcmp(in_buf->data, out_buf->data, bytes) != 0) {
+ fprintf(stderr,"%s: ERROR - data corruption\n", program_name);
+ failed_already=1;
+ return;
+ }
+ }
+ /*
+ * shutdown reads and writes
+ */
+ if (PR_Shutdown(sockfd, PR_SHUTDOWN_BOTH) < 0) {
+ fprintf(stderr,"%s: ERROR - PR_Shutdown\n", program_name);
+ failed_already=1;
+ }
+ PR_Close(sockfd);
+ }
+
+ PR_DELETE(out_buf);
+ PR_DELETE(in_buf);
+
+ /*
+ * Decrement exit_counter and notify parent thread
+ */
+
+ PR_EnterMonitor(cp->exit_mon);
+ --(*cp->exit_counter);
+ PR_Notify(cp->exit_mon);
+ PR_ExitMonitor(cp->exit_mon);
+ DPRINTF(("TCP_Client exiting\n"));
+}
+
+/*
+ * TCP_Socket_Client_Server_Test - concurrent server test
+ *
+ * Each client connects to the server and sends a chunk of data
+ * For each connection, server reads the data
+ * from the client and sends it back to the client, unmodified.
+ * Each client checks that data received from server is same as the
+ * data it sent to the server.
+ *
+ */
+
+static PRInt32
+TCP_Socket_Client_Server_Test(void)
+{
+ int i;
+ Client_Param *cparamp;
+ PRMonitor *mon2;
+ PRInt32 datalen;
+ PRInt32 connections = 0;
+ PRThread *thr;
+
+ datalen = tcp_mesg_size;
+ connections = 0;
+
+ mon2 = PR_NewMonitor();
+ if (mon2 == NULL) {
+ fprintf(stderr,"%s: PR_NewMonitor failed\n", program_name);
+ failed_already=1;
+ return -1;
+ }
+
+ /*
+ * Start client jobs
+ */
+ cparamp = PR_NEW(Client_Param);
+ if (cparamp == NULL) {
+ fprintf(stderr,"%s: PR_NEW failed\n", program_name);
+ failed_already=1;
+ return -1;
+ }
+ cparamp->server_addr.inet.family = PR_AF_INET;
+ cparamp->server_addr.inet.port = PR_htons(server_port);
+ cparamp->server_addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+ cparamp->exit_mon = mon2;
+ cparamp->exit_counter = &connections;
+ cparamp->datalen = datalen;
+ for (i = 0; i < num_tcp_clients; i++) {
+ thr = PR_CreateThread(PR_USER_THREAD, TCP_Client, (void *)cparamp,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+ if (NULL == thr) {
+ fprintf(stderr,"%s: PR_CreateThread failed\n", program_name);
+ failed_already=1;
+ return -1;
+ }
+ PR_EnterMonitor(mon2);
+ connections++;
+ PR_ExitMonitor(mon2);
+ DPRINTF(("Created TCP client = 0x%lx\n", thr));
+ }
+ /* Wait for client jobs to exit */
+ PR_EnterMonitor(mon2);
+ while (0 != connections) {
+ PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("Client job count = %d\n", connections));
+ }
+ PR_ExitMonitor(mon2);
+ printf("%30s","TCP_Socket_Client_Server_Test:");
+ printf("%2ld Server %2ld Clients %2ld connections_per_client\n",1l,
+ num_tcp_clients, num_tcp_connections_per_client);
+ printf("%30s %2ld messages_per_connection %4ld bytes_per_message\n",":",
+ num_tcp_mesgs_per_connection, tcp_mesg_size);
+
+ PR_DELETE(cparamp);
+ return 0;
+}
+
+/************************************************************************/
+
+int main(int argc, char **argv)
+{
+ /*
+ * -d debug mode
+ */
+ PLOptStatus os;
+ PLOptState *opt;
+ program_name = argv[0];
+
+ opt = PL_CreateOptState(argc, argv, "dp:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ case 'p':
+ server_port = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ PR_SetConcurrency(4);
+
+ TCP_Socket_Client_Server_Test();
+
+ PR_Cleanup();
+ if (failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
diff --git a/nsprpub/pr/tests/thrpool_server.c b/nsprpub/pr/tests/thrpool_server.c
new file mode 100644
index 0000000000..ecae117a8e
--- /dev/null
+++ b/nsprpub/pr/tests/thrpool_server.c
@@ -0,0 +1,579 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: thrpool.c
+**
+** Description: Test threadpool functionality.
+**
+** Modification History:
+*/
+#include "primpl.h"
+
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#ifdef XP_UNIX
+#include <sys/mman.h>
+#endif
+#if defined(_PR_PTHREADS)
+#include <pthread.h>
+#endif
+
+/* for getcwd */
+#if defined(XP_UNIX) || defined (XP_OS2)
+#include <unistd.h>
+#elif defined(XP_PC)
+#include <direct.h>
+#endif
+
+#ifdef WIN32
+#include <process.h>
+#endif
+
+static int _debug_on = 0;
+static char *program_name = NULL;
+static void serve_client_write(void *arg);
+
+#include "obsolete/prsem.h"
+
+#ifdef XP_PC
+#define mode_t int
+#endif
+
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+
+#define BUF_DATA_SIZE (2 * 1024)
+#define TCP_MESG_SIZE 1024
+#define NUM_TCP_CLIENTS 10 /* for a listen queue depth of 5 */
+
+
+#define NUM_TCP_CONNECTIONS_PER_CLIENT 10
+#define NUM_TCP_MESGS_PER_CONNECTION 10
+#define TCP_SERVER_PORT 10000
+#define SERVER_MAX_BIND_COUNT 100
+
+#ifdef WINCE
+char *getcwd(char *buf, size_t size)
+{
+ wchar_t wpath[MAX_PATH];
+ _wgetcwd(wpath, MAX_PATH);
+ WideCharToMultiByte(CP_ACP, 0, wpath, -1, buf, size, 0, 0);
+}
+
+#define perror(s)
+#endif
+
+static PRInt32 num_tcp_clients = NUM_TCP_CLIENTS;
+static PRInt32 num_tcp_connections_per_client = NUM_TCP_CONNECTIONS_PER_CLIENT;
+static PRInt32 tcp_mesg_size = TCP_MESG_SIZE;
+static PRInt32 num_tcp_mesgs_per_connection = NUM_TCP_MESGS_PER_CONNECTION;
+static void TCP_Server_Accept(void *arg);
+
+
+int failed_already=0;
+typedef struct buffer {
+ char data[BUF_DATA_SIZE];
+} buffer;
+
+
+typedef struct Server_Param {
+ PRJobIoDesc iod; /* socket to read from/write to */
+ PRInt32 datalen; /* bytes of data transfered in each read/write */
+ PRNetAddr netaddr;
+ PRMonitor *exit_mon; /* monitor to signal on exit */
+ PRInt32 *job_counterp; /* counter to decrement, before exit */
+ PRInt32 conn_counter; /* counter to decrement, before exit */
+ PRThreadPool *tp;
+} Server_Param;
+
+typedef struct Serve_Client_Param {
+ PRJobIoDesc iod; /* socket to read from/write to */
+ PRInt32 datalen; /* bytes of data transfered in each read/write */
+ PRMonitor *exit_mon; /* monitor to signal on exit */
+ PRInt32 *job_counterp; /* counter to decrement, before exit */
+ PRThreadPool *tp;
+} Serve_Client_Param;
+
+typedef struct Session {
+ PRJobIoDesc iod; /* socket to read from/write to */
+ buffer *in_buf;
+ PRInt32 bytes;
+ PRInt32 msg_num;
+ PRInt32 bytes_read;
+ PRMonitor *exit_mon; /* monitor to signal on exit */
+ PRInt32 *job_counterp; /* counter to decrement, before exit */
+ PRThreadPool *tp;
+} Session;
+
+static void
+serve_client_read(void *arg)
+{
+ Session *sp = (Session *) arg;
+ int rem;
+ int bytes;
+ int offset;
+ PRFileDesc *sockfd;
+ char *buf;
+ PRJob *jobp;
+
+ PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
+
+ sockfd = sp->iod.socket;
+ buf = sp->in_buf->data;
+
+ PR_ASSERT(sp->msg_num < num_tcp_mesgs_per_connection);
+ PR_ASSERT(sp->bytes_read < sp->bytes);
+
+ offset = sp->bytes_read;
+ rem = sp->bytes - offset;
+ bytes = PR_Recv(sockfd, buf + offset, rem, 0, timeout);
+ if (bytes < 0) {
+ return;
+ }
+ sp->bytes_read += bytes;
+ sp->iod.timeout = PR_SecondsToInterval(60);
+ if (sp->bytes_read < sp->bytes) {
+ jobp = PR_QueueJob_Read(sp->tp, &sp->iod, serve_client_read, sp,
+ PR_FALSE);
+ PR_ASSERT(NULL != jobp);
+ return;
+ }
+ PR_ASSERT(sp->bytes_read == sp->bytes);
+ DPRINTF(("serve_client: read complete, msg(%d) \n", sp->msg_num));
+
+ sp->iod.timeout = PR_SecondsToInterval(60);
+ jobp = PR_QueueJob_Write(sp->tp, &sp->iod, serve_client_write, sp,
+ PR_FALSE);
+ PR_ASSERT(NULL != jobp);
+
+ return;
+}
+
+static void
+serve_client_write(void *arg)
+{
+ Session *sp = (Session *) arg;
+ int bytes;
+ PRFileDesc *sockfd;
+ char *buf;
+ PRJob *jobp;
+
+ sockfd = sp->iod.socket;
+ buf = sp->in_buf->data;
+
+ PR_ASSERT(sp->msg_num < num_tcp_mesgs_per_connection);
+
+ bytes = PR_Send(sockfd, buf, sp->bytes, 0, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(bytes == sp->bytes);
+
+ if (bytes < 0) {
+ return;
+ }
+ DPRINTF(("serve_client: write complete, msg(%d) \n", sp->msg_num));
+ sp->msg_num++;
+ if (sp->msg_num < num_tcp_mesgs_per_connection) {
+ sp->bytes_read = 0;
+ sp->iod.timeout = PR_SecondsToInterval(60);
+ jobp = PR_QueueJob_Read(sp->tp, &sp->iod, serve_client_read, sp,
+ PR_FALSE);
+ PR_ASSERT(NULL != jobp);
+ return;
+ }
+
+ DPRINTF(("serve_client: read/write complete, msg(%d) \n", sp->msg_num));
+ if (PR_Shutdown(sockfd, PR_SHUTDOWN_BOTH) < 0) {
+ fprintf(stderr,"%s: ERROR - PR_Shutdown\n", program_name);
+ }
+
+ PR_Close(sockfd);
+ PR_EnterMonitor(sp->exit_mon);
+ --(*sp->job_counterp);
+ PR_Notify(sp->exit_mon);
+ PR_ExitMonitor(sp->exit_mon);
+
+ PR_DELETE(sp->in_buf);
+ PR_DELETE(sp);
+
+ return;
+}
+
+/*
+ * Serve_Client
+ * Thread, started by the server, for serving a client connection.
+ * Reads data from socket and writes it back, unmodified, and
+ * closes the socket
+ */
+static void PR_CALLBACK
+Serve_Client(void *arg)
+{
+ Serve_Client_Param *scp = (Serve_Client_Param *) arg;
+ buffer *in_buf;
+ Session *sp;
+ PRJob *jobp;
+
+ sp = PR_NEW(Session);
+ sp->iod = scp->iod;
+
+ in_buf = PR_NEW(buffer);
+ if (in_buf == NULL) {
+ fprintf(stderr,"%s: failed to alloc buffer struct\n",program_name);
+ failed_already=1;
+ return;
+ }
+
+ sp->in_buf = in_buf;
+ sp->bytes = scp->datalen;
+ sp->msg_num = 0;
+ sp->bytes_read = 0;
+ sp->tp = scp->tp;
+ sp->exit_mon = scp->exit_mon;
+ sp->job_counterp = scp->job_counterp;
+
+ sp->iod.timeout = PR_SecondsToInterval(60);
+ jobp = PR_QueueJob_Read(sp->tp, &sp->iod, serve_client_read, sp,
+ PR_FALSE);
+ PR_ASSERT(NULL != jobp);
+ PR_DELETE(scp);
+}
+
+static void
+print_stats(void *arg)
+{
+ Server_Param *sp = (Server_Param *) arg;
+ PRThreadPool *tp = sp->tp;
+ PRInt32 counter;
+ PRJob *jobp;
+
+ PR_EnterMonitor(sp->exit_mon);
+ counter = (*sp->job_counterp);
+ PR_ExitMonitor(sp->exit_mon);
+
+ printf("PRINT_STATS: #client connections = %d\n",counter);
+
+
+ jobp = PR_QueueJob_Timer(tp, PR_MillisecondsToInterval(500),
+ print_stats, sp, PR_FALSE);
+
+ PR_ASSERT(NULL != jobp);
+}
+
+static int job_counter = 0;
+/*
+ * TCP Server
+ * Server binds an address to a socket, starts a client process and
+ * listens for incoming connections.
+ * Each client connects to the server and sends a chunk of data
+ * Starts a Serve_Client job for each incoming connection, to read
+ * the data from the client and send it back to the client, unmodified.
+ * Each client checks that data received from server is same as the
+ * data it sent to the server.
+ * Finally, the threadpool is shutdown
+ */
+static void PR_CALLBACK
+TCP_Server(void *arg)
+{
+ PRThreadPool *tp = (PRThreadPool *) arg;
+ Server_Param *sp;
+ PRFileDesc *sockfd;
+ PRNetAddr netaddr;
+ PRMonitor *sc_mon;
+ PRJob *jobp;
+ int i;
+ PRStatus rval;
+
+ /*
+ * Create a tcp socket
+ */
+ if ((sockfd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"%s: PR_NewTCPSocket failed\n", program_name);
+ return;
+ }
+ memset(&netaddr, 0, sizeof(netaddr));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
+ netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ /*
+ * try a few times to bind server's address, if addresses are in
+ * use
+ */
+ i = 0;
+ while (PR_Bind(sockfd, &netaddr) < 0) {
+ if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
+ netaddr.inet.port += 2;
+ if (i++ < SERVER_MAX_BIND_COUNT) {
+ continue;
+ }
+ }
+ fprintf(stderr,"%s: ERROR - PR_Bind failed\n", program_name);
+ perror("PR_Bind");
+ failed_already=1;
+ return;
+ }
+
+ if (PR_Listen(sockfd, 32) < 0) {
+ fprintf(stderr,"%s: ERROR - PR_Listen failed\n", program_name);
+ failed_already=1;
+ return;
+ }
+
+ if (PR_GetSockName(sockfd, &netaddr) < 0) {
+ fprintf(stderr,"%s: ERROR - PR_GetSockName failed\n", program_name);
+ failed_already=1;
+ return;
+ }
+
+ DPRINTF((
+ "TCP_Server: PR_BIND netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n",
+ netaddr.inet.ip, netaddr.inet.port));
+
+ sp = PR_NEW(Server_Param);
+ if (sp == NULL) {
+ fprintf(stderr,"%s: PR_NEW failed\n", program_name);
+ failed_already=1;
+ return;
+ }
+ sp->iod.socket = sockfd;
+ sp->iod.timeout = PR_SecondsToInterval(60);
+ sp->datalen = tcp_mesg_size;
+ sp->exit_mon = sc_mon;
+ sp->job_counterp = &job_counter;
+ sp->conn_counter = 0;
+ sp->tp = tp;
+ sp->netaddr = netaddr;
+
+ /* create and cancel an io job */
+ jobp = PR_QueueJob_Accept(tp, &sp->iod, TCP_Server_Accept, sp,
+ PR_FALSE);
+ PR_ASSERT(NULL != jobp);
+ rval = PR_CancelJob(jobp);
+ PR_ASSERT(PR_SUCCESS == rval);
+
+ /*
+ * create the client process
+ */
+ {
+#define MAX_ARGS 4
+ char *argv[MAX_ARGS + 1];
+ int index = 0;
+ char port[32];
+ char path[1024 + sizeof("/thrpool_client")];
+
+ getcwd(path, sizeof(path));
+
+ (void)strcat(path, "/thrpool_client");
+#ifdef XP_PC
+ (void)strcat(path, ".exe");
+#endif
+ argv[index++] = path;
+ sprintf(port,"%d",PR_ntohs(netaddr.inet.port));
+ if (_debug_on)
+ {
+ argv[index++] = "-d";
+ argv[index++] = "-p";
+ argv[index++] = port;
+ argv[index++] = NULL;
+ } else {
+ argv[index++] = "-p";
+ argv[index++] = port;
+ argv[index++] = NULL;
+ }
+ PR_ASSERT(MAX_ARGS >= (index - 1));
+
+ DPRINTF(("creating client process %s ...\n", path));
+ if (PR_FAILURE == PR_CreateProcessDetached(path, argv, NULL, NULL)) {
+ fprintf(stderr,
+ "thrpool_server: ERROR - PR_CreateProcessDetached failed\n");
+ failed_already=1;
+ return;
+ }
+ }
+
+ sc_mon = PR_NewMonitor();
+ if (sc_mon == NULL) {
+ fprintf(stderr,"%s: PR_NewMonitor failed\n", program_name);
+ failed_already=1;
+ return;
+ }
+
+ sp->iod.socket = sockfd;
+ sp->iod.timeout = PR_SecondsToInterval(60);
+ sp->datalen = tcp_mesg_size;
+ sp->exit_mon = sc_mon;
+ sp->job_counterp = &job_counter;
+ sp->conn_counter = 0;
+ sp->tp = tp;
+ sp->netaddr = netaddr;
+
+ /* create and cancel a timer job */
+ jobp = PR_QueueJob_Timer(tp, PR_MillisecondsToInterval(5000),
+ print_stats, sp, PR_FALSE);
+ PR_ASSERT(NULL != jobp);
+ rval = PR_CancelJob(jobp);
+ PR_ASSERT(PR_SUCCESS == rval);
+
+ DPRINTF(("TCP_Server: Accepting connections \n"));
+
+ jobp = PR_QueueJob_Accept(tp, &sp->iod, TCP_Server_Accept, sp,
+ PR_FALSE);
+ PR_ASSERT(NULL != jobp);
+ return;
+}
+
+static void
+TCP_Server_Accept(void *arg)
+{
+ Server_Param *sp = (Server_Param *) arg;
+ PRThreadPool *tp = sp->tp;
+ Serve_Client_Param *scp;
+ PRFileDesc *newsockfd;
+ PRJob *jobp;
+
+ if ((newsockfd = PR_Accept(sp->iod.socket, &sp->netaddr,
+ PR_INTERVAL_NO_TIMEOUT)) == NULL) {
+ fprintf(stderr,"%s: ERROR - PR_Accept failed\n", program_name);
+ failed_already=1;
+ goto exit;
+ }
+ scp = PR_NEW(Serve_Client_Param);
+ if (scp == NULL) {
+ fprintf(stderr,"%s: PR_NEW failed\n", program_name);
+ failed_already=1;
+ goto exit;
+ }
+
+ /*
+ * Start a Serve_Client job for each incoming connection
+ */
+ scp->iod.socket = newsockfd;
+ scp->iod.timeout = PR_SecondsToInterval(60);
+ scp->datalen = tcp_mesg_size;
+ scp->exit_mon = sp->exit_mon;
+ scp->job_counterp = sp->job_counterp;
+ scp->tp = sp->tp;
+
+ PR_EnterMonitor(sp->exit_mon);
+ (*sp->job_counterp)++;
+ PR_ExitMonitor(sp->exit_mon);
+ jobp = PR_QueueJob(tp, Serve_Client, scp,
+ PR_FALSE);
+
+ PR_ASSERT(NULL != jobp);
+ DPRINTF(("TCP_Server: Created Serve_Client = 0x%lx\n", jobp));
+
+ /*
+ * single-threaded update; no lock needed
+ */
+ sp->conn_counter++;
+ if (sp->conn_counter <
+ (num_tcp_clients * num_tcp_connections_per_client)) {
+ jobp = PR_QueueJob_Accept(tp, &sp->iod, TCP_Server_Accept, sp,
+ PR_FALSE);
+ PR_ASSERT(NULL != jobp);
+ return;
+ }
+ jobp = PR_QueueJob_Timer(tp, PR_MillisecondsToInterval(500),
+ print_stats, sp, PR_FALSE);
+
+ PR_ASSERT(NULL != jobp);
+ DPRINTF(("TCP_Server: Created print_stats timer job = 0x%lx\n", jobp));
+
+exit:
+ PR_EnterMonitor(sp->exit_mon);
+ /* Wait for server jobs to finish */
+ while (0 != *sp->job_counterp) {
+ PR_Wait(sp->exit_mon, PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("TCP_Server: conn_counter = %d\n",
+ *sp->job_counterp));
+ }
+
+ PR_ExitMonitor(sp->exit_mon);
+ if (sp->iod.socket) {
+ PR_Close(sp->iod.socket);
+ }
+ PR_DestroyMonitor(sp->exit_mon);
+ printf("%30s","TCP_Socket_Client_Server_Test:");
+ printf("%2ld Server %2ld Clients %2ld connections_per_client\n",1l,
+ num_tcp_clients, num_tcp_connections_per_client);
+ printf("%30s %2ld messages_per_connection %4ld bytes_per_message\n",":",
+ num_tcp_mesgs_per_connection, tcp_mesg_size);
+
+ DPRINTF(("%s: calling PR_ShutdownThreadPool\n", program_name));
+ PR_ShutdownThreadPool(sp->tp);
+ PR_DELETE(sp);
+}
+
+/************************************************************************/
+
+#define DEFAULT_INITIAL_THREADS 4
+#define DEFAULT_MAX_THREADS 100
+#define DEFAULT_STACKSIZE (512 * 1024)
+
+int main(int argc, char **argv)
+{
+ PRInt32 initial_threads = DEFAULT_INITIAL_THREADS;
+ PRInt32 max_threads = DEFAULT_MAX_THREADS;
+ PRInt32 stacksize = DEFAULT_STACKSIZE;
+ PRThreadPool *tp = NULL;
+ PRStatus rv;
+ PRJob *jobp;
+
+ /*
+ * -d debug mode
+ */
+ PLOptStatus os;
+ PLOptState *opt;
+
+ program_name = argv[0];
+ opt = PL_CreateOptState(argc, argv, "d");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ PR_SetConcurrency(4);
+
+ tp = PR_CreateThreadPool(initial_threads, max_threads, stacksize);
+ if (NULL == tp) {
+ printf("PR_CreateThreadPool failed\n");
+ failed_already=1;
+ goto done;
+ }
+ jobp = PR_QueueJob(tp, TCP_Server, tp, PR_TRUE);
+ rv = PR_JoinJob(jobp);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ DPRINTF(("%s: calling PR_JoinThreadPool\n", program_name));
+ rv = PR_JoinThreadPool(tp);
+ PR_ASSERT(PR_SUCCESS == rv);
+ DPRINTF(("%s: returning from PR_JoinThreadPool\n", program_name));
+
+done:
+ PR_Cleanup();
+ if (failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
diff --git a/nsprpub/pr/tests/thruput.c b/nsprpub/pr/tests/thruput.c
new file mode 100644
index 0000000000..156bbde9a3
--- /dev/null
+++ b/nsprpub/pr/tests/thruput.c
@@ -0,0 +1,414 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: thruput.c
+** Description: Test server's throughput capability comparing various
+** implmentation strategies.
+**
+** Note: Requires a server machine and an aribitrary number of
+** clients to bang on it. Trust the numbers on the server
+** more than those being displayed by the various clients.
+*/
+
+#include "prerror.h"
+#include "prinrval.h"
+#include "prinit.h"
+#include "prio.h"
+#include "prlock.h"
+#include "prmem.h"
+#include "prnetdb.h"
+#include "prprf.h"
+#include "prthread.h"
+#include "pprio.h"
+#include "plerror.h"
+#include "plgetopt.h"
+
+#define ADDR_BUFFER 100
+
+#ifdef DEBUG
+#define PORT_INC_DO +100
+#else
+#define PORT_INC_DO
+#endif
+#ifdef IS_64
+#define PORT_INC_3264 +200
+#else
+#define PORT_INC_3264
+#endif
+
+#define PORT_NUMBER 51877 PORT_INC_DO PORT_INC_3264
+
+#define SAMPLING_INTERVAL 10
+#define BUFFER_SIZE (32 * 1024)
+
+static PRInt32 domain = PR_AF_INET;
+static PRInt32 protocol = 6; /* TCP */
+static PRFileDesc *err = NULL;
+static PRIntn concurrency = 1;
+static PRInt32 xport_buffer = -1;
+static PRUint32 initial_streams = 1;
+static PRInt32 buffer_size = BUFFER_SIZE;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+typedef struct Shared
+{
+ PRLock *ml;
+ PRUint32 sampled;
+ PRUint32 threads;
+ PRIntervalTime timein;
+ PRNetAddr server_address;
+} Shared;
+
+static Shared *shared = NULL;
+
+static PRStatus PrintAddress(const PRNetAddr* address)
+{
+ char buffer[ADDR_BUFFER];
+ PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer));
+ if (PR_SUCCESS == rv) {
+ PR_fprintf(err, "%s:%u\n", buffer, PR_ntohs(address->inet.port));
+ }
+ else {
+ PL_FPrintError(err, "PR_NetAddrToString");
+ }
+ return rv;
+} /* PrintAddress */
+
+
+static void PR_CALLBACK Clientel(void *arg)
+{
+ PRStatus rv;
+ PRFileDesc *xport;
+ PRInt32 bytes, sampled;
+ PRIntervalTime now, interval;
+ PRBool do_display = PR_FALSE;
+ Shared *shared = (Shared*)arg;
+ char *buffer = (char*)PR_Malloc(buffer_size);
+ PRNetAddr *server_address = &shared->server_address;
+ PRIntervalTime connect_timeout = PR_SecondsToInterval(5);
+ PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL);
+
+ PR_fprintf(err, "Client connecting to ");
+ (void)PrintAddress(server_address);
+
+ do
+ {
+ xport = PR_Socket(domain, PR_SOCK_STREAM, protocol);
+ if (NULL == xport)
+ {
+ PL_FPrintError(err, "PR_Socket");
+ return;
+ }
+
+ if (xport_buffer != -1)
+ {
+ PRSocketOptionData data;
+ data.option = PR_SockOpt_RecvBufferSize;
+ data.value.recv_buffer_size = (PRSize)xport_buffer;
+ rv = PR_SetSocketOption(xport, &data);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err, "PR_SetSocketOption - ignored");
+ }
+ data.option = PR_SockOpt_SendBufferSize;
+ data.value.send_buffer_size = (PRSize)xport_buffer;
+ rv = PR_SetSocketOption(xport, &data);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err, "PR_SetSocketOption - ignored");
+ }
+ }
+
+ rv = PR_Connect(xport, server_address, connect_timeout);
+ if (PR_FAILURE == rv)
+ {
+ PL_FPrintError(err, "PR_Connect");
+ if (PR_IO_TIMEOUT_ERROR != PR_GetError()) {
+ PR_Sleep(connect_timeout);
+ }
+ PR_Close(xport); /* delete it and start over */
+ }
+ } while (PR_FAILURE == rv);
+
+ do
+ {
+ bytes = PR_Recv(
+ xport, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT);
+ PR_Lock(shared->ml);
+ now = PR_IntervalNow();
+ shared->sampled += bytes;
+ interval = now - shared->timein;
+ if (interval > sampling_interval)
+ {
+ sampled = shared->sampled;
+ shared->timein = now;
+ shared->sampled = 0;
+ do_display = PR_TRUE;
+ }
+ PR_Unlock(shared->ml);
+
+ if (do_display)
+ {
+ PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval);
+ PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate);
+ do_display = PR_FALSE;
+ }
+
+ } while (bytes > 0);
+} /* Clientel */
+
+static void Client(const char *server_name)
+{
+ PRStatus rv;
+ PRHostEnt host;
+ char buffer[PR_NETDB_BUF_SIZE];
+ PRIntervalTime dally = PR_SecondsToInterval(60);
+ PR_fprintf(err, "Translating the name %s\n", server_name);
+ rv = PR_GetHostByName(server_name, buffer, sizeof(buffer), &host);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err, "PR_GetHostByName");
+ }
+ else
+ {
+ if (PR_EnumerateHostEnt(
+ 0, &host, PORT_NUMBER, &shared->server_address) < 0) {
+ PL_FPrintError(err, "PR_EnumerateHostEnt");
+ }
+ else
+ {
+ do
+ {
+ shared->threads += 1;
+ (void)PR_CreateThread(
+ PR_USER_THREAD, Clientel, shared,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_UNJOINABLE_THREAD, 8 * 1024);
+ if (shared->threads == initial_streams)
+ {
+ PR_Sleep(dally);
+ initial_streams += 1;
+ }
+ } while (PR_TRUE);
+ }
+ }
+}
+
+static void PR_CALLBACK Servette(void *arg)
+{
+ PRInt32 bytes, sampled;
+ PRIntervalTime now, interval;
+ PRBool do_display = PR_FALSE;
+ PRFileDesc *client = (PRFileDesc*)arg;
+ char *buffer = (char*)PR_Malloc(buffer_size);
+ PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL);
+
+ if (xport_buffer != -1)
+ {
+ PRStatus rv;
+ PRSocketOptionData data;
+ data.option = PR_SockOpt_RecvBufferSize;
+ data.value.recv_buffer_size = (PRSize)xport_buffer;
+ rv = PR_SetSocketOption(client, &data);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err, "PR_SetSocketOption - ignored");
+ }
+ data.option = PR_SockOpt_SendBufferSize;
+ data.value.send_buffer_size = (PRSize)xport_buffer;
+ rv = PR_SetSocketOption(client, &data);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err, "PR_SetSocketOption - ignored");
+ }
+ }
+
+ do
+ {
+ bytes = PR_Send(
+ client, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT);
+
+ PR_Lock(shared->ml);
+ now = PR_IntervalNow();
+ shared->sampled += bytes;
+ interval = now - shared->timein;
+ if (interval > sampling_interval)
+ {
+ sampled = shared->sampled;
+ shared->timein = now;
+ shared->sampled = 0;
+ do_display = PR_TRUE;
+ }
+ PR_Unlock(shared->ml);
+
+ if (do_display)
+ {
+ PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval);
+ PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate);
+ do_display = PR_FALSE;
+ }
+ } while (bytes > 0);
+} /* Servette */
+
+static void Server(void)
+{
+ PRStatus rv;
+ PRNetAddr server_address, client_address;
+ PRFileDesc *xport = PR_Socket(domain, PR_SOCK_STREAM, protocol);
+
+ if (NULL == xport)
+ {
+ PL_FPrintError(err, "PR_Socket");
+ return;
+ }
+
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, PORT_NUMBER, &server_address);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err, "PR_InitializeNetAddr");
+ }
+ else
+ {
+ rv = PR_Bind(xport, &server_address);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err, "PR_Bind");
+ }
+ else
+ {
+ PRFileDesc *client;
+ rv = PR_Listen(xport, 10);
+ PR_fprintf(err, "Server listening on ");
+ (void)PrintAddress(&server_address);
+ do
+ {
+ client = PR_Accept(
+ xport, &client_address, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == client) {
+ PL_FPrintError(err, "PR_Accept");
+ }
+ else
+ {
+ PR_fprintf(err, "Server accepting from ");
+ (void)PrintAddress(&client_address);
+ shared->threads += 1;
+ (void)PR_CreateThread(
+ PR_USER_THREAD, Servette, client,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_UNJOINABLE_THREAD, 8 * 1024);
+ }
+ } while (PR_TRUE);
+
+ }
+ }
+} /* Server */
+
+static void Help(void)
+{
+ PR_fprintf(err, "Usage: [-h] [<server>]\n");
+ PR_fprintf(err, "\t-s <n> Initial # of connections (default: 1)\n");
+ PR_fprintf(err, "\t-C <n> Set 'concurrency' (default: 1)\n");
+ PR_fprintf(err, "\t-b <nK> Client buffer size (default: 32k)\n");
+ PR_fprintf(err, "\t-B <nK> Transport recv/send buffer size (default: sys)\n");
+ PR_fprintf(err, "\t-G Use GLOBAL threads (default: LOCAL)\n");
+ PR_fprintf(err, "\t-X Use XTP transport (default: TCP)\n");
+ PR_fprintf(err, "\t-6 Use IPv6 (default: IPv4)\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+ PR_fprintf(err, "\t<server> DNS name of server\n");
+ PR_fprintf(err, "\t\tIf <server> is not specified, this host will be\n");
+ PR_fprintf(err, "\t\tthe server and not act as a client.\n");
+} /* Help */
+
+int main(int argc, char **argv)
+{
+ PLOptStatus os;
+ const char *server_name = NULL;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hGX6C:b:s:B:");
+
+ err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 0: /* Name of server */
+ server_name = opt->value;
+ break;
+ case 'G': /* Globular threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'X': /* Use XTP as the transport */
+ protocol = 36;
+ break;
+ case '6': /* Use IPv6 */
+ domain = PR_AF_INET6;
+ break;
+ case 's': /* initial_streams */
+ initial_streams = atoi(opt->value);
+ break;
+ case 'C': /* concurrency */
+ concurrency = atoi(opt->value);
+ break;
+ case 'b': /* buffer size */
+ buffer_size = 1024 * atoi(opt->value);
+ break;
+ case 'B': /* buffer size */
+ xport_buffer = 1024 * atoi(opt->value);
+ break;
+ case 'h': /* user wants some guidance */
+ default:
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ shared = PR_NEWZAP(Shared);
+ shared->ml = PR_NewLock();
+
+ PR_fprintf(err,
+ "This machine is %s\n",
+ (NULL == server_name) ? "the SERVER" : "a CLIENT");
+
+ PR_fprintf(err,
+ "Transport being used is %s\n",
+ (6 == protocol) ? "TCP" : "XTP");
+
+ if (PR_GLOBAL_THREAD == thread_scope)
+ {
+ if (1 != concurrency)
+ {
+ PR_fprintf(err, " **Concurrency > 1 and GLOBAL threads!?!?\n");
+ PR_fprintf(err, " **Ignoring concurrency\n");
+ concurrency = 1;
+ }
+ }
+
+ if (1 != concurrency)
+ {
+ PR_SetConcurrency(concurrency);
+ PR_fprintf(err, "Concurrency set to %u\n", concurrency);
+ }
+
+ PR_fprintf(err,
+ "All threads will be %s\n",
+ (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL");
+
+ PR_fprintf(err, "Client buffer size will be %u\n", buffer_size);
+
+ if (-1 != xport_buffer)
+ PR_fprintf(
+ err, "Transport send & receive buffer size will be %u\n", xport_buffer);
+
+
+ if (NULL == server_name) {
+ Server();
+ }
+ else {
+ Client(server_name);
+ }
+
+ return 0;
+} /* main */
+
+/* thruput.c */
+
diff --git a/nsprpub/pr/tests/time.c b/nsprpub/pr/tests/time.c
new file mode 100644
index 0000000000..cdb3ef73a8
--- /dev/null
+++ b/nsprpub/pr/tests/time.c
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Program to test different ways to get the time; right now it is tuned
+ * only for solaris.
+ * solaris results (100000 iterations):
+ * time to get time with time(): 4.63 usec avg, 463 msec total
+ * time to get time with gethrtime(): 2.17 usec avg, 217 msec total
+ * time to get time with gettimeofday(): 1.25 usec avg, 125 msec total
+ *
+ *
+ */
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "prpriv.h"
+#include "prinrval.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#define DEFAULT_COUNT 100000
+PRInt32 count;
+
+time_t itime;
+hrtime_t ihrtime;
+
+void
+ftime_init()
+{
+ itime = time(NULL);
+ ihrtime = gethrtime();
+}
+
+time_t
+ftime()
+{
+ hrtime_t now = gethrtime();
+
+ return itime + ((now - ihrtime) / 1000000000ll);
+}
+
+static void timeTime(void)
+{
+ PRInt32 index = count;
+ time_t rv;
+
+ for (; index--;) {
+ rv = time(NULL);
+ }
+}
+
+static void timeGethrtime(void)
+{
+ PRInt32 index = count;
+ time_t rv;
+
+ for (; index--;) {
+ rv = ftime();
+ }
+}
+
+static void timeGettimeofday(void)
+{
+ PRInt32 index = count;
+ time_t rv;
+ struct timeval tp;
+
+ for (; index--;) {
+ rv = gettimeofday(&tp, NULL);
+ }
+}
+
+static void timePRTime32(void)
+{
+ PRInt32 index = count;
+ PRInt32 rv32;
+ PRTime q;
+ PRTime rv;
+
+ LL_I2L(q, 1000000);
+
+ for (; index--;) {
+ rv = PR_Now();
+ LL_DIV(rv, rv, q);
+ LL_L2I(rv32, rv);
+ }
+}
+
+static void timePRTime64(void)
+{
+ PRInt32 index = count;
+ PRTime rv;
+
+ for (; index--;) {
+ rv = PR_Now();
+ }
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+ PRInt32 tot;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+ tot = PR_IntervalToMilliseconds(stop-start);
+
+ if (debug_mode) {
+ printf("%40s: %6.2f usec avg, %d msec total\n", msg, d / count, tot);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (argc > 1) {
+ count = atoi(argv[1]);
+ } else {
+ count = DEFAULT_COUNT;
+ }
+
+ ftime_init();
+
+ Measure(timeTime, "time to get time with time()");
+ Measure(timeGethrtime, "time to get time with gethrtime()");
+ Measure(timeGettimeofday, "time to get time with gettimeofday()");
+ Measure(timePRTime32, "time to get time with PR_Time() (32bit)");
+ Measure(timePRTime64, "time to get time with PR_Time() (64bit)");
+
+ PR_Cleanup();
+ return 0;
+}
+
+
+
diff --git a/nsprpub/pr/tests/timemac.c b/nsprpub/pr/tests/timemac.c
new file mode 100644
index 0000000000..b0f8b17d8a
--- /dev/null
+++ b/nsprpub/pr/tests/timemac.c
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * file: timemac.c
+ * description: test time and date routines on the Mac
+ */
+#include <stdio.h>
+#include "prinit.h"
+#include "prtime.h"
+
+
+static char *dayOfWeek[] =
+{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "???" };
+static char *month[] =
+{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???"
+};
+
+static void printExplodedTime(const PRExplodedTime *et) {
+ PRInt32 totalOffset;
+ PRInt32 hourOffset, minOffset;
+ const char *sign;
+
+ /* Print day of the week, month, day, hour, minute, and second */
+ printf( "%s %s %ld %02ld:%02ld:%02ld ",
+ dayOfWeek[et->tm_wday], month[et->tm_month], et->tm_mday,
+ et->tm_hour, et->tm_min, et->tm_sec);
+
+ /* Print time zone */
+ totalOffset = et->tm_params.tp_gmt_offset + et->tm_params.tp_dst_offset;
+ if (totalOffset == 0) {
+ printf("UTC ");
+ } else {
+ sign = "";
+ if (totalOffset < 0) {
+ totalOffset = -totalOffset;
+ sign = "-";
+ }
+ hourOffset = totalOffset / 3600;
+ minOffset = (totalOffset % 3600) / 60;
+ printf("%s%02ld%02ld ", sign, hourOffset, minOffset);
+ }
+
+ /* Print year */
+ printf("%d", et->tm_year);
+}
+
+int main(int argc, char** argv)
+{
+ PR_STDIO_INIT();
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+
+
+ /*
+ *************************************************************
+ **
+ ** Testing PR_Now(), PR_ExplodeTime, and PR_ImplodeTime
+ ** on the current time
+ **
+ *************************************************************
+ */
+
+ {
+ PRTime t1, t2;
+ PRExplodedTime et;
+
+ printf("*********************************************\n");
+ printf("** **\n");
+ printf("** Testing PR_Now(), PR_ExplodeTime, and **\n");
+ printf("** PR_ImplodeTime on the current time **\n");
+ printf("** **\n");
+ printf("*********************************************\n\n");
+ t1 = PR_Now();
+
+ /* First try converting to UTC */
+
+ PR_ExplodeTime(t1, PR_GMTParameters, &et);
+ if (et.tm_params.tp_gmt_offset || et.tm_params.tp_dst_offset) {
+ printf("ERROR: UTC has nonzero gmt or dst offset.\n");
+ return 1;
+ }
+ printf("Current UTC is ");
+ printExplodedTime(&et);
+ printf("\n");
+
+ t2 = PR_ImplodeTime(&et);
+ if (LL_NE(t1, t2)) {
+ printf("ERROR: Explode and implode are NOT inverse.\n");
+ return 1;
+ }
+
+ /* Next, try converting to local (US Pacific) time */
+
+ PR_ExplodeTime(t1, PR_LocalTimeParameters, &et);
+ printf("Current local time is ");
+ printExplodedTime(&et);
+ printf("\n");
+ printf("GMT offset is %ld, DST offset is %ld\n",
+ et.tm_params.tp_gmt_offset, et.tm_params.tp_dst_offset);
+ t2 = PR_ImplodeTime(&et);
+ if (LL_NE(t1, t2)) {
+ printf("ERROR: Explode and implode are NOT inverse.\n");
+ return 1;
+ }
+ }
+
+ printf("Please examine the results\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/timetest.c b/nsprpub/pr/tests/timetest.c
new file mode 100644
index 0000000000..9f96025d2f
--- /dev/null
+++ b/nsprpub/pr/tests/timetest.c
@@ -0,0 +1,823 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * file: timetest.c
+ * description: test time and date routines
+ */
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prtime.h"
+#include "prprf.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int failed_already=0;
+PRBool debug_mode = PR_FALSE;
+
+static char *dayOfWeek[] =
+{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "???" };
+static char *month[] =
+{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???"
+};
+
+static void PrintExplodedTime(const PRExplodedTime *et) {
+ PRInt32 totalOffset;
+ PRInt32 hourOffset, minOffset;
+ const char *sign;
+
+ /* Print day of the week, month, day, hour, minute, and second */
+ if (debug_mode) printf("%s %s %ld %02ld:%02ld:%02ld ",
+ dayOfWeek[et->tm_wday], month[et->tm_month], et->tm_mday,
+ et->tm_hour, et->tm_min, et->tm_sec);
+
+ /* Print time zone */
+ totalOffset = et->tm_params.tp_gmt_offset + et->tm_params.tp_dst_offset;
+ if (totalOffset == 0) {
+ if (debug_mode) {
+ printf("UTC ");
+ }
+ } else {
+ sign = "+";
+ if (totalOffset < 0) {
+ totalOffset = -totalOffset;
+ sign = "-";
+ }
+ hourOffset = totalOffset / 3600;
+ minOffset = (totalOffset % 3600) / 60;
+ if (debug_mode) {
+ printf("%s%02ld%02ld ", sign, hourOffset, minOffset);
+ }
+ }
+
+ /* Print year */
+ if (debug_mode) {
+ printf("%hd", et->tm_year);
+ }
+}
+
+static int ExplodedTimeIsEqual(const PRExplodedTime *et1,
+ const PRExplodedTime *et2)
+{
+ if (et1->tm_usec == et2->tm_usec &&
+ et1->tm_sec == et2->tm_sec &&
+ et1->tm_min == et2->tm_min &&
+ et1->tm_hour == et2->tm_hour &&
+ et1->tm_mday == et2->tm_mday &&
+ et1->tm_month == et2->tm_month &&
+ et1->tm_year == et2->tm_year &&
+ et1->tm_wday == et2->tm_wday &&
+ et1->tm_yday == et2->tm_yday &&
+ et1->tm_params.tp_gmt_offset == et2->tm_params.tp_gmt_offset &&
+ et1->tm_params.tp_dst_offset == et2->tm_params.tp_dst_offset) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void
+testParseTimeString(PRTime t)
+{
+ PRExplodedTime et;
+ PRTime t2;
+ char timeString[128];
+ char buf[128];
+ PRInt32 totalOffset;
+ PRInt32 hourOffset, minOffset;
+ const char *sign;
+ PRInt64 usec_per_sec;
+
+ /* Truncate the microsecond part of PRTime */
+ LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
+ LL_DIV(t, t, usec_per_sec);
+ LL_MUL(t, t, usec_per_sec);
+
+ PR_ExplodeTime(t, PR_LocalTimeParameters, &et);
+
+ /* Print day of the week, month, day, hour, minute, and second */
+ PR_snprintf(timeString, 128, "%s %s %ld %02ld:%02ld:%02ld ",
+ dayOfWeek[et.tm_wday], month[et.tm_month], et.tm_mday,
+ et.tm_hour, et.tm_min, et.tm_sec);
+ /* Print time zone */
+ totalOffset = et.tm_params.tp_gmt_offset + et.tm_params.tp_dst_offset;
+ if (totalOffset == 0) {
+ strcat(timeString, "GMT "); /* I wanted to use "UTC" here, but
+ * PR_ParseTimeString doesn't
+ * understand "UTC". */
+ } else {
+ sign = "+";
+ if (totalOffset < 0) {
+ totalOffset = -totalOffset;
+ sign = "-";
+ }
+ hourOffset = totalOffset / 3600;
+ minOffset = (totalOffset % 3600) / 60;
+ PR_snprintf(buf, 128, "%s%02ld%02ld ", sign, hourOffset, minOffset);
+ strcat(timeString, buf);
+ }
+ /* Print year */
+ PR_snprintf(buf, 128, "%hd", et.tm_year);
+ strcat(timeString, buf);
+
+ if (PR_ParseTimeString(timeString, PR_FALSE, &t2) == PR_FAILURE) {
+ fprintf(stderr, "PR_ParseTimeString() failed\n");
+ exit(1);
+ }
+ if (LL_NE(t, t2)) {
+ fprintf(stderr, "PR_ParseTimeString() incorrect\n");
+ PR_snprintf(buf, 128, "t is %lld, t2 is %lld, time string is %s\n",
+ t, t2, timeString);
+ fprintf(stderr, "%s\n", buf);
+ exit(1);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt;
+
+ PR_STDIO_INIT();
+ opt = PL_CreateOptState(argc, argv, "d");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+
+ /* Testing zero PRTime (the epoch) */
+ {
+ PRTime t;
+ PRExplodedTime et;
+
+ LL_I2L(t, 0);
+ if (debug_mode) {
+ printf("The NSPR epoch is:\n");
+ }
+ PR_ExplodeTime(t, PR_LocalTimeParameters, &et);
+ PrintExplodedTime(&et);
+ if (debug_mode) {
+ printf("\n");
+ }
+ PR_ExplodeTime(t, PR_GMTParameters, &et);
+ PrintExplodedTime(&et);
+ if (debug_mode) {
+ printf("\n\n");
+ }
+ testParseTimeString(t);
+ }
+
+ /*
+ *************************************************************
+ **
+ ** Testing PR_Now(), PR_ExplodeTime, and PR_ImplodeTime
+ ** on the current time
+ **
+ *************************************************************
+ */
+
+ {
+ PRTime t1, t2;
+ PRExplodedTime et;
+
+ if (debug_mode) {
+ printf("*********************************************\n");
+ printf("** **\n");
+ printf("** Testing PR_Now(), PR_ExplodeTime, and **\n");
+ printf("** PR_ImplodeTime on the current time **\n");
+ printf("** **\n");
+ printf("*********************************************\n\n");
+ }
+ t1 = PR_Now();
+
+ /* First try converting to UTC */
+
+ PR_ExplodeTime(t1, PR_GMTParameters, &et);
+ if (et.tm_params.tp_gmt_offset || et.tm_params.tp_dst_offset) {
+ if (debug_mode) {
+ printf("ERROR: UTC has nonzero gmt or dst offset.\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return 1;
+ }
+ if (debug_mode) {
+ printf("Current UTC is ");
+ }
+ PrintExplodedTime(&et);
+ if (debug_mode) {
+ printf("\n");
+ }
+
+ t2 = PR_ImplodeTime(&et);
+ if (LL_NE(t1, t2)) {
+ if (debug_mode) {
+ printf("ERROR: Explode and implode are NOT inverse.\n");
+ }
+ else {
+ printf("FAIL\n");
+ }
+ return 1;
+ }
+
+ /* Next, try converting to local (US Pacific) time */
+
+ PR_ExplodeTime(t1, PR_LocalTimeParameters, &et);
+ if (debug_mode) {
+ printf("Current local time is ");
+ }
+ PrintExplodedTime(&et);
+ if (debug_mode) {
+ printf("\n");
+ }
+ if (debug_mode) printf("GMT offset is %ld, DST offset is %ld\n",
+ et.tm_params.tp_gmt_offset, et.tm_params.tp_dst_offset);
+ t2 = PR_ImplodeTime(&et);
+ if (LL_NE(t1, t2)) {
+ if (debug_mode) {
+ printf("ERROR: Explode and implode are NOT inverse.\n");
+ }
+ return 1;
+ }
+
+ if (debug_mode) {
+ printf("Please examine the results\n");
+ }
+ testParseTimeString(t1);
+ }
+
+
+ /*
+ *******************************************
+ **
+ ** Testing PR_NormalizeTime()
+ **
+ *******************************************
+ */
+
+ /* July 4, 2001 is Wednesday */
+ {
+ PRExplodedTime et;
+
+ if (debug_mode) {
+ printf("\n");
+ printf("**********************************\n");
+ printf("** **\n");
+ printf("** Testing PR_NormalizeTime() **\n");
+ printf("** **\n");
+ printf("**********************************\n\n");
+ }
+ et.tm_year = 2001;
+ et.tm_month = 7 - 1;
+ et.tm_mday = 4;
+ et.tm_hour = 0;
+ et.tm_min = 0;
+ et.tm_sec = 0;
+ et.tm_usec = 0;
+ et.tm_params = PR_GMTParameters(&et);
+
+ PR_NormalizeTime(&et, PR_GMTParameters);
+
+ if (debug_mode) {
+ printf("July 4, 2001 is %s.\n", dayOfWeek[et.tm_wday]);
+ }
+ if (et.tm_wday == 3) {
+ if (debug_mode) {
+ printf("PASS\n");
+ }
+ } else {
+ if (debug_mode) {
+ printf("ERROR: It should be Wednesday\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return 1;
+ }
+ testParseTimeString(PR_ImplodeTime(&et));
+
+ /* June 12, 1997 23:00 PST == June 13, 1997 00:00 PDT */
+ et.tm_year = 1997;
+ et.tm_month = 6 - 1;
+ et.tm_mday = 12;
+ et.tm_hour = 23;
+ et.tm_min = 0;
+ et.tm_sec = 0;
+ et.tm_usec = 0;
+ et.tm_params.tp_gmt_offset = -8 * 3600;
+ et.tm_params.tp_dst_offset = 0;
+
+ PR_NormalizeTime(&et, PR_USPacificTimeParameters);
+
+ if (debug_mode) {
+ printf("Thu Jun 12, 1997 23:00:00 PST is ");
+ }
+ PrintExplodedTime(&et);
+ if (debug_mode) {
+ printf(".\n");
+ }
+ if (et.tm_wday == 5) {
+ if (debug_mode) {
+ printf("PASS\n");
+ }
+ } else {
+ if (debug_mode) {
+ printf("ERROR: It should be Friday\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return 1;
+ }
+ testParseTimeString(PR_ImplodeTime(&et));
+
+ /* Feb 14, 1997 00:00:00 PDT == Feb 13, 1997 23:00:00 PST */
+ et.tm_year = 1997;
+ et.tm_month = 2 - 1;
+ et.tm_mday = 14;
+ et.tm_hour = 0;
+ et.tm_min = 0;
+ et.tm_sec = 0;
+ et.tm_usec = 0;
+ et.tm_params.tp_gmt_offset = -8 * 3600;
+ et.tm_params.tp_dst_offset = 3600;
+
+ PR_NormalizeTime(&et, PR_USPacificTimeParameters);
+
+ if (debug_mode) {
+ printf("Fri Feb 14, 1997 00:00:00 PDT is ");
+ }
+ PrintExplodedTime(&et);
+ if (debug_mode) {
+ printf(".\n");
+ }
+ if (et.tm_wday == 4) {
+ if (debug_mode) {
+ printf("PASS\n");
+ }
+ } else {
+ if (debug_mode) {
+ printf("ERROR: It should be Thursday\n");
+ }
+ else {
+ failed_already=1;
+ }
+ return 1;
+ }
+ testParseTimeString(PR_ImplodeTime(&et));
+
+ /* What time is Nov. 7, 1996, 18:29:23 PDT? */
+ et.tm_year = 1996;
+ et.tm_month = 11 - 1;
+ et.tm_mday = 7;
+ et.tm_hour = 18;
+ et.tm_min = 29;
+ et.tm_sec = 23;
+ et.tm_usec = 0;
+ et.tm_params.tp_gmt_offset = -8 * 3600; /* PDT */
+ et.tm_params.tp_dst_offset = 3600;
+
+ PR_NormalizeTime(&et, PR_LocalTimeParameters);
+ if (debug_mode) {
+ printf("Nov 7 18:29:23 PDT 1996 is ");
+ }
+ PrintExplodedTime(&et);
+ if (debug_mode) {
+ printf(".\n");
+ }
+ testParseTimeString(PR_ImplodeTime(&et));
+
+ /* What time is Oct. 7, 1995, 18:29:23 PST? */
+ et.tm_year = 1995;
+ et.tm_month = 10 - 1;
+ et.tm_mday = 7;
+ et.tm_hour = 18;
+ et.tm_min = 29;
+ et.tm_sec = 23;
+ et.tm_params.tp_gmt_offset = -8 * 3600; /* PST */
+ et.tm_params.tp_dst_offset = 0;
+
+ PR_NormalizeTime(&et, PR_LocalTimeParameters);
+ if (debug_mode) {
+ printf("Oct 7 18:29:23 PST 1995 is ");
+ }
+ PrintExplodedTime(&et);
+ if (debug_mode) {
+ printf(".\n");
+ }
+ testParseTimeString(PR_ImplodeTime(&et));
+
+ if (debug_mode) {
+ printf("Please examine the results\n");
+ }
+ }
+
+ /*
+ **************************************************************
+ **
+ ** Testing range of years
+ **
+ **************************************************************
+ */
+
+ {
+ PRExplodedTime et1, et2;
+ PRTime ttt;
+ PRTime secs;
+
+ if (debug_mode) {
+ printf("\n");
+ printf("***************************************\n");
+ printf("** **\n");
+ printf("** Testing range of years **\n");
+ printf("** **\n");
+ printf("***************************************\n\n");
+ }
+ /* April 4, 1917 GMT */
+ et1.tm_usec = 0;
+ et1.tm_sec = 0;
+ et1.tm_min = 0;
+ et1.tm_hour = 0;
+ et1.tm_mday = 4;
+ et1.tm_month = 4 - 1;
+ et1.tm_year = 1917;
+ et1.tm_params = PR_GMTParameters(&et1);
+ PR_NormalizeTime(&et1, PR_LocalTimeParameters);
+ secs = PR_ImplodeTime(&et1);
+ if (LL_GE_ZERO(secs)) {
+ if (debug_mode) {
+ printf("ERROR: April 4, 1917 GMT returns a nonnegative second count\n");
+ }
+ failed_already = 1;
+ return 1;
+ }
+ PR_ExplodeTime(secs, PR_LocalTimeParameters, &et2);
+ if (!ExplodedTimeIsEqual(&et1, &et2)) {
+ if (debug_mode) {
+ printf("ERROR: PR_ImplodeTime and PR_ExplodeTime are not inverse for April 4, 1917 GMT\n");
+ }
+ failed_already=1;
+ return 1;
+ }
+ ttt = PR_ImplodeTime(&et1);
+ testParseTimeString( ttt );
+
+ if (debug_mode) {
+ printf("Test passed for April 4, 1917\n");
+ }
+
+ /* July 4, 2050 */
+ et1.tm_usec = 0;
+ et1.tm_sec = 0;
+ et1.tm_min = 0;
+ et1.tm_hour = 0;
+ et1.tm_mday = 4;
+ et1.tm_month = 7 - 1;
+ et1.tm_year = 2050;
+ et1.tm_params = PR_GMTParameters(&et1);
+ PR_NormalizeTime(&et1, PR_LocalTimeParameters);
+ secs = PR_ImplodeTime(&et1);
+ if (!LL_GE_ZERO(secs)) {
+ if (debug_mode) {
+ printf("ERROR: July 4, 2050 GMT returns a negative second count\n");
+ }
+ failed_already = 1;
+ return 1;
+ }
+ PR_ExplodeTime(secs, PR_LocalTimeParameters, &et2);
+ if (!ExplodedTimeIsEqual(&et1, &et2)) {
+ if (debug_mode) {
+ printf("ERROR: PR_ImplodeTime and PR_ExplodeTime are not inverse for July 4, 2050 GMT\n");
+ }
+ failed_already=1;
+ return 1;
+ }
+ testParseTimeString(PR_ImplodeTime(&et1));
+
+ if (debug_mode) {
+ printf("Test passed for July 4, 2050\n");
+ }
+
+ }
+
+ /*
+ **************************************************************
+ **
+ ** Stress test
+ *
+ ** Go through four years, starting from
+ ** 00:00:00 PST Jan. 1, 2005, incrementing
+ ** every 10 minutes.
+ **
+ **************************************************************
+ */
+
+ {
+ PRExplodedTime et, et1, et2;
+ PRInt64 usecPer10Min;
+ int day, hour, min;
+ PRTime usecs;
+ int dstInEffect = 0;
+
+ if (debug_mode) {
+ printf("\n");
+ printf("*******************************************************\n");
+ printf("** **\n");
+ printf("** Stress test Pacific Time **\n");
+ printf("** Starting from midnight Jan. 1, 2005 PST, **\n");
+ printf("** going through four years in 10-minute increment **\n");
+ printf("** **\n");
+ printf("*******************************************************\n\n");
+ }
+ LL_I2L(usecPer10Min, 600000000L);
+
+ /* 00:00:00 PST Jan. 1, 2005 */
+ et.tm_usec = 0;
+ et.tm_sec = 0;
+ et.tm_min = 0;
+ et.tm_hour = 0;
+ et.tm_mday = 1;
+ et.tm_month = 0;
+ et.tm_year = 2005;
+ et.tm_params.tp_gmt_offset = -8 * 3600;
+ et.tm_params.tp_dst_offset = 0;
+ usecs = PR_ImplodeTime(&et);
+
+ for (day = 0; day < 4 * 365 + 1; day++) {
+ for (hour = 0; hour < 24; hour++) {
+ for (min = 0; min < 60; min += 10) {
+ LL_ADD(usecs, usecs, usecPer10Min);
+ PR_ExplodeTime(usecs, PR_USPacificTimeParameters, &et1);
+
+ et2 = et;
+ et2.tm_usec += 600000000L;
+ PR_NormalizeTime(&et2, PR_USPacificTimeParameters);
+
+ if (!ExplodedTimeIsEqual(&et1, &et2)) {
+ printf("ERROR: componentwise comparison failed\n");
+ PrintExplodedTime(&et1);
+ printf("\n");
+ PrintExplodedTime(&et2);
+ printf("\n");
+ failed_already=1;
+ return 1;
+ }
+
+ if (LL_NE(usecs, PR_ImplodeTime(&et1))) {
+ printf("ERROR: PR_ExplodeTime and PR_ImplodeTime are not inverse\n");
+ PrintExplodedTime(&et1);
+ printf("\n");
+ failed_already=1;
+ return 1;
+ }
+ testParseTimeString(usecs);
+
+ if (!dstInEffect && et1.tm_params.tp_dst_offset) {
+ dstInEffect = 1;
+ if (debug_mode) {
+ printf("DST changeover from ");
+ PrintExplodedTime(&et);
+ printf(" to ");
+ PrintExplodedTime(&et1);
+ printf(".\n");
+ }
+ } else if (dstInEffect && !et1.tm_params.tp_dst_offset) {
+ dstInEffect = 0;
+ if (debug_mode) {
+ printf("DST changeover from ");
+ PrintExplodedTime(&et);
+ printf(" to ");
+ PrintExplodedTime(&et1);
+ printf(".\n");
+ }
+ }
+
+ et = et1;
+ }
+ }
+ }
+ if (debug_mode) {
+ printf("Test passed\n");
+ }
+ }
+
+
+ /* Same stress test, but with PR_LocalTimeParameters */
+
+ {
+ PRExplodedTime et, et1, et2;
+ PRInt64 usecPer10Min;
+ int day, hour, min;
+ PRTime usecs;
+ int dstInEffect = 0;
+
+ if (debug_mode) {
+ printf("\n");
+ printf("*******************************************************\n");
+ printf("** **\n");
+ printf("** Stress test Local Time **\n");
+ printf("** Starting from midnight Jan. 1, 2005 PST, **\n");
+ printf("** going through four years in 10-minute increment **\n");
+ printf("** **\n");
+ printf("*******************************************************\n\n");
+ }
+
+ LL_I2L(usecPer10Min, 600000000L);
+
+ /* 00:00:00 PST Jan. 1, 2005 */
+ et.tm_usec = 0;
+ et.tm_sec = 0;
+ et.tm_min = 0;
+ et.tm_hour = 0;
+ et.tm_mday = 1;
+ et.tm_month = 0;
+ et.tm_year = 2005;
+ et.tm_params.tp_gmt_offset = -8 * 3600;
+ et.tm_params.tp_dst_offset = 0;
+ usecs = PR_ImplodeTime(&et);
+
+ for (day = 0; day < 4 * 365 + 1; day++) {
+ for (hour = 0; hour < 24; hour++) {
+ for (min = 0; min < 60; min += 10) {
+ LL_ADD(usecs, usecs, usecPer10Min);
+ PR_ExplodeTime(usecs, PR_LocalTimeParameters, &et1);
+
+ et2 = et;
+ et2.tm_usec += 600000000L;
+ PR_NormalizeTime(&et2, PR_LocalTimeParameters);
+
+ if (!ExplodedTimeIsEqual(&et1, &et2)) {
+ printf("ERROR: componentwise comparison failed\n");
+ PrintExplodedTime(&et1);
+ printf("\n");
+ PrintExplodedTime(&et2);
+ printf("\n");
+ return 1;
+ }
+
+ if (LL_NE(usecs, PR_ImplodeTime(&et1))) {
+ printf("ERROR: PR_ExplodeTime and PR_ImplodeTime are not inverse\n");
+ PrintExplodedTime(&et1);
+ printf("\n");
+ failed_already=1;
+ return 1;
+ }
+ testParseTimeString(usecs);
+
+ if (!dstInEffect && et1.tm_params.tp_dst_offset) {
+ dstInEffect = 1;
+ if (debug_mode) {
+ printf("DST changeover from ");
+ PrintExplodedTime(&et);
+ printf(" to ");
+ PrintExplodedTime(&et1);
+ printf(".\n");
+ }
+ } else if (dstInEffect && !et1.tm_params.tp_dst_offset) {
+ dstInEffect = 0;
+ if (debug_mode) {
+ printf("DST changeover from ");
+ PrintExplodedTime(&et);
+ printf(" to ");
+ PrintExplodedTime(&et1);
+ printf(".\n");
+ }
+ }
+
+ et = et1;
+ }
+ }
+ }
+ if (debug_mode) {
+ printf("Test passed\n");
+ }
+ }
+
+ /* Same stress test, but with PR_LocalTimeParameters and going backward */
+
+ {
+ PRExplodedTime et, et1, et2;
+ PRInt64 usecPer10Min;
+ int day, hour, min;
+ PRTime usecs;
+ int dstInEffect = 0;
+
+ if (debug_mode) {
+ printf("\n");
+ printf("*******************************************************\n");
+ printf("** **\n");
+ printf("** Stress test Local Time **\n");
+ printf("** Starting from midnight Jan. 1, 2009 PST, **\n");
+ printf("** going back four years in 10-minute increment **\n");
+ printf("** **\n");
+ printf("*******************************************************\n\n");
+ }
+
+ LL_I2L(usecPer10Min, 600000000L);
+
+ /* 00:00:00 PST Jan. 1, 2009 */
+ et.tm_usec = 0;
+ et.tm_sec = 0;
+ et.tm_min = 0;
+ et.tm_hour = 0;
+ et.tm_mday = 1;
+ et.tm_month = 0;
+ et.tm_year = 2009;
+ et.tm_params.tp_gmt_offset = -8 * 3600;
+ et.tm_params.tp_dst_offset = 0;
+ usecs = PR_ImplodeTime(&et);
+
+ for (day = 0; day < 4 * 365 + 1; day++) {
+ for (hour = 0; hour < 24; hour++) {
+ for (min = 0; min < 60; min += 10) {
+ LL_SUB(usecs, usecs, usecPer10Min);
+ PR_ExplodeTime(usecs, PR_LocalTimeParameters, &et1);
+
+ et2 = et;
+ et2.tm_usec -= 600000000L;
+ PR_NormalizeTime(&et2, PR_LocalTimeParameters);
+
+ if (!ExplodedTimeIsEqual(&et1, &et2)) {
+ printf("ERROR: componentwise comparison failed\n");
+ PrintExplodedTime(&et1);
+ printf("\n");
+ PrintExplodedTime(&et2);
+ printf("\n");
+ return 1;
+ }
+
+ if (LL_NE(usecs, PR_ImplodeTime(&et1))) {
+ printf("ERROR: PR_ExplodeTime and PR_ImplodeTime are not inverse\n");
+ PrintExplodedTime(&et1);
+ printf("\n");
+ failed_already=1;
+ return 1;
+ }
+ testParseTimeString(usecs);
+
+ if (!dstInEffect && et1.tm_params.tp_dst_offset) {
+ dstInEffect = 1;
+ if (debug_mode) {
+ printf("DST changeover from ");
+ PrintExplodedTime(&et);
+ printf(" to ");
+ PrintExplodedTime(&et1);
+ printf(".\n");
+ }
+ } else if (dstInEffect && !et1.tm_params.tp_dst_offset) {
+ dstInEffect = 0;
+ if (debug_mode) {
+ printf("DST changeover from ");
+ PrintExplodedTime(&et);
+ printf(" to ");
+ PrintExplodedTime(&et1);
+ printf(".\n");
+ }
+ }
+
+ et = et1;
+ }
+ }
+ }
+ }
+
+ if (failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+}
diff --git a/nsprpub/pr/tests/tmoacc.c b/nsprpub/pr/tests/tmoacc.c
new file mode 100644
index 0000000000..d7e64cbea6
--- /dev/null
+++ b/nsprpub/pr/tests/tmoacc.c
@@ -0,0 +1,339 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+#ifdef DEBUG
+#define PORT_INC_DO +100
+#else
+#define PORT_INC_DO
+#endif
+#ifdef IS_64
+#define PORT_INC_3264 +200
+#else
+#define PORT_INC_3264
+#endif
+
+#define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264
+#define DEFAULT_THREADS 1
+#define DEFAULT_BACKLOG 10
+#define DEFAULT_TIMEOUT 10
+#define RANDOM_RANGE 100 /* should be significantly smaller than RAND_MAX */
+
+typedef enum {running, stopped} Status;
+
+typedef struct Shared
+{
+ PRLock *ml;
+ PRCondVar *cv;
+ PRBool passed;
+ PRBool random;
+ PRFileDesc *debug;
+ PRIntervalTime timeout;
+ PRFileDesc *listenSock;
+ Status status;
+} Shared;
+
+static PRIntervalTime Timeout(const Shared *shared)
+{
+ PRIntervalTime timeout = shared->timeout;
+ if (shared->random)
+ {
+ PRIntervalTime half = timeout >> 1; /* one half of the interval */
+ PRIntervalTime quarter = half >> 1; /* one quarter of the interval */
+ /* something in [0..timeout / 2) */
+ PRUint32 random = (rand() % RANDOM_RANGE) * half / RANDOM_RANGE;
+ timeout = (3 * quarter) + random; /* [75..125)% */
+ }
+ return timeout;
+} /* Timeout */
+
+static void Accept(void *arg)
+{
+ PRStatus rv;
+ char *buffer = NULL;
+ PRNetAddr clientAddr;
+ Shared *shared = (Shared*)arg;
+ PRInt32 recv_length = 0, flags = 0;
+ PRFileDesc *clientSock;
+ PRIntn toread, byte, bytes, loop = 0;
+ struct Descriptor {
+ PRInt32 length;
+ PRUint32 checksum;
+ } descriptor;
+
+ do
+ {
+ PRUint32 checksum = 0;
+ if (NULL != shared->debug) {
+ PR_fprintf(shared->debug, "[%d]accepting ... ", loop++);
+ }
+ clientSock = PR_Accept(
+ shared->listenSock, &clientAddr, Timeout(shared));
+ if (clientSock != NULL)
+ {
+ if (NULL != shared->debug) {
+ PR_fprintf(shared->debug, "reading length ... ");
+ }
+ bytes = PR_Recv(
+ clientSock, &descriptor, sizeof(descriptor),
+ flags, Timeout(shared));
+ if (sizeof(descriptor) == bytes)
+ {
+ /* and, before doing something stupid ... */
+ descriptor.length = PR_ntohl(descriptor.length);
+ descriptor.checksum = PR_ntohl(descriptor.checksum);
+ if (NULL != shared->debug) {
+ PR_fprintf(shared->debug, "%d bytes ... ", descriptor.length);
+ }
+ toread = descriptor.length;
+ if (recv_length < descriptor.length)
+ {
+ if (NULL != buffer) {
+ PR_DELETE(buffer);
+ }
+ buffer = (char*)PR_MALLOC(descriptor.length);
+ recv_length = descriptor.length;
+ }
+ for (toread = descriptor.length; toread > 0; toread -= bytes)
+ {
+ bytes = PR_Recv(
+ clientSock, &buffer[descriptor.length - toread],
+ toread, flags, Timeout(shared));
+ if (-1 == bytes)
+ {
+ if (NULL != shared->debug) {
+ PR_fprintf(shared->debug, "read data failed...");
+ }
+ bytes = 0;
+ }
+ }
+ }
+ else if (NULL != shared->debug)
+ {
+ PR_fprintf(shared->debug, "read desciptor failed...");
+ descriptor.length = -1;
+ }
+ if (NULL != shared->debug) {
+ PR_fprintf(shared->debug, "closing");
+ }
+ rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH);
+ if ((PR_FAILURE == rv) && (NULL != shared->debug))
+ {
+ PR_fprintf(shared->debug, " failed");
+ shared->passed = PR_FALSE;
+ }
+ rv = PR_Close(clientSock);
+ if (PR_FAILURE == rv) if (NULL != shared->debug)
+ {
+ PR_fprintf(shared->debug, " failed");
+ shared->passed = PR_FALSE;
+ }
+ if (descriptor.length > 0)
+ {
+ for (byte = 0; byte < descriptor.length; ++byte)
+ {
+ PRUint32 overflow = checksum & 0x80000000;
+ checksum = (checksum << 1);
+ if (0x00000000 != overflow) {
+ checksum += 1;
+ }
+ checksum += buffer[byte];
+ }
+ if ((descriptor.checksum != checksum) && (NULL != shared->debug))
+ {
+ PR_fprintf(shared->debug, " ... data mismatch");
+ shared->passed = PR_FALSE;
+ }
+ }
+ else if (0 == descriptor.length)
+ {
+ PR_Lock(shared->ml);
+ shared->status = stopped;
+ PR_NotifyCondVar(shared->cv);
+ PR_Unlock(shared->ml);
+ }
+ if (NULL != shared->debug) {
+ PR_fprintf(shared->debug, "\n");
+ }
+ }
+ else
+ {
+ if (PR_PENDING_INTERRUPT_ERROR != PR_GetError())
+ {
+ if (NULL != shared->debug) {
+ PL_PrintError("Accept");
+ }
+ shared->passed = PR_FALSE;
+ }
+ }
+ } while (running == shared->status);
+ if (NULL != buffer) {
+ PR_DELETE(buffer);
+ }
+} /* Accept */
+
+PRIntn Tmoacc(PRIntn argc, char **argv)
+{
+ PRStatus rv;
+ PRIntn exitStatus;
+ PRIntn index;
+ Shared *shared;
+ PLOptStatus os;
+ PRThread **thread;
+ PRNetAddr listenAddr;
+ PRSocketOptionData sockOpt;
+ PRIntn timeout = DEFAULT_TIMEOUT;
+ PRIntn threads = DEFAULT_THREADS;
+ PRIntn backlog = DEFAULT_BACKLOG;
+ PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dGb:t:T:R");
+
+ shared = PR_NEWZAP(Shared);
+
+ shared->debug = NULL;
+ shared->passed = PR_TRUE;
+ shared->random = PR_TRUE;
+ shared->status = running;
+ shared->ml = PR_NewLock();
+ shared->cv = PR_NewCondVar(shared->ml);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ shared->debug = PR_GetSpecialFD(PR_StandardError);
+ break;
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'b': /* size of listen backlog */
+ backlog = atoi(opt->value);
+ break;
+ case 't': /* number of threads doing accept */
+ threads = atoi(opt->value);
+ break;
+ case 'T': /* timeout used for network operations */
+ timeout = atoi(opt->value);
+ break;
+ case 'R': /* randomize the timeout values */
+ shared->random = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ if (0 == threads) {
+ threads = DEFAULT_THREADS;
+ }
+ if (0 == backlog) {
+ backlog = DEFAULT_BACKLOG;
+ }
+ if (0 == timeout) {
+ timeout = DEFAULT_TIMEOUT;
+ }
+
+ PR_STDIO_INIT();
+ memset(&listenAddr, 0, sizeof(listenAddr));
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, BASE_PORT, &listenAddr);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ shared->timeout = PR_SecondsToInterval(timeout);
+
+ /* First bind to the socket */
+ shared->listenSock = PR_NewTCPSocket();
+ if (shared->listenSock)
+ {
+ sockOpt.option = PR_SockOpt_Reuseaddr;
+ sockOpt.value.reuse_addr = PR_TRUE;
+ rv = PR_SetSocketOption(shared->listenSock, &sockOpt);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Bind(shared->listenSock, &listenAddr);
+ if (rv != PR_FAILURE)
+ {
+ rv = PR_Listen(shared->listenSock, threads + backlog);
+ if (PR_SUCCESS == rv)
+ {
+ thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*));
+ for (index = 0; index < threads; ++index)
+ {
+ thread[index] = PR_CreateThread(
+ PR_USER_THREAD, Accept, shared,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(NULL != thread[index]);
+ }
+
+ PR_Lock(shared->ml);
+ while (shared->status == running) {
+ PR_WaitCondVar(shared->cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(shared->ml);
+ for (index = 0; index < threads; ++index)
+ {
+ rv = PR_Interrupt(thread[index]);
+ PR_ASSERT(PR_SUCCESS== rv);
+ rv = PR_JoinThread(thread[index]);
+ PR_ASSERT(PR_SUCCESS== rv);
+ }
+ PR_DELETE(thread);
+ }
+ else
+ {
+ if (shared->debug) {
+ PL_PrintError("Listen");
+ }
+ shared->passed = PR_FALSE;
+ }
+ }
+ else
+ {
+ if (shared->debug) {
+ PL_PrintError("Bind");
+ }
+ shared->passed = PR_FALSE;
+ }
+
+ PR_Close(shared->listenSock);
+ }
+ else
+ {
+ if (shared->debug) {
+ PL_PrintError("Create");
+ }
+ shared->passed = PR_FALSE;
+ }
+
+ PR_DestroyCondVar(shared->cv);
+ PR_DestroyLock(shared->ml);
+
+ PR_fprintf(
+ PR_GetSpecialFD(PR_StandardError), "%s\n",
+ ((shared->passed) ? "PASSED" : "FAILED"));
+
+ exitStatus = (shared->passed) ? 0 : 1;
+ PR_DELETE(shared);
+ return exitStatus;
+}
+
+int main(int argc, char **argv)
+{
+ return (PR_VersionCheck(PR_VERSION)) ?
+ PR_Initialize(Tmoacc, argc, argv, 4) : -1;
+} /* main */
+
+/* tmoacc */
diff --git a/nsprpub/pr/tests/tmocon.c b/nsprpub/pr/tests/tmocon.c
new file mode 100644
index 0000000000..3d992c3443
--- /dev/null
+++ b/nsprpub/pr/tests/tmocon.c
@@ -0,0 +1,435 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/***********************************************************************
+**
+** Name: tmocon.c
+**
+** Description: test client socket connection.
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "pprio.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* for getcwd */
+#if defined(XP_UNIX) || defined (XP_OS2)
+#include <unistd.h>
+#elif defined(XP_PC)
+#include <direct.h>
+#endif
+
+#ifdef WINCE
+#include <windows.h>
+char *getcwd(char *buf, size_t size)
+{
+ wchar_t wpath[MAX_PATH];
+ _wgetcwd(wpath, MAX_PATH);
+ WideCharToMultiByte(CP_ACP, 0, wpath, -1, buf, size, 0, 0);
+}
+#endif
+
+#ifdef DEBUG
+#define PORT_INC_DO +100
+#else
+#define PORT_INC_DO
+#endif
+#ifdef IS_64
+#define PORT_INC_3264 +200
+#else
+#define PORT_INC_3264
+#endif
+
+#define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264
+
+#define DEFAULT_DALLY 1
+#define DEFAULT_THREADS 1
+#define DEFAULT_TIMEOUT 10
+#define DEFAULT_MESSAGES 100
+#define DEFAULT_MESSAGESIZE 100
+
+static PRFileDesc *debug_out = NULL;
+
+typedef struct Shared
+{
+ PRBool random;
+ PRBool failed;
+ PRBool intermittant;
+ PRIntn debug;
+ PRInt32 messages;
+ PRIntervalTime dally;
+ PRIntervalTime timeout;
+ PRInt32 message_length;
+ PRNetAddr serverAddress;
+} Shared;
+
+static PRIntervalTime Timeout(const Shared *shared)
+{
+ PRIntervalTime timeout = shared->timeout;
+ if (shared->random)
+ {
+ PRIntervalTime quarter = timeout >> 2; /* one quarter of the interval */
+ PRUint32 random = rand() % quarter; /* something in[0..timeout / 4) */
+ timeout = (((3 * quarter) + random) >> 2) + quarter; /* [75..125)% */
+ }
+ return timeout;
+} /* Timeout */
+
+static void CauseTimeout(const Shared *shared)
+{
+ if (shared->intermittant) {
+ PR_Sleep(Timeout(shared));
+ }
+} /* CauseTimeout */
+
+static PRStatus MakeReceiver(Shared *shared)
+{
+ PRStatus rv = PR_FAILURE;
+ if (PR_IsNetAddrType(&shared->serverAddress, PR_IpAddrLoopback))
+ {
+ char *argv[3];
+ char path[1024 + sizeof("/tmoacc")];
+
+ getcwd(path, sizeof(path));
+
+ (void)strcat(path, "/tmoacc");
+#ifdef XP_PC
+ (void)strcat(path, ".exe");
+#endif
+ argv[0] = path;
+ if (shared->debug > 0)
+ {
+ argv[1] = "-d";
+ argv[2] = NULL;
+ }
+ else {
+ argv[1] = NULL;
+ }
+ if (shared->debug > 1) {
+ PR_fprintf(debug_out, " creating accept process %s ...", path);
+ }
+ fflush(stdout);
+ rv = PR_CreateProcessDetached(path, argv, NULL, NULL);
+ if (PR_SUCCESS == rv)
+ {
+ if (shared->debug > 1) {
+ PR_fprintf(debug_out, " wait 5 seconds");
+ }
+ if (shared->debug > 1) {
+ PR_fprintf(debug_out, " before connecting to accept process ...");
+ }
+ fflush(stdout);
+ PR_Sleep(PR_SecondsToInterval(5));
+ return rv;
+ }
+ shared->failed = PR_TRUE;
+ if (shared->debug > 0) {
+ PL_FPrintError(debug_out, "PR_CreateProcessDetached failed");
+ }
+ }
+ return rv;
+} /* MakeReceiver */
+
+static void Connect(void *arg)
+{
+ PRStatus rv;
+ char *buffer = NULL;
+ PRFileDesc *clientSock;
+ Shared *shared = (Shared*)arg;
+ PRInt32 loop, bytes, flags = 0;
+ struct Descriptor {
+ PRInt32 length;
+ PRUint32 checksum;
+ } descriptor;
+ debug_out = (0 == shared->debug) ? NULL : PR_GetSpecialFD(PR_StandardError);
+
+ buffer = (char*)PR_MALLOC(shared->message_length);
+
+ for (bytes = 0; bytes < shared->message_length; ++bytes) {
+ buffer[bytes] = (char)bytes;
+ }
+
+ descriptor.checksum = 0;
+ for (bytes = 0; bytes < shared->message_length; ++bytes)
+ {
+ PRUint32 overflow = descriptor.checksum & 0x80000000;
+ descriptor.checksum = (descriptor.checksum << 1);
+ if (0x00000000 != overflow) {
+ descriptor.checksum += 1;
+ }
+ descriptor.checksum += buffer[bytes];
+ }
+ descriptor.checksum = PR_htonl(descriptor.checksum);
+
+ for (loop = 0; loop < shared->messages; ++loop)
+ {
+ if (shared->debug > 1) {
+ PR_fprintf(debug_out, "[%d]socket ... ", loop);
+ }
+ clientSock = PR_NewTCPSocket();
+ if (clientSock)
+ {
+ /*
+ * We need to slow down the rate of generating connect requests,
+ * otherwise the listen backlog queue on the accept side may
+ * become full and we will get connection refused or timeout
+ * error.
+ */
+
+ PR_Sleep(shared->dally);
+ if (shared->debug > 1)
+ {
+ char buf[128];
+ PR_NetAddrToString(&shared->serverAddress, buf, sizeof(buf));
+ PR_fprintf(debug_out, "connecting to %s ... ", buf);
+ }
+ rv = PR_Connect(
+ clientSock, &shared->serverAddress, Timeout(shared));
+ if (PR_SUCCESS == rv)
+ {
+ PRInt32 descriptor_length = (loop < (shared->messages - 1)) ?
+ shared->message_length : 0;
+ descriptor.length = PR_htonl(descriptor_length);
+ if (shared->debug > 1)
+ PR_fprintf(
+ debug_out, "sending %d bytes ... ", descriptor_length);
+ CauseTimeout(shared); /* might cause server to timeout */
+ bytes = PR_Send(
+ clientSock, &descriptor, sizeof(descriptor),
+ flags, Timeout(shared));
+ if (bytes != sizeof(descriptor))
+ {
+ shared->failed = PR_TRUE;
+ if (shared->debug > 0) {
+ PL_FPrintError(debug_out, "PR_Send failed");
+ }
+ }
+ if (0 != descriptor_length)
+ {
+ CauseTimeout(shared);
+ bytes = PR_Send(
+ clientSock, buffer, descriptor_length,
+ flags, Timeout(shared));
+ if (bytes != descriptor_length)
+ {
+ shared->failed = PR_TRUE;
+ if (shared->debug > 0) {
+ PL_FPrintError(debug_out, "PR_Send failed");
+ }
+ }
+ }
+ if (shared->debug > 1) {
+ PR_fprintf(debug_out, "closing ... ");
+ }
+ rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH);
+ rv = PR_Close(clientSock);
+ if (shared->debug > 1)
+ {
+ if (PR_SUCCESS == rv) {
+ PR_fprintf(debug_out, "\n");
+ }
+ else {
+ PL_FPrintError(debug_out, "shutdown failed");
+ }
+ }
+ }
+ else
+ {
+ if (shared->debug > 1) {
+ PL_FPrintError(debug_out, "connect failed");
+ }
+ PR_Close(clientSock);
+ if ((loop == 0) && (PR_GetError() == PR_CONNECT_REFUSED_ERROR))
+ {
+ if (MakeReceiver(shared) == PR_FAILURE) {
+ break;
+ }
+ }
+ else
+ {
+ if (shared->debug > 1) {
+ PR_fprintf(debug_out, " exiting\n");
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ shared->failed = PR_TRUE;
+ if (shared->debug > 0) {
+ PL_FPrintError(debug_out, "create socket");
+ }
+ break;
+ }
+ }
+
+ PR_DELETE(buffer);
+} /* Connect */
+
+int Tmocon(int argc, char **argv)
+{
+ /*
+ * USAGE
+ * -d turn on debugging output (default = off)
+ * -v turn on verbose output (default = off)
+ * -h <n> dns name of host serving the connection (default = self)
+ * -i dally intermittantly to cause timeouts (default = off)
+ * -m <n> number of messages to send (default = 100)
+ * -s <n> size of each message (default = 100)
+ * -t <n> number of threads sending (default = 1)
+ * -G use global threads (default = local)
+ * -T <n> timeout on I/O operations (seconds) (default = 10)
+ * -D <n> dally between connect requests (seconds)(default = 0)
+ * -R randomize the dally types around 'T' (default = no)
+ */
+
+ PRStatus rv;
+ int exitStatus;
+ PLOptStatus os;
+ Shared *shared = NULL;
+ PRThread **thread = NULL;
+ PRIntn index, threads = DEFAULT_THREADS;
+ PRThreadScope thread_scope = PR_LOCAL_THREAD;
+ PRInt32 dally = DEFAULT_DALLY, timeout = DEFAULT_TIMEOUT;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "divGRh:m:s:t:T:D:");
+
+ shared = PR_NEWZAP(Shared);
+
+ shared->debug = 0;
+ shared->failed = PR_FALSE;
+ shared->random = PR_FALSE;
+ shared->messages = DEFAULT_MESSAGES;
+ shared->message_length = DEFAULT_MESSAGESIZE;
+
+ PR_STDIO_INIT();
+ memset(&shared->serverAddress, 0, sizeof(shared->serverAddress));
+ rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &shared->serverAddress);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd':
+ if (0 == shared->debug) {
+ shared->debug = 1;
+ }
+ break;
+ case 'v':
+ if (0 == shared->debug) {
+ shared->debug = 2;
+ }
+ break;
+ case 'i':
+ shared->intermittant = PR_TRUE;
+ break;
+ case 'R':
+ shared->random = PR_TRUE;
+ break;
+ case 'G':
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'h': /* the value for backlock */
+ {
+ PRIntn es = 0;
+ PRHostEnt host;
+ char buffer[1024];
+ (void)PR_GetHostByName(
+ opt->value, buffer, sizeof(buffer), &host);
+ es = PR_EnumerateHostEnt(
+ es, &host, BASE_PORT, &shared->serverAddress);
+ PR_ASSERT(es > 0);
+ }
+ break;
+ case 'm': /* number of messages to send */
+ shared->messages = atoi(opt->value);
+ break;
+ case 't': /* number of threads sending */
+ threads = atoi(opt->value);
+ break;
+ case 'D': /* dally time between transmissions */
+ dally = atoi(opt->value);
+ break;
+ case 'T': /* timeout on I/O operations */
+ timeout = atoi(opt->value);
+ break;
+ case 's': /* total size of each message */
+ shared->message_length = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == timeout) {
+ timeout = DEFAULT_TIMEOUT;
+ }
+ if (0 == threads) {
+ threads = DEFAULT_THREADS;
+ }
+ if (0 == shared->messages) {
+ shared->messages = DEFAULT_MESSAGES;
+ }
+ if (0 == shared->message_length) {
+ shared->message_length = DEFAULT_MESSAGESIZE;
+ }
+
+ shared->dally = PR_SecondsToInterval(dally);
+ shared->timeout = PR_SecondsToInterval(timeout);
+
+ thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*));
+
+ for (index = 0; index < threads; ++index)
+ thread[index] = PR_CreateThread(
+ PR_USER_THREAD, Connect, shared,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 0);
+ for (index = 0; index < threads; ++index) {
+ rv = PR_JoinThread(thread[index]);
+ }
+
+ PR_DELETE(thread);
+
+ PR_fprintf(
+ PR_GetSpecialFD(PR_StandardError), "%s\n",
+ ((shared->failed) ? "FAILED" : "PASSED"));
+ exitStatus = (shared->failed) ? 1 : 0;
+ PR_DELETE(shared);
+ return exitStatus;
+}
+
+int main(int argc, char **argv)
+{
+ return (PR_VersionCheck(PR_VERSION)) ?
+ PR_Initialize(Tmocon, argc, argv, 4) : -1;
+} /* main */
+
+/* tmocon.c */
+
+
diff --git a/nsprpub/pr/tests/tpd.c b/nsprpub/pr/tests/tpd.c
new file mode 100644
index 0000000000..cac8c59aaa
--- /dev/null
+++ b/nsprpub/pr/tests/tpd.c
@@ -0,0 +1,308 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** File: tpd.c
+** Description: Exercising the thread private data bailywick.
+*/
+
+#include "prmem.h"
+#include "prinit.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prthread.h"
+#include "prtypes.h"
+
+#include "private/pprio.h"
+
+#include "plgetopt.h"
+
+static PRUintn key[128];
+static PRIntn debug = 0;
+static PRBool failed = PR_FALSE;
+static PRBool should = PR_TRUE;
+static PRBool did = PR_TRUE;
+static PRFileDesc *fout = NULL;
+
+static void PrintProgress(PRIntn line)
+{
+ failed = failed || (should && !did);
+ failed = failed || (!should && did);
+ if (debug > 0)
+ {
+#if defined(WIN16)
+ printf(
+ "@ line %d destructor should%s have been called and was%s\n",
+ line, ((should) ? "" : " NOT"), ((did) ? "" : " NOT"));
+#else
+ PR_fprintf(
+ fout, "@ line %d destructor should%s have been called and was%s\n",
+ line, ((should) ? "" : " NOT"), ((did) ? "" : " NOT"));
+#endif
+ }
+} /* PrintProgress */
+
+static void MyAssert(const char *expr, const char *file, PRIntn line)
+{
+ if (debug > 0) {
+ (void)PR_fprintf(fout, "'%s' in file: %s: %d\n", expr, file, line);
+ }
+} /* MyAssert */
+
+#define MY_ASSERT(_expr) \
+ ((_expr)?((void)0):MyAssert(# _expr,__FILE__,__LINE__))
+
+
+static void PR_CALLBACK Destructor(void *data)
+{
+ MY_ASSERT(NULL != data);
+ if (should) {
+ did = PR_TRUE;
+ }
+ else {
+ failed = PR_TRUE;
+ }
+ /*
+ * We don't actually free the storage since it's actually allocated
+ * on the stack. Normally, this would not be the case and this is
+ * the opportunity to free whatever.
+ PR_Free(data);
+ */
+} /* Destructor */
+
+static void PR_CALLBACK Thread(void *null)
+{
+ void *pd;
+ PRStatus rv;
+ PRUintn keys;
+ char *key_string[] = {
+ "Key #0", "Key #1", "Key #2", "Key #3",
+ "Bogus #5", "Bogus #6", "Bogus #7", "Bogus #8"
+ };
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 8; ++keys)
+ {
+ pd = PR_GetThreadPrivate(key[keys]);
+ MY_ASSERT(NULL == pd);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 4; keys < 8; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_FAILURE == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], "EXTENSION");
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+
+ /* put in keys and leave them there for thread exit */
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+ did = PR_FALSE; should = PR_TRUE;
+
+} /* Thread */
+
+static PRIntn PR_CALLBACK Tpd(PRIntn argc, char **argv)
+{
+ void *pd;
+ PRStatus rv;
+ PRUintn keys;
+ PRThread *thread;
+ char *key_string[] = {
+ "Key #0", "Key #1", "Key #2", "Key #3",
+ "Bogus #5", "Bogus #6", "Bogus #7", "Bogus #8"
+ };
+
+ fout = PR_STDOUT;
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_NewThreadPrivateIndex(&key[keys], Destructor);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 8; ++keys)
+ {
+ pd = PR_GetThreadPrivate(key[keys]);
+ MY_ASSERT(NULL == pd);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ for (keys = 4; keys < 8; ++keys) {
+ key[keys] = 4096; /* set to invalid value */
+ }
+ did = should = PR_FALSE;
+ for (keys = 4; keys < 8; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_FAILURE == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = PR_NewThreadPrivateIndex(&key[keys], Destructor);
+ MY_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetThreadPrivate(key[keys], "EXTENSION");
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+
+ thread = PR_CreateThread(
+ PR_USER_THREAD, Thread, NULL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ (void)PR_JoinThread(thread);
+
+ PrintProgress(__LINE__);
+
+#if defined(WIN16)
+ printf(
+ "%s\n",((PR_TRUE == failed) ? "FAILED" : "PASSED"));
+#else
+ (void)PR_fprintf(
+ fout, "%s\n",((PR_TRUE == failed) ? "FAILED" : "PASSED"));
+#endif
+
+ return 0;
+
+} /* Tpd */
+
+int main(int argc, char **argv)
+{
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dl:r:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ PR_STDIO_INIT();
+ return PR_Initialize(Tpd, argc, argv, 0);
+} /* main */
+
+/* tpd.c */
diff --git a/nsprpub/pr/tests/udpsrv.c b/nsprpub/pr/tests/udpsrv.c
new file mode 100644
index 0000000000..39a919c606
--- /dev/null
+++ b/nsprpub/pr/tests/udpsrv.c
@@ -0,0 +1,546 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*******************************************************************
+** udpsrc.c -- Test basic function of UDP server
+**
+** udpsrv operates on the same machine with program udpclt.
+** udpsrv is the server side of a udp sockets application.
+** udpclt is the client side of a udp sockets application.
+**
+** The test is designed to assist developers in porting/debugging
+** the UDP socket functions of NSPR.
+**
+** This test is not a stress test.
+**
+** main() starts two threads: UDP_Server() and UDP_Client();
+** main() uses PR_JoinThread() to wait for the threads to complete.
+**
+** UDP_Server() does repeated recvfrom()s from a socket.
+** He detects an EOF condition set by UDP_Client(). For each
+** packet received by UDP_Server(), he checks its content for
+** expected content, then sends the packet back to UDP_Client().
+**
+** UDP_Client() sends packets to UDP_Server() using sendto()
+** he recieves packets back from the server via recvfrom().
+** After he sends enough packets containing UDP_AMOUNT_TO_WRITE
+** bytes of data, he sends an EOF message.
+**
+** The test issues a pass/fail message at end.
+**
+** Notes:
+** The variable "_debug_on" can be set to 1 to cause diagnostic
+** messages related to client/server synchronization. Useful when
+** the test hangs.
+**
+** Error messages are written to stdout.
+**
+********************************************************************
+*/
+/* --- include files --- */
+#include "nspr.h"
+#include "prpriv.h"
+
+#include "plgetopt.h"
+#include "prttools.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef XP_PC
+#define mode_t int
+#endif
+
+#define UDP_BUF_SIZE 4096
+#define UDP_DGRAM_SIZE 128
+#define UDP_AMOUNT_TO_WRITE (PRInt32)((UDP_DGRAM_SIZE * 1000l) +1)
+#define NUM_UDP_CLIENTS 1
+#define NUM_UDP_DATAGRAMS_PER_CLIENT 5
+#define UDP_SERVER_PORT 9050
+#define UDP_CLIENT_PORT 9053
+#define MY_INADDR PR_INADDR_ANY
+#define PEER_INADDR PR_INADDR_LOOPBACK
+
+#define UDP_TIMEOUT 400000
+/* #define UDP_TIMEOUT PR_INTERVAL_NO_TIMEOUT */
+
+/* --- static data --- */
+static PRIntn _debug_on = 0;
+static PRBool passed = PR_TRUE;
+static PRUint32 cltBytesRead = 0;
+static PRUint32 srvBytesRead = 0;
+static PRFileDesc *output = NULL;
+
+/* --- static function declarations --- */
+#define DPRINTF(arg) if (_debug_on) PR_fprintf(output, arg)
+
+
+
+/*******************************************************************
+** ListNetAddr() -- Display the Net Address on stdout
+**
+** Description: displays the component parts of a PRNetAddr struct
+**
+** Arguments: address of PRNetAddr structure to display
+**
+** Returns: void
+**
+** Notes:
+**
+********************************************************************
+*/
+void ListNetAddr( char *msg, PRNetAddr *na )
+{
+ char mbuf[256];
+
+ sprintf( mbuf, "ListNetAddr: %s family: %d, port: %d, ip: %8.8X\n",
+ msg, na->inet.family, PR_ntohs( na->inet.port), PR_ntohl(na->inet.ip) );
+#if 0
+ DPRINTF( mbuf );
+#endif
+} /* --- end ListNetAddr() --- */
+
+/********************************************************************
+** UDP_Server() -- Test a UDP server application
+**
+** Description: The Server side of a UDP Client/Server application.
+**
+** Arguments: none
+**
+** Returns: void
+**
+** Notes:
+**
+**
+********************************************************************
+*/
+static void PR_CALLBACK UDP_Server( void *arg )
+{
+ static char svrBuf[UDP_BUF_SIZE];
+ PRFileDesc *svrSock;
+ PRInt32 rv;
+ PRNetAddr netaddr;
+ PRBool bound = PR_FALSE;
+ PRBool endOfInput = PR_FALSE;
+ PRInt32 numBytes = UDP_DGRAM_SIZE;
+
+ DPRINTF("udpsrv: UDP_Server(): starting\n" );
+
+ /* --- Create the socket --- */
+ DPRINTF("udpsrv: UDP_Server(): Creating UDP Socket\n" );
+ svrSock = PR_NewUDPSocket();
+ if ( svrSock == NULL )
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Server(): PR_NewUDPSocket() returned NULL\n" );
+ return;
+ }
+
+ /* --- Initialize the sockaddr_in structure --- */
+ memset( &netaddr, 0, sizeof( netaddr ));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.port = PR_htons( UDP_SERVER_PORT );
+ netaddr.inet.ip = PR_htonl( MY_INADDR );
+
+ /* --- Bind the socket --- */
+ while ( !bound )
+ {
+ DPRINTF("udpsrv: UDP_Server(): Binding socket\n" );
+ rv = PR_Bind( svrSock, &netaddr );
+ if ( rv < 0 )
+ {
+ if ( PR_GetError() == PR_ADDRESS_IN_USE_ERROR )
+ {
+ if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): \
+ PR_Bind(): reports: PR_ADDRESS_IN_USE_ERROR\n");
+ PR_Sleep( PR_MillisecondsToInterval( 2000 ));
+ continue;
+ }
+ else
+ {
+ passed = PR_FALSE;
+ if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): \
+ PR_Bind(): failed: %ld with error: %ld\n",
+ rv, PR_GetError() );
+ PR_Close( svrSock );
+ return;
+ }
+ }
+ else {
+ bound = PR_TRUE;
+ }
+ }
+ ListNetAddr( "UDP_Server: after bind", &netaddr );
+
+ /* --- Recv the socket --- */
+ while( !endOfInput )
+ {
+ DPRINTF("udpsrv: UDP_Server(): RecvFrom() socket\n" );
+ rv = PR_RecvFrom( svrSock, svrBuf, numBytes, 0, &netaddr, UDP_TIMEOUT );
+ if ( rv == -1 )
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Server(): PR_RecvFrom(): failed with error: %ld\n",
+ PR_GetError() );
+ PR_Close( svrSock );
+ return;
+ }
+ ListNetAddr( "UDP_Server after RecvFrom", &netaddr );
+
+ srvBytesRead += rv;
+
+ if ( svrBuf[0] == 'E' )
+ {
+ DPRINTF("udpsrv: UDP_Server(): EOF on input detected\n" );
+ endOfInput = PR_TRUE;
+ }
+
+ /* --- Send the socket --- */
+ DPRINTF("udpsrv: UDP_Server(): SendTo(): socket\n" );
+ rv = PR_SendTo( svrSock, svrBuf, rv, 0, &netaddr, PR_INTERVAL_NO_TIMEOUT );
+ if ( rv == -1 )
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Server(): PR_SendTo(): failed with error: %ld\n",
+ PR_GetError() );
+ PR_Close( svrSock );
+ return;
+ }
+ ListNetAddr( "UDP_Server after SendTo", &netaddr );
+ }
+
+ /* --- Close the socket --- */
+ DPRINTF("udpsrv: UDP_Server(): Closing socket\n" );
+ rv = PR_Close( svrSock );
+ if ( rv != PR_SUCCESS )
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Server(): PR_Close(): failed to close socket\n" );
+ return;
+ }
+
+ DPRINTF("udpsrv: UDP_Server(): Normal end\n" );
+} /* --- end UDP_Server() --- */
+
+
+static char cltBuf[UDP_BUF_SIZE];
+static char cltBufin[UDP_BUF_SIZE];
+/********************************************************************
+** UDP_Client() -- Test a UDP client application
+**
+** Description:
+**
+** Arguments:
+**
+**
+** Returns:
+** 0 -- Successful execution
+** 1 -- Test failed.
+**
+** Notes:
+**
+**
+********************************************************************
+*/
+static void PR_CALLBACK UDP_Client( void *arg )
+{
+ PRFileDesc *cltSock;
+ PRInt32 rv;
+ PRBool bound = PR_FALSE;
+ PRNetAddr netaddr;
+ PRNetAddr netaddrx;
+ PRBool endOfInput = PR_FALSE;
+ PRInt32 numBytes = UDP_DGRAM_SIZE;
+ PRInt32 writeThisMany = UDP_AMOUNT_TO_WRITE;
+ int i;
+
+
+ DPRINTF("udpsrv: UDP_Client(): starting\n" );
+
+ /* --- Create the socket --- */
+ cltSock = PR_NewUDPSocket();
+ if ( cltSock == NULL )
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Client(): PR_NewUDPSocket() returned NULL\n" );
+ return;
+ }
+
+ /* --- Initialize the sockaddr_in structure --- */
+ memset( &netaddr, 0, sizeof( netaddr ));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.ip = PR_htonl( MY_INADDR );
+ netaddr.inet.port = PR_htons( UDP_CLIENT_PORT );
+
+ /* --- Initialize the write buffer --- */
+ for ( i = 0; i < UDP_BUF_SIZE ; i++ ) {
+ cltBuf[i] = i;
+ }
+
+ /* --- Bind the socket --- */
+ while ( !bound )
+ {
+ DPRINTF("udpsrv: UDP_Client(): Binding socket\n" );
+ rv = PR_Bind( cltSock, &netaddr );
+ if ( rv < 0 )
+ {
+ if ( PR_GetError() == PR_ADDRESS_IN_USE_ERROR )
+ {
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Client(): PR_Bind(): reports: PR_ADDRESS_IN_USE_ERROR\n");
+ PR_Sleep( PR_MillisecondsToInterval( 2000 ));
+ continue;
+ }
+ else
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Client(): PR_Bind(): failed: %ld with error: %ld\n",
+ rv, PR_GetError() );
+ PR_Close( cltSock );
+ return;
+ }
+ }
+ else {
+ bound = PR_TRUE;
+ }
+ }
+ ListNetAddr( "UDP_Client after Bind", &netaddr );
+
+ /* --- Initialize the sockaddr_in structure --- */
+ memset( &netaddr, 0, sizeof( netaddr ));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.ip = PR_htonl( PEER_INADDR );
+ netaddr.inet.port = PR_htons( UDP_SERVER_PORT );
+
+ /* --- send and receive packets until no more data left */
+ while( !endOfInput )
+ {
+ /*
+ ** Signal EOF in the data stream on the last packet
+ */
+ if ( writeThisMany <= UDP_DGRAM_SIZE )
+ {
+ DPRINTF("udpsrv: UDP_Client(): Send EOF packet\n" );
+ cltBuf[0] = 'E';
+ endOfInput = PR_TRUE;
+ }
+
+ /* --- SendTo the socket --- */
+ if ( writeThisMany > UDP_DGRAM_SIZE ) {
+ numBytes = UDP_DGRAM_SIZE;
+ }
+ else {
+ numBytes = writeThisMany;
+ }
+ writeThisMany -= numBytes;
+ {
+ char mbuf[256];
+ sprintf( mbuf, "udpsrv: UDP_Client(): write_this_many: %d, numbytes: %d\n",
+ writeThisMany, numBytes );
+ DPRINTF( mbuf );
+ }
+
+ DPRINTF("udpsrv: UDP_Client(): SendTo(): socket\n" );
+ rv = PR_SendTo( cltSock, cltBuf, numBytes, 0, &netaddr, UDP_TIMEOUT );
+ if ( rv == -1 )
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Client(): PR_SendTo(): failed with error: %ld\n",
+ PR_GetError() );
+ PR_Close( cltSock );
+ return;
+ }
+ ListNetAddr( "UDP_Client after SendTo", &netaddr );
+
+ /* --- RecvFrom the socket --- */
+ memset( cltBufin, 0, UDP_BUF_SIZE );
+ DPRINTF("udpsrv: UDP_Client(): RecvFrom(): socket\n" );
+ rv = PR_RecvFrom( cltSock, cltBufin, numBytes, 0, &netaddrx, UDP_TIMEOUT );
+ if ( rv == -1 )
+ {
+ passed = PR_FALSE;
+ if (debug_mode) PR_fprintf(output,
+ "udpsrv: UDP_Client(): PR_RecvFrom(): failed with error: %ld\n",
+ PR_GetError() );
+ PR_Close( cltSock );
+ return;
+ }
+ ListNetAddr( "UDP_Client after RecvFrom()", &netaddr );
+ cltBytesRead += rv;
+
+ /* --- verify buffer --- */
+ for ( i = 0; i < rv ; i++ )
+ {
+ if ( cltBufin[i] != i )
+ {
+ /* --- special case, end of input --- */
+ if ( endOfInput && i == 0 && cltBufin[0] == 'E' ) {
+ continue;
+ }
+ passed = PR_FALSE;
+ if (debug_mode) PR_fprintf(output,
+ "udpsrv: UDP_Client(): return data mismatch\n" );
+ PR_Close( cltSock );
+ return;
+ }
+ }
+ if (debug_mode) {
+ PR_fprintf(output, ".");
+ }
+ }
+
+ /* --- Close the socket --- */
+ DPRINTF("udpsrv: UDP_Server(): Closing socket\n" );
+ rv = PR_Close( cltSock );
+ if ( rv != PR_SUCCESS )
+ {
+ passed = PR_FALSE;
+ if (debug_mode) PR_fprintf(output,
+ "udpsrv: UDP_Client(): PR_Close(): failed to close socket\n" );
+ return;
+ }
+ DPRINTF("udpsrv: UDP_Client(): ending\n" );
+} /* --- end UDP_Client() --- */
+
+/********************************************************************
+** main() -- udpsrv
+**
+** arguments:
+**
+** Returns:
+** 0 -- Successful execution
+** 1 -- Test failed.
+**
+** Description:
+**
+** Standard test case setup.
+**
+** Calls the function UDP_Server()
+**
+********************************************************************
+*/
+
+int main(int argc, char **argv)
+{
+ PRThread *srv, *clt;
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d -v
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dv");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'v': /* verbose mode */
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+ output = PR_STDERR;
+
+ PR_SetConcurrency(4);
+
+ /*
+ ** Create the Server thread
+ */
+ DPRINTF( "udpsrv: Creating Server Thread\n" );
+ srv = PR_CreateThread( PR_USER_THREAD,
+ UDP_Server,
+ (void *) 0,
+ PR_PRIORITY_LOW,
+ PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0 );
+ if ( srv == NULL )
+ {
+ if (debug_mode) {
+ PR_fprintf(output, "udpsrv: Cannot create server thread\n" );
+ }
+ passed = PR_FALSE;
+ }
+
+ /*
+ ** Give the Server time to Start
+ */
+ DPRINTF( "udpsrv: Pausing to allow Server to start\n" );
+ PR_Sleep( PR_MillisecondsToInterval(200) );
+
+ /*
+ ** Create the Client thread
+ */
+ DPRINTF( "udpsrv: Creating Client Thread\n" );
+ clt = PR_CreateThread( PR_USER_THREAD,
+ UDP_Client,
+ (void *) 0,
+ PR_PRIORITY_LOW,
+ PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0 );
+ if ( clt == NULL )
+ {
+ if (debug_mode) {
+ PR_fprintf(output, "udpsrv: Cannot create server thread\n" );
+ }
+ passed = PR_FALSE;
+ }
+
+ /*
+ **
+ */
+ DPRINTF("udpsrv: Waiting to join Server & Client Threads\n" );
+ PR_JoinThread( srv );
+ PR_JoinThread( clt );
+
+ /*
+ ** Evaluate test results
+ */
+ if (debug_mode) PR_fprintf(output, "\n\nudpsrv: main(): cltBytesRead(%ld), \
+ srvBytesRead(%ld), expected(%ld)\n",
+ cltBytesRead, srvBytesRead, UDP_AMOUNT_TO_WRITE );
+ if ( cltBytesRead != srvBytesRead || cltBytesRead != UDP_AMOUNT_TO_WRITE )
+ {
+ passed = PR_FALSE;
+ }
+ PR_Cleanup();
+ if ( passed ) {
+ return 0;
+ }
+ else {
+ return 1;
+ }
+} /* --- end main() --- */
diff --git a/nsprpub/pr/tests/ut_ttools.h b/nsprpub/pr/tests/ut_ttools.h
new file mode 100644
index 0000000000..e3cac5daa9
--- /dev/null
+++ b/nsprpub/pr/tests/ut_ttools.h
@@ -0,0 +1,11 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Used in Regress Tool */
+#define NOSTATUS 2
+#define PASS 1
+#define FAIL 0
+
+PRIntn debug_mode=0;
diff --git a/nsprpub/pr/tests/vercheck.c b/nsprpub/pr/tests/vercheck.c
new file mode 100644
index 0000000000..95bda45292
--- /dev/null
+++ b/nsprpub/pr/tests/vercheck.c
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: vercheck.c
+ *
+ * Description:
+ * This test tests the PR_VersionCheck() function. The
+ * compatible_version and incompatible_version arrays
+ * need to be updated for each patch or release.
+ *
+ * Tested areas: library version compatibility check.
+ */
+
+#include "prinit.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+ * This release (4.10.10) is backward compatible with the
+ * 4.0.x, 4.1.x, 4.2.x, 4.3.x, 4.4.x, 4.5.x, 4.6.x, 4.7.x,
+ * 4.8.x, 4.9.x, 4.10.x and 4.11.X releases.
+ * It, of course, is compatible with itself.
+ */
+static char *compatible_version[] = {
+ "4.0", "4.0.1", "4.1", "4.1.1", "4.1.2", "4.1.3",
+ "4.2", "4.2.1", "4.2.2", "4.3", "4.4", "4.4.1",
+ "4.5", "4.5.1",
+ "4.6", "4.6.1", "4.6.2", "4.6.3", "4.6.4", "4.6.5",
+ "4.6.6", "4.6.7", "4.6.8",
+ "4.7", "4.7.1", "4.7.2", "4.7.3", "4.7.4", "4.7.5",
+ "4.7.6",
+ "4.8", "4.8.1", "4.8.2", "4.8.3", "4.8.4", "4.8.5",
+ "4.8.6", "4.8.7", "4.8.8", "4.8.9",
+ "4.9", "4.9.1", "4.9.2", "4.9.3", "4.9.4", "4.9.5",
+ "4.9.6",
+ "4.10", "4.10.1", "4.10.2", "4.10.3", "4.10.4",
+ "4.10.5", "4.10.6", "4.10.7", "4.10.8", "4.10.9",
+ "4.10.10", "4.11", "4.12", "4.13", "4.14", "4.15",
+ "4.16", "4.17", "4.18", "4.19", "4.20", "4.21", "4.22",
+ "4.23", "4.24", "4.25", "4,26", "4.27", "4.28", "4.29",
+ "4.30", "4.31", "4.32", "4.33", "4.34",
+ PR_VERSION
+};
+
+/*
+ * This release is not backward compatible with the old
+ * NSPR 2.1 and 3.x releases.
+ *
+ * Any release is incompatible with future releases and
+ * patches.
+ */
+static char *incompatible_version[] = {
+ "2.1 19980529",
+ "3.0", "3.0.1",
+ "3.1", "3.1.1", "3.1.2", "3.1.3",
+ "3.5", "3.5.1",
+ "4.35.1",
+ "4.36", "4.36.1",
+ "10.0", "11.1", "12.14.20"
+};
+
+int main(int argc, char **argv)
+{
+ int idx;
+ int num_compatible = sizeof(compatible_version) / sizeof(char *);
+ int num_incompatible = sizeof(incompatible_version) / sizeof(char *);
+
+ printf("NSPR release %s:\n", PR_VERSION);
+ for (idx = 0; idx < num_compatible; idx++) {
+ if (PR_VersionCheck(compatible_version[idx]) == PR_FALSE) {
+ fprintf(stderr, "Should be compatible with version %s\n",
+ compatible_version[idx]);
+ exit(1);
+ }
+ printf("Compatible with version %s\n", compatible_version[idx]);
+ }
+
+ for (idx = 0; idx < num_incompatible; idx++) {
+ if (PR_VersionCheck(incompatible_version[idx]) == PR_TRUE) {
+ fprintf(stderr, "Should be incompatible with version %s\n",
+ incompatible_version[idx]);
+ exit(1);
+ }
+ printf("Incompatible with version %s\n", incompatible_version[idx]);
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/version.c b/nsprpub/pr/tests/version.c
new file mode 100644
index 0000000000..49e8a05d78
--- /dev/null
+++ b/nsprpub/pr/tests/version.c
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prio.h"
+#include "prprf.h"
+#include "prlink.h"
+#include "prvrsion.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+PR_IMPORT(const PRVersionDescription *) libVersionPoint(void);
+
+int main(int argc, char **argv)
+{
+ PRIntn rv = 1;
+ PLOptStatus os;
+ PRIntn verbosity = 0;
+ PRLibrary *runtime = NULL;
+ const char *library_name = NULL;
+ const PRVersionDescription *version_info;
+ char buffer[100];
+ PRExplodedTime exploded;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d");
+
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 0: /* fully qualified library name */
+ library_name = opt->value;
+ break;
+ case 'd': /* verbodity */
+ verbosity += 1;
+ break;
+ default:
+ PR_fprintf(err, "Usage: version [-d] {fully qualified library name}\n");
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (NULL != library_name)
+ {
+ runtime = PR_LoadLibrary(library_name);
+ if (NULL == runtime) {
+ PL_FPrintError(err, "PR_LoadLibrary");
+ return 3;
+ } else {
+ versionEntryPointType versionPoint = (versionEntryPointType)
+ PR_FindSymbol(runtime, "libVersionPoint");
+ if (NULL == versionPoint) {
+ PL_FPrintError(err, "PR_FindSymbol");
+ return 4;
+ }
+ version_info = versionPoint();
+ }
+ } else {
+ version_info = libVersionPoint(); /* NSPR's version info */
+ }
+
+ (void)PR_fprintf(err, "Runtime library version information\n");
+ PR_ExplodeTime(
+ version_info->buildTime, PR_GMTParameters, &exploded);
+ (void)PR_FormatTime(
+ buffer, sizeof(buffer), "%d %b %Y %H:%M:%S", &exploded);
+ (void)PR_fprintf(err, " Build time: %s GMT\n", buffer);
+ (void)PR_fprintf(
+ err, " Build time: %s\n", version_info->buildTimeString);
+ (void)PR_fprintf(
+ err, " %s V%u.%u.%u (%s%s%s)\n",
+ version_info->description,
+ version_info->vMajor,
+ version_info->vMinor,
+ version_info->vPatch,
+ (version_info->beta ? " beta " : ""),
+ (version_info->debug ? " debug " : ""),
+ (version_info->special ? " special" : ""));
+ (void)PR_fprintf(err, " filename: %s\n", version_info->filename);
+ (void)PR_fprintf(err, " security: %s\n", version_info->security);
+ (void)PR_fprintf(err, " copyright: %s\n", version_info->copyright);
+ (void)PR_fprintf(err, " comment: %s\n", version_info->comment);
+ rv = 0;
+ return rv;
+}
+
+/* version.c */
diff --git a/nsprpub/pr/tests/writev.c b/nsprpub/pr/tests/writev.c
new file mode 100644
index 0000000000..f72f00fb44
--- /dev/null
+++ b/nsprpub/pr/tests/writev.c
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+
+#include "plgetopt.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+
+#ifndef IOV_MAX
+#define IOV_MAX 16
+#endif
+
+#ifdef DEBUG
+#define PORT_INC_DO +100
+#else
+#define PORT_INC_DO
+#endif
+#ifdef IS_64
+#define PORT_INC_3264 +200
+#else
+#define PORT_INC_3264
+#endif
+
+#define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264
+
+int PR_CALLBACK Writev(int argc, char **argv)
+{
+
+ PRStatus rv;
+ PRNetAddr serverAddr;
+ PRFileDesc *clientSock, *debug = NULL;
+
+ char *buffer = NULL;
+ PRIOVec *iov = NULL;
+ PRBool passed = PR_TRUE;
+ PRIntervalTime timein, elapsed, timeout;
+ PRIntervalTime tmo_min = 0x7fffffff, tmo_max = 0, tmo_elapsed = 0;
+ PRInt32 tmo_counted = 0, iov_index, loop, bytes, number_fragments;
+ PRInt32 message_length = 100, fragment_length = 100, messages = 100;
+ struct Descriptor {
+ PRInt32 length;
+ PRUint32 checksum;
+ } descriptor;
+
+ /*
+ * USAGE
+ * -h dns name of host serving the connection (default = self)
+ * -m number of messages to send (default = 100)
+ * -s size of each message (default = 100)
+ * -f size of each message fragment (default = 100)
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dh:m:s:f:");
+
+ PR_STDIO_INIT();
+ rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &serverAddr);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'h': /* the remote host */
+ {
+ PRIntn es = 0;
+ PRHostEnt host;
+ char buffer[1024];
+ (void)PR_GetHostByName(opt->value, buffer, sizeof(buffer), &host);
+ es = PR_EnumerateHostEnt(es, &host, BASE_PORT, &serverAddr);
+ PR_ASSERT(es > 0);
+ }
+ break;
+ case 'd': /* debug mode */
+ debug = PR_GetSpecialFD(PR_StandardError);
+ break;
+ case 'm': /* number of messages to send */
+ messages = atoi(opt->value);
+ break;
+ case 's': /* total size of each message */
+ message_length = atoi(opt->value);
+ break;
+ case 'f': /* size of each message fragment */
+ fragment_length = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ buffer = (char*)malloc(message_length);
+
+ number_fragments = (message_length + fragment_length - 1) / fragment_length + 1;
+ while (IOV_MAX < number_fragments)
+ {
+ fragment_length = message_length / (IOV_MAX - 2);
+ number_fragments = (message_length + fragment_length - 1) /
+ fragment_length + 1;
+ if (NULL != debug) PR_fprintf(debug,
+ "Too many fragments - reset fragment length to %ld\n", fragment_length);
+ }
+ iov = (PRIOVec*)malloc(number_fragments * sizeof(PRIOVec));
+
+ iov[0].iov_base = (char*)&descriptor;
+ iov[0].iov_len = sizeof(descriptor);
+ for (iov_index = 1; iov_index < number_fragments; ++iov_index)
+ {
+ iov[iov_index].iov_base = buffer + (iov_index - 1) * fragment_length;
+ iov[iov_index].iov_len = fragment_length;
+ }
+
+ for (bytes = 0; bytes < message_length; ++bytes) {
+ buffer[bytes] = (char)bytes;
+ }
+
+ timeout = PR_SecondsToInterval(1);
+
+ for (loop = 0; loop < messages; ++loop)
+ {
+ if (NULL != debug) {
+ PR_fprintf(debug, "[%d]socket ... ", loop);
+ }
+ clientSock = PR_NewTCPSocket();
+ if (clientSock)
+ {
+ timein = PR_IntervalNow();
+ if (NULL != debug) {
+ PR_fprintf(debug, "connecting ... ");
+ }
+ rv = PR_Connect(clientSock, &serverAddr, timeout);
+ if (PR_SUCCESS == rv)
+ {
+ descriptor.checksum = 0;
+ descriptor.length = (loop < (messages - 1)) ? message_length : 0;
+ if (0 == descriptor.length) {
+ number_fragments = 1;
+ }
+ else
+ for (iov_index = 0; iov_index < descriptor.length; ++iov_index)
+ {
+ PRUint32 overflow = descriptor.checksum & 0x80000000;
+ descriptor.checksum = (descriptor.checksum << 1);
+ if (0x00000000 != overflow) {
+ descriptor.checksum += 1;
+ }
+ descriptor.checksum += buffer[iov_index];
+ }
+ if (NULL != debug) PR_fprintf(
+ debug, "sending %d bytes ... ", descriptor.length);
+
+ /* then, at the last moment ... */
+ descriptor.length = PR_ntohl(descriptor.length);
+ descriptor.checksum = PR_ntohl(descriptor.checksum);
+
+ bytes = PR_Writev(clientSock, iov, number_fragments, timeout);
+ if (NULL != debug) {
+ PR_fprintf(debug, "closing ... ");
+ }
+ rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH);
+ rv = PR_Close(clientSock);
+ if (NULL != debug) PR_fprintf(
+ debug, "%s\n", ((PR_SUCCESS == rv) ? "good" : "bad"));
+ elapsed = PR_IntervalNow() - timein;
+ if (elapsed < tmo_min) {
+ tmo_min = elapsed;
+ }
+ else if (elapsed > tmo_max) {
+ tmo_max = elapsed;
+ }
+ tmo_elapsed += elapsed;
+ tmo_counted += 1;
+ }
+ else
+ {
+ if (NULL != debug) PR_fprintf(
+ debug, "failed - retrying (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ PR_Close(clientSock);
+ }
+ }
+ else if (NULL != debug)
+ {
+ PR_fprintf(debug, "unable to create client socket\n");
+ passed = PR_FALSE;
+ }
+ }
+ if (NULL != debug) {
+ if (0 == tmo_counted) {
+ PR_fprintf(debug, "No connection made\n");
+ } else {
+ PR_fprintf(
+ debug, "\nTimings: %d [%d] %d (microseconds)\n",
+ PR_IntervalToMicroseconds(tmo_min),
+ PR_IntervalToMicroseconds(tmo_elapsed / tmo_counted),
+ PR_IntervalToMicroseconds(tmo_max));
+ }
+ }
+
+ PR_DELETE(buffer);
+ PR_DELETE(iov);
+
+ PR_fprintf(
+ PR_GetSpecialFD(PR_StandardError),
+ "%s\n", (passed) ? "PASSED" : "FAILED");
+ return (passed) ? 0 : 1;
+}
+
+int main(int argc, char **argv)
+{
+ return (PR_VersionCheck(PR_VERSION)) ?
+ PR_Initialize(Writev, argc, argv, 4) : -1;
+} /* main */
+
+/* writev.c */
+
+
diff --git a/nsprpub/pr/tests/xnotify.c b/nsprpub/pr/tests/xnotify.c
new file mode 100644
index 0000000000..7e6f0e2936
--- /dev/null
+++ b/nsprpub/pr/tests/xnotify.c
@@ -0,0 +1,429 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prprf.h"
+#include "prio.h"
+#include "prcvar.h"
+#include "prmon.h"
+#include "prcmon.h"
+#include "prlock.h"
+#include "prerror.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prthread.h"
+
+static PRLock *ml = NULL;
+static PRIntervalTime base;
+static PRFileDesc *err = NULL;
+
+typedef struct CMonShared
+{
+ PRInt32 o1, o2;
+} CMonShared;
+
+typedef struct MonShared
+{
+ PRMonitor *o1, *o2;
+} MonShared;
+
+typedef struct LockShared
+{
+ PRLock *o1, *o2;
+ PRCondVar *cv1, *cv2;
+} LockShared;
+
+static void LogNow(const char *msg, PRStatus rv)
+{
+ PRIntervalTime now = PR_IntervalNow();
+ PR_Lock(ml);
+ PR_fprintf(err, "%6ld: %s", (now - base), msg);
+ if (PR_FAILURE == rv) {
+ PL_FPrintError(err, " ");
+ }
+ else {
+ PR_fprintf(err, "\n");
+ }
+ PR_Unlock(ml);
+} /* LogNow */
+
+static void Help(void)
+{
+ PR_fprintf(err, "Usage: [-[d][l][m][c]] [-h]\n");
+ PR_fprintf(err, "\t-d debug mode (default: FALSE)\n");
+ PR_fprintf(err, "\t-l test with locks (default: FALSE)\n");
+ PR_fprintf(err, "\t-m tests with monitors (default: FALSE)\n");
+ PR_fprintf(err, "\t-c tests with cmonitors (default: FALSE)\n");
+ PR_fprintf(err, "\t-h help\n");
+} /* Help */
+
+static void PR_CALLBACK T2CMon(void *arg)
+{
+ PRStatus rv;
+ CMonShared *shared = (CMonShared*)arg;
+
+ PR_CEnterMonitor(&shared->o1);
+ LogNow("T2 waiting 5 seconds on o1", PR_SUCCESS);
+ rv = PR_CWait(&shared->o1, PR_SecondsToInterval(5));
+ if (PR_SUCCESS == rv) {
+ LogNow("T2 resuming on o1", rv);
+ }
+ else {
+ LogNow("T2 wait failed on o1", rv);
+ }
+
+ rv = PR_CNotify(&shared->o1);
+ if (PR_SUCCESS == rv) {
+ LogNow("T2 notified o1", rv);
+ }
+ else {
+ LogNow("T2 notify on o1 failed", rv);
+ }
+
+ PR_CExitMonitor(&shared->o1);
+} /* T2CMon */
+
+static void PR_CALLBACK T3CMon(void *arg)
+{
+ PRStatus rv;
+ CMonShared *shared = (CMonShared*)arg;
+
+ PR_CEnterMonitor(&shared->o2);
+ LogNow("T3 waiting 5 seconds on o2", PR_SUCCESS);
+ rv = PR_CWait(&shared->o2, PR_SecondsToInterval(5));
+ if (PR_SUCCESS == rv) {
+ LogNow("T3 resuming on o2", rv);
+ }
+ else {
+ LogNow("T3 wait failed on o2", rv);
+ }
+ rv = PR_CNotify(&shared->o2);
+ LogNow("T3 notify on o2", rv);
+ PR_CExitMonitor(&shared->o2);
+
+} /* T3CMon */
+
+static CMonShared sharedCM;
+
+static void T1CMon(void)
+{
+ PRStatus rv;
+ PRThread *t2, *t3;
+
+ PR_fprintf(err, "\n**********************************\n");
+ PR_fprintf(err, " CACHED MONITORS\n");
+ PR_fprintf(err, "**********************************\n");
+
+ base = PR_IntervalNow();
+
+ PR_CEnterMonitor(&sharedCM.o1);
+ LogNow("T1 waiting 3 seconds on o1", PR_SUCCESS);
+ rv = PR_CWait(&sharedCM.o1, PR_SecondsToInterval(3));
+ if (PR_SUCCESS == rv) {
+ LogNow("T1 resuming on o1", rv);
+ }
+ else {
+ LogNow("T1 wait on o1 failed", rv);
+ }
+ PR_CExitMonitor(&sharedCM.o1);
+
+ LogNow("T1 creating T2", PR_SUCCESS);
+ t2 = PR_CreateThread(
+ PR_USER_THREAD, T2CMon, &sharedCM, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ LogNow("T1 creating T3", PR_SUCCESS);
+ t3 = PR_CreateThread(
+ PR_USER_THREAD, T3CMon, &sharedCM, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ PR_CEnterMonitor(&sharedCM.o2);
+ LogNow("T1 waiting forever on o2", PR_SUCCESS);
+ rv = PR_CWait(&sharedCM.o2, PR_INTERVAL_NO_TIMEOUT);
+ if (PR_SUCCESS == rv) {
+ LogNow("T1 resuming on o2", rv);
+ }
+ else {
+ LogNow("T1 wait on o2 failed", rv);
+ }
+ PR_CExitMonitor(&sharedCM.o2);
+
+ (void)PR_JoinThread(t2);
+ (void)PR_JoinThread(t3);
+
+} /* T1CMon */
+
+static void PR_CALLBACK T2Mon(void *arg)
+{
+ PRStatus rv;
+ MonShared *shared = (MonShared*)arg;
+
+ PR_EnterMonitor(shared->o1);
+ LogNow("T2 waiting 5 seconds on o1", PR_SUCCESS);
+ rv = PR_Wait(shared->o1, PR_SecondsToInterval(5));
+ if (PR_SUCCESS == rv) {
+ LogNow("T2 resuming on o1", rv);
+ }
+ else {
+ LogNow("T2 wait failed on o1", rv);
+ }
+
+ rv = PR_Notify(shared->o1);
+ if (PR_SUCCESS == rv) {
+ LogNow("T2 notified o1", rv);
+ }
+ else {
+ LogNow("T2 notify on o1 failed", rv);
+ }
+
+ PR_ExitMonitor(shared->o1);
+} /* T2Mon */
+
+static void PR_CALLBACK T3Mon(void *arg)
+{
+ PRStatus rv;
+ MonShared *shared = (MonShared*)arg;
+
+ PR_EnterMonitor(shared->o2);
+ LogNow("T3 waiting 5 seconds on o2", PR_SUCCESS);
+ rv = PR_Wait(shared->o2, PR_SecondsToInterval(5));
+ if (PR_SUCCESS == rv) {
+ LogNow("T3 resuming on o2", rv);
+ }
+ else {
+ LogNow("T3 wait failed on o2", rv);
+ }
+ rv = PR_Notify(shared->o2);
+ LogNow("T3 notify on o2", rv);
+ PR_ExitMonitor(shared->o2);
+
+} /* T3Mon */
+
+static MonShared sharedM;
+static void T1Mon(void)
+{
+ PRStatus rv;
+ PRThread *t2, *t3;
+
+ PR_fprintf(err, "\n**********************************\n");
+ PR_fprintf(err, " MONITORS\n");
+ PR_fprintf(err, "**********************************\n");
+
+ sharedM.o1 = PR_NewMonitor();
+ sharedM.o2 = PR_NewMonitor();
+
+ base = PR_IntervalNow();
+
+ PR_EnterMonitor(sharedM.o1);
+ LogNow("T1 waiting 3 seconds on o1", PR_SUCCESS);
+ rv = PR_Wait(sharedM.o1, PR_SecondsToInterval(3));
+ if (PR_SUCCESS == rv) {
+ LogNow("T1 resuming on o1", rv);
+ }
+ else {
+ LogNow("T1 wait on o1 failed", rv);
+ }
+ PR_ExitMonitor(sharedM.o1);
+
+ LogNow("T1 creating T2", PR_SUCCESS);
+ t2 = PR_CreateThread(
+ PR_USER_THREAD, T2Mon, &sharedM, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ LogNow("T1 creating T3", PR_SUCCESS);
+ t3 = PR_CreateThread(
+ PR_USER_THREAD, T3Mon, &sharedM, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ PR_EnterMonitor(sharedM.o2);
+ LogNow("T1 waiting forever on o2", PR_SUCCESS);
+ rv = PR_Wait(sharedM.o2, PR_INTERVAL_NO_TIMEOUT);
+ if (PR_SUCCESS == rv) {
+ LogNow("T1 resuming on o2", rv);
+ }
+ else {
+ LogNow("T1 wait on o2 failed", rv);
+ }
+ PR_ExitMonitor(sharedM.o2);
+
+ (void)PR_JoinThread(t2);
+ (void)PR_JoinThread(t3);
+
+ PR_DestroyMonitor(sharedM.o1);
+ PR_DestroyMonitor(sharedM.o2);
+
+} /* T1Mon */
+
+static void PR_CALLBACK T2Lock(void *arg)
+{
+ PRStatus rv;
+ LockShared *shared = (LockShared*)arg;
+
+ PR_Lock(shared->o1);
+ LogNow("T2 waiting 5 seconds on o1", PR_SUCCESS);
+ rv = PR_WaitCondVar(shared->cv1, PR_SecondsToInterval(5));
+ if (PR_SUCCESS == rv) {
+ LogNow("T2 resuming on o1", rv);
+ }
+ else {
+ LogNow("T2 wait failed on o1", rv);
+ }
+
+ rv = PR_NotifyCondVar(shared->cv1);
+ if (PR_SUCCESS == rv) {
+ LogNow("T2 notified o1", rv);
+ }
+ else {
+ LogNow("T2 notify on o1 failed", rv);
+ }
+
+ PR_Unlock(shared->o1);
+} /* T2Lock */
+
+static void PR_CALLBACK T3Lock(void *arg)
+{
+ PRStatus rv;
+ LockShared *shared = (LockShared*)arg;
+
+ PR_Lock(shared->o2);
+ LogNow("T3 waiting 5 seconds on o2", PR_SUCCESS);
+ rv = PR_WaitCondVar(shared->cv2, PR_SecondsToInterval(5));
+ if (PR_SUCCESS == rv) {
+ LogNow("T3 resuming on o2", rv);
+ }
+ else {
+ LogNow("T3 wait failed on o2", rv);
+ }
+ rv = PR_NotifyCondVar(shared->cv2);
+ LogNow("T3 notify on o2", rv);
+ PR_Unlock(shared->o2);
+
+} /* T3Lock */
+
+/*
+** Make shared' a static variable for Win16
+*/
+static LockShared sharedL;
+
+static void T1Lock(void)
+{
+ PRStatus rv;
+ PRThread *t2, *t3;
+ sharedL.o1 = PR_NewLock();
+ sharedL.o2 = PR_NewLock();
+ sharedL.cv1 = PR_NewCondVar(sharedL.o1);
+ sharedL.cv2 = PR_NewCondVar(sharedL.o2);
+
+ PR_fprintf(err, "\n**********************************\n");
+ PR_fprintf(err, " LOCKS\n");
+ PR_fprintf(err, "**********************************\n");
+
+ base = PR_IntervalNow();
+
+ PR_Lock(sharedL.o1);
+ LogNow("T1 waiting 3 seconds on o1", PR_SUCCESS);
+ rv = PR_WaitCondVar(sharedL.cv1, PR_SecondsToInterval(3));
+ if (PR_SUCCESS == rv) {
+ LogNow("T1 resuming on o1", rv);
+ }
+ else {
+ LogNow("T1 wait on o1 failed", rv);
+ }
+ PR_Unlock(sharedL.o1);
+
+ LogNow("T1 creating T2", PR_SUCCESS);
+ t2 = PR_CreateThread(
+ PR_USER_THREAD, T2Lock, &sharedL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ LogNow("T1 creating T3", PR_SUCCESS);
+ t3 = PR_CreateThread(
+ PR_USER_THREAD, T3Lock, &sharedL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ PR_Lock(sharedL.o2);
+ LogNow("T1 waiting forever on o2", PR_SUCCESS);
+ rv = PR_WaitCondVar(sharedL.cv2, PR_INTERVAL_NO_TIMEOUT);
+ if (PR_SUCCESS == rv) {
+ LogNow("T1 resuming on o2", rv);
+ }
+ else {
+ LogNow("T1 wait on o2 failed", rv);
+ }
+ PR_Unlock(sharedL.o2);
+
+ (void)PR_JoinThread(t2);
+ (void)PR_JoinThread(t3);
+
+ PR_DestroyLock(sharedL.o1);
+ PR_DestroyLock(sharedL.o2);
+ PR_DestroyCondVar(sharedL.cv1);
+ PR_DestroyCondVar(sharedL.cv2);
+} /* T1Lock */
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dhlmc");
+ PRBool locks = PR_FALSE, monitors = PR_FALSE, cmonitors = PR_FALSE;
+
+ err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode (noop) */
+ break;
+ case 'l': /* locks */
+ locks = PR_TRUE;
+ break;
+ case 'm': /* monitors */
+ monitors = PR_TRUE;
+ break;
+ case 'c': /* cached monitors */
+ cmonitors = PR_TRUE;
+ break;
+ case 'h': /* needs guidance */
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ ml = PR_NewLock();
+ if (locks) {
+ T1Lock();
+ }
+ if (monitors) {
+ T1Mon();
+ }
+ if (cmonitors) {
+ T1CMon();
+ }
+
+ PR_DestroyLock(ml);
+
+ PR_fprintf(err, "Done!\n");
+ return 0;
+} /* main */
+
+
+int main(int argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
+/* xnotify.c */
diff --git a/nsprpub/pr/tests/y2k.c b/nsprpub/pr/tests/y2k.c
new file mode 100644
index 0000000000..31b35e7e88
--- /dev/null
+++ b/nsprpub/pr/tests/y2k.c
@@ -0,0 +1,799 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * file: y2k.c
+ * description: Test for y2k compliance for NSPR.
+ *
+ * Sep 1999. lth. Added "Sun" specified dates to the test data.
+ */
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prtime.h"
+#include "prprf.h"
+#include "prlog.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define PRINT_DETAILS
+
+int failed_already=0;
+PRBool debug_mode = PR_FALSE;
+
+static char *dayOfWeek[] =
+{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "???" };
+static char *month[] =
+{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???"
+};
+
+PRLogModuleInfo *lm;
+
+static void PrintExplodedTime(const PRExplodedTime *et) {
+ PRInt32 totalOffset;
+ PRInt32 hourOffset, minOffset;
+ const char *sign;
+
+ /* Print day of the week, month, day, hour, minute, and second */
+ printf("%s %s %2ld %02ld:%02ld:%02ld ",
+ dayOfWeek[et->tm_wday], month[et->tm_month], et->tm_mday,
+ et->tm_hour, et->tm_min, et->tm_sec);
+
+ /* Print year */
+ printf("%hd ", et->tm_year);
+
+ /* Print time zone */
+ totalOffset = et->tm_params.tp_gmt_offset + et->tm_params.tp_dst_offset;
+ if (totalOffset == 0) {
+ printf("UTC ");
+ } else {
+ sign = "+";
+ if (totalOffset < 0) {
+ totalOffset = -totalOffset;
+ sign = "-";
+ }
+ hourOffset = totalOffset / 3600;
+ minOffset = (totalOffset % 3600) / 60;
+ printf("%s%02ld%02ld ", sign, hourOffset, minOffset);
+ }
+#ifdef PRINT_DETAILS
+ printf("{%d, %d, %d, %d, %d, %d, %d, %d, %d, { %d, %d}}\n",et->tm_usec,
+ et->tm_sec,
+ et->tm_min,
+ et->tm_hour,
+ et->tm_mday,
+ et->tm_month,
+ et->tm_year,
+ et->tm_wday,
+ et->tm_yday,
+ et->tm_params.tp_gmt_offset,
+ et->tm_params.tp_dst_offset);
+#endif
+}
+
+static int ExplodedTimeIsEqual(const PRExplodedTime *et1,
+ const PRExplodedTime *et2)
+{
+ if (et1->tm_usec == et2->tm_usec &&
+ et1->tm_sec == et2->tm_sec &&
+ et1->tm_min == et2->tm_min &&
+ et1->tm_hour == et2->tm_hour &&
+ et1->tm_mday == et2->tm_mday &&
+ et1->tm_month == et2->tm_month &&
+ et1->tm_year == et2->tm_year &&
+ et1->tm_wday == et2->tm_wday &&
+ et1->tm_yday == et2->tm_yday &&
+ et1->tm_params.tp_gmt_offset == et2->tm_params.tp_gmt_offset &&
+ et1->tm_params.tp_dst_offset == et2->tm_params.tp_dst_offset) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * TEST 1: TestExplodeImplodeTime
+ * Description:
+ * For each given timestamp T (a PRTime value), call PR_ExplodeTime
+ * with GMT, US Pacific, and local time parameters. Compare the
+ * resulting calendar (exploded) time values with the expected
+ * values.
+ *
+ * Note: the expected local time values depend on the local time
+ * zone. The local time values stored in this test are for the US
+ * Pacific Time Zone. If you are running this test in a different
+ * time zone, you need to modify the values in the localt array.
+ * An example is provided below.
+ *
+ * Call PR_ImplodeTime for each of the exploded values and compare
+ * the resulting PRTime values with the original input.
+ *
+ * This test is run for the values of time T corresponding to the
+ * following dates:
+ * - 12/31/99 - before 2000
+ * - 01/01/00 - after 2000
+ * - Leap year - Feb 29, 2000
+ * - March 1st, 2001 (after 1 year)
+ * - March 1st, 2005 (after second leap year)
+ * - 09/09/99 (used by some programs as an end of file marker)
+ *
+ * Call PR_Now, convert to calendar time using PR_ExplodeTime and
+ * manually check the result for correctness. The time should match
+ * the system clock.
+ *
+ * Tested functions: PR_Now, PR_ExplodeTime, PR_ImplodeTime,
+ * PR_LocalTimeParameters, PR_GMTParameters.
+ */
+
+static PRTime prt[] = {
+ LL_INIT(220405, 2133125120), /* 946634400000000 */
+ LL_INIT(220425, 2633779200), /* 946720800000000 */
+ LL_INIT(221612, 2107598848), /* 951818400000000 */
+ LL_INIT(228975, 663398400), /* 983440800000000 */
+ LL_INIT(258365, 1974568960), /* 1109671200000000 */
+ LL_INIT(218132, 1393788928), /* 936871200000000 */
+ /* Sun's dates follow */
+ LL_INIT( 213062, 4077979648 ), /* Dec 31 1998 10:00:00 */
+ LL_INIT( 218152, 1894443008 ), /* Sep 10 1999 10:00:00 */
+ LL_INIT( 221592, 1606944768 ), /* Feb 28 2000 10:00:00 */
+ LL_INIT( 227768, 688924672 ), /* Dec 31 2000 10:00:00 */
+ LL_INIT( 227788, 1189578752 ), /* Jan 1 2001 10:00:00 */
+};
+
+static PRExplodedTime gmt[] = {
+ { 0, 0, 0, 10, 31, 11, 1999, 5, 364, {0, 0}}, /* 1999/12/31 10:00:00 GMT */
+ { 0, 0, 0, 10, 1, 0, 2000, 6, 0, {0, 0}}, /* 2000/01/01 10:00:00 GMT */
+ { 0, 0, 0, 10, 29, 1, 2000, 2, 59, {0, 0}}, /* 2000/02/29 10:00:00 GMT */
+ { 0, 0, 0, 10, 1, 2, 2001, 4, 59, {0, 0}}, /* 2001/3/1 10:00:00 GMT */
+ { 0, 0, 0, 10, 1, 2, 2005, 2, 59, {0, 0}}, /* 2005/3/1 10:00:00 GMT */
+ { 0, 0, 0, 10, 9, 8, 1999, 4, 251, {0, 0}}, /* 1999/9/9 10:00:00 GMT */
+ /* Sun's dates follow */
+ { 0, 0, 0, 10, 31, 11, 1998, 4, 364, {0, 0}}, /* 12/31/1998 10:00:00 GMT */
+ { 0, 0, 0, 10, 10, 8, 1999, 5, 252, {0, 0}}, /* 9/10/1999 10:00:00 GMT */
+ { 0, 0, 0, 10, 28, 1, 2000, 1, 58, {0, 0}}, /* 2/28/2000 10:00:00 GMT */
+ { 0, 0, 0, 10, 31, 11, 2000, 0, 365, {0, 0}}, /* 12/31/2000 10:00:00 GMT */
+ { 0, 0, 0, 10, 1, 0, 2001, 1, 0, {0, 0}} /* 1/1/2001 10:00:00 GMT */
+};
+
+static PRExplodedTime uspt[] = {
+ { 0, 0, 0, 2, 31, 11, 1999, 5, 364, {-28800, 0}}, /* 1999/12/31 2:00:00 PST */
+ { 0, 0, 0, 2, 1, 0, 2000, 6, 0, {-28800, 0}}, /* 2000/01/01 2:00:00 PST */
+ { 0, 0, 0, 2, 29, 1, 2000, 2, 59, {-28800, 0}}, /* 2000/02/29 2:00:00 PST */
+ { 0, 0, 0, 2, 1, 2, 2001, 4, 59, {-28800, 0}}, /* 2001/3/1 2:00:00 PST */
+ { 0, 0, 0, 2, 1, 2, 2005, 2, 59, {-28800, 0}}, /* 2005/3/1 2:00:00 PST */
+ { 0, 0, 0, 3, 9, 8, 1999, 4, 251, {-28800, 3600}}, /* 1999/9/9 3:00:00 PDT */
+ /* Sun's dates follow */
+ { 0, 0, 0, 2, 31, 11, 1998, 4, 364, {-28800, 0}}, /* 12/31/1998 00:00:00 GMT */
+ { 0, 0, 0, 3, 10, 8, 1999, 5, 252, {-28800, 3600}}, /* 9/10/1999 00:00:00 GMT */
+ { 0, 0, 0, 2, 28, 1, 2000, 1, 58, {-28800, 0}}, /* 2/28/2000 00:00:00 GMT */
+ { 0, 0, 0, 2, 31, 11, 2000, 0, 365, {-28800, 0}}, /* 12/31/2000 00:00:00 GMT */
+ { 0, 0, 0, 2, 1, 0, 2001, 1, 0, {-28800, 0}} /* 1/1/2001 00:00:00 GMT */
+};
+
+/*
+ * This test assumes that we are in US Pacific Time Zone.
+ * If you are running this test in a different time zone,
+ * you need to modify the localt array and fill in the
+ * expected results. The localt array for US Eastern Time
+ * Zone is provided as an example.
+ */
+static PRExplodedTime localt[] = {
+ { 0, 0, 0, 2, 31, 11, 1999, 5, 364, {-28800, 0}}, /* 1999/12/31 2:00:00 PST */
+ { 0, 0, 0, 2, 1, 0, 2000, 6, 0, {-28800, 0}}, /* 2000/01/01 2:00:00 PST */
+ { 0, 0, 0, 2, 29, 1, 2000, 2, 59, {-28800, 0}}, /* 2000/02/29 2:00:00 PST */
+ { 0, 0, 0, 2, 1, 2, 2001, 4, 59, {-28800, 0}}, /* 2001/3/1 2:00:00 PST */
+ { 0, 0, 0, 2, 1, 2, 2005, 2, 59, {-28800, 0}}, /* 2005/3/1 2:00:00 PST */
+ { 0, 0, 0, 3, 9, 8, 1999, 4, 251, {-28800, 3600}}, /* 1999/9/9 3:00:00 PDT */
+ /* Sun's dates follow */
+ { 0, 0, 0, 2, 31, 11, 1998, 4, 364, {-28800, 0}}, /* 12/31/1998 00:00:00 GMT */
+ { 0, 0, 0, 3, 10, 8, 1999, 5, 252, {-28800, 3600}}, /* 9/10/1999 00:00:00 GMT */
+ { 0, 0, 0, 2, 28, 1, 2000, 1, 58, {-28800, 0}}, /* 2/28/2000 00:00:00 GMT */
+ { 0, 0, 0, 2, 31, 11, 2000, 0, 365, {-28800, 0}}, /* 12/31/2000 00:00:00 GMT */
+ { 0, 0, 0, 2, 1, 0, 2001, 1, 0, {-28800, 0}} /* 1/1/2001 00:00:00 GMT */
+};
+
+#ifdef US_EASTERN_TIME
+static PRExplodedTime localt[] = {
+ { 0, 0, 0, 5, 31, 11, 1999, 5, 364, {-18000, 0}}, /* 1999/12/31 2:00:00 EST */
+ { 0, 0, 0, 5, 1, 0, 2000, 6, 0, {-18000, 0}}, /* 2000/01/01 2:00:00 EST */
+ { 0, 0, 0, 5, 29, 1, 2000, 2, 59, {-18000, 0}}, /* 2000/02/29 2:00:00 EST */
+ { 0, 0, 0, 5, 1, 2, 2001, 4, 59, {-18000, 0}}, /* 2001/3/1 2:00:00 EST */
+ { 0, 0, 0, 5, 1, 2, 2005, 2, 59, {-18000, 0}}, /* 2005/3/1 2:00:00 EST */
+ { 0, 0, 0, 6, 9, 8, 1999, 4, 251, {-18000, 3600}}, /* 1999/9/9 3:00:00 EDT */
+ /* Sun's dates follow */
+ { 0, 0, 0, 5, 31, 11, 1998, 4, 364, {-18000 0}}, /* 12/31/1998 00:00:00 GMT */
+ { 0, 0, 0, 6, 10, 8, 1999, 5, 252, {-18000 3600}}, /* 9/10/1999 00:00:00 GMT */
+ { 0, 0, 0, 5, 28, 1, 2000, 1, 58, {-18000 0}}, /* 2/28/2000 00:00:00 GMT */
+ { 0, 0, 0, 5, 31, 11, 2000, 0, 365, {-18000 0}}, /* 12/31/2000 00:00:00 GMT */
+ { 0, 0, 0, 5, 1, 0, 2001, 1, 0, {-18000 0}} /* 1/1/2001 00:00:00 GMT */
+};
+#endif
+
+static PRStatus TestExplodeImplodeTime(void)
+{
+ PRTime prt_tmp;
+ PRTime now;
+ int idx;
+ int array_size = sizeof(prt) / sizeof(PRTime);
+ PRExplodedTime et_tmp;
+ char buf[1024];
+
+ for (idx = 0; idx < array_size; idx++) {
+ PR_snprintf(buf, sizeof(buf), "%lld", prt[idx]);
+ if (debug_mode) {
+ printf("Time stamp %s\n", buf);
+ }
+ PR_ExplodeTime(prt[idx], PR_GMTParameters, &et_tmp);
+ if (!ExplodedTimeIsEqual(&et_tmp, &gmt[idx])) {
+ fprintf(stderr, "GMT not equal\n");
+ PrintExplodedTime(&et_tmp);
+ PrintExplodedTime(&gmt[idx]);
+ exit(1);
+ }
+ prt_tmp = PR_ImplodeTime(&et_tmp);
+ if (LL_NE(prt_tmp, prt[idx])) {
+ fprintf(stderr, "PRTime not equal\n");
+ exit(1);
+ }
+ if (debug_mode) {
+ printf("GMT: ");
+ PrintExplodedTime(&et_tmp);
+ printf("\n");
+ }
+
+ PR_ExplodeTime(prt[idx], PR_USPacificTimeParameters, &et_tmp);
+ if (!ExplodedTimeIsEqual(&et_tmp, &uspt[idx])) {
+ fprintf(stderr, "US Pacific Time not equal\n");
+ PrintExplodedTime(&et_tmp);
+ PrintExplodedTime(&uspt[idx]);
+ exit(1);
+ }
+ prt_tmp = PR_ImplodeTime(&et_tmp);
+ if (LL_NE(prt_tmp, prt[idx])) {
+ fprintf(stderr, "PRTime not equal\n");
+ exit(1);
+ }
+ if (debug_mode) {
+ printf("US Pacific Time: ");
+ PrintExplodedTime(&et_tmp);
+ printf("\n");
+ }
+
+ PR_ExplodeTime(prt[idx], PR_LocalTimeParameters, &et_tmp);
+ if (!ExplodedTimeIsEqual(&et_tmp, &localt[idx])) {
+ fprintf(stderr, "not equal\n");
+ PrintExplodedTime(&et_tmp);
+ PrintExplodedTime(&localt[idx]);
+ exit(1);
+ }
+ prt_tmp = PR_ImplodeTime(&et_tmp);
+ if (LL_NE(prt_tmp, prt[idx])) {
+ fprintf(stderr, "not equal\n");
+ exit(1);
+ }
+ if (debug_mode) {
+ printf("Local time:");
+ PrintExplodedTime(&et_tmp);
+ printf("\n\n");
+ }
+ }
+
+ now = PR_Now();
+ PR_ExplodeTime(now, PR_GMTParameters, &et_tmp);
+ printf("Current GMT is ");
+ PrintExplodedTime(&et_tmp);
+ printf("\n");
+ prt_tmp = PR_ImplodeTime(&et_tmp);
+ if (LL_NE(prt_tmp, now)) {
+ fprintf(stderr, "not equal\n");
+ exit(1);
+ }
+ PR_ExplodeTime(now, PR_USPacificTimeParameters, &et_tmp);
+ printf("Current US Pacific Time is ");
+ PrintExplodedTime(&et_tmp);
+ printf("\n");
+ prt_tmp = PR_ImplodeTime(&et_tmp);
+ if (LL_NE(prt_tmp, now)) {
+ fprintf(stderr, "not equal\n");
+ exit(1);
+ }
+ PR_ExplodeTime(now, PR_LocalTimeParameters, &et_tmp);
+ printf("Current local time is ");
+ PrintExplodedTime(&et_tmp);
+ printf("\n");
+ prt_tmp = PR_ImplodeTime(&et_tmp);
+ if (LL_NE(prt_tmp, now)) {
+ fprintf(stderr, "not equal\n");
+ exit(1);
+ }
+ printf("Please verify the results\n\n");
+
+ if (debug_mode) {
+ printf("Test 1 passed\n");
+ }
+ return PR_SUCCESS;
+}
+/* End of Test 1: TestExplodeImplodeTime */
+
+/*
+ * Test 2: Normalize Time
+ */
+
+/*
+ * time increment for addition to PRExplodeTime
+ */
+typedef struct time_increment {
+ PRInt32 ti_usec;
+ PRInt32 ti_sec;
+ PRInt32 ti_min;
+ PRInt32 ti_hour;
+} time_increment_t;
+
+/*
+ * Data for testing PR_Normalize
+ * Add the increment to base_time, normalize it to GMT and US Pacific
+ * Time zone.
+ */
+typedef struct normalize_test_data {
+ PRExplodedTime base_time;
+ time_increment_t increment;
+ PRExplodedTime expected_gmt_time;
+ PRExplodedTime expected_uspt_time;
+} normalize_test_data_t;
+
+
+/*
+ * Test data - the base time values cover dates of interest including y2k - 1,
+ * y2k + 1, y2k leap year, y2k leap date + 1year,
+ * y2k leap date + 4 years
+ */
+normalize_test_data_t normalize_test_array[] = {
+ /*usec sec min hour mday mo year wday yday {gmtoff, dstoff }*/
+
+ /* Fri 12/31/1999 19:32:48 PST */
+ { {0, 48, 32, 19, 31, 11, 1999, 5, 364, { -28800, 0}},
+ {0, 0, 30, 20},
+ {0, 48, 2, 0, 2, 0, 2000, 0, 1, { 0, 0}}, /*Sun Jan 2 00:02:48 UTC 2000*/
+ {0, 48, 2, 16, 1, 0, 2000, 6, 0, { -28800, 0}},/* Sat Jan 1 16:02:48
+ PST 2000*/
+ },
+ /* Fri 99-12-31 23:59:02 GMT */
+ { {0, 2, 59, 23, 31, 11, 1999, 5, 364, { 0, 0}},
+ {0, 0, 45, 0},
+ {0, 2, 44, 0, 1, 0, 2000, 6, 0, { 0, 0}},/* Sat Jan 1 00:44:02 UTC 2000*/
+ {0, 2, 44, 16, 31, 11, 1999, 5, 364, { -28800, 0}}/*Fri Dec 31 16:44:02
+ PST 1999*/
+ },
+ /* 99-12-25 12:00:00 GMT */
+ { {0, 0, 0, 12, 25, 11, 1999, 6, 358, { 0, 0}},
+ {0, 0, 0, 364 * 24},
+ {0, 0, 0, 12, 23, 11, 2000, 6, 357, { 0, 0}},/*Sat Dec 23 12:00:00
+ 2000 UTC*/
+ {0, 0, 0, 4, 23, 11, 2000, 6, 357, { -28800, 0}}/*Sat Dec 23 04:00:00
+ 2000 -0800*/
+ },
+ /* 00-01-1 00:00:00 PST */
+ { {0, 0, 0, 0, 1, 0, 2000, 6, 0, { -28800, 0}},
+ {0, 0, 0, 48},
+ {0, 0, 0, 8, 3, 0, 2000, 1, 2, { 0, 0}},/*Mon Jan 3 08:00:00 2000 UTC*/
+ {0, 0, 0, 0, 3, 0, 2000, 1, 2, { -28800, 0}}/*Mon Jan 3 00:00:00 2000
+ -0800*/
+ },
+ /* 00-01-10 12:00:00 PST */
+ { {0, 0, 0, 12, 10, 0, 2000, 1, 9, { -28800, 0}},
+ {0, 0, 0, 364 * 5 * 24},
+ {0, 0, 0, 20, 3, 0, 2005, 1, 2, { 0, 0}},/*Mon Jan 3 20:00:00 2005 UTC */
+ {0, 0, 0, 12, 3, 0, 2005, 1, 2, { -28800, 0}}/*Mon Jan 3 12:00:00
+ 2005 -0800*/
+ },
+ /* 00-02-28 15:39 GMT */
+ { {0, 0, 39, 15, 28, 1, 2000, 1, 58, { 0, 0}},
+ {0, 0, 0, 24},
+ {0, 0, 39, 15, 29, 1, 2000, 2, 59, { 0, 0}}, /*Tue Feb 29 15:39:00 2000
+ UTC*/
+ {0, 0, 39, 7, 29, 1, 2000, 2, 59, { -28800, 0}}/*Tue Feb 29 07:39:00
+ 2000 -0800*/
+ },
+ /* 01-03-01 12:00 PST */
+ { {0, 0, 0, 12, 3, 0, 2001, 3, 2, { -28800, 0}},/*Wed Jan 3 12:00:00
+ -0800 2001*/
+ {0, 30, 30,45},
+ {0, 30, 30, 17, 5, 0, 2001, 5, 4, { 0, 0}}, /*Fri Jan 5 17:30:30 2001
+ UTC*/
+ {0, 30, 30, 9, 5, 0, 2001, 5, 4, { -28800, 0}} /*Fri Jan 5 09:30:30
+ 2001 -0800*/
+ },
+ /* 2004-04-26 12:00 GMT */
+ { {0, 0, 0, 20, 3, 0, 2001, 3, 2, { 0, 0}},
+ {0, 0, 30,0},
+ {0, 0, 30, 20, 3, 0, 2001, 3, 2, { 0, 0}},/*Wed Jan 3 20:30:00 2001 UTC*/
+ {0, 0, 30, 12, 3, 0, 2001, 3, 2, { -28800, 0}}/*Wed Jan 3 12:30:00
+ 2001 -0800*/
+ },
+ /* 99-09-09 00:00 GMT */
+ { {0, 0, 0, 0, 9, 8, 1999, 4, 251, { 0, 0}},
+ {0, 0, 0, 12},
+ {0, 0, 0, 12, 9, 8, 1999, 4, 251, { 0, 0}},/*Thu Sep 9 12:00:00 1999 UTC*/
+ {0, 0, 0, 5, 9, 8, 1999, 4, 251, { -28800, 3600}}/*Thu Sep 9 05:00:00
+ 1999 -0700*/
+ }
+};
+
+void add_time_increment(PRExplodedTime *et1, time_increment_t *it)
+{
+ et1->tm_usec += it->ti_usec;
+ et1->tm_sec += it->ti_sec;
+ et1->tm_min += it->ti_min;
+ et1->tm_hour += it->ti_hour;
+}
+
+/*
+** TestNormalizeTime() -- Test PR_NormalizeTime()
+** For each data item, add the time increment to the base_time and then
+** normalize it for GMT and local time zones. This test assumes that
+** the local time zone is the Pacific Time Zone. The normalized values
+** should match the expected values in the data item.
+**
+*/
+PRStatus TestNormalizeTime(void)
+{
+ int idx, count;
+ normalize_test_data_t *itemp;
+ time_increment_t *itp;
+
+ count = sizeof(normalize_test_array)/sizeof(normalize_test_array[0]);
+ for (idx = 0; idx < count; idx++) {
+ itemp = &normalize_test_array[idx];
+ if (debug_mode) {
+ printf("%2d. %15s",idx +1,"Base time: ");
+ PrintExplodedTime(&itemp->base_time);
+ printf("\n");
+ }
+ itp = &itemp->increment;
+ if (debug_mode) {
+ printf("%20s %2d hrs %2d min %3d sec\n","Add",itp->ti_hour,
+ itp->ti_min, itp->ti_sec);
+ }
+ add_time_increment(&itemp->base_time, &itemp->increment);
+ PR_NormalizeTime(&itemp->base_time, PR_LocalTimeParameters);
+ if (debug_mode) {
+ printf("%19s","PST time: ");
+ PrintExplodedTime(&itemp->base_time);
+ printf("\n");
+ }
+ if (!ExplodedTimeIsEqual(&itemp->base_time,
+ &itemp->expected_uspt_time)) {
+ printf("PR_NormalizeTime failed\n");
+ if (debug_mode) {
+ PrintExplodedTime(&itemp->expected_uspt_time);
+ }
+ return PR_FAILURE;
+ }
+ PR_NormalizeTime(&itemp->base_time, PR_GMTParameters);
+ if (debug_mode) {
+ printf("%19s","GMT time: ");
+ PrintExplodedTime(&itemp->base_time);
+ printf("\n");
+ }
+
+ if (!ExplodedTimeIsEqual(&itemp->base_time,
+ &itemp->expected_gmt_time)) {
+ printf("PR_NormalizeTime failed\n");
+ return PR_FAILURE;
+ }
+ }
+ return PR_SUCCESS;
+}
+
+
+/*
+** ParseTest. Structure defining a string time and a matching exploded time
+**
+*/
+typedef struct ParseTest
+{
+ char *sDate; /* string to be converted using PR_ParseTimeString() */
+ PRExplodedTime et; /* expected result of the conversion */
+} ParseTest;
+
+static ParseTest parseArray[] =
+{
+ /* |<----- expected result ------------------------------------------->| */
+ /* "string to test" usec sec min hour day mo year wday julian {gmtoff, dstoff }*/
+ { "Thursday 1 Jan 1970 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "1 Jan 1970 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "1-Jan-1970 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "01-Jan-1970 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "January 1, 1970", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "January 1, 1970 00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "January 01, 1970 00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "January 01 1970 00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "January 01 1970 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "01-01-1970", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "01/01/1970", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "01/01/70", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "01/01/70 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "70/01/01 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "70/1/1 00:00:", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "00:00 Thursday, January 1, 1970",{ 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "1-Jan-70 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "70-01-01 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+ { "70/01/01 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}},
+
+ /* 31-Dec-1969 */
+ { "Wed 31 Dec 1969 00:00:00", { 000000, 00, 00, 00, 31, 11, 1969, 3, 364, {-28800, 0 }}},
+ { "31 Dec 1969 00:00:00", { 000000, 00, 00, 00, 31, 11, 1969, 3, 364, {-28800, 0 }}},
+ { "12/31/69 00:00:00", { 000000, 00, 00, 00, 31, 11, 2069, 2, 364, {-28800, 0 }}},
+ { "12/31/1969 00:00:00", { 000000, 00, 00, 00, 31, 11, 1969, 3, 364, {-28800, 0 }}},
+ { "12-31-69 00:00:00", { 000000, 00, 00, 00, 31, 11, 2069, 2, 364, {-28800, 0 }}},
+ { "12-31-1969 00:00:00", { 000000, 00, 00, 00, 31, 11, 1969, 3, 364, {-28800, 0 }}},
+ { "69-12-31 00:00:00", { 000000, 00, 00, 00, 31, 11, 2069, 2, 364, {-28800, 0 }}},
+ { "69/12/31 00:00:00", { 000000, 00, 00, 00, 31, 11, 2069, 2, 364, {-28800, 0 }}},
+
+ /* "Sun". 31-Dec-1998 (?) */
+ { "Thu 31 Dec 1998 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}},
+ { "12/31/98 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}},
+ { "12/31/1998 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}},
+ { "12-31-98 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}},
+ { "12-31-1998 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}},
+ { "98-12-31 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}},
+ { "98/12/31 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}},
+
+ /* 09-Sep-1999. Interesting because of its use as an eof marker? */
+ { "09 Sep 1999 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}},
+ { "9/9/99 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}},
+ { "9/9/1999 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}},
+ { "9-9-99 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}},
+ { "9-9-1999 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}},
+ { "09-09-99 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}},
+ { "09-09-1999 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}},
+ { "99-09-09 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}},
+
+ /* "Sun". 10-Sep-1999. Because Sun said so. */
+ { "10 Sep 1999 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}},
+ { "9/10/99 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}},
+ { "9/10/1999 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}},
+ { "9-10-99 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}},
+ { "9-10-1999 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}},
+ { "09-10-99 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}},
+ { "09-10-1999 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}},
+ { "99-09-10 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}},
+
+ /* 31-Dec-1999 */
+ { "31 Dec 1999 00:00:00", { 000000, 00, 00, 00, 31, 11, 1999, 5, 364, {-28800, 0 }}},
+ { "12/31/99 00:00:00", { 000000, 00, 00, 00, 31, 11, 1999, 5, 364, {-28800, 0 }}},
+ { "12/31/1999 00:00:00", { 000000, 00, 00, 00, 31, 11, 1999, 5, 364, {-28800, 0 }}},
+ { "12-31-99 00:00:00", { 000000, 00, 00, 00, 31, 11, 1999, 5, 364, {-28800, 0 }}},
+ { "12-31-1999 00:00:00", { 000000, 00, 00, 00, 31, 11, 1999, 5, 364, {-28800, 0 }}},
+ { "99-12-31 00:00:00", { 000000, 00, 00, 00, 31, 11, 1999, 5, 364, {-28800, 0 }}},
+ { "99/12/31 00:00:00", { 000000, 00, 00, 00, 31, 11, 1999, 5, 364, {-28800, 0 }}},
+
+ /* 01-Jan-2000 */
+ { "01 Jan 2000 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}},
+ { "1/1/00 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}},
+ { "1/1/2000 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}},
+ { "1-1-00 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}},
+ { "1-1-2000 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}},
+ { "01-01-00 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}},
+ { "Saturday 01-01-2000 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}},
+
+ /* "Sun". 28-Feb-2000 */
+ { "28 Feb 2000 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}},
+ { "2/28/00 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}},
+ { "2/28/2000 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}},
+ { "2-28-00 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}},
+ { "2-28-2000 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}},
+ { "02-28-00 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}},
+ { "02-28-2000 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}},
+
+ /* 29-Feb-2000 */
+ { "29 Feb 2000 00:00:00", { 000000, 00, 00, 00, 29, 1, 2000, 2, 59, {-28800, 0 }}},
+ { "2/29/00 00:00:00", { 000000, 00, 00, 00, 29, 1, 2000, 2, 59, {-28800, 0 }}},
+ { "2/29/2000 00:00:00", { 000000, 00, 00, 00, 29, 1, 2000, 2, 59, {-28800, 0 }}},
+ { "2-29-00 00:00:00", { 000000, 00, 00, 00, 29, 1, 2000, 2, 59, {-28800, 0 }}},
+ { "2-29-2000 00:00:00", { 000000, 00, 00, 00, 29, 1, 2000, 2, 59, {-28800, 0 }}},
+ { "02-29-00 00:00:00", { 000000, 00, 00, 00, 29, 1, 2000, 2, 59, {-28800, 0 }}},
+ { "02-29-2000 00:00:00", { 000000, 00, 00, 00, 29, 1, 2000, 2, 59, {-28800, 0 }}},
+
+ /* 01-Mar-2000 */
+ { "01 Mar 2000 00:00:00", { 000000, 00, 00, 00, 1, 2, 2000, 3, 60, {-28800, 0 }}},
+ { "3/1/00 00:00:00", { 000000, 00, 00, 00, 1, 2, 2000, 3, 60, {-28800, 0 }}},
+ { "3/1/2000 00:00:00", { 000000, 00, 00, 00, 1, 2, 2000, 3, 60, {-28800, 0 }}},
+ { "3-1-00 00:00:00", { 000000, 00, 00, 00, 1, 2, 2000, 3, 60, {-28800, 0 }}},
+ { "03-01-00 00:00:00", { 000000, 00, 00, 00, 1, 2, 2000, 3, 60, {-28800, 0 }}},
+ { "03-01-2000 00:00:00", { 000000, 00, 00, 00, 1, 2, 2000, 3, 60, {-28800, 0 }}},
+
+ /* "Sun". 31-Dec-2000 */
+ { "31 Dec 2000 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}},
+ { "12/31/00 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}},
+ { "12/31/2000 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}},
+ { "12-31-00 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}},
+ { "12-31-2000 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}},
+ { "00-12-31 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}},
+ { "00/12/31 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}},
+
+ /* "Sun". 01-Jan-2001 */
+ { "01 Jan 2001 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}},
+ { "1/1/01 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}},
+ { "1/1/2001 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}},
+ { "1-1-01 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}},
+ { "1-1-2001 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}},
+ { "01-01-01 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}},
+ { "Saturday 01-01-2001 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}},
+
+ /* 01-Mar-2001 */
+ { "01 Mar 2001 00:00:00", { 000000, 00, 00, 00, 1, 2, 2001, 4, 59, {-28800, 0 }}},
+ { "3/1/01 00:00:00", { 000000, 00, 00, 00, 1, 2, 2001, 4, 59, {-28800, 0 }}},
+ { "3/1/2001 00:00:00", { 000000, 00, 00, 00, 1, 2, 2001, 4, 59, {-28800, 0 }}},
+ { "3-1-01 00:00:00", { 000000, 00, 00, 00, 1, 2, 2001, 4, 59, {-28800, 0 }}},
+ { "3-1-2001 00:00:00", { 000000, 00, 00, 00, 1, 2, 2001, 4, 59, {-28800, 0 }}},
+ { "03-01-01 00:00:00", { 000000, 00, 00, 00, 1, 2, 2001, 4, 59, {-28800, 0 }}},
+ { "03-01-2001 00:00:00", { 000000, 00, 00, 00, 1, 2, 2001, 4, 59, {-28800, 0 }}},
+
+ /* 29-Feb-2004 */
+ { "29 Feb 2004 00:00:00", { 000000, 00, 00, 00, 29, 1, 2004, 0, 59, {-28800, 0 }}},
+ { "2/29/04 00:00:00", { 000000, 00, 00, 00, 29, 1, 2004, 0, 59, {-28800, 0 }}},
+ { "2/29/2004 00:00:00", { 000000, 00, 00, 00, 29, 1, 2004, 0, 59, {-28800, 0 }}},
+ { "2-29-04 00:00:00", { 000000, 00, 00, 00, 29, 1, 2004, 0, 59, {-28800, 0 }}},
+ { "2-29-2004 00:00:00", { 000000, 00, 00, 00, 29, 1, 2004, 0, 59, {-28800, 0 }}},
+
+ /* 01-Mar-2004 */
+ { "01 Mar 2004 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}},
+ { "3/1/04 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}},
+ { "3/1/2004 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}},
+ { "3-1-04 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}},
+ { "3-1-2004 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}},
+ { "03-01-04 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}},
+ { "03-01-2004 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}},
+
+ /* 01-Mar-2005 */
+ { "01 Mar 2005 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}},
+ { "3/1/05 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}},
+ { "3/1/2005 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}},
+ { "3-1-05 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}},
+ { "3-1-2005 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}},
+ { "03-01-05 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}},
+ { "03-01-2005 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}},
+
+ /* last element. string must be null */
+ { NULL }
+}; /* end array of ParseTest */
+
+/*
+** TestParseTime() -- Test PR_ParseTimeString() for y2k compliance
+**
+** TestParseTime() loops thru the array parseArray. For each element in
+** the array, he calls PR_ParseTimeString() with sDate as the conversion
+** argument. The result (ct) is then converted to a PRExplodedTime structure
+** and compared with the exploded time value (parseArray[n].et) in the
+** array element; if equal, the element passes the test.
+**
+** The array parseArray[] contains entries that are interesting to the
+** y2k problem.
+**
+**
+*/
+static PRStatus TestParseTime( void )
+{
+ ParseTest *ptp = parseArray;
+ PRTime ct;
+ PRExplodedTime cet;
+ char *sp = ptp->sDate;
+ PRStatus rc;
+ PRStatus rv = PR_SUCCESS;
+
+ while ( sp != NULL)
+ {
+ rc = PR_ParseTimeString( sp, PR_FALSE, &ct );
+ if ( PR_FAILURE == rc )
+ {
+ printf("TestParseTime(): PR_ParseTimeString() failed to convert: %s\n", sp );
+ rv = PR_FAILURE;
+ failed_already = 1;
+ }
+ else
+ {
+ PR_ExplodeTime( ct, PR_LocalTimeParameters, &cet );
+
+ if ( !ExplodedTimeIsEqual( &cet, &ptp->et ))
+ {
+ printf("TestParseTime(): Exploded time compare failed: %s\n", sp );
+ if ( debug_mode )
+ {
+ PrintExplodedTime( &cet );
+ printf("\n");
+ PrintExplodedTime( &ptp->et );
+ printf("\n");
+ }
+
+ rv = PR_FAILURE;
+ failed_already = 1;
+ }
+ }
+
+ /* point to next element in array, keep going */
+ ptp++;
+ sp = ptp->sDate;
+ } /* end while() */
+
+ return( rv );
+} /* end TestParseTime() */
+
+int main(int argc, char** argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt;
+
+ PR_STDIO_INIT();
+ opt = PL_CreateOptState(argc, argv, "d");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ lm = PR_NewLogModule("test");
+
+ if ( PR_FAILURE == TestExplodeImplodeTime())
+ {
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("TestExplodeImplodeTime() failed"));
+ }
+ else {
+ printf("Test 1: Calendar Time Test passed\n");
+ }
+
+ if ( PR_FAILURE == TestNormalizeTime())
+ {
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("TestNormalizeTime() failed"));
+ }
+ else {
+ printf("Test 2: Normalize Time Test passed\n");
+ }
+
+ if ( PR_FAILURE == TestParseTime())
+ {
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("TestParseTime() failed"));
+ }
+ else {
+ printf("Test 3: Parse Time Test passed\n");
+ }
+
+ if (failed_already) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+} /* end main() y2k.c */
+
diff --git a/nsprpub/pr/tests/y2ktmo.c b/nsprpub/pr/tests/y2ktmo.c
new file mode 100644
index 0000000000..b28467e4d5
--- /dev/null
+++ b/nsprpub/pr/tests/y2ktmo.c
@@ -0,0 +1,554 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Test: y2ktmo
+ *
+ * Description:
+ * This test tests the interval time facilities in NSPR for Y2K
+ * compliance. All the functions that take a timeout argument
+ * are tested: PR_Sleep, socket I/O (PR_Accept is taken as a
+ * representative), PR_Poll, PR_WaitCondVar, PR_Wait, and
+ * PR_CWait. A thread of each thread scope (local, global, and
+ * global bound) is created to call each of these functions.
+ * The test should be started at the specified number of seconds
+ * (called the lead time) before a Y2K rollover test date. The
+ * timeout values for these threads will span over the rollover
+ * date by at least the specified number of seconds. For
+ * example, if the lead time is 5 seconds, the test should
+ * be started at time (D - 5), where D is a rollover date, and
+ * the threads will time out at or after time (D + 5). The
+ * timeout values for the threads are spaced one second apart.
+ *
+ * When a thread times out, it calls PR_IntervalNow() to verify
+ * that it did wait for the specified time. In addition, it
+ * calls a platform-native function to verify the actual elapsed
+ * time again, to rule out the possibility that PR_IntervalNow()
+ * is broken. We allow the actual elapsed time to deviate from
+ * the specified timeout by a certain tolerance (in milliseconds).
+ */
+
+#include "nspr.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(XP_UNIX)
+#include <sys/time.h> /* for gettimeofday */
+#endif
+#if defined(WIN32)
+#if defined(WINCE)
+#include <windows.h>
+#else
+#include <sys/types.h>
+#include <sys/timeb.h> /* for _ftime */
+#endif
+#endif
+
+#define DEFAULT_LEAD_TIME_SECS 5
+#define DEFAULT_TOLERANCE_MSECS 500
+
+static PRBool debug_mode = PR_FALSE;
+static PRInt32 lead_time_secs = DEFAULT_LEAD_TIME_SECS;
+static PRInt32 tolerance_msecs = DEFAULT_TOLERANCE_MSECS;
+static PRIntervalTime start_time;
+static PRIntervalTime tolerance;
+
+#if defined(XP_UNIX)
+static struct timeval start_time_tv;
+#endif
+#if defined(WIN32)
+#if defined(WINCE)
+static DWORD start_time_tick;
+#else
+static struct _timeb start_time_tb;
+#endif
+#endif
+
+static void SleepThread(void *arg)
+{
+ PRIntervalTime timeout = (PRIntervalTime) arg;
+ PRIntervalTime elapsed;
+#if defined(XP_UNIX) || defined(WIN32)
+ PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout);
+ PRInt32 elapsed_msecs;
+#endif
+#if defined(XP_UNIX)
+ struct timeval end_time_tv;
+#endif
+#if defined(WIN32) && !defined(WINCE)
+ struct _timeb end_time_tb;
+#endif
+
+ if (PR_Sleep(timeout) == PR_FAILURE) {
+ fprintf(stderr, "PR_Sleep failed\n");
+ exit(1);
+ }
+ elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time);
+ if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) {
+ fprintf(stderr, "timeout wrong\n");
+ exit(1);
+ }
+#if defined(XP_UNIX)
+ gettimeofday(&end_time_tv, NULL);
+ elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec)
+ + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000;
+#endif
+#if defined(WIN32)
+#if defined(WINCE)
+ elapsed_msecs = GetTickCount() - start_time_tick;
+#else
+ _ftime(&end_time_tb);
+ elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time)
+ + (end_time_tb.millitm - start_time_tb.millitm);
+#endif
+#endif
+#if defined(XP_UNIX) || defined(WIN32)
+ if (elapsed_msecs + tolerance_msecs < timeout_msecs
+ || elapsed_msecs > timeout_msecs + tolerance_msecs) {
+ fprintf(stderr, "timeout wrong\n");
+ exit(1);
+ }
+#endif
+ if (debug_mode) {
+ fprintf(stderr, "Sleep thread (scope %d) done\n",
+ PR_GetThreadScope(PR_GetCurrentThread()));
+ }
+}
+
+static void AcceptThread(void *arg)
+{
+ PRIntervalTime timeout = (PRIntervalTime) arg;
+ PRIntervalTime elapsed;
+#if defined(XP_UNIX) || defined(WIN32)
+ PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout);
+ PRInt32 elapsed_msecs;
+#endif
+#if defined(XP_UNIX)
+ struct timeval end_time_tv;
+#endif
+#if defined(WIN32) && !defined(WINCE)
+ struct _timeb end_time_tb;
+#endif
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ PRFileDesc *accepted;
+
+ sock = PR_NewTCPSocket();
+ if (sock == NULL) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ exit(1);
+ }
+ memset(&addr, 0, sizeof(addr));
+ addr.inet.family = PR_AF_INET;
+ addr.inet.port = 0;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ if (PR_Bind(sock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_Bind failed\n");
+ exit(1);
+ }
+ if (PR_Listen(sock, 5) == PR_FAILURE) {
+ fprintf(stderr, "PR_Listen failed\n");
+ exit(1);
+ }
+ accepted = PR_Accept(sock, NULL, timeout);
+ if (accepted != NULL || PR_GetError() != PR_IO_TIMEOUT_ERROR) {
+ fprintf(stderr, "PR_Accept did not time out\n");
+ exit(1);
+ }
+ elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time);
+ if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) {
+ fprintf(stderr, "timeout wrong\n");
+ exit(1);
+ }
+#if defined(XP_UNIX)
+ gettimeofday(&end_time_tv, NULL);
+ elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec)
+ + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000;
+#endif
+#if defined(WIN32)
+#if defined(WINCE)
+ elapsed_msecs = GetTickCount() - start_time_tick;
+#else
+ _ftime(&end_time_tb);
+ elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time)
+ + (end_time_tb.millitm - start_time_tb.millitm);
+#endif
+#endif
+#if defined(XP_UNIX) || defined(WIN32)
+ if (elapsed_msecs + tolerance_msecs < timeout_msecs
+ || elapsed_msecs > timeout_msecs + tolerance_msecs) {
+ fprintf(stderr, "timeout wrong\n");
+ exit(1);
+ }
+#endif
+ if (PR_Close(sock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ if (debug_mode) {
+ fprintf(stderr, "Accept thread (scope %d) done\n",
+ PR_GetThreadScope(PR_GetCurrentThread()));
+ }
+}
+
+static void PollThread(void *arg)
+{
+ PRIntervalTime timeout = (PRIntervalTime) arg;
+ PRIntervalTime elapsed;
+#if defined(XP_UNIX) || defined(WIN32)
+ PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout);
+ PRInt32 elapsed_msecs;
+#endif
+#if defined(XP_UNIX)
+ struct timeval end_time_tv;
+#endif
+#if defined(WIN32) && !defined(WINCE)
+ struct _timeb end_time_tb;
+#endif
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ PRPollDesc pd;
+ PRIntn rv;
+
+ sock = PR_NewTCPSocket();
+ if (sock == NULL) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ exit(1);
+ }
+ memset(&addr, 0, sizeof(addr));
+ addr.inet.family = PR_AF_INET;
+ addr.inet.port = 0;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ if (PR_Bind(sock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_Bind failed\n");
+ exit(1);
+ }
+ if (PR_Listen(sock, 5) == PR_FAILURE) {
+ fprintf(stderr, "PR_Listen failed\n");
+ exit(1);
+ }
+ pd.fd = sock;
+ pd.in_flags = PR_POLL_READ;
+ rv = PR_Poll(&pd, 1, timeout);
+ if (rv != 0) {
+ fprintf(stderr, "PR_Poll did not time out\n");
+ exit(1);
+ }
+ elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time);
+ if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) {
+ fprintf(stderr, "timeout wrong\n");
+ exit(1);
+ }
+#if defined(XP_UNIX)
+ gettimeofday(&end_time_tv, NULL);
+ elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec)
+ + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000;
+#endif
+#if defined(WIN32)
+#if defined(WINCE)
+ elapsed_msecs = GetTickCount() - start_time_tick;
+#else
+ _ftime(&end_time_tb);
+ elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time)
+ + (end_time_tb.millitm - start_time_tb.millitm);
+#endif
+#endif
+#if defined(XP_UNIX) || defined(WIN32)
+ if (elapsed_msecs + tolerance_msecs < timeout_msecs
+ || elapsed_msecs > timeout_msecs + tolerance_msecs) {
+ fprintf(stderr, "timeout wrong\n");
+ exit(1);
+ }
+#endif
+ if (PR_Close(sock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ if (debug_mode) {
+ fprintf(stderr, "Poll thread (scope %d) done\n",
+ PR_GetThreadScope(PR_GetCurrentThread()));
+ }
+}
+
+static void WaitCondVarThread(void *arg)
+{
+ PRIntervalTime timeout = (PRIntervalTime) arg;
+ PRIntervalTime elapsed;
+#if defined(XP_UNIX) || defined(WIN32)
+ PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout);
+ PRInt32 elapsed_msecs;
+#endif
+#if defined(XP_UNIX)
+ struct timeval end_time_tv;
+#endif
+#if defined(WIN32) && !defined(WINCE)
+ struct _timeb end_time_tb;
+#endif
+ PRLock *ml;
+ PRCondVar *cv;
+
+ ml = PR_NewLock();
+ if (ml == NULL) {
+ fprintf(stderr, "PR_NewLock failed\n");
+ exit(1);
+ }
+ cv = PR_NewCondVar(ml);
+ if (cv == NULL) {
+ fprintf(stderr, "PR_NewCondVar failed\n");
+ exit(1);
+ }
+ PR_Lock(ml);
+ PR_WaitCondVar(cv, timeout);
+ PR_Unlock(ml);
+ elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time);
+ if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) {
+ fprintf(stderr, "timeout wrong\n");
+ exit(1);
+ }
+#if defined(XP_UNIX)
+ gettimeofday(&end_time_tv, NULL);
+ elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec)
+ + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000;
+#endif
+#if defined(WIN32)
+#if defined(WINCE)
+ elapsed_msecs = GetTickCount() - start_time_tick;
+#else
+ _ftime(&end_time_tb);
+ elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time)
+ + (end_time_tb.millitm - start_time_tb.millitm);
+#endif
+#endif
+#if defined(XP_UNIX) || defined(WIN32)
+ if (elapsed_msecs + tolerance_msecs < timeout_msecs
+ || elapsed_msecs > timeout_msecs + tolerance_msecs) {
+ fprintf(stderr, "timeout wrong\n");
+ exit(1);
+ }
+#endif
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+ if (debug_mode) {
+ fprintf(stderr, "wait cond var thread (scope %d) done\n",
+ PR_GetThreadScope(PR_GetCurrentThread()));
+ }
+}
+
+static void WaitMonitorThread(void *arg)
+{
+ PRIntervalTime timeout = (PRIntervalTime) arg;
+ PRIntervalTime elapsed;
+#if defined(XP_UNIX) || defined(WIN32)
+ PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout);
+ PRInt32 elapsed_msecs;
+#endif
+#if defined(XP_UNIX)
+ struct timeval end_time_tv;
+#endif
+#if defined(WIN32) && !defined(WINCE)
+ struct _timeb end_time_tb;
+#endif
+ PRMonitor *mon;
+
+ mon = PR_NewMonitor();
+ if (mon == NULL) {
+ fprintf(stderr, "PR_NewMonitor failed\n");
+ exit(1);
+ }
+ PR_EnterMonitor(mon);
+ PR_Wait(mon, timeout);
+ PR_ExitMonitor(mon);
+ elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time);
+ if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) {
+ fprintf(stderr, "timeout wrong\n");
+ exit(1);
+ }
+#if defined(XP_UNIX)
+ gettimeofday(&end_time_tv, NULL);
+ elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec)
+ + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000;
+#endif
+#if defined(WIN32)
+#if defined(WINCE)
+ elapsed_msecs = GetTickCount() - start_time_tick;
+#else
+ _ftime(&end_time_tb);
+ elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time)
+ + (end_time_tb.millitm - start_time_tb.millitm);
+#endif
+#endif
+#if defined(XP_UNIX) || defined(WIN32)
+ if (elapsed_msecs + tolerance_msecs < timeout_msecs
+ || elapsed_msecs > timeout_msecs + tolerance_msecs) {
+ fprintf(stderr, "timeout wrong\n");
+ exit(1);
+ }
+#endif
+ PR_DestroyMonitor(mon);
+ if (debug_mode) {
+ fprintf(stderr, "wait monitor thread (scope %d) done\n",
+ PR_GetThreadScope(PR_GetCurrentThread()));
+ }
+}
+
+static void WaitCMonitorThread(void *arg)
+{
+ PRIntervalTime timeout = (PRIntervalTime) arg;
+ PRIntervalTime elapsed;
+#if defined(XP_UNIX) || defined(WIN32)
+ PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout);
+ PRInt32 elapsed_msecs;
+#endif
+#if defined(XP_UNIX)
+ struct timeval end_time_tv;
+#endif
+#if defined(WIN32) && !defined(WINCE)
+ struct _timeb end_time_tb;
+#endif
+ int dummy;
+
+ PR_CEnterMonitor(&dummy);
+ PR_CWait(&dummy, timeout);
+ PR_CExitMonitor(&dummy);
+ elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time);
+ if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) {
+ fprintf(stderr, "timeout wrong\n");
+ exit(1);
+ }
+#if defined(XP_UNIX)
+ gettimeofday(&end_time_tv, NULL);
+ elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec)
+ + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000;
+#endif
+#if defined(WIN32)
+#if defined(WINCE)
+ elapsed_msecs = GetTickCount() - start_time_tick;
+#else
+ _ftime(&end_time_tb);
+ elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time)
+ + (end_time_tb.millitm - start_time_tb.millitm);
+#endif
+#endif
+#if defined(XP_UNIX) || defined(WIN32)
+ if (elapsed_msecs + tolerance_msecs < timeout_msecs
+ || elapsed_msecs > timeout_msecs + tolerance_msecs) {
+ fprintf(stderr, "timeout wrong\n");
+ exit(1);
+ }
+#endif
+ if (debug_mode) {
+ fprintf(stderr, "wait cached monitor thread (scope %d) done\n",
+ PR_GetThreadScope(PR_GetCurrentThread()));
+ }
+}
+
+typedef void (*NSPRThreadFunc)(void*);
+
+static NSPRThreadFunc threadFuncs[] = {
+ SleepThread, AcceptThread, PollThread,
+ WaitCondVarThread, WaitMonitorThread, WaitCMonitorThread
+};
+
+static PRThreadScope threadScopes[] = {
+ PR_LOCAL_THREAD, PR_GLOBAL_THREAD, PR_GLOBAL_BOUND_THREAD
+};
+
+static void Help(void)
+{
+ fprintf(stderr, "y2ktmo test program usage:\n");
+ fprintf(stderr, "\t-d debug mode (FALSE)\n");
+ fprintf(stderr, "\t-l <secs> lead time (%d)\n",
+ DEFAULT_LEAD_TIME_SECS);
+ fprintf(stderr, "\t-t <msecs> tolerance (%d)\n",
+ DEFAULT_TOLERANCE_MSECS);
+ fprintf(stderr, "\t-h this message\n");
+} /* Help */
+
+int main(int argc, char **argv)
+{
+ PRThread **threads;
+ int num_thread_funcs = sizeof(threadFuncs)/sizeof(NSPRThreadFunc);
+ int num_thread_scopes = sizeof(threadScopes)/sizeof(PRThreadScope);
+ int i, j;
+ int idx;
+ PRInt32 secs;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dl:t:h");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option) {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'l': /* lead time */
+ lead_time_secs = atoi(opt->value);
+ break;
+ case 't': /* tolerance */
+ tolerance_msecs = atoi(opt->value);
+ break;
+ case 'h':
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (debug_mode) {
+ fprintf(stderr, "lead time: %d secs\n", lead_time_secs);
+ fprintf(stderr, "tolerance: %d msecs\n", tolerance_msecs);
+ }
+
+ start_time = PR_IntervalNow();
+#if defined(XP_UNIX)
+ gettimeofday(&start_time_tv, NULL);
+#endif
+#if defined(WIN32)
+#ifdef WINCE
+ start_time_tick = GetTickCount();
+#else
+ _ftime(&start_time_tb);
+#endif
+#endif
+ tolerance = PR_MillisecondsToInterval(tolerance_msecs);
+
+ threads = PR_Malloc(
+ num_thread_scopes * num_thread_funcs * sizeof(PRThread*));
+ if (threads == NULL) {
+ fprintf(stderr, "PR_Malloc failed\n");
+ exit(1);
+ }
+
+ /* start to time out 5 seconds after a rollover date */
+ secs = lead_time_secs + 5;
+ idx = 0;
+ for (i = 0; i < num_thread_scopes; i++) {
+ for (j = 0; j < num_thread_funcs; j++) {
+ threads[idx] = PR_CreateThread(PR_USER_THREAD, threadFuncs[j],
+ (void*)PR_SecondsToInterval(secs), PR_PRIORITY_NORMAL,
+ threadScopes[i], PR_JOINABLE_THREAD, 0);
+ if (threads[idx] == NULL) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+ secs++;
+ idx++;
+ }
+ }
+ for (idx = 0; idx < num_thread_scopes*num_thread_funcs; idx++) {
+ if (PR_JoinThread(threads[idx]) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+ }
+ PR_Free(threads);
+ printf("PASS\n");
+ return 0;
+}
diff --git a/nsprpub/pr/tests/yield.c b/nsprpub/pr/tests/yield.c
new file mode 100644
index 0000000000..eb55926b58
--- /dev/null
+++ b/nsprpub/pr/tests/yield.c
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include "prthread.h"
+#include "prinit.h"
+#ifndef XP_OS2
+#include "private/pprmisc.h"
+#include <windows.h>
+#else
+#include "primpl.h"
+#include <os2.h>
+#endif
+
+#define THREADS 10
+
+
+void
+threadmain(void *_id)
+{
+ int id = (int)_id;
+ int index;
+
+ printf("thread %d alive\n", id);
+ for (index=0; index<10; index++) {
+ printf("thread %d yielding\n", id);
+ PR_Sleep(0);
+ printf("thread %d awake\n", id);
+ }
+ printf("thread %d dead\n", id);
+
+}
+
+int main(int argc, char **argv)
+{
+ int index;
+ PRThread *a[THREADS];
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 5);
+ PR_STDIO_INIT();
+
+ for (index=0; index<THREADS; index++) {
+ a[index] = PR_CreateThread(PR_USER_THREAD,
+ threadmain,
+ (void *)index,
+ PR_PRIORITY_NORMAL,
+ index%2?PR_LOCAL_THREAD:PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ }
+ for(index=0; index<THREADS; index++) {
+ PR_JoinThread(a[index]);
+ }
+ printf("main dying\n");
+}
diff --git a/nsprpub/pr/tests/zerolen.c b/nsprpub/pr/tests/zerolen.c
new file mode 100644
index 0000000000..a84bd43228
--- /dev/null
+++ b/nsprpub/pr/tests/zerolen.c
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Test: zerolen.c
+ *
+ * Description: a test for Bugzilla bug #17699. We perform
+ * the same test for PR_Writev, PR_Write, and PR_Send. In
+ * each test the server thread first fills up the connection
+ * to the client so that the next write operation will fail
+ * with EAGAIN. Then it calls PR_Writev, PR_Write, or PR_Send
+ * with a zero-length buffer. The client thread initially
+ * does not read so that the connection can be filled up.
+ * Then it empties the connection so that the server thread's
+ * PR_Writev, PR_Write, or PR_Send call can succeed.
+ *
+ * Bug #17699 is specific to the pthreads version on Unix,
+ * so on other platforms this test does nothing.
+ */
+
+#ifndef XP_UNIX
+
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ printf("PASS\n");
+ return 0;
+}
+
+#else /* XP_UNIX */
+
+#include "nspr.h"
+#include "private/pprio.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+static void ClientThread(void *arg)
+{
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ PRUint16 port = (PRUint16) arg;
+ char buf[1024];
+ PRInt32 nbytes;
+
+ sock = PR_NewTCPSocket();
+ if (NULL == sock) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ exit(1);
+ }
+ if (PR_InitializeNetAddr(PR_IpAddrLoopback, port, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_InitializeNetAddr failed\n");
+ exit(1);
+ }
+ if (PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ fprintf(stderr, "PR_Connect failed\n");
+ exit(1);
+ }
+ /*
+ * Sleep 5 seconds to force the server thread to get EAGAIN.
+ */
+ if (PR_Sleep(PR_SecondsToInterval(5)) == PR_FAILURE) {
+ fprintf(stderr, "PR_Sleep failed\n");
+ exit(1);
+ }
+ /*
+ * Then start reading.
+ */
+ while (nbytes = PR_Read(sock, buf, sizeof(buf)) > 0) {
+ /* empty loop body */
+ }
+ if (-1 == nbytes) {
+ fprintf(stderr, "PR_Read failed\n");
+ exit(1);
+ }
+ if (PR_Close(sock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+}
+
+int main()
+{
+ PRFileDesc *listenSock;
+ PRFileDesc *acceptSock;
+ int osfd;
+ PRThread *clientThread;
+ PRNetAddr addr;
+ char buf[1024];
+ PRInt32 nbytes;
+ PRIOVec iov;
+
+ memset(buf, 0, sizeof(buf)); /* Initialize the buffer. */
+ listenSock = PR_NewTCPSocket();
+ if (NULL == listenSock) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ exit(1);
+ }
+ if (PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_InitializeNetAddr failed\n");
+ exit(1);
+ }
+ if (PR_Bind(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_Bind failed\n");
+ exit(1);
+ }
+ /* Find out what port number we are bound to. */
+ if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ exit(1);
+ }
+ if (PR_Listen(listenSock, 5) == PR_FAILURE) {
+ fprintf(stderr, "PR_Listen failed\n");
+ exit(1);
+ }
+
+ /*
+ * First test PR_Writev.
+ */
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ ClientThread, (void *) PR_ntohs(PR_NetAddrInetPort(&addr)),
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (NULL == clientThread) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+ acceptSock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == acceptSock) {
+ fprintf(stderr, "PR_Accept failed\n");
+ exit(1);
+ }
+ osfd = PR_FileDesc2NativeHandle(acceptSock);
+ while (write(osfd, buf, sizeof(buf)) != -1) {
+ /* empty loop body */
+ }
+ if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) {
+ fprintf(stderr, "write failed\n");
+ exit(1);
+ }
+ iov.iov_base = buf;
+ iov.iov_len = 0;
+ printf("calling PR_Writev with a zero-length buffer\n");
+ fflush(stdout);
+ nbytes = PR_Writev(acceptSock, &iov, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (nbytes != 0) {
+ fprintf(stderr, "PR_Writev should return 0 but returns %d\n", nbytes);
+ exit(1);
+ }
+ if (PR_Close(acceptSock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ if (PR_JoinThread(clientThread) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+
+ /*
+ * Then test PR_Write.
+ */
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ ClientThread, (void *) PR_ntohs(PR_NetAddrInetPort(&addr)),
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (NULL == clientThread) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+ acceptSock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == acceptSock) {
+ fprintf(stderr, "PR_Accept failed\n");
+ exit(1);
+ }
+ osfd = PR_FileDesc2NativeHandle(acceptSock);
+ while (write(osfd, buf, sizeof(buf)) != -1) {
+ /* empty loop body */
+ }
+ if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) {
+ fprintf(stderr, "write failed\n");
+ exit(1);
+ }
+ printf("calling PR_Write with a zero-length buffer\n");
+ fflush(stdout);
+ nbytes = PR_Write(acceptSock, buf, 0);
+ if (nbytes != 0) {
+ fprintf(stderr, "PR_Write should return 0 but returns %d\n", nbytes);
+ exit(1);
+ }
+ if (PR_Close(acceptSock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ if (PR_JoinThread(clientThread) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+
+ /*
+ * Finally test PR_Send.
+ */
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ ClientThread, (void *) PR_ntohs(PR_NetAddrInetPort(&addr)),
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (NULL == clientThread) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ exit(1);
+ }
+ acceptSock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == acceptSock) {
+ fprintf(stderr, "PR_Accept failed\n");
+ exit(1);
+ }
+ osfd = PR_FileDesc2NativeHandle(acceptSock);
+ while (write(osfd, buf, sizeof(buf)) != -1) {
+ /* empty loop body */
+ }
+ if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) {
+ fprintf(stderr, "write failed\n");
+ exit(1);
+ }
+ printf("calling PR_Send with a zero-length buffer\n");
+ fflush(stdout);
+ nbytes = PR_Send(acceptSock, buf, 0, 0, PR_INTERVAL_NO_TIMEOUT);
+ if (nbytes != 0) {
+ fprintf(stderr, "PR_Send should return 0 but returns %d\n", nbytes);
+ exit(1);
+ }
+ if (PR_Close(acceptSock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ if (PR_JoinThread(clientThread) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ exit(1);
+ }
+
+ if (PR_Close(listenSock) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ exit(1);
+ }
+ printf("PASS\n");
+ return 0;
+}
+
+#endif /* XP_UNIX */