| Description | The existing interface for Unix.select is adequate, but it's often not so nice to work with. Here's an example using the current API:
(* You have a data structure with some file descriptors and auxilliary data *)
let foo = [(fd1, "hello"); (fd2, "world"); (fd3, "baz")] in
(* Now you need a list of file descriptors *)
let fds = List.map fst foo in
(* Feed them to select *)
let _, outs, _ = Unix.select [] fds [] (-1.0) in
(* Now do something with each active descriptor *)
List.iter (fun fd ->
let data = List.assoc fd foo in
do_something fd data) outs
The problem is that when you select on a file descriptor there's usually some context you care about that's associated with that descriptor -- e.g. some data to write or a buffer to read into. This means that you need to create an otherwise useless list of file descriptors (fds in the example) before calling select, and after you find out that file descriptor x is ready you have to search through some auxilliary data structure to find the data associated with x. This means extra work for the programmer and the program both before and after the select call.
I would like to propose a new, additional interface for the select call, which I'm calling select2. You can probably guess the idea by looking at the type.
val select2:
(Unix.file_descr * 'a) list ->
(Unix.file_descr * 'b) list ->
(Unix.file_descr * 'c) list ->
float ->
(Unix.file_descr * 'a) list *
(Unix.file_descr * 'b) list *
(Unix.file_descr * 'c) list
To be clear, the idea is that each file descriptor can have a value associated with it, and these values are returned along with the file descriptors on output. This interface is much nicer to work with and even slightly more efficient, since there's no need to search for the contextual data after the call. The example becomes:
(* You have a data structure with some file descriptors and auxilliary data *)
let foo = [(fd1, "hello"); (fd2, "world"); (fd3, "baz")] in
(* Feed it to select2 *)
let _, outs, _ = Unix.select2 [] foo [] (-1.0) in
(* Now do something with the data *)
List.iter (fun (fd, data) -> do_something fd data) outs
I've attached a patch against OCaml 3.09.2 that implements select2 for Unix platforms. The Win32 implementation would be trivial. The vmthreads implementation would probably best be done as an OCaml wrapper of regular select. I will be happy to do this work if the patch is accepted, but first I thought I would see if the OCaml team is interested in the idea. |
| Attached Files | select2.diff [^] (7,179 bytes) 2006-08-24 02:31 [Show Content] [Hide Content]diff -rN -u old-ocaml-3.09.2-n8/otherlibs/threads/unix.ml new-ocaml-3.09.2-n8/otherlibs/threads/unix.ml
--- old-ocaml-3.09.2-n8/otherlibs/threads/unix.ml 2006-08-23 16:39:30.000000000 -0700
+++ new-ocaml-3.09.2-n8/otherlibs/threads/unix.ml 2006-08-23 16:39:35.000000000 -0700
@@ -353,6 +353,10 @@
Resumed_select(r, w, e) -> (r, w, e)
| _ -> ([], [], [])
+(* XXX: n8 *)
+let select2 _ _ _ _ =
+ raise (Invalid_argument "select2 not implemented")
+
type lock_command =
F_ULOCK
| F_LOCK
diff -rN -u old-ocaml-3.09.2-n8/otherlibs/unix/select.c new-ocaml-3.09.2-n8/otherlibs/unix/select.c
--- old-ocaml-3.09.2-n8/otherlibs/unix/select.c 2006-08-23 16:39:31.000000000 -0700
+++ new-ocaml-3.09.2-n8/otherlibs/unix/select.c 2006-08-23 16:39:35.000000000 -0700
@@ -62,8 +62,42 @@
return res;
}
-CAMLprim value unix_select(value readfds, value writefds, value exceptfds,
- value timeout)
+/* XXX: n8 */
+/* Operations on lists of (fd, 'a) pairs instead of lists of fds. */
+static void fdlist2_to_fdset(value fdlist, fd_set *fdset, int *maxfd)
+{
+ value l, p;
+ FD_ZERO(fdset);
+ for (l = fdlist; l != Val_int(0); l = Field(l, 1)) {
+ p = Field(l, 0);
+ int fd = Int_val(Field(p, 0));
+ FD_SET(fd, fdset);
+ if (fd > *maxfd) *maxfd = fd;
+ }
+}
+
+static value fdset_to_fdlist2(value fdlist, fd_set *fdset)
+{
+ value l, p, newres;
+ value res = Val_int(0);
+
+ Begin_roots3(l, p, res);
+ for (l = fdlist; l != Val_int(0); l = Field(l, 1)) {
+ p = Field(l, 0);
+ int fd = Int_val(Field(p, 0));
+ if (FD_ISSET(fd, fdset)) {
+ newres = alloc_small(2, 0);
+ Field(newres, 0) = p;
+ Field(newres, 1) = res;
+ res = newres;
+ }
+ }
+ End_roots();
+ return res;
+}
+
+static value unix_select_helper(value readfds, value writefds, value exceptfds,
+ value timeout, int pairs)
{
fd_set read, write, except;
int maxfd;
@@ -75,9 +109,15 @@
Begin_roots3 (readfds, writefds, exceptfds);
maxfd = -1;
- fdlist_to_fdset(readfds, &read, &maxfd);
- fdlist_to_fdset(writefds, &write, &maxfd);
- fdlist_to_fdset(exceptfds, &except, &maxfd);
+ if (pairs) {
+ fdlist2_to_fdset(readfds, &read, &maxfd);
+ fdlist2_to_fdset(writefds, &write, &maxfd);
+ fdlist2_to_fdset(exceptfds, &except, &maxfd);
+ } else {
+ fdlist_to_fdset(readfds, &read, &maxfd);
+ fdlist_to_fdset(writefds, &write, &maxfd);
+ fdlist_to_fdset(exceptfds, &except, &maxfd);
+ }
tm = Double_val(timeout);
if (tm < 0.0)
tvp = (struct timeval *) NULL;
@@ -90,9 +130,15 @@
retcode = select(maxfd + 1, &read, &write, &except, tvp);
leave_blocking_section();
if (retcode == -1) uerror("select", Nothing);
- readfds = fdset_to_fdlist(readfds, &read);
- writefds = fdset_to_fdlist(writefds, &write);
- exceptfds = fdset_to_fdlist(exceptfds, &except);
+ if (pairs) {
+ readfds = fdset_to_fdlist2(readfds, &read);
+ writefds = fdset_to_fdlist2(writefds, &write);
+ exceptfds = fdset_to_fdlist2(exceptfds, &except);
+ } else {
+ readfds = fdset_to_fdlist(readfds, &read);
+ writefds = fdset_to_fdlist(writefds, &write);
+ exceptfds = fdset_to_fdlist(exceptfds, &except);
+ }
res = alloc_small(3, 0);
Field(res, 0) = readfds;
Field(res, 1) = writefds;
@@ -101,10 +147,26 @@
return res;
}
+CAMLprim value unix_select(value readfds, value writefds, value exceptfds,
+ value timeout)
+{
+ return unix_select_helper(readfds, writefds, exceptfds, timeout, 0);
+}
+
+CAMLprim value unix_select2(value readfds, value writefds, value exceptfds,
+ value timeout)
+{
+ return unix_select_helper(readfds, writefds, exceptfds, timeout, 1);
+}
+
#else
CAMLprim value unix_select(value readfds, value writefds, value exceptfds,
value timeout)
{ invalid_argument("select not implemented"); }
+CAMLprim value unix_select2(value readfds, value writefds, value exceptfds,
+ value timeout)
+{ invalid_argument("select2 not implemented"); }
+
#endif
diff -rN -u old-ocaml-3.09.2-n8/otherlibs/unix/unix.ml new-ocaml-3.09.2-n8/otherlibs/unix/unix.ml
--- old-ocaml-3.09.2-n8/otherlibs/unix/unix.ml 2006-08-23 16:39:31.000000000 -0700
+++ new-ocaml-3.09.2-n8/otherlibs/unix/unix.ml 2006-08-23 16:39:35.000000000 -0700
@@ -296,6 +296,12 @@
external select :
file_descr list -> file_descr list -> file_descr list -> float ->
file_descr list * file_descr list * file_descr list = "unix_select"
+
+(* XXX: n8 *)
+external select2 :
+ (file_descr * 'a) list -> (file_descr * 'b) list -> (file_descr * 'c) list ->
+ float -> (file_descr * 'a) list * (file_descr * 'b) list *
+ (file_descr * 'c) list = "unix_select2"
type lock_command =
F_ULOCK
diff -rN -u old-ocaml-3.09.2-n8/otherlibs/unix/unix.mli new-ocaml-3.09.2-n8/otherlibs/unix/unix.mli
--- old-ocaml-3.09.2-n8/otherlibs/unix/unix.mli 2006-08-23 16:39:31.000000000 -0700
+++ new-ocaml-3.09.2-n8/otherlibs/unix/unix.mli 2006-08-23 16:39:35.000000000 -0700
@@ -618,6 +618,15 @@
and over which an exceptional condition is pending (third
component). *)
+(* XXX: n8 *)
+val select2 :
+ (file_descr * 'a) list -> (file_descr * 'b) list -> (file_descr * 'c) list ->
+ float ->
+ (file_descr * 'a) list * (file_descr * 'b) list * (file_descr * 'c) list
+(** Identical to {!Unix.select}, but a value can be associated with each file
+ descriptor. These values are returned along with their file descriptors in
+ the output lists. *)
+
(** {6 Locking} *)
diff -rN -u old-ocaml-3.09.2-n8/otherlibs/unix/unixLabels.mli new-ocaml-3.09.2-n8/otherlibs/unix/unixLabels.mli
--- old-ocaml-3.09.2-n8/otherlibs/unix/unixLabels.mli 2006-08-23 16:39:31.000000000 -0700
+++ new-ocaml-3.09.2-n8/otherlibs/unix/unixLabels.mli 2006-08-23 16:39:35.000000000 -0700
@@ -628,6 +628,15 @@
and over which an exceptional condition is pending (third
component). *)
+(* XXX: n8 *)
+val select2 :
+ read:(file_descr * 'a) list -> write:(file_descr * 'b) list ->
+ except:(file_descr * 'c) list -> timeout:float ->
+ (file_descr * 'a) list * (file_descr * 'b) list * (file_descr * 'c) list
+(** Identical to {!Unix.select}, but a value can be associated with each file
+ descriptor. These values are returned along with their file descriptors in
+ the output lists. *)
+
(** {6 Locking} *)
diff -rN -u old-ocaml-3.09.2-n8/otherlibs/win32unix/unix.ml new-ocaml-3.09.2-n8/otherlibs/win32unix/unix.ml
--- old-ocaml-3.09.2-n8/otherlibs/win32unix/unix.ml 2006-08-23 16:39:31.000000000 -0700
+++ new-ocaml-3.09.2-n8/otherlibs/win32unix/unix.ml 2006-08-23 16:39:35.000000000 -0700
@@ -857,6 +857,10 @@
external select :
file_descr list -> file_descr list -> file_descr list -> float ->
file_descr list * file_descr list * file_descr list = "unix_select"
+
+(* XXX: n8 *)
+let select2 _ _ _ _ =
+ raise (Invalid_argument "select2 not implemented")
(* High-level network functions *)
|