aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/sys/win32
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/emulator/sys/win32
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/emulator/sys/win32')
-rw-r--r--erts/emulator/sys/win32/dosmap.c282
-rw-r--r--erts/emulator/sys/win32/driver_int.h39
-rw-r--r--erts/emulator/sys/win32/erl.def4
-rw-r--r--erts/emulator/sys/win32/erl_main.c29
-rw-r--r--erts/emulator/sys/win32/erl_poll.c1361
-rw-r--r--erts/emulator/sys/win32/erl_win32_sys_ddll.c206
-rw-r--r--erts/emulator/sys/win32/erl_win_dyn_driver.h489
-rw-r--r--erts/emulator/sys/win32/erl_win_sys.h212
-rw-r--r--erts/emulator/sys/win32/sys.c3093
-rw-r--r--erts/emulator/sys/win32/sys_env.c261
-rw-r--r--erts/emulator/sys/win32/sys_float.c145
-rw-r--r--erts/emulator/sys/win32/sys_interrupt.c142
-rw-r--r--erts/emulator/sys/win32/sys_time.c96
13 files changed, 6359 insertions, 0 deletions
diff --git a/erts/emulator/sys/win32/dosmap.c b/erts/emulator/sys/win32/dosmap.c
new file mode 100644
index 0000000000..15416a66c5
--- /dev/null
+++ b/erts/emulator/sys/win32/dosmap.c
@@ -0,0 +1,282 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * _dosmaperr: maps Windows OS errors to Unix System V errno values
+ *
+ * Contributor: Michael Regen
+ */
+
+/* Only use for win32 if linking to MSVCR??.DLL and not if statically linking
+ to LIBCMT.LIB */
+#if defined(WIN32) && defined(_MT) && defined(_DLL)
+
+#include <errno.h>
+#include <winerror.h>
+#include <stdlib.h>
+
+/* Position in table = Windows OS error -> Posix errno
+** An exception for ERROR_NOT_ENOUGH_QUOTA - 1816 is in _dosmaperr
+*/
+static const unsigned char errMapTable[] = {
+ EINVAL, /* ERROR_SUCCESS 0 */
+ EINVAL, /* ERROR_INVALID_FUNCTION 1 */
+ ENOENT, /* ERROR_FILE_NOT_FOUND 2 */
+ ENOENT, /* ERROR_PATH_NOT_FOUND 3 */
+ EMFILE, /* ERROR_TOO_MANY_OPEN_FILES 4 */
+ EACCES, /* ERROR_ACCESS_DENIED 5 */
+ EBADF, /* ERROR_INVALID_HANDLE 6 */
+ ENOMEM, /* ERROR_ARENA_TRASHED 7 */
+ ENOMEM, /* ERROR_NOT_ENOUGH_MEMORY 8 */
+ ENOMEM, /* ERROR_INVALID_BLOCK 9 */
+ E2BIG, /* ERROR_BAD_ENVIRONMENT 10 */
+ ENOEXEC, /* ERROR_BAD_FORMAT 11 */
+ EINVAL, /* ERROR_INVALID_ACCESS 12 */
+ EINVAL, /* ERROR_INVALID_DATA 13 */
+ EINVAL, /* ERROR_OUTOFMEMORY 14 */
+ ENOENT, /* ERROR_INVALID_DRIVE 15 */
+ EACCES, /* ERROR_CURRENT_DIRECTORY 16 */
+ EXDEV, /* ERROR_NOT_SAME_DEVICE 17 */
+ ENOENT, /* ERROR_NO_MORE_FILES 18 */
+ EACCES, /* ERROR_WRITE_PROTECT 19 */
+ EACCES, /* ERROR_BAD_UNIT 20 */
+ EACCES, /* ERROR_NOT_READY 21 */
+ EACCES, /* ERROR_BAD_COMMAND 22 */
+ EACCES, /* ERROR_CRC 23 */
+ EACCES, /* ERROR_BAD_LENGTH 24 */
+ EACCES, /* ERROR_SEEK 25 */
+ EACCES, /* ERROR_NOT_DOS_DISK 26 */
+ EACCES, /* ERROR_SECTOR_NOT_FOUND 27 */
+ EACCES, /* ERROR_OUT_OF_PAPER 28 */
+ EACCES, /* ERROR_WRITE_FAULT 29 */
+ EACCES, /* ERROR_READ_FAULT 30 */
+ EACCES, /* ERROR_GEN_FAILURE 31 */
+ EACCES, /* ERROR_SHARING_VIOLATION 32 */
+ EACCES, /* ERROR_LOCK_VIOLATION 33 */
+ EACCES, /* ERROR_WRONG_DISK 34 */
+ EACCES, /* 35 */
+ EACCES, /* ERROR_SHARING_BUFFER_EXCEEDED 36 */
+ EINVAL, /* 37 */
+ EINVAL, /* ERROR_HANDLE_EOF 38 */
+ EINVAL, /* ERROR_HANDLE_DISK_FULL 39 */
+ EINVAL, /* 40 */
+ EINVAL, /* 41 */
+ EINVAL, /* 42 */
+ EINVAL, /* 43 */
+ EINVAL, /* 44 */
+ EINVAL, /* 45 */
+ EINVAL, /* 46 */
+ EINVAL, /* 47 */
+ EINVAL, /* 48 */
+ EINVAL, /* 49 */
+ EINVAL, /* ERROR_NOT_SUPPORTED 50 */
+ EINVAL, /* ERROR_REM_NOT_LIST 51 */
+ EINVAL, /* ERROR_DUP_NAME 52 */
+ ENOENT, /* ERROR_BAD_NETPATH 53 */
+ EINVAL, /* ERROR_NETWORK_BUSY 54 */
+ EINVAL, /* ERROR_DEV_NOT_EXIST 55 */
+ EINVAL, /* ERROR_TOO_MANY_CMDS 56 */
+ EINVAL, /* ERROR_ADAP_HDW_ERR 57 */
+ EINVAL, /* ERROR_BAD_NET_RESP 58 */
+ EINVAL, /* ERROR_UNEXP_NET_ERR 59 */
+ EINVAL, /* ERROR_BAD_REM_ADAP 60 */
+ EINVAL, /* ERROR_PRINTQ_FULL 61 */
+ EINVAL, /* ERROR_NO_SPOOL_SPACE 62 */
+ EINVAL, /* ERROR_PRINT_CANCELLED 63 */
+ EINVAL, /* ERROR_NETNAME_DELETED 64 */
+ EACCES, /* ERROR_NETWORK_ACCESS_DENIED 65 */
+ EINVAL, /* ERROR_BAD_DEV_TYPE 66 */
+ ENOENT, /* ERROR_BAD_NET_NAME 67 */
+ EINVAL, /* ERROR_TOO_MANY_NAMES 68 */
+ EINVAL, /* ERROR_TOO_MANY_SESS 69 */
+ EINVAL, /* ERROR_SHARING_PAUSED 70 */
+ EINVAL, /* ERROR_REQ_NOT_ACCEP 71 */
+ EINVAL, /* ERROR_REDIR_PAUSED 72 */
+ EINVAL, /* 73 */
+ EINVAL, /* 74 */
+ EINVAL, /* 75 */
+ EINVAL, /* 76 */
+ EINVAL, /* 77 */
+ EINVAL, /* 78 */
+ EINVAL, /* 79 */
+ EEXIST, /* ERROR_FILE_EXISTS 80 */
+ EINVAL, /* 81 */
+ EACCES, /* ERROR_CANNOT_MAKE 82 */
+ EACCES, /* ERROR_FAIL_I24 83 */
+ EINVAL, /* ERROR_OUT_OF_STRUCTURES 84 */
+ EINVAL, /* ERROR_ALREADY_ASSIGNED 85 */
+ EINVAL, /* ERROR_INVALID_PASSWORD 86 */
+ EINVAL, /* ERROR_INVALID_PARAMETER 87 */
+ EINVAL, /* ERROR_NET_WRITE_FAULT 88 */
+ EAGAIN, /* ERROR_NO_PROC_SLOTS 89 */
+ EINVAL, /* 90 */
+ EINVAL, /* 91 */
+ EINVAL, /* 92 */
+ EINVAL, /* 93 */
+ EINVAL, /* 94 */
+ EINVAL, /* 95 */
+ EINVAL, /* 96 */
+ EINVAL, /* 97 */
+ EINVAL, /* 98 */
+ EINVAL, /* 99 */
+ EINVAL, /* ERROR_TOO_MANY_SEMAPHORES 100 */
+ EINVAL, /* ERROR_EXCL_SEM_ALREADY_OWNED 101 */
+ EINVAL, /* ERROR_SEM_IS_SET 102 */
+ EINVAL, /* ERROR_TOO_MANY_SEM_REQUESTS 103 */
+ EINVAL, /* ERROR_INVALID_AT_INTERRUPT_TIME 104 */
+ EINVAL, /* ERROR_SEM_OWNER_DIED 105 */
+ EINVAL, /* ERROR_SEM_USER_LIMIT 106 */
+ EINVAL, /* ERROR_DISK_CHANGE 107 */
+ EACCES, /* ERROR_DRIVE_LOCKED 108 */
+ EPIPE, /* ERROR_BROKEN_PIPE 109 */
+ EINVAL, /* ERROR_OPEN_FAILED 110 */
+ EINVAL, /* ERROR_BUFFER_OVERFLOW 111 */
+ ENOSPC, /* ERROR_DISK_FULL 112 */
+ EINVAL, /* ERROR_NO_MORE_SEARCH_HANDLES 113 */
+ EBADF, /* ERROR_INVALID_TARGET_HANDLE 114 */
+ EINVAL, /* 115 */
+ EINVAL, /* 116 */
+ EINVAL, /* ERROR_INVALID_CATEGORY 117 */
+ EINVAL, /* ERROR_INVALID_VERIFY_SWITCH 118 */
+ EINVAL, /* ERROR_BAD_DRIVER_LEVEL 119 */
+ EINVAL, /* ERROR_CALL_NOT_IMPLEMENTED 120 */
+ EINVAL, /* ERROR_SEM_TIMEOUT 121 */
+ EINVAL, /* ERROR_INSUFFICIENT_BUFFER 122 */
+ EINVAL, /* ERROR_INVALID_NAME 123 */
+ EINVAL, /* ERROR_INVALID_LEVEL 124 */
+ EINVAL, /* ERROR_NO_VOLUME_LABEL 125 */
+ EINVAL, /* ERROR_MOD_NOT_FOUND 126 */
+ EINVAL, /* ERROR_PROC_NOT_FOUND 127 */
+ ECHILD, /* ERROR_WAIT_NO_CHILDREN 128 */
+ ECHILD, /* ERROR_CHILD_NOT_COMPLETE 129 */
+ EBADF, /* ERROR_DIRECT_ACCESS_HANDLE 130 */
+ EINVAL, /* ERROR_NEGATIVE_SEEK 131 */
+ EACCES, /* ERROR_SEEK_ON_DEVICE 132 */
+ EINVAL, /* ERROR_IS_JOIN_TARGET 133 */
+ EINVAL, /* ERROR_IS_JOINED 134 */
+ EINVAL, /* ERROR_IS_SUBSTED 135 */
+ EINVAL, /* ERROR_NOT_JOINED 136 */
+ EINVAL, /* ERROR_NOT_SUBSTED 137 */
+ EINVAL, /* ERROR_JOIN_TO_JOIN 138 */
+ EINVAL, /* ERROR_SUBST_TO_SUBST 139 */
+ EINVAL, /* ERROR_JOIN_TO_SUBST 140 */
+ EINVAL, /* ERROR_SUBST_TO_JOIN 141 */
+ EINVAL, /* ERROR_BUSY_DRIVE 142 */
+ EINVAL, /* ERROR_SAME_DRIVE 143 */
+ EINVAL, /* ERROR_DIR_NOT_ROOT 144 */
+ ENOTEMPTY, /* ERROR_DIR_NOT_EMPTY 145 */
+ EINVAL, /* ERROR_IS_SUBST_PATH 146 */
+ EINVAL, /* ERROR_IS_JOIN_PATH 147 */
+ EINVAL, /* ERROR_PATH_BUSY 148 */
+ EINVAL, /* ERROR_IS_SUBST_TARGET 149 */
+ EINVAL, /* ERROR_SYSTEM_TRACE 150 */
+ EINVAL, /* ERROR_INVALID_EVENT_COUNT 151 */
+ EINVAL, /* ERROR_TOO_MANY_MUXWAITERS 152 */
+ EINVAL, /* ERROR_INVALID_LIST_FORMAT 153 */
+ EINVAL, /* ERROR_LABEL_TOO_LONG 154 */
+ EINVAL, /* ERROR_TOO_MANY_TCBS 155 */
+ EINVAL, /* ERROR_SIGNAL_REFUSED 156 */
+ EINVAL, /* ERROR_DISCARDED 157 */
+ EACCES, /* ERROR_NOT_LOCKED 158 */
+ EINVAL, /* ERROR_BAD_THREADID_ADDR 159 */
+ EINVAL, /* ERROR_BAD_ARGUMENTS 160 */
+ ENOENT, /* ERROR_BAD_PATHNAME 161 */
+ EINVAL, /* ERROR_SIGNAL_PENDING 162 */
+ EINVAL, /* 163 */
+ EAGAIN, /* ERROR_MAX_THRDS_REACHED 164 */
+ EINVAL, /* 165 */
+ EINVAL, /* 166 */
+ EACCES, /* ERROR_LOCK_FAILED 167 */
+ EINVAL, /* 168 */
+ EINVAL, /* 169 */
+ EINVAL, /* ERROR_BUSY 170 */
+ EINVAL, /* 171 */
+ EINVAL, /* 172 */
+ EINVAL, /* ERROR_CANCEL_VIOLATION 173 */
+ EINVAL, /* ERROR_ATOMIC_LOCKS_NOT_SUPPORTED 174 */
+ EINVAL, /* 175 */
+ EINVAL, /* 176 */
+ EINVAL, /* 177 */
+ EINVAL, /* 178 */
+ EINVAL, /* 179 */
+ EINVAL, /* ERROR_INVALID_SEGMENT_NUMBER 180 */
+ EINVAL, /* 181 */
+ EINVAL, /* ERROR_INVALID_ORDINAL 182 */
+ EEXIST, /* ERROR_ALREADY_EXISTS 183 */
+ EINVAL, /* 184 */
+ EINVAL, /* 185 */
+ EINVAL, /* ERROR_INVALID_FLAG_NUMBER 186 */
+ EINVAL, /* ERROR_SEM_NOT_FOUND 187 */
+ ENOEXEC, /* ERROR_INVALID_STARTING_CODESEG 188 */
+ ENOEXEC, /* ERROR_INVALID_STACKSEG 189 */
+ ENOEXEC, /* ERROR_INVALID_MODULETYPE 190 */
+ ENOEXEC, /* ERROR_INVALID_EXE_SIGNATURE 191 */
+ ENOEXEC, /* ERROR_EXE_MARKED_INVALID 192 */
+ ENOEXEC, /* ERROR_BAD_EXE_FORMAT 193 */
+ ENOEXEC, /* ERROR_ITERATED_DATA_EXCEEDS_64k 194 */
+ ENOEXEC, /* ERROR_INVALID_MINALLOCSIZE 195 */
+ ENOEXEC, /* ERROR_DYNLINK_FROM_INVALID_RING 196 */
+ ENOEXEC, /* ERROR_IOPL_NOT_ENABLED 197 */
+ ENOEXEC, /* ERROR_INVALID_SEGDPL 198 */
+ ENOEXEC, /* ERROR_AUTODATASEG_EXCEEDS_64k 199 */
+ ENOEXEC, /* ERROR_RING2SEG_MUST_BE_MOVABLE 200 */
+ ENOEXEC, /* ERROR_RELOC_CHAIN_XEEDS_SEGLIM 201 */
+ ENOEXEC, /* ERROR_INFLOOP_IN_RELOC_CHAIN 202 */
+ EINVAL, /* ERROR_ENVVAR_NOT_FOUND 203 */
+ EINVAL, /* 204 */
+ EINVAL, /* ERROR_NO_SIGNAL_SENT 205 */
+ ENOENT, /* ERROR_FILENAME_EXCED_RANGE 206 */
+ EINVAL, /* ERROR_RING2_STACK_IN_USE 207 */
+ EINVAL, /* ERROR_META_EXPANSION_TOO_LONG 208 */
+ EINVAL, /* ERROR_INVALID_SIGNAL_NUMBER 209 */
+ EINVAL, /* ERROR_THREAD_1_INACTIVE 210 */
+ EINVAL, /* 211 */
+ EINVAL, /* ERROR_LOCKED 212 */
+ EINVAL, /* 213 */
+ EINVAL, /* ERROR_TOO_MANY_MODULES 214 */
+ EAGAIN /* ERROR_NESTING_NOT_ALLOWED 215 */
+};
+
+/* size of the table */
+#define ERRMAPTABLESIZE (sizeof(errMapTable)/sizeof(errMapTable[0]))
+
+/*
+** void __cdecl _dosmaperr(winerrno)
+**
+** Takes a Windows error number and tries to map it to a Unix System V errno.
+** Sets:
+** _doserrno = Windows error number
+** errno = Unix System V errno.
+*/
+void __cdecl _dosmaperr(unsigned long winerrno)
+{
+ _doserrno = winerrno;
+
+ if (winerrno >= ERRMAPTABLESIZE) {
+ if (winerrno == ERROR_NOT_ENOUGH_QUOTA) { /* exception for 1816 */
+ errno = ENOMEM;
+ } else {
+ errno = EINVAL;
+ }
+ } else {
+ errno = (unsigned int) errMapTable[winerrno];
+ }
+}
+
+#endif /* WIN32 && _MT && _DLL */
+
diff --git a/erts/emulator/sys/win32/driver_int.h b/erts/emulator/sys/win32/driver_int.h
new file mode 100644
index 0000000000..97e188816e
--- /dev/null
+++ b/erts/emulator/sys/win32/driver_int.h
@@ -0,0 +1,39 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*----------------------------------------------------------------------
+** Purpose : System dependant driver declarations
+**---------------------------------------------------------------------- */
+
+#ifndef __DRIVER_INT_H__
+#define __DRIVER_INT_H__
+
+#if !defined __WIN32__
+# define __WIN32__
+#endif
+
+/*
+ * This structure can be cast to a WSABUF structure.
+ */
+
+typedef struct _SysIOVec {
+ unsigned long iov_len;
+ char* iov_base;
+} SysIOVec;
+
+#endif
diff --git a/erts/emulator/sys/win32/erl.def b/erts/emulator/sys/win32/erl.def
new file mode 100644
index 0000000000..59e940847d
--- /dev/null
+++ b/erts/emulator/sys/win32/erl.def
@@ -0,0 +1,4 @@
+EXPORTS
+ erl_start
+ sys_get_key
+ sys_primitive_init
diff --git a/erts/emulator/sys/win32/erl_main.c b/erts/emulator/sys/win32/erl_main.c
new file mode 100644
index 0000000000..5471bffb52
--- /dev/null
+++ b/erts/emulator/sys/win32/erl_main.c
@@ -0,0 +1,29 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "sys.h"
+#include "global.h"
+
+void
+main(int argc, char **argv)
+{
+ erl_start(argc, argv);
+}
diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c
new file mode 100644
index 0000000000..d816cc2c07
--- /dev/null
+++ b/erts/emulator/sys/win32/erl_poll.c
@@ -0,0 +1,1361 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2007-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define WANT_NONBLOCKING
+
+#include "sys.h"
+#include "erl_alloc.h"
+#include "erl_poll.h"
+
+/*
+ * Some debug macros
+ */
+
+/*#define HARDDEBUG */
+#ifdef HARDDEBUG
+#ifdef HARDTRACE
+#define HARDTRACEF(X) my_debug_printf##X
+#else
+#define HARDTRACEF(X)
+#endif
+
+#define HARDDEBUGF(X) my_debug_printf##X
+static void my_debug_printf(char *fmt, ...)
+{
+ char buffer[1024];
+ va_list args;
+
+ va_start(args, fmt);
+ erts_vsnprintf(buffer,1024,fmt,args);
+ va_end(args);
+ erts_fprintf(stderr,"%s\r\n",buffer);
+}
+#else
+#define HARDTRACEF(X)
+#define HARDDEBUGF(X)
+#endif
+
+#ifdef DEBUG
+#define NoMansLandFill 0xFD /* fill no-man's land with this */
+#define DeadLandFill 0xDD /* fill free objects with this */
+#define CleanLandFill 0xCD /* fill new objects with this */
+
+static void consistency_check(struct _Waiter* w);
+static void* debug_alloc(ErtsAlcType_t, Uint);
+static void* debug_realloc(ErtsAlcType_t, void *, Uint, Uint);
+
+# define SEL_ALLOC debug_alloc
+# define SEL_REALLOC debug_realloc
+# define SEL_FREE erts_free
+
+static void *debug_alloc(ErtsAlcType_t type, Uint size)
+{
+ void* p = erts_alloc(type, size);
+ memset(p, CleanLandFill, size);
+ return p;
+}
+
+static void *debug_realloc(ErtsAlcType_t type, void *ptr, Uint prev_size,
+ Uint size)
+{
+ void *p;
+ size_t fill_size;
+ void *fill_ptr;
+
+ if (prev_size > size) {
+ size_t fill_size = (size_t) (prev_size - size);
+ void *fill_ptr = (void *) (((char *) ptr) + size);
+ memset(fill_ptr, NoMansLandFill, fill_size);
+ }
+
+ p = erts_realloc(type, ptr, size);
+
+ if (size > prev_size) {
+ size_t fill_size = (size_t) (size - prev_size);
+ void *fill_ptr = (void *) (((char *) p) + prev_size);
+ memset(fill_ptr, CleanLandFill, fill_size);
+ }
+
+ return p;
+}
+#else
+# define SEL_ALLOC erts_alloc
+# define SEL_REALLOC realloc_wrap
+# define SEL_FREE erts_free
+
+static ERTS_INLINE void *
+realloc_wrap(ErtsAlcType_t t, void *p, Uint ps, Uint s)
+{
+ return erts_realloc(t, p, s);
+}
+#endif
+
+
+#ifdef HARD_POLL_DEBUG
+#define OP_SELECT 1
+#define OP_DESELECT 2
+#define OP_FIRED 3
+#define OP_READ_BEGIN 4
+#define OP_READ_DONE 5
+#define OP_WRITE_BEGIN 6
+#define OP_WRITE_DONE 7
+#define OP_REPORTED 8
+#define OP_DIED 9
+#define OP_ASYNC_INIT 10
+#define OP_ASYNC_IMMED 11
+#define OP_FD_MOVED 12
+
+static struct {
+ int op;
+ ErtsSysFdType active;
+ int xdata;
+} debug_save_ops[1024];
+
+static int num_debug_save_ops = 0;
+
+static ErtsSysFdType active_debug_fd;
+static int active_debug_fd_set = 0;
+
+static erts_mtx_t save_ops_mtx;
+
+static void poll_debug_init(void)
+{
+ erts_mtx_init(&save_ops_mtx, "save_ops_lock");
+}
+
+void poll_debug_set_active_fd(ErtsSysFdType fd)
+{
+ erts_mtx_lock(&save_ops_mtx);
+ active_debug_fd_set = 1;
+ active_debug_fd = fd;
+ erts_mtx_unlock(&save_ops_mtx);
+}
+
+static void do_save_op(ErtsSysFdType fd, int op, int xdata)
+{
+ erts_mtx_lock(&save_ops_mtx);
+ if (fd == active_debug_fd && num_debug_save_ops < 1024) {
+ int x = num_debug_save_ops++;
+ debug_save_ops[x].op = op;
+ debug_save_ops[x].active = fd;
+ debug_save_ops[x].xdata = xdata;
+ }
+ erts_mtx_unlock(&save_ops_mtx);
+}
+
+void poll_debug_moved(ErtsSysFdType fd, int s1, int s2)
+{
+ do_save_op(fd,OP_FD_MOVED,s1 | (s2 << 16));
+}
+
+void poll_debug_select(ErtsSysFdType fd, int mode)
+{
+ do_save_op(fd,OP_SELECT,mode);
+}
+
+void poll_debug_deselect(ErtsSysFdType fd)
+{
+ do_save_op(fd,OP_DESELECT,0);
+}
+
+void poll_debug_fired(ErtsSysFdType fd)
+{
+ do_save_op(fd,OP_FIRED,0);
+}
+
+void poll_debug_read_begin(ErtsSysFdType fd)
+{
+ do_save_op(fd,OP_READ_BEGIN,0);
+}
+
+void poll_debug_read_done(ErtsSysFdType fd, int bytes)
+{
+ do_save_op(fd,OP_READ_DONE,bytes);
+}
+
+void poll_debug_async_initialized(ErtsSysFdType fd)
+{
+ do_save_op(fd,OP_ASYNC_INIT,0);
+}
+
+void poll_debug_async_immediate(ErtsSysFdType fd, int bytes)
+{
+ do_save_op(fd,OP_ASYNC_IMMED,bytes);
+}
+
+void poll_debug_write_begin(ErtsSysFdType fd)
+{
+ do_save_op(fd,OP_WRITE_BEGIN,0);
+}
+
+void poll_debug_write_done(ErtsSysFdType fd, int bytes)
+{
+ do_save_op(fd,OP_WRITE_DONE,bytes);
+}
+
+void poll_debug_reported(ErtsSysFdType fd, int mode)
+{
+ do_save_op(fd,OP_REPORTED,mode);
+}
+
+void poll_debug_died(ErtsSysFdType fd)
+{
+ do_save_op(fd,OP_DIED,0);
+}
+
+#endif /* DEBUG */
+
+/*
+ * End of debug macros
+ */
+
+
+
+/*
+ * Handles that we poll, but that are actually signalled from outside
+ * this module
+ */
+
+extern HANDLE erts_service_event;
+extern HANDLE erts_sys_break_event;
+
+
+/*
+ * The structure we hold for each event (i.e. fd)
+ */
+typedef struct _EventData {
+ HANDLE event; /* For convenience. */
+ ErtsPollEvents mode; /* The current select mode. */
+ struct _EventData *next; /* Next in free or delete lists. */
+} EventData;
+
+/*
+ * The structure to represent a waiter thread
+ */
+typedef struct _Waiter {
+ HANDLE events[MAXIMUM_WAIT_OBJECTS]; /* The events. */
+ EventData* evdata[MAXIMUM_WAIT_OBJECTS]; /* Pointers to associated data. */
+ int active_events; /* Number of events to wait for */
+ int total_events; /* Total number of events in the arrays. */
+ int highwater; /* Events processed up to here */
+ EventData evdata_heap[MAXIMUM_WAIT_OBJECTS]; /* Pre-allocated EventDatas */
+ EventData* first_free_evdata; /* Index of first free EventData object. */
+ HANDLE go_ahead; /* The waiter may continue. (Auto-reset) */
+ void *xdata; /* used when thread parameter */
+ erts_tid_t this; /* Thread "handle" of this waiter */
+ erts_mtx_t mtx; /* Mutex for updating/reading pollset, but the
+ currently used set require thread stopping
+ to be updated */
+} Waiter;
+
+/*
+ * The structure for a pollset. There can currently be only one...
+ */
+struct ErtsPollSet_ {
+ Waiter** waiter;
+ int allocated_waiters; /* Size ow waiter array */
+ int num_waiters; /* Number of waiter threads. */
+ erts_atomic_t sys_io_ready; /* Tells us there is I/O ready (already). */
+ int restore_events; /* Tells us to restore waiters events
+ next time around */
+ HANDLE event_io_ready; /* To be used when waiting for io */
+ /* These are used to wait for workers to enter standby */
+ volatile int standby_wait_counter; /* Number of threads to wait for */
+ CRITICAL_SECTION standby_crit; /* CS to guard the counter */
+ HANDLE standby_wait_event; /* Event signalled when counte == 0 */
+#ifdef ERTS_SMP
+ erts_smp_atomic_t woken;
+ erts_smp_mtx_t mtx;
+ erts_smp_atomic_t interrupt;
+#endif
+ erts_smp_atomic_t timeout;
+};
+
+#ifdef ERTS_SMP
+
+#define ERTS_POLLSET_LOCK(PS) \
+ erts_smp_mtx_lock(&(PS)->mtx)
+#define ERTS_POLLSET_UNLOCK(PS) \
+ erts_smp_mtx_unlock(&(PS)->mtx)
+#define ERTS_POLLSET_SET_POLLED_CHK(PS) \
+ ((int) erts_smp_atomic_xchg(&(PS)->polled, (long) 1))
+#define ERTS_POLLSET_SET_POLLED(PS) \
+ erts_smp_atomic_set(&(PS)->polled, (long) 1)
+#define ERTS_POLLSET_UNSET_POLLED(PS) \
+ erts_smp_atomic_set(&(PS)->polled, (long) 0)
+#define ERTS_POLLSET_IS_POLLED(PS) \
+ ((int) erts_smp_atomic_read(&(PS)->polled))
+#define ERTS_POLLSET_SET_POLLER_WOKEN_CHK(PS) \
+ ((int) erts_smp_atomic_xchg(&(PS)->woken, (long) 1))
+#define ERTS_POLLSET_SET_POLLER_WOKEN(PS) \
+ erts_smp_atomic_set(&(PS)->woken, (long) 1)
+#define ERTS_POLLSET_UNSET_POLLER_WOKEN(PS) \
+ erts_smp_atomic_set(&(PS)->woken, (long) 0)
+#define ERTS_POLLSET_IS_POLLER_WOKEN(PS) \
+ ((int) erts_smp_atomic_read(&(PS)->woken))
+
+#define ERTS_POLLSET_UNSET_INTERRUPTED_CHK(PS) \
+ ((int) erts_smp_atomic_xchg(&(PS)->interrupt, (long) 0))
+#define ERTS_POLLSET_UNSET_INTERRUPTED(PS) \
+ erts_smp_atomic_set(&(PS)->interrupt, (long) 0)
+#define ERTS_POLLSET_SET_INTERRUPTED(PS) \
+ erts_smp_atomic_set(&(PS)->interrupt, (long) 1)
+#define ERTS_POLLSET_IS_INTERRUPTED(PS) \
+ ((int) erts_smp_atomic_read(&(PS)->interrupt))
+
+#else
+
+#define ERTS_POLLSET_LOCK(PS)
+#define ERTS_POLLSET_UNLOCK(PS)
+#define ERTS_POLLSET_SET_POLLED_CHK(PS) 0
+#define ERTS_POLLSET_UNSET_POLLED(PS)
+#define ERTS_POLLSET_IS_POLLED(PS) 0
+#define ERTS_POLLSET_SET_POLLER_WOKEN_CHK(PS) 1
+#define ERTS_POLLSET_SET_POLLER_WOKEN(PS)
+#define ERTS_POLLSET_UNSET_POLLER_WOKEN(PS)
+#define ERTS_POLLSET_IS_POLLER_WOKEN(PS) 1
+
+
+#endif
+
+/*
+ * While atomics are not yet implemented for windows in the common library...
+ *
+ * MSDN doc states that SMP machines and old compilers require
+ * InterLockedExchange to properly read and write interlocked
+ * variables, otherwise the processors might reschedule
+ * the access and order of atomics access is destroyed...
+ * While they only mention it in white-papers, the problem
+ * in VS2003 is due to the IA64 arch, so we can still count
+ * on the CPU not rescheduling the access to volatile in X86 arch using
+ * even the slightly older compiler...
+ *
+ * So here's (hopefully) a subset of the generally working atomic
+ * variable access...
+ */
+
+#if defined(__GNUC__)
+# if defined(__i386__) || defined(__x86_64__)
+# define VOLATILE_IN_SEQUENCE 1
+# else
+# define VOLATILE_IN_SEQUENCE 0
+# endif
+#elif defined(_MSC_VER)
+# if _MSC_VER < 1300
+# define VOLATILE_IN_SEQUENCE 0 /* Dont trust really old compilers */
+# else
+# if defined(_M_IX86)
+# define VOLATILE_IN_SEQUENCE 1
+# else /* I.e. IA64 */
+# if _MSC_VER >= 1400
+# define VOLATILE_IN_SEQUENCE 1
+# else
+# define VOLATILE_IN_SEQUENCE 0
+# endif
+# endif
+# endif
+#else
+# define VOLATILE_IN_SEQUENCE 0
+#endif
+
+
+
+/*
+ * Communication with sys_interrupt
+ */
+
+#ifdef ERTS_SMP
+extern erts_smp_atomic_t erts_break_requested;
+#define ERTS_SET_BREAK_REQUESTED \
+ erts_smp_atomic_set(&erts_break_requested, (long) 1)
+#define ERTS_UNSET_BREAK_REQUESTED \
+ erts_smp_atomic_set(&erts_break_requested, (long) 0)
+#else
+extern volatile int erts_break_requested;
+#define ERTS_SET_BREAK_REQUESTED (erts_break_requested = 1)
+#define ERTS_UNSET_BREAK_REQUESTED (erts_break_requested = 0)
+#endif
+
+static erts_mtx_t break_waiter_lock;
+static HANDLE break_happened_event;
+static erts_atomic_t break_waiter_state;
+#define BREAK_WAITER_GOT_BREAK 1
+#define BREAK_WAITER_GOT_HALT 2
+
+
+/*
+ * Forward declarations
+ */
+
+static void *threaded_waiter(void *param);
+static void *break_waiter(void *param);
+
+/*
+ * Sychronization macros and functions
+ */
+#define START_WAITER(PS, w) \
+ SetEvent((w)->go_ahead)
+
+#define STOP_WAITER(PS,w) \
+do { \
+ setup_standby_wait((PS),1); \
+ SetEvent((w)->events[0]); \
+ wait_standby(PS); \
+} while(0)
+
+#define START_WAITERS(PS) \
+do { \
+ int i; \
+ for (i = 0; i < (PS)->num_waiters; i++) { \
+ SetEvent((PS)->waiter[i]->go_ahead); \
+ } \
+ } while(0)
+
+#define STOP_WAITERS(PS) \
+do { \
+ int i; \
+ setup_standby_wait((PS),(PS)->num_waiters); \
+ for (i = 0; i < (PS)->num_waiters; i++) { \
+ SetEvent((PS)->waiter[i]->events[0]); \
+ } \
+ wait_standby(PS); \
+ } while(0)
+
+#if ERTS_POLL_ASYNC_INTERRUPT_SUPPORT && !defined(ERTS_SMP)
+
+static ERTS_INLINE int
+unset_interrupted_chk(ErtsPollSet ps)
+{
+ /* This operation isn't atomic, but we have no need at all for an
+ atomic operation here... */
+ int res = ps->interrupt;
+ ps->interrupt = 0;
+ return res;
+}
+
+#endif
+
+#ifdef ERTS_SMP
+static ERTS_INLINE void
+wake_poller(ErtsPollSet ps)
+{
+ if (!ERTS_POLLSET_SET_POLLER_WOKEN_CHK(ps)) {
+ SetEvent(ps->event_io_ready);
+ }
+}
+#endif
+
+static void setup_standby_wait(ErtsPollSet ps, int num_threads)
+{
+ EnterCriticalSection(&(ps->standby_crit));
+ ps->standby_wait_counter = num_threads;
+ ResetEvent(ps->standby_wait_event);
+ LeaveCriticalSection(&(ps->standby_crit));
+}
+
+static void signal_standby(ErtsPollSet ps)
+{
+ EnterCriticalSection(&(ps->standby_crit));
+ --(ps->standby_wait_counter);
+ if (ps->standby_wait_counter < 0) {
+ LeaveCriticalSection(&(ps->standby_crit));
+ erl_exit(1,"Standby signalled by more threads than expected");
+ }
+ if (!(ps->standby_wait_counter)) {
+ SetEvent(ps->standby_wait_event);
+ }
+ LeaveCriticalSection(&(ps->standby_crit));
+}
+
+static void wait_standby(ErtsPollSet ps)
+{
+ WaitForSingleObject(ps->standby_wait_event,INFINITE);
+}
+
+static void remove_event_from_set(Waiter *w, int j)
+{
+ w->evdata[j]->event = INVALID_HANDLE_VALUE;
+ w->evdata[j]->mode = 0;
+ w->evdata[j]->next = w->first_free_evdata;
+ w->first_free_evdata = w->evdata[j];
+
+ /*
+ * If the event is active, we will overwrite it
+ * with the last active event and make the hole
+ * the first non-active event.
+ */
+
+ if (j < w->active_events) {
+ w->active_events--;
+ w->highwater--;
+ w->total_events--;
+ w->events[j] = w->events[w->active_events];
+ w->evdata[j] = w->evdata[w->active_events];
+ w->events[w->active_events] = w->events[w->highwater];
+ w->evdata[w->active_events] = w->evdata[w->highwater];
+ w->events[w->highwater] = w->events[w->total_events];
+ w->evdata[w->highwater] = w->evdata[w->total_events];
+ } else if (j < w->highwater) {
+ w->highwater--;
+ w->total_events--;
+ w->events[j] = w->events[w->highwater];
+ w->evdata[j] = w->evdata[w->highwater];
+ w->events[w->highwater] = w->events[w->total_events];
+ w->evdata[w->highwater] = w->evdata[w->total_events];
+ } else {
+ w->total_events--;
+ w->events[j] = w->events[w->total_events];
+ w->evdata[j] = w->evdata[w->total_events];
+ }
+
+#ifdef DEBUG
+ w->events[w->total_events] = (HANDLE) CleanLandFill;
+ w->evdata[w->total_events] = (EventData *) CleanLandFill;
+ consistency_check(w);
+#endif
+}
+
+/*
+ * Thread handling
+ */
+
+#ifdef DEBUG
+static void consistency_check(Waiter* w)
+{
+ int i;
+
+ ASSERT(w->active_events <= w->total_events);
+ ASSERT(w->evdata[0] == NULL);
+
+ for (i = 1; i < w->total_events; i++) {
+ ASSERT(w->events[i] == w->evdata[i]->event);
+ ASSERT(w->evdata[i]->mode != 0);
+ }
+}
+
+#endif
+
+static void new_waiter(ErtsPollSet ps)
+{
+ register Waiter* w;
+ DWORD tid; /* Id for thread. */
+ erts_tid_t thread;
+ int i;
+ int tres;
+
+ if (ps->num_waiters == ps->allocated_waiters) {
+ Uint old_size = sizeof(Waiter *)*ps->allocated_waiters;
+ ps->allocated_waiters += 64;
+ ps->waiter = SEL_REALLOC(ERTS_ALC_T_WAITER_OBJ,
+ (void *) ps->waiter,
+ old_size,
+ sizeof(Waiter *) * (ps->allocated_waiters));
+ }
+
+ w = (Waiter *) SEL_ALLOC(ERTS_ALC_T_WAITER_OBJ, sizeof(Waiter));
+ ps->waiter[ps->num_waiters] = w;
+
+ w->events[0] = CreateAutoEvent(FALSE);
+ w->evdata[0] = NULL; /* Should never be used. */
+ w->active_events = 1;
+ w->highwater = 1;
+ w->total_events = 1;
+ erts_mtx_init(&w->mtx, "pollwaiter");
+
+
+ /*
+ * Form the free list of EventData objects.
+ */
+
+ w->evdata_heap[0].next = 0; /* Last in free list. */
+ for (i = 1; i < MAXIMUM_WAIT_OBJECTS; i++) {
+ w->evdata_heap[i].next = w->evdata_heap+i-1;
+ }
+ w->first_free_evdata = w->evdata_heap+MAXIMUM_WAIT_OBJECTS-1;
+
+ /*
+ * Create the other events.
+ */
+
+ w->go_ahead = CreateAutoEvent(FALSE);
+
+ /*
+ * Create the thread.
+ */
+ w->xdata = ps;
+ erts_thr_create(&thread, &threaded_waiter, w, NULL);
+ w->this = thread;
+
+ /*
+ * Finally, done.
+ */
+
+ (ps->num_waiters)++;
+}
+
+static void *break_waiter(void *param)
+{
+ HANDLE harr[2];
+ int i = 0;
+ harr[i++] = erts_sys_break_event;
+ if (erts_service_event != NULL) {
+ harr[i++] = erts_service_event;
+ }
+
+ for(;;) {
+ switch (WaitForMultipleObjects(i,harr,FALSE,INFINITE)) {
+ case WAIT_OBJECT_0:
+ ResetEvent(harr[0]);
+ erts_mtx_lock(&break_waiter_lock);
+ erts_atomic_set(&break_waiter_state,BREAK_WAITER_GOT_BREAK);
+ SetEvent(break_happened_event);
+ erts_mtx_unlock(&break_waiter_lock);
+ break;
+ case (WAIT_OBJECT_0+1):
+ ResetEvent(harr[1]);
+ erts_mtx_lock(&break_waiter_lock);
+ erts_atomic_set(&break_waiter_state,BREAK_WAITER_GOT_HALT);
+ SetEvent(break_happened_event);
+ erts_mtx_unlock(&break_waiter_lock);
+ break;
+ default:
+ erl_exit(1,"Unexpected event in break_waiter");
+ }
+ }
+}
+
+static void *threaded_waiter(void *param)
+{
+ register Waiter* w = (Waiter *) param;
+ ErtsPollSet ps = (ErtsPollSet) w->xdata;
+#ifdef HARD_POLL_DEBUG2
+ HANDLE oold_fired[64];
+ int num_oold_fired;
+ HANDLE old_fired[64];
+ int num_old_fired = 0;
+ HANDLE fired[64];
+ int num_fired = 0;
+ HANDLE errors[1024];
+ int num_errors = 0;
+ HANDLE save_events[64];
+ int save_active_events;
+ int save_total_events;
+ int save_highwater;
+#endif
+
+ again:
+ WaitForSingleObject(w->go_ahead, INFINITE);
+ /* Atomic enough when just checking, skip lock */
+ if (w->total_events == 0) {
+ return NULL;
+ }
+ if (w->active_events == 0) {
+ goto again;
+ }
+ ASSERT(w->evdata[0] == NULL);
+#ifdef HARD_POLL_DEBUG2
+ num_oold_fired = num_old_fired;
+ memcpy(oold_fired,old_fired,num_old_fired*sizeof(HANDLE));
+ num_old_fired = num_fired;
+ memcpy(old_fired,fired,num_fired*sizeof(HANDLE));
+ num_fired = 0;
+#endif
+ for (;;) {
+ int i;
+ int j;
+#ifdef HARD_POLL_DEBUG2
+ erts_mtx_lock(&w->mtx);
+ memcpy(save_events,w->events,w->active_events*sizeof(HANDLE));
+ save_active_events = w->active_events;
+ save_total_events = w->total_events;
+ save_highwater = w->highwater;
+ erts_mtx_unlock(&w->mtx);
+#endif
+ i = WaitForMultipleObjects(w->active_events, w->events, FALSE, INFINITE);
+ switch (i) {
+ case WAIT_FAILED:
+ DEBUGF(("Wait failed: %s\n", last_error()));
+ erts_mtx_lock(&w->mtx);
+ /* Dont wait for our signal event */
+ for (j = 1; j < w->active_events; j++) {
+ int tmp;
+ if ((tmp = WaitForSingleObject(w->events[j], 0))
+ == WAIT_FAILED) {
+ DEBUGF(("Invalid handle: i = %d, handle = 0x%0x\n",
+ j, w->events[j]));
+#ifdef HARD_POLL_DEBUG2
+ if (num_errors < 1024)
+ errors[num_errors++] = w->events[j];
+#endif
+#ifdef HARD_POLL_DEBUG
+ poll_debug_died(w->events[j]);
+#endif
+ remove_event_from_set(w,j);
+#ifdef DEBUG
+ consistency_check(w);
+#endif
+ } else if (tmp == WAIT_OBJECT_0) {
+ i = WAIT_OBJECT_0 + j;
+ goto event_happened;
+ }
+ }
+ erts_mtx_unlock(&w->mtx);
+ break;
+ case WAIT_OBJECT_0:
+ signal_standby(ps);
+ goto again;
+#ifdef DEBUG
+ case WAIT_TIMEOUT:
+ ASSERT(0);
+#endif
+ default:
+ erts_mtx_lock(&w->mtx);
+#ifdef HARD_POLL_DEBUG2
+ {
+ int x = memcmp(save_events,w->events,w->active_events*sizeof(HANDLE));
+ ASSERT(x == 0 && save_active_events == w->active_events);
+ }
+#endif
+event_happened:
+#ifdef DEBUG
+ consistency_check(w);
+#endif
+ ASSERT(WAIT_OBJECT_0 < i && i < WAIT_OBJECT_0+w->active_events);
+ if (!erts_atomic_xchg(&ps->sys_io_ready,1)) {
+ HARDDEBUGF(("SET EventIoReady (%d)",erts_atomic_read(&ps->sys_io_ready)));
+ SetEvent(ps->event_io_ready);
+ } else {
+ HARDDEBUGF(("DONT SET EventIoReady"));
+ }
+
+ /*
+ * The main thread wont start working on our arrays untill we're
+ * stopped, so we can work in peace although the main thread runs
+ */
+ ASSERT(i >= WAIT_OBJECT_0+1);
+ i -= WAIT_OBJECT_0;
+ ASSERT(i >= 1);
+ w->active_events--;
+ HARDDEBUGF(("i = %d, a,h,t = %d,%d,%d",i,
+ w->active_events, w->highwater, w->total_events));
+#ifdef HARD_POLL_DEBUG2
+ fired[num_fired++] = w->events[i];
+#endif
+#ifdef HARD_POLL_DEBUG
+ poll_debug_fired(w->events[i]);
+#endif
+ if (i < w->active_events) {
+ HANDLE te = w->events[i];
+ EventData* tp = w->evdata[i];
+ w->events[i] = w->events[w->active_events];
+ w->evdata[i] = w->evdata[w->active_events];
+ w->events[w->active_events] = te;
+ w->evdata[w->active_events] = tp;
+ }
+ HARDDEBUGF(("i = %d, a,h,t = %d,%d,%d",i,
+ w->active_events, w->highwater, w->total_events));
+#ifdef DEBUG
+ consistency_check(w);
+#endif
+ erts_mtx_unlock(&w->mtx);
+ break;
+ }
+ }
+}
+
+/*
+ * The actual adding and removing from pollset utilities
+ */
+
+static int set_driver_select(ErtsPollSet ps, HANDLE event, ErtsPollEvents mode)
+{
+ int i;
+ int best_waiter = -1; /* The waiter with lowest number of events. */
+ int lowest = MAXIMUM_WAIT_OBJECTS; /* Lowest number of events
+ * in any waiter.
+ */
+ EventData* ev;
+ Waiter* w;
+
+ /*
+ * Find the waiter which is least busy.
+ */
+
+#ifdef HARD_POLL_DEBUG
+ poll_debug_select(event, mode);
+#endif
+
+ /* total_events can no longer be read without the lock, it's changed in the waiter */
+ for (i = 0; i < ps->num_waiters; i++) {
+ erts_mtx_lock(&(ps->waiter[i]->mtx));
+ if (ps->waiter[i]->total_events < lowest) {
+ lowest = ps->waiter[i]->total_events;
+ best_waiter = i;
+ }
+ erts_mtx_unlock(&(ps->waiter[i]->mtx));
+ }
+
+ /*
+ * Stop the selected waiter, or start a new waiter if all were busy.
+ */
+
+ if (best_waiter >= 0) {
+ w = ps->waiter[best_waiter];
+ STOP_WAITER(ps,w);
+ erts_mtx_lock(&w->mtx);
+ } else {
+ new_waiter(ps);
+ w = ps->waiter[(ps->num_waiters)-1];
+ erts_mtx_lock(&w->mtx);
+ }
+
+#ifdef DEBUG
+ consistency_check(w);
+#endif
+
+ /*
+ * Allocate and initialize an EventData structure.
+ */
+
+ ev = w->first_free_evdata;
+ w->first_free_evdata = ev->next;
+ ev->event = event;
+ ev->mode = mode;
+ ev->next = NULL;
+
+ /*
+ * At this point, the selected waiter (newly-created or not) is
+ * standing by. Put the new event into the active part of the array.
+ */
+
+ if (w->active_events < w->total_events) {
+ /*
+ * Move the first event beyond the active part of the array to
+ * the very end to make place for the new event.
+ */
+
+#ifdef HARD_POLL_DEBUG
+ poll_debug_moved(w->events[w->highwater],w->highwater,w->total_events);
+#endif
+ w->events[w->total_events] = w->events[w->highwater];
+ w->evdata[w->total_events] = w->evdata[w->highwater];
+#ifdef HARD_POLL_DEBUG
+ poll_debug_moved(w->events[w->active_events],w->active_events,w->highwater);
+#endif
+ w->events[w->highwater] = w->events[w->active_events];
+ w->evdata[w->highwater] = w->evdata[w->active_events];
+
+ }
+ w->events[w->active_events] = event;
+ w->evdata[w->active_events] = ev;
+ w->active_events++;
+ w->highwater++;
+ w->total_events++;
+
+#ifdef DEBUG
+ consistency_check(w);
+#endif
+ erts_mtx_unlock(&w->mtx);
+ START_WAITER(ps,w);
+ HARDDEBUGF(("add select %d %d %d %d",best_waiter,
+ w->active_events,w->highwater,w->total_events));
+ return mode;
+}
+
+
+static int cancel_driver_select(ErtsPollSet ps, HANDLE event)
+{
+ int i;
+
+ ASSERT(event != INVALID_HANDLE_VALUE);
+ restart:
+ for (i = 0; i < ps->num_waiters; i++) {
+ Waiter* w = ps->waiter[i];
+ int j;
+
+ erts_mtx_lock(&w->mtx);
+#ifdef DEBUG
+ consistency_check(w);
+#endif
+ for (j = 0; j < w->total_events; j++) {
+ if (w->events[j] == event) {
+ int stopped = 0;
+ /*
+ * Free the event's EventData structure.
+ */
+
+ if (j < w->active_events) {
+ HARDDEBUGF(("Stopped in remove select"));
+ stopped = 1;
+ erts_mtx_unlock(&w->mtx);
+ STOP_WAITER(ps,w);
+ erts_mtx_lock(&w->mtx);
+ if ( j >= w->active_events || w->events[j] != event) {
+ /* things happened while unlocked */
+ START_WAITER(ps,w);
+ erts_mtx_unlock(&w->mtx);
+ goto restart;
+ }
+ }
+#ifdef HARD_POLL_DEBUG
+ poll_debug_deselect(w->events[j]);
+#endif
+ remove_event_from_set(w, j);
+ if (stopped) {
+ START_WAITER(ps,w);
+ }
+ HARDDEBUGF(("removed select %d,%d %d %d %d",i,j,
+ w->active_events,w->highwater,w->total_events));
+ break;
+ }
+ }
+ erts_mtx_unlock(&w->mtx);
+ }
+ return 0;
+}
+
+/*
+ * Interface functions
+ */
+
+void erts_poll_interrupt(ErtsPollSet ps, int set /* bool */)
+{
+ HARDTRACEF(("In erts_poll_interrupt(%d)",set));
+#ifdef ERTS_SMP
+ if (set) {
+ ERTS_POLLSET_SET_INTERRUPTED(ps);
+ wake_poller(ps);
+ }
+ else {
+ ERTS_POLLSET_UNSET_INTERRUPTED(ps);
+ }
+#endif
+ HARDTRACEF(("Out erts_poll_interrupt(%d)",set));
+}
+
+void erts_poll_interrupt_timed(ErtsPollSet ps,
+ int set /* bool */,
+ long msec)
+{
+ HARDTRACEF(("In erts_poll_interrupt_timed(%d,%ld)",set,msec));
+#ifdef ERTS_SMP
+ if (set) {
+ if (erts_smp_atomic_read(&ps->timeout) > msec) {
+ ERTS_POLLSET_SET_INTERRUPTED(ps);
+ wake_poller(ps);
+ }
+ }
+ else {
+ ERTS_POLLSET_UNSET_INTERRUPTED(ps);
+ }
+#endif
+ HARDTRACEF(("Out erts_poll_interrupt_timed"));
+}
+
+
+/*
+ * Windows is special, there is actually only one event type, and
+ * the only difference between ERTS_POLL_EV_IN and ERTS_POLL_EV_OUT
+ * is which driver callback will eventually be called.
+ */
+static ErtsPollEvents do_poll_control(ErtsPollSet ps,
+ ErtsSysFdType fd,
+ ErtsPollEvents pe,
+ int on /* bool */)
+{
+ HANDLE event = (HANDLE) fd;
+ ErtsPollEvents mode;
+ ErtsPollEvents result;
+ ASSERT(event != INVALID_HANDLE_VALUE);
+
+ if (on) {
+ if (pe & ERTS_POLL_EV_IN || !(pe & ERTS_POLL_EV_OUT )) {
+ mode = ERTS_POLL_EV_IN;
+ } else {
+ mode = ERTS_POLL_EV_OUT; /* ready output only in this case */
+ }
+ result = set_driver_select(ps, event, mode);
+ } else {
+ result = cancel_driver_select(ps, event);
+ }
+ return result;
+}
+
+ErtsPollEvents erts_poll_control(ErtsPollSet ps,
+ ErtsSysFdType fd,
+ ErtsPollEvents pe,
+ int on,
+ int* do_wake) /* In: Wake up polling thread */
+ /* Out: Poller is woken */
+{
+ ErtsPollEvents result;
+ HARDTRACEF(("In erts_poll_control(0x%08X, %u, %d)",(unsigned long) fd, (unsigned) pe, on));
+ ERTS_POLLSET_LOCK(ps);
+ result=do_poll_control(ps,fd,pe,on);
+ ERTS_POLLSET_UNLOCK(ps);
+ *do_wake = 0; /* Never any need to wake polling threads on windows */
+ HARDTRACEF(("Out erts_poll_control -> %u",(unsigned) result));
+ return result;
+}
+
+void erts_poll_controlv(ErtsPollSet ps,
+ ErtsPollControlEntry pcev[],
+ int len)
+{
+ int i;
+ int hshur = 0;
+ int do_wake = 0;
+
+ HARDTRACEF(("In erts_poll_controlv(%d)",len));
+ ERTS_POLLSET_LOCK(ps);
+
+ for (i = 0; i < len; i++) {
+ pcev[i].events = do_poll_control(ps,
+ pcev[i].fd,
+ pcev[i].events,
+ pcev[i].on);
+ }
+ ERTS_POLLSET_LOCK(ps);
+ HARDTRACEF(("Out erts_poll_controlv"));
+}
+
+int erts_poll_wait(ErtsPollSet ps,
+ ErtsPollResFd pr[],
+ int *len,
+ SysTimeval *utvp)
+{
+ SysTimeval *tvp = utvp;
+ SysTimeval itv;
+ int no_fds;
+ DWORD timeout;
+ EventData* ev;
+ int res = 0;
+ int num = 0;
+ int n;
+ int i;
+ int break_state;
+
+ HARDTRACEF(("In erts_poll_wait"));
+ ERTS_POLLSET_LOCK(ps);
+
+ if (!erts_atomic_read(&ps->sys_io_ready) && ps->restore_events) {
+ HARDDEBUGF(("Restore events: %d",ps->num_waiters));
+ ps->restore_events = 0;
+ for (i = 0; i < ps->num_waiters; ++i) {
+ Waiter* w = ps->waiter[i];
+ erts_mtx_lock(&w->mtx);
+ HARDDEBUGF(("Maybe reset %d %d %d %d",i,
+ w->active_events,w->highwater,w->total_events));
+ if (w->active_events < w->total_events) {
+ erts_mtx_unlock(&w->mtx);
+ STOP_WAITER(ps,w);
+ HARDDEBUGF(("Need reset %d %d %d %d",i,
+ w->active_events,w->highwater,w->total_events));
+ erts_mtx_lock(&w->mtx);
+ /* Need reset, just check that it doesn't have got more to tell */
+ if (w->highwater != w->active_events) {
+ HARDDEBUGF(("Oups!"));
+ /* Oups, got signalled before we took the lock, can't reset */
+ if(erts_atomic_read(&ps->sys_io_ready) == 0) {
+ erl_exit(1,"Internal error: "
+ "Inconsistent io structures in erl_poll.\n");
+ }
+ START_WAITER(ps,w);
+ erts_mtx_unlock(&w->mtx);
+ ps->restore_events = 1;
+ continue;
+ }
+ w->active_events = w->highwater = w->total_events;
+ START_WAITER(ps,w);
+ erts_mtx_unlock(&w->mtx);
+ } else {
+ erts_mtx_unlock(&w->mtx);
+ }
+ }
+ }
+
+ no_fds = *len;
+
+#ifdef ERTS_POLL_MAX_RES
+ if (no_fds >= ERTS_POLL_MAX_RES)
+ no_fds = ERTS_POLL_MAX_RES;
+#endif
+
+
+ ResetEvent(ps->event_io_ready);
+ ERTS_POLLSET_UNSET_POLLER_WOKEN(ps);
+
+#ifdef ERTS_SMP
+ if (ERTS_POLLSET_IS_INTERRUPTED(ps)) {
+ /* Interrupt use zero timeout */
+ itv.tv_sec = 0;
+ itv.tv_usec = 0;
+ tvp = &itv;
+ }
+#endif
+
+ timeout = tvp->tv_sec * 1000 + tvp->tv_usec / 1000;
+ /*HARDDEBUGF(("timeout = %ld",(long) timeout));*/
+ erts_smp_atomic_set(&ps->timeout, timeout);
+
+ if (timeout > 0 && ! erts_atomic_read(&ps->sys_io_ready) && ! erts_atomic_read(&break_waiter_state)) {
+ HANDLE harr[2] = {ps->event_io_ready, break_happened_event};
+ int num_h = 2;
+
+ HARDDEBUGF(("Start waiting %d [%d]",num_h, (long) timeout));
+ ERTS_POLLSET_UNLOCK(ps);
+ WaitForMultipleObjects(num_h, harr, FALSE, timeout);
+ ERTS_POLLSET_LOCK(ps);
+ HARDDEBUGF(("Stop waiting %d [%d]",num_h, (long) timeout));
+ }
+
+ ERTS_UNSET_BREAK_REQUESTED;
+ if(erts_atomic_read(&break_waiter_state)) {
+ erts_mtx_lock(&break_waiter_lock);
+ break_state = erts_atomic_read(&break_waiter_state);
+ erts_atomic_set(&break_waiter_state,0);
+ ResetEvent(break_happened_event);
+ erts_mtx_unlock(&break_waiter_lock);
+ switch (break_state) {
+ case BREAK_WAITER_GOT_BREAK:
+ ERTS_SET_BREAK_REQUESTED;
+ break;
+ case BREAK_WAITER_GOT_HALT:
+ erl_exit(0,"");
+ break;
+ default:
+ break;
+ }
+ }
+
+ ERTS_POLLSET_SET_POLLER_WOKEN(ps);
+
+ if (!erts_atomic_read(&ps->sys_io_ready)) {
+ res = EINTR;
+ HARDDEBUGF(("EINTR!"));
+ goto done;
+ }
+
+ erts_atomic_set(&ps->sys_io_ready,0);
+
+ n = ps->num_waiters;
+
+ for (i = 0; i < n; i++) {
+ Waiter* w = ps->waiter[i];
+ int j;
+ int first;
+ int last;
+ erts_mtx_lock(&w->mtx);
+#ifdef DEBUG
+ consistency_check(w);
+#endif
+
+ first = w->active_events;
+ last = w->highwater;
+ w->highwater = w->active_events;
+
+ for (j = last-1; j >= first; --j) {
+ if (num >= no_fds) {
+ w->highwater=j+1;
+ erts_mtx_unlock(&w->mtx);
+ /* This might mean we still have data to report, set
+ back the global flag! */
+ erts_atomic_set(&ps->sys_io_ready,1);
+ HARDDEBUGF(("To many FD's to report!"));
+ goto done;
+ }
+ HARDDEBUGF(("SET! Restore events"));
+ ps->restore_events = 1;
+ HARDDEBUGF(("Report %d,%d",i,j));
+ pr[num].fd = (ErtsSysFdType) w->events[j];
+ pr[num].events = w->evdata[j]->mode;
+#ifdef HARD_POLL_DEBUG
+ poll_debug_reported(w->events[j],w->highwater | (j << 16));
+ poll_debug_reported(w->events[j],first | (last << 16));
+#endif
+ ++num;
+ }
+
+#ifdef DEBUG
+ consistency_check(w);
+#endif
+ erts_mtx_unlock(&w->mtx);
+ }
+ done:
+ erts_smp_atomic_set(&ps->timeout, LONG_MAX);
+ *len = num;
+ ERTS_POLLSET_UNLOCK(ps);
+ HARDTRACEF(("Out erts_poll_wait"));
+ return res;
+
+}
+
+int erts_poll_max_fds(void)
+{
+ int res = sys_max_files();
+ HARDTRACEF(("In/Out erts_poll_max_fds -> %d",res));
+ return res;
+}
+
+void erts_poll_info(ErtsPollSet ps,
+ ErtsPollInfo *pip)
+{
+ Uint size = 0;
+ Uint num_events = 0;
+ int i;
+
+ HARDTRACEF(("In erts_poll_info"));
+ ERTS_POLLSET_LOCK(ps);
+
+ size += sizeof(struct ErtsPollSet_);
+ size += sizeof(Waiter *) * ps->allocated_waiters;
+ for (i = 0; i < ps->num_waiters; ++i) {
+ Waiter *w = ps->waiter[i];
+ if (w != NULL) {
+ size += sizeof(Waiter);
+ erts_mtx_lock(&w->mtx);
+ size += sizeof(EventData) * w->total_events;
+ num_events += (w->total_events - 1); /* First event is internal */
+ erts_mtx_unlock(&w->mtx);
+ }
+ }
+
+ pip->primary = "WaitForMultipleObjects";
+
+ pip->fallback = NULL;
+
+ pip->kernel_poll = NULL;
+
+ pip->memory_size = size;
+
+ pip->poll_set_size = num_events;
+
+ pip->fallback_poll_set_size = 0;
+
+ pip->lazy_updates = 0;
+
+ pip->pending_updates = 0;
+
+ pip->batch_updates = 0;
+
+ pip->concurrent_updates = 0;
+ ERTS_POLLSET_UNLOCK(ps);
+
+ pip->max_fds = erts_poll_max_fds();
+ HARDTRACEF(("Out erts_poll_info"));
+
+}
+
+ErtsPollSet erts_poll_create_pollset(void)
+{
+ ErtsPollSet ps = SEL_ALLOC(ERTS_ALC_T_POLLSET,
+ sizeof(struct ErtsPollSet_));
+ HARDTRACEF(("In erts_poll_create_pollset"));
+
+ ps->num_waiters = 0;
+ ps->allocated_waiters = 64;
+ ps->waiter = SEL_ALLOC(ERTS_ALC_T_WAITER_OBJ,
+ sizeof(Waiter *)*ps->allocated_waiters);
+ InitializeCriticalSection(&(ps->standby_crit));
+ ps->standby_wait_counter = 0;
+ ps->event_io_ready = CreateManualEvent(FALSE);
+ ps->standby_wait_event = CreateManualEvent(FALSE);
+ erts_atomic_init(&ps->sys_io_ready,0);
+ ps->restore_events = 0;
+
+#ifdef ERTS_SMP
+ erts_smp_atomic_init(&ps->woken, 0);
+ erts_smp_mtx_init(&ps->mtx, "pollset");
+ erts_smp_atomic_init(&ps->interrupt, 0);
+#endif
+ erts_smp_atomic_init(&ps->timeout, LONG_MAX);
+
+ HARDTRACEF(("Out erts_poll_create_pollset"));
+ return ps;
+}
+
+void erts_poll_destroy_pollset(ErtsPollSet ps)
+{
+ int i;
+ HARDTRACEF(("In erts_poll_destroy_pollset"));
+ ERTS_POLLSET_LOCK(ps);
+ STOP_WAITERS(ps);
+ for (i=0;i<ps->num_waiters;++i) {
+ Waiter *w = ps->waiter[i];
+ void *dummy;
+ erts_tid_t t = w->this;
+ /* Assume we're alone, no locking here... */
+ w->active_events = w->total_events = w->highwater = 0;
+ START_WAITER(ps,w);
+ erts_thr_join(t,&dummy);
+ CloseHandle(w->go_ahead);
+ CloseHandle(w->events[0]);
+ erts_mtx_destroy(&w->mtx);
+ SEL_FREE(ERTS_ALC_T_WAITER_OBJ, (void *) w);
+ }
+ SEL_FREE(ERTS_ALC_T_WAITER_OBJ,ps->waiter);
+ CloseHandle(ps->event_io_ready);
+ CloseHandle(ps->standby_wait_event);
+ ERTS_POLLSET_UNLOCK(ps);
+#ifdef ERTS_SMP
+ erts_smp_mtx_destroy(&ps->mtx);
+#endif
+ SEL_FREE(ERTS_ALC_T_POLLSET, (void *) ps);
+ HARDTRACEF(("Out erts_poll_destroy_pollset"));
+}
+
+/*
+ * Actually mostly initializes the friend module sys_interrupt...
+ */
+void erts_poll_init(void)
+{
+ erts_tid_t thread;
+
+#ifdef HARD_POLL_DEBUG
+ poll_debug_init();
+#endif
+
+ HARDTRACEF(("In erts_poll_init"));
+ erts_sys_break_event = CreateManualEvent(FALSE);
+
+ erts_mtx_init(&break_waiter_lock,"break_waiter_lock");
+ break_happened_event = CreateManualEvent(FALSE);
+ erts_atomic_init(&break_waiter_state, 0);
+
+ erts_thr_create(&thread, &break_waiter, NULL, NULL);
+ ERTS_UNSET_BREAK_REQUESTED;
+ HARDTRACEF(("Out erts_poll_init"));
+}
+
+/*
+ * Non windows friendly interface, not used when fd's are not continous
+ */
+void erts_poll_get_selected_events(ErtsPollSet ps,
+ ErtsPollEvents ev[],
+ int len)
+{
+ int i;
+ HARDTRACEF(("In erts_poll_get_selected_events"));
+ for (i = 0; i < len; ++i)
+ ev[i] = 0;
+ HARDTRACEF(("Out erts_poll_get_selected_events"));
+}
diff --git a/erts/emulator/sys/win32/erl_win32_sys_ddll.c b/erts/emulator/sys/win32/erl_win32_sys_ddll.c
new file mode 100644
index 0000000000..a19f49af10
--- /dev/null
+++ b/erts/emulator/sys/win32/erl_win32_sys_ddll.c
@@ -0,0 +1,206 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2006-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Interface functions to the dynamic linker using dl* functions.
+ * (As far as I know it works on SunOS 4, 5, Linux and FreeBSD. /Seb)
+ */
+
+#include <windows.h>
+
+#define GET_ERTS_ALC_TEST
+#include "sys.h"
+#include "global.h"
+#include "erl_alloc.h"
+
+#include "erl_driver.h"
+#include "erl_win_dyn_driver.h"
+
+#include "erl_nif.h"
+
+#define EXT_LEN 4
+#define FILE_EXT ".dll"
+
+static DWORD tls_index = 0;
+static TWinDynDriverCallbacks wddc;
+static TWinDynNifCallbacks nif_callbacks;
+
+void erl_sys_ddll_init(void) {
+ tls_index = TlsAlloc();
+ ERL_INIT_CALLBACK_STRUCTURE(wddc);
+
+#define ERL_NIF_API_FUNC_DECL(RET,NAME,ARGS) nif_callbacks.NAME = NAME
+#include "erl_nif_api_funcs.h"
+#undef ERL_NIF_API_FUNC_DECL
+
+ return;
+}
+
+/*
+ * Open a shared object
+ */
+int erts_sys_ddll_open2(char *full_name, void **handle, ErtsSysDdllError* err)
+{
+ int len;
+ char dlname[MAXPATHLEN + 1];
+
+ if ((len = sys_strlen(full_name)) >= MAXPATHLEN - EXT_LEN) {
+ if (err != NULL) {
+ err->str = "Library name too long";
+ }
+ return ERL_DE_LOAD_ERROR_NAME_TO_LONG;
+ }
+ sys_strcpy(dlname, full_name);
+ sys_strcpy(dlname+len, FILE_EXT);
+ return erts_sys_ddll_open_noext(dlname, handle, err);
+}
+int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err)
+{
+ HINSTANCE hinstance;
+
+ if ((hinstance = LoadLibrary(dlname)) == NULL) {
+ int code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError();
+ if (err != NULL) {
+ err->str = erts_sys_ddll_error(code);
+ }
+ return code;
+ } else {
+ *handle = (void *) hinstance;
+ return ERL_DE_NO_ERROR;
+ }
+}
+
+/*
+ * Find a symbol in the shared object
+ */
+int erts_sys_ddll_sym2(void *handle, char *func_name, void **function,
+ ErtsSysDdllError* err)
+{
+ FARPROC proc;
+ if ((proc = GetProcAddress( (HINSTANCE) handle, func_name)) == NULL) {
+ int code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError();
+ if (err != NULL) {
+ err->str = erts_sys_ddll_error(code);
+ }
+ return code;
+ }
+ *function = (void *) proc;
+ return ERL_DE_NO_ERROR;
+}
+
+/* XXX:PaN These two will be changed with new driver interface! */
+
+/*
+ * Load the driver init function, might appear under different names depending on object arch...
+ */
+
+int erts_sys_ddll_load_driver_init(void *handle, void **function)
+{
+ void *fn;
+ int res;
+ if ((res = erts_sys_ddll_sym(handle, "driver_init", &fn)) != ERL_DE_NO_ERROR) {
+ return res;
+ }
+ *function = fn;
+ return res;
+}
+
+int erts_sys_ddll_load_nif_init(void *handle, void **function, ErtsSysDdllError* err)
+{
+ void *fn;
+ int res;
+ if ((res = erts_sys_ddll_sym2(handle, "nif_init", &fn, err)) != ERL_DE_NO_ERROR) {
+ return res;
+ }
+ *function = fn;
+ return res;
+}
+
+
+/*
+ * Call the driver_init function, whatever it's really called, simple on unix...
+*/
+void *erts_sys_ddll_call_init(void *function) {
+ void *(*initfn)(TWinDynDriverCallbacks *) = function;
+ return (*initfn)(&wddc);
+}
+
+void *erts_sys_ddll_call_nif_init(void *function) {
+ void *(*initfn)(TWinDynNifCallbacks *) = function;
+ return (*initfn)(&nif_callbacks);
+}
+
+
+/*
+ * Close a chared object
+ */
+int erts_sys_ddll_close2(void *handle, ErtsSysDdllError* err)
+{
+ if (!FreeLibrary((HINSTANCE) handle)) {
+ int code = ERL_DE_DYNAMIC_ERROR_OFFSET - GetLastError();
+ if (err != NULL) {
+ err->str = erts_sys_ddll_error(code);
+ }
+ return code;
+ }
+ return ERL_DE_NO_ERROR;
+}
+
+/*
+ * Return string that describes the (current) error
+ */
+#define MAX_ERROR 255
+char *erts_sys_ddll_error(int code)
+{
+ int actual_code;
+ char *local_ptr;
+ if (code > ERL_DE_DYNAMIC_ERROR_OFFSET) {
+ return "Unspecified error";
+ }
+ actual_code = -1*(code - ERL_DE_DYNAMIC_ERROR_OFFSET);
+
+ local_ptr = TlsGetValue(tls_index);
+ if (local_ptr == NULL) {
+ local_ptr = erts_alloc(ERTS_ALC_T_DDLL_ERRCODES, MAX_ERROR);
+ TlsSetValue(tls_index,local_ptr);
+ }
+ if (!FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ (DWORD) actual_code,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ local_ptr,
+ MAX_ERROR, NULL )) {
+ return "Unspecified error";
+ } else {
+ char *ptr = local_ptr + strlen(local_ptr) - 1;
+ while (ptr >= local_ptr && (*ptr == '\r' || *ptr == '\n')) {
+ *ptr-- = '\0';
+ }
+ }
+ return local_ptr;
+}
+
+void erts_sys_ddll_free_error(ErtsSysDdllError* err)
+{
+ /* err->str may be either a static string or reused as thread local data,
+ * so wo don't bother free it.
+ */
+}
+
diff --git a/erts/emulator/sys/win32/erl_win_dyn_driver.h b/erts/emulator/sys/win32/erl_win_dyn_driver.h
new file mode 100644
index 0000000000..4949998abc
--- /dev/null
+++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h
@@ -0,0 +1,489 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2003-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Include file for erlang driver writers using dynamic drivers on windows.
+ */
+
+/* Maybe this should be auto generated, but I'll leave that for now... */
+
+#ifndef _ERL_WIN_DYN_DRIVER_H
+#define _ERL_WIN_DYN_DRIVER_H
+
+#define WDD_FTYPE(FunctionName) TWinDynDriver##FunctionName
+
+#define WDD_TYPEDEF(RetType, FunctionName, Params) \
+ typedef RetType WDD_FTYPE(FunctionName) Params
+
+WDD_TYPEDEF(int, null_func,(void));
+WDD_TYPEDEF(int, driver_failure_atom,(ErlDrvPort, char *));
+WDD_TYPEDEF(int, driver_failure_posix,(ErlDrvPort, int));
+WDD_TYPEDEF(int, driver_failure,(ErlDrvPort, int));
+WDD_TYPEDEF(int, driver_exit, (ErlDrvPort, int));
+WDD_TYPEDEF(int, driver_failure_eof, (ErlDrvPort));
+WDD_TYPEDEF(int, driver_select, (ErlDrvPort, ErlDrvEvent, int, int));
+WDD_TYPEDEF(int, driver_event, (ErlDrvPort, ErlDrvEvent,ErlDrvEventData));
+WDD_TYPEDEF(int, driver_output, (ErlDrvPort, char *, int));
+WDD_TYPEDEF(int, driver_output2, (ErlDrvPort, char *, int,char *, int));
+WDD_TYPEDEF(int, driver_output_binary, (ErlDrvPort, char *, int,ErlDrvBinary*, int, int));
+WDD_TYPEDEF(int, driver_outputv, (ErlDrvPort, char*, int, ErlIOVec *,int));
+WDD_TYPEDEF(int, driver_vec_to_buf, (ErlIOVec *, char *, int));
+WDD_TYPEDEF(int, driver_set_timer, (ErlDrvPort, unsigned long));
+WDD_TYPEDEF(int, driver_cancel_timer, (ErlDrvPort));
+WDD_TYPEDEF(int, driver_read_timer, (ErlDrvPort, unsigned long *));
+WDD_TYPEDEF(char *, erl_errno_id, (int));
+WDD_TYPEDEF(void, set_busy_port, (ErlDrvPort, int));
+WDD_TYPEDEF(void, set_port_control_flags, (ErlDrvPort, int));
+WDD_TYPEDEF(int, get_port_flags, (ErlDrvPort));
+WDD_TYPEDEF(ErlDrvBinary *, driver_alloc_binary, (int));
+WDD_TYPEDEF(ErlDrvBinary *, driver_realloc_binary, (ErlDrvBinary *, int));
+WDD_TYPEDEF(void, driver_free_binary, (ErlDrvBinary *));
+WDD_TYPEDEF(void *, driver_alloc, (size_t));
+WDD_TYPEDEF(void *, driver_realloc, (void *, size_t));
+WDD_TYPEDEF(void, driver_free, (void *));
+WDD_TYPEDEF(int, driver_enq, (ErlDrvPort, char*, int));
+WDD_TYPEDEF(int, driver_pushq, (ErlDrvPort, char*, int));
+WDD_TYPEDEF(int, driver_deq, (ErlDrvPort, int));
+WDD_TYPEDEF(int, driver_sizeq, (ErlDrvPort));
+WDD_TYPEDEF(int, driver_enq_bin, (ErlDrvPort, ErlDrvBinary *, int,int));
+WDD_TYPEDEF(int, driver_pushq_bin, (ErlDrvPort, ErlDrvBinary *, int,int));
+WDD_TYPEDEF(int, driver_peekqv, (ErlDrvPort, ErlIOVec *));
+WDD_TYPEDEF(SysIOVec *, driver_peekq, (ErlDrvPort, int *));
+WDD_TYPEDEF(int, driver_enqv, (ErlDrvPort, ErlIOVec *, int));
+WDD_TYPEDEF(int, driver_pushqv, (ErlDrvPort, ErlIOVec *, int));
+WDD_TYPEDEF(void, add_driver_entry, (ErlDrvEntry *));
+WDD_TYPEDEF(int, remove_driver_entry, (ErlDrvEntry *));
+WDD_TYPEDEF(ErlDrvTermData, driver_mk_atom, (char*));
+WDD_TYPEDEF(ErlDrvTermData, driver_mk_port,(ErlDrvPort));
+WDD_TYPEDEF(ErlDrvTermData, driver_connected,(ErlDrvPort));
+WDD_TYPEDEF(ErlDrvTermData, driver_caller,(ErlDrvPort));
+WDD_TYPEDEF(ErlDrvTermData, driver_mk_term_nil,(void));
+WDD_TYPEDEF(int, driver_output_term, (ErlDrvPort, ErlDrvTermData*, int));
+WDD_TYPEDEF(int, driver_send_term, (ErlDrvPort, ErlDrvTermData, ErlDrvTermData*, int));
+WDD_TYPEDEF(long, driver_async, (ErlDrvPort,unsigned int*,void (*)(void*),void*,void (*)(void*)));
+WDD_TYPEDEF(int, driver_async_cancel, (unsigned int));
+WDD_TYPEDEF(int, driver_lock_driver, (ErlDrvPort));
+WDD_TYPEDEF(void *, driver_dl_open, (char *));
+WDD_TYPEDEF(void *, driver_dl_sym, (void *, char *));
+WDD_TYPEDEF(int, driver_dl_close, (void *));
+WDD_TYPEDEF(char *, driver_dl_error, (void));
+WDD_TYPEDEF(unsigned long, erts_alc_test, (unsigned long,
+ unsigned long,
+ unsigned long,
+ unsigned long));
+WDD_TYPEDEF(long, driver_binary_get_refc, (ErlDrvBinary *dbp));
+WDD_TYPEDEF(long, driver_binary_inc_refc, (ErlDrvBinary *dbp));
+WDD_TYPEDEF(long, driver_binary_dec_refc, (ErlDrvBinary *dbp));
+WDD_TYPEDEF(ErlDrvPDL, driver_pdl_create, (ErlDrvPort));
+WDD_TYPEDEF(void, driver_pdl_lock, (ErlDrvPDL));
+WDD_TYPEDEF(void, driver_pdl_unlock, (ErlDrvPDL));
+WDD_TYPEDEF(long, driver_pdl_get_refc, (ErlDrvPDL));
+WDD_TYPEDEF(long, driver_pdl_inc_refc, (ErlDrvPDL));
+WDD_TYPEDEF(long, driver_pdl_dec_refc, (ErlDrvPDL));
+WDD_TYPEDEF(void, driver_system_info, (ErlDrvSysInfo *, size_t));
+WDD_TYPEDEF(int, driver_get_now, (ErlDrvNowData *));
+WDD_TYPEDEF(int, driver_monitor_process, (ErlDrvPort port,
+ ErlDrvTermData process,
+ ErlDrvMonitor *monitor));
+WDD_TYPEDEF(int, driver_demonitor_process, (ErlDrvPort port,
+ const ErlDrvMonitor *monitor));
+WDD_TYPEDEF(ErlDrvTermData, driver_get_monitored_process,
+ (ErlDrvPort port, const ErlDrvMonitor *monitor));
+WDD_TYPEDEF(int, driver_compare_monitors,
+ (const ErlDrvMonitor *, const ErlDrvMonitor *));
+WDD_TYPEDEF(ErlDrvMutex *, erl_drv_mutex_create, (char *name));
+WDD_TYPEDEF(void, erl_drv_mutex_destroy, (ErlDrvMutex *mtx));
+WDD_TYPEDEF(int, erl_drv_mutex_trylock, (ErlDrvMutex *mtx));
+WDD_TYPEDEF(void, erl_drv_mutex_lock, (ErlDrvMutex *mtx));
+WDD_TYPEDEF(void, erl_drv_mutex_unlock, (ErlDrvMutex *mtx));
+WDD_TYPEDEF(ErlDrvCond *, erl_drv_cond_create, (char *name));
+WDD_TYPEDEF(void, erl_drv_cond_destroy, (ErlDrvCond *cnd));
+WDD_TYPEDEF(void, erl_drv_cond_signal, (ErlDrvCond *cnd));
+WDD_TYPEDEF(void, erl_drv_cond_broadcast, (ErlDrvCond *cnd));
+WDD_TYPEDEF(void, erl_drv_cond_wait, (ErlDrvCond *cnd, ErlDrvMutex *mtx));
+WDD_TYPEDEF(ErlDrvRWLock *, erl_drv_rwlock_create, (char *name));
+WDD_TYPEDEF(void, erl_drv_rwlock_destroy, (ErlDrvRWLock *rwlck));
+WDD_TYPEDEF(int, erl_drv_rwlock_tryrlock, (ErlDrvRWLock *rwlck));
+WDD_TYPEDEF(void, erl_drv_rwlock_rlock, (ErlDrvRWLock *rwlck));
+WDD_TYPEDEF(void, erl_drv_rwlock_runlock, (ErlDrvRWLock *rwlck));
+WDD_TYPEDEF(int, erl_drv_rwlock_tryrwlock, (ErlDrvRWLock *rwlck));
+WDD_TYPEDEF(void, erl_drv_rwlock_rwlock, (ErlDrvRWLock *rwlck));
+WDD_TYPEDEF(void, erl_drv_rwlock_rwunlock, (ErlDrvRWLock *rwlck));
+WDD_TYPEDEF(int, erl_drv_tsd_key_create, (char *name, ErlDrvTSDKey *key));
+WDD_TYPEDEF(void, erl_drv_tsd_key_destroy, (ErlDrvTSDKey key));
+WDD_TYPEDEF(void, erl_drv_tsd_set, (ErlDrvTSDKey key, void *data));
+WDD_TYPEDEF(void *, erl_drv_tsd_get, (ErlDrvTSDKey key));
+WDD_TYPEDEF(ErlDrvThreadOpts *, erl_drv_thread_opts_create, (char *name));
+WDD_TYPEDEF(void, erl_drv_thread_opts_destroy, (ErlDrvThreadOpts *opts));
+WDD_TYPEDEF(int, erl_drv_thread_create, (char *name,
+ ErlDrvTid *tid,
+ void * (*func)(void *),
+ void *args,
+ ErlDrvThreadOpts *opts));
+WDD_TYPEDEF(ErlDrvTid, erl_drv_thread_self, (void));
+WDD_TYPEDEF(int, erl_drv_equal_tids, (ErlDrvTid tid1, ErlDrvTid tid2));
+WDD_TYPEDEF(void, erl_drv_thread_exit, (void *resp));
+WDD_TYPEDEF(int, erl_drv_thread_join, (ErlDrvTid, void **respp));
+WDD_TYPEDEF(int, erl_drv_putenv, (char *key, char *value));
+WDD_TYPEDEF(int, erl_drv_getenv, (char *key, char *value, size_t *value_size));
+
+typedef struct {
+ WDD_FTYPE(null_func) *null_func;
+ WDD_FTYPE(driver_failure_atom) *driver_failure_atom;
+ WDD_FTYPE(driver_failure_posix) *driver_failure_posix;
+ WDD_FTYPE(driver_failure) *driver_failure;
+ WDD_FTYPE(driver_exit) *driver_exit;
+ WDD_FTYPE(driver_failure_eof) *driver_failure_eof;
+ WDD_FTYPE(driver_select) *driver_select;
+ WDD_FTYPE(driver_event) *driver_event;
+ WDD_FTYPE(driver_output) *driver_output;
+ WDD_FTYPE(driver_output2) *driver_output2;
+ WDD_FTYPE(driver_output_binary) *driver_output_binary;
+ WDD_FTYPE(driver_outputv) *driver_outputv;
+ WDD_FTYPE(driver_vec_to_buf) *driver_vec_to_buf;
+ WDD_FTYPE(driver_set_timer) *driver_set_timer;
+ WDD_FTYPE(driver_cancel_timer) *driver_cancel_timer;
+ WDD_FTYPE(driver_read_timer) *driver_read_timer;
+ WDD_FTYPE(erl_errno_id) *erl_errno_id;
+ WDD_FTYPE(set_busy_port)* set_busy_port;
+ WDD_FTYPE(set_port_control_flags) *set_port_control_flags;
+ WDD_FTYPE(get_port_flags) *get_port_flags;
+ WDD_FTYPE(driver_alloc_binary) *driver_alloc_binary;
+ WDD_FTYPE(driver_realloc_binary) *driver_realloc_binary;
+ WDD_FTYPE(driver_free_binary) *driver_free_binary;
+ WDD_FTYPE(driver_alloc) *driver_alloc;
+ WDD_FTYPE(driver_realloc) *driver_realloc;
+ WDD_FTYPE(driver_free) *driver_free;
+ WDD_FTYPE(driver_enq) *driver_enq;
+ WDD_FTYPE(driver_pushq) *driver_pushq;
+ WDD_FTYPE(driver_deq) *driver_deq;
+ WDD_FTYPE(driver_sizeq) *driver_sizeq;
+ WDD_FTYPE(driver_enq_bin)* driver_enq_bin;
+ WDD_FTYPE(driver_pushq_bin) *driver_pushq_bin;
+ WDD_FTYPE(driver_peekqv) *driver_peekqv;
+ WDD_FTYPE(driver_peekq) *driver_peekq;
+ WDD_FTYPE(driver_enqv) *driver_enqv;
+ WDD_FTYPE(driver_pushqv) *driver_pushqv;
+ WDD_FTYPE(add_driver_entry) *add_driver_entry;
+ WDD_FTYPE(remove_driver_entry) *remove_driver_entry;
+ WDD_FTYPE(driver_mk_atom) *driver_mk_atom;
+ WDD_FTYPE(driver_mk_port) *driver_mk_port;
+ WDD_FTYPE(driver_connected) *driver_connected;
+ WDD_FTYPE(driver_caller) *driver_caller;
+ WDD_FTYPE(driver_mk_term_nil) *driver_mk_term_nil;
+ WDD_FTYPE(driver_output_term) *driver_output_term;
+ WDD_FTYPE(driver_send_term) *driver_send_term;
+ WDD_FTYPE(driver_async) *driver_async;
+ WDD_FTYPE(driver_async_cancel) *driver_async_cancel;
+ WDD_FTYPE(driver_lock_driver) *driver_lock_driver;
+ WDD_FTYPE(driver_dl_open) *driver_dl_open;
+ WDD_FTYPE(driver_dl_sym) *driver_dl_sym;
+ WDD_FTYPE(driver_dl_close) *driver_dl_close;
+ WDD_FTYPE(driver_dl_error) *driver_dl_error;
+ WDD_FTYPE(erts_alc_test) *erts_alc_test;
+ WDD_FTYPE(driver_binary_get_refc) *driver_binary_get_refc;
+ WDD_FTYPE(driver_binary_inc_refc) *driver_binary_inc_refc;
+ WDD_FTYPE(driver_binary_dec_refc) *driver_binary_dec_refc;
+ WDD_FTYPE(driver_pdl_create) *driver_pdl_create;
+ WDD_FTYPE(driver_pdl_lock) *driver_pdl_lock;
+ WDD_FTYPE(driver_pdl_unlock) *driver_pdl_unlock;
+ WDD_FTYPE(driver_pdl_get_refc) *driver_pdl_get_refc;
+ WDD_FTYPE(driver_pdl_inc_refc) *driver_pdl_inc_refc;
+ WDD_FTYPE(driver_pdl_dec_refc) *driver_pdl_dec_refc;
+ WDD_FTYPE(driver_system_info) *driver_system_info;
+ WDD_FTYPE(driver_get_now) *driver_get_now;
+ WDD_FTYPE(driver_monitor_process) *driver_monitor_process;
+ WDD_FTYPE(driver_demonitor_process) *driver_demonitor_process;
+ WDD_FTYPE(driver_get_monitored_process) *driver_get_monitored_process;
+ WDD_FTYPE(driver_compare_monitors) *driver_compare_monitors;
+ WDD_FTYPE(erl_drv_mutex_create) *erl_drv_mutex_create;
+ WDD_FTYPE(erl_drv_mutex_destroy) *erl_drv_mutex_destroy;
+ WDD_FTYPE(erl_drv_mutex_trylock) *erl_drv_mutex_trylock;
+ WDD_FTYPE(erl_drv_mutex_lock) *erl_drv_mutex_lock;
+ WDD_FTYPE(erl_drv_mutex_unlock) *erl_drv_mutex_unlock;
+ WDD_FTYPE(erl_drv_cond_create) *erl_drv_cond_create;
+ WDD_FTYPE(erl_drv_cond_destroy) *erl_drv_cond_destroy;
+ WDD_FTYPE(erl_drv_cond_signal) *erl_drv_cond_signal;
+ WDD_FTYPE(erl_drv_cond_broadcast) *erl_drv_cond_broadcast;
+ WDD_FTYPE(erl_drv_cond_wait) *erl_drv_cond_wait;
+ WDD_FTYPE(erl_drv_rwlock_create) *erl_drv_rwlock_create;
+ WDD_FTYPE(erl_drv_rwlock_destroy) *erl_drv_rwlock_destroy;
+ WDD_FTYPE(erl_drv_rwlock_tryrlock) *erl_drv_rwlock_tryrlock;
+ WDD_FTYPE(erl_drv_rwlock_rlock) *erl_drv_rwlock_rlock;
+ WDD_FTYPE(erl_drv_rwlock_runlock) *erl_drv_rwlock_runlock;
+ WDD_FTYPE(erl_drv_rwlock_tryrwlock) *erl_drv_rwlock_tryrwlock;
+ WDD_FTYPE(erl_drv_rwlock_rwlock) *erl_drv_rwlock_rwlock;
+ WDD_FTYPE(erl_drv_rwlock_rwunlock) *erl_drv_rwlock_rwunlock;
+ WDD_FTYPE(erl_drv_tsd_key_create) *erl_drv_tsd_key_create;
+ WDD_FTYPE(erl_drv_tsd_key_destroy) *erl_drv_tsd_key_destroy;
+ WDD_FTYPE(erl_drv_tsd_set) *erl_drv_tsd_set;
+ WDD_FTYPE(erl_drv_tsd_get) *erl_drv_tsd_get;
+ WDD_FTYPE(erl_drv_thread_opts_create) *erl_drv_thread_opts_create;
+ WDD_FTYPE(erl_drv_thread_opts_destroy) *erl_drv_thread_opts_destroy;
+ WDD_FTYPE(erl_drv_thread_create) *erl_drv_thread_create;
+ WDD_FTYPE(erl_drv_thread_self) *erl_drv_thread_self;
+ WDD_FTYPE(erl_drv_equal_tids) *erl_drv_equal_tids;
+ WDD_FTYPE(erl_drv_thread_exit) *erl_drv_thread_exit;
+ WDD_FTYPE(erl_drv_thread_join) *erl_drv_thread_join;
+ WDD_FTYPE(erl_drv_putenv) *erl_drv_putenv;
+ WDD_FTYPE(erl_drv_getenv) *erl_drv_getenv;
+ /* Add new calls here */
+} TWinDynDriverCallbacks;
+
+/* This header is included explicitly by the ddll static driver, it musn't define things then */
+#ifndef STATIC_ERLANG_DRIVER
+
+extern TWinDynDriverCallbacks WinDynDriverCallbacks;
+
+#define null_func (WinDynDriverCallbacks.null_func)
+#define driver_failure_atom (WinDynDriverCallbacks.driver_failure_atom)
+#define driver_failure_posix (WinDynDriverCallbacks.driver_failure_posix)
+#define driver_failure (WinDynDriverCallbacks.driver_failure)
+#define driver_exit (WinDynDriverCallbacks.driver_exit)
+#define driver_failure_eof (WinDynDriverCallbacks.driver_failure_eof)
+#define driver_select (WinDynDriverCallbacks.driver_select)
+#define driver_event (WinDynDriverCallbacks.driver_event)
+#define driver_output (WinDynDriverCallbacks.driver_output)
+#define driver_output2 (WinDynDriverCallbacks.driver_output2)
+#define driver_output_binary (WinDynDriverCallbacks.driver_output_binary)
+#define driver_outputv (WinDynDriverCallbacks.driver_outputv)
+#define driver_vec_to_buf (WinDynDriverCallbacks.driver_vec_to_buf)
+#define driver_set_timer (WinDynDriverCallbacks.driver_set_timer)
+#define driver_cancel_timer (WinDynDriverCallbacks.driver_cancel_timer)
+#define driver_read_timer (WinDynDriverCallbacks.driver_read_timer)
+#define erl_errno_id (WinDynDriverCallbacks.erl_errno_id)
+#define set_busy_port (WinDynDriverCallbacks.set_busy_port)
+#define set_port_control_flags (WinDynDriverCallbacks.set_port_control_flags)
+#define get_port_flags (WinDynDriverCallbacks.get_port_flags)
+#define driver_alloc_binary (WinDynDriverCallbacks.driver_alloc_binary)
+#define driver_realloc_binary (WinDynDriverCallbacks.driver_realloc_binary)
+#define driver_free_binary (WinDynDriverCallbacks.driver_free_binary)
+#define driver_alloc (WinDynDriverCallbacks.driver_alloc)
+#define driver_realloc (WinDynDriverCallbacks.driver_realloc)
+#define driver_free (WinDynDriverCallbacks.driver_free)
+#define driver_enq (WinDynDriverCallbacks.driver_enq)
+#define driver_pushq (WinDynDriverCallbacks.driver_pushq)
+#define driver_deq (WinDynDriverCallbacks.driver_deq)
+#define driver_sizeq (WinDynDriverCallbacks.driver_sizeq)
+#define driver_enq_bin (WinDynDriverCallbacks.driver_enq_bin)
+#define driver_pushq_bin (WinDynDriverCallbacks.driver_pushq_bin)
+#define driver_peekqv (WinDynDriverCallbacks.driver_peekqv)
+#define driver_peekq (WinDynDriverCallbacks.driver_peekq)
+#define driver_enqv (WinDynDriverCallbacks.driver_enqv)
+#define driver_pushqv (WinDynDriverCallbacks.driver_pushqv)
+#define add_driver_entry (WinDynDriverCallbacks.add_driver_entry)
+#define remove_driver_entry (WinDynDriverCallbacks.remove_driver_entry)
+#define driver_mk_atom (WinDynDriverCallbacks.driver_mk_atom)
+#define driver_mk_port (WinDynDriverCallbacks.driver_mk_port)
+#define driver_connected (WinDynDriverCallbacks.driver_connected)
+#define driver_caller (WinDynDriverCallbacks.driver_caller)
+#define driver_mk_term_nil (WinDynDriverCallbacks.driver_mk_term_nil)
+#define driver_output_term (WinDynDriverCallbacks.driver_output_term)
+#define driver_send_term (WinDynDriverCallbacks.driver_send_term)
+#define driver_async (WinDynDriverCallbacks.driver_async)
+#define driver_async_cancel (WinDynDriverCallbacks.driver_async_cancel)
+#define driver_lock_driver (WinDynDriverCallbacks.driver_lock_driver)
+#define driver_dl_open (WinDynDriverCallbacks.driver_dl_open)
+#define driver_dl_sym (WinDynDriverCallbacks.driver_dl_sym)
+#define driver_dl_close (WinDynDriverCallbacks.driver_dl_close)
+#define driver_dl_error (WinDynDriverCallbacks.driver_dl_error)
+#define erts_alc_test (WinDynDriverCallbacks.erts_alc_test)
+#define driver_binary_get_refc (WinDynDriverCallbacks.driver_binary_get_refc)
+#define driver_binary_inc_refc (WinDynDriverCallbacks.driver_binary_inc_refc)
+#define driver_binary_dec_refc (WinDynDriverCallbacks.driver_binary_dec_refc)
+#define driver_pdl_create (WinDynDriverCallbacks.driver_pdl_create)
+#define driver_pdl_lock (WinDynDriverCallbacks.driver_pdl_lock)
+#define driver_pdl_unlock (WinDynDriverCallbacks.driver_pdl_unlock)
+#define driver_pdl_get_refc (WinDynDriverCallbacks.driver_pdl_get_refc)
+#define driver_pdl_inc_refc (WinDynDriverCallbacks.driver_pdl_inc_refc)
+#define driver_pdl_dec_refc (WinDynDriverCallbacks.driver_pdl_dec_refc)
+#define driver_system_info (WinDynDriverCallbacks.driver_system_info)
+#define driver_get_now (WinDynDriverCallbacks.driver_get_now)
+#define driver_monitor_process \
+(WinDynDriverCallbacks.driver_monitor_process)
+#define driver_demonitor_process \
+(WinDynDriverCallbacks.driver_demonitor_process)
+#define driver_get_monitored_process \
+(WinDynDriverCallbacks.driver_get_monitored_process)
+#define driver_compare_monitors \
+(WinDynDriverCallbacks.driver_compare_monitors)
+#define erl_drv_mutex_create (WinDynDriverCallbacks.erl_drv_mutex_create)
+#define erl_drv_mutex_destroy (WinDynDriverCallbacks.erl_drv_mutex_destroy)
+#define erl_drv_mutex_trylock (WinDynDriverCallbacks.erl_drv_mutex_trylock)
+#define erl_drv_mutex_lock (WinDynDriverCallbacks.erl_drv_mutex_lock)
+#define erl_drv_mutex_unlock (WinDynDriverCallbacks.erl_drv_mutex_unlock)
+#define erl_drv_cond_create (WinDynDriverCallbacks.erl_drv_cond_create)
+#define erl_drv_cond_destroy (WinDynDriverCallbacks.erl_drv_cond_destroy)
+#define erl_drv_cond_signal (WinDynDriverCallbacks.erl_drv_cond_signal)
+#define erl_drv_cond_broadcast (WinDynDriverCallbacks.erl_drv_cond_broadcast)
+#define erl_drv_cond_wait (WinDynDriverCallbacks.erl_drv_cond_wait)
+#define erl_drv_rwlock_create (WinDynDriverCallbacks.erl_drv_rwlock_create)
+#define erl_drv_rwlock_destroy (WinDynDriverCallbacks.erl_drv_rwlock_destroy)
+#define erl_drv_rwlock_tryrlock (WinDynDriverCallbacks.erl_drv_rwlock_tryrlock)
+#define erl_drv_rwlock_rlock (WinDynDriverCallbacks.erl_drv_rwlock_rlock)
+#define erl_drv_rwlock_runlock (WinDynDriverCallbacks.erl_drv_rwlock_runlock)
+#define erl_drv_rwlock_tryrwlock \
+(WinDynDriverCallbacks.erl_drv_rwlock_tryrwlock)
+#define erl_drv_rwlock_rwlock (WinDynDriverCallbacks.erl_drv_rwlock_rwlock)
+#define erl_drv_rwlock_rwunlock (WinDynDriverCallbacks.erl_drv_rwlock_rwunlock)
+#define erl_drv_tsd_key_create (WinDynDriverCallbacks.erl_drv_tsd_key_create)
+#define erl_drv_tsd_key_destroy (WinDynDriverCallbacks.erl_drv_tsd_key_destroy)
+#define erl_drv_tsd_set (WinDynDriverCallbacks.erl_drv_tsd_set)
+#define erl_drv_tsd_get (WinDynDriverCallbacks.erl_drv_tsd_get)
+#define erl_drv_thread_opts_create \
+(WinDynDriverCallbacks.erl_drv_thread_opts_create)
+#define erl_drv_thread_opts_destroy \
+(WinDynDriverCallbacks.erl_drv_thread_opts_destroy)
+#define erl_drv_thread_create (WinDynDriverCallbacks.erl_drv_thread_create)
+#define erl_drv_thread_self (WinDynDriverCallbacks.erl_drv_thread_self)
+#define erl_drv_equal_tids (WinDynDriverCallbacks.erl_drv_equal_tids)
+#define erl_drv_thread_exit (WinDynDriverCallbacks.erl_drv_thread_exit)
+#define erl_drv_thread_join (WinDynDriverCallbacks.erl_drv_thread_join)
+#define erl_drv_putenv (WinDynDriverCallbacks.erl_drv_putenv)
+#define erl_drv_getenv (WinDynDriverCallbacks.erl_drv_getenv)
+
+/* The only variable in the interface... */
+#define driver_term_nil (driver_mk_term_nil())
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define DRIVER_INIT(DriverName) \
+ErlDrvEntry *erl_dyndriver_real_driver_init(void); \
+TWinDynDriverCallbacks WinDynDriverCallbacks; \
+__declspec(dllexport) ErlDrvEntry *driver_init(TWinDynDriverCallbacks *callbacks) \
+{ \
+ memcpy(&WinDynDriverCallbacks,callbacks,sizeof(TWinDynDriverCallbacks)); \
+ return erl_dyndriver_real_driver_init(); \
+} \
+ErlDrvEntry *erl_dyndriver_real_driver_init(void)
+
+/* This is to make erl_driver.h avoid changing what's done here */
+#define ERL_DRIVER_TYPES_ONLY
+
+#else /* defined(STATIC_ERLANG_DRIVER) */
+/* This is for the ddll driver */
+
+#define ERL_INIT_CALLBACK_STRUCTURE(W) \
+do { \
+((W).null_func) = null_func; \
+((W).driver_failure_atom) = driver_failure_atom; \
+((W).driver_failure_posix) = driver_failure_posix; \
+((W).driver_failure) = driver_failure; \
+((W).driver_exit) = driver_exit; \
+((W).driver_failure_eof) = driver_failure_eof; \
+((W).driver_select) = driver_select; \
+((W).driver_event) = driver_event; \
+((W).driver_output) = driver_output; \
+((W).driver_output2) = driver_output2; \
+((W).driver_output_binary) = driver_output_binary; \
+((W).driver_outputv) = driver_outputv; \
+((W).driver_vec_to_buf) = driver_vec_to_buf; \
+((W).driver_set_timer) = driver_set_timer; \
+((W).driver_cancel_timer) = driver_cancel_timer; \
+((W).driver_read_timer) = driver_read_timer; \
+((W).erl_errno_id) = erl_errno_id; \
+((W).set_busy_port) = set_busy_port; \
+((W).set_port_control_flags) = set_port_control_flags; \
+((W).get_port_flags) = get_port_flags; \
+((W).driver_alloc_binary) = driver_alloc_binary; \
+((W).driver_realloc_binary) = driver_realloc_binary; \
+((W).driver_free_binary) = driver_free_binary; \
+((W).driver_alloc) = driver_alloc; \
+((W).driver_realloc) = driver_realloc; \
+((W).driver_free) = driver_free; \
+((W).driver_enq) = driver_enq; \
+((W).driver_pushq) = driver_pushq; \
+((W).driver_deq) = driver_deq; \
+((W).driver_sizeq) = driver_sizeq; \
+((W).driver_enq_bin) = driver_enq_bin; \
+((W).driver_pushq_bin) = driver_pushq_bin; \
+((W).driver_peekqv) = driver_peekqv; \
+((W).driver_peekq) = driver_peekq; \
+((W).driver_enqv) = driver_enqv; \
+((W).driver_pushqv) = driver_pushqv; \
+((W).add_driver_entry) = add_driver_entry; \
+((W).remove_driver_entry) = remove_driver_entry; \
+((W).driver_mk_atom) = driver_mk_atom; \
+((W).driver_mk_port) = driver_mk_port; \
+((W).driver_connected) = driver_connected; \
+((W).driver_caller) = driver_caller; \
+((W).driver_mk_term_nil) = driver_mk_term_nil; \
+((W).driver_output_term) = driver_output_term; \
+((W).driver_send_term) = driver_send_term; \
+((W).driver_async) = driver_async; \
+((W).driver_async_cancel) = driver_async_cancel; \
+((W).driver_lock_driver) = driver_lock_driver; \
+((W).driver_dl_open) = driver_dl_open; \
+((W).driver_dl_sym) = driver_dl_sym; \
+((W).driver_dl_close) = driver_dl_close; \
+((W).driver_dl_error) = driver_dl_error; \
+((W).erts_alc_test) = erts_alc_test; \
+((W).driver_binary_get_refc) = driver_binary_get_refc; \
+((W).driver_binary_inc_refc) = driver_binary_inc_refc; \
+((W).driver_binary_dec_refc) = driver_binary_dec_refc; \
+((W).driver_pdl_create) = driver_pdl_create; \
+((W).driver_pdl_lock) = driver_pdl_lock; \
+((W).driver_pdl_unlock) = driver_pdl_unlock; \
+((W).driver_pdl_get_refc) = driver_pdl_get_refc; \
+((W).driver_pdl_inc_refc) = driver_pdl_inc_refc; \
+((W).driver_pdl_dec_refc) = driver_pdl_dec_refc; \
+((W).driver_system_info) = driver_system_info; \
+((W).driver_get_now) = driver_get_now; \
+((W).driver_monitor_process) = driver_monitor_process; \
+((W).driver_demonitor_process) = driver_demonitor_process; \
+((W).driver_get_monitored_process) = driver_get_monitored_process; \
+((W).driver_compare_monitors) = driver_compare_monitors;\
+((W).erl_drv_mutex_create) = erl_drv_mutex_create; \
+((W).erl_drv_mutex_destroy) = erl_drv_mutex_destroy; \
+((W).erl_drv_mutex_trylock) = erl_drv_mutex_trylock; \
+((W).erl_drv_mutex_lock) = erl_drv_mutex_lock; \
+((W).erl_drv_mutex_unlock) = erl_drv_mutex_unlock; \
+((W).erl_drv_cond_create) = erl_drv_cond_create; \
+((W).erl_drv_cond_destroy) = erl_drv_cond_destroy; \
+((W).erl_drv_cond_signal) = erl_drv_cond_signal; \
+((W).erl_drv_cond_broadcast) = erl_drv_cond_broadcast; \
+((W).erl_drv_cond_wait) = erl_drv_cond_wait; \
+((W).erl_drv_rwlock_create) = erl_drv_rwlock_create; \
+((W).erl_drv_rwlock_destroy) = erl_drv_rwlock_destroy; \
+((W).erl_drv_rwlock_tryrlock) = erl_drv_rwlock_tryrlock;\
+((W).erl_drv_rwlock_rlock) = erl_drv_rwlock_rlock; \
+((W).erl_drv_rwlock_runlock) = erl_drv_rwlock_runlock; \
+((W).erl_drv_rwlock_tryrwlock) = erl_drv_rwlock_tryrwlock;\
+((W).erl_drv_rwlock_rwlock) = erl_drv_rwlock_rwlock; \
+((W).erl_drv_rwlock_rwunlock) = erl_drv_rwlock_rwunlock;\
+((W).erl_drv_tsd_key_create) = erl_drv_tsd_key_create; \
+((W).erl_drv_tsd_key_destroy) = erl_drv_tsd_key_destroy;\
+((W).erl_drv_tsd_set) = erl_drv_tsd_set; \
+((W).erl_drv_tsd_get) = erl_drv_tsd_get; \
+((W).erl_drv_thread_opts_create) = erl_drv_thread_opts_create;\
+((W).erl_drv_thread_opts_destroy) = erl_drv_thread_opts_destroy;\
+((W).erl_drv_thread_create) = erl_drv_thread_create; \
+((W).erl_drv_thread_self) = erl_drv_thread_self; \
+((W).erl_drv_equal_tids) = erl_drv_equal_tids; \
+((W).erl_drv_thread_exit) = erl_drv_thread_exit; \
+((W).erl_drv_thread_join) = erl_drv_thread_join; \
+((W).erl_drv_putenv) = erl_drv_putenv; \
+((W).erl_drv_getenv) = erl_drv_getenv; \
+} while (0)
+
+
+
+#endif /* STATIC_ERLANG_DRIVER */
+#endif /* _ERL_WIN_DYN_DRIVER_H */
diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h
new file mode 100644
index 0000000000..92d8577537
--- /dev/null
+++ b/erts/emulator/sys/win32/erl_win_sys.h
@@ -0,0 +1,212 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * This file handles differences between operating systems.
+ * This should be the only place with conditional compilation
+ * depending on the type of OS.
+ */
+
+#ifndef _ERL_WIN_SYS_H
+#define _ERL_WIN_SYS_H
+
+#define HAS_STDARG
+
+#ifdef __GNUC__
+#ifdef pid_t
+/* Really... */
+#undef pid_t
+#endif
+#endif
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <process.h>
+#include <malloc.h>
+#ifndef __GNUC__
+#include <direct.h>
+#endif
+#include "erl_errno.h"
+#include <io.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/timeb.h>
+#pragma comment(linker,"/manifestdependency:\"type='win32' "\
+ "name='Microsoft.Windows.Common-Controls' "\
+ "version='6.0.0.0' processorArchitecture='*' "\
+ "publicKeyToken='6595b64144ccf1df' language='*'\"")
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+
+/*
+ * Define MAXPATHLEN in terms of MAXPATH if available.
+ */
+
+#ifndef MAXPATH
+#define MAXPATH MAX_PATH
+#endif /* MAXPATH */
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN MAXPATH
+#endif /* MAXPATHLEN */
+
+/*
+ * Various configuration options, used to be in the Makefile.
+ */
+
+#define NO_ASINH
+#define NO_ACOSH
+#define NO_ATANH
+#define NO_ERF
+#define NO_ERFC
+
+#define NO_SYSLOG
+#define NO_SYSCONF
+#define NO_DAEMON
+#define NO_PWD
+/*#define HAVE_MEMMOVE*/
+
+#define strncasecmp _strnicmp
+
+/*
+ * Practial Windows specific macros.
+ */
+
+#define CreateAutoEvent(state) CreateEvent(NULL, FALSE, state, NULL)
+#define CreateManualEvent(state) CreateEvent(NULL, TRUE, state, NULL)
+
+
+/*
+ * Our own type of "FD's"
+ */
+#define ERTS_SYS_FD_TYPE HANDLE
+#define NO_FSTAT_ON_SYS_FD_TYPE 1 /* They are events, not files */
+
+#define HAVE_ERTS_CHECK_IO_DEBUG
+int erts_check_io_debug(void);
+
+/*
+ * For erl_time_sup
+ */
+#define HAVE_GETHRTIME
+
+#define sys_init_hrtime() /* Nothing */
+
+#define SYS_CLK_TCK 1000
+#define SYS_CLOCK_RESOLUTION 1
+
+typedef struct {
+ long tv_sec;
+ long tv_usec;
+} SysTimeval;
+
+typedef struct {
+ clock_t tms_utime;
+ clock_t tms_stime;
+ clock_t tms_cutime;
+ clock_t tms_cstime;
+} SysTimes;
+
+#define HAVE_INT64 1
+#if defined (__GNUC__)
+typedef unsigned long long Uint64;
+typedef long long Sint64;
+
+typedef long long SysHrTime;
+#else
+typedef ULONGLONG Uint64;
+typedef LONGLONG Sint64;
+
+typedef LONGLONG SysHrTime;
+#endif
+
+extern int sys_init_time(void);
+extern void sys_gettimeofday(SysTimeval *tv);
+extern SysHrTime sys_gethrtime(void);
+extern clock_t sys_times(SysTimes *buffer);
+
+extern char *win_build_environment(char *);
+
+typedef struct {
+ char *environment_strings;
+ char *next_string;
+} GETENV_STATE;
+
+void erts_sys_env_init(void);
+
+/*
+ ** These are to avoid irritating warnings
+ */
+#pragma warning(disable : 4244)
+#pragma warning(disable : 4018)
+
+/*
+ * Floating point support.
+ */
+
+extern volatile int erl_fp_exception;
+
+#include <float.h>
+#if defined (__GNUC__)
+int _finite(double x);
+#endif
+#endif
+
+/*#define NO_FPE_SIGNALS*/
+#define erts_get_current_fp_exception() NULL
+#define __ERTS_FP_CHECK_INIT(fpexnp) do {} while (0)
+#define __ERTS_FP_ERROR(fpexnp, f, Action) if (!_finite(f)) { Action; } else {}
+#define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) __ERTS_FP_ERROR(fpexnp, f, Action)
+#define __ERTS_SAVE_FP_EXCEPTION(fpexnp)
+#define __ERTS_RESTORE_FP_EXCEPTION(fpexnp)
+
+#define ERTS_FP_CHECK_INIT(p) __ERTS_FP_CHECK_INIT(&(p)->fp_exception)
+#define ERTS_FP_ERROR(p, f, A) __ERTS_FP_ERROR(&(p)->fp_exception, f, A)
+#define ERTS_SAVE_FP_EXCEPTION(p) __ERTS_SAVE_FP_EXCEPTION(&(p)->fp_exception)
+#define ERTS_RESTORE_FP_EXCEPTION(p) __ERTS_RESTORE_FP_EXCEPTION(&(p)->fp_exception)
+#define ERTS_FP_ERROR_THOROUGH(p, f, A) __ERTS_FP_ERROR_THOROUGH(&(p)->fp_exception, f, A)
+
+#define erts_sys_block_fpe() 0
+#define erts_sys_unblock_fpe(x) do{}while(0)
+
+#define SIZEOF_SHORT 2
+#define SIZEOF_INT 4
+#define SIZEOF_LONG 4
+#define SIZEOF_VOID_P 4
+#define SIZEOF_SIZE_T 4
+#define SIZEOF_OFF_T 4
+
+/*
+ * Seems to be missing.
+ */
+#ifndef __GNUC__
+typedef long ssize_t;
+#endif
+
+/* Threads */
+#ifdef USE_THREADS
+int init_async(int);
+int exit_async(void);
+#endif
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
new file mode 100644
index 0000000000..3194493ac8
--- /dev/null
+++ b/erts/emulator/sys/win32/sys.c
@@ -0,0 +1,3093 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * system-dependent functions
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_alloc.h"
+#include "erl_sys_driver.h"
+#include "global.h"
+#include "erl_threads.h"
+#include "../../drivers/win32/win_con.h"
+
+
+void erts_sys_init_float(void);
+
+void erl_start(int, char**);
+void erl_exit(int n, char*, _DOTS_);
+void erl_error(char*, va_list);
+void erl_crash_dump(char*, int, char*, ...);
+
+/*
+ * Microsoft-specific function to map a WIN32 error code to a Posix errno.
+ */
+extern void _dosmaperr(DWORD);
+
+#ifdef ERL_RUN_SHARED_LIB
+#ifdef __argc
+#undef __argc
+#endif
+#define __argc e_argc
+#ifdef __argv
+#undef __argv
+#endif
+#define __argv e_argv
+#endif
+
+static void init_console();
+static int get_and_remove_option(int* argc, char** argv, const char* option);
+static char *get_and_remove_option2(int *argc, char **argv,
+ const char *option);
+static int init_async_io(struct async_io* aio, int use_threads);
+static void release_async_io(struct async_io* aio, ErlDrvPort);
+static void async_read_file(struct async_io* aio, LPVOID buf, DWORD numToRead);
+static int async_write_file(struct async_io* aio, LPVOID buf, DWORD numToWrite);
+static int get_overlapped_result(struct async_io* aio,
+ LPDWORD pBytesRead, BOOL wait);
+static FUNCTION(BOOL, CreateChildProcess, (char *, HANDLE, HANDLE,
+ HANDLE, LPHANDLE, BOOL,
+ LPVOID, LPTSTR, unsigned,
+ char **, int *));
+static int create_pipe(LPHANDLE, LPHANDLE, BOOL, BOOL);
+static int ApplicationType(const char* originalName, char fullPath[MAX_PATH],
+ BOOL search_in_path, BOOL handle_quotes,
+ int *error_return);
+
+HANDLE erts_service_event;
+
+#ifdef ERTS_SMP
+static erts_smp_tsd_key_t win32_errstr_key;
+#endif
+
+static erts_smp_atomic_t pipe_creation_counter;
+
+static erts_smp_mtx_t sys_driver_data_lock;
+
+
+/* Results from ApplicationType is one of */
+#define APPL_NONE 0
+#define APPL_DOS 1
+#define APPL_WIN3X 2
+#define APPL_WIN32 3
+
+static FUNCTION(int, driver_write, (long, HANDLE, byte*, int));
+static void common_stop(int);
+static int create_file_thread(struct async_io* aio, int mode);
+static DWORD WINAPI threaded_reader(LPVOID param);
+static DWORD WINAPI threaded_writer(LPVOID param);
+static DWORD WINAPI threaded_exiter(LPVOID param);
+
+#ifdef DEBUG
+static void debug_console(void);
+#endif
+
+BOOL WINAPI ctrl_handler(DWORD dwCtrlType);
+
+#define PORT_BUFSIZ 4096
+
+#define PORT_FREE (-1)
+#define PORT_EXITING (-2)
+
+#define DRV_BUF_ALLOC(SZ) \
+ erts_alloc_fnf(ERTS_ALC_T_DRV_DATA_BUF, (SZ))
+#define DRV_BUF_REALLOC(P, SZ) \
+ erts_realloc_fnf(ERTS_ALC_T_DRV_DATA_BUF, (P), (SZ))
+#define DRV_BUF_FREE(P) \
+ erts_free(ERTS_ALC_T_DRV_DATA_BUF, (P))
+
+/********************* General functions ****************************/
+
+/*
+ * Whether create_pipe() should use a named pipe or an anonymous.
+ * (Named pipes are not supported on Windows 95.)
+ */
+
+static int max_files = 1024;
+
+static BOOL use_named_pipes;
+static BOOL win_console = FALSE;
+
+
+static OSVERSIONINFO int_os_version; /* Version information for Win32. */
+
+
+/* This is the system's main function (which may or may not be called "main")
+ - do general system-dependent initialization
+ - call erl_start() to parse arguments and do other init
+*/
+
+static erts_smp_atomic_t sys_misc_mem_sz;
+
+HMODULE beam_module = NULL;
+
+void erl_sys_init();
+
+void erl_sys_args(int* argc, char** argv);
+
+int nohup;
+#ifndef __GNUC__
+void erts_sys_invalid_parameter_handler(const wchar_t * expression,
+ const wchar_t * function,
+ const wchar_t * file,
+ unsigned int line,
+ uintptr_t pReserved
+ )
+{
+#ifdef DEBUG
+ fprintf(stderr,
+ "Debug: Invalid parameter\"%ls\" "
+ "(detected in \"%ls\" [%ls:%d]) \n",
+ (expression) ? expression : L"(unknown)",
+ (function) ? function : L"(unknown)",
+ (file) ? file : L"(unknown)",
+ line);
+#endif
+ return;
+}
+#endif
+
+void sys_primitive_init(HMODULE beam)
+{
+#ifndef __GNUC__
+ /* Initialize this module handle (the beam.dll module handle) and
+ take care of the standard library's aggressive invalid parameter
+ handling... */
+ _set_invalid_parameter_handler(&erts_sys_invalid_parameter_handler);
+#endif
+ beam_module = (HMODULE) beam;
+}
+
+Uint
+erts_sys_misc_mem_sz(void)
+{
+ Uint res = (Uint) erts_check_io_size();
+ res += (Uint) erts_smp_atomic_read(&sys_misc_mem_sz);
+ return res;
+}
+
+void erl_sys_args(int* argc, char** argv)
+{
+ char *event_name;
+ nohup = get_and_remove_option(argc, argv, "-nohup");
+
+#ifdef DEBUG
+ /*
+ * Start a debug console if -console option given.
+ */
+
+ if (get_and_remove_option(argc, argv, "-console")) {
+ debug_console();
+ }
+#endif
+
+ if (nohup && (event_name = get_and_remove_option2(argc, argv,
+ "-service_event"))) {
+ if ((erts_service_event =
+ OpenEvent(EVENT_ALL_ACCESS,FALSE,event_name)) == NULL) {
+ erts_fprintf(stderr,
+ "Warning: could not open service event: %s\r\n",
+ event_name);
+ }
+ } else {
+ erts_service_event = NULL;
+ }
+
+#ifdef DEBUG
+ /*
+ * Given the "-threads" option, always use threads instead of
+ * named pipes.
+ */
+
+ if (get_and_remove_option(argc, argv, "-threads")) {
+ use_named_pipes = FALSE;
+ }
+#endif
+}
+
+void
+erts_sys_prepare_crash_dump(void)
+{
+ /* Windows - free file descriptors are hopefully available */
+ return;
+}
+
+static void
+init_console()
+{
+ char* mode = erts_read_env("ERL_CONSOLE_MODE");
+
+ if (!mode || strcmp(mode, "window") == 0) {
+ win_console = TRUE;
+ ConInit();
+ /*nohup = 0;*/
+ } else if (strncmp(mode, "tty:", 4) == 0) {
+ if (mode[5] == 'c') {
+ setvbuf(stdout, NULL, _IONBF, 0);
+ }
+ if (mode[6] == 'c') {
+ setvbuf(stderr, NULL, _IONBF, 0);
+ }
+ }
+
+ erts_free_read_env(mode);
+}
+
+int sys_max_files()
+{
+ return max_files;
+}
+
+/*
+ * Looks for the given option in the argv vector. If it is found,
+ * it will be removed from the argv vector.
+ *
+ * If the return value indicates that the option was found and removed,
+ * it is the responsibility of the caller to decrement the value of argc.
+ *
+ * Returns: 0 if the option wasn't found, 1 if it was found
+ */
+
+static int
+get_and_remove_option(argc, argv, option)
+ int* argc; /* Number of arguments. */
+ char* argv[]; /* The argument vector. */
+ const char* option; /* Option to search for and remove. */
+{
+ int i;
+
+ for (i = 1; i < *argc; i++) {
+ if (strcmp(argv[i], option) == 0) {
+ (*argc)--;
+ while (i < *argc) {
+ argv[i] = argv[i+1];
+ i++;
+ }
+ argv[i] = NULL;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static char *get_and_remove_option2(int *argc, char **argv,
+ const char *option)
+{
+ char *ret;
+ int i;
+
+ for (i = 1; i < *argc; i++) {
+ if (strcmp(argv[i], option) == 0) {
+ if (i+1 < *argc) {
+ ret = argv[i+1];
+ (*argc) -= 2;
+ while (i < *argc) {
+ argv[i] = argv[i+2];
+ i++;
+ }
+ argv[i] = NULL;
+ return ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+/************************** OS info *******************************/
+
+/* Used by erlang:info/1. */
+/* (This code was formerly in drv.XXX/XXX_os_drv.c) */
+
+char os_type[] = "win32";
+
+void
+os_flavor(namebuf, size)
+char* namebuf; /* Where to return the name. */
+unsigned size; /* Size of name buffer. */
+{
+ switch (int_os_version.dwPlatformId) {
+ case VER_PLATFORM_WIN32_WINDOWS:
+ strcpy(namebuf, "windows");
+ break;
+ case VER_PLATFORM_WIN32_NT:
+ strcpy(namebuf, "nt");
+ break;
+ default: /* Can't happen. */
+ strcpy(namebuf, "unknown");
+ break;
+ }
+}
+
+void
+os_version(pMajor, pMinor, pBuild)
+int* pMajor; /* Pointer to major version. */
+int* pMinor; /* Pointer to minor version. */
+int* pBuild; /* Pointer to build number. */
+{
+ *pMajor = int_os_version.dwMajorVersion;
+ *pMinor = int_os_version.dwMinorVersion;
+ *pBuild = int_os_version.dwBuildNumber;
+}
+
+/************************** Port I/O *******************************/
+
+/* I. Common stuff */
+
+/* II. The spawn/fd/vanilla drivers */
+
+/*
+ * Definitions for driver flags.
+ */
+
+#define DF_OVR_READY 1 /* Overlapped result is ready. */
+#define DF_EXIT_THREAD 2 /* The thread should exit. */
+#define DF_XLAT_CR 4 /* The thread should translate CRs. */
+#define DF_DROP_IF_INVH 8 /* Drop packages instead of crash if
+ invalid handle (stderr) */
+
+#define OV_BUFFER_PTR(dp) ((LPVOID) ((dp)->ov.Internal))
+#define OV_NUM_TO_READ(dp) ((dp)->ov.InternalHigh)
+
+/*
+ * This data is used to make overlapped I/O operations work on both
+ * Windows NT (using true overlapped I/O) and Windows 95 (using threads).
+ */
+
+typedef struct async_io {
+ unsigned flags; /* Driver flags, definitions found above. */
+ HANDLE thread; /* If -1, overlapped I/O is used (Windows NT).
+ * Otherwise, it is the handle of the thread used
+ * for simulating overlapped I/O (Windows 95 and
+ * the console for Windows NT).
+ */
+ HANDLE fd; /* Handle for file or pipe. */
+#ifdef ERTS_SMP
+ int async_io_active; /* if true, a close of the file will signal the event in ov */
+#endif
+ OVERLAPPED ov; /* Control structure for overlapped reading.
+ * When overlapped reading is simulated with
+ * a thread, the fields are used as follows:
+ * ov.Internal - Read buffer.
+ * ov.InternalHigh - Number of bytes to read.
+ * See macros above.
+ */
+ HANDLE ioAllowed; /* The thread will wait for this event
+ * before starting a new read or write.
+ */
+ DWORD pendingError; /* Used to delay presentating an error to Erlang
+ * until the check_io function is entered.
+ */
+ DWORD bytesTransferred; /* Bytes read or write in the last operation.
+ * Valid only when DF_OVR_READY is set.
+ */
+} AsyncIo;
+
+
+/*
+ * Input thread for fd_driver (if fd_driver is running).
+ */
+static AsyncIo* fd_driver_input = NULL;
+static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD);
+
+/*
+ * This data is used by the spawn and vanilla drivers.
+ * There will be one entry for each port, even if the input
+ * and output HANDLES are different. Since handles are not
+ * guaranteed to be small numbers in Win32, we cannot index
+ * with them. I.e. the index for each entry is not equal to
+ * none of the file handles.
+ */
+
+typedef struct driver_data {
+ int totalNeeded; /* Total number of bytes needed to fill
+ * up the packet header or packet. */
+ int bytesInBuffer; /* Number of bytes read so far in
+ * the input buffer.
+ */
+ int inBufSize; /* Size of input buffer. */
+ byte *inbuf; /* Buffer to use for overlapped read. */
+ int outBufSize; /* Size of output buffer. */
+ byte *outbuf; /* Buffer to use for overlapped write. */
+ ErlDrvPort port_num; /* The port number. */
+ int packet_bytes; /* 0: continous stream, 1, 2, or 4: the number
+ * of bytes in the packet header.
+ */
+ HANDLE port_pid; /* PID of the port process. */
+ AsyncIo in; /* Control block for overlapped reading. */
+ AsyncIo out; /* Control block for overlapped writing. */
+ int report_exit; /* Do report exit status for the port */
+} DriverData;
+
+static DriverData* driver_data; /* Pointer to array of driver data. */
+
+/* Driver interfaces */
+static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*);
+static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*);
+static ErlDrvData vanilla_start(ErlDrvPort, char*, SysDriverOpts*);
+static int spawn_init(void);
+static int fd_init(void);
+static void fd_stop(ErlDrvData);
+static void stop(ErlDrvData);
+static void output(ErlDrvData, char*, int);
+static void ready_input(ErlDrvData, ErlDrvEvent);
+static void ready_output(ErlDrvData, ErlDrvEvent);
+static void stop_select(ErlDrvEvent, void*);
+
+struct erl_drv_entry spawn_driver_entry = {
+ spawn_init,
+ spawn_start,
+ stop,
+ output,
+ ready_input,
+ ready_output,
+ "spawn",
+ NULL, /* finish */
+ NULL, /* handle */
+ NULL, /* control */
+ NULL, /* timeout */
+ NULL, /* outputv */
+ NULL, /* ready_async */
+ NULL, /* flush */
+ NULL, /* call */
+ NULL, /* event */
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0, /* ERL_DRV_FLAGs */
+ NULL,
+ NULL, /* process_exit */
+ stop_select
+};
+
+#ifdef HARD_POLL_DEBUG
+extern void poll_debug_set_active_fd(ErtsSysFdType fd);
+extern void poll_debug_read_begin(ErtsSysFdType fd);
+extern void poll_debug_read_done(ErtsSysFdType fd, int bytes);
+extern void poll_debug_async_initialized(ErtsSysFdType fd);
+extern void poll_debug_async_immediate(ErtsSysFdType fd, int bytes);
+extern void poll_debug_write_begin(ErtsSysFdType fd);
+extern void poll_debug_write_done(ErtsSysFdType fd, int bytes);
+#endif
+
+extern int null_func(void);
+
+struct erl_drv_entry fd_driver_entry = {
+ fd_init,
+ fd_start,
+ fd_stop,
+ output,
+ ready_input,
+ ready_output,
+ "fd",
+ NULL, /* finish */
+ NULL, /* handle */
+ NULL, /* control */
+ NULL, /* timeout */
+ NULL, /* outputv */
+ NULL, /* ready_async */
+ NULL, /* flush */
+ NULL, /* call */
+ NULL, /* event */
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0, /* ERL_DRV_FLAGs */
+ NULL,
+ NULL, /* process_exit */
+ stop_select
+};
+
+struct erl_drv_entry vanilla_driver_entry = {
+ null_func,
+ vanilla_start,
+ stop,
+ output,
+ ready_input,
+ ready_output,
+ "vanilla",
+ NULL, /* finish */
+ NULL, /* handle */
+ NULL, /* control */
+ NULL, /* timeout */
+ NULL, /* outputv */
+ NULL, /* ready_async */
+ NULL, /* flush */
+ NULL, /* call */
+ NULL, /* event */
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0, /* ERL_DRV_FLAGs */
+ NULL,
+ NULL, /* process_exit */
+ stop_select
+};
+
+#if defined(USE_THREADS) && !defined(ERTS_SMP)
+
+static int async_drv_init(void);
+static ErlDrvData async_drv_start(ErlDrvPort, char*, SysDriverOpts*);
+static void async_drv_stop(ErlDrvData);
+static void async_drv_input(ErlDrvData, ErlDrvEvent);
+
+/* INTERNAL use only */
+
+void null_output(ErlDrvData drv_data, char* buf, int len)
+{
+}
+
+void null_ready_output(ErlDrvData drv_data, ErlDrvEvent event)
+{
+}
+
+struct erl_drv_entry async_driver_entry = {
+ async_drv_init,
+ async_drv_start,
+ async_drv_stop,
+ null_output,
+ async_drv_input,
+ null_ready_output,
+ "async",
+ NULL, /* finish */
+ NULL, /* handle */
+ NULL, /* control */
+ NULL, /* timeout */
+ NULL, /* outputv */
+ NULL, /* ready_async */
+ NULL, /* flush */
+ NULL, /* call */
+ NULL, /* event */
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0, /* ERL_DRV_FLAGs */
+ NULL,
+ NULL, /* process_exit */
+ stop_select
+};
+
+#endif
+
+/*
+ * Initialises a DriverData structure.
+ *
+ * Results: Returns a pointer to a DriverData structure, or NULL
+ * if the initialsation failed.
+ */
+
+static DriverData*
+new_driver_data(port_num, packet_bytes, wait_objs_required, use_threads)
+ int port_num; /* The port number. */
+ int packet_bytes; /* Number of bytes in header. */
+ int wait_objs_required; /* The number objects this port is going
+ /* wait for (typically 1 or 2). */
+ int use_threads; /* TRUE if threads are intended to be used. */
+{
+ DriverData* dp;
+
+ erts_smp_mtx_lock(&sys_driver_data_lock);
+
+ DEBUGF(("new_driver_data(port_num %d, pb %d)\n",
+ port_num, packet_bytes));
+
+ /*
+ * We used to test first at all that there is enough room in the
+ * array used by WaitForMultipleObjects(), but that is not necessary
+ * any more, since driver_select() can't fail.
+ */
+
+ /*
+ * Search for a free slot.
+ */
+
+ for (dp = driver_data; dp < driver_data+max_files; dp++) {
+ if (dp->port_num == PORT_FREE) {
+ dp->bytesInBuffer = 0;
+ dp->totalNeeded = packet_bytes;
+ dp->inBufSize = PORT_BUFSIZ;
+ dp->inbuf = DRV_BUF_ALLOC(dp->inBufSize);
+ if (dp->inbuf == NULL) {
+ erts_smp_mtx_unlock(&sys_driver_data_lock);
+ return NULL;
+ }
+ erts_smp_atomic_add(&sys_misc_mem_sz, dp->inBufSize);
+ dp->outBufSize = 0;
+ dp->outbuf = NULL;
+ dp->port_num = port_num;
+ dp->packet_bytes = packet_bytes;
+ dp->port_pid = INVALID_HANDLE_VALUE;
+ if (init_async_io(&dp->in, use_threads) == -1)
+ break;
+ if (init_async_io(&dp->out, use_threads) == -1)
+ break;
+ erts_smp_mtx_unlock(&sys_driver_data_lock);
+ return dp;
+ }
+ }
+
+ /*
+ * Error or no free driver data.
+ */
+
+ if (dp < driver_data+max_files) {
+ release_async_io(&dp->in, dp->port_num);
+ release_async_io(&dp->out, dp->port_num);
+ }
+ erts_smp_mtx_unlock(&sys_driver_data_lock);
+ return NULL;
+}
+
+static void
+release_driver_data(DriverData* dp)
+{
+ erts_smp_mtx_lock(&sys_driver_data_lock);
+
+#ifdef ERTS_SMP
+ /* This is a workaround for the fact that CancelIo cant cancel
+ requests issued by another thread and that we still cant use
+ CancelIoEx as that's only availabele in Vista etc. */
+ if(dp->in.async_io_active && dp->in.fd != INVALID_HANDLE_VALUE) {
+ CloseHandle(dp->in.fd);
+ dp->in.fd = INVALID_HANDLE_VALUE;
+ DEBUGF(("Waiting for the in event thingie"));
+ WaitForSingleObject(dp->in.ov.hEvent,INFINITE);
+ DEBUGF(("...done\n"));
+ }
+ if(dp->out.async_io_active && dp->out.fd != INVALID_HANDLE_VALUE) {
+ CloseHandle(dp->out.fd);
+ dp->out.fd = INVALID_HANDLE_VALUE;
+ DEBUGF(("Waiting for the out event thingie"));
+ WaitForSingleObject(dp->out.ov.hEvent,INFINITE);
+ DEBUGF(("...done\n"));
+ }
+#else
+ if (dp->out.thread == (HANDLE) -1 && dp->in.fd != INVALID_HANDLE_VALUE) {
+ CancelIo(dp->in.fd);
+ }
+ if (dp->out.thread == (HANDLE) -1 && dp->out.fd != INVALID_HANDLE_VALUE) {
+ CancelIo(dp->out.fd);
+ }
+#endif
+
+ if (dp->inbuf != NULL) {
+ ASSERT(erts_smp_atomic_read(&sys_misc_mem_sz) >= dp->inBufSize);
+ erts_smp_atomic_add(&sys_misc_mem_sz, -1*dp->inBufSize);
+ DRV_BUF_FREE(dp->inbuf);
+ dp->inBufSize = 0;
+ dp->inbuf = NULL;
+ }
+ ASSERT(dp->inBufSize == 0);
+
+ if (dp->outbuf != NULL) {
+ ASSERT(erts_smp_atomic_read(&sys_misc_mem_sz) >= dp->outBufSize);
+ erts_smp_atomic_add(&sys_misc_mem_sz, -1*dp->outBufSize);
+ DRV_BUF_FREE(dp->outbuf);
+ dp->outBufSize = 0;
+ dp->outbuf = NULL;
+ }
+ ASSERT(dp->outBufSize == 0);
+
+ if (dp->port_pid != INVALID_HANDLE_VALUE) {
+ CloseHandle(dp->port_pid);
+ dp->port_pid = INVALID_HANDLE_VALUE;
+ }
+
+ release_async_io(&dp->in, dp->port_num);
+ release_async_io(&dp->out, dp->port_num);
+
+ /*
+ * This must be last, because this function might be executed from
+ * the exit thread.
+ */
+
+ dp->port_num = PORT_FREE;
+ erts_smp_mtx_unlock(&sys_driver_data_lock);
+}
+
+/*
+ * Stores input and output file descriptors in the DriverData structure,
+ * and calls driver_select().
+ *
+ * This function fortunately can't fail!
+ */
+
+static ErlDrvData
+set_driver_data(dp, ifd, ofd, read_write, report_exit)
+ DriverData* dp;
+ HANDLE ifd;
+ HANDLE ofd;
+ int read_write;
+ int report_exit;
+{
+ int index = dp - driver_data;
+ int result;
+
+ dp->in.fd = ifd;
+ dp->out.fd = ofd;
+ dp->report_exit = report_exit;
+
+ if (read_write & DO_READ) {
+ result = driver_select(dp->port_num, (ErlDrvEvent)dp->in.ov.hEvent,
+ ERL_DRV_READ|ERL_DRV_USE, 1);
+ ASSERT(result != -1);
+ async_read_file(&dp->in, dp->inbuf, dp->inBufSize);
+ }
+
+ if (read_write & DO_WRITE) {
+ result = driver_select(dp->port_num, (ErlDrvEvent)dp->out.ov.hEvent,
+ ERL_DRV_WRITE|ERL_DRV_USE, 1);
+ ASSERT(result != -1);
+ }
+ return (ErlDrvData)index;
+}
+
+/*
+ * Initialises an AsyncIo structure.
+ */
+
+static int
+init_async_io(AsyncIo* aio, int use_threads)
+{
+ aio->flags = 0;
+ aio->thread = (HANDLE) -1;
+ aio->fd = INVALID_HANDLE_VALUE;
+ aio->ov.hEvent = NULL;
+ aio->ov.Offset = 0L;
+ aio->ov.OffsetHigh = 0L;
+ aio->ioAllowed = NULL;
+ aio->pendingError = 0;
+ aio->bytesTransferred = 0;
+#ifdef ERTS_SMP
+ aio->async_io_active = 0;
+#endif
+ aio->ov.hEvent = CreateManualEvent(FALSE);
+ if (aio->ov.hEvent == NULL)
+ return -1;
+ if (use_threads) {
+ aio->ioAllowed = CreateAutoEvent(FALSE);
+ if (aio->ioAllowed == NULL)
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Releases everything allocated in an AsyncIo structure.
+ */
+
+static void
+release_async_io(AsyncIo* aio, ErlDrvPort port_num)
+{
+ aio->flags = 0;
+
+ if (aio->thread != (HANDLE) -1)
+ CloseHandle(aio->thread);
+ aio->thread = (HANDLE) -1;
+
+ if (aio->fd != INVALID_HANDLE_VALUE)
+ CloseHandle(aio->fd);
+ aio->fd = INVALID_HANDLE_VALUE;
+
+ if (aio->ov.hEvent != NULL) {
+ (void) driver_select(port_num,
+ (ErlDrvEvent)aio->ov.hEvent,
+ ERL_DRV_USE, 0);
+ /* was CloseHandle(aio->ov.hEvent); */
+ }
+
+ aio->ov.hEvent = NULL;
+
+ if (aio->ioAllowed != NULL)
+ CloseHandle(aio->ioAllowed);
+ aio->ioAllowed = NULL;
+}
+
+/* ----------------------------------------------------------------------
+ * async_read_file --
+ * Initiaties an asynchronous file read, or simulates that using
+ * the thread associated with this driver data. To get the results,
+ * call get_overlapped_result().
+ *
+ * Results:
+ * None.
+ * ----------------------------------------------------------------------
+ */
+
+static void
+async_read_file(aio, buf, numToRead)
+ AsyncIo* aio; /* Pointer to driver data. */
+ LPVOID buf; /* Pointer to buffer to receive data. */
+ DWORD numToRead; /* Number of bytes to read. */
+{
+ aio->pendingError = NO_ERROR;
+#ifdef HARD_POLL_DEBUG
+ poll_debug_async_initialized(aio->ov.hEvent);
+#endif
+ if (aio->thread != (HANDLE) -1) {
+ DEBUGF(("async_read_file: signaling thread 0x%x, event 0x%x\n",
+ aio->thread, aio->ioAllowed));
+ OV_BUFFER_PTR(aio) = buf;
+ OV_NUM_TO_READ(aio) = numToRead;
+ ResetEvent(aio->ov.hEvent);
+ SetEvent(aio->ioAllowed);
+ } else {
+#ifdef ERTS_SMP
+ aio->async_io_active = 1; /* Will get 0 when the event actually happened */
+#endif
+ if (ReadFile(aio->fd, buf, numToRead,
+ &aio->bytesTransferred, &aio->ov)) {
+ DEBUGF(("async_read_file: ReadFile() suceeded: %d bytes\n",
+ aio->bytesTransferred));
+#ifdef HARD_POLL_DEBUG
+ poll_debug_async_immediate(aio->ov.hEvent, aio->bytesTransferred);
+#endif
+ aio->flags |= DF_OVR_READY;
+ SetEvent(aio->ov.hEvent);
+ } else {
+ DWORD error = GetLastError();
+ if (error != ERROR_IO_PENDING) {
+#ifdef HARD_POLL_DEBUG
+ poll_debug_async_immediate(aio->ov.hEvent, 0);
+#endif
+ aio->pendingError = error;
+ SetEvent(aio->ov.hEvent);
+ }
+ DEBUGF(("async_read_file: ReadFile() -> %s\n", win32_errorstr(error)));
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------
+ * async_write_file --
+ * Initiaties an asynchronous file write, or simulates that using
+ * the output thread associated with this driver data.
+ * To get the results, call get_overlapped_result().
+ *
+ * Results:
+ * None.
+ * ----------------------------------------------------------------------
+ */
+static int
+async_write_file(aio, buf, numToWrite)
+ AsyncIo* aio; /* Pointer to async control block. */
+ LPVOID buf; /* Pointer to buffer with data to write. */
+ DWORD numToWrite; /* Number of bytes to write. */
+{
+ aio->pendingError = NO_ERROR;
+ if (aio->thread != (HANDLE) -1) {
+ DEBUGF(("async_write_file: signaling thread 0x%x, event 0x%x\n",
+ aio->thread, aio->ioAllowed));
+ OV_BUFFER_PTR(aio) = buf;
+ OV_NUM_TO_READ(aio) = numToWrite;
+ ResetEvent(aio->ov.hEvent);
+ SetEvent(aio->ioAllowed);
+ } else {
+#ifdef ERTS_SMP
+ aio->async_io_active = 1; /* Will get 0 when the event actually happened */
+#endif
+ if (WriteFile(aio->fd, buf, numToWrite,
+ &aio->bytesTransferred, &aio->ov)) {
+ DEBUGF(("async_write_file: WriteFile() suceeded: %d bytes\n",
+ aio->bytesTransferred));
+#ifdef ERTS_SMP
+ aio->async_io_active = 0; /* The event will not be signalled */
+#endif
+ ResetEvent(aio->ov.hEvent);
+ return TRUE;
+ } else {
+ DWORD error = GetLastError();
+ if (error != ERROR_IO_PENDING) {
+ aio->pendingError = error;
+ SetEvent(aio->ov.hEvent);
+ }
+ DEBUGF(("async_write_file: WriteFile() -> %s\n", win32_errorstr(error)));
+ }
+ }
+ return FALSE;
+}
+
+/* ----------------------------------------------------------------------
+ * get_overlapped_result --
+ *
+ * Results:
+ * Returns the error code for the overlapped result, or NO_ERROR
+ * if no error.
+ * ----------------------------------------------------------------------
+ */
+static int
+get_overlapped_result(aio, pBytesRead, wait)
+ AsyncIo* aio; /* Pointer to async control block. */
+ LPDWORD pBytesRead; /* Where to place the number of bytes
+ * transferred.
+ */
+ BOOL wait; /* If true, wait until result is ready. */
+{
+ DWORD error = NO_ERROR; /* Error status from last function. */
+
+ if (aio->thread != (HANDLE) -1) {
+
+ /*
+ * Simulate overlapped io with a thread.
+ */
+ DEBUGF(("get_overlapped_result: about to wait for event 0x%x\n",
+ aio->ov.hEvent));
+ error = WaitForSingleObject(aio->ov.hEvent, wait ? INFINITE : 0);
+ switch (error) {
+ case WAIT_OBJECT_0:
+ error = aio->pendingError;
+ aio->pendingError = NO_ERROR;
+ *pBytesRead = aio->bytesTransferred;
+ ResetEvent(aio->ov.hEvent);
+ DEBUGF(("get_overlapped_result -> %s\n",
+ win32_errorstr(error)));
+ return error;
+ case WAIT_TIMEOUT:
+ DEBUGF(("get_overlapped_result -> %s\n",
+ ERROR_IO_INCOMPLETE));
+ return ERROR_IO_INCOMPLETE;
+ case WAIT_FAILED: /* XXX: Shouldn't happen? */
+ error = GetLastError();
+ DEBUGF(("get_overlapped_result (WAIT_FAILED) -> %s\n",
+ win32_errorstr(error)));
+ return error;
+ }
+ } else if (aio->pendingError != NO_ERROR) { /* Pending error. */
+ error = aio->pendingError;
+ aio->pendingError = NO_ERROR;
+ ResetEvent(aio->ov.hEvent);
+ DEBUGF(("get_overlapped_result: pending error: %s\n",
+ win32_errorstr(error)));
+ return error;
+ } else if (aio->flags & DF_OVR_READY) { /* Operation succeded. */
+ aio->flags &= ~DF_OVR_READY;
+ *pBytesRead = aio->bytesTransferred;
+ ResetEvent(aio->ov.hEvent);
+ DEBUGF(("get_overlapped_result: delayed success: %d bytes\n",
+ aio->bytesTransferred));
+ } else if (!GetOverlappedResult(aio->fd, &aio->ov, pBytesRead, wait)) {
+ error = GetLastError();
+ ResetEvent(aio->ov.hEvent);
+ DEBUGF(("get_overlapped_result: error: %s\n", win32_errorstr(error)));
+ return error;
+ } else { /* Success. */
+ DEBUGF(("get_overlapped_result: success\n"));
+ ResetEvent(aio->ov.hEvent);
+ }
+ return NO_ERROR;
+}
+
+static int
+fd_init(void)
+{
+ char kernel_dll_name[] = "kernel32";
+ HMODULE module;
+ module = GetModuleHandle(kernel_dll_name);
+ fpSetHandleInformation = (module != NULL) ?
+ (BOOL (WINAPI *)(HANDLE,DWORD,DWORD))
+ GetProcAddress(module,"SetHandleInformation") :
+ NULL;
+
+ return 0;
+}
+static int
+spawn_init()
+{
+ int i;
+
+ driver_data = (struct driver_data *)
+ erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data));
+ erts_smp_atomic_add(&sys_misc_mem_sz, max_files*sizeof(struct driver_data));
+ for (i = 0; i < max_files; i++)
+ driver_data[i].port_num = PORT_FREE;
+ return 0;
+}
+
+static ErlDrvData
+spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
+{
+ HANDLE hToChild = INVALID_HANDLE_VALUE; /* Write handle to child. */
+ HANDLE hFromChild = INVALID_HANDLE_VALUE; /* Read handle from child. */
+ HANDLE hChildStdin = INVALID_HANDLE_VALUE; /* Child's stdin. */
+ HANDLE hChildStdout = INVALID_HANDLE_VALUE; /* Child's stout. */
+ HANDLE hChildStderr = INVALID_HANDLE_VALUE; /* Child's sterr. */
+ int close_child_stderr = 0;
+ DriverData* dp; /* Pointer to driver data. */
+ ErlDrvData retval = ERL_DRV_ERROR_GENERAL; /* Return value. */
+ int ok;
+ int neededSelects = 0;
+ SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
+ char* envir = opts->envir;
+ int errno_return = -1;
+
+ if (opts->read_write & DO_READ)
+ neededSelects++;
+ if (opts->read_write & DO_WRITE)
+ neededSelects++;
+
+ if ((dp = new_driver_data(port_num, opts->packet_bytes, neededSelects,
+ !use_named_pipes)) == NULL)
+ return ERL_DRV_ERROR_GENERAL;
+
+ /*
+ * Create two pipes to communicate with the port program.
+ */
+
+ if (opts->read_write & DO_READ) {
+ if (!create_pipe(&hFromChild, &hChildStdout, FALSE,
+ opts->overlapped_io))
+ goto error;
+ } else {
+ hChildStdout = CreateFile("nul", GENERIC_WRITE, 0,
+ &sa, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ DEBUGF(("Created nul file for hChildStdout = %d\n",hChildStdout));
+ }
+ if (opts->read_write & DO_WRITE) {
+ if (!create_pipe(&hChildStdin, &hToChild, TRUE, opts->overlapped_io)) {
+ CloseHandle(hFromChild);
+ hFromChild = INVALID_HANDLE_VALUE;
+ CloseHandle(hChildStdout);
+ goto error;
+ }
+ } else {
+ hChildStdin = CreateFile("nul", GENERIC_READ, 0,
+ &sa, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ DEBUGF(("Created nul file for hChildStdin = %d\n",hChildStdin));
+ }
+
+ /*
+ * Make sure that standard error is valid handle, because a Command Prompt
+ * window not work properly otherwise. We leave standard error alone if
+ * it is okay and no redirection was specified.
+ */
+ hChildStderr = GetStdHandle(STD_ERROR_HANDLE);
+ if (opts->redir_stderr) {
+ hChildStderr = hChildStdout;
+ } else if (hChildStderr == INVALID_HANDLE_VALUE || hChildStderr == 0) {
+ hChildStderr = CreateFile("nul", GENERIC_WRITE, 0, &sa, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ close_child_stderr = 1;
+ }
+ if (fpSetHandleInformation != NULL) {
+ (*fpSetHandleInformation)(hChildStderr, HANDLE_FLAG_INHERIT, 1);
+ }
+ /*
+ * Spawn the port program.
+ */
+
+ DEBUGF(("Spawning \"%s\"\n", name));
+ envir = win_build_environment(envir);
+ ok = CreateChildProcess(name,
+ hChildStdin,
+ hChildStdout,
+ hChildStderr,
+ &dp->port_pid,
+ opts->hide_window,
+ (LPVOID) envir,
+ (LPTSTR) opts->wd,
+ opts->spawn_type,
+ opts->argv,
+ &errno_return);
+ CloseHandle(hChildStdin);
+ CloseHandle(hChildStdout);
+ if (close_child_stderr && hChildStderr != INVALID_HANDLE_VALUE &&
+ hChildStderr != 0) {
+ CloseHandle(hChildStderr);
+ }
+ if (envir != NULL) {
+ erts_free(ERTS_ALC_T_ENVIRONMENT, envir);
+ }
+
+ if (!ok) {
+ dp->port_pid = INVALID_HANDLE_VALUE;
+ if (errno_return >= 0) {
+ retval = ERL_DRV_ERROR_ERRNO;
+ }
+ } else {
+ if (!use_named_pipes) {
+ if ((opts->read_write & DO_READ) &&
+ !create_file_thread(&dp->in, DO_READ))
+ goto error;
+ if ((opts->read_write & DO_WRITE) &&
+ !create_file_thread(&dp->out, DO_WRITE)) {
+ dp->in.flags = DF_EXIT_THREAD;
+ SetEvent(dp->in.ioAllowed);
+ WaitForSingleObject(dp->in.thread, INFINITE);
+ dp->in.thread = (HANDLE) -1;
+ goto error;
+ }
+ }
+#ifdef HARD_POLL_DEBUG
+ if (strncmp(name,"inet_gethost",12) == 0) {
+ erts_printf("Debugging \"%s\"\n", name);
+ poll_debug_set_active_fd(dp->in.ov.hEvent);
+ }
+#endif
+ retval = set_driver_data(dp, hFromChild, hToChild, opts->read_write,
+ opts->exit_status);
+ }
+
+ if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO)
+ return retval;
+
+ error:
+ if (hFromChild != INVALID_HANDLE_VALUE)
+ CloseHandle(hFromChild);
+ if (hToChild != INVALID_HANDLE_VALUE)
+ CloseHandle(hToChild);
+ release_driver_data(dp);
+ if (retval == ERL_DRV_ERROR_ERRNO) {
+ errno = errno_return;
+ }
+ return retval;
+}
+
+static int
+create_file_thread(AsyncIo* aio, int mode)
+{
+ DWORD tid; /* Id for thread. */
+
+ aio->thread = (HANDLE)
+ _beginthreadex(NULL, 0,
+ (mode & DO_WRITE) ? threaded_writer : threaded_reader,
+ aio, 0, &tid);
+
+ return aio->thread != (HANDLE) -1;
+}
+
+/*
+ * A helper function used by CreateChildProcess().
+ * Parses a command line with arguments and returns the length of the
+ * first part containing the program name.
+ * Example: input = "\"Program Files\"\\erl arg1 arg2"
+ * gives 19 as result.
+ * The length returned is equivalent with length(argv[0]) if the
+ * comman line should have been prepared by _setargv for the main function
+*/
+int parse_command(char* cmd){
+#define NORMAL 2
+#define STRING 1
+#define STOP 0
+ int i =0;
+ int state = NORMAL;
+ while (cmd[i]) {
+ switch (cmd[i]) {
+ case '"':
+ if (state == NORMAL)
+ state = STRING;
+ else
+ state = NORMAL;
+ break;
+ case '\\':
+ if ((state == STRING) && (cmd[i+1]=='"'))
+ i++;
+ break;
+ case ' ':
+ if (state == NORMAL)
+ state = STOP;
+ break;
+ default:
+ break;
+ }
+ if (state == STOP) {
+ return i;
+ }
+ i++;
+ }
+ return i;
+}
+
+BOOL need_quotes(char *str)
+{
+ int in_quote = 0;
+ int backslashed = 0;
+ int naked_space = 0;
+ while (*str != '\0') {
+ switch (*str) {
+ case '\\' :
+ backslashed = !backslashed;
+ break;
+ case '"':
+ if (backslashed) {
+ backslashed=0;
+ } else {
+ in_quote = !in_quote;
+ }
+ break;
+ case ' ':
+ backslashed = 0;
+ if (!(backslashed || in_quote)) {
+ naked_space++;
+ }
+ break;
+ default:
+ backslashed = 0;
+ }
+ ++str;
+ }
+ return (naked_space > 0);
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateChildProcess --
+ *
+ * Create a child process that has pipes as its
+ * standard input, output, and error. The child process runs
+ * synchronously under Win32s and asynchronously under Windows NT
+ * and Windows 95, and runs with the same environment variables
+ * as the creating process.
+ *
+ * The complete Windows search path is searched to find the specified
+ * executable. If an executable by the given name is not found,
+ * automatically tries appending ".com", ".exe", and ".bat" to the
+ * executable name.
+ *
+ * Results:
+ * The return value is FALSE if there was a problem creating the child process.
+ * Otherwise, the return value is 0 and *phPid is
+ * filled with the process id of the child process.
+ *
+ * Side effects:
+ * A process is created.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static BOOL
+CreateChildProcess
+(
+ char *origcmd, /* Command line for child process (including
+ * name of executable). Or whole executable if st is
+ * ERTS_SPAWN_EXECUTABLE
+ */
+ HANDLE hStdin, /* The standard input handle for child. */
+ HANDLE hStdout, /* The standard output handle for child. */
+ HANDLE hStderr, /* The standard error handle for child. */
+ LPHANDLE phPid, /* Pointer to variable to received PID. */
+ BOOL hide, /* Hide the window unconditionally. */
+ LPVOID env, /* Environment for the child */
+ LPTSTR wd, /* Working dir for the child */
+ unsigned st, /* Flags for spawn, tells us how to interpret origcmd */
+ char **argv, /* Argument vector if given. */
+ int *errno_return /* Place to put an errno in in case of failure */
+ )
+{
+ PROCESS_INFORMATION piProcInfo = {0};
+ STARTUPINFO siStartInfo = {0};
+ BOOL ok = FALSE;
+ int applType;
+ /* Not to be changed for different types of executables */
+ int staticCreateFlags = GetPriorityClass(GetCurrentProcess());
+ int createFlags = DETACHED_PROCESS;
+ char *newcmdline = NULL;
+ char execPath[MAX_PATH];
+ int cmdlength;
+ char* thecommand;
+ LPTSTR appname = NULL;
+ HANDLE hProcess = GetCurrentProcess();
+
+ *errno_return = -1;
+
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES;
+ siStartInfo.hStdInput = hStdin;
+ siStartInfo.hStdOutput = hStdout;
+ siStartInfo.hStdError = hStderr;
+
+
+ if (st != ERTS_SPAWN_EXECUTABLE) {
+ /*
+ * Parse out the program name from the command line (it can be quoted and
+ * contain spaces).
+ */
+ newcmdline = erts_alloc(ERTS_ALC_T_TMP, 2048);
+ cmdlength = parse_command(origcmd);
+ thecommand = (char *) erts_alloc(ERTS_ALC_T_TMP, cmdlength+1);
+ strncpy(thecommand, origcmd, cmdlength);
+ thecommand[cmdlength] = '\0';
+ DEBUGF(("spawn command: %s\n", thecommand));
+
+ applType = ApplicationType(thecommand, execPath, TRUE,
+ TRUE, errno_return);
+ DEBUGF(("ApplicationType returned for (%s) is %d\n", thecommand, applType));
+ erts_free(ERTS_ALC_T_TMP, (void *) thecommand);
+ if (applType == APPL_NONE) {
+ erts_free(ERTS_ALC_T_TMP,newcmdline);
+ return FALSE;
+ }
+ newcmdline[0] = '\0';
+
+ if (applType == APPL_DOS) {
+ /*
+ * Under NT, 16-bit DOS applications will not run unless they
+ * can be attached to a console. Run the 16-bit program as
+ * a normal process inside of a hidden console application,
+ * and then run that hidden console as a detached process.
+ */
+
+ siStartInfo.wShowWindow = SW_HIDE;
+ siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
+ createFlags = CREATE_NEW_CONSOLE;
+ strcat(newcmdline, "cmd.exe /c ");
+ } else if (hide) {
+ DEBUGF(("hiding window\n"));
+ siStartInfo.wShowWindow = SW_HIDE;
+ siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
+ createFlags = 0;
+ }
+
+ strcat(newcmdline, execPath);
+ strcat(newcmdline, origcmd+cmdlength);
+ } else { /* ERTS_SPAWN_EXECUTABLE */
+ int run_cmd = 0;
+ applType = ApplicationType(origcmd, execPath, FALSE, FALSE,
+ errno_return);
+ if (applType == APPL_NONE) {
+ return FALSE;
+ }
+ if (applType == APPL_DOS) {
+ /*
+ * See comment above
+ */
+
+ siStartInfo.wShowWindow = SW_HIDE;
+ siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
+ createFlags = CREATE_NEW_CONSOLE;
+ run_cmd = 1;
+ } else if (hide) {
+ DEBUGF(("hiding window\n"));
+ siStartInfo.wShowWindow = SW_HIDE;
+ siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
+ createFlags = 0;
+ }
+ if (run_cmd) {
+ char cmdPath[MAX_PATH];
+ int cmdType;
+ cmdType = ApplicationType("cmd.exe", cmdPath, TRUE, FALSE, errno_return);
+ if (cmdType == APPL_NONE || cmdType == APPL_DOS) {
+ return FALSE;
+ }
+ appname = (char *) erts_alloc(ERTS_ALC_T_TMP, strlen(cmdPath)+1);
+ strcpy(appname,cmdPath);
+ } else {
+ appname = (char *) erts_alloc(ERTS_ALC_T_TMP, strlen(execPath)+1);
+ strcpy(appname,execPath);
+ }
+ if (argv == NULL) {
+ BOOL orig_need_q = need_quotes(execPath);
+ char *ptr;
+ int ocl = strlen(execPath);
+ if (run_cmd) {
+ newcmdline = (char *) erts_alloc(ERTS_ALC_T_TMP,
+ ocl + ((orig_need_q) ? 3 : 1)
+ + 11);
+ memcpy(newcmdline,"cmd.exe /c ",11);
+ ptr = newcmdline + 11;
+ } else {
+ newcmdline = (char *) erts_alloc(ERTS_ALC_T_TMP,
+ ocl + ((orig_need_q) ? 3 : 1));
+ ptr = newcmdline;
+ }
+ if (orig_need_q) {
+ *ptr++ = '"';
+ }
+ memcpy(ptr,execPath,ocl);
+ ptr += ocl;
+ if (orig_need_q) {
+ *ptr++ = '"';
+ }
+ *ptr = '\0';
+ } else {
+ int sum = 1; /* '\0' */
+ char **ar = argv;
+ char *n;
+ char *save_arg0 = NULL;
+ if (argv[0] == erts_default_arg0 || run_cmd) {
+ save_arg0 = argv[0];
+ argv[0] = execPath;
+ }
+ if (run_cmd) {
+ sum += 11; /* cmd.exe /c */
+ }
+ while (*ar != NULL) {
+ sum += strlen(*ar);
+ if (need_quotes(*ar)) {
+ sum += 2; /* quotes */
+ }
+ sum++; /* space */
+ ++ar;
+ }
+ ar = argv;
+ newcmdline = erts_alloc(ERTS_ALC_T_TMP, sum);
+ n = newcmdline;
+ if (run_cmd) {
+ memcpy(n,"cmd.exe /c ",11);
+ n += 11;
+ }
+ while (*ar != NULL) {
+ int q = need_quotes(*ar);
+ sum = strlen(*ar);
+ if (q) {
+ *n++ = '"';
+ }
+ memcpy(n,*ar,sum);
+ n += sum;
+ if (q) {
+ *n++ = '"';
+ }
+ *n++ = ' ';
+ ++ar;
+ }
+ ASSERT(n > newcmdline);
+ *(n-1) = '\0';
+ if (save_arg0 != NULL) {
+ argv[0] = save_arg0;
+ }
+ }
+
+ }
+ DEBUGF(("Creating child process: %s, createFlags = %d\n", newcmdline, createFlags));
+ ok = CreateProcess(appname,
+ newcmdline,
+ NULL,
+ NULL,
+ TRUE,
+ createFlags | staticCreateFlags,
+ env,
+ wd,
+ &siStartInfo,
+ &piProcInfo);
+
+ if (newcmdline != NULL) {
+ erts_free(ERTS_ALC_T_TMP,newcmdline);
+ }
+ if (appname != NULL) {
+ erts_free(ERTS_ALC_T_TMP,appname);
+ }
+ if (!ok) {
+ DEBUGF(("CreateProcess failed: %s\n", last_error()));
+ if (*errno_return < 0) {
+ *errno_return = EACCES;
+ }
+ return FALSE;
+ }
+ CloseHandle(piProcInfo.hThread); /* Necessary to avoid resource leak. */
+ *phPid = piProcInfo.hProcess;
+
+ if (applType == APPL_DOS) {
+ WaitForSingleObject(hProcess, 50);
+ }
+
+ /*
+ * When an application spawns a process repeatedly, a new thread
+ * instance will be created for each process but the previous
+ * instances may not be cleaned up. This results in a significant
+ * virtual memory loss each time the process is spawned. If there
+ * is a WaitForInputIdle() call between CreateProcess() and
+ * CloseHandle(), the problem does not occur. PSS ID Number: Q124121
+ */
+
+ WaitForInputIdle(piProcInfo.hProcess, 5000);
+
+ return ok;
+}
+
+/*
+ * Note, inheritRead == FALSE means "inhetitWrite", i e one of the
+ * pipe ends is always expected to be inherited. The pipe end that should
+ * be inherited is opened without overlapped io flags, as the child program
+ * would expect stdout not to demand overlapped I/O.
+ */
+static int create_pipe(HANDLE *phRead, HANDLE *phWrite, BOOL inheritRead, BOOL overlapped_io)
+{
+ SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
+ char pipe_name[128]; /* Name of pipe. */
+ Uint calls;
+
+ /*
+ * If we should't use named pipes, create anonmous pipes.
+ */
+
+ if (!use_named_pipes) {
+ int success;
+ HANDLE non_inherited; /* Non-inherited copy of handle. */
+
+ if (!CreatePipe(phRead, phWrite, &sa, 0)) {
+ DEBUGF(("Error creating anonyomous pipe: %s\n", last_error()));
+ return FALSE;
+ }
+
+ if (inheritRead) {
+ success = DuplicateHandle(GetCurrentProcess(), *phWrite,
+ GetCurrentProcess(), &non_inherited, 0,
+ FALSE, DUPLICATE_SAME_ACCESS);
+ CloseHandle(*phWrite);
+ *phWrite = non_inherited;
+ } else {
+ success = DuplicateHandle(GetCurrentProcess(), *phRead,
+ GetCurrentProcess(), &non_inherited, 0,
+ FALSE, DUPLICATE_SAME_ACCESS);
+ CloseHandle(*phRead);
+ *phRead = non_inherited;
+ }
+ return success;
+ }
+
+
+ /*
+ * Otherwise, create named pipes.
+ */
+
+ calls = (Uint) erts_smp_atomic_inctest(&pipe_creation_counter);
+ sprintf(pipe_name, "\\\\.\\pipe\\erlang44_%d_%d",
+ getpid(), calls);
+
+ DEBUGF(("Creating pipe %s\n", pipe_name));
+ sa.bInheritHandle = inheritRead;
+ if ((*phRead = CreateNamedPipe(pipe_name,
+ PIPE_ACCESS_INBOUND |
+ ((inheritRead && !overlapped_io) ? 0 : FILE_FLAG_OVERLAPPED),
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
+ 1,
+ 0,
+ 0,
+ 2000,
+ &sa)) == NULL) {
+ DEBUGF(("Error creating pipe: %s\n", last_error()));
+ return FALSE;
+ }
+
+ sa.bInheritHandle = !inheritRead;
+ if ((*phWrite = CreateFile(pipe_name,
+ GENERIC_WRITE,
+ 0, /* No sharing */
+ &sa,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL |
+ ((inheritRead || overlapped_io) ? FILE_FLAG_OVERLAPPED : 0),
+ NULL)) == INVALID_HANDLE_VALUE) {
+ CloseHandle(*phRead);
+ DEBUGF(("Error opening other end of pipe: %s\n", last_error()));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+
+
+static int ApplicationType
+(
+ const char *originalName, /* Name of the application to find. */
+ char fullPath[MAX_PATH], /* Filled with complete path to
+ * application. */
+ BOOL search_in_path, /* If we should search the system wide path */
+ BOOL handle_quotes, /* If we should handle quotes around executable */
+ int *error_return /* A place to put an error code */
+ )
+{
+ int applType, i;
+ HANDLE hFile;
+ char *ext, *rest;
+ char buf[2];
+ DWORD read;
+ IMAGE_DOS_HEADER header;
+ static char extensions[][5] = {"", ".com", ".exe", ".bat"};
+ int is_quoted;
+ int len;
+
+ /* Look for the program as an external program. First try the name
+ * as it is, then try adding .com, .exe, and .bat, in that order, to
+ * the name, looking for an executable.
+ * NOTE! that we does not support execution of .com programs on Windows NT
+ *
+ *
+ * Using the raw SearchPath() procedure doesn't do quite what is
+ * necessary. If the name of the executable already contains a '.'
+ * character, it will not try appending the specified extension when
+ * searching (in other words, SearchPath will not find the program
+ * "a.b.exe" if the arguments specified "a.b" and ".exe").
+ * So, first look for the file as it is named. Then manually append
+ * the extensions, looking for a match. (')
+ */
+
+ len = strlen(originalName);
+ is_quoted = handle_quotes && len > 0 && originalName[0] == '"' &&
+ originalName[len-1] == '"';
+
+ applType = APPL_NONE;
+ *error_return = ENOENT;
+ for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
+ if(is_quoted) {
+ lstrcpyn(fullPath, originalName+1, MAX_PATH - 7);
+ len = strlen(fullPath);
+ if(len > 0) {
+ fullPath[len-1] = '\0';
+ }
+ } else {
+ lstrcpyn(fullPath, originalName, MAX_PATH - 5);
+ }
+ lstrcat(fullPath, extensions[i]);
+ SearchPath((search_in_path) ? NULL : ".", fullPath, NULL, MAX_PATH, fullPath, &rest);
+
+ /*
+ * Ignore matches on directories or data files, return if identified
+ * a known type.
+ */
+
+ if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
+ continue;
+ }
+
+ ext = strrchr(fullPath, '.');
+ if ((ext != NULL) && (strcmpi(ext, ".bat") == 0)) {
+ *error_return = EACCES;
+ applType = APPL_DOS;
+ break;
+ }
+
+ hFile = CreateFile(fullPath, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ continue;
+ }
+
+ *error_return = EACCES; /* If considered an error,
+ it's an access error */
+ header.e_magic = 0;
+ ReadFile(hFile, (void *) &header, sizeof(header), &read, NULL);
+ if (header.e_magic != IMAGE_DOS_SIGNATURE) {
+ /*
+ * Doesn't have the magic number for relocatable executables. If
+ * filename ends with .com, assume it's a DOS application anyhow.
+ * Note that we didn't make this assumption at first, because some
+ * supposed .com files are really 32-bit executables with all the
+ * magic numbers and everything.
+ */
+
+ CloseHandle(hFile);
+ if ((ext != NULL) && (strcmpi(ext, ".com") == 0)) {
+ applType = APPL_DOS;
+ break;
+ }
+ continue;
+ }
+ if (header.e_lfarlc != sizeof(header)) {
+ /*
+ * All Windows 3.X and Win32 and some DOS programs have this value
+ * set here. If it doesn't, assume that since it already had the
+ * other magic number it was a DOS application.
+ */
+
+ CloseHandle(hFile);
+ applType = APPL_DOS;
+ break;
+ }
+
+ /*
+ * The DWORD at header.e_lfanew points to yet another magic number.
+ */
+
+ buf[0] = '\0';
+ SetFilePointer(hFile, header.e_lfanew, NULL, FILE_BEGIN);
+ ReadFile(hFile, (void *) buf, 2, &read, NULL);
+ CloseHandle(hFile);
+
+ if ((buf[0] == 'L') && (buf[1] == 'E')) {
+ applType = APPL_DOS;
+ } else if ((buf[0] == 'N') && (buf[1] == 'E')) {
+ applType = APPL_WIN3X;
+ } else if ((buf[0] == 'P') && (buf[1] == 'E')) {
+ applType = APPL_WIN32;
+ } else {
+ continue;
+ }
+ break;
+ }
+
+ if (applType == APPL_NONE) {
+ return APPL_NONE;
+ }
+
+ if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) {
+ /*
+ * Replace long path name of executable with short path name for
+ * 16-bit applications. Otherwise the application may not be able
+ * to correctly parse its own command line to separate off the
+ * application name from the arguments.
+ */
+
+ GetShortPathName(fullPath, fullPath, MAX_PATH);
+ }
+ if (is_quoted) {
+ /* restore quotes on quoted program name */
+ len = strlen(fullPath);
+ memmove(fullPath+1,fullPath,len);
+ fullPath[0]='"';
+ fullPath[len+1]='"';
+ fullPath[len+2]='\0';
+ }
+ return applType;
+}
+
+/*
+ * Thread function used to emulate overlapped reading.
+ */
+
+DWORD WINAPI
+threaded_reader(LPVOID param)
+{
+ AsyncIo* aio = (AsyncIo *) param;
+ HANDLE thread = GetCurrentThread();
+ char* buf;
+ DWORD numToRead;
+
+ for (;;) {
+ WaitForSingleObject(aio->ioAllowed, INFINITE);
+ if (aio->flags & DF_EXIT_THREAD)
+ break;
+ buf = OV_BUFFER_PTR(aio);
+ numToRead = OV_NUM_TO_READ(aio);
+ aio->pendingError = 0;
+ if (!ReadFile(aio->fd, buf, numToRead, &aio->bytesTransferred, NULL))
+ aio->pendingError = GetLastError();
+ else if (aio->flags & DF_XLAT_CR) {
+ char *s;
+ int n;
+
+ n = aio->bytesTransferred;
+ for (s = buf; s < buf+n; s++) {
+ if (*s == '\r') {
+ if (s < buf + n - 1 && s[1] == '\n') {
+ memmove(s, s+1, (buf+n - s - 1));
+ --n;
+ } else {
+ *s = '\n';
+ }
+ }
+ }
+ aio->bytesTransferred = n;
+ }
+ SetEvent(aio->ov.hEvent);
+ if ((aio->flags & DF_XLAT_CR) == 0 && aio->bytesTransferred == 0) {
+ break;
+ }
+ if (aio->pendingError != NO_ERROR) {
+ break;
+ }
+ if (aio->flags & DF_EXIT_THREAD)
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Thread function used to emulate overlapped writing
+ */
+
+DWORD WINAPI
+threaded_writer(LPVOID param)
+{
+ AsyncIo* aio = (AsyncIo *) param;
+ HANDLE thread = GetCurrentThread();
+ char* buf;
+ DWORD numToWrite;
+ int ok;
+
+ for (;;) {
+ WaitForSingleObject(aio->ioAllowed, INFINITE);
+ if (aio->flags & DF_EXIT_THREAD)
+ break;
+ buf = OV_BUFFER_PTR(aio);
+ numToWrite = OV_NUM_TO_READ(aio);
+ aio->pendingError = 0;
+ ok = WriteFile(aio->fd, buf, numToWrite, &aio->bytesTransferred, NULL);
+ if (!ok) {
+ aio->pendingError = GetLastError();
+ if (aio->pendingError == ERROR_INVALID_HANDLE &&
+ aio->flags & DF_DROP_IF_INVH) {
+ /* This is standard error and we'we got an
+ invalid standard error FD (non-inheritable) from parent.
+ Just drop the message and be happy. */
+ aio->pendingError = 0;
+ aio->bytesTransferred = numToWrite;
+ } else if (aio->pendingError == ERROR_NOT_ENOUGH_MEMORY) {
+ /* This could be a console, which limits utput to 64kbytes,
+ which might translate to less on a unicode system.
+ Try 16k chunks and see if it works before giving up. */
+ int done = 0;
+ DWORD transferred;
+ aio->pendingError = 0;
+ aio->bytesTransferred = 0;
+ ok = 1;
+ while (ok && (numToWrite - done) > 0x4000) {
+ ok = WriteFile(aio->fd, buf + done, 0x4000, &transferred, NULL);
+ aio->bytesTransferred += transferred;
+ done += 0x4000;
+ }
+ if (ok && (numToWrite - done) > 0) {
+ ok = WriteFile(aio->fd, buf + done, (numToWrite - done),
+ &transferred, NULL);
+ aio->bytesTransferred += transferred;
+ }
+ if (!ok) {
+ aio->pendingError = GetLastError();
+ }
+ }
+ }
+ SetEvent(aio->ov.hEvent);
+ if (aio->pendingError != NO_ERROR || aio->bytesTransferred == 0)
+ break;
+ if (aio->flags & DF_EXIT_THREAD)
+ break;
+ }
+ CloseHandle(aio->fd);
+ aio->fd = INVALID_HANDLE_VALUE;
+ return 0;
+}
+
+static HANDLE
+translate_fd(int fd)
+{
+ DWORD access;
+ HANDLE handle;
+
+ switch (fd) {
+ case 0:
+ access = GENERIC_READ;
+ handle = GetStdHandle(STD_INPUT_HANDLE);
+ break;
+ case 1:
+ access = GENERIC_WRITE;
+ handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ break;
+ case 2:
+ access = GENERIC_WRITE;
+ handle = GetStdHandle(STD_ERROR_HANDLE);
+ break;
+ default:
+ return (HANDLE) fd;
+ }
+ DEBUGF(("translate_fd(%d) -> std(%d)\n", fd, handle));
+
+ if (handle == INVALID_HANDLE_VALUE || handle == 0) {
+ handle = CreateFile("nul", access, 0,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ }
+ DEBUGF(("translate_fd(%d) -> %d\n", fd, handle));
+ return handle;
+}
+
+static ErlDrvData
+fd_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
+{
+ DriverData* dp;
+ int is_std_error = (opts->ofd == 2);
+
+ opts->ifd = (int) translate_fd(opts->ifd);
+ opts->ofd = (int) translate_fd(opts->ofd);
+ if ((dp = new_driver_data(port_num, opts->packet_bytes, 2, TRUE)) == NULL)
+ return ERL_DRV_ERROR_GENERAL;
+
+ if (!create_file_thread(&dp->in, DO_READ)) {
+ dp->port_num = PORT_FREE;
+ return ERL_DRV_ERROR_GENERAL;
+ }
+
+ if (!create_file_thread(&dp->out, DO_WRITE)) {
+ dp->port_num = PORT_FREE;
+ return ERL_DRV_ERROR_GENERAL;
+ }
+
+ fd_driver_input = &(dp->in);
+ dp->in.flags = DF_XLAT_CR;
+ if (is_std_error) {
+ dp->out.flags |= DF_DROP_IF_INVH; /* Just drop messages if stderror
+ is an invalid handle */
+ }
+ return set_driver_data(dp, opts->ifd, opts->ofd, opts->read_write, 0);
+}
+
+static void fd_stop(ErlDrvData d)
+{
+ int fd = (int)d;
+ /*
+ * I don't know a clean way to terminate the threads
+ * (TerminateThread() doesn't release the stack),
+ * so will we'll let the threads live. Normally, the fd
+ * driver is only used to support the -oldshell option,
+ * so this shouldn't be a problem in practice.
+ *
+ * Since we will not attempt to terminate the threads,
+ * better not close the input or output files either.
+ */
+
+ driver_data[fd].in.thread = (HANDLE) -1;
+ driver_data[fd].out.thread = (HANDLE) -1;
+ driver_data[fd].in.fd = INVALID_HANDLE_VALUE;
+ driver_data[fd].out.fd = INVALID_HANDLE_VALUE;
+
+ /*return */ common_stop(fd);
+}
+
+static ErlDrvData
+vanilla_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
+{
+ HANDLE ofd,ifd;
+ DriverData* dp;
+ DWORD access; /* Access mode: GENERIC_READ, GENERIC_WRITE. */
+ DWORD crFlags;
+ HANDLE this_process = GetCurrentProcess();
+
+ access = 0;
+ if (opts->read_write == DO_READ)
+ access |= GENERIC_READ;
+ if (opts->read_write == DO_WRITE)
+ access |= GENERIC_WRITE;
+
+ if (opts->read_write == DO_READ)
+ crFlags = OPEN_EXISTING;
+ else if (opts->read_write == DO_WRITE)
+ crFlags = CREATE_ALWAYS;
+ else
+ crFlags = OPEN_ALWAYS;
+
+ if ((dp = new_driver_data(port_num, opts->packet_bytes, 2, FALSE)) == NULL)
+ return ERL_DRV_ERROR_GENERAL;
+ ofd = CreateFile(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, crFlags, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (!DuplicateHandle(this_process, (HANDLE) ofd,
+ this_process, &ifd, 0,
+ FALSE, DUPLICATE_SAME_ACCESS)) {
+ CloseHandle(ofd);
+ ofd = INVALID_HANDLE_VALUE;
+ }
+ if (ofd == INVALID_HANDLE_VALUE)
+ return ERL_DRV_ERROR_GENERAL;
+ return set_driver_data(dp, ifd, ofd, opts->read_write,0);
+}
+
+static void
+stop(ErlDrvData index)
+{
+ common_stop((int)index);
+}
+
+static void common_stop(int index)
+{
+ DriverData* dp = driver_data+index;
+
+ DEBUGF(("common_stop(%d)\n", index));
+
+ if (dp->in.ov.hEvent != NULL) {
+ (void) driver_select(dp->port_num,
+ (ErlDrvEvent)dp->in.ov.hEvent,
+ ERL_DRV_READ, 0);
+ }
+ if (dp->out.ov.hEvent != NULL) {
+ (void) driver_select(dp->port_num,
+ (ErlDrvEvent)dp->out.ov.hEvent,
+ ERL_DRV_WRITE, 0);
+ }
+
+ if (dp->out.thread == (HANDLE) -1 && dp->in.thread == (HANDLE) -1) {
+ release_driver_data(dp);
+ } else {
+ /*
+ * If there are read or write threads, start a thread which will
+ * wait for them to finish.
+ */
+ HANDLE thread;
+ DWORD tid;
+ dp->port_num = PORT_EXITING;
+ thread = (HANDLE *) _beginthreadex(NULL, 0, threaded_exiter, dp, 0, &tid);
+ CloseHandle(thread);
+ }
+}
+
+DWORD WINAPI
+threaded_exiter(LPVOID param)
+{
+ DriverData* dp = (DriverData *) param;
+ HANDLE handles[2];
+ int i;
+
+ /*
+ * Ask the threads to terminated.
+ *
+ * Note that we can't reliable test the state of the ioAllowed event,
+ * because it is an auto reset event. Therefore, always set the
+ * exit flag and signal the event.
+ */
+
+ i = 0;
+ if (dp->out.thread != (HANDLE) -1) {
+ dp->out.flags = DF_EXIT_THREAD;
+ SetEvent(dp->out.ioAllowed);
+ handles[i++] = dp->out.thread;
+ }
+ if (dp->in.thread != (HANDLE) -1) {
+ dp->in.flags = DF_EXIT_THREAD;
+ SetEvent(dp->in.ioAllowed);
+ handles[i++] = dp->in.thread;
+ }
+
+ /*
+ * If we were lucky, the following happened above:
+ * 1) The output thread terminated (and closed the pipe).
+ * 2) As a consequence of that, the port program received
+ * EOF on its standard input.
+ * 3) Hopefully, because of (2), the port program terminated.
+ * 4) Because of (3), the input thread terminated.
+ *
+ * But this might need some time; therefore, we must wait for
+ * both threads to terminate.
+ */
+
+ if (i > 0) {
+ switch (WaitForMultipleObjects(i, handles, TRUE, 5000)) {
+ case WAIT_TIMEOUT:
+ DEBUGF(("Timeout waiting for %d threads failed\n", i));
+ break;
+ case WAIT_FAILED:
+ DEBUGF(("Wait for %d threads failed: %s\n",
+ i, win32_errorstr(GetLastError())));
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Wait for threads to terminate didn't help. Now use some force.
+ * TerminateThread() is *not* a good idea, because it doesn't clean
+ * up the thread's stack.
+ *
+ * Instead we well terminate the port program and wait for the
+ * threads to terminate themselves when they receive end of file.
+ */
+
+ if (dp->out.thread != (HANDLE) -1) {
+ int error;
+
+ if (WaitForSingleObject(dp->out.thread, 0) == WAIT_OBJECT_0) {
+ CloseHandle(dp->out.thread);
+ dp->out.thread = (HANDLE) -1;
+ } else if (dp->port_pid != INVALID_HANDLE_VALUE) {
+ DEBUGF(("Killing port process 0x%x (output thread)\n", dp->port_pid));
+ TerminateProcess(dp->port_pid, 0);
+ if (!CloseHandle(dp->port_pid))
+ DEBUGF(("Failed to close output handle!!!\n"));
+ dp->port_pid = INVALID_HANDLE_VALUE;
+ DEBUGF(("Waiting for output thread 0x%x to finish\n", dp->out.thread));
+ error = WaitForSingleObject(dp->out.thread, INFINITE);
+ }
+ }
+
+ if (dp->in.thread != (HANDLE) -1) {
+ if (WaitForSingleObject(dp->in.thread, 0) == WAIT_OBJECT_0) {
+ CloseHandle(dp->in.thread);
+ dp->in.thread = (HANDLE) -1;
+ } else if (dp->port_pid != INVALID_HANDLE_VALUE) {
+ DEBUGF(("Killing port process 0x%x (input thread)\n", dp->port_pid));
+ TerminateProcess(dp->port_pid, 0);
+ if (!CloseHandle(dp->port_pid))
+ DEBUGF(("Failed to close input handle!!!\n"));
+ dp->port_pid = INVALID_HANDLE_VALUE;
+
+ DEBUGF(("Waiting for input thread 0x%x to finish\n", dp->in.thread));
+ switch (WaitForSingleObject(dp->in.thread, INFINITE)) {
+ case WAIT_OBJECT_0:
+ CloseHandle(dp->in.thread);
+ dp->in.thread = (HANDLE) -1;
+ break;
+ default:
+ DEBUGF(("Wait for input thread to finish failed: %s\n",
+ win32_errorstr(GetLastError())));
+ break;
+ }
+ }
+ }
+
+ release_driver_data(dp);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------
+ * output --
+ * Outputs data from Erlang to the port program.
+ *
+ * Results:
+ * Returns the actual number of bytes written (including the
+ * packet header) or -1 if an error occurred.
+ * ----------------------------------------------------------------------
+ */
+
+static void
+output(ErlDrvData drv_data, char* buf, int len)
+/* long drv_data; /* The slot to use in the driver data table.
+ * For Windows NT, this is *NOT* a file handle.
+ * The handle is found in the driver data.
+ */
+/* char *buf; /* Pointer to data to write to the port program. */
+/* int len; /* Number of bytes to write. */
+{
+ DriverData* dp;
+ int pb; /* The header size for this port. */
+ int port_num; /* The actual port number (for diagnostics). */
+ char* current;
+
+ dp = driver_data + (int)drv_data;
+ if ((port_num = dp->port_num) == -1)
+ return ; /*-1;*/
+
+ pb = dp->packet_bytes;
+
+ if ((pb+len) == 0)
+ return ; /* 0; */
+
+ /*
+ * Check that the message can be sent with given header length.
+ */
+
+ if ((pb == 2 && len > 65535) || (pb == 1 && len > 255)) {
+ driver_failure_posix(port_num, EINVAL);
+ return ; /* -1; */
+ }
+
+ /*
+ * Allocate memory for both the message and the header.
+ */
+
+ ASSERT(dp->outbuf == NULL);
+ ASSERT(dp->outBufSize == 0);
+
+ ASSERT(!dp->outbuf);
+ dp->outbuf = DRV_BUF_ALLOC(pb+len);
+ if (!dp->outbuf) {
+ driver_failure_posix(port_num, ENOMEM);
+ return ; /* -1; */
+ }
+
+ dp->outBufSize = pb+len;
+ erts_smp_atomic_add(&sys_misc_mem_sz, dp->outBufSize);
+
+ /*
+ * Store header bytes (if any).
+ */
+
+ current = dp->outbuf;
+ switch (pb) {
+ case 4:
+ *current++ = (len >> 24) & 255;
+ *current++ = (len >> 16) & 255;
+ case 2:
+ *current++ = (len >> 8) & 255;
+ case 1:
+ *current++ = len & 255;
+ }
+
+ /*
+ * Start the write.
+ */
+
+ if (len)
+ memcpy(current, buf, len);
+
+ if (!async_write_file(&dp->out, dp->outbuf, pb+len)) {
+ set_busy_port(port_num, 1);
+ } else {
+ dp->out.ov.Offset += pb+len; /* For vanilla driver. */
+ /* XXX OffsetHigh should be changed too. */
+ ASSERT(erts_smp_atomic_read(&sys_misc_mem_sz) >= dp->outBufSize);
+ erts_smp_atomic_add(&sys_misc_mem_sz, -1*dp->outBufSize);
+ DRV_BUF_FREE(dp->outbuf);
+ dp->outBufSize = 0;
+ dp->outbuf = NULL;
+ }
+ /*return 0;*/
+}
+
+
+/* ----------------------------------------------------------------------
+ * ready_input --
+ * This function is called (indirectly) from check_io() when an
+ * event object has been signaled, indicating that there is
+ * something to read on the corresponding file handle.
+ *
+ * If the port is working in the continous stream mode (packet_bytes == 0),
+ * whatever data read will be sent straight to Erlang.
+ *
+ * Results:
+ * Always 0.
+ * ----------------------------------------------------------------------
+ */
+
+static void
+ready_input(ErlDrvData drv_data, ErlDrvEvent ready_event)
+/* long drv_data; /* Driver data. */
+/* HANDLE ready_event; /* The handle for the ready event. */
+{
+ int error = 0; /* The error code (assume initially no errors). */
+ DWORD bytesRead; /* Number of bytes read. */
+ DriverData* dp;
+ int pb;
+
+ dp = driver_data+(int)drv_data;
+ pb = dp->packet_bytes;
+#ifdef ERTS_SMP
+ if(dp->in.thread == (HANDLE) -1) {
+ dp->in.async_io_active = 0;
+ }
+#endif
+ DEBUGF(("ready_input: dp %p, event 0x%x\n", dp, ready_event));
+
+ /*
+ * Evaluate the result of the overlapped read.
+ */
+
+#ifdef HARD_POLL_DEBUG
+ poll_debug_read_begin(dp->in.ov.hEvent);
+#endif
+
+ error = get_overlapped_result(&dp->in, &bytesRead, TRUE);
+
+#ifdef HARD_POLL_DEBUG
+ poll_debug_read_done(dp->in.ov.hEvent,bytesRead);
+#endif
+
+ if (error == NO_ERROR) {
+ if (pb == 0) { /* Continous stream. */
+#ifdef DEBUG
+ DEBUGF(("ready_input: %d: ", bytesRead));
+ erl_bin_write(dp->inbuf, 16, bytesRead);
+ DEBUGF(("\n"));
+#endif
+ driver_output(dp->port_num, dp->inbuf, bytesRead);
+ } else { /* Packet mode */
+ dp->bytesInBuffer += bytesRead;
+
+ /*
+ * Loop until we've exhausted the data in the buffer.
+ */
+
+ for (;;) {
+
+ /*
+ * Check for completion of a header read.
+ */
+
+ if (dp->bytesInBuffer >= dp->totalNeeded &&
+ dp->totalNeeded == pb) {
+
+ /*
+ * We have successfully read the packet header
+ * (and perhaps even the packet). Get the packet size
+ * from the header and update dp->totalNeeded to include
+ * the packet size.
+ */
+
+ int packet_size = 0;
+ unsigned char *header = (unsigned char *) dp->inbuf;
+
+ switch (pb) {
+ case 4:
+ packet_size = (packet_size << 8) | *header++;
+ packet_size = (packet_size << 8) | *header++;
+ case 2:
+ packet_size = (packet_size << 8) | *header++;
+ case 1:
+ packet_size = (packet_size << 8) | *header++;
+ }
+
+ dp->totalNeeded += packet_size;
+
+ /*
+ * Make sure that the receive buffer is big enough.
+ */
+
+ if (dp->inBufSize < dp->totalNeeded) {
+ char* new_buf;
+
+ new_buf = DRV_BUF_REALLOC(dp->inbuf, dp->totalNeeded);
+ if (new_buf == NULL) {
+ error = ERROR_NOT_ENOUGH_MEMORY;
+ break; /* Break out of loop into error handler. */
+ }
+ ASSERT(erts_smp_atomic_read(&sys_misc_mem_sz) >= dp->inBufSize);
+ erts_smp_atomic_add(&sys_misc_mem_sz,
+ dp->totalNeeded - dp->inBufSize);
+ dp->inBufSize = dp->totalNeeded;
+ dp->inbuf = new_buf;
+ }
+ }
+
+ /*
+ * Check for completion of a packet read.
+ */
+
+ if (dp->bytesInBuffer < dp->totalNeeded) {
+ /*
+ * Not enough bytes in the buffer. Break out of
+ * the loop and initiate a new read.
+ */
+
+ break;
+ } else {
+
+ /*
+ * We have successfully read a complete packet, which
+ * can be passed to Erlang.
+ */
+
+ driver_output(dp->port_num, dp->inbuf+pb, dp->totalNeeded-pb);
+
+ /*
+ * Update the number of bytes remaining in the buffer,
+ * and move the data remaining (if any) to the beginning
+ * of the buffer.
+ */
+
+ dp->bytesInBuffer -= dp->totalNeeded;
+ if (dp->bytesInBuffer > 0) {
+ memmove(dp->inbuf, dp->inbuf+dp->totalNeeded,
+ dp->bytesInBuffer);
+ }
+
+ /*
+ * Indicate that we need the size of a header, and
+ * go through the loop once more (to either process
+ * remaining bytes or initiate reading more).
+ */
+
+ dp->totalNeeded = pb;
+ }
+ }
+ }
+ }
+
+ /*
+ * Start a new overlapped read, or report the error.
+ */
+
+ if (error == NO_ERROR) {
+ async_read_file(&dp->in, dp->inbuf+dp->bytesInBuffer,
+ dp->inBufSize - dp->bytesInBuffer);
+ } else {
+ DEBUGF(("ready_input(): error: %s\n", win32_errorstr(error)));
+ if (error == ERROR_BROKEN_PIPE || error == ERROR_HANDLE_EOF) {
+ /* Maybe check exit status */
+ if (dp->report_exit) {
+ DWORD exitcode;
+ if (GetExitCodeProcess(dp->port_pid, &exitcode) &&
+ exitcode != STILL_ACTIVE) {
+ driver_report_exit(dp->port_num, exitcode);
+ }
+ }
+ driver_failure_eof(dp->port_num);
+ } else { /* Report real errors. */
+ int error = GetLastError();
+ (void) driver_select(dp->port_num, ready_event, ERL_DRV_READ, 0);
+ _dosmaperr(error);
+ driver_failure_posix(dp->port_num, errno);
+ }
+ }
+
+ /*return 0;*/
+}
+
+static void
+ready_output(ErlDrvData drv_data, ErlDrvEvent ready_event)
+{
+ DWORD bytesWritten;
+ DriverData* dp = driver_data + (int)drv_data;
+ int error;
+
+#ifdef ERTS_SMP
+ if(dp->out.thread == (HANDLE) -1) {
+ dp->out.async_io_active = 0;
+ }
+#endif
+ DEBUGF(("ready_output(%d, 0x%x)\n", drv_data, ready_event));
+ set_busy_port(dp->port_num, 0);
+ if (!(dp->outbuf)) {
+ /* Happens because event sometimes get signalled during a succesful
+ write... */
+ return;
+ }
+ ASSERT(erts_smp_atomic_read(&sys_misc_mem_sz) >= dp->outBufSize);
+ erts_smp_atomic_add(&sys_misc_mem_sz, -1*dp->outBufSize);
+ DRV_BUF_FREE(dp->outbuf);
+ dp->outBufSize = 0;
+ dp->outbuf = NULL;
+#ifdef HARD_POLL_DEBUG
+ poll_debug_write_begin(dp->out.ov.hEvent);
+#endif
+ error = get_overlapped_result(&dp->out, &bytesWritten, TRUE);
+#ifdef HARD_POLL_DEBUG
+ poll_debug_write_done(dp->out.ov.hEvent,bytesWritten);
+#endif
+
+ if (error == NO_ERROR) {
+ dp->out.ov.Offset += bytesWritten; /* For vanilla driver. */
+ return ; /* 0; */
+ }
+
+ (void) driver_select(dp->port_num, ready_event, ERL_DRV_WRITE, 0);
+ _dosmaperr(error);
+ driver_failure_posix(dp->port_num, errno);
+ /* return 0; */
+}
+
+static void stop_select(ErlDrvEvent e, void* _)
+{
+ CloseHandle((HANDLE)e);
+}
+
+/* Fills in the systems representation of the beam process identifier.
+** The Pid is put in STRING representation in the supplied buffer,
+** no interpretation of this should be done by the rest of the
+** emulator. The buffer should be at least 21 bytes long.
+*/
+void sys_get_pid(char *buffer){
+ DWORD p = GetCurrentProcessId();
+ /* The pid is scalar and is an unsigned long. */
+ sprintf(buffer,"%lu",(unsigned long) p);
+}
+
+void
+sys_init_io(void)
+{
+
+ /* Now heres an icky one... This is called before drivers are, so we
+ can change our view of the number of open files possible.
+ We estimate the number to twice the amount of ports.
+ We really dont know on windows, do we? */
+ max_files = 2*erts_max_ports;
+
+#ifdef USE_THREADS
+#ifdef ERTS_SMP
+ if (init_async(-1) < 0)
+ erl_exit(1, "Failed to initialize async-threads\n");
+#else
+ {
+ /* This is special stuff, starting a driver from the
+ * system routines, but is a nice way of handling stuff
+ * the erlang way
+ */
+ SysDriverOpts dopts;
+ int ret;
+
+ sys_memset((void*)&dopts, 0, sizeof(SysDriverOpts));
+ add_driver_entry(&async_driver_entry);
+ ret = erts_open_driver(NULL, NIL, "async", &dopts, NULL);
+ DEBUGF(("open_driver = %d\n", ret));
+ if (ret < 0)
+ erl_exit(1, "Failed to open async driver\n");
+ erts_port[ret].status |= ERTS_PORT_SFLG_IMMORTAL;
+ }
+#endif
+#endif
+}
+
+#ifdef ERTS_SMP
+void
+erts_sys_main_thread(void)
+{
+ HANDLE dummy;
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_set_thread_name("parent_thread");
+#endif
+ dummy = CreateEvent(NULL, FALSE, FALSE, NULL);
+ for(;;) {
+ WaitForSingleObject(dummy, INFINITE);
+ }
+}
+#endif
+
+void erts_sys_alloc_init(void)
+{
+ elib_ensure_initialized();
+}
+
+void *erts_sys_alloc(ErtsAlcType_t t, void *x, Uint sz)
+{
+ return malloc((size_t) sz);
+}
+
+void *erts_sys_realloc(ErtsAlcType_t t, void *x, void *p, Uint sz)
+{
+ return realloc(p, (size_t) sz);
+}
+
+void erts_sys_free(ErtsAlcType_t t, void *x, void *p)
+{
+ free(p);
+}
+
+static Preload* preloaded = NULL;
+static unsigned* res_name = NULL;
+static int num_preloaded = 0;
+
+/* Return a pointer to a vector of names of preloaded modules */
+
+Preload* sys_preloaded(void)
+{
+ HRSRC hRes;
+ unsigned char* data;
+
+#define GETWORD(p) (0[p] | 1[p] << 8)
+#define GETDWORD(p) (GETWORD(p) | GETWORD(p+2) << 16)
+
+
+ if (preloaded == NULL) {
+ int i;
+ ASSERT(beam_module != NULL);
+ hRes = FindResource(beam_module, 0, "ERLANG_DICT");
+ /* We might have a resource compiler laying out the 0 resource with
+ "0" as a textual name instead... */
+ if (hRes == NULL) {
+ hRes = FindResource(beam_module, "0", "ERLANG_DICT");
+ }
+ if (hRes == NULL) {
+ DWORD n = GetLastError();
+ fprintf(stderr, "No ERLANG_DICT resource\n");
+ exit(1);
+ }
+ data = (unsigned char *) LoadResource(beam_module, hRes);
+
+ num_preloaded = GETWORD(data);
+ if (num_preloaded == 0) {
+ fprintf(stderr, "No preloaded modules\n");
+ exit(1);
+ }
+
+ data += 2;
+ preloaded = erts_alloc(ERTS_ALC_T_PRELOADED,
+ (num_preloaded+1)*sizeof(Preload));
+ res_name = erts_alloc(ERTS_ALC_T_PRELOADED,
+ (num_preloaded+1)*sizeof(unsigned));
+ erts_smp_atomic_add(&sys_misc_mem_sz,
+ (num_preloaded+1)*sizeof(Preload)
+ + (num_preloaded+1)*sizeof(unsigned));
+ for (i = 0; i < num_preloaded; i++) {
+ int n;
+
+ preloaded[i].size = GETDWORD(data);
+ data += 4;
+ res_name[i] = GETWORD(data);
+ data += 2;
+ n = GETWORD(data);
+ data += 2;
+ preloaded[i].name = erts_alloc(ERTS_ALC_T_PRELOADED, n+1);
+ erts_smp_atomic_add(&sys_misc_mem_sz, n+1);
+ sys_memcpy(preloaded[i].name, data, n);
+ preloaded[i].name[n] = '\0';
+ data += n;
+ DEBUGF(("name: %s; size: %d; resource: %p\n",
+ preloaded[i].name, preloaded[i].size, res_name[i]));
+ }
+ preloaded[i].name = NULL;
+ }
+
+#undef GETWORD
+#undef GETDWORD
+ return preloaded;
+}
+
+/* Return a pointer to preloaded code for module "module" */
+unsigned char* sys_preload_begin(Preload* pp)
+{
+ HRSRC hRes;
+ unsigned resource;
+
+ ASSERT(beam_module != NULL);
+
+ resource = res_name[pp-preloaded];
+ DEBUGF(("Loading name: %s; size: %d; resource: %p\n",
+ pp->name, pp->size, resource));
+ hRes = FindResource(beam_module, (char *) resource, "ERLANG_CODE");
+ return pp->code = LoadResource(beam_module, hRes);
+}
+
+/* Clean up if allocated */
+void sys_preload_end(Preload* pp)
+{
+}
+
+/* Read a key from console */
+
+int
+sys_get_key(int fd)
+{
+ ASSERT(fd == 0);
+
+ if (win_console) {
+ return ConGetKey();
+ }
+
+ /*
+ * Black magic follows. (Code stolen from get_overlapped_result())
+ */
+
+ if (fd_driver_input != NULL && fd_driver_input->thread != (HANDLE)-1) {
+ DWORD error;
+ int key;
+
+ error = WaitForSingleObject(fd_driver_input->ov.hEvent, INFINITE);
+ if (error == WAIT_OBJECT_0) {
+ if (fd_driver_input->bytesTransferred > 0) {
+ int n;
+ int i;
+ char* buf = OV_BUFFER_PTR(fd_driver_input);
+
+ fd_driver_input->bytesTransferred--;
+ n = fd_driver_input->bytesTransferred;
+ key = buf[0];
+ for (i = n; i > 0; i--) {
+ buf[i-1] = buf[i];
+ }
+ return key;
+ }
+ }
+ }
+ return '*'; /* Error! */
+}
+
+/*
+ * Returns a human-readable description of the last error.
+ * The returned pointer will be valid only as long as last-error()
+ * isn't called again.
+ */
+
+char* win32_errorstr(int error)
+{
+#ifdef SMP
+ LPTSTR lpBufPtr = erts_smp_tsd_get(win32_errstr_key);
+#else
+ static LPTSTR lpBufPtr = NULL;
+#endif
+ if (lpBufPtr) {
+ LocalFree(lpBufPtr);
+ }
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpBufPtr,
+ 0,
+ NULL);
+ SetLastError(error);
+#ifdef ERTS_SMP
+ erts_smp_tsd_set(win32_errstr_key,lpBufPtr);
+#endif
+ return lpBufPtr;
+}
+
+char* last_error(void)
+{
+ return win32_errorstr(GetLastError());
+}
+
+static void* sys_func_memzero(void* s, size_t n)
+{
+ return sys_memzero(s, n);
+}
+
+#ifdef DEBUG
+static HANDLE hDebugWrite = INVALID_HANDLE_VALUE;
+
+void erl_debug(char *fmt,...)
+{
+ char sbuf[1024]; /* Temporary buffer. */
+ DWORD written; /* Actual number of chars written. */
+ va_list va;
+
+ if (hDebugWrite != INVALID_HANDLE_VALUE) {
+ va_start(va, fmt);
+ vsprintf(sbuf, fmt, va);
+ WriteFile(hDebugWrite, sbuf, strlen(sbuf), &written, NULL);
+ va_end(va);
+ }
+}
+
+static void debug_console(void)
+{
+ HANDLE hRead; /* Handle to read end of pipe. */
+ SECURITY_ATTRIBUTES sa;
+ PROCESS_INFORMATION procInfo;
+ STARTUPINFO startInfo;
+ BOOL ok;
+
+ /*
+ * Create a pipe for communicating with the sub process.
+ */
+
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+ if (!CreatePipe(&hRead, &hDebugWrite, &sa, 0)) {
+ fprintf(stderr, "Failed to create pipe: %d\n",
+ GetLastError());
+ exit(1);
+ }
+
+ startInfo.cb = sizeof(STARTUPINFO);
+ startInfo.lpTitle = "Erlang Debug Log";
+ startInfo.lpReserved = NULL;
+ startInfo.lpReserved2 = NULL;
+ startInfo.cbReserved2 = 0;
+ startInfo.lpDesktop = NULL;
+ startInfo.dwFlags = STARTF_USESTDHANDLES;
+ startInfo.hStdInput = hRead;
+
+ /* The following handles are not intended to be used. */
+ startInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ startInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+ ok = CreateProcess(NULL,
+ "erl_log.exe", /* Application */
+ NULL, /* Process security attributes. */
+ NULL, /* Thread security attributes. */
+ TRUE, /* Handle inheritance flag. */
+ CREATE_NEW_CONSOLE, /* Flags. */
+ NULL, /* Environment. */
+ NULL, /* Current directory. */
+ &startInfo,/* Startup info. */
+ &procInfo /* Process information. */
+ );
+
+ CloseHandle(hRead);
+
+ if (ok) {
+ /*
+ * Since we don't use these, close them at once to avoid a resource
+ * leak.
+ */
+ CloseHandle(procInfo.hProcess);
+ CloseHandle(procInfo.hThread);
+ } else {
+ fprintf(stderr, "Create process failed: %s\n", last_error());
+ exit(1);
+ }
+}
+
+void
+erl_bin_write(buf, sz, max)
+ unsigned char* buf;
+ int sz;
+ int max;
+{
+ int i, imax;
+ char comma[5] = ",";
+
+ if (hDebugWrite == INVALID_HANDLE_VALUE)
+ return;
+
+ if (!sz)
+ return;
+ if (sz > max)
+ imax = max;
+ else
+ imax = sz;
+
+ for (i=0; i<imax; i++) {
+ if (i == imax-1) {
+ if (sz > max)
+ strcpy(comma, ",...");
+ else
+ comma[0] = 0;
+ }
+ if (isdigit(buf[i]))
+ erl_debug("%u%s", (int)(buf[i]), comma);
+ else {
+ if (isalpha(buf[i])) {
+ erl_debug("%c%s", buf[i], comma);
+ }
+ else
+ erl_debug("%u%s", (int)(buf[i]), comma);
+ }
+ }
+}
+
+void
+erl_assert_error(char* expr, char* file, int line)
+{
+ char message[1024];
+
+ sprintf(message, "File %hs, line %d: %hs", file, line, expr);
+ MessageBox(GetActiveWindow(), message, "Assertion failed",
+ MB_OK | MB_ICONERROR);
+#if 0
+ erl_crash_dump(file, line, "Assertion failed: %hs\n", expr);
+#endif
+ DebugBreak();
+}
+
+#endif /* DEBUG */
+
+static void
+check_supported_os_version(void)
+{
+#if defined(_WIN32_WINNT)
+ {
+ DWORD major = (_WIN32_WINNT >> 8) & 0xff;
+ DWORD minor = _WIN32_WINNT & 0xff;
+
+ if (int_os_version.dwPlatformId != VER_PLATFORM_WIN32_NT
+ || int_os_version.dwMajorVersion < major
+ || (int_os_version.dwMajorVersion == major
+ && int_os_version.dwMinorVersion < minor))
+ erl_exit(-1,
+ "Windows version not supported "
+ "(min required: winnt %d.%d)\n",
+ major, minor);
+ }
+#else
+ erl_exit(-1,
+ "Windows version not supported "
+ "(min required: win %d.%d)\n",
+ nt_major, nt_minor);
+#endif
+}
+
+#ifdef USE_THREADS
+static void *ethr_internal_alloc(size_t size)
+{
+ return erts_alloc_fnf(ERTS_ALC_T_ETHR_INTERNAL, (Uint) size);
+}
+static void *ethr_internal_realloc(void *ptr, size_t size)
+{
+ return erts_realloc_fnf(ERTS_ALC_T_ETHR_INTERNAL, ptr, (Uint) size);
+}
+static void ethr_internal_free(void *ptr)
+{
+ erts_free(ERTS_ALC_T_ETHR_INTERNAL, ptr);
+}
+#endif
+
+void
+erts_sys_pre_init(void)
+{
+ int_os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&int_os_version);
+ check_supported_os_version();
+#ifdef USE_THREADS
+ {
+ erts_thr_init_data_t eid = ERTS_THR_INIT_DATA_DEF_INITER;
+ eid.alloc = ethr_internal_alloc;
+ eid.realloc = ethr_internal_realloc;
+ eid.free = ethr_internal_free;
+ erts_thr_init(&eid);
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ erts_lcnt_init();
+#endif
+ }
+#endif
+ erts_smp_atomic_init(&sys_misc_mem_sz, 0);
+ erts_sys_env_init();
+}
+
+/*
+ * the last two only used for standalone erlang
+ * they should are used by sae_main in beam dll to
+ * enable standalone execution via erl_api-routines
+ */
+
+void noinherit_std_handle(DWORD type)
+{
+ HANDLE h = GetStdHandle(type);
+ if (h != 0 && h != INVALID_HANDLE_VALUE) {
+ SetHandleInformation(h,HANDLE_FLAG_INHERIT,0);
+ }
+}
+
+
+void erl_sys_init(void)
+{
+ HANDLE handle;
+
+ noinherit_std_handle(STD_OUTPUT_HANDLE);
+ noinherit_std_handle(STD_INPUT_HANDLE);
+ noinherit_std_handle(STD_ERROR_HANDLE);
+
+
+ erts_smp_mtx_init(&sys_driver_data_lock, "sys_driver_data_lock");
+
+#ifdef ERTS_SMP
+ erts_smp_tsd_key_create(&win32_errstr_key);
+#endif
+ erts_smp_atomic_init(&pipe_creation_counter,0);
+ /*
+ * Test if we have named pipes or not.
+ */
+
+ switch (int_os_version.dwPlatformId) {
+ case VER_PLATFORM_WIN32_WINDOWS:
+ DEBUGF(("Running on Windows 95"));
+ use_named_pipes = FALSE;
+ break;
+ case VER_PLATFORM_WIN32_NT:
+ DEBUGF(("Running on Windows NT"));
+#ifdef DISABLE_NAMED_PIPES
+ use_named_pipes = FALSE;
+#else
+ use_named_pipes = TRUE;
+#endif
+ break;
+ default: /* Unsupported platform. */
+ exit(1);
+ }
+ DEBUGF((" %d.%d, build %d, %s\n",
+ int_os_version.dwMajorVersion, int_os_version.dwMinorVersion,
+ int_os_version.dwBuildNumber, int_os_version.szCSDVersion));
+
+ ASSERT(beam_module != NULL);
+ init_console();
+
+ /*
+ * The following makes sure that the current directory for the current drive
+ * is remembered (in the environment).
+ */
+
+ chdir(".");
+
+ /*
+ * Make sure that the standard error handle is valid.
+ */
+ handle = GetStdHandle(STD_ERROR_HANDLE);
+ if (handle == INVALID_HANDLE_VALUE || handle == 0) {
+ SetStdHandle(STD_ERROR_HANDLE, GetStdHandle(STD_OUTPUT_HANDLE));
+ }
+ erts_sys_init_float();
+ erts_init_check_io();
+
+ /* Suppress windows error message popups */
+ SetErrorMode(SetErrorMode(0) |
+ SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
+}
+
+#ifdef ERTS_SMP
+void
+erts_sys_schedule_interrupt(int set)
+{
+ erts_check_io_interrupt(set);
+}
+
+void
+erts_sys_schedule_interrupt_timed(int set, long msec)
+{
+ erts_check_io_interrupt_timed(set, msec);
+}
+#endif
+
+/*
+ * Called from schedule() when it runs out of runnable processes,
+ * or when Erlang code has performed INPUT_REDUCTIONS reduction
+ * steps. runnable == 0 iff there are no runnable Erlang processes.
+ */
+void
+erl_sys_schedule(int runnable)
+{
+#ifdef ERTS_SMP
+ erts_check_io(!runnable);
+ ERTS_SMP_LC_ASSERT(!ERTS_LC_IS_BLOCKING);
+#else
+ erts_check_io_interrupt(0);
+ if (runnable) {
+ erts_check_io(0); /* Poll for I/O */
+ check_async_ready(); /* Check async completions */
+ } else {
+ erts_check_io(check_async_ready() ? 0 : 1);
+ }
+#endif
+}
+
+#if defined(USE_THREADS) && !defined(ERTS_SMP)
+/*
+ * Async operation support.
+ */
+
+static ErlDrvEvent async_drv_event;
+
+void
+sys_async_ready(int fd)
+{
+ SetEvent((HANDLE)async_drv_event);
+}
+
+static int
+async_drv_init(void)
+{
+ async_drv_event = (ErlDrvEvent) NULL;
+ return 0;
+}
+
+static ErlDrvData
+async_drv_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
+{
+ if (async_drv_event != (ErlDrvEvent) NULL) {
+ return ERL_DRV_ERROR_GENERAL;
+ }
+ if ((async_drv_event = (ErlDrvEvent)CreateAutoEvent(FALSE)) == (ErlDrvEvent) NULL) {
+ return ERL_DRV_ERROR_GENERAL;
+ }
+
+ driver_select(port_num, async_drv_event, ERL_DRV_READ|ERL_DRV_USE, 1);
+ if (init_async(async_drv_event) < 0) {
+ return ERL_DRV_ERROR_GENERAL;
+ }
+ return (ErlDrvData)port_num;
+}
+
+static void
+async_drv_stop(ErlDrvData port_num)
+{
+ exit_async();
+ driver_select((ErlDrvPort)port_num, async_drv_event, ERL_DRV_READ|ERL_DRV_USE, 0);
+ /*CloseHandle((HANDLE)async_drv_event);*/
+ async_drv_event = (ErlDrvEvent) NULL;
+}
+
+
+static void
+async_drv_input(ErlDrvData port_num, ErlDrvEvent e)
+{
+ check_async_ready();
+
+ /*
+ * Our event is auto-resetting.
+ */
+}
+
+#endif
+
diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c
new file mode 100644
index 0000000000..ac4be3f316
--- /dev/null
+++ b/erts/emulator/sys/win32/sys_env.c
@@ -0,0 +1,261 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_sys_driver.h"
+#include "erl_alloc.h"
+
+static char* merge_environment(char *current, char *add);
+static char* arg_to_env(char **arg);
+static char** env_to_arg(char *env);
+static char** find_arg(char **arg, char *str);
+static int compare(const void *a, const void *b);
+
+static erts_smp_rwmtx_t environ_rwmtx;
+
+void
+erts_sys_env_init(void)
+{
+ erts_smp_rwmtx_init(&environ_rwmtx, "environ");
+}
+
+int
+erts_sys_putenv(char *key_value, int sep_ix)
+{
+ int res;
+ char sep = key_value[sep_ix];
+ ASSERT(sep == '=');
+ key_value[sep_ix] = '\0';
+ erts_smp_rwmtx_rwlock(&environ_rwmtx);
+ res = (SetEnvironmentVariable((LPCTSTR) key_value,
+ (LPCTSTR) &key_value[sep_ix+1]) ? 0 : 1);
+ erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+ key_value[sep_ix] = sep;
+ return res;
+}
+
+int
+erts_sys_getenv(char *key, char *value, size_t *size)
+{
+ size_t req_size = 0;
+ int res = 0;
+ DWORD new_size;
+
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ SetLastError(0);
+ new_size = GetEnvironmentVariable((LPCTSTR) key,
+ (LPTSTR) value,
+ (DWORD) *size);
+ res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ if (res < 0)
+ return res;
+ res = new_size > *size ? 1 : 0;
+ *size = new_size;
+ return res;
+}
+
+struct win32_getenv_state {
+ char *env;
+ char *next;
+};
+
+
+void init_getenv_state(GETENV_STATE *state)
+{
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ state->environment_strings = (char *) GetEnvironmentStrings();
+ state->next_string = state->environment_strings;
+}
+
+char *getenv_string(GETENV_STATE *state)
+{
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
+ if (state->next_string[0] == '\0')
+ return NULL;
+ else {
+ char *res = state->next_string;
+ state->next_string += sys_strlen(res) + 1;
+ return res;
+ }
+}
+
+void fini_getenv_state(GETENV_STATE *state)
+{
+ FreeEnvironmentStrings(state->environment_strings);
+ state->environment_strings = state->next_string = NULL;
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+}
+
+char*
+win_build_environment(char* new_env)
+{
+ if (new_env == NULL) {
+ return NULL;
+ } else {
+ char *tmp, *merged;
+
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ tmp = GetEnvironmentStrings();
+ merged = merge_environment(tmp, new_env);
+
+ FreeEnvironmentStrings(tmp);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ return merged;
+ }
+}
+
+static char*
+merge_environment(char *old, char *add)
+{
+ char **a_arg = env_to_arg(add);
+ char **c_arg = env_to_arg(old);
+ char *ret;
+ int i, j;
+
+ for(i = 0; c_arg[i] != NULL; ++i)
+ ;
+
+ for(j = 0; a_arg[j] != NULL; ++j)
+ ;
+
+ c_arg = erts_realloc(ERTS_ALC_T_TMP,
+ c_arg, (i+j+1) * sizeof(char *));
+
+ for(j = 0; a_arg[j] != NULL; ++j){
+ char **tmp;
+ char *current = a_arg[j];
+
+ if ((tmp = find_arg(c_arg, current)) != NULL) {
+ if (current[strlen(current)-1] != '=') {
+ *tmp = current;
+ } else {
+ *tmp = c_arg[--i];
+ c_arg[i] = NULL;
+ }
+ } else if (current[strlen(current)-1] != '=') {
+ c_arg[i++] = current;
+ c_arg[i] = NULL;
+ }
+ }
+ ret = arg_to_env(c_arg);
+ erts_free(ERTS_ALC_T_TMP, c_arg);
+ erts_free(ERTS_ALC_T_TMP, a_arg);
+ return ret;
+}
+
+static char**
+find_arg(char **arg, char *str)
+{
+ char *tmp;
+ int len;
+
+ if ((tmp = strchr(str, '=')) != NULL) {
+ tmp++;
+ len = tmp - str;
+ while (*arg != NULL){
+ if (_strnicmp(*arg, str, len) == 0){
+ return arg;
+ }
+ ++arg;
+ }
+ }
+ return NULL;
+}
+
+static int
+compare(const void *a, const void *b)
+{
+ char *s1 = *((char **) a);
+ char *s2 = *((char **) b);
+ char *e1 = strchr(s1,'=');
+ char *e2 = strchr(s2,'=');
+ int ret;
+ int len;
+
+ if(!e1)
+ e1 = s1 + strlen(s1);
+ if(!e2)
+ e2 = s2 + strlen(s2);
+
+ if((e1 - s1) > (e2 - s2))
+ len = (e2 - s2);
+ else
+ len = (e1 - s1);
+
+ ret = _strnicmp(s1,s2,len);
+ if (ret == 0)
+ return ((e1 - s1) - (e2 - s2));
+ else
+ return ret;
+}
+
+static char**
+env_to_arg(char *env)
+{
+ char **ret;
+ char *tmp;
+ int i;
+ int num_strings = 0;
+
+ for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1) {
+ ++num_strings;
+ }
+ ret = erts_alloc(ERTS_ALC_T_TMP, sizeof(char *) * (num_strings + 1));
+ i = 0;
+ for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1){
+ ret[i++] = tmp;
+ }
+ ret[i] = NULL;
+ return ret;
+}
+
+static char*
+arg_to_env(char **arg)
+{
+ char *block;
+ char *ptr;
+ int i;
+ int totlen = 1; /* extra '\0' */
+
+ for(i = 0; arg[i] != NULL; ++i) {
+ totlen += strlen(arg[i])+1;
+ }
+
+ /* sort the environment vector */
+ qsort(arg, i, sizeof(char *), &compare);
+
+ if (totlen == 1){
+ block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, 2);
+ block[0] = block[1] = '\0';
+ } else {
+ block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, totlen);
+ ptr = block;
+ for(i=0; arg[i] != NULL; ++i){
+ strcpy(ptr, arg[i]);
+ ptr += strlen(ptr)+1;
+ }
+ *ptr = '\0';
+ }
+ return block;
+}
diff --git a/erts/emulator/sys/win32/sys_float.c b/erts/emulator/sys/win32/sys_float.c
new file mode 100644
index 0000000000..9e67ca7f48
--- /dev/null
+++ b/erts/emulator/sys/win32/sys_float.c
@@ -0,0 +1,145 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/* Float conversions */
+
+#include "sys.h"
+#include "signal.h"
+
+/* global variable for floating point checks, (see sys.h) */
+/* Note! This is part of the interface Machine <---> sys.c */
+volatile int erl_fp_exception = 0;
+
+static void fpe_exception(int sig);
+
+void
+erts_sys_init_float(void)
+{
+}
+void erts_thread_init_float(void)
+{
+}
+void erts_thread_disable_fpe(void)
+{
+}
+
+/*
+ ** These two functions should maybe use localeconv() to pick up
+ ** the current radix character, but since it is uncertain how
+ ** expensive such a system call is, and since no-one has heard
+ ** of other radix characters than '.' and ',' an ad-hoc
+ ** low execution time solution is used instead.
+ */
+
+int
+sys_chars_to_double(char *buf, double *fp)
+{
+ char *s = buf, *t, *dp;
+
+ /* Robert says that something like this is what he really wanted:
+ * (The [.,] radix test is NOT what Robert wanted - it was added later)
+ *
+ * 7 == sscanf(Tbuf, "%[+-]%[0-9][.,]%[0-9]%[eE]%[+-]%[0-9]%s", ....);
+ * if (*s2 == 0 || *s3 == 0 || *s4 == 0 || *s6 == 0 || *s7)
+ * break;
+ */
+
+ /* Scan string to check syntax. */
+ if (*s == '+' || *s == '-') s++;
+ if (!isdigit(*s)) /* Leading digits. */
+ return -1;
+ while (isdigit(*s)) s++;
+ if (*s != '.' && *s != ',')/* Decimal part. */
+ return -1;
+ dp = s++; /* Remember decimal point pos just in case */
+ if (!isdigit(*s))
+ return -1;
+ while (isdigit(*s)) s++;
+ if (*s == 'e' || *s == 'E') {
+ /* There is an exponent. */
+ s++;
+ if (*s == '+' || *s == '-') s++;
+ if (!isdigit(*s))
+ return -1;
+ while (isdigit(*s)) s++;
+ }
+ if (*s) /* That should be it */
+ return -1;
+
+ errno = 0;
+ *fp = strtod(buf, &t);
+ if (t != s) { /* Whole string not scanned */
+ /* Try again with other radix char */
+ *dp = (*dp == '.') ? ',' : '.';
+ errno = 0;
+ *fp = strtod(buf, &t);
+ if (t != s) { /* Whole string not scanned */
+ return -1;
+ }
+ }
+ if (*fp < -1.0e-307 || 1.0e-307 < *fp) {
+ if (errno == ERANGE) {
+ return -1;
+ }
+ } else {
+ if (errno == ERANGE) {
+ /* Special case: Windows (at least some) regard very small
+ * i.e non-normalized numbers as a range error for strtod().
+ * But not for atof.
+ */
+ *fp = atof(buf);
+ }
+ }
+
+ return 0;
+}
+
+/*
+** Convert a double to ascii format 0.dddde[+|-]ddd
+** return number of characters converted
+*/
+
+int
+sys_double_to_chars(double fp, char *buf)
+{
+ char *s = buf;
+
+ (void) sprintf(buf, "%.20e", fp);
+ /* Search upto decimal point */
+ if (*s == '+' || *s == '-') s++;
+ while (isdigit(*s)) s++;
+ if (*s == ',') *s++ = '.'; /* Replace ',' with '.' */
+ /* Scan to end of string */
+ while (*s) s++;
+ return s-buf; /* i.e strlen(buf) */
+}
+
+int
+matherr(struct _exception *exc)
+{
+ erl_fp_exception = 1;
+ DEBUGF(("FP exception (matherr) (0x%x) (%d)\n", exc->type, erl_fp_exception));
+ return 1;
+}
+
+static void
+fpe_exception(int sig)
+{
+ erl_fp_exception = 1;
+ DEBUGF(("FP exception\n"));
+}
diff --git a/erts/emulator/sys/win32/sys_interrupt.c b/erts/emulator/sys/win32/sys_interrupt.c
new file mode 100644
index 0000000000..d2449a1bdb
--- /dev/null
+++ b/erts/emulator/sys/win32/sys_interrupt.c
@@ -0,0 +1,142 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Purpose: Interrupt handling in windows.
+ */
+#include "sys.h"
+#include "erl_alloc.h"
+#include "erl_driver.h"
+#include "../../drivers/win32/win_con.h"
+
+#if defined(__GNUC__)
+# define WIN_SYS_INLINE __inline__
+#elif defined(__WIN32__)
+# define WIN_SYS_INLINE __forceinline
+#endif
+
+#ifdef ERTS_SMP
+erts_smp_atomic_t erts_break_requested;
+#define ERTS_SET_BREAK_REQUESTED \
+ erts_smp_atomic_set(&erts_break_requested, (long) 1)
+#define ERTS_UNSET_BREAK_REQUESTED \
+ erts_smp_atomic_set(&erts_break_requested, (long) 0)
+#else
+volatile int erts_break_requested = 0;
+#define ERTS_SET_BREAK_REQUESTED (erts_break_requested = 1)
+#define ERTS_UNSET_BREAK_REQUESTED (erts_break_requested = 0)
+#endif
+
+extern int nohup;
+HANDLE erts_sys_break_event = NULL;
+
+void erts_do_break_handling(void)
+{
+ /*
+ * Most functions that do_break() calls are intentionally not thread safe;
+ * therefore, make sure that all threads but this one are blocked before
+ * proceeding!
+ */
+ erts_smp_block_system(0);
+ /* call the break handling function, reset the flag */
+ do_break();
+
+ ResetEvent(erts_sys_break_event);
+ ERTS_UNSET_BREAK_REQUESTED;
+
+ erts_smp_release_system();
+}
+
+
+BOOL WINAPI ctrl_handler_ignore_break(DWORD dwCtrlType)
+{
+ switch (dwCtrlType) {
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ return TRUE;
+ break;
+ case CTRL_LOGOFF_EVENT:
+ if (nohup)
+ return TRUE;
+ /* else pour through... */
+ case CTRL_CLOSE_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
+ erl_exit(0, "");
+ break;
+ }
+ return TRUE;
+}
+
+void erts_set_ignore_break(void) {
+ ConSetCtrlHandler(ctrl_handler_ignore_break);
+ SetConsoleCtrlHandler(ctrl_handler_ignore_break, TRUE);
+}
+
+BOOL WINAPI ctrl_handler_replace_intr(DWORD dwCtrlType)
+{
+ switch (dwCtrlType) {
+ case CTRL_C_EVENT:
+ return FALSE;
+ case CTRL_BREAK_EVENT:
+ SetEvent(erts_sys_break_event);
+ break;
+ case CTRL_LOGOFF_EVENT:
+ if (nohup)
+ return TRUE;
+ /* else pour through... */
+ case CTRL_CLOSE_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
+ erl_exit(0, "");
+ break;
+ }
+ return TRUE;
+}
+
+
+/* Don't use ctrl-c for break handler but let it be
+ used by the shell instead (see user_drv.erl) */
+void erts_replace_intr(void) {
+ ConSetCtrlHandler(ctrl_handler_replace_intr);
+ SetConsoleCtrlHandler(ctrl_handler_replace_intr, TRUE);
+}
+
+BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
+{
+ switch (dwCtrlType) {
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ SetEvent(erts_sys_break_event);
+ break;
+ case CTRL_LOGOFF_EVENT:
+ if (nohup)
+ return TRUE;
+ /* else pour through... */
+ case CTRL_CLOSE_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
+ erl_exit(0, "");
+ break;
+ }
+ return TRUE;
+}
+
+void init_break_handler()
+{
+ ConSetCtrlHandler(ctrl_handler);
+ SetConsoleCtrlHandler(ctrl_handler, TRUE);
+}
+
diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c
new file mode 100644
index 0000000000..50e43065b5
--- /dev/null
+++ b/erts/emulator/sys/win32/sys_time.c
@@ -0,0 +1,96 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Purpose: System-dependent time functions.
+ */
+
+#include "sys.h"
+#include "assert.h"
+
+#ifdef __GNUC__
+#define LL_LITERAL(X) X##LL
+#else
+#define LL_LITERAL(X) X##i64
+#endif
+
+/******************* Routines for time measurement *********************/
+
+#define EPOCH_JULIAN_DIFF LL_LITERAL(11644473600)
+
+static SysHrTime wrap = 0;
+static DWORD last_tick_count = 0;
+
+int
+sys_init_time(void)
+{
+ return 1;
+}
+
+void
+sys_gettimeofday(SysTimeval *tv)
+{
+ SYSTEMTIME t;
+ FILETIME ft;
+ LONGLONG lft;
+
+ GetSystemTime(&t);
+ SystemTimeToFileTime(&t, &ft);
+ memcpy(&lft, &ft, sizeof(lft));
+ tv->tv_usec = (long) ((lft / LL_LITERAL(10)) % LL_LITERAL(1000000));
+ tv->tv_sec = (long) ((lft / LL_LITERAL(10000000)) - EPOCH_JULIAN_DIFF);
+}
+
+SysHrTime
+sys_gethrtime(void)
+{
+ DWORD ticks = (SysHrTime) (GetTickCount() & 0x7FFFFFFF);
+ if (ticks < (SysHrTime) last_tick_count) {
+ wrap += LL_LITERAL(1) << 31;
+ }
+ last_tick_count = ticks;
+ return ((((LONGLONG) ticks) + wrap) * LL_LITERAL(1000000));
+}
+
+clock_t
+sys_times(SysTimes *buffer) {
+ clock_t kernel_ticks = (GetTickCount() /
+ (1000 / SYS_CLK_TCK)) & 0x7FFFFFFF;
+ FILETIME dummy;
+ LONGLONG user;
+ LONGLONG system;
+
+ buffer->tms_utime = buffer->tms_stime = buffer->tms_cutime =
+ buffer->tms_cstime = 0;
+
+ if (GetProcessTimes(GetCurrentProcess(), &dummy, &dummy,
+ (FILETIME *) &system, (FILETIME *) &user) == 0)
+ return kernel_ticks;
+ system /= (LONGLONG)(10000000 / SYS_CLK_TCK);
+ user /= (LONGLONG)(10000000 / SYS_CLK_TCK);
+
+ buffer->tms_utime = (clock_t) (user & LL_LITERAL(0x7FFFFFFF));
+ buffer->tms_stime = (clock_t) (system & LL_LITERAL(0x7FFFFFFF));
+ return kernel_ticks;
+}
+
+
+
+
+
+