summaryrefslogtreecommitdiffstats
path: root/nsprpub/pr/src/md
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /nsprpub/pr/src/md
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'nsprpub/pr/src/md')
-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
79 files changed, 28453 insertions, 0 deletions
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);
+}