camlspotter avatar camlspotter committed 74b8ba1

my epoll interface

Comments (0)

Files changed (8)

+.*\.cm[a-z]+$
+.*~$
+\.(sp[io]t|annot|o|cm[a-z]+|orig|omc|lock|a|so)$
+\.(byt|opt|run)$
+\.omakedb$
+\.depend$
+CVS/.*
+OMakeroot
+name="ocaml-epoll"
+version="1.0.0"
+description="Simple epoll interface"
+requires="unix"
+archive(byte)="ocaml_epoll.cmo"
+archive(native)="ocaml_epoll.cmx"
+linkopts = ""
+# How-to-build using OMake
+#
+# yes no | omake --install # to create OMakeroot for the first time
+
+# If OMakeroot is here, include OMyMakefile
+if $(file-exists OMakeroot)
+   include OMyMakefile
+   export
+
+Subdirs()
+
+.PHONY: all install clean
+
+OCAMLINCLUDES +=
+
+OCAMLFLAGS    += -annot -w Ae
+
+CFLAGS+= -fPIC -I $(OCAML_WHERE)
+
+OCAMLPACKS[]= unix
+
+MODULES[] =
+    epoll
+
+CMODULES[] =
+    ocaml_epoll_stub
+
+MyOCamlPackage(ocaml_epoll, $(MODULES), $(CMODULES), $(EMPTY))
+
+OCAMLCFLAGS   +=
+OCAMLOPTFLAGS +=
+OCAML_LINK_FLAGS +=
+OCAML_BYTE_LINK_FLAGS +=
+OCAML_NATIVE_LINK_FLAGS +=
+
+OCAML_LIBS += ocaml_epoll
+OCAML_CLIBS +=
+OCAML_OTHER_LIBS +=
+OCAML_LIB_FLAGS +=
+
+MyOCamlProgram(test, test)
+# ==========================
+# OMyMakefile
+# ==========================
+# Useful functions to build OCaml projects
+
+#| A flag to tell that we can use OMyMakefile functions
+WithOMy=true
+
+.PHONY: all install uninstall clean
+
+# Directories
+# =====================================================================
+
+#| The build root directory  
+BIG_ROOT=$(dir .)
+
+# Installation mark files
+# =======================================================================
+
+#| To enable the installation mark files, you must define INSTALLED path variable
+# for the mark file directory like INSTALLED=$(BIG_ROOT)/installed and make sure
+# the directory $(INSTALLED) exists. This preparation must be done outside of 
+# this OMyMakefile.
+
+#|Returns the installation mark files of $(packs)
+Installed(packs) = 
+  if $(defined INSTALLED)
+      return $(addprefix $(INSTALLED)/, $(packs))
+  else
+      return $(array)
+
+#|Create md5 sum file of $(targets)
+CreateCheckSum(pack, targets)=
+    chan=$(fopen $(pack), w)
+    fprintln($(chan), $(string $(targets)))
+    fprintln($(chan), $(string $(digest $(targets))))
+    close($(chan))
+
+#|Create $(Installed $(pack)) file from the digests of $(targets)
+CreateInstalled(pack, targets)=
+    if $(defined INSTALLED)
+        println(dump md5 $(INSTALLED)/$(pack))
+        CreateCheckSum($(INSTALLED)/$(pack), $(targets))
+
+# Misc tools
+# ======================================================================
+
+#|ditto.
+mkdir_if_not_exists(dir) =
+  if $(not $(test -e $(dir))):
+    mkdir $(dir) 
+  return
+
+# OCaml -where
+# ========================================================================
+
+#|Path to the OCaml library directory
+OCAML_WHERE = $(shell ocamlc -where)
+
+# OCamlFind
+# ========================================================================
+
+#|OMy requires OCamlFind! Do not ask me how to use OMy without OCamlFind. Please.
+USE_OCAMLFIND = true
+OCAMLFIND_DESTDIR= $(shell ocamlfind printconf destdir)
+
+#|Preinstalled libraries which are always available for normal ocaml.
+#
+# You may want to add the required packages which are built and installed out of OMy framework:
+#
+#::
+#
+#   include OMyMakefile
+#   
+#   OCAML_PREINSTALLED_PACKS += llvm # llvm has been installed already, independently
+#   
+#   Subdirs()
+#
+# It includes "findlib" by default. If you want to build findlib in OMy framework, you have to remove it from the list.
+OCAML_PREINSTALLED_PACKS[]= bigarray camlp4 dbm dynlink graphics num num-top stdlib str threads unix findlib
+
+# byte/nat
+NATIVE_ENABLED = $(OCAMLOPT_EXISTS)
+#|If set false in a project directory, byte compilation is disabled there.
+BYTE_ENABLED = true
+
+######################### Compiler
+OCAMLPACKAGEFLAGS=
+
+# Why we need "public." ?
+public.OCamlC() =
+    value $(OCAMLFIND) $(OCAMLC) $(OCAMLPACKAGEFLAGS) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS)\
+              $(OCAMLCFLAGS) $(OCAMLPPFLAGS) $(PREFIXED_OCAMLINCLUDES)
+
+public.OCamlOpt() =
+    value $(OCAMLFIND) $(OCAMLOPT) $(OCAMLPACKAGEFLAGS) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS)\
+              $(OCAMLOPTFLAGS) $(OCAMLPPFLAGS) $(PREFIXED_OCAMLINCLUDES)
+
+# Spot files (OCamlSpotter)
+# ==================================================================
+
+#| OCAML_SPOT is true if the compiler supports ocamlspot
+OCAML_SPOT = false
+match $(string $(shell ocamlc -version)) # We cannot use OCamlC since there may not be ocamlfind
+case $"ocamlspot"
+  OCAML_SPOT = true 
+  export
+
+#| Define OCAML_ANNOT so that custom ocamlc/ocamlopt automatically create spot/spit/annot files, even without -annot option.
+setenv(OCAML_ANNOT, 1)
+
+# Additional implicit rules by file extensions
+
+# annot, spot, spit files
+%.annot %.spot: %.ml %.cmi
+	$(OCamlC) -c $<
+
+%.spit: %.mli 
+	$(OCamlC) -c $<
+
+# Packages
+# =========================================================
+
+#| OCaml packages required for compilation. MyCaml* functions automatically add necessary dependencies over packages in $(OCAMLPACKS).
+# 
+# .. note:: They are also required for dependency analysis.
+public.OCAMLPACKS[]=
+
+#| CamlP4 syntax extension packages required for parsing. MyCaml* functions automatically add necessary dependencies over packages in $(CAMLP4PACKS).
+public.CAMLP4PACKS[]=
+
+# Dependencies
+# =========================================================================
+
+#|Returns packages managed by OMy framework
+OMyManagedPackages(packages) =
+   return $(set-diff $(packages), $(OCAML_PREINSTALLED_PACKS))
+
+#|Add dependencies of any build activity of this directory over $(files).
+#
+# .. note:: These functions introduce implicit rules: *you may need to export it, if you use this function in a local context.*
+RequireFiles(files) =
+    .SCANNER: scan-%: $(files)
+    % : $(files)
+    export
+
+#|Add dependencies of any build activity over $(packages).
+#
+# .. note:: These functions introduce implicit rules: *you may need to export it, if you use this function in a local context.*
+RequirePackages(packages) =
+    RequireFiles($(OMyManagedPackages $(packages)))
+    export
+
+#|Add dependencies of any build activity of this directory over $(targets) and their dependencies
+# Creates an intermidiate md5 memo dependencies.md5
+#
+# .. note:: These functions introduce implicit rules: *you may need to export it, if you use this function in a local context.*
+RequireBuild(targets) =
+    dependencies.md5: $(targets)
+        CreateCheckSum($@, $(sequence-sort $(compare), $(dependencies-all $(targets))))
+    RequireFiles(dependencies.md5)
+    export
+
+#|Add dependencies of OCaml compiled files (cmx, cmo, etc.) over $(packages).
+# $(packages) listed in OCAML_PREINSTALLED_PACKS are ignored.
+#
+# .. note:: These functions introduce implicit rules: *you may need to export it, if you use this function in a local context.*
+#
+# .. note:: Usually you do not need to call this function. Use OCAMLPACKS variable instead. 
+OCamlRequirePackages(packages) =
+    required_packs = $(OMyManagedPackages $(packages))
+    if $(defined INSTALLED)
+      %.cmx %.cmo %.cmi %.cma %.cmxa %.annot %.spot %.spit : $(Installed $(required_packs))
+      export
+    export
+
+#|Add dependencies of OCaml dependency analysis and build over $(packages).
+# Use this for adding dependencies for CamlP4 extensions.
+# $(packages) listed in OCAML_PREINSTALLED_PACKS are ignored.
+#
+# .. note:: These functions introduce implicit rules: *you may need to export it, if you use this function in a local context.*
+#
+# .. note:: Usually you do not need to call this function. Use CAML4PACKS variable instead. 
+OCamlRequireCamlP4Packages(packages) =
+    required_packs = $(OMyManagedPackages $(packages))
+    if $(defined INSTALLED)
+      .SCANNER: scan-ocaml-%: $(Installed $(required_packs))
+      %.cmx %.cmo %.cmi %.cma %.cmxa %.annot %.spot %.spit : $(Installed $(required_packs))
+      export 
+    export
+
+#|``omake xxx.auto.mli`` generates .mli file from xxx.ml 
+%.auto.mli: %.ml
+	$(OCamlC) -i -c $< > $@
+
+# Build rules
+# ==========================================================
+
+# Extend the bundled OCamlPackage with .spot creation
+public.OCamlPackage(name, files) =
+   # XXX: JYH: these variables should be marked private in 0.9.9
+   protected.OFILES   = $(addsuffix $(EXT_OBJ), $(files))
+   protected.CMOFILES = $(addsuffix .cmo, $(files))
+   protected.CMXFILES = $(addsuffix .cmx, $(files))
+
+   protected.OBJ       = $(file $(name)$(EXT_OBJ))
+   protected.CMO       = $(file $(name).cmo)
+   protected.CMX       = $(file $(name).cmx)
+   protected.CMI       = $(file $(name).cmi)
+   protected.MLI       = $(file $(name).mli)
+
+   protected.BYTE_TARGETS   = $(CMO)
+   protected.NATIVE_TARGETS = $(CMX) $(OBJ)
+
+   if $(OCAML_SPOT)
+       if $(BYTE_ENABLED)
+          BYTE_TARGETS += $(file $(name).spot)
+          export
+       else
+          NATIVE_TARGETS += $(file $(name).spot)
+          export
+       export
+
+   protected.TARGETS = $(CMI)
+   if $(NATIVE_ENABLED)
+       TARGETS += $(NATIVE_TARGETS)
+       export
+
+   if $(BYTE_ENABLED)
+       TARGETS += $(BYTE_TARGETS)
+       export
+
+   #
+   # Link commands
+   #
+   protected.BYTE_DEPS = $(CMOFILES)
+   $(BYTE_TARGETS): $(CMOFILES)
+      section rule
+         if $(or $(NATIVE_ENABLED), $(target-exists $(MLI)))
+             BYTE_DEPS += $(CMI)
+             export
+         else
+             BYTE_TARGETS += $(CMI)
+             export
+         $(BYTE_TARGETS): $(BYTE_DEPS)
+            $(OCAMLFIND) $(OCAMLC) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS) \
+                $(OCAMLCFLAGS) $(OCAML_LIB_FLAGS) -pack -o $(CMO) $(OCamlLinkSort $(CMOFILES))
+
+   protected.NATIVE_DEPS = $(CMXFILES) $(OFILES)
+   $(NATIVE_TARGETS): $(NATIVE_DEPS)
+      section rule
+         if $(target-exists $(MLI))
+            NATIVE_DEPS += $(CMI)
+            export
+         else
+            NATIVE_TARGETS += $(CMI)
+            export
+         $(NATIVE_TARGETS): $(NATIVE_DEPS)
+            $(OCAMLFIND) $(OCAMLOPTLINK) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS) \
+                $(OCAMLOPTFLAGS) $(OCAML_LIB_FLAGS) -pack -o $(CMX) $(OCamlLinkSort $(CMXFILES))
+
+   $(CMI):
+      section rule
+         if $(target-exists $(MLI))
+            $(CMI): $(MLI) :scanner: scan-ocaml-$(name).mli
+                $(OCamlC) -c $<
+         elseif $(NATIVE_ENABLED)
+            $(NATIVE_TARGETS) $(CMI): $(NATIVE_DEPS)
+               $(OCAMLFIND) $(OCAMLOPTLINK) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS) \
+                   $(OCAMLOPTFLAGS) $(OCAML_LIB_FLAGS) -pack -o $(CMX) $(OCamlLinkSort $(CMXFILES))
+         else
+            $(BYTE_TARGETS) $(CMI): $(BYTE_DEPS)
+               $(OCAMLFIND) $(OCAMLC) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLFLAGS) \
+                   $(OCAMLCFLAGS) $(OCAML_LIB_FLAGS) -pack -o $(CMO) $(OCamlLinkSort $(CMOFILES))
+
+   return $(TARGETS)
+
+# Add implicit dependencies over the packages declared in OCAMLPACKS and CAMLP4PACKS
+# If this function is used in a local scope, you may want to export. 
+AddLocalOCamlPackageDependencies() =
+  # We make sure the required libraries are installed
+  OCamlRequirePackages($(OCAMLPACKS)) # must be exported!
+  OCamlRequireCamlP4Packages($(OCAMLPACKS) $(CAMLP4PACKS))
+  export
+
+#| Add a rule for OCaml package $(library_name).cmo, $(library_name).cmx and etc.
+#     library_name
+#         target package name
+#     files
+#         ML module names (without .ml)
+#     cmodules
+#         C source files (without .c)
+#     linkopts
+#         C library link option (without OCaml -cclib options)    
+#
+#  You can specify MyOCamlPackageExtras before using this function to install files out of the scope of this rule.   
+#
+#  Example::
+#
+#      MyOCamlPackage(foo, alpha beta, $(EMPTY), $(EMPTY))
+#
+#  Todo: external C library
+MyOCamlPackage(library_name, files, cmodules, linkopts) =
+  AddLocalOCamlPackageDependencies()
+  export # The above thing is local: need to be exported
+
+  CSTUBS=$(addsuffix .o,$(cmodules))
+  CMOS=$(addsuffix .cmo,$(library_name))
+  CMXS=$(addsuffix .cmx,$(library_name))
+  CMA=$(library_name).cma
+  CMXA=$(library_name).cmxa
+
+  if $(not $(defined MyOCamlPackageExtras))
+      MyOCamlPackageExtras[]=
+      export
+
+  CSTUBLIBRARIES=
+  if $(not $(equal $(cmodules), $(EMPTY)))
+      CSTUBLIBRARIES= dll$(library_name).so lib$(library_name).a 
+      export
+
+  # CR jfuruse: I guess we do not need the following
+  # export # export the implicit rule above
+
+  .DEFAULT: $(library_name).cmo $(library_name).cmx $(library_name).cma $(library_name).cmxa $(MyOCamlPackageExtras)
+
+  $(CMA) $(CMXA) $(library_name).a $(CSTUBLIBRARIES) : $(CSTUBS) $(CMOS) $(CMXS)
+      ocamlmklib -verbose -o $(library_name) $(CSTUBS) $(linkopts) $(CMOS) $(CMXS)
+
+  ## the followings are necessary for packing
+
+  OCAMLPACKAGEFLAGS += -for-pack $(capitalize $(library_name))
+  export OCAMLPACKAGEFLAGS
+
+  ## build rule
+
+  OCamlPackage($(library_name), $(files))
+
+  ## clean
+  AutoClean()
+  clean:
+	rm -f $(library_name).spot
+
+  ## install
+
+  # CR jfuruse: x.cmi is required if x.mli does not exist!
+  targets[]=META $(glob i, *.mli) $(library_name).cmi $(library_name).cmo $(library_name).cmx $(library_name).cma $(library_name).cmxa $(library_name).o $(library_name).a $(CSTUBLIBRARIES) $(MyOCamlPackageExtras)
+
+  if $(OCAML_SPOT)
+    targets[]+= $(library_name).spot
+
+  if $(defined INSTALLED)
+    $(Installed $(library_name)): $(targets)
+	$(OCAMLFIND) remove $(library_name)
+	section:
+            $(OCAMLFIND) install $(library_name) $(targets)
+            CreateInstalled($(library_name), $(targets))
+
+    install: $(Installed $(library_name))
+
+    uninstall:
+	rm -f $(Installed $(library_name))
+	$(OCAMLFIND) remove $(library_name)
+
+    export
+  else
+    install:
+	$(OCAMLFIND) remove $(library_name)
+        $(OCAMLFIND) install $(library_name) $(targets)
+
+    uninstall:
+	$(OCAMLFIND) remove $(library_name)
+
+    export
+
+############################################################## build ocaml exec
+
+#| Add a rule to build a program $(name)
+#      name
+#          Name of the program
+#      files
+#          OCaml module names (without .ml)
+MyOCamlProgram(name, files) =
+  AddLocalOCamlPackageDependencies()
+  export # The above thing is local: need to be exported
+
+  $(name).run $(name).opt: $(Installed $(OMyManagedPackages $(OCAMLPACKS)))
+
+  # CR jfuruse: forgot to add the deps over the packages!
+  .DEFAULT: $(OCamlProgram $(name), $(files))
+
+  # The following clean the files twice if MyOCamlPackge coexists,
+  # but who cases ?
+  AutoClean()
+
+#|  Add rules to build OCaml library $(name)
+#        name
+#            Name of the library
+#        files
+#            OCaml module name (without .ml)
+#
+#   .. note :: Probably you should use MyOCamlPackage
+MyOCamlLibrary(name, files) =
+  AddLocalOCamlPackageDependencies()
+  export # The above thing is local: need to be exported
+
+  # CR jfuruse: forgot to add the deps over the packages!
+  .DEFAULT: $(OCamlLibrary $(name), $(files))
+
+  # The following clean the files twice if MyOCamlPacakge coexists,
+  # but who cases ?
+  AutoClean()
+
+# Auto clean
+# ====================================================================
+
+#| Install clean command which cleans all the target files exists under the directory. Use with care.
+AutoClean()=
+    .PHONY: clean
+    clean:
+        rm -f $(filter-proper-targets $(ls R, .))
+
+# Subdir traversal
+# =====================================================================
+
+#| Recursively traverse the subdirs except $(dirs)
+Subdirs_except(dirs) =
+  # println(PWD: $(shell pwd))
+
+  # need to export since .SUBDIRS is evaluated in the global scope
+  export VISIT_SUBDIRS
+
+  sub_omakefiles = $(glob i, */OMakefile)
+  subdirs = $(sub_omakefiles.map $(dirname))
+
+  VISIT_SUBDIRS=$(set-diff $(subdirs), $(dirs))
+
+  # printing requires $(string ...) to convert arrays to strings
+  # println(SUBDIRS: $(string $(VISIT_SUBDIRS)))
+
+  # The rule
+  .SUBDIRS: $(VISIT_SUBDIRS)
+
+#| Recursively traverse all the subdirs
+Subdirs() =
+  Subdirs_except($(array))
+
+#| Recursively traverse the given subdirs $(dirs)
+Subdirs_only(dirs) =
+ .SUBDIRS: $(dirs)
+
+# Dependency dot files for Graphviz
+# ======================================================================
+
+#| Add a rule for ``depend.dot`` for a dependency graph of OCaml files in the current directory
+Dot() =
+	depend.dot: $(ls *.ml *.mli)
+	    $(OCAMLFIND) ocamldoc -I +threads $(OCAMLPACKAGEFLAGS) $(LAZY_OCAMLFINDFLAGS) $(PREFIXED_OCAMLPACKS) $(OCAMLPPFLAGS) $(PREFIXED_OCAMLINCLUDES) -dot -dot-include-all -dot-reduce $+ -o $@
+
+
+#| filter list
+#  example::
+#     is_url(file) =
+#       match $(file)
+#       case $'://'
+#         return true
+#       default
+#         return false
+# 
+#     DOWNLOAD_URLS[]=$(list_filter $(fun x, $(is_url $(x))), $(URLS))
+list_filter(pred, list) =
+  result=
+  foreach(x, $(list))
+    if $(apply $(pred), $(x))
+      result+=$(x)
+      export
+    export
+  return $(result)
+
+#| Obtain files installed as PACK
+GET_OCAMLFIND_OBJS(pack)=
+    return $(Files $(OCAMLFIND_DESTDIR)/$(pack))
+type t = Unix.file_descr
+
+type flags
+
+type flag = 
+  | IN
+  | PRI
+  | OUT
+  | RDNORM
+  | RDBAND
+  | WRNORM
+  | WRBAND
+  | MSG
+  | ERR
+  | HUP
+  | RDHUP
+  | ONESHOT
+  | ET 
+
+external make_flags : flag array -> flags = "caml_to_c_epoll_event_flags"
+external get_flags  : flags -> flag list  = "c_to_caml_epoll_event_flags"
+
+external create  : int -> t = "caml_epoll_create"
+let close = Unix.close
+
+external ctl_add : t -> Unix.file_descr -> flags -> unit = "caml_epoll_ctl_add"
+external ctl_mod : t -> Unix.file_descr -> flags -> unit = "caml_epoll_ctl_mod"
+external ctl_del : t -> Unix.file_descr -> unit          = "caml_epoll_ctl_del"
+
+external wait : t -> maxevents:int -> timeout:int -> (Unix.file_descr * flags) array = "caml_epoll_wait"
+  (* CR jfuruse: 
+     It creates an array, which causes lots of allocations. 
+     We can optimize this by just copying the memory area and provide special iterator on it. *)
+type t = Unix.file_descr
+
+type flags
+
+type flag = 
+  | IN
+  | PRI
+  | OUT
+  | RDNORM
+  | RDBAND
+  | WRNORM
+  | WRBAND
+  | MSG
+  | ERR
+  | HUP
+  | RDHUP
+  | ONESHOT
+  | ET 
+
+external make_flags : flag array -> flags = "caml_to_c_epoll_event_flags"
+external get_flags  : flags -> flag list  = "c_to_caml_epoll_event_flags"
+
+external create  : int -> t = "caml_epoll_create"
+val close : t -> unit
+
+external ctl_add : t -> Unix.file_descr -> flags -> unit = "caml_epoll_ctl_add"
+external ctl_mod : t -> Unix.file_descr -> flags -> unit = "caml_epoll_ctl_mod"
+external ctl_del : t -> Unix.file_descr -> unit          = "caml_epoll_ctl_del"
+
+external wait : t -> maxevents:int -> timeout:int -> (Unix.file_descr * flags) array = "caml_epoll_wait"

ocaml_epoll_stub.c

+#include <caml/mlvalues.h>
+#include <caml/alloc.h>
+#include <caml/memory.h>
+#include <caml/fail.h>
+
+#include <sys/epoll.h>
+
+ // Defined in OCaml's otherlibs/unix/unixsupport.{c,h}
+#define Nothing ((value) 0)
+extern void uerror(char *cmdname, value cmdarg);
+
+// typical bit flags
+
+#define NUM_EPOLL_EVENTS 13
+int caml_epoll_events[NUM_EPOLL_EVENTS] = {
+    EPOLLIN,
+    EPOLLPRI,
+    EPOLLOUT,
+    EPOLLRDNORM,
+    EPOLLRDBAND,
+    EPOLLWRNORM,
+    EPOLLWRBAND,
+    EPOLLMSG,
+    EPOLLERR,
+    EPOLLHUP,
+    EPOLLRDHUP,
+    EPOLLONESHOT,
+    EPOLLET 
+};
+
+inline value caml_to_c_epoll_event_flags(value caml)
+{
+    int res = 0;
+    int size = Wosize_val(caml);
+    int register i;
+    for(i = 0; i < size; i++){
+        res |= caml_epoll_events[Int_val(Field(caml, i))];
+    }
+    return caml_copy_int32(res);
+}
+
+CAMLprim value c_to_caml_epoll_event_flags(value flags)
+{
+    CAMLparam0();
+    CAMLlocal2(res, tmp);
+    res = Val_int(0);
+    int register i;
+    int iflags = Int32_val(flags);
+    for(i = 0; i < NUM_EPOLL_EVENTS; i++){
+        if( iflags & caml_epoll_events[i] ){
+            tmp = caml_alloc_small(2, 0);
+            Field(tmp, 0) = Val_int(i);
+            Field(tmp, 1) = res;
+            res = tmp;
+        }
+    }
+    CAMLreturn(res);
+}
+
+CAMLprim value caml_epoll_create(value size)
+{
+    int ret = epoll_create(Int_val(size));
+    if (ret == -1) uerror("epoll_create", Nothing);
+    return Val_int(ret); 
+}
+
+CAMLprim void caml_epoll_ctl_add(value epfd, value fd, value flags)
+{
+    struct epoll_event ev;
+    ev.events = Int32_val(flags);
+    ev.data.fd = Int_val(fd);
+
+    int ret = epoll_ctl(Int_val(epfd), EPOLL_CTL_ADD, Int_val(fd), &ev);
+    if (ret == -1) uerror("epoll_ctl_add", Nothing);
+    return;
+}
+
+CAMLprim void caml_epoll_ctl_mod(value epfd, value fd, value flags)
+{
+    struct epoll_event ev;
+    ev.events = Int32_val(flags);
+    ev.data.fd = Int_val(fd);
+
+    int ret = epoll_ctl(Int_val(epfd), EPOLL_CTL_MOD, Int_val(fd), &ev);
+    if (ret == -1) uerror("epoll_ctl_mod", Nothing);
+    return;
+}
+
+CAMLprim void caml_epoll_ctl_del(value epfd, value fd)
+{
+    int ret = epoll_ctl(Int_val(epfd), EPOLL_CTL_DEL, Int_val(fd), NULL);
+    if (ret == -1) uerror("epoll_ctl_del", Nothing);
+    return;
+}
+
+CAMLprim value caml_epoll_wait(value epfd, 
+                               value maxevents, 
+                               value timeout)
+{
+    CAMLparam3(epfd, maxevents, timeout);
+    CAMLlocal3(res, tmp, vevents);
+    int imaxevents = Int_val(maxevents);
+    struct epoll_event events[imaxevents]; // no check of maxevents > 0
+    int nfd = epoll_wait(Int_val(epfd), events, imaxevents, Int_val(timeout));
+    if( nfd == -1 ) uerror("epoll_wait", Nothing);
+    res = caml_alloc_tuple(nfd);
+    int i;
+    for (i = 0; i < nfd; i++){
+        vevents = caml_copy_int32(events[i].events); // it must be before alloc_small! Since alloc_small hates other allocs!
+        tmp = caml_alloc_small(2, 0);
+        Field(tmp, 0) = Val_int(events[i].data.fd);
+        Field(tmp, 1) = vevents;
+        Store_field(res, i, tmp);
+    }
+    CAMLreturn(res);
+}
+
+
+
+
+(* Simple echo server *)
+(* Inspired from https://github.com/methane/echoserver/blob/master/server_epoll.cpp *)
+
+open Unix
+
+let port = 5000
+let max_events = 1000
+
+let setup_server_socket port =
+  let sock = Unix.socket PF_INET SOCK_STREAM 0 in
+  Unix.setsockopt sock SO_REUSEADDR true;
+  let sin = ADDR_INET (Unix.inet_addr_any, port) in
+  Unix.bind sock sin;
+  Unix.listen sock 1024;
+  sock
+
+let unix_error_report e s1 s2 =
+  Format.eprintf "Unix error: %s %s %s@."
+    (Unix.error_message e)
+    s1
+    s2
+
+module Conn = struct
+
+  type t = Buffer.t
+
+  let create () = Buffer.create 32
+      
+  let read_buffer_len = 1024
+  let read_buffer = String.create read_buffer_len (* CR jfuruse: not good for multi-threading *)
+
+  let read sock t =
+    let rec read () = 
+        let n = Unix.read sock read_buffer 0 read_buffer_len in
+        if n = 0 then true (* end of input *)
+        else begin
+          Buffer.add_substring t read_buffer 0 n;
+          read ()
+        end
+    in
+    try
+      read ()
+    with
+    | Unix_error (EAGAIN, _, _) -> false
+    | Unix_error (e, s1, s2) -> unix_error_report e s1 s2; assert false
+
+  let write sock t = 
+    let s = Buffer.contents t in (* CR jfuruse: It copies the string. Bad. *)
+    let slen = String.length s in
+    let rec write from =
+      try
+        let n = Unix.write sock s from (slen - from) in
+        let from = from + n in
+        if from < slen then write from
+        else begin
+          Buffer.clear t;
+          true
+        end
+      with
+      | Unix_error (EAGAIN, _, _) -> 
+          Buffer.clear t;
+          Buffer.add_substring t s from (slen - from);
+          false
+      | Unix_error (e, s1, s2) -> unix_error_report e s1 s2; assert false
+    in
+    write 0
+
+  let handle sock t =
+    let read_done = read sock t in
+    let write_done = write sock t in
+    read_done && write_done
+end
+
+let main () =
+  let procs = 
+    let procs = ref 1 in
+    Arg.parse [ "-f", Arg.Int (fun i -> procs := i), "# of process to fork. Never tested." ] (fun _ -> ()) "server_ocaml <opts>";
+    !procs
+  in
+
+  let listener = setup_server_socket port in
+  prerr_endline "listening...";
+
+  begin try 
+    for i = 2 to procs do
+      prerr_endline "Forking...";
+      if Unix.fork () = 0 then raise Exit
+    done
+  with
+  | Exit -> ()
+  end;
+
+  let epfd = Epoll.create 128 in
+
+  let listener_flags = Epoll.make_flags [| Epoll.IN |] in
+  Epoll.ctl_add epfd listener listener_flags;
+
+  Format.eprintf "Listening port %d@." port;
+
+  let client_flags = Epoll.make_flags [| Epoll.IN; Epoll.ET |] in
+
+  let clients = Hashtbl.create 1031 in
+  let tim_prev = ref (Unix.gettimeofday ()) in
+  let proc = ref 0 in
+
+  let rec loop () =
+    let fd_flags_array = Epoll.wait epfd ~maxevents:max_events ~timeout:(-1) in
+    
+    Array.iter (fun (fd, _flags) -> 
+
+      if fd = listener then begin 
+        (* event on listener *)
+        let client, _client_addr = Unix.accept listener in
+        Unix.set_nonblock client;
+        Epoll.ctl_add epfd client client_flags;
+        Hashtbl.add clients client (Conn.create ());
+        (* Format.eprintf "connected: %d@." (Obj.magic client); *)
+
+      end else 
+        (* event on client *)
+        let conn = 
+          try Hashtbl.find clients fd  with
+          | Not_found -> Format.eprintf "Unknown fd %d@." (Obj.magic fd); assert false
+        in
+        let close () =
+          Epoll.ctl_del epfd fd;
+          Unix.close fd;
+          Hashtbl.remove clients fd;
+          (* Format.eprintf "closed: %d@." (Obj.magic fd); *)
+        in
+        try
+          incr proc;
+          if Conn.handle fd conn then close ()
+        with
+        | Not_found -> assert false
+        | Unix_error (e, s1, s2) -> unix_error_report e s1 s2; assert false
+
+    ) fd_flags_array;
+
+    if !proc > 100000 then begin
+      proc := 0;
+      let tim = Unix.gettimeofday () in
+      let d = tim -. !tim_prev in
+      tim_prev := tim;
+      Format.eprintf "%f reqs per sec@." (100000.0 /. d);
+      loop ()
+    end else loop ()
+  in
+
+  loop ()
+
+let _ = main ()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.