summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXavier Leroy <xavier.leroy@inria.fr>2008-07-29 08:31:41 +0000
committerXavier Leroy <xavier.leroy@inria.fr>2008-07-29 08:31:41 +0000
commit776ae225a0cc5fa44b9279f81d45e9fd3dfa3cca (patch)
treef647c865d1681b0b8678ebb0c53c731055064c03
parentdf023f535b9b4bb051cbce6dc39ea3b835bb80f1 (diff)
ocamldebug under Win32 (S. Le Gall, Lexifi)
git-svn-id: http://caml.inria.fr/svn/ocaml/trunk@8955 f963ae5c-01c2-4b8c-9fe0-0dff7051ff02
-rw-r--r--Makefile.nt13
-rw-r--r--README.win329
-rw-r--r--byterun/Makefile.nt4
-rw-r--r--byterun/debugger.c77
-rw-r--r--config/Makefile.mingw10
-rw-r--r--config/Makefile.msvc12
-rw-r--r--config/Makefile.msvc646
-rw-r--r--debugger/Makefile104
-rw-r--r--debugger/Makefile.nt17
-rw-r--r--debugger/Makefile.shared116
-rw-r--r--debugger/command_line.ml14
-rw-r--r--debugger/debugcom.ml11
-rw-r--r--debugger/debugger_config.ml11
-rw-r--r--debugger/exec.ml7
-rw-r--r--debugger/main.ml11
-rw-r--r--debugger/program_loading.ml39
-rw-r--r--debugger/program_management.ml1
-rw-r--r--debugger/unix_tools.ml4
-rw-r--r--myocamlbuild.ml4
-rw-r--r--otherlibs/win32unix/Makefile.nt5
-rw-r--r--otherlibs/win32unix/dllunix.dlib1
-rw-r--r--otherlibs/win32unix/libunix.clib1
-rw-r--r--otherlibs/win32unix/select.c1076
-rw-r--r--otherlibs/win32unix/startup.c11
24 files changed, 1356 insertions, 208 deletions
diff --git a/Makefile.nt b/Makefile.nt
index 99d4b6cdc..855274bed 100644
--- a/Makefile.nt
+++ b/Makefile.nt
@@ -120,7 +120,7 @@ defaultentry:
@echo "Please refer to the installation instructions in file README.win32."
# Recompile the system using the bootstrap compiler
-all: runtime ocamlc ocamllex ocamlyacc ocamltools library ocaml otherlibraries ocamldoc.byte ocamlbuild.byte camlp4out win32gui
+all: runtime ocamlc ocamllex ocamlyacc ocamltools library ocaml otherlibraries ocamldoc.byte ocamlbuild.byte camlp4out $(DEBUGGER) win32gui
# The compilation of ocaml will fail if the runtime has changed.
# Never mind, just do make bootstrap to reach fixpoint again.
@@ -232,6 +232,8 @@ installbyt:
cd ocamldoc ; $(MAKEREC) install
mkdir -p $(STUBLIBDIR)
for i in $(OTHERLIBRARIES); do $(MAKEREC) -C otherlibs/$$i install; done
+ if test -f debugger/ocamldebug.exe; then (cd debugger; $(MAKEREC) install); \
+ else :; fi
cd win32caml ; $(MAKE) install
./build/partial-install.sh
cp config/Makefile $(LIBDIR)/Makefile.config
@@ -581,6 +583,15 @@ clean::
alldepend::
for i in $(OTHERLIBRARIES); do $(MAKEREC) -C otherlibs/$$i depend; done
+# The replay debugger
+
+ocamldebugger: ocamlc ocamlyacc ocamllex
+ cd debugger; $(MAKEREC) all
+partialclean::
+ cd debugger; $(MAKEREC) clean
+alldepend::
+ cd debugger; $(MAKEREC) depend
+
# Camlp4
camlp4out: ocamlc otherlibraries ocamlbuild-partial-boot ocamlbuild.byte
diff --git a/README.win32 b/README.win32
index 782aa120a..26a3e6703 100644
--- a/README.win32
+++ b/README.win32
@@ -22,7 +22,7 @@ Third-party software required
Speed of bytecode interpreter 70% 100% 100%
-Replay debugger no no yes
+Replay debugger yes (**) yes (**) yes
The Unix library partial partial full
@@ -37,6 +37,9 @@ the GPL. Thus, these .exe files can only be distributed under a license
that is compatible with the GPL. Executables generated by MSVC or by
MinGW have no such restrictions.
+(**) The debugger is supported but the "replay" function of it are not enabled.
+Other functions are available (step, goto, run...).
+
The remainder of this document gives more information on each port.
------------------------------------------------------------------------------
@@ -150,7 +153,7 @@ Unix/GCC or Cygwin or Mingw on similar hardware.
* Libraries available in this port: "num", "str", "threads", "graphics",
"labltk", and large parts of "unix".
-* The replay debugger is not supported.
+* The replay debugger is partially supported.
CREDITS:
@@ -247,7 +250,7 @@ NOTES:
* Libraries available in this port: "num", "str", "threads", "graphics",
"labltk", and large parts of "unix".
-* The replay debugger is not supported.
+* The replay debugger is partially supported.
------------------------------------------------------------------------------
diff --git a/byterun/Makefile.nt b/byterun/Makefile.nt
index 1dbda5852..455de6fa6 100644
--- a/byterun/Makefile.nt
+++ b/byterun/Makefile.nt
@@ -22,10 +22,10 @@ OBJS=$(COMMONOBJS:.o=.$(O)) win32.$(O) main.$(O)
DOBJS=$(OBJS:.$(O)=.$(DBGO)) instrtrace.$(DBGO)
ocamlrun$(EXE): libcamlrun.$(A) prims.$(O)
- $(MKEXE) -o ocamlrun$(EXE) prims.$(O) libcamlrun.$(A)
+ $(MKEXE) -o ocamlrun$(EXE) prims.$(O) $(call SYSLIB,ws2_32) libcamlrun.$(A)
ocamlrund$(EXE): libcamlrund.$(A) prims.$(O) main.$(O)
- $(MKEXE) -o ocamlrun$(EXE) $(BYTECCDBGCOMPOPTS) prims.$(O) libcamlrund.$(A)
+ $(MKEXE) -o ocamlrun$(EXE) $(BYTECCDBGCOMPOPTS) prims.$(O) $(call SYSLIB,ws2_32) libcamlrund.$(A)
libcamlrun.$(A): $(OBJS)
$(call MKLIB,libcamlrun.$(A),$(OBJS))
diff --git a/byterun/debugger.c b/byterun/debugger.c
index d6d3a2b5e..38b1923e6 100644
--- a/byterun/debugger.c
+++ b/byterun/debugger.c
@@ -15,6 +15,10 @@
/* Interface with the debugger */
+#ifdef _WIN32
+#include <io.h>
+#endif /* _WIN32 */
+
#include <string.h>
#include "config.h"
@@ -32,7 +36,7 @@
int caml_debugger_in_use = 0;
uintnat caml_event_count;
-#if !defined(HAS_SOCKETS) || defined(_WIN32)
+#if !defined(HAS_SOCKETS)
void caml_debugger_init(void)
{
@@ -49,17 +53,26 @@ void caml_debugger(enum event_kind event)
#endif
#include <errno.h>
#include <sys/types.h>
+#ifndef _WIN32
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
+#else
+#define ATOM ATOM_WS
+#include <winsock.h>
+#undef ATOM
+#include <process.h>
+#endif
static int sock_domain; /* Socket domain for the debugger */
static union { /* Socket address for the debugger */
struct sockaddr s_gen;
+#ifndef _WIN32
struct sockaddr_un s_unix;
+#endif
struct sockaddr_in s_inet;
} sock_addr;
static int sock_addr_len; /* Length of sock_addr */
@@ -72,16 +85,46 @@ static char *dbg_addr = "(none)";
static void open_connection(void)
{
+#ifdef _WIN32
+ /* Set socket to synchronous mode so that file descriptor-oriented
+ functions (read()/write() etc.) can be used */
+
+ int oldvalue, oldvaluelen, newvalue, retcode;
+ oldvaluelen = sizeof(oldvalue);
+ retcode = getsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
+ (char *) &oldvalue, &oldvaluelen);
+ if (retcode == 0) {
+ newvalue = SO_SYNCHRONOUS_NONALERT;
+ setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
+ (char *) &newvalue, sizeof(newvalue));
+ }
+#endif
dbg_socket = socket(sock_domain, SOCK_STREAM, 0);
+#ifdef _WIN32
+ if (retcode == 0) {
+ /* Restore initial mode */
+ setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
+ (char *) &oldvalue, oldvaluelen);
+ }
+#endif
if (dbg_socket == -1 ||
connect(dbg_socket, &sock_addr.s_gen, sock_addr_len) == -1){
caml_fatal_error_arg2 ("cannot connect to debugger at %s", dbg_addr,
"error: %s\n", strerror (errno));
}
+#ifdef _WIN32
+ dbg_socket = _open_osfhandle(dbg_socket, 0);
+ if (dbg_socket == -1)
+ caml_fatal_error("_open_osfhandle failed");
+#endif
dbg_in = caml_open_descriptor_in(dbg_socket);
dbg_out = caml_open_descriptor_out(dbg_socket);
if (!caml_debugger_in_use) caml_putword(dbg_out, -1); /* first connection */
+#ifdef _WIN32
+ caml_putword(dbg_out, _getpid());
+#else
caml_putword(dbg_out, getpid());
+#endif
caml_flush(dbg_out);
}
@@ -92,6 +135,20 @@ static void close_connection(void)
dbg_socket = -1; /* was closed by caml_close_channel */
}
+#ifdef _WIN32
+static void winsock_startup(void)
+{
+ WSADATA wsaData;
+ int err = WSAStartup(MAKEWORD(2, 0), &wsaData);
+ if (err) caml_fatal_error("WSAStartup failed");
+}
+
+static void winsock_cleanup(void)
+{
+ WSACleanup();
+}
+#endif
+
void caml_debugger_init(void)
{
char * address;
@@ -103,12 +160,17 @@ void caml_debugger_init(void)
if (address == NULL) return;
dbg_addr = address;
+#ifdef _WIN32
+ winsock_startup();
+ (void)atexit(winsock_cleanup);
+#endif
/* Parse the address */
port = NULL;
for (p = address; *p != 0; p++) {
if (*p == ':') { *p = 0; port = p+1; break; }
}
if (port == NULL) {
+#ifndef _WIN32
/* Unix domain */
sock_domain = PF_UNIX;
sock_addr.s_unix.sun_family = AF_UNIX;
@@ -117,6 +179,9 @@ void caml_debugger_init(void)
sock_addr_len =
((char *)&(sock_addr.s_unix.sun_path) - (char *)&(sock_addr.s_unix))
+ strlen(address);
+#else
+ caml_fatal_error("Unix sockets not supported");
+#endif
} else {
/* Internet domain */
sock_domain = PF_INET;
@@ -241,6 +306,7 @@ void caml_debugger(enum event_kind event)
caml_set_instruction(caml_start_code + pos, caml_saved_code[pos]);
break;
case REQ_CHECKPOINT:
+#ifndef _WIN32
i = fork();
if (i == 0) {
close_connection(); /* Close parent connection. */
@@ -249,6 +315,10 @@ void caml_debugger(enum event_kind event)
caml_putword(dbg_out, i);
caml_flush(dbg_out);
}
+#else
+ caml_fatal_error("error: REQ_CHECKPOINT command");
+ exit(-1);
+#endif
break;
case REQ_GO:
caml_event_count = caml_getword(dbg_in);
@@ -257,7 +327,12 @@ void caml_debugger(enum event_kind event)
exit(0);
break;
case REQ_WAIT:
+#ifndef _WIN32
wait(NULL);
+#else
+ caml_fatal_error("Fatal error: REQ_WAIT command");
+ exit(-1);
+#endif
break;
case REQ_INITIAL_FRAME:
frame = caml_extern_sp + 1;
diff --git a/config/Makefile.mingw b/config/Makefile.mingw
index 1583ccebd..0a2bb0a03 100644
--- a/config/Makefile.mingw
+++ b/config/Makefile.mingw
@@ -66,7 +66,7 @@ ASPP=gcc
ASPPPROFFLAGS=
PROFILING=noprof
DYNLINKOPTS=
-DEBUGGER=
+DEBUGGER=ocamldebugger
CC_PROFILE=
SYSTHREAD_SUPPORT=true
EXTRALIBS=
@@ -87,8 +87,8 @@ BYTECCLINKOPTS=
DLLCCCOMPOPTS=-O -mms-bitfields -Wall -Wno-unused -DCAML_DLL
### Libraries needed
-BYTECCLIBS=
-NATIVECCLIBS=
+BYTECCLIBS=-lws2_32
+NATIVECCLIBS=-lws2_32
### How to invoke the C preprocessor
CPP=$(BYTECC) -E
@@ -149,8 +149,8 @@ BNG_ASM_LEVEL=1
# There must be no spaces or special characters in $(TK_ROOT)
TK_ROOT=c:/tcl
TK_DEFS=-I$(TK_ROOT)/include
-TK_LINK=$(TK_ROOT)/bin/tk83.dll $(TK_ROOT)/bin/tcl83.dll -lwsock32
-#TK_LINK=$(TK_ROOT)/lib/tk84.lib $(TK_ROOT)/lib/tcl84.lib -lwsock32
+TK_LINK=$(TK_ROOT)/bin/tk83.dll $(TK_ROOT)/bin/tcl83.dll -lws2_32
+#TK_LINK=$(TK_ROOT)/lib/tk84.lib $(TK_ROOT)/lib/tcl84.lib -lws2_32
############# Aliases for common commands
diff --git a/config/Makefile.msvc b/config/Makefile.msvc
index aafcf0a60..a16b86c4d 100644
--- a/config/Makefile.msvc
+++ b/config/Makefile.msvc
@@ -65,7 +65,7 @@ ASPP=
ASPPPROFFLAGS=
PROFILING=noprof
DYNLINKOPTS=
-DEBUGGER=
+DEBUGGER=ocamldebugger
CC_PROFILE=
SYSTHREAD_SUPPORT=true
EXTRALIBS=
@@ -86,8 +86,8 @@ BYTECCLINKOPTS=/MD /F16777216
DLLCCCOMPOPTS=/Ox /MD
### Libraries needed
-BYTECCLIBS=advapi32.lib
-NATIVECCLIBS=advapi32.lib
+BYTECCLIBS=advapi32.lib ws2_32.lib
+NATIVECCLIBS=advapi32.lib ws2_32.lib
### How to invoke the C preprocessor
CPP=cl /nologo /EP
@@ -152,11 +152,11 @@ TK_DEFS=-I$(TK_ROOT)/include
# produced by OCaml, and is therefore required for binary distribution
# of these libraries. However, $(TK_ROOT) must be added to the LIB
# environment variable, as described in README.win32.
-#TK_LINK=tk84.lib tcl84.lib wsock32.lib
-TK_LINK=tk83.lib tcl83.lib wsock32.lib
+#TK_LINK=tk84.lib tcl84.lib ws2_32.lib
+TK_LINK=tk83.lib tcl83.lib ws2_32.lib
# An alternative definition that avoids mucking with the LIB variable,
# but hard-wires the Tcl/Tk location in the binaries
-# TK_LINK=$(TK_ROOT)/tk83.lib $(TK_ROOT)/tcl83.lib wsock32.lib
+# TK_LINK=$(TK_ROOT)/tk83.lib $(TK_ROOT)/tcl83.lib ws2_32.lib
############# Aliases for common commands
diff --git a/config/Makefile.msvc64 b/config/Makefile.msvc64
index cef571117..a067fd065 100644
--- a/config/Makefile.msvc64
+++ b/config/Makefile.msvc64
@@ -66,7 +66,7 @@ ASPP=
ASPPPROFFLAGS=
PROFILING=noprof
DYNLINKOPTS=
-DEBUGGER=
+DEBUGGER=ocamldebugger
CC_PROFILE=
SYSTHREAD_SUPPORT=true
CMXS=cmxs
@@ -90,8 +90,8 @@ DLLCCCOMPOPTS=/Ox /MD
### Libraries needed
EXTRALIBS=bufferoverflowu.lib
-BYTECCLIBS=advapi32.lib $(EXTRALIBS)
-NATIVECCLIBS=advapi32.lib $(EXTRALIBS)
+BYTECCLIBS=advapi32.lib ws2_32.lib $(EXTRALIBS)
+NATIVECCLIBS=advapi32.lib ws2_32.lib $(EXTRALIBS)
### How to invoke the C preprocessor
CPP=cl /nologo /EP
diff --git a/debugger/Makefile b/debugger/Makefile
index a1faabd1f..3ff1b54aa 100644
--- a/debugger/Makefile
+++ b/debugger/Makefile
@@ -12,105 +12,5 @@
# $Id$
-include ../config/Makefile
-
-CAMLC=../ocamlcomp.sh
-COMPFLAGS=-warn-error A $(INCLUDES)
-LINKFLAGS=-linkall -I ../otherlibs/unix
-CAMLYACC=../boot/ocamlyacc
-YACCFLAGS=
-CAMLLEX=../boot/ocamlrun ../boot/ocamllex
-CAMLDEP=../boot/ocamlrun ../tools/ocamldep
-DEPFLAGS=$(INCLUDES)
-
-INCLUDES=\
- -I ../utils -I ../parsing -I ../typing -I ../bytecomp -I ../toplevel \
- -I ../otherlibs/unix
-
-OTHEROBJS=\
- ../otherlibs/unix/unix.cma \
- ../utils/misc.cmo ../utils/config.cmo \
- ../utils/tbl.cmo ../utils/clflags.cmo ../utils/consistbl.cmo \
- ../parsing/longident.cmo \
- ../typing/ident.cmo ../typing/path.cmo ../typing/types.cmo \
- ../typing/btype.cmo ../typing/primitive.cmo ../typing/typedtree.cmo \
- ../typing/subst.cmo ../typing/predef.cmo \
- ../typing/datarepr.cmo ../typing/env.cmo ../typing/oprint.cmo \
- ../typing/ctype.cmo ../typing/printtyp.cmo ../typing/mtype.cmo \
- ../bytecomp/runtimedef.cmo ../bytecomp/bytesections.cmo \
- ../bytecomp/dll.cmo ../bytecomp/meta.cmo ../bytecomp/symtable.cmo \
- ../bytecomp/opcodes.cmo \
- ../toplevel/genprintval.cmo
-
-
-OBJS=\
- dynlink.cmo \
- int64ops.cmo \
- primitives.cmo \
- unix_tools.cmo \
- debugger_config.cmo \
- envaux.cmo \
- parameters.cmo \
- lexer.cmo \
- input_handling.cmo \
- question.cmo \
- debugcom.cmo \
- exec.cmo \
- source.cmo \
- pos.cmo \
- checkpoints.cmo \
- events.cmo \
- symbols.cmo \
- breakpoints.cmo \
- trap_barrier.cmo \
- history.cmo \
- program_loading.cmo \
- printval.cmo \
- show_source.cmo \
- time_travel.cmo \
- program_management.cmo \
- frames.cmo \
- eval.cmo \
- show_information.cmo \
- loadprinter.cmo \
- parser.cmo \
- command_line.cmo \
- main.cmo
-
-all: ocamldebug$(EXE)
-
-ocamldebug$(EXE): $(OBJS) $(OTHEROBJS)
- $(CAMLC) $(LINKFLAGS) -o ocamldebug$(EXE) -linkall $(OTHEROBJS) $(OBJS)
-
-install:
- cp ocamldebug$(EXE) $(BINDIR)/ocamldebug$(EXE)
-
-clean::
- rm -f ocamldebug$(EXE)
- rm -f *.cmo *.cmi
-
-.SUFFIXES:
-.SUFFIXES: .ml .cmo .mli .cmi
-
-.ml.cmo:
- $(CAMLC) -c $(COMPFLAGS) $<
-
-.mli.cmi:
- $(CAMLC) -c $(COMPFLAGS) $<
-
-depend: beforedepend
- $(CAMLDEP) $(DEPFLAGS) *.mli *.ml > .depend
-
-lexer.ml: lexer.mll
- $(CAMLLEX) lexer.mll
-clean::
- rm -f lexer.ml
-beforedepend:: lexer.ml
-
-parser.ml parser.mli: parser.mly
- $(CAMLYACC) parser.mly
-clean::
- rm -f parser.ml parser.mli
-beforedepend:: parser.ml parser.mli
-
-include .depend
+UNIXDIR=../otherlibs/unix
+include Makefile.shared
diff --git a/debugger/Makefile.nt b/debugger/Makefile.nt
new file mode 100644
index 000000000..3630d32e0
--- /dev/null
+++ b/debugger/Makefile.nt
@@ -0,0 +1,17 @@
+#########################################################################
+# #
+# Objective Caml #
+# #
+# Xavier Leroy, projet Cristal, INRIA Rocquencourt #
+# #
+# Copyright 1999 Institut National de Recherche en Informatique et #
+# en Automatique. All rights reserved. This file is distributed #
+# under the terms of the Q Public License version 1.0. #
+# #
+#########################################################################
+
+# $Id$
+
+UNIXDIR=../otherlibs/win32unix
+include Makefile.shared
+
diff --git a/debugger/Makefile.shared b/debugger/Makefile.shared
new file mode 100644
index 000000000..4ed986a54
--- /dev/null
+++ b/debugger/Makefile.shared
@@ -0,0 +1,116 @@
+#########################################################################
+# #
+# Objective Caml #
+# #
+# Xavier Leroy, projet Cristal, INRIA Rocquencourt #
+# #
+# Copyright 1999 Institut National de Recherche en Informatique et #
+# en Automatique. All rights reserved. This file is distributed #
+# under the terms of the Q Public License version 1.0. #
+# #
+#########################################################################
+
+# $Id$
+
+include ../config/Makefile
+
+CAMLC=../ocamlcomp.sh
+COMPFLAGS=-warn-error A $(INCLUDES)
+LINKFLAGS=-linkall -I $(UNIXDIR)
+CAMLYACC=../boot/ocamlyacc
+YACCFLAGS=
+CAMLLEX=../boot/ocamlrun ../boot/ocamllex
+CAMLDEP=../boot/ocamlrun ../tools/ocamldep
+DEPFLAGS=$(INCLUDES)
+
+INCLUDES=\
+ -I ../utils -I ../parsing -I ../typing -I ../bytecomp -I ../toplevel \
+ -I $(UNIXDIR)
+
+OTHEROBJS=\
+ $(UNIXDIR)/unix.cma \
+ ../utils/misc.cmo ../utils/config.cmo \
+ ../utils/tbl.cmo ../utils/clflags.cmo ../utils/consistbl.cmo \
+ ../parsing/longident.cmo \
+ ../typing/ident.cmo ../typing/path.cmo ../typing/types.cmo \
+ ../typing/btype.cmo ../typing/primitive.cmo ../typing/typedtree.cmo \
+ ../typing/subst.cmo ../typing/predef.cmo \
+ ../typing/datarepr.cmo ../typing/env.cmo ../typing/oprint.cmo \
+ ../typing/ctype.cmo ../typing/printtyp.cmo ../typing/mtype.cmo \
+ ../bytecomp/runtimedef.cmo ../bytecomp/bytesections.cmo \
+ ../bytecomp/dll.cmo ../bytecomp/meta.cmo ../bytecomp/symtable.cmo \
+ ../bytecomp/opcodes.cmo \
+ ../toplevel/genprintval.cmo
+
+
+OBJS=\
+ dynlink.cmo \
+ int64ops.cmo \
+ primitives.cmo \
+ unix_tools.cmo \
+ debugger_config.cmo \
+ envaux.cmo \
+ parameters.cmo \
+ lexer.cmo \
+ input_handling.cmo \
+ question.cmo \
+ debugcom.cmo \
+ exec.cmo \
+ source.cmo \
+ pos.cmo \
+ checkpoints.cmo \
+ events.cmo \
+ symbols.cmo \
+ breakpoints.cmo \
+ trap_barrier.cmo \
+ history.cmo \
+ program_loading.cmo \
+ printval.cmo \
+ show_source.cmo \
+ time_travel.cmo \
+ program_management.cmo \
+ frames.cmo \
+ eval.cmo \
+ show_information.cmo \
+ loadprinter.cmo \
+ parser.cmo \
+ command_line.cmo \
+ main.cmo
+
+all: ocamldebug$(EXE)
+
+ocamldebug$(EXE): $(OBJS) $(OTHEROBJS)
+ $(CAMLC) $(LINKFLAGS) -o ocamldebug$(EXE) -linkall $(OTHEROBJS) $(OBJS)
+
+install:
+ cp ocamldebug$(EXE) $(BINDIR)/ocamldebug$(EXE)
+
+clean::
+ rm -f ocamldebug$(EXE)
+ rm -f *.cmo *.cmi
+
+.SUFFIXES:
+.SUFFIXES: .ml .cmo .mli .cmi
+
+.ml.cmo:
+ $(CAMLC) -c $(COMPFLAGS) $<
+
+.mli.cmi:
+ $(CAMLC) -c $(COMPFLAGS) $<
+
+depend: beforedepend
+ $(CAMLDEP) $(DEPFLAGS) *.mli *.ml > .depend
+
+lexer.ml: lexer.mll
+ $(CAMLLEX) lexer.mll
+clean::
+ rm -f lexer.ml
+beforedepend:: lexer.ml
+
+parser.ml parser.mli: parser.mly
+ $(CAMLYACC) parser.mly
+clean::
+ rm -f parser.ml parser.mli
+beforedepend:: parser.ml parser.mli
+
+include .depend
diff --git a/debugger/command_line.ml b/debugger/command_line.ml
index cfbdee303..f37d529b3 100644
--- a/debugger/command_line.ml
+++ b/debugger/command_line.ml
@@ -76,6 +76,13 @@ let error text =
eprintf "%s@." text;
raise Toplevel
+let check_not_windows feature =
+ match Sys.os_type with
+ | "Win32" ->
+ error ("'"^feature^"' feature not supported on Windows")
+ | _ ->
+ ()
+
let eol =
end_of_line Lexer.lexeme
@@ -220,7 +227,7 @@ let instr_shell ppf lexbuf =
let instr_pwd ppf lexbuf =
eol lexbuf;
- ignore(system "/bin/pwd")
+ fprintf ppf "%s@." (Sys.getcwd ())
let instr_dir ppf lexbuf =
let new_directory = argument_list_eol argument lexbuf in
@@ -254,6 +261,7 @@ let instr_run ppf lexbuf =
let instr_reverse ppf lexbuf =
eol lexbuf;
+ check_not_windows "reverse";
ensure_loaded ();
reset_named_values();
back_run ();
@@ -276,6 +284,7 @@ let instr_back ppf lexbuf =
| None -> _1
| Some x -> x
in
+ check_not_windows "backstep";
ensure_loaded ();
reset_named_values();
step (_0 -- step_count);
@@ -301,6 +310,7 @@ let instr_next ppf lexbuf =
let instr_start ppf lexbuf =
eol lexbuf;
+ check_not_windows "start";
ensure_loaded ();
reset_named_values();
start ();
@@ -312,6 +322,7 @@ let instr_previous ppf lexbuf =
| None -> 1
| Some x -> x
in
+ check_not_windows "previous";
ensure_loaded ();
reset_named_values();
previous step_count;
@@ -672,6 +683,7 @@ let instr_last ppf lexbuf =
| None -> _1
| Some x -> x
in
+ check_not_windows "last";
reset_named_values();
go_to (History.previous_time count);
show_current_event ppf
diff --git a/debugger/debugcom.ml b/debugger/debugcom.ml
index edec45472..307f4258d 100644
--- a/debugger/debugcom.ml
+++ b/debugger/debugcom.ml
@@ -99,10 +99,13 @@ let rec do_go n =
(* Perform a checkpoint *)
let do_checkpoint () =
- output_char !conn.io_out 'c';
- flush !conn.io_out;
- let pid = input_binary_int !conn.io_in in
- if pid = -1 then Checkpoint_failed else Checkpoint_done pid
+ match Sys.os_type with
+ "Win32" -> failwith "do_checkpoint"
+ | _ ->
+ output_char !conn.io_out 'c';
+ flush !conn.io_out;
+ let pid = input_binary_int !conn.io_in in
+ if pid = -1 then Checkpoint_failed else Checkpoint_done pid
(* Kill the given process. *)
let stop chan =
diff --git a/debugger/debugger_config.ml b/debugger/debugger_config.ml
index fa6fd7018..13e3f086c 100644
--- a/debugger/debugger_config.ml
+++ b/debugger/debugger_config.ml
@@ -51,7 +51,10 @@ let event_mark_before = "<|b|>"
let event_mark_after = "<|a|>"
(* Name of shell used to launch the debuggee *)
-let shell = "/bin/sh"
+let shell =
+ match Sys.os_type with
+ "Win32" -> "cmd"
+ | _ -> "/bin/sh"
(* Name of the Objective Caml runtime. *)
let runtime_program = "ocamlrun"
@@ -71,5 +74,7 @@ let checkpoint_small_step = ref (~~ "1000")
let checkpoint_max_count = ref 15
(* Whether to keep checkpoints or not. *)
-let make_checkpoints = ref true
-
+let make_checkpoints = ref
+ (match Sys.os_type with
+ "Win32" -> false
+ | _ -> true)
diff --git a/debugger/exec.ml b/debugger/exec.ml
index d97a8c4e7..1ea165978 100644
--- a/debugger/exec.ml
+++ b/debugger/exec.ml
@@ -25,8 +25,11 @@ let break signum =
else raise Sys.Break
let _ =
- Sys.set_signal Sys.sigint (Sys.Signal_handle break);
- Sys.set_signal Sys.sigpipe (Sys.Signal_handle (fun _ -> raise End_of_file))
+ match Sys.os_type with
+ "Win32" -> ()
+ | _ ->
+ Sys.set_signal Sys.sigint (Sys.Signal_handle break);
+ Sys.set_signal Sys.sigpipe (Sys.Signal_handle (fun _ -> raise End_of_file))
let protect f =
if !is_protected then
diff --git a/debugger/main.ml b/debugger/main.ml
index 4920d0d79..fda242bc5 100644
--- a/debugger/main.ml
+++ b/debugger/main.ml
@@ -148,8 +148,15 @@ let speclist = [
let main () =
try
- socket_name := Filename.concat Filename.temp_dir_name
- ("camldebug" ^ (string_of_int (Unix.getpid ())));
+ socket_name :=
+ (match Sys.os_type with
+ "Win32" ->
+ (Unix.string_of_inet_addr Unix.inet_addr_loopback)^
+ ":"^
+ (string_of_int (10000 + ((Unix.getpid ()) mod 10000)))
+ | _ -> Filename.concat Filename.temp_dir_name
+ ("camldebug" ^ (string_of_int (Unix.getpid ())))
+ );
begin try
Arg.parse speclist anonymous "";
Arg.usage speclist
diff --git a/debugger/program_loading.ml b/debugger/program_loading.ml
index e1507e5d9..1a750a2bb 100644
--- a/debugger/program_loading.ml
+++ b/debugger/program_loading.ml
@@ -37,7 +37,7 @@ let load_program () =
(*** Launching functions. ***)
(* A generic function for launching the program *)
-let generic_exec cmdline = function () ->
+let generic_exec_unix cmdline = function () ->
if !debug_loading then
prerr_endline "Launching program...";
let child =
@@ -64,11 +64,36 @@ let generic_exec cmdline = function () ->
(_, WEXITED 0) -> ()
| _ -> raise Toplevel
+let generic_exec_win cmdline = function () ->
+ if !debug_loading then
+ prerr_endline "Launching program...";
+ try ignore(create_process "cmd.exe" [| "/C"; cmdline() |] stdin stdout stderr)
+ with x ->
+ Unix_tools.report_error x;
+ raise Toplevel
+
+let generic_exec =
+ match Sys.os_type with
+ "Win32" -> generic_exec_win
+ | _ -> generic_exec_unix
+
(* Execute the program by calling the runtime explicitely *)
let exec_with_runtime =
generic_exec
(function () ->
- Printf.sprintf "CAML_DEBUG_SOCKET=%s %s %s %s"
+ match Sys.os_type with
+ "Win32" ->
+ (* This fould fail on a file name with spaces
+ but quoting is even worse because Unix.create_process
+ thinks each command line parameter is a file.
+ So no good solution so far *)
+ Printf.sprintf "set CAML_DEBUG_SOCKET=%s && %s %s %s"
+ !socket_name
+ runtime_program
+ !program_name
+ !arguments
+ | _ ->
+ Printf.sprintf "CAML_DEBUG_SOCKET=%s %s %s %s"
!socket_name
(Filename.quote runtime_program)
(Filename.quote !program_name)
@@ -78,7 +103,15 @@ let exec_with_runtime =
let exec_direct =
generic_exec
(function () ->
- Printf.sprintf "CAML_DEBUG_SOCKET=%s %s %s"
+ match Sys.os_type with
+ "Win32" ->
+ (* See the comment above *)
+ Printf.sprintf "set CAML_DEBUG_SOCKET=%s && %s %s"
+ !socket_name
+ !program_name
+ !arguments
+ | _ ->
+ Printf.sprintf "CAML_DEBUG_SOCKET=%s %s %s"
!socket_name
(Filename.quote !program_name)
!arguments)
diff --git a/debugger/program_management.ml b/debugger/program_management.ml
index d38adc713..35f74d654 100644
--- a/debugger/program_management.ml
+++ b/debugger/program_management.ml
@@ -74,6 +74,7 @@ let open_connection address continue =
let sock = socket sock_domain SOCK_STREAM 0 in
(try
bind sock sock_address;
+ setsockopt sock SO_REUSEADDR true;
listen sock 3;
connection := io_channel_of_descr sock;
Input_handling.add_file !connection (accept_connection continue);
diff --git a/debugger/unix_tools.ml b/debugger/unix_tools.ml
index 5061bb1dd..5328a2aad 100644
--- a/debugger/unix_tools.ml
+++ b/debugger/unix_tools.ml
@@ -36,7 +36,9 @@ let convert_address address =
prerr_endline "The port number should be an integer";
failwith "Can't convert address")))
with Not_found ->
- (PF_UNIX, ADDR_UNIX address)
+ match Sys.os_type with
+ "Win32" -> failwith "Unix sockets not supported"
+ | _ -> (PF_UNIX, ADDR_UNIX address)
(*** Report a unix error. ***)
let report_error = function
diff --git a/myocamlbuild.ml b/myocamlbuild.ml
index 5a61f2eb0..143f07fde 100644
--- a/myocamlbuild.ml
+++ b/myocamlbuild.ml
@@ -432,8 +432,8 @@ flag ["c"; "compile"; "otherlibs_num"] begin
A"-I"; P"../otherlibs/num"]
end;;
flag ["c"; "compile"; "otherlibs_win32unix"] (A"-I../otherlibs/win32unix");;
-flag [(* "ocaml" or "c"; *) "ocamlmklib"; "otherlibs_win32unix"] (S[A"-cclib"; Quote (syslib "wsock32")]);;
-flag ["c"; "link"; "dll"; "otherlibs_win32unix"] (syslib "wsock32");;
+flag [(* "ocaml" or "c"; *) "ocamlmklib"; "otherlibs_win32unix"] (S[A"-cclib"; Quote (syslib "ws2_32")]);;
+flag ["c"; "link"; "dll"; "otherlibs_win32unix"] (syslib "ws2_32");;
let flags = S[syslib "kernel32"; syslib "gdi32"; syslib "user32"] in
flag ["c"; "ocamlmklib"; "otherlibs_win32graph"] (S[A"-cclib"; Quote flags]);
flag ["c"; "link"; "dll"; "otherlibs_win32graph"] flags;;
diff --git a/otherlibs/win32unix/Makefile.nt b/otherlibs/win32unix/Makefile.nt
index 3ff9b7952..1fd635a7c 100644
--- a/otherlibs/win32unix/Makefile.nt
+++ b/otherlibs/win32unix/Makefile.nt
@@ -21,7 +21,8 @@ WIN_FILES = accept.c bind.c channels.c close.c \
mkdir.c open.c pipe.c read.c rename.c \
select.c sendrecv.c \
shutdown.c sleep.c socket.c sockopt.c startup.c stat.c \
- system.c unixsupport.c windir.c winwait.c write.c
+ system.c unixsupport.c windir.c winwait.c write.c \
+ winlist.c winworker.c windbug.c
# Files from the ../unix directory
UNIX_FILES = access.c addrofstr.c chdir.c chmod.c cst2constr.c \
@@ -33,7 +34,7 @@ UNIX_FILES = access.c addrofstr.c chdir.c chmod.c cst2constr.c \
UNIX_CAML_FILES = unix.mli unixLabels.mli unixLabels.ml
ALL_FILES=$(WIN_FILES) $(UNIX_FILES)
-WSOCKLIB=$(call SYSLIB,wsock32)
+WSOCKLIB=$(call SYSLIB,ws2_32)
LIBNAME=unix
COBJS=$(ALL_FILES:.c=.$(O))
diff --git a/otherlibs/win32unix/dllunix.dlib b/otherlibs/win32unix/dllunix.dlib
index 01ffc59e6..e3ebf34e9 100644
--- a/otherlibs/win32unix/dllunix.dlib
+++ b/otherlibs/win32unix/dllunix.dlib
@@ -7,6 +7,7 @@ mkdir.d.o open.d.o pipe.d.o read.d.o rename.d.o
select.d.o sendrecv.d.o
shutdown.d.o sleep.d.o socket.d.o sockopt.d.o startup.d.o stat.d.o
system.d.o unixsupport.d.o windir.d.o winwait.d.o write.d.o
+winlist.d.o winworker.d.o windbug.d.o
# Files from the ../unix directory
access.d.o addrofstr.d.o chdir.d.o chmod.d.o cst2constr.d.o
diff --git a/otherlibs/win32unix/libunix.clib b/otherlibs/win32unix/libunix.clib
index 29b8d6e68..043dcf760 100644
--- a/otherlibs/win32unix/libunix.clib
+++ b/otherlibs/win32unix/libunix.clib
@@ -7,6 +7,7 @@ mkdir.o open.o pipe.o read.o rename.o
select.o sendrecv.o
shutdown.o sleep.o socket.o sockopt.o startup.o stat.o
system.o unixsupport.o windir.o winwait.o write.o
+winlist.o winworker.o windbug.o
# Files from the ../unix directory
access.o addrofstr.o chdir.o chmod.o cst2constr.o
diff --git a/otherlibs/win32unix/select.c b/otherlibs/win32unix/select.c
index 50a7da30b..1753a127e 100644
--- a/otherlibs/win32unix/select.c
+++ b/otherlibs/win32unix/select.c
@@ -17,85 +17,1031 @@
#include <alloc.h>
#include <memory.h>
#include <signals.h>
+#include <winsock2.h>
+#include <windows.h>
#include "unixsupport.h"
+#include "windbug.h"
+#include "winworker.h"
+#include "winlist.h"
-static void fdlist_to_fdset(value fdlist, fd_set *fdset)
+/* This constant define the maximum number of objects that
+ * can be handle by a SELECTDATA.
+ * It takes the following parameters into account:
+ * - limitation on number of objects is mostly due to limitation
+ * a WaitForMultipleObjects
+ * - there is always an event "hStop" to watch
+ *
+ * This lead to pick the following value as the biggest possible
+ * value
+ */
+#define MAXIMUM_SELECT_OBJECTS (MAXIMUM_WAIT_OBJECTS - 1)
+
+/* Manage set of handle */
+typedef struct _SELECTHANDLESET {
+ LPHANDLE lpHdl;
+ DWORD nMax;
+ DWORD nLast;
+} SELECTHANDLESET;
+
+typedef SELECTHANDLESET *LPSELECTHANDLESET;
+
+void handle_set_init (LPSELECTHANDLESET hds, LPHANDLE lpHdl, DWORD max)
+{
+ DWORD i;
+
+ hds->lpHdl = lpHdl;
+ hds->nMax = max;
+ hds->nLast = 0;
+
+ /* Set to invalid value every entry of the handle */
+ for (i = 0; i < hds->nMax; i++)
+ {
+ hds->lpHdl[i] = INVALID_HANDLE_VALUE;
+ };
+}
+
+void handle_set_add (LPSELECTHANDLESET hds, HANDLE hdl)
+{
+ LPSELECTHANDLESET res;
+
+ if (hds->nLast < hds->nMax)
+ {
+ hds->lpHdl[hds->nLast] = hdl;
+ hds->nLast++;
+ }
+
+ DBUG_PRINT("Adding handle %x to set %x", hdl, hds);
+}
+
+BOOL handle_set_mem (LPSELECTHANDLESET hds, HANDLE hdl)
+{
+ BOOL res;
+ DWORD i;
+
+ res = FALSE;
+ for (i = 0; !res && i < hds->nLast; i++)
+ {
+ res = (hds->lpHdl[i] == hdl);
+ }
+
+ return res;
+}
+
+void handle_set_reset (LPSELECTHANDLESET hds)
+{
+ DWORD i;
+
+ for (i = 0; i < hds->nMax; i++)
+ {
+ hds->lpHdl[i] = INVALID_HANDLE_VALUE;
+ }
+ hds->nMax = 0;
+ hds->nLast = 0;
+ hds->lpHdl = NULL;
+}
+
+/* Data structure for handling select */
+
+typedef enum _SELECTHANDLETYPE {
+ SELECT_HANDLE_NONE = 0,
+ SELECT_HANDLE_DISK,
+ SELECT_HANDLE_CONSOLE,
+ SELECT_HANDLE_PIPE,
+ SELECT_HANDLE_SOCKET,
+} SELECTHANDLETYPE;
+
+typedef enum _SELECTMODE {
+ SELECT_MODE_NONE = 0,
+ SELECT_MODE_READ,
+ SELECT_MODE_WRITE,
+ SELECT_MODE_EXCEPT,
+} SELECTMODE;
+
+typedef enum _SELECTSTATE {
+ SELECT_STATE_NONE = 0,
+ SELECT_STATE_INITFAILED,
+ SELECT_STATE_ERROR,
+ SELECT_STATE_SIGNALED
+} SELECTSTATE;
+
+typedef enum _SELECTTYPE {
+ SELECT_TYPE_NONE = 0,
+ SELECT_TYPE_STATIC, /* Result is known without running anything */
+ SELECT_TYPE_CONSOLE_READ, /* Reading data on console */
+ SELECT_TYPE_PIPE_READ, /* Reading data on pipe */
+ SELECT_TYPE_SOCKET /* Classic select */
+} SELECTTYPE;
+
+/* Data structure for results */
+typedef struct _SELECTRESULT {
+ LIST lst;
+ SELECTMODE EMode;
+ LPVOID lpOrig;
+} SELECTRESULT;
+
+typedef SELECTRESULT *LPSELECTRESULT;
+
+/* Data structure for query */
+typedef struct _SELECTQUERY {
+ LIST lst;
+ SELECTMODE EMode;
+ HANDLE hFileDescr;
+ LPVOID lpOrig;
+} SELECTQUERY;
+
+typedef SELECTQUERY *LPSELECTQUERY;
+
+typedef struct _SELECTDATA {
+ LIST lst;
+ SELECTTYPE EType;
+ SELECTRESULT aResults[MAXIMUM_SELECT_OBJECTS];
+ DWORD nResultsCount;
+ /* Data following are dedicated to APC like call, they
+ will be initialized if required.
+ */
+ WORKERFUNC funcWorker;
+ SELECTQUERY aQueries[MAXIMUM_SELECT_OBJECTS];
+ DWORD nQueriesCount;
+ SELECTSTATE EState;
+ DWORD nError;
+ LPWORKER lpWorker;
+} SELECTDATA;
+
+typedef SELECTDATA *LPSELECTDATA;
+
+/* Get error status if associated condition is false */
+static BOOL check_error(LPSELECTDATA lpSelectData, BOOL bFailed)
+{
+ if (bFailed && lpSelectData->nError == 0)
+ {
+ lpSelectData->EState = SELECT_STATE_ERROR;
+ lpSelectData->nError = GetLastError();
+ }
+ return bFailed;
+}
+
+/* Create data associated with a select operation */
+LPSELECTDATA select_data_new (LPSELECTDATA lpSelectData, SELECTTYPE EType)
+{
+ /* Allocate the data structure */
+ LPSELECTDATA res;
+ DWORD i;
+
+ if (!HeapLock(GetProcessHeap()))
+ {
+ win32_maperr(GetLastError());
+ uerror("select", Nothing);
+ }
+ res = (LPSELECTDATA)HeapAlloc(GetProcessHeap(), 0, sizeof(SELECTDATA));
+ HeapUnlock(GetProcessHeap());
+
+ /* Init common data */
+ list_init((LPLIST)res);
+ list_next_set((LPLIST)res, (LPLIST)lpSelectData);
+ res->EType = EType;
+ res->nResultsCount = 0;
+
+
+ /* Data following are dedicated to APC like call, they
+ will be initialized if required. For now they are set to
+ invalid values.
+ */
+ res->funcWorker = NULL;
+ res->nQueriesCount = 0;
+ res->EState = SELECT_STATE_NONE;
+ res->nError = 0;
+ res->lpWorker = NULL;
+
+ return res;
+}
+
+/* Free select data */
+void select_data_free (LPSELECTDATA lpSelectData)
+{
+ DWORD i;
+
+ DBUG_PRINT("Freeing data of %x", lpSelectData);
+
+ /* Free APC related data, if they exists */
+ if (lpSelectData->lpWorker != NULL)
+ {
+ worker_job_finish(lpSelectData->lpWorker);
+ lpSelectData->lpWorker = NULL;
+ };
+
+ /* Make sure results/queries cannot be accessed */
+ lpSelectData->nResultsCount = 0;
+ lpSelectData->nQueriesCount = 0;
+
+ if (!HeapLock(GetProcessHeap()))
+ {
+ win32_maperr(GetLastError());
+ uerror("select_data_free", Nothing);
+ };
+ HeapFree(GetProcessHeap(), 0, lpSelectData);
+ HeapUnlock(GetProcessHeap());
+}
+
+/* Add a result to select data, return zero if something goes wrong. */
+DWORD select_data_result_add (LPSELECTDATA lpSelectData, SELECTMODE EMode, LPVOID lpOrig)
+{
+ DWORD res;
+ DWORD i;
+
+ res = 0;
+ if (lpSelectData->nResultsCount < MAXIMUM_SELECT_OBJECTS)
+ {
+ i = lpSelectData->nResultsCount;
+ lpSelectData->aResults[i].EMode = EMode;
+ lpSelectData->aResults[i].lpOrig = lpOrig;
+ lpSelectData->nResultsCount++;
+ res = 1;
+ }
+
+ return res;
+}
+
+/* Add a query to select data, return zero if something goes wrong */
+DWORD select_data_query_add (LPSELECTDATA lpSelectData, SELECTMODE EMode, HANDLE hFileDescr, LPVOID lpOrig)
{
- value l;
- FD_ZERO(fdset);
- for (l = fdlist; l != Val_int(0); l = Field(l, 1)) {
- FD_SET(Socket_val(Field(l, 0)), fdset);
+ DWORD res;
+ DWORD i;
+
+ res = 0;
+ if (lpSelectData->nQueriesCount < MAXIMUM_SELECT_OBJECTS)
+ {
+ i = lpSelectData->nQueriesCount;
+ lpSelectData->aQueries[i].EMode = EMode;
+ lpSelectData->aQueries[i].hFileDescr = hFileDescr;
+ lpSelectData->aQueries[i].lpOrig = lpOrig;
+ lpSelectData->nQueriesCount++;
+ res = 1;
}
+
+ return res;
}
-static value fdset_to_fdlist(value fdlist, fd_set *fdset)
+/* Search for a job that has available query slots and that match provided type.
+ * If none is found, create a new one. Return the corresponding SELECTDATA, and
+ * update provided SELECTDATA head, if required.
+ */
+LPSELECTDATA select_data_job_search (LPSELECTDATA *lppSelectData, SELECTTYPE EType)
{
- value res = Val_int(0);
- Begin_roots2(fdlist, res)
- for (/*nothing*/; fdlist != Val_int(0); fdlist = Field(fdlist, 1)) {
- value s = Field(fdlist, 0);
- if (FD_ISSET(Socket_val(s), fdset)) {
- value newres = alloc_small(2, 0);
- Field(newres, 0) = s;
- Field(newres, 1) = res;
- res = newres;
+ LPSELECTDATA res;
+
+ res = NULL;
+
+ /* Search for job */
+ DBUG_PRINT("Searching an available job for type %d", EType);
+ res = *lppSelectData;
+ while (
+ res != NULL
+ && !(
+ res->EType == EType
+ && res->nQueriesCount < MAXIMUM_SELECT_OBJECTS
+ )
+ )
+ {
+ res = LIST_NEXT(LPSELECTDATA, res);
+ }
+
+ /* No matching job found, create one */
+ if (res == NULL)
+ {
+ DBUG_PRINT("No job for type %d found, create one", EType);
+ res = select_data_new(*lppSelectData, EType);
+ *lppSelectData = res;
+ }
+
+ return res;
+}
+
+/***********************/
+/* Console */
+/***********************/
+
+void read_console_poll(HANDLE hStop, void *_data)
+{
+ HANDLE events[2];
+ INPUT_RECORD record;
+ DWORD waitRes;
+ DWORD n;
+ LPSELECTDATA lpSelectData;
+ LPSELECTQUERY lpQuery;
+
+ DBUG_PRINT("Waiting for data on console");
+
+ record;
+ waitRes = 0;
+ n = 0;
+ lpSelectData = (LPSELECTDATA)_data;
+ lpQuery = &(lpSelectData->aQueries[0]);
+
+ events[0] = hStop;
+ events[1] = lpQuery->hFileDescr;
+ while (lpSelectData->EState == SELECT_STATE_NONE)
+ {
+ waitRes = WaitForMultipleObjects(2, events, FALSE, INFINITE);
+ if (waitRes == WAIT_OBJECT_0 || check_error(lpSelectData, waitRes == WAIT_FAILED))
+ {
+ /* stop worker event or error */
+ break;
+ }
+ /* console event */
+ if (check_error(lpSelectData, PeekConsoleInput(lpQuery->hFileDescr, &record, 1, &n) == 0))
+ {
+ break;
+ }
+ /* check for ASCII keypress only */
+ if (record.EventType == KEY_EVENT &&
+ record.Event.KeyEvent.bKeyDown &&
+ record.Event.KeyEvent.uChar.AsciiChar != 0)
+ {
+ select_data_result_add(lpSelectData, lpQuery->EMode, lpQuery->lpOrig);
+ lpSelectData->EState = SELECT_STATE_SIGNALED;
+ break;
+ }
+ else
+ {
+ /* discard everything else and try again */
+ if (check_error(lpSelectData, ReadConsoleInput(lpQuery->hFileDescr, &record, 1, &n) == 0))
+ {
+ break;
}
}
- End_roots();
+ };
+}
+
+/* Add a function to monitor console input */
+LPSELECTDATA read_console_poll_add (LPSELECTDATA lpSelectData, SELECTMODE EMode, HANDLE hFileDescr, LPVOID lpOrig)
+{
+ LPSELECTDATA res;
+
+ res = select_data_new(lpSelectData, SELECT_TYPE_CONSOLE_READ);
+ res->funcWorker = read_console_poll;
+ select_data_query_add(res, SELECT_MODE_READ, hFileDescr, lpOrig);
+
return res;
}
-CAMLprim value unix_select(value readfds, value writefds, value exceptfds, value timeout)
+/***********************/
+/* Pipe */
+/***********************/
+
+/* Monitor a pipe for input */
+void read_pipe_poll (HANDLE hStop, void *_data)
+{
+ DWORD event;
+ DWORD n;
+ LPSELECTQUERY iterQuery;
+ LPSELECTDATA lpSelectData;
+ DWORD i;
+
+ /* Poll pipe */
+ event = 0;
+ n = 0;
+ lpSelectData = (LPSELECTDATA)_data;
+
+ DBUG_PRINT("Checking data pipe");
+ while (lpSelectData->EState == SELECT_STATE_NONE)
+ {
+ for (i = 0; i < lpSelectData->nQueriesCount; i++)
+ {
+ iterQuery = &(lpSelectData->aQueries[i]);
+ if (check_error(
+ lpSelectData,
+ PeekNamedPipe(
+ iterQuery->hFileDescr,
+ NULL,
+ 0,
+ NULL,
+ &n,
+ NULL) == 0))
+ {
+ break;
+ };
+
+ if (n > 0)
+ {
+ lpSelectData->EState = SELECT_STATE_SIGNALED;
+ select_data_result_add(lpSelectData, iterQuery->EMode, iterQuery->lpOrig);
+ };
+ };
+
+ /* Alas, nothing except polling seems to work for pipes.
+ Check the state & stop_worker_event every 10 ms
+ */
+ if (lpSelectData->EState == SELECT_STATE_NONE)
+ {
+ event = WaitForSingleObject(hStop, 10);
+ if (event == WAIT_OBJECT_0 || check_error(lpSelectData, event == WAIT_FAILED))
+ {
+ break;
+ }
+ }
+ }
+ DBUG_PRINT("Finish checking data on pipe");
+}
+
+/* Add a function to monitor pipe input */
+LPSELECTDATA read_pipe_poll_add (LPSELECTDATA lpSelectData, SELECTMODE EMode, HANDLE hFileDescr, LPVOID lpOrig)
+{
+ LPSELECTDATA res;
+ LPSELECTDATA hd;
+
+ hd = lpSelectData;
+ /* Polling pipe is a non blocking operation by default. This means that each
+ worker can handle many pipe. We begin to try to find a worker that is
+ polling pipe, but for which there is under the limit of pipe per worker.
+ */
+ DBUG_PRINT("Searching an available worker handling pipe");
+ res = select_data_job_search(&hd, SELECT_TYPE_PIPE_READ);
+
+ /* Add a new pipe to poll */
+ res->funcWorker = read_pipe_poll;
+ select_data_query_add(res, EMode, hFileDescr, lpOrig);
+
+ return hd;
+}
+
+/***********************/
+/* Socket */
+/***********************/
+
+/* Monitor socket */
+void socket_poll (HANDLE hStop, void *_data)
+{
+ LPSELECTDATA lpSelectData;
+ LPSELECTQUERY iterQuery;
+ HANDLE aEvents[MAXIMUM_SELECT_OBJECTS];
+ DWORD nEvents;
+ long maskEvents;
+ DWORD i;
+ u_long iMode;
+
+ lpSelectData = (LPSELECTDATA)_data;
+
+ for (nEvents = 0; nEvents < lpSelectData->nQueriesCount; nEvents++)
+ {
+ iterQuery = &(lpSelectData->aQueries[nEvents]);
+ aEvents[nEvents] = CreateEvent(NULL, TRUE, FALSE, NULL);
+ maskEvents = 0;
+ switch (iterQuery->EMode)
+ {
+ case SELECT_MODE_READ:
+ maskEvents = FD_READ | FD_ACCEPT | FD_CLOSE;
+ break;
+ case SELECT_MODE_WRITE:
+ maskEvents = FD_WRITE | FD_CONNECT | FD_CLOSE;
+ break;
+ case SELECT_MODE_EXCEPT:
+ maskEvents = FD_OOB;
+ break;
+ }
+ check_error(lpSelectData,
+ WSAEventSelect(
+ (SOCKET)(iterQuery->hFileDescr),
+ aEvents[nEvents],
+ maskEvents) == SOCKET_ERROR);
+ }
+
+ /* Add stop event */
+ aEvents[nEvents] = hStop;
+ nEvents++;
+
+ if (lpSelectData->nError == 0)
+ {
+ check_error(lpSelectData,
+ WaitForMultipleObjects(
+ nEvents,
+ aEvents,
+ FALSE,
+ INFINITE) == WAIT_FAILED);
+ };
+
+ if (lpSelectData->nError == 0)
+ {
+ for (i = 0; i < lpSelectData->nQueriesCount; i++)
+ {
+ iterQuery = &(lpSelectData->aQueries[i]);
+ if (WaitForSingleObject(aEvents[i], 0) == WAIT_OBJECT_0)
+ {
+ DBUG_PRINT("Socket %d has pending events", (i - 1));
+ if (iterQuery != NULL)
+ {
+ select_data_result_add(lpSelectData, iterQuery->EMode, iterQuery->lpOrig);
+ }
+ }
+ /* WSAEventSelect() automatically sets socket to nonblocking mode.
+ Restore the blocking one. */
+ iMode = 0;
+ check_error(lpSelectData,
+ WSAEventSelect((SOCKET)(iterQuery->hFileDescr), aEvents[i], 0) != 0 ||
+ ioctlsocket((SOCKET)(iterQuery->hFileDescr), FIONBIO, &iMode) != 0);
+
+ CloseHandle(aEvents[i]);
+ aEvents[i] = INVALID_HANDLE_VALUE;
+ }
+ }
+}
+
+/* Add a function to monitor socket */
+LPSELECTDATA socket_poll_add (LPSELECTDATA lpSelectData, SELECTMODE EMode, HANDLE hFileDescr, LPVOID lpOrig)
+{
+ LPSELECTDATA res;
+ LPSELECTDATA hd;
+
+ hd = lpSelectData;
+ /* Polling socket can be done mulitple handle at the same time. You just
+ need one worker to use it. Try to find if there is already a worker
+ handling this kind of request.
+ */
+ DBUG_PRINT("Scanning list of worker to find one that already handle socket");
+ res = select_data_job_search(&hd, SELECT_TYPE_SOCKET);
+
+ /* Add a new socket to poll */
+ res->funcWorker = socket_poll;
+ DBUG_PRINT("Add socket %x to worker", hFileDescr);
+ select_data_query_add(res, EMode, hFileDescr, lpOrig);
+ DBUG_PRINT("Socket %x added", hFileDescr);
+
+ return hd;
+}
+
+/***********************/
+/* Static */
+/***********************/
+
+/* Add a static result */
+LPSELECTDATA static_poll_add (LPSELECTDATA lpSelectData, SELECTMODE EMode, HANDLE hFileDescr, LPVOID lpOrig)
{
- fd_set read, write, except;
- double tm;
- struct timeval tv;
- struct timeval * tvp;
- int retcode;
- value res;
- value read_list = Val_unit, write_list = Val_unit, except_list = Val_unit;
- DWORD err = 0;
-
- Begin_roots3 (readfds, writefds, exceptfds)
- Begin_roots3 (read_list, write_list, except_list)
- tm = Double_val(timeout);
- if (readfds == Val_int(0)
- && writefds == Val_int(0)
- && exceptfds == Val_int(0)) {
- if ( tm > 0.0 ) {
- enter_blocking_section();
- Sleep( (int)(tm * 1000));
- leave_blocking_section();
+ LPSELECTDATA res;
+ LPSELECTDATA hd;
+
+ /* Look for an already initialized static element */
+ hd = lpSelectData;
+ res = select_data_job_search(&hd, SELECT_TYPE_STATIC);
+
+ /* Add a new query/result */
+ select_data_query_add(res, EMode, hFileDescr, lpOrig);
+ select_data_result_add(res, EMode, lpOrig);
+
+ return hd;
+}
+
+/********************************/
+/* Generic select data handling */
+/********************************/
+
+/* Guess handle type */
+static SELECTHANDLETYPE get_handle_type(value fd)
+{
+ DWORD mode;
+ SELECTHANDLETYPE res;
+
+ CAMLparam1(fd);
+
+ mode = 0;
+ res = SELECT_HANDLE_NONE;
+
+ if (Descr_kind_val(fd) == KIND_SOCKET)
+ {
+ res = SELECT_HANDLE_SOCKET;
+ }
+ else
+ {
+ switch(GetFileType(Handle_val(fd)))
+ {
+ case FILE_TYPE_DISK:
+ res = SELECT_HANDLE_DISK;
+ break;
+
+ case FILE_TYPE_CHAR: /* character file or a console */
+ if (GetConsoleMode(Handle_val(fd), &mode) != 0)
+ {
+ res = SELECT_HANDLE_CONSOLE;
+ }
+ else
+ {
+ res = SELECT_HANDLE_NONE;
+ };
+ break;
+
+ case FILE_TYPE_PIPE: /* a named or an anonymous pipe (socket already handled) */
+ res = SELECT_HANDLE_PIPE;
+ break;
+ };
+ };
+
+ CAMLreturnT(SELECTHANDLETYPE, res);
+}
+
+/* Choose what to do with given data */
+LPSELECTDATA select_data_dispatch (LPSELECTDATA lpSelectData, SELECTMODE EMode, value fd)
+{
+ LPSELECTDATA res;
+ HANDLE hFileDescr;
+ void *lpOrig;
+ struct sockaddr sa;
+ int sa_len;
+ BOOL alreadyAdded;
+
+ CAMLparam1(fd);
+
+ res = lpSelectData;
+ hFileDescr = Handle_val(fd);
+ lpOrig = (void *)fd;
+ sa_len = sizeof(sa);
+ alreadyAdded = FALSE;
+
+ DBUG_PRINT("Begin dispatching handle %x", hFileDescr);
+
+ DBUG_PRINT("Waiting for %d on handle %x", EMode, hFileDescr);
+
+ /* There is only 2 way to have except mode: transmission of OOB data through
+ a socket TCP/IP and through a strange interaction with a TTY.
+ With windows, we only consider the TCP/IP except condition
+ */
+ switch(get_handle_type(fd))
+ {
+ case SELECT_HANDLE_DISK:
+ DBUG_PRINT("Handle %x is a disk handle", hFileDescr);
+ /* Disk is always ready in read/write operation */
+ if (EMode == SELECT_MODE_READ || EMode == SELECT_MODE_WRITE)
+ {
+ res = static_poll_add(res, EMode, hFileDescr, lpOrig);
+ };
+ break;
+
+ case SELECT_HANDLE_CONSOLE:
+ DBUG_PRINT("Handle %x is a console handle", hFileDescr);
+ /* Console is always ready in write operation, need to check for read. */
+ if (EMode == SELECT_MODE_READ)
+ {
+ res = read_console_poll_add(res, EMode, hFileDescr, lpOrig);
}
- read_list = write_list = except_list = Val_int(0);
- } else {
- fdlist_to_fdset(readfds, &read);
- fdlist_to_fdset(writefds, &write);
- fdlist_to_fdset(exceptfds, &except);
- if (tm < 0.0)
- tvp = (struct timeval *) NULL;
- else {
- tv.tv_sec = (int) tm;
- tv.tv_usec = (int) (1e6 * (tm - (int) tm));
- tvp = &tv;
+ else if (EMode == SELECT_MODE_WRITE)
+ {
+ res = static_poll_add(res, EMode, hFileDescr, lpOrig);
+ };
+ break;
+
+ case SELECT_HANDLE_PIPE:
+ DBUG_PRINT("Handle %x is a pipe handle", hFileDescr);
+ /* Console is always ready in write operation, need to check for read. */
+ if (EMode == SELECT_MODE_READ)
+ {
+ DBUG_PRINT("Need to check availability of data on pipe");
+ res = read_pipe_poll_add(res, EMode, hFileDescr, lpOrig);
}
- enter_blocking_section();
- if (select(FD_SETSIZE, &read, &write, &except, tvp) == -1)
- err = WSAGetLastError();
- leave_blocking_section();
- if (err) {
- win32_maperr(err);
- uerror("select", Nothing);
+ else if (EMode == SELECT_MODE_WRITE)
+ {
+ DBUG_PRINT("No need to check availability of data on pipe, write operation always possible");
+ res = static_poll_add(res, EMode, hFileDescr, lpOrig);
+ };
+ break;
+
+ case SELECT_HANDLE_SOCKET:
+ DBUG_PRINT("Handle %x is a socket handle", hFileDescr);
+ if (getsockname((SOCKET)hFileDescr, &sa, &sa_len) == SOCKET_ERROR)
+ {
+ if (WSAGetLastError() == WSAEINVAL)
+ {
+ /* Socket is not bound */
+ DBUG_PRINT("Socket is not connected");
+ if (EMode == SELECT_MODE_WRITE || EMode == SELECT_MODE_READ)
+ {
+ res = static_poll_add(res, EMode, hFileDescr, lpOrig);
+ alreadyAdded = TRUE;
+ }
+ }
}
- read_list = fdset_to_fdlist(readfds, &read);
- write_list = fdset_to_fdlist(writefds, &write);
- except_list = fdset_to_fdlist(exceptfds, &except);
- }
- res = alloc_small(3, 0);
- Field(res, 0) = read_list;
- Field(res, 1) = write_list;
- Field(res, 2) = except_list;
- End_roots();
- End_roots();
- return res;
+ if (!alreadyAdded)
+ {
+ res = socket_poll_add(res, EMode, hFileDescr, lpOrig);
+ }
+ break;
+
+ default:
+ DBUG_PRINT("Handle %x is unknown", hFileDescr);
+ caml_failwith("Unknown handle");
+ break;
+ };
+
+ DBUG_PRINT("Finish dispatching handle %x", hFileDescr);
+
+ CAMLreturnT(LPSELECTDATA, res);
+}
+
+static DWORD caml_list_length (value lst)
+{
+ DWORD res;
+
+ CAMLparam1 (lst);
+ CAMLlocal1 (l);
+
+ for (res = 0, l = lst; l != Val_int(0); l = Field(l, 1), res++)
+ { }
+
+ CAMLreturnT(DWORD, res);
+}
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+CAMLprim value unix_select(value readfds, value writefds, value exceptfds, value timeout)
+{
+ /* Event associated to handle */
+ DWORD nEventsCount;
+ DWORD nEventsMax;
+ HANDLE *lpEventsDone;
+
+ /* Data for all handles */
+ LPSELECTDATA lpSelectData;
+ LPSELECTDATA iterSelectData;
+
+ /* Iterator for results */
+ LPSELECTRESULT iterResult;
+
+ /* Iterator */
+ DWORD i;
+
+ /* Error status */
+ DWORD err;
+
+ /* Time to wait */
+ DWORD milliseconds;
+
+ /* Wait return */
+ DWORD waitRet;
+
+ /* Set of handle */
+ SELECTHANDLESET hds;
+ DWORD hdsMax;
+ LPHANDLE hdsData;
+
+ /* Length of each list */
+ DWORD readfds_len;
+ DWORD writefds_len;
+ DWORD exceptfds_len;
+
+ CAMLparam4 (readfds, writefds, exceptfds, timeout);
+ CAMLlocal5 (read_list, write_list, except_list, res, l);
+ CAMLlocal1 (fd);
+
+ DBUG_PRINT("in select");
+
+ nEventsCount = 0;
+ nEventsMax = 0;
+ lpEventsDone = NULL;
+ lpSelectData = NULL;
+ iterSelectData = NULL;
+ iterResult = NULL;
+ err = 0;
+ waitRet = 0;
+ readfds_len = caml_list_length(readfds);
+ writefds_len = caml_list_length(writefds);
+ exceptfds_len = caml_list_length(exceptfds);
+ hdsMax = MAX(readfds_len, MAX(writefds_len, exceptfds_len));
+
+ if (!HeapLock(GetProcessHeap()))
+ {
+ win32_maperr(GetLastError());
+ uerror("select", Nothing);
+ }
+ hdsData = (HANDLE *)HeapAlloc(
+ GetProcessHeap(),
+ 0,
+ sizeof(HANDLE) * hdsMax);
+ HeapUnlock(GetProcessHeap());
+
+ if (Double_val(timeout) >= 0.0)
+ {
+ milliseconds = 1000 * Double_val(timeout);
+ DBUG_PRINT("Will wait %d ms", milliseconds);
+ }
+ else
+ {
+ milliseconds = INFINITE;
+ }
+
+
+ /* Create list of select data, based on the different list of fd to watch */
+ DBUG_PRINT("Dispatch read fd");
+ handle_set_init(&hds, hdsData, hdsMax);
+ for (l = readfds; l != Val_int(0); l = Field(l, 1))
+ {
+ fd = Field(l, 0);
+ if (!handle_set_mem(&hds, Handle_val(fd)))
+ {
+ handle_set_add(&hds, Handle_val(fd));
+ lpSelectData = select_data_dispatch(lpSelectData, SELECT_MODE_READ, fd);
+ }
+ else
+ {
+ DBUG_PRINT("Discarding handle %x which is already monitor for read", Handle_val(fd));
+ }
+ }
+ handle_set_reset(&hds);
+
+ DBUG_PRINT("Dispatch write fd");
+ handle_set_init(&hds, hdsData, hdsMax);
+ for (l = writefds; l != Val_int(0); l = Field(l, 1))
+ {
+ fd = Field(l, 0);
+ if (!handle_set_mem(&hds, Handle_val(fd)))
+ {
+ handle_set_add(&hds, Handle_val(fd));
+ lpSelectData = select_data_dispatch(lpSelectData, SELECT_MODE_WRITE, fd);
+ }
+ else
+ {
+ DBUG_PRINT("Discarding handle %x which is already monitor for write", Handle_val(fd));
+ }
+ }
+ handle_set_reset(&hds);
+
+ DBUG_PRINT("Dispatch exceptional fd");
+ handle_set_init(&hds, hdsData, hdsMax);
+ for (l = exceptfds; l != Val_int(0); l = Field(l, 1))
+ {
+ fd = Field(l, 0);
+ if (!handle_set_mem(&hds, Handle_val(fd)))
+ {
+ handle_set_add(&hds, Handle_val(fd));
+ lpSelectData = select_data_dispatch(lpSelectData, SELECT_MODE_EXCEPT, fd);
+ }
+ else
+ {
+ DBUG_PRINT("Discarding handle %x which is already monitor for exceptional", Handle_val(fd));
+ }
+ }
+ handle_set_reset(&hds);
+
+ /* Building the list of handle to wait for */
+ DBUG_PRINT("Building events done array");
+ nEventsMax = list_length((LPLIST)lpSelectData);
+ nEventsCount = 0;
+ if (!HeapLock(GetProcessHeap()))
+ {
+ win32_maperr(GetLastError());
+ uerror("select", Nothing);
+ }
+ lpEventsDone = (HANDLE *)HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE) * nEventsMax);
+ HeapUnlock(GetProcessHeap());
+
+ iterSelectData = lpSelectData;
+ while (iterSelectData != NULL)
+ {
+ /* Execute APC */
+ if (iterSelectData->funcWorker != NULL)
+ {
+ iterSelectData->lpWorker =
+ worker_job_submit(
+ iterSelectData->funcWorker,
+ (void *)iterSelectData);
+ DBUG_PRINT("Job submitted to worker %x", iterSelectData->lpWorker);
+ lpEventsDone[nEventsCount] = worker_job_event_done(iterSelectData->lpWorker);
+ nEventsCount++;
+ };
+ iterSelectData = LIST_NEXT(LPSELECTDATA, iterSelectData);
+ };
+
+ DBUG_PRINT("Need to watch %d workers", nEventsCount);
+
+ /* Processing select itself */
+ enter_blocking_section();
+ /* There are worker started, waiting to be monitored */
+ if (nEventsCount > 0)
+ {
+ /* Waiting for event */
+ if (err == 0)
+ {
+ DBUG_PRINT("Waiting for one select worker to be done");
+ switch (WaitForMultipleObjects(nEventsCount, lpEventsDone, FALSE, milliseconds))
+ {
+ case WAIT_FAILED:
+ err = GetLastError();
+ break;
+
+ case WAIT_TIMEOUT:
+ DBUG_PRINT("Select timeout");
+ break;
+
+ default:
+ DBUG_PRINT("One worker is done");
+ break;
+ };
+ }
+
+ /* Ordering stop to every worker */
+ DBUG_PRINT("Sending stop signal to every select workers");
+ iterSelectData = lpSelectData;
+ while (iterSelectData != NULL)
+ {
+ if (iterSelectData->lpWorker != NULL)
+ {
+ worker_job_stop(iterSelectData->lpWorker);
+ };
+ iterSelectData = LIST_NEXT(LPSELECTDATA, iterSelectData);
+ };
+
+ DBUG_PRINT("Waiting for every select worker to be done");
+ switch (WaitForMultipleObjects(nEventsCount, lpEventsDone, TRUE, INFINITE))
+ {
+ case WAIT_FAILED:
+ err = GetLastError();
+ break;
+
+ default:
+ DBUG_PRINT("Every worker is done");
+ break;
+ }
+ }
+ /* Nothing to monitor but some time to wait. */
+ else
+ {
+ Sleep(milliseconds);
+ }
+ leave_blocking_section();
+
+ DBUG_PRINT("Error status: %d (0 is ok)", err);
+ /* Build results */
+ if (err == 0)
+ {
+ DBUG_PRINT("Building result");
+ read_list = Val_unit;
+ write_list = Val_unit;
+ except_list = Val_unit;
+
+ iterSelectData = lpSelectData;
+ while (iterSelectData != NULL)
+ {
+ for (i = 0; i < iterSelectData->nResultsCount; i++)
+ {
+ iterResult = &(iterSelectData->aResults[i]);
+ l = alloc_small(2, 0);
+ Store_field(l, 0, (value)iterResult->lpOrig);
+ switch (iterResult->EMode)
+ {
+ case SELECT_MODE_READ:
+ Store_field(l, 1, read_list);
+ read_list = l;
+ break;
+ case SELECT_MODE_WRITE:
+ Store_field(l, 1, write_list);
+ write_list = l;
+ break;
+ case SELECT_MODE_EXCEPT:
+ Store_field(l, 1, except_list);
+ except_list = l;
+ break;
+ }
+ }
+ /* We try to only process the first error, bypass other errors */
+ if (err == 0 && iterSelectData->EState == SELECT_STATE_ERROR)
+ {
+ err = iterSelectData->nError;
+ }
+ iterSelectData = LIST_NEXT(LPSELECTDATA, iterSelectData);
+ }
+ }
+
+ /* Free resources */
+ DBUG_PRINT("Free selectdata resources");
+ iterSelectData = lpSelectData;
+ while (iterSelectData != NULL)
+ {
+ lpSelectData = iterSelectData;
+ iterSelectData = LIST_NEXT(LPSELECTDATA, iterSelectData);
+ select_data_free(lpSelectData);
+ }
+ lpSelectData = NULL;
+
+ /* Free allocated events/handle set array */
+ DBUG_PRINT("Free local allocated resources");
+ if (!HeapLock(GetProcessHeap()))
+ {
+ win32_maperr(GetLastError());
+ uerror("select", Nothing);
+ }
+ HeapFree(GetProcessHeap(), 0, lpEventsDone);
+ HeapFree(GetProcessHeap(), 0, hdsData);
+ HeapUnlock(GetProcessHeap());
+
+ DBUG_PRINT("Raise error if required");
+ if (err != 0)
+ {
+ win32_maperr(err);
+ uerror("select", Nothing);
+ }
+
+ DBUG_PRINT("Build final result");
+ res = alloc_small(3, 0);
+ Store_field(res, 0, read_list);
+ Store_field(res, 1, write_list);
+ Store_field(res, 2, except_list);
+
+ DBUG_PRINT("out select");
+
+ CAMLreturn(res);
}
diff --git a/otherlibs/win32unix/startup.c b/otherlibs/win32unix/startup.c
index ae584e569..bbf5fe1fe 100644
--- a/otherlibs/win32unix/startup.c
+++ b/otherlibs/win32unix/startup.c
@@ -16,6 +16,8 @@
#include <stdlib.h>
#include <mlvalues.h>
#include "unixsupport.h"
+#include "winworker.h"
+#include "windbug.h"
value val_process_id;
@@ -26,18 +28,27 @@ CAMLprim value win_startup(unit)
int i;
HANDLE h;
+ DBUG_INIT;
+
(void) WSAStartup(MAKEWORD(2, 0), &wsaData);
DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(),
GetCurrentProcess(), &h, 0, TRUE,
DUPLICATE_SAME_ACCESS);
val_process_id = Val_int(h);
+ worker_init();
+
return Val_unit;
}
CAMLprim value win_cleanup(unit)
value unit;
{
+ worker_cleanup();
+
(void) WSACleanup();
+
+ DBUG_CLEANUP;
+
return Val_unit;
}