From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- erts/emulator/sys/win32/dosmap.c | 282 +++ erts/emulator/sys/win32/driver_int.h | 39 + erts/emulator/sys/win32/erl.def | 4 + erts/emulator/sys/win32/erl_main.c | 29 + erts/emulator/sys/win32/erl_poll.c | 1361 ++++++++++++ erts/emulator/sys/win32/erl_win32_sys_ddll.c | 206 ++ erts/emulator/sys/win32/erl_win_dyn_driver.h | 489 ++++ erts/emulator/sys/win32/erl_win_sys.h | 212 ++ erts/emulator/sys/win32/sys.c | 3093 ++++++++++++++++++++++++++ erts/emulator/sys/win32/sys_env.c | 261 +++ erts/emulator/sys/win32/sys_float.c | 145 ++ erts/emulator/sys/win32/sys_interrupt.c | 142 ++ erts/emulator/sys/win32/sys_time.c | 96 + 13 files changed, 6359 insertions(+) create mode 100644 erts/emulator/sys/win32/dosmap.c create mode 100644 erts/emulator/sys/win32/driver_int.h create mode 100644 erts/emulator/sys/win32/erl.def create mode 100644 erts/emulator/sys/win32/erl_main.c create mode 100644 erts/emulator/sys/win32/erl_poll.c create mode 100644 erts/emulator/sys/win32/erl_win32_sys_ddll.c create mode 100644 erts/emulator/sys/win32/erl_win_dyn_driver.h create mode 100644 erts/emulator/sys/win32/erl_win_sys.h create mode 100644 erts/emulator/sys/win32/sys.c create mode 100644 erts/emulator/sys/win32/sys_env.c create mode 100644 erts/emulator/sys/win32/sys_float.c create mode 100644 erts/emulator/sys/win32/sys_interrupt.c create mode 100644 erts/emulator/sys/win32/sys_time.c (limited to 'erts/emulator/sys/win32') 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 +#include +#include + +/* 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;inum_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 + +#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 +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#ifndef __GNUC__ +#include +#endif +#include "erl_errno.h" +#include +#include +#include +#include +#include +#include +#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 +#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 +#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 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; +} + + + + + + -- cgit v1.2.3