diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
index 3c990e9..1dd26e9 100644
--- a/.github/workflows/workflow.yml
+++ b/.github/workflows/workflow.yml
@@ -25,6 +25,7 @@ jobs:
           - 4.11.x
           - 4.12.x
           - 4.13.x
+          - 4.14.x
         libev:
           - true
           - false
@@ -38,7 +39,7 @@ jobs:
             !lwt_domain.opam
         include:
           - os: ubuntu-latest
-            ocaml-compiler: ocaml-variants.4.13.1+options,ocaml-option-flambda,ocaml-option-musl,ocaml-option-static
+            ocaml-compiler: ocaml-variants.4.14.0+options,ocaml-option-flambda,ocaml-option-musl,ocaml-option-static
             libev: false
             ppx: true
             domain: false
@@ -52,7 +53,7 @@ jobs:
             domain: true
             local-packages: "*.opam"
           - os: macos-latest
-            ocaml-compiler: 4.13.x
+            ocaml-compiler: 4.14.x
             libev: true
             ppx: true
             domain: false
@@ -60,22 +61,13 @@ jobs:
               *.opam
               !lwt_domain.opam
           - os: windows-latest
-            ocaml-compiler: 4.13.x
+            ocaml-compiler: 4.14.x
             libev: false
             ppx: true
             domain: false
             local-packages: |
               *.opam
               !lwt_domain.opam
-          - os: ubuntu-latest
-            ocaml-compiler: 4.02.x
-            libev: true
-            ppx: false
-            domain: false
-            local-packages: |
-              *.opam
-              !lwt_domain.opam
-              !lwt_ppx.opam
           - os: ubuntu-latest
             ocaml-compiler: 4.03.x
             libev: true
@@ -86,7 +78,7 @@ jobs:
               !lwt_domain.opam
               !lwt_ppx.opam
           - os: macos-latest
-            ocaml-compiler: 4.02.x
+            ocaml-compiler: 4.03.x
             libev: true
             ppx: false
             domain: false
diff --git a/CHANGES b/CHANGES
index 2a08836..554b25d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,31 @@
+===== dev =====
+
+====== Installability ======
+
+  * Lwt is now compatible with OCaml 5.00. Lwt is now incompatible with OCaml 4.02. (#925, #923, Kate Deplaix, Patrick Ferris)
+
+
+====== Additions ======
+
+  * In the Lwt_io module, add `?cloexec:bool` optional arguments to functions that create file descriptors (`pipe`). The `?cloexec` argument is simply forwarded to the wrapped Lwt_unix function. (#872, #911, Antonin Décimo)
+  * Add Lwt_result.error, Lwt_result.iter, and Lwt_result.iter_error for consistency with Stdlib. (#927, Antonin Décimo)
+
+====== Misc ======
+
+  * On Windows, make Lwt_process.create_process duplicate standard handles given to the child process if they're not inheritable, to mimic the behaviour of Unix.create_process. (#909, Antonin Décimo)
+
+====== Fixes ======
+
+  * Fix win32_spawn leaking dev_null fd in the parent process. (#906, Antonin Décimo)
+  * Prefer SetHandleInformation to DuplicateHandle in set_close_on_exec for sockets. DuplicateHandle mustn't be used on sockets. (#907, Antonin Décimo)
+  * Lwt.pick and Lwt.choose select preferentially failed promises as per
+  documentation (#856, #874, Raman Varabets)
+  * Use the WSA_FLAG_NO_HANDLE_INHERIT on Windows when creating sockets with WSASocket if the cloexec (non-inheritable) parameter is true. Fixes a Windows problem where a child process would inherit a supposedly non-inheritable socket. (#910, Antonin Décimo)
+
+====== Deprecations ======
+
+  * Alias Lwt_result.map_err and Lwt_result.bind_lwt_err to Lwt_result.map_error and Lwt_result.bind_lwt_error for consistency with Stdlib. (#927, Antonin Décimo)
+
 ===== 5.5.0 =====
 
 ====== Deprecations ======
diff --git a/README.md b/README.md
index fb469df..9b895ce 100644
--- a/README.md
+++ b/README.md
@@ -119,7 +119,7 @@ promise, and has nothing to do with system or preemptive threads.*
 
 [manual]:   http://ocsigen.org/lwt/
 [rwo-lwt]:  https://github.com/dkim/rwo-lwt#readme
-[mirage-tutorial]: https://mirage.io/wiki/tutorial-lwt
+[mirage-tutorial]: https://mirage.io/docs/tutorial-lwt
 [counter-server]: http://www.baturin.org/code/lwt-counter-server/
 
 
diff --git a/debian/changelog b/debian/changelog
index fda63e0..b15a59b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+lwt (5.5.0+git20220415.1.654952b-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Sat, 07 May 2022 05:45:08 -0000
+
 lwt (5.5.0-1) unstable; urgency=low
 
   [ Stéphane Glondu ]
diff --git a/debian/patches/0001-Comment-out-unix_mcast-tests.patch b/debian/patches/0001-Comment-out-unix_mcast-tests.patch
index 3f41a39..d817e55 100644
--- a/debian/patches/0001-Comment-out-unix_mcast-tests.patch
+++ b/debian/patches/0001-Comment-out-unix_mcast-tests.patch
@@ -8,10 +8,10 @@ environments.
  test/unix/test_mcast.ml | 2 ++
  1 file changed, 2 insertions(+)
 
-diff --git a/test/unix/test_mcast.ml b/test/unix/test_mcast.ml
-index a5bcf40..abd8c9d 100644
---- a/test/unix/test_mcast.ml
-+++ b/test/unix/test_mcast.ml
+Index: lwt/test/unix/test_mcast.ml
+===================================================================
+--- lwt.orig/test/unix/test_mcast.ml
++++ lwt/test/unix/test_mcast.ml
 @@ -72,8 +72,10 @@ let test_mcast name join set_loop =
  let suite =
    suite "unix_mcast"
diff --git a/debian/patches/0003-Disable-page_size-test.patch b/debian/patches/0003-Disable-page_size-test.patch
index 3881f39..73db912 100644
--- a/debian/patches/0003-Disable-page_size-test.patch
+++ b/debian/patches/0003-Disable-page_size-test.patch
@@ -6,10 +6,10 @@ Subject: Disable page_size test
  test/unix/test_lwt_bytes.ml | 2 ++
  1 file changed, 2 insertions(+)
 
-diff --git a/test/unix/test_lwt_bytes.ml b/test/unix/test_lwt_bytes.ml
-index 4c42542..7bb6d5e 100644
---- a/test/unix/test_lwt_bytes.ml
-+++ b/test/unix/test_lwt_bytes.ml
+Index: lwt/test/unix/test_lwt_bytes.ml
+===================================================================
+--- lwt.orig/test/unix/test_lwt_bytes.ml
++++ lwt/test/unix/test_lwt_bytes.ml
 @@ -773,10 +773,12 @@ let suite = suite "lwt_bytes" [
        Lwt.return check
      end;
diff --git a/docs/Makefile b/docs/Makefile
index a554cf6..5485ae4 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -5,18 +5,21 @@ PKGS=\
 	-package bytes -package result \
 	-package bigarray -package unix \
 	-package ocaml-migrate-parsetree -package ppx_tools_versioned \
-	-package react
+	-package react \
+	-package domainslib
 
 INCS=\
 	-I ${BLD}/core/.lwt.objs/byte \
 	-I ${BLD}/ppx/.ppx_lwt.objs/byte \
 	-I ${BLD}/react/.lwt_react.objs/byte \
+	-I ${BLD}/domain/.lwt_domain.objs/byte \
 	-I ${BLD}/unix/.lwt_unix.objs/byte
 
 MLIS=\
 	$(wildcard ${SRC}/core/*.mli) \
 	$(wildcard ${SRC}/ppx/*.mli) \
 	$(wildcard ${SRC}/react/*.mli) \
+	$(wildcard ${SRC}/domain/*.mli) \
 	$(filter-out ${BLD}/unix/lwt_unix.cppo.mli,$(wildcard ${BLD}/unix/*.mli))
 
 MLIS := $(filter-out %.pp.mli,$(MLIS))
diff --git a/docs/apiref-intro b/docs/apiref-intro
index 6f0875a..bd7140c 100644
--- a/docs/apiref-intro
+++ b/docs/apiref-intro
@@ -18,6 +18,7 @@ Lwt_pool
 Lwt_stream
 Lwt_switch
 Lwt_sequence
+Lwt_seq
 Lwt_pqueue
 }
 
@@ -70,6 +71,18 @@ Syntactic sugar for Lwt, such as [let%lwt x = e in e'] syntax for [bind].
 Ppx_lwt
 }
 
+{2 Multicore helpers}
+
+The {e lwt_domain} package provides helpers for executing some computations on
+different cores. It is an Lwt-specific abstraction over the {e domainslib}
+package.
+
+{!modules:
+Lwt_domain
+}
+
+This package depends on the {e core} library and the {e domainslib} package.
+
 {2 Miscellaneous}
 
 The following modules are wrapper for integration of non-Lwt
diff --git a/docs/manual.wiki b/docs/manual.wiki
index f59a983..4e877f3 100644
--- a/docs/manual.wiki
+++ b/docs/manual.wiki
@@ -412,10 +412,10 @@ val p3 : '_a Lwt.t = <abstr>
 
 === {{{let*}}} syntax ===
 
-  To use Lwt with the {{{let*}}} syntax introduced in OCaml 4.08, you can define:
+  To use Lwt with the {{{let*}}} syntax introduced in OCaml 4.08, you can open
+  the {{{Syntax}}} module:
 
-<<code language="ocaml" |let (let*) = Lwt.bind
-let (and*) = Lwt.both
+<<code language="ocaml" |open Syntax
 >>
 
   Then, you can write
@@ -730,6 +730,30 @@ let () =
 
 == Other libraries ==
 
+=== Parallelise computations to other cores ===
+
+  If you have some compute-intensive steps within your program, you can execute
+  them on a separate core. You can get performance benefits from the
+  parallelisation. In addition, whilst your compute-intensive function is running
+  on a different core, your normal I/O-bound tasks continue running on the
+  original core.
+
+  The module {{{Lwt_domain}}} from the {{{lwt_domain}}} package provides all the
+  necessary helpers to achieve this. It is based on the {{{Domainslib}}} library
+  and uses similar concepts (such as tasks and pools).
+
+  First, you need to create a task pool:
+
+<<code language="ocaml" |val setup_pool : ?name:string -> int -> pool
+>>
+
+  Then you simple detach the function calls to the created pool:
+
+<<code language="ocaml" |val detach : pool -> ('a -> 'b) -> 'a -> 'b Lwt.t
+>>
+
+  The returned promise resolves as soon as the function returns.
+
 === Detaching computation to preemptive threads ===
 
   It may happen that you want to run a function which will take time to
diff --git a/lwt.opam b/lwt.opam
index 353585c..9224d58 100644
--- a/lwt.opam
+++ b/lwt.opam
@@ -22,11 +22,10 @@ depends: [
   "cppo" {build & >= "1.1.0"}
   "dune" {>= "1.8.0"}
   "dune-configurator"
-  "mmap" {>= "1.1.0" & "os" != "win32"} # mmap is needed as long as Lwt supports OCaml < 4.06.0.
-  "ocaml" {>= "4.02.0" & "os" != "win32 " | >= "4.06.0"}
+  "mmap" {>= "1.1.0"} # mmap is needed as long as Lwt supports OCaml < 4.06.0.
+  "ocaml" {>= "4.03.0" & os != "win32" | >= "4.06.0"}
   ("ocaml" {>= "4.08.0"} | "ocaml-syntax-shims")
   "ocplib-endian"
-  "result" {os != "win32"} # result is needed as long as Lwt supports OCaml 4.02.
   "seq" # seq is needed as long as Lwt supports OCaml < 4.07.0.
 
   # Until https://github.com/aantron/bisect_ppx/pull/327.
diff --git a/lwt_domain.opam b/lwt_domain.opam
index b9e4332..1035725 100644
--- a/lwt_domain.opam
+++ b/lwt_domain.opam
@@ -19,7 +19,7 @@ dev-repo: "git+https://github.com/ocsigen/lwt.git"
 depends: [
   "dune" {>= "1.8.0"}
   "lwt" {>= "3.0.0"}
-  "ocaml"
+  "ocaml" {>= "4.03"}
   "domainslib" {>= "0.3.2"}
 ]
 
diff --git a/lwt_luv.opam b/lwt_luv.opam
index 6c5d280..caa0d63 100644
--- a/lwt_luv.opam
+++ b/lwt_luv.opam
@@ -17,9 +17,8 @@ dev-repo: "git+https://github.com/ocsigen/lwt.git"
 depends: [
   "lwt"
   "dune" {>= "1.8.0"}
-  "ocaml" {>= "4.02.0"}
+  "ocaml" {>= "4.03.0"}
   "luv"
-  "result" # result is needed as long as Lwt supports OCaml 4.02.
 
   # Until https://github.com/aantron/bisect_ppx/pull/327.
   # "bisect_ppx" {dev & >= "2.0.0"}
diff --git a/lwt_ppx.opam b/lwt_ppx.opam
index 2c4d903..a334730 100644
--- a/lwt_ppx.opam
+++ b/lwt_ppx.opam
@@ -19,7 +19,7 @@ dev-repo: "git+https://github.com/ocsigen/lwt.git"
 depends: [
   "dune" {>= "1.8.0"}
   "lwt"
-  "ocaml" {>= "4.02.0"}
+  "ocaml" {>= "4.03.0"}
   "ppxlib" {>= "0.16.0"}
 ]
 
diff --git a/src/core/dune b/src/core/dune
index 5e779d7..2e19133 100644
--- a/src/core/dune
+++ b/src/core/dune
@@ -26,7 +26,7 @@ let () = Jbuild_plugin.V1.send @@ {|
  (synopsis "Monadic promises and concurrent I/O")
  (wrapped false)
  |} ^ preprocess ^ {|
- (libraries bytes result seq)
+ (libraries bytes seq)
  (flags (:standard -w +A-29)))
 
 (documentation
diff --git a/src/core/lwt.ml b/src/core/lwt.ml
index 9dd731b..da51d2d 100644
--- a/src/core/lwt.ml
+++ b/src/core/lwt.ml
@@ -362,6 +362,13 @@
 module Lwt_sequence = Lwt_sequence
 [@@@ocaml.warning "+3"]
 
+(* TODO: Remove this when the minimum required version of OCaml is >= 4.08 *)
+module Result = struct
+  type (+'a, +'b) t = ('a, 'b) result =
+    | Ok of 'a
+    | Error of 'b
+end
+
 
 
 (* Some sequence-associated storage types
@@ -587,7 +594,7 @@ struct
      later in the module. This is to avoid potential confusion with
      [Pervasives.result]/[Result.result], as the public name would not be
      prefixed with [Lwt.] inside this file. *)
-  type +'a lwt_result = ('a, exn) Result.result
+  type +'a lwt_result = ('a, exn) Result.t
 
   (* This could probably save an allocation by using [Obj.magic]. *)
   let state_of_result = function
@@ -1489,8 +1496,8 @@ sig
   val return_false : bool t
   val return_none : _ option t
   val return_some : 'a -> 'a option t
-  val return_ok : 'a -> ('a, _) Result.result t
-  val return_error : 'e -> (_, 'e) Result.result t
+  val return_ok : 'a -> ('a, _) Result.t t
+  val return_error : 'e -> (_, 'e) Result.t t
   val return_nil : _ list t
 
   val fail_with : string -> _ t
@@ -2642,15 +2649,28 @@ struct
      [choose]/[pick] implementation, which may actually be optimal anyway with
      Flambda. *)
 
-  let count_resolved_promises_in (ps : _ t list) =
-    let accumulate total p =
-      let Internal p = to_internal_promise p in
-      match (underlying p).state with
-      | Fulfilled _ -> total + 1
-      | Rejected _ -> total + 1
-      | Pending _ -> total
+  let count_resolved_promises_in (ps : 'a t list) =
+    let rec count_and_gather_rejected total rejected ps =
+       match ps with
+       | [] -> Result.Error (total, rejected)
+       | p :: ps ->
+            let Internal q = to_internal_promise p in
+            match (underlying q).state with
+            | Fulfilled _ -> count_and_gather_rejected total rejected ps
+            | Rejected _ -> count_and_gather_rejected (total + 1) (p :: rejected) ps
+            | Pending _ -> count_and_gather_rejected total rejected ps
     in
-    List.fold_left accumulate 0 ps
+    let rec count_fulfilled total ps =
+       match ps with
+       | [] -> Result.Ok total
+       | p :: ps ->
+            let Internal q = to_internal_promise p in
+            match (underlying q).state with
+            | Fulfilled _ -> count_fulfilled (total + 1) ps
+            | Rejected _ -> count_and_gather_rejected 1 [p] ps
+            | Pending _ -> count_fulfilled total ps
+    in
+    count_fulfilled 0 ps
 
   (* Evaluates to the [n]th promise in [ps], among only those promises in [ps]
      that are resolved. The caller is expected to ensure that there are at
@@ -2704,7 +2724,7 @@ struct
       invalid_arg
         "Lwt.choose [] would return a promise that is pending forever";
     match count_resolved_promises_in ps with
-    | 0 ->
+    | Result.Ok 0 ->
       let p = new_pending ~how_to_cancel:(propagate_cancel_to_several ps) in
 
       let callback result =
@@ -2718,17 +2738,20 @@ struct
 
       to_public_promise p
 
-    | 1 ->
+    | Result.Ok 1 ->
       nth_resolved ps 0
 
-    | n ->
+    | Result.Ok n ->
+      nth_resolved ps (Random.State.int (Lazy.force prng) n)
+
+    | Result.Error (n, ps) ->
       nth_resolved ps (Random.State.int (Lazy.force prng) n)
 
   let pick ps =
     if ps = [] then
       invalid_arg "Lwt.pick [] would return a promise that is pending forever";
     match count_resolved_promises_in ps with
-    | 0 ->
+    | Ok 0 ->
       let p = new_pending ~how_to_cancel:(propagate_cancel_to_several ps) in
 
       let callback result =
@@ -2743,13 +2766,17 @@ struct
 
       to_public_promise p
 
-    | 1 ->
+    | Ok 1 ->
       nth_resolved_and_cancel_pending ps 0
 
-    | n ->
+    | Ok n ->
       nth_resolved_and_cancel_pending ps
         (Random.State.int (Lazy.force prng) n)
 
+    | Error (n, qs) ->
+      List.iter cancel ps;
+      nth_resolved qs (Random.State.int (Lazy.force prng) n)
+
 
 
   (* If [nchoose ps] or [npick ps] found all promises in [ps] pending, the
diff --git a/src/core/lwt.mli b/src/core/lwt.mli
index a37fe60..fb12c0c 100644
--- a/src/core/lwt.mli
+++ b/src/core/lwt.mli
@@ -1561,9 +1561,49 @@ val return_false : bool t
 
 
 
+(** {3 Trivial promises} *)
+
+val return_some : 'a -> ('a option) t
+(** Counterpart to {!Lwt.return_none}. However, unlike {!Lwt.return_none}, this
+    function performs no {{: #VALreturn_unit} optimization}. This is because it
+    takes an argument, so it cannot be evaluated at initialization time, at
+    which time the argument is not yet available. *)
+
+val return_ok : 'a -> (('a, _) result) t
+(** Like {!Lwt.return_some}, this function performs no optimization.
+
+    @since Lwt 2.6.0 *)
+
+val return_error : 'e -> ((_, 'e) result) t
+(** Like {!Lwt.return_some}, this function performs no optimization.
+
+    @since Lwt 2.6.0 *)
+
+val fail_with : string -> _ t
+(** [Lwt.fail_with s] is an abbreviation for
+
+{[
+Lwt.fail (Stdlib.Failure s)
+]}
+
+    In most cases, it is better to use [failwith s] from the standard library.
+    See {!Lwt.fail} for an explanation. *)
+
+val fail_invalid_arg : string -> _ t
+(** [Lwt.invalid_arg s] is an abbreviation for
+
+{[
+Lwt.fail (Stdlib.Invalid_argument s)
+]}
+
+    In most cases, it is better to use [invalid_arg s] from the standard
+    library. See {!Lwt.fail} for an explanation. *)
+
+
+
 (** {3 Result type} *)
 
-type +'a result = ('a, exn) Result.result
+type nonrec +'a result = ('a, exn) result
 (** Representation of the content of a resolved promise of type
     ['a ]{!Lwt.t}.
 
@@ -1795,7 +1835,9 @@ val make_value : 'a -> 'a result
     {{: https://ocaml.org/api/Stdlib.html#TYPEresult}
     [Ok v]} since OCaml 4.03. If you need compatibility with OCaml 4.02, use
     [Result.Ok] and depend on opam package
-    {{: https://opam.ocaml.org/packages/result/} [result]}. *)
+    {{: https://opam.ocaml.org/packages/result/} [result]}.
+
+    @deprecated Use [Result.Ok] instead *)
 
 val make_error : exn -> _ result
   [@@ocaml.deprecated
@@ -1804,7 +1846,9 @@ val make_error : exn -> _ result
     {{: https://ocaml.org/api/Stdlib.html#TYPEresult}
     [Error exn]} since OCaml 4.03. If you need compatibility with OCaml 4.02,
     use [Result.Error] and depend on opam package
-    {{: https://opam.ocaml.org/packages/result/} [result]}. *)
+    {{: https://opam.ocaml.org/packages/result/} [result]}.
+
+    @deprecated Use [Result.Error] instead. *)
 
 val waiter_of_wakener : 'a u -> 'a t
   [@@ocaml.deprecated
@@ -1814,8 +1858,7 @@ val waiter_of_wakener : 'a u -> 'a t
 (** [Lwt.waiter_of_wakener r] evaluates to the promise associated with resolver
     [r].
 
-    It is recommended to explicitly keep the reference to the promise
-    instead. *)
+    @deprecated Keep the reference to the promise instead. *)
 
 
 
@@ -1836,7 +1879,7 @@ Lwt.on_cancel p (fun () -> Lwt_sequence.remove node);
 p
 ]}
 
-    Use of this function is discouraged for two reasons:
+    @deprecated Use of this function is discouraged for two reasons:
 
     - {!Lwt_sequence} should not be used outside Lwt.
     - This function only exists because it performs a minor internal
@@ -1847,7 +1890,9 @@ val add_task_l : ('a u) Lwt_sequence.t -> 'a t
 " Deprecated because Lwt_sequence is an implementation detail of Lwt. See
   https://github.com/ocsigen/lwt/issues/361"]
 (** Like {!Lwt.add_task_r}, but the equivalent code calls {!Lwt_sequence.add_l}
-    instead. *)
+    instead.
+
+    @deprecated See [add_task_r]. *)
 
 [@@@ocaml.warning "+3"]
 
@@ -1986,46 +2031,6 @@ let g v =
 
 
 
-(** {3 Trivial promises} *)
-
-val return_some : 'a -> ('a option) t
-(** Counterpart to {!Lwt.return_none}. However, unlike {!Lwt.return_none}, this
-    function performs no {{: #VALreturn_unit} optimization}. This is because it
-    takes an argument, so it cannot be evaluated at initialization time, at
-    which time the argument is not yet available. *)
-
-val return_ok : 'a -> (('a, _) Result.result) t
-(** Like {!Lwt.return_some}, this function performs no optimization.
-
-    @since Lwt 2.6.0 *)
-
-val return_error : 'e -> ((_, 'e) Result.result) t
-(** Like {!Lwt.return_some}, this function performs no optimization.
-
-    @since Lwt 2.6.0 *)
-
-val fail_with : string -> _ t
-(** [Lwt.fail_with s] is an abbreviation for
-
-{[
-Lwt.fail (Stdlib.Failure s)
-]}
-
-    In most cases, it is better to use [failwith s] from the standard library.
-    See {!Lwt.fail} for an explanation. *)
-
-val fail_invalid_arg : string -> _ t
-(** [Lwt.invalid_arg s] is an abbreviation for
-
-{[
-Lwt.fail (Stdlib.Invalid_argument s)
-]}
-
-    In most cases, it is better to use [invalid_arg s] from the standard
-    library. See {!Lwt.fail} for an explanation. *)
-
-
-
 (** {3 Unscoped infix operators} *)
 
 val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
diff --git a/src/core/lwt_result.ml b/src/core/lwt_result.ml
index 7d14a07..98c327b 100644
--- a/src/core/lwt_result.ml
+++ b/src/core/lwt_result.ml
@@ -1,19 +1,26 @@
 (* This file is part of Lwt, released under the MIT license. See LICENSE.md for
    details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *)
 
+(* TODO: Remove this when the minimum required version of OCaml is >= 4.08 *)
+module Result = struct
+  type (+'a, +'b) t = ('a, 'b) result =
+    | Ok of 'a
+    | Error of 'b
+end
 
 
 (** Module [Lwt_result]: explicit error handling *)
 
 open Result
 
-type (+'a, +'b) t = ('a, 'b) Result.result Lwt.t
+type (+'a, +'b) t = ('a, 'b) Result.t Lwt.t
 
 let return x = Lwt.return (Ok x)
 let fail e = Lwt.return (Error e)
 
 let lift = Lwt.return
 let ok x = Lwt.map (fun y -> Ok y) x
+let error x = Lwt.map (fun y -> Error y) x
 
 let map f e =
   Lwt.map
@@ -22,12 +29,13 @@ let map f e =
       | Ok x -> Ok (f x))
     e
 
-let map_err f e =
+let map_error f e =
   Lwt.map
     (function
       | Error e -> Error (f e)
       | Ok x -> Ok x)
     e
+let map_err f e = map_error f e
 
 let catch e =
   Lwt.catch
@@ -59,11 +67,12 @@ let bind_result e f =
       | Ok x -> f x)
     e
 
-let bind_lwt_err e f =
+let bind_lwt_error e f =
   Lwt.bind e
     (function
       | Error e -> Lwt.bind (f e) fail
       | Ok x -> return x)
+let bind_lwt_err e f = bind_lwt_error e f
 
 let both a b =
   let s = ref None in
@@ -72,7 +81,7 @@ let both a b =
     | None -> s:= Some e
     | Some _ -> ()
   in
-  let (a,b) = map_err set_once a,map_err set_once b in
+  let (a,b) = map_error set_once a,map_error set_once b in
   let some_assert = function
     | None -> assert false
     | Some e -> Error e
@@ -81,10 +90,22 @@ let both a b =
     (function
       | Ok x, Ok y -> Ok (x,y)
       | Error _, Ok _
-      | Ok _,Error _ 
+      | Ok _,Error _
       | Error _, Error _ -> some_assert !s)
     (Lwt.both a b)
 
+let iter f r =
+  Lwt.bind r
+    (function
+      | Ok x -> f x
+      | Error _ -> Lwt.return_unit)
+
+let iter_error f r =
+  Lwt.bind r
+    (function
+      | Error e -> f e
+      | Ok _ -> Lwt.return_unit)
+
 module Infix = struct
   let (>>=) = bind
   let (>|=) e f = map f e
diff --git a/src/core/lwt_result.mli b/src/core/lwt_result.mli
index bb891b9..5792e9d 100644
--- a/src/core/lwt_result.mli
+++ b/src/core/lwt_result.mli
@@ -10,16 +10,19 @@
 (** This module provides helpers for values of type [('a, 'b) result Lwt.t].
     The module is experimental and may change in the future. *)
 
-type (+'a, +'b) t = ('a, 'b) Result.result Lwt.t
+type (+'a, +'b) t = ('a, 'b) result Lwt.t
 
 val return : 'a -> ('a, _) t
 
 val fail : 'b -> (_, 'b) t
 
-val lift : ('a, 'b) Result.result -> ('a, 'b) t
+val lift : ('a, 'b) result -> ('a, 'b) t
 
 val ok : 'a Lwt.t -> ('a, _) t
 
+val error : 'b Lwt.t -> (_, 'b) t
+(** @since 5.6.0  *)
+
 val catch : 'a Lwt.t -> ('a, exn) t
 (** [catch x] behaves like [return y] if [x] evaluates to [y],
     and like [fail e] if [x] raises [e] *)
@@ -31,15 +34,17 @@ val get_exn : ('a, exn) t -> 'a Lwt.t
 
 val map : ('a -> 'b) -> ('a,'e) t -> ('b,'e) t
 
-val map_err : ('e1 -> 'e2) -> ('a,'e1) t -> ('a,'e2) t
+val map_error : ('e1 -> 'e2) -> ('a,'e1) t -> ('a,'e2) t
+(** @since 5.6.0 *)
 
 val bind : ('a,'e) t -> ('a -> ('b,'e) t) -> ('b,'e) t
 
 val bind_lwt : ('a,'e) t -> ('a -> 'b Lwt.t) -> ('b,'e) t
 
-val bind_lwt_err : ('a,'e1) t -> ('e1 -> 'e2 Lwt.t) -> ('a,'e2) t
+val bind_lwt_error : ('a,'e1) t -> ('e1 -> 'e2 Lwt.t) -> ('a,'e2) t
+(** @since 5.6.0 *)
 
-val bind_result : ('a,'e) t -> ('a -> ('b,'e) Result.result) -> ('b,'e) t
+val bind_result : ('a,'e) t -> ('a -> ('b,'e) result) -> ('b,'e) t
 
 val both : ('a,'e) t -> ('b,'e) t -> ('a * 'b,'e) t
 (** [Lwt.both p_1 p_2] returns a promise that is pending until {e both} promises
@@ -49,6 +54,19 @@ val both : ('a,'e) t -> ('b,'e) t -> ('a * 'b,'e) t
     If both [p_1] and [p_2] resolve with [Error _], the promise is resolved with
     the error that occurred first. *)
 
+val iter : ('a -> unit Lwt.t) -> ('a, 'e) t -> unit Lwt.t
+(** [iter f r] is [f v] if [r] is a promise resolved with [Ok v], and
+    {!Lwt.return_unit} otherwise.
+
+    @since Lwt 5.6.0
+*)
+
+val iter_error : ('e -> unit Lwt.t) -> ('a, 'e) t -> unit Lwt.t
+(** [iter_error f r] is [f v] if [r] is a promise resolved with [Error v],
+    and {!Lwt.return_unit} otherwise.
+
+    @since Lwt 5.6.0
+*)
 
 module Infix : sig
   val (>|=) : ('a,'e) t -> ('a -> 'b) -> ('b,'e) t
@@ -95,3 +113,11 @@ module Syntax : sig
 end
 
 include module type of Infix
+
+(** {3 Deprecated} *)
+
+val map_err : ('e1 -> 'e2) -> ('a,'e1) t -> ('a,'e2) t [@@deprecated "Alias to map_error"]
+(** @deprecated Alias to [map_error] since 5.6.0. *)
+
+val bind_lwt_err : ('a,'e1) t -> ('e1 -> 'e2 Lwt.t) -> ('a,'e2) t [@@deprecated "Alias to bind_lwt_error"]
+(** @deprecated Alias to [bind_lwt_error] since 5.6.0. *)
diff --git a/src/core/lwt_stream.ml b/src/core/lwt_stream.ml
index 011a45b..e75f03d 100644
--- a/src/core/lwt_stream.ml
+++ b/src/core/lwt_stream.ml
@@ -1,6 +1,12 @@
 (* This file is part of Lwt, released under the MIT license. See LICENSE.md for
    details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *)
 
+(* TODO: Remove this when the minimum required version of OCaml is >= 4.08 *)
+module Result = struct
+  type (+'a, +'b) t = ('a, 'b) result =
+    | Ok of 'a
+    | Error of 'b
+end
 
 
 open Lwt.Infix
diff --git a/src/domain/lwt_domain.ml b/src/domain/lwt_domain.ml
index 00e1243..40cd7ff 100644
--- a/src/domain/lwt_domain.ml
+++ b/src/domain/lwt_domain.ml
@@ -1,3 +1,10 @@
+(* TODO: Remove this when the minimum required version of OCaml is >= 4.08 *)
+module Result = struct
+  type (+'a, +'b) t = ('a, 'b) result =
+    | Ok of 'a
+    | Error of 'b
+end
+
 open Lwt.Infix
 
 module C = Domainslib.Chan
diff --git a/src/unix/config/discover.ml b/src/unix/config/discover.ml
index 28af2b0..c29c8c2 100644
--- a/src/unix/config/discover.ml
+++ b/src/unix/config/discover.ml
@@ -64,7 +64,7 @@
 
 module Configurator = Configurator.V1
 let split = Configurator.Flags.extract_blank_separated_words
-let uppercase = String.uppercase [@ocaml.warning "-3"]
+let uppercase = String.uppercase_ascii
 
 
 
diff --git a/src/unix/luv/dune b/src/unix/luv/dune
index 06407e3..20be1e6 100644
--- a/src/unix/luv/dune
+++ b/src/unix/luv/dune
@@ -10,7 +10,7 @@ let () = Jbuild_plugin.V1.send @@ {|
 
 (library
  (public_name lwt_luv)
- (libraries luv lwt lwt.unix result)
+ (libraries luv lwt lwt.unix)
  |} ^ preprocess ^ {|
 )
 |}
diff --git a/src/unix/luv/lwt_luv.ml b/src/unix/luv/lwt_luv.ml
index ae1f7ff..e0bc98a 100644
--- a/src/unix/luv/lwt_luv.ml
+++ b/src/unix/luv/lwt_luv.ml
@@ -1,3 +1,10 @@
+(* TODO: Remove this when the minimum required version of OCaml is >= 4.08 *)
+module Result = struct
+  type (+'a, +'b) t = ('a, 'b) result =
+    | Ok of 'a
+    | Error of 'b
+end
+
 let from_unix : Unix.file_descr -> int = Obj.magic
 
 class engine = object
diff --git a/src/unix/lwt_io.ml b/src/unix/lwt_io.ml
index 5bb2e25..22dcd40 100644
--- a/src/unix/lwt_io.ml
+++ b/src/unix/lwt_io.ml
@@ -1367,8 +1367,8 @@ let eprintl txt = write_line stderr txt
 let eprintf fmt = Printf.ksprintf eprint fmt
 let eprintlf fmt = Printf.ksprintf eprintl fmt
 
-let pipe ?in_buffer ?out_buffer _ =
-  let fd_r, fd_w = Lwt_unix.pipe () in
+let pipe ?cloexec ?in_buffer ?out_buffer _ =
+  let fd_r, fd_w = Lwt_unix.pipe ?cloexec () in
   (of_fd ?buffer:in_buffer ~mode:input fd_r,
    of_fd ?buffer:out_buffer ~mode:output fd_w)
 
diff --git a/src/unix/lwt_io.mli b/src/unix/lwt_io.mli
index 030b648..7a2fcff 100644
--- a/src/unix/lwt_io.mli
+++ b/src/unix/lwt_io.mli
@@ -86,9 +86,10 @@ val null : output_channel
 
 (** {2 Channels creation/manipulation} *)
 
-val pipe : ?in_buffer : Lwt_bytes.t -> ?out_buffer : Lwt_bytes.t -> unit ->
+val pipe : ?cloexec : bool ->
+  ?in_buffer : Lwt_bytes.t -> ?out_buffer : Lwt_bytes.t -> unit ->
   input_channel * output_channel
-  (** [pipe ?in_buffer ?out_buffer ()] creates a pipe using
+  (** [pipe ?cloexec ?in_buffer ?out_buffer ()] creates a pipe using
       {!Lwt_unix.pipe} and makes two channels from the two returned file
       descriptors *)
 
diff --git a/src/unix/lwt_main.mli b/src/unix/lwt_main.mli
index 11c28b9..75a3a6e 100644
--- a/src/unix/lwt_main.mli
+++ b/src/unix/lwt_main.mli
@@ -47,9 +47,9 @@ val yield : unit -> unit Lwt.t [@@deprecated "Use Lwt.pause instead"]
       calling all currently ready callbacks, i.e. it is fulfilled on the next
       “tick.”
 
-      [yield] is now deprecated in favor of the more general {!Lwt.pause}
-      in order to avoid discrepancies in resolution (see below) and stay
-      compatible with other execution environments such as js_of_ocaml.
+      @deprecated Since 5.5.0 [yield] is deprecated in favor of the more general
+      {!Lwt.pause} in order to avoid discrepancies in resolution (see below) and
+      stay compatible with other execution environments such as js_of_ocaml.
 
       Currently, paused promises are resolved more frequently than yielded promises.
       The difference is unintended but existing applications could depend on it.
diff --git a/src/unix/lwt_preemptive.ml b/src/unix/lwt_preemptive.ml
index f4b71ec..b8ecece 100644
--- a/src/unix/lwt_preemptive.ml
+++ b/src/unix/lwt_preemptive.ml
@@ -12,6 +12,13 @@
 module Lwt_sequence = Lwt_sequence
 [@@@ocaml.warning "+3"]
 
+(* TODO: Remove this when the minimum required version of OCaml is >= 4.08 *)
+module Result = struct
+  type (+'a, +'b) t = ('a, 'b) result =
+    | Ok of 'a
+    | Error of 'b
+end
+
 open Lwt.Infix
 
 (* +-----------------------------------------------------------------+
diff --git a/src/unix/lwt_process.ml b/src/unix/lwt_process.ml
index cbd2a6e..5f0bb47 100644
--- a/src/unix/lwt_process.ml
+++ b/src/unix/lwt_process.ml
@@ -36,7 +36,7 @@ let win32_get_fd fd redirection =
   | `Keep ->
     Some fd
   | `Dev_null ->
-    Some (Unix.openfile "nul" [Unix.O_RDWR] 0o666)
+    Some (Unix.openfile "nul" [Unix.O_RDWR; Unix.O_CLOEXEC] 0o666)
   | `Close ->
     None
   | `FD_copy fd' ->
@@ -56,11 +56,12 @@ let win32_quote arg =
     Filename.quote arg
 
 let win32_spawn
-    (prog, args) env ?cwd
+    ?cwd
     ?(stdin:redirection=`Keep)
     ?(stdout:redirection=`Keep)
     ?(stderr:redirection=`Keep)
-    toclose =
+    (prog, args) env
+  =
   let cmdline = String.concat " " (List.map win32_quote (Array.to_list args)) in
   let env =
     match env with
@@ -82,7 +83,6 @@ let win32_spawn
       Bytes.set res ofs '\000';
       Some (Bytes.unsafe_to_string res)
   in
-  List.iter Unix.set_close_on_exec toclose;
   let stdin_fd  = win32_get_fd Unix.stdin stdin
   and stdout_fd = win32_get_fd Unix.stdout stdout
   and stderr_fd = win32_get_fd Unix.stderr stderr in
@@ -91,15 +91,15 @@ let win32_spawn
       (if prog = "" then None else Some prog) cmdline env cwd
       (stdin_fd, stdout_fd, stderr_fd)
   in
-  let close = function
-    | `FD_move fd ->
-      Unix.close fd
-    | _ ->
-      ()
+  let close fd fd' =
+    match fd with
+    | `FD_move _ | `Dev_null ->
+      Unix.close (match fd' with Some fd' -> fd' | _ -> assert false)
+    | _ -> ()
   in
-  close stdin;
-  close stdout;
-  close stderr;
+  close stdin stdin_fd;
+  close stdout stdout_fd;
+  close stderr stderr_fd;
   proc
 
 external win32_wait_job : Unix.file_descr -> int Lwt_unix.job =
@@ -123,7 +123,7 @@ let unix_redirect fd redirection = match redirection with
     ()
   | `Dev_null ->
     Unix.close fd;
-    let dev_null = Unix.openfile "/dev/null" [Unix.O_RDWR] 0o666 in
+    let dev_null = Unix.openfile "/dev/null" [Unix.O_RDWR; Unix.O_CLOEXEC] 0o666 in
     if fd <> dev_null then begin
       Unix.dup2 dev_null fd;
       Unix.close dev_null
@@ -139,18 +139,18 @@ let unix_redirect fd redirection = match redirection with
 external unix_exit : int -> 'a = "unix_exit"
 
 let unix_spawn
-    (prog, args) env ?cwd
+    ?cwd
     ?(stdin:redirection=`Keep)
     ?(stdout:redirection=`Keep)
     ?(stderr:redirection=`Keep)
-    toclose =
+    (prog, args) env
+  =
   let prog = if prog = "" && Array.length args > 0 then args.(0) else prog in
   match Lwt_unix.fork () with
   | 0 ->
     unix_redirect Unix.stdin stdin;
     unix_redirect Unix.stdout stdout;
     unix_redirect Unix.stderr stderr;
-    List.iter Unix.close toclose;
     begin
       try
         begin match cwd with
@@ -263,41 +263,38 @@ class virtual common timeout proc channels =
   end
 
 class process_none ?timeout ?env ?cwd ?stdin ?stdout ?stderr cmd =
-  let proc = spawn cmd env ?cwd ?stdin ?stdout ?stderr [] in
+  let proc = spawn cmd env ?cwd ?stdin ?stdout ?stderr in
   object
     inherit common timeout proc []
   end
 
 class process_in ?timeout ?env ?cwd ?stdin ?stderr cmd =
-  let stdout_r, stdout_w = Unix.pipe () in
-  let proc =
-    spawn cmd env ?cwd ?stdin ~stdout:(`FD_move stdout_w) ?stderr [stdout_r] in
-  let stdout = Lwt_io.of_unix_fd ~mode:Lwt_io.input stdout_r in
+  let stdout_r, stdout_w = Lwt_unix.pipe_in ~cloexec:true () in
+  let proc = spawn cmd env ?cwd ?stdin ~stdout:(`FD_move stdout_w) ?stderr in
+  let stdout = Lwt_io.of_fd ~mode:Lwt_io.input stdout_r in
   object
     inherit common timeout proc [cast_chan stdout]
     method stdout = stdout
   end
 
 class process_out ?timeout ?env ?cwd ?stdout ?stderr cmd =
-  let stdin_r, stdin_w = Unix.pipe () in
-  let proc =
-    spawn cmd env ?cwd ~stdin:(`FD_move stdin_r) ?stdout ?stderr [stdin_w] in
-  let stdin = Lwt_io.of_unix_fd ~mode:Lwt_io.output stdin_w in
+  let stdin_r, stdin_w = Lwt_unix.pipe_out ~cloexec:true () in
+  let proc = spawn cmd env ?cwd ~stdin:(`FD_move stdin_r) ?stdout ?stderr in
+  let stdin = Lwt_io.of_fd ~mode:Lwt_io.output stdin_w in
   object
     inherit common timeout proc [cast_chan stdin]
     method stdin = stdin
   end
 
 class process ?timeout ?env ?cwd ?stderr cmd =
-  let stdin_r, stdin_w = Unix.pipe ()
-  and stdout_r, stdout_w = Unix.pipe () in
+  let stdin_r, stdin_w = Lwt_unix.pipe_out ~cloexec:true ()
+  and stdout_r, stdout_w = Lwt_unix.pipe_in ~cloexec:true () in
   let proc =
     spawn
       cmd env ?cwd ~stdin:(`FD_move stdin_r) ~stdout:(`FD_move stdout_w) ?stderr
-      [stdin_w; stdout_r]
   in
-  let stdin = Lwt_io.of_unix_fd ~mode:Lwt_io.output stdin_w
-  and stdout = Lwt_io.of_unix_fd ~mode:Lwt_io.input stdout_r in
+  let stdin = Lwt_io.of_fd ~mode:Lwt_io.output stdin_w
+  and stdout = Lwt_io.of_fd ~mode:Lwt_io.input stdout_r in
   object
     inherit common timeout proc [cast_chan stdin; cast_chan stdout]
     method stdin = stdin
@@ -305,20 +302,19 @@ class process ?timeout ?env ?cwd ?stderr cmd =
   end
 
 class process_full ?timeout ?env ?cwd cmd =
-  let stdin_r, stdin_w = Unix.pipe ()
-  and stdout_r, stdout_w = Unix.pipe ()
-  and stderr_r, stderr_w = Unix.pipe () in
+  let stdin_r, stdin_w = Lwt_unix.pipe_out ~cloexec:true ()
+  and stdout_r, stdout_w = Lwt_unix.pipe_in ~cloexec:true ()
+  and stderr_r, stderr_w = Lwt_unix.pipe_in ~cloexec:true () in
   let proc =
     spawn
       cmd env ?cwd
       ~stdin:(`FD_move stdin_r)
       ~stdout:(`FD_move stdout_w)
       ~stderr:(`FD_move stderr_w)
-      [stdin_w; stdout_r; stderr_r]
   in
-  let stdin = Lwt_io.of_unix_fd ~mode:Lwt_io.output stdin_w
-  and stdout = Lwt_io.of_unix_fd ~mode:Lwt_io.input stdout_r
-  and stderr = Lwt_io.of_unix_fd ~mode:Lwt_io.input stderr_r in
+  let stdin = Lwt_io.of_fd ~mode:Lwt_io.output stdin_w
+  and stdout = Lwt_io.of_fd ~mode:Lwt_io.input stdout_r
+  and stderr = Lwt_io.of_fd ~mode:Lwt_io.input stderr_r in
   object
     inherit
       common timeout proc [cast_chan stdin; cast_chan stdout; cast_chan stderr]
diff --git a/src/unix/lwt_process_stubs.c b/src/unix/lwt_process_stubs.c
index ff8ffa3..88318b9 100644
--- a/src/unix/lwt_process_stubs.c
+++ b/src/unix/lwt_process_stubs.c
@@ -34,6 +34,31 @@ static HANDLE get_handle(value opt) {
     return INVALID_HANDLE_VALUE;
 }
 
+/* Ensures the handle [h] is inheritable. Returns the handle for the
+   child process in [hStd] and in [to_close] if it needs to be closed
+   after CreateProcess. */
+static int ensure_inheritable(HANDLE h /* in */,
+                              HANDLE * hStd /* out */,
+                              HANDLE * to_close /* out */)
+{
+  DWORD flags;
+  HANDLE hp;
+
+  if (h == INVALID_HANDLE_VALUE || h == NULL)
+    return 1;
+  if (! GetHandleInformation(h, &flags))
+    return 0;
+  hp = GetCurrentProcess();
+  if (! (flags & HANDLE_FLAG_INHERIT)) {
+    if (! DuplicateHandle(hp, h, hp, hStd, 0, TRUE, DUPLICATE_SAME_ACCESS))
+      return 0;
+    *to_close = *hStd;
+  } else {
+    *hStd = h;
+  }
+  return 1;
+}
+
 CAMLprim value lwt_process_create_process(value prog, value cmdline, value env,
                                           value cwd, value fds) {
   CAMLparam5(prog, cmdline, env, cwd, fds);
@@ -41,8 +66,29 @@ CAMLprim value lwt_process_create_process(value prog, value cmdline, value env,
 
   STARTUPINFO si;
   PROCESS_INFORMATION pi;
-  DWORD flags = CREATE_UNICODE_ENVIRONMENT;
-  BOOL ret;
+  DWORD flags = 0, err;
+  HANDLE hp, fd0, fd1, fd2;
+  HANDLE to_close0 = INVALID_HANDLE_VALUE, to_close1 = INVALID_HANDLE_VALUE,
+    to_close2 = INVALID_HANDLE_VALUE;
+
+  fd0 = get_handle(Field(fds, 0));
+  fd1 = get_handle(Field(fds, 1));
+  fd2 = get_handle(Field(fds, 2));
+
+  err = ERROR_SUCCESS;
+  ZeroMemory(&si, sizeof(si));
+  ZeroMemory(&pi, sizeof(pi));
+  si.cb = sizeof(si);
+  si.dwFlags = STARTF_USESTDHANDLES;
+
+  /* If needed, duplicate the handles fd1, fd2, fd3 to make sure they
+     are inheritable. */
+  if (! ensure_inheritable(fd0, &si.hStdInput, &to_close0) ||
+      ! ensure_inheritable(fd1, &si.hStdOutput, &to_close1) ||
+      ! ensure_inheritable(fd2, &si.hStdError, &to_close2)) {
+    err = GetLastError(); goto ret;
+  }
+
 
 #define string_option(opt) \
   (Is_block(opt) ? caml_stat_strdup_to_os(String_val(Field(opt, 0))) : NULL)
@@ -55,23 +101,25 @@ CAMLprim value lwt_process_create_process(value prog, value cmdline, value env,
 
 #undef string_option
 
-  ZeroMemory(&si, sizeof(si));
-  ZeroMemory(&pi, sizeof(pi));
-  si.cb = sizeof(si);
-  si.dwFlags = STARTF_USESTDHANDLES;
-  si.hStdInput = get_handle(Field(fds, 0));
-  si.hStdOutput = get_handle(Field(fds, 1));
-  si.hStdError = get_handle(Field(fds, 2));
+  flags |= CREATE_UNICODE_ENVIRONMENT;
+  if (! CreateProcess(progs, cmdlines, NULL, NULL, TRUE, flags,
+                      envs, cwds, &si, &pi)) {
+    err = GetLastError();
+  }
 
-  ret = CreateProcess(progs, cmdlines, NULL, NULL, TRUE, flags,
-                      envs, cwds, &si, &pi);
   caml_stat_free(progs);
   caml_stat_free(cmdlines);
   caml_stat_free(envs);
   caml_stat_free(cwds);
 
-  if (!ret) {
-    win32_maperr(GetLastError());
+ret:
+/* Close the handles if we duplicated them above. */
+  if (to_close0 != INVALID_HANDLE_VALUE) CloseHandle(to_close0);
+  if (to_close1 != INVALID_HANDLE_VALUE) CloseHandle(to_close1);
+  if (to_close2 != INVALID_HANDLE_VALUE) CloseHandle(to_close2);
+
+  if (err != ERROR_SUCCESS) {
+    win32_maperr(err);
     uerror("CreateProcess", Nothing);
   }
 
diff --git a/src/unix/lwt_unix.cppo.ml b/src/unix/lwt_unix.cppo.ml
index 876a35f..448ff2a 100644
--- a/src/unix/lwt_unix.cppo.ml
+++ b/src/unix/lwt_unix.cppo.ml
@@ -12,6 +12,13 @@
 module Lwt_sequence = Lwt_sequence
 [@@@ocaml.warning "+3"]
 
+(* TODO: Remove this when the minimum required version of OCaml is >= 4.08 *)
+module Result = struct
+  type (+'a, +'b) t = ('a, 'b) result =
+    | Ok of 'a
+    | Error of 'b
+end
+
 open Lwt.Infix
 
 (* +-----------------------------------------------------------------+
@@ -1717,17 +1724,7 @@ let shutdown ch shutdown_command =
   check_descriptor ch;
   Unix.shutdown ch.fd shutdown_command
 
-external stub_socketpair : socket_domain -> socket_type -> int -> Unix.file_descr * Unix.file_descr = "lwt_unix_socketpair_stub"
-
-#if OCAML_VERSION >= (4, 05, 0)
-let stub_socketpair ?cloexec dom typ proto =
-  let (s1, s2) = stub_socketpair dom typ proto in
-  if cloexec = Some true then begin
-    Unix.set_close_on_exec s1;
-    Unix.set_close_on_exec s2
-  end;
-  (s1, s2)
-#endif
+external stub_socketpair : ?cloexec:bool -> socket_domain -> socket_type -> int -> Unix.file_descr * Unix.file_descr = "lwt_unix_socketpair_stub"
 
 let socketpair ?cloexec dom typ proto =
   let (s1, s2) =
@@ -1739,12 +1736,15 @@ let socketpair ?cloexec dom typ proto =
     if Sys.win32 then stub_socketpair ?cloexec dom typ proto
     else Unix.socketpair ?cloexec dom typ proto in
 #else
-    if Sys.win32 then stub_socketpair dom typ proto
-    else Unix.socketpair dom typ proto in
-  if cloexec = Some true then begin
-    Unix.set_close_on_exec s1;
-    Unix.set_close_on_exec s2
-  end;
+    if Sys.win32 then stub_socketpair ?cloexec dom typ proto
+    else begin
+      let (s1, s2) = Unix.socketpair dom typ proto in
+      if cloexec = Some true then begin
+        Unix.set_close_on_exec s1;
+        Unix.set_close_on_exec s2
+      end;
+      (s1, s2)
+    end in
 #endif
   (mk_ch ~blocking:false s1, mk_ch ~blocking:false s2)
 
@@ -1899,7 +1899,7 @@ type socket_int_option =
   Unix.socket_int_option =
   | SO_SNDBUF
   | SO_RCVBUF
-  | SO_ERROR
+  | SO_ERROR [@ocaml.deprecated "Use Unix.getsockopt_error instead."]
   | SO_TYPE
   | SO_RCVLOWAT
   | SO_SNDLOWAT
diff --git a/src/unix/lwt_unix.cppo.mli b/src/unix/lwt_unix.cppo.mli
index 735573d..7eba23d 100644
--- a/src/unix/lwt_unix.cppo.mli
+++ b/src/unix/lwt_unix.cppo.mli
@@ -49,13 +49,17 @@ val handle_unix_error : ('a -> 'b Lwt.t) -> 'a -> 'b Lwt.t
 
 val sleep : float -> unit Lwt.t
   (** [sleep d] is a promise that remains in a pending state for [d] seconds
-      and after which it is resolved with value [()]. *)
+      after which it is resolved with value [()]. *)
 
 val yield : unit -> unit Lwt.t [@@deprecated "Use Lwt.pause instead"]
   (** [yield ()] is a promise in a pending state. It resumes itself as soon as
-      possible and resolves with value [()]. *)
+      possible and resolves with value [()].
 
-val auto_yield : float -> (unit -> unit Lwt.t) [@@deprecated "Use Lwt.auto_pause instead"]
+      @deprecated Since 5.5.0 [yield] is deprecated. Use the more general
+      {!Lwt.pause} instead. See {!Lwt_main.yield} for additional details. *)
+
+val auto_yield : float -> (unit -> unit Lwt.t) [@@deprecated "Use Lwt_unix.auto_pause instead"]
+(** @deprecated Since 5.5.0. Use {!auto_pause} instead. *)
 
 val auto_pause : float -> (unit -> unit Lwt.t)
   (** [auto_pause timeout] returns a function [f], and [f ()] has the following
@@ -71,7 +75,11 @@ exception Timeout
 
 val timeout : float -> 'a Lwt.t
   (** [timeout d] is a promise that remains pending for [d] seconds
-      and then is rejected with {!Timeout}. *)
+      and then is rejected with {!Timeout}.
+
+      @raise Timeout
+      The promise [timeout d] is rejected with {!Timeout} unless it is
+      cancelled. *)
 
 val with_timeout : float -> (unit -> 'a Lwt.t) -> 'a Lwt.t
   (** [with_timeout d f] is a short-hand for:
@@ -79,16 +87,16 @@ val with_timeout : float -> (unit -> 'a Lwt.t) -> 'a Lwt.t
       {[
         Lwt.pick [Lwt_unix.timeout d; f ()]
       ]}
-  *)
 
-(** {2 Operation on file-descriptors} *)
+      @raise Timeout 
+      The promise [with_timeout d f] raises {!Timeout} if the promise returned
+      by [f ()] takes more than [d] seconds to resolve. *)
 
-type file_descr
-  (** The abstract type for {b file descriptor}s. A Lwt {b file
-      descriptor} is a pair of a unix {b file descriptor} (of type
-      [Unix.file_descr]) and a {b state}.
+(** {2 Operations on file-descriptors} *)
 
-      A {b file descriptor} may be:
+type file_descr
+  (** The abstract type for {b file descriptor}s. An Lwt {b file
+      descriptor} can be
 
       - {b opened}, in which case it is fully usable
       - {b closed} or {b aborted}, in which case it is no longer
@@ -106,20 +114,20 @@ type state =
           possible is {!close}, all others will fail. *)
 
 val state : file_descr -> state
-  (** [state fd] returns the state of [fd] *)
+  (** [state fd] returns the {!type:state} of [fd] *)
 
 val unix_file_descr : file_descr -> Unix.file_descr
   (** Returns the underlying unix {b file descriptor}. It always
       succeeds, even if the {b file descriptor}'s state is not
-      [Open]. *)
+      [Opened]. *)
 
 val of_unix_file_descr : ?blocking : bool -> ?set_flags : bool -> Unix.file_descr -> file_descr
-(** Wraps a [Unix] file descriptor [fd] in an [Lwt_unix.file_descr] [fd'].
+(** Wraps a [Unix] file descriptor [fd] in an Lwt {!type:file_descr} [fd'].
 
     [~blocking] controls the {e internal} strategy Lwt uses to perform I/O on
     the underlying [fd]. Regardless of [~blocking], at the API level,
     [Lwt_unix.read], [Lwt_unix.write], etc. on [fd'] {e always} block the Lwt
-    thread, but {e never} block the whole process. However, for performance
+    promise, but {e never} block the whole process. However, for performance
     reasons, it is important that [~blocking] match the actual blocking mode of
     [fd].
 
@@ -184,7 +192,7 @@ val abort : file_descr -> exn -> unit
 val fork : unit -> int
   (** [fork ()] does the same as [Unix.fork]. You must use this
       function instead of [Unix.fork] when you want to use Lwt in the
-      child process.
+      child process, even if you have not started using Lwt before the fork.
 
       Notes:
 
@@ -240,7 +248,7 @@ val wait4 : wait_flag list -> int -> (int * process_status * resource_usage) Lwt
       On windows it will always returns [{ utime = 0.0; stime = 0.0 }]. *)
 
 val wait_count : unit -> int
-  (** Returns the number of threads waiting for a child to
+  (** Returns the number of promises waiting for a child process to
       terminate. *)
 
 val system : string -> process_status Lwt.t
@@ -252,14 +260,13 @@ val system : string -> process_status Lwt.t
 (** {2 Basic file input/output} *)
 
 val stdin : file_descr
-  (** The standard {b file descriptor} for input. This one is usually
-      a terminal is the program is started from a terminal. *)
+  (** The {b file descriptor} for standard input. *)
 
 val stdout : file_descr
-  (** The standard {b file descriptor} for output *)
+  (** The {b file descriptor} for standard output. *)
 
 val stderr : file_descr
-  (** The standard {b file descriptor} for printing error messages *)
+  (** The {b file descriptor} for standard error. *)
 
 type file_perm = Unix.file_perm
 
@@ -293,20 +300,20 @@ val close : file_descr -> unit Lwt.t
 val read : file_descr -> bytes -> int -> int -> int Lwt.t
 (** [read fd buf ofs len] reads up to [len] bytes from [fd], and writes them to
     [buf], starting at offset [ofs]. The function immediately evaluates to an
-    Lwt thread, which waits for the operation to complete. If it completes
-    successfully, the thread indicates the number of bytes actually read, or
+    Lwt promise which waits for the operation to complete. If it completes
+    successfully, the promise resolves to the number of bytes actually read, or
     zero if the end of file has been reached.
 
-    Note that the Lwt thread waits for data (or end of file) even if the
+    Note that the Lwt promise waits for data (or end of file) even if the
     underlying file descriptor is in non-blocking mode. See
     {!of_unix_file_descr} for a discussion of non-blocking I/O and Lwt.
 
     If Lwt is using blocking I/O on [fd], [read] writes data into a temporary
     buffer, then copies it into [buf].
 
-    The thread can fail with any exception that can be raised by [Unix.read],
-    except [Unix.Unix_error Unix.EAGAIN], [Unix.Unix_error Unix.EWOULDBLOCK] or
-    [Unix.Unix_error Unix.EINTR]. *)
+    The promise can be rejected with any exception that can be raised by
+    [Unix.read], except [Unix.Unix_error Unix.EAGAIN],
+    [Unix.Unix_error Unix.EWOULDBLOCK] or [Unix.Unix_error Unix.EINTR]. *)
 
 val pread : file_descr -> bytes -> file_offset:int -> int -> int -> int Lwt.t
 (** [pread fd buf ~file_offset ofs len] on file descriptors allowing seek,
@@ -316,23 +323,23 @@ val pread : file_descr -> bytes -> file_offset:int -> int -> int -> int Lwt.t
     On Unix systems, the file descriptor position is unaffected. On Windows
     it is changed to be just after the last read position.
 
-    The thread can fail with any exception that can be raised by [read] or
-    [lseek]. *)
+    The promise can be rejected with any exception that can be raised by [read]
+    or [lseek]. *)
 
 val write : file_descr -> bytes -> int -> int -> int Lwt.t
 (** [write fd buf ofs len] writes up to [len] bytes to [fd] from [buf], starting
-    at buffer offset [ofs]. The function immediately evaluates to an Lwt thread,
+    at buffer offset [ofs]. The function immediately evaluates to an Lwt promise
     which waits for the operation to complete. If the operation completes
-    successfully, the thread indicates the number of bytes actually written,
+    successfully, the promise resolves to the number of bytes actually written,
     which may be less than [len].
 
-    Note that the Lwt thread waits to write even if the underlying file
+    Note that the Lwt promise waits to write even if the underlying file
     descriptor is in non-blocking mode. See {!of_unix_file_descr} for a
     discussion of non-blocking I/O and Lwt.
 
     If Lwt is using blocking I/O on [fd], [buf] is copied before writing.
 
-    The thread can fail with any exception that can be raised by
+    The promise can be rejected with any exception that can be raised by
     [Unix.single_write], except [Unix.Unix_error Unix.EAGAIN],
     [Unix.Unix_error Unix.EWOULDBLOCK] or [Unix.Unix_error Unix.EINTR]. *)
 
@@ -345,8 +352,8 @@ val pwrite : file_descr -> bytes -> file_offset:int -> int -> int -> int Lwt.t
     On Unix systems, the file descriptor position is unaffected. On Windows
     it is changed to be just after the last written position.
 
-    The thread can fail with any exception that can be raised by [write] or
-    [lseek]. *)
+    The promise can be rejected with any exception that can be raised by [write]
+    or [lseek]. *)
 
 val write_string : file_descr -> string -> int -> int -> int Lwt.t
   (** See {!write}. *)
@@ -409,15 +416,15 @@ end
 
 val readv : file_descr -> IO_vectors.t -> int Lwt.t
 (** [readv fd vs] reads bytes from [fd] into the buffer slices [vs]. If the
-    operation completes successfully, the resulting thread indicates the number
-    of bytes read.
+    operation completes successfully, the resulting promise resolves to the
+    number of bytes read.
 
     Data is always read directly into [Bigarray] slices. If the Unix file
     descriptor underlying [fd] is in non-blocking mode, data is also read
     directly into [bytes] slices. Otherwise, data for [bytes] slices is first
     read into temporary buffers, then copied.
 
-    Note that the returned Lwt thread is blocked until failure or a successful
+    Note that the returned Lwt promise is pending until failure or a successful
     read, even if the underlying file descriptor is in non-blocking mode. See
     {!of_unix_file_descr} for a discussion of non-blocking I/O and Lwt.
 
@@ -435,13 +442,13 @@ val readv : file_descr -> IO_vectors.t -> int Lwt.t
 val writev : file_descr -> IO_vectors.t -> int Lwt.t
 (** [writev fd vs] writes the bytes in the buffer slices [vs] to the file
     descriptor [fd]. If the operation completes successfully, the resulting
-    thread indicates the number of bytes written.
+    promise resolves to the number of bytes written.
 
     If the Unix file descriptor underlying [fd] is in non-blocking mode,
     [writev] does not make a copy the bytes before writing. Otherwise, it copies
     [bytes] slices, but not [Bigarray] slices.
 
-    Note that the returned Lwt thread is blocked until failure or a successful
+    Note that the returned Lwt promise is pending until failure or a successful
     write, even if the underlying descriptor is in non-blocking mode. See
     {!of_unix_file_descr} for a discussion of non-blocking I/O and Lwt.
 
@@ -471,7 +478,7 @@ val writable : file_descr -> bool
       writable. *)
 
 val wait_read : file_descr -> unit Lwt.t
-  (** Waits (without blocking other threads) until there is something
+  (** Waits (without blocking other promises) until there is something
       to read from the file descriptor.
 
       Note that you don't need to use this function if you are
@@ -482,7 +489,7 @@ val wait_read : file_descr -> unit Lwt.t
       existing libraries that are known to be blocking. *)
 
 val wait_write : file_descr -> unit Lwt.t
-  (** Waits (without blocking other threads) until it is possible to
+  (** Waits (without blocking other promises) until it is possible to
       write on the file descriptor.
 
       Note that you don't need to use this function if you are
@@ -907,8 +914,7 @@ val accept_n : ?cloexec:bool ->
                file_descr -> int -> ((file_descr * sockaddr) list * exn option) Lwt.t
   (** [accept_n fd count] accepts up to [count] connections at one time.
 
-      - if no connection is available right now, it returns a sleeping
-      thread
+      - if no connection is available right now, it returns a pending promise
 
       - if more than 1 and less than [count] are available, it returns
       all of them
@@ -1047,7 +1053,7 @@ type socket_int_option =
     Unix.socket_int_option =
   | SO_SNDBUF
   | SO_RCVBUF
-  | SO_ERROR
+  | SO_ERROR [@ocaml.deprecated "Use Unix.getsockopt_error instead."]
   | SO_TYPE
   | SO_RCVLOWAT
   | SO_SNDLOWAT
@@ -1289,14 +1295,14 @@ type async_method =
           entire program. *)
   | Async_detach
       (** System calls are made in another system thread, thus without
-          blocking other Lwt threads. The drawback is that it may
+          blocking other Lwt promises. The drawback is that it may
           degrade performance in some cases.
 
           This is the default. *)
   | Async_switch
     [@ocaml.deprecated " Use Lwt_unix.Async_detach."]
-      (** Currently a synonym for [Async_detach]. This was a different method in
-          the past. *)
+      (** @deprecated A synonym for [Async_detach]. This was a
+          different method in the past. *)
 
 val default_async_method : unit -> async_method
   [@@ocaml.deprecated
@@ -1395,7 +1401,7 @@ val wrap_syscall : io_event -> file_descr -> (unit -> 'a) -> 'a Lwt.t
       performed immediately without blocking, it is registered for
       later.
 
-      In the latter case, if the thread is canceled, [action] is
+      In the latter case, if the promise is canceled, [action] is
       removed from [set]. *)
 
 val check_descriptor : file_descr -> unit
@@ -1589,6 +1595,7 @@ val has_wait4 : bool
 
 val somaxconn : unit -> int
   [@@ocaml.deprecated " This is an internal function."]
+  (** @deprecated This is for internal use only. *)
 
 val retained : 'a -> bool ref
   (** @deprecated Used for testing. *)
@@ -1596,7 +1603,9 @@ val retained : 'a -> bool ref
 val read_bigarray :
   string -> file_descr -> IO_vectors._bigarray -> int -> int -> int Lwt.t
   [@@ocaml.deprecated " This is an internal function."]
+  (** @deprecated This is for internal use only. *)
 
 val write_bigarray :
   string -> file_descr -> IO_vectors._bigarray -> int -> int -> int Lwt.t
   [@@ocaml.deprecated " This is an internal function."]
+  (** @deprecated This is for internal use only. *)
diff --git a/src/unix/lwt_unix_stubs.c b/src/unix/lwt_unix_stubs.c
index c6a4f48..e65a13c 100644
--- a/src/unix/lwt_unix_stubs.c
+++ b/src/unix/lwt_unix_stubs.c
@@ -19,6 +19,7 @@
 #include <caml/memory.h>
 #include <caml/mlvalues.h>
 #include <caml/signals.h>
+#include <caml/version.h>
 
 #include <assert.h>
 #include <errno.h>
@@ -342,8 +343,57 @@ void lwt_unix_condition_wait(lwt_unix_condition *condition,
 
 #if defined(LWT_ON_WINDOWS)
 
+#if OCAML_VERSION < 41400
+static int win_set_inherit(HANDLE fd, BOOL inherit)
+{
+  if (! SetHandleInformation(fd,
+                             HANDLE_FLAG_INHERIT,
+                             inherit ? HANDLE_FLAG_INHERIT : 0)) {
+    win32_maperr(GetLastError());
+    return -1;
+  }
+  return 0;
+}
+#endif
+
+static SOCKET lwt_win_socket(int domain, int type, int protocol,
+                             LPWSAPROTOCOL_INFO info,
+                             BOOL inherit)
+{
+  SOCKET s;
+  DWORD flags = WSA_FLAG_OVERLAPPED;
+
+#ifndef WSA_FLAG_NO_HANDLE_INHERIT
+#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
+#endif
+
+  if (! inherit)
+    flags |= WSA_FLAG_NO_HANDLE_INHERIT;
+
+  s = WSASocket(domain, type, protocol, info, 0, flags);
+  if (s == INVALID_SOCKET) {
+    if (! inherit && WSAGetLastError() == WSAEINVAL) {
+      /* WSASocket probably doesn't suport WSA_FLAG_NO_HANDLE_INHERIT,
+       * retry without. */
+      flags &= ~(DWORD)WSA_FLAG_NO_HANDLE_INHERIT;
+      s = WSASocket(domain, type, protocol, info, 0, flags);
+      if (s == INVALID_SOCKET)
+        goto err;
+      win_set_inherit((HANDLE) s, FALSE);
+      return s;
+    }
+    goto err;
+  }
+
+  return s;
+
+ err:
+  win32_maperr(WSAGetLastError());
+  return INVALID_SOCKET;
+}
+
 static void lwt_unix_socketpair(int domain, int type, int protocol,
-                                SOCKET sockets[2]) {
+                                SOCKET sockets[2], BOOL inherit) {
   union {
     struct sockaddr_in inaddr;
     struct sockaddr_in6 inaddr6;
@@ -360,7 +410,7 @@ static void lwt_unix_socketpair(int domain, int type, int protocol,
   sockets[0] = INVALID_SOCKET;
   sockets[1] = INVALID_SOCKET;
 
-  listener = socket(domain, type, protocol);
+  listener = lwt_win_socket(domain, type, protocol, NULL, inherit);
   if (listener == INVALID_SOCKET) goto failure;
 
   memset(&a, 0, sizeof(a));
@@ -394,7 +444,7 @@ static void lwt_unix_socketpair(int domain, int type, int protocol,
 
   if (listen(listener, 1) == SOCKET_ERROR) goto failure;
 
-  sockets[0] = socket(domain, type, protocol);
+  sockets[0] = lwt_win_socket(domain, type, protocol, NULL, inherit);
   if (sockets[0] == INVALID_SOCKET) goto failure;
 
   addrlen = domain == PF_INET ? sizeof(a.inaddr) : sizeof(a.inaddr6);
@@ -421,14 +471,15 @@ static int socket_domain_table[] = {PF_UNIX, PF_INET, PF_INET6};
 static int socket_type_table[] = {SOCK_STREAM, SOCK_DGRAM, SOCK_RAW,
                                   SOCK_SEQPACKET};
 
-CAMLprim value lwt_unix_socketpair_stub(value domain, value type,
+CAMLprim value lwt_unix_socketpair_stub(value cloexec, value domain, value type,
                                         value protocol) {
-  CAMLparam3(domain, type, protocol);
+  CAMLparam4(cloexec, domain, type, protocol);
   CAMLlocal1(result);
   SOCKET sockets[2];
   lwt_unix_socketpair(socket_domain_table[Int_val(domain)],
                       socket_type_table[Int_val(type)], Int_val(protocol),
-                      sockets);
+                      sockets,
+                      ! unix_cloexec_p(cloexec));
   result = caml_alloc_tuple(2);
   Store_field(result, 0, win_alloc_socket(sockets[0]));
   Store_field(result, 1, win_alloc_socket(sockets[1]));
@@ -608,18 +659,6 @@ value lwt_unix_recv_notifications() {
 
 #if defined(LWT_ON_WINDOWS)
 
-static SOCKET set_close_on_exec(SOCKET socket) {
-  SOCKET new_socket;
-  if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)socket, GetCurrentProcess(),
-                       (HANDLE *)&new_socket, 0L, FALSE,
-                       DUPLICATE_SAME_ACCESS)) {
-    win32_maperr(GetLastError());
-    uerror("set_close_on_exec", Nothing);
-  }
-  closesocket(socket);
-  return new_socket;
-}
-
 static SOCKET socket_r, socket_w;
 
 static int windows_notification_send() {
@@ -653,10 +692,10 @@ value lwt_unix_init_notification() {
 
   /* Since pipes do not works with select, we need to use a pair of
      sockets. */
-  lwt_unix_socketpair(AF_INET, SOCK_STREAM, IPPROTO_TCP, sockets);
+  lwt_unix_socketpair(AF_INET, SOCK_STREAM, IPPROTO_TCP, sockets, FALSE);
 
-  socket_r = set_close_on_exec(sockets[0]);
-  socket_w = set_close_on_exec(sockets[1]);
+  socket_r = sockets[0];
+  socket_w = sockets[1];
   notification_mode = NOTIFICATION_MODE_WINDOWS;
   notification_send = windows_notification_send;
   notification_recv = windows_notification_recv;
@@ -690,7 +729,7 @@ static int eventfd_notification_recv() {
 static int notification_fds[2];
 
 static int pipe_notification_send() {
-  char buf;
+  char buf = 0;
   return write(notification_fds[1], &buf, 1);
 }
 
diff --git a/test/core/test_lwt.ml b/test/core/test_lwt.ml
index 7c14f5e..d758afd 100644
--- a/test/core/test_lwt.ml
+++ b/test/core/test_lwt.ml
@@ -12,6 +12,13 @@
 module Lwt_sequence = Lwt_sequence
 [@@@ocaml.warning "+3"]
 
+(* TODO: Remove this when the minimum required version of OCaml is >= 4.08 *)
+module Result = struct
+  type (+'a, +'b) t = ('a, 'b) result =
+    | Ok of 'a
+    | Error of 'b
+end
+
 open Test
 
 
@@ -2274,11 +2281,10 @@ let choose_tests = suite "choose" [
   end;
 
   test "multiple resolved" begin fun () ->
-    (* This is run in a loop to exercise the internal PRNG. *)
-    let outcomes = Array.make 3 0 in
+    (* This is run in a loop to check that it consistently returns the failed
+       result as per documentation. *)
     let rec repeat n =
-      if n <= 0 then ()
-      else
+      n <= 0 || begin
         let p =
           Lwt.choose
             [fst (Lwt.wait ());
@@ -2286,19 +2292,15 @@ let choose_tests = suite "choose" [
              Lwt.fail Exception;
              Lwt.return "bar"]
         in
-        begin match Lwt.state p with
-        | Lwt.Return "foo" -> outcomes.(0) <- outcomes.(0) + 1
-        | Lwt.Fail Exception -> outcomes.(1) <- outcomes.(1) + 1
-        | Lwt.Return "bar" -> outcomes.(2) <- outcomes.(2) + 1
+        match Lwt.state p with
+        | Lwt.Return "foo" -> false
+        | Lwt.Fail Exception -> repeat (n - 1)
+        | Lwt.Return "bar" -> false
         | _ -> assert false
         end [@ocaml.warning "-4"];
-        repeat (n - 1)
     in
-    let count = 1000 in
-    repeat count;
-    Lwt.return
-      (outcomes.(0) > 0 && outcomes.(1) > 0 && outcomes.(2) > 0 &&
-       outcomes.(0) + outcomes.(1) + outcomes.(2) = count)
+    let count = 100 in
+    Lwt.return (repeat count)
   end;
 
   test "pending" begin fun () ->
@@ -2982,31 +2984,29 @@ let pick_tests = suite "pick" [
   end;
 
   test "multiple resolved" begin fun () ->
-    (* This is run in a loop to exercise the internal PRNG. *)
-    let outcomes = Array.make 3 0 in
+    (* This is run in a loop to check that it consistently returns the failed
+       result as per documentation. *)
     let rec repeat n =
-      if n <= 0 then ()
-      else
+      n <= 0 || begin
+        let (waiter, _) = Lwt.task () in
         let p =
           Lwt.pick
-            [fst (Lwt.wait ());
+            [waiter;
              Lwt.return "foo";
              Lwt.fail Exception;
              Lwt.return "bar"]
         in
-        begin match Lwt.state p with
-        | Lwt.Return "foo" -> outcomes.(0) <- outcomes.(0) + 1
-        | Lwt.Fail Exception -> outcomes.(1) <- outcomes.(1) + 1
-        | Lwt.Return "bar" -> outcomes.(2) <- outcomes.(2) + 1
+        match Lwt.state p with
+        | Lwt.Return "foo" -> false
+        | Lwt.Fail Exception ->
+            Lwt.state waiter = Lwt.Fail Lwt.Canceled
+            && repeat (n - 1)
+        | Lwt.Return "bar" -> false
         | _ -> assert false
         end [@ocaml.warning "-4"];
-        repeat (n - 1)
     in
-    let count = 1000 in
-    repeat count;
-    Lwt.return
-      (outcomes.(0) > 0 && outcomes.(1) > 0 && outcomes.(2) > 0 &&
-       outcomes.(0) + outcomes.(1) + outcomes.(2) = count)
+    let count = 100 in
+    Lwt.return (repeat count)
   end;
 
   test "pending" begin fun () ->
diff --git a/test/core/test_lwt_result.ml b/test/core/test_lwt_result.ml
index e9c80d4..a111bf9 100644
--- a/test/core/test_lwt_result.ml
+++ b/test/core/test_lwt_result.ml
@@ -1,6 +1,12 @@
 (* This file is part of Lwt, released under the MIT license. See LICENSE.md for
    details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *)
 
+(* TODO: Remove this when the minimum required version of OCaml is >= 4.08 *)
+module Result = struct
+  type (+'a, +'b) t = ('a, 'b) result =
+    | Ok of 'a
+    | Error of 'b
+end
 
 
 open Test
@@ -32,17 +38,17 @@ let suite =
          Lwt.return (Lwt_result.map ((+) 1) x = x)
       );
 
-    test "map_err"
+    test "map_error"
       (fun () ->
          let x = Lwt_result.return 0 in
-         Lwt.return (Lwt_result.map_err ((+) 1) x = x)
+         Lwt.return (Lwt_result.map_error ((+) 1) x = x)
       );
 
-    test "map_err, error case"
+    test "map_error, error case"
       (fun () ->
          let x = Lwt_result.fail 0 in
          let correct = Lwt_result.fail 1 in
-         Lwt.return (Lwt_result.map_err ((+) 1) x = correct)
+         Lwt.return (Lwt_result.map_error ((+) 1) x = correct)
       );
 
     test "bind"
@@ -66,6 +72,12 @@ let suite =
          Lwt.return (Lwt_result.ok x = Lwt_result.return 0)
       );
 
+    test "error"
+      (fun () ->
+        let x = Lwt.return 0 in
+        Lwt.return (Lwt_result.error x = Lwt_result.fail 0)
+      );
+
     test "catch"
       (fun () ->
          let x = Lwt.return 0 in
@@ -104,18 +116,18 @@ let suite =
          Lwt.return (Lwt_result.bind_lwt x f = Lwt_result.fail 0)
       );
 
-    test "bind_lwt_err"
+    test "bind_lwt_error"
       (fun () ->
          let x = Lwt_result.return 0 in
          let f y = Lwt.return (y + 1) in
-         Lwt.return (Lwt_result.bind_lwt_err x f = Lwt_result.return 0)
+         Lwt.return (Lwt_result.bind_lwt_error x f = Lwt_result.return 0)
       );
 
-    test "bind_lwt_err, error case"
+    test "bind_lwt_error, error case"
       (fun () ->
          let x = Lwt_result.fail 0 in
          let f y = Lwt.return (y + 1) in
-         Lwt.return (Lwt_result.bind_lwt_err x f = Lwt_result.fail 1)
+         Lwt.return (Lwt_result.bind_lwt_error x f = Lwt_result.fail 1)
       );
 
     test "bind_result"
@@ -186,6 +198,42 @@ let suite =
          Lwt.bind p (fun x -> Lwt.return (x = Result.Error 1))
       );
 
+    test "iter"
+      (fun () ->
+        let x = Lwt_result.return 1 in
+        let actual = ref 0 in
+        Lwt.bind
+          (Lwt_result.iter (fun y -> actual := y + 1; Lwt.return_unit) x)
+          (fun () -> Lwt.return (!actual = 2))
+      );
+
+    test "iter, error case"
+      (fun () ->
+        let x = Lwt_result.fail 1 in
+        let actual = ref 0 in
+        Lwt.bind
+          (Lwt_result.iter (fun y -> actual := y + 1; Lwt.return_unit) x)
+          (fun () -> Lwt.return (!actual <> 2))
+      );
+
+    test "iter_error"
+      (fun () ->
+        let x = Lwt_result.fail 1 in
+        let actual = ref 0 in
+        Lwt.bind
+          (Lwt_result.iter_error (fun y -> actual := y + 1; Lwt.return_unit) x)
+          (fun () -> Lwt.return (!actual = 2))
+      );
+
+    test "iter_error, success case"
+      (fun () ->
+        let x = Lwt_result.return 1 in
+        let actual = ref 0 in
+        Lwt.bind
+          (Lwt_result.iter_error (fun y -> actual := y + 1; Lwt.return_unit) x)
+          (fun () -> Lwt.return (!actual <> 2))
+      );
+
     test "let*"
       (fun () ->
         let p1, r1 = Lwt.wait () in
diff --git a/test/core/test_lwt_stream.ml b/test/core/test_lwt_stream.ml
index 4451b6a..2c5c3cb 100644
--- a/test/core/test_lwt_stream.ml
+++ b/test/core/test_lwt_stream.ml
@@ -1,6 +1,12 @@
 (* This file is part of Lwt, released under the MIT license. See LICENSE.md for
    details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *)
 
+(* TODO: Remove this when the minimum required version of OCaml is >= 4.08 *)
+module Result = struct
+  type (+'a, +'b) t = ('a, 'b) result =
+    | Ok of 'a
+    | Error of 'b
+end
 
 
 open Lwt
diff --git a/test/unix/dummy.ml b/test/unix/dummy.ml
new file mode 100644
index 0000000..ad53a31
--- /dev/null
+++ b/test/unix/dummy.ml
@@ -0,0 +1,6 @@
+let () =
+  let str = "the quick brown fox jumps over the lazy dog" in
+  match Sys.argv.(1) with
+  | "read" -> if read_line () <> str then exit 1
+  | "write" -> print_string str
+  | _ -> invalid_arg "Sys.argv"
diff --git a/test/unix/dune b/test/unix/dune
index 8d961a2..1eb5673 100644
--- a/test/unix/dune
+++ b/test/unix/dune
@@ -6,7 +6,11 @@
 (library
  (name tester)
  (libraries lwt lwttester)
- (modules (:standard \ main luv_main) ))
+ (modules (:standard \ main luv_main dummy) ))
+
+(executable
+ (name dummy)
+ (modules dummy))
 
 (executable
  (name main)
@@ -22,7 +26,7 @@
  (name runtest)
  (package lwt)
  (action (run %{exe:main.exe}))
- (deps bytes_io_data)
+ (deps bytes_io_data %{exe:dummy.exe})
 )
 
 (alias
diff --git a/test/unix/test_lwt_bytes.ml b/test/unix/test_lwt_bytes.ml
index 4c42542..5b68b77 100644
--- a/test/unix/test_lwt_bytes.ml
+++ b/test/unix/test_lwt_bytes.ml
@@ -600,7 +600,7 @@ let suite = suite "lwt_bytes" [
     test "read: buffer retention" ~sequential:true begin fun () ->
       let buffer = Lwt_bytes.create 3 in
 
-      let read_fd, write_fd = Lwt_unix.pipe () in
+      let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
       Lwt_unix.set_blocking read_fd true;
 
       Lwt_unix.write_string write_fd "foo" 0 3 >>= fun _ ->
@@ -637,7 +637,7 @@ let suite = suite "lwt_bytes" [
     test "write: buffer retention" ~sequential:true begin fun () ->
       let buffer = Lwt_bytes.create 3 in
 
-      let read_fd, write_fd = Lwt_unix.pipe () in
+      let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
       Lwt_unix.set_blocking write_fd true;
 
       let retained = Lwt_unix.retained buffer in
diff --git a/test/unix/test_lwt_process.ml b/test/unix/test_lwt_process.ml
index 25bde6c..e82e27c 100644
--- a/test/unix/test_lwt_process.ml
+++ b/test/unix/test_lwt_process.ml
@@ -6,6 +6,8 @@
 open Test
 open Lwt.Infix
 
+let expected = "the quick brown fox jumps over the lazy dog"
+
 let suite = suite "lwt_process" [
   (* The sleep command is not available on Win32. *)
   test "lazy_undefined" ~only_if:(fun () -> not Sys.win32)
@@ -16,5 +18,44 @@ let suite = suite "lwt_process" [
             Lwt.catch
               (fun () -> Lwt_io.read p#stdout)
               (fun _ -> Lwt.return ""))
-        >>= fun _ -> Lwt.return_true)
+        >>= fun _ -> Lwt.return_true);
+
+  test "pread"
+    (fun () ->
+      let args = [|"dummy.exe"; "write"|] in
+      Lwt_process.pread ~stdin:`Close ~stderr:`Close ("./dummy.exe", args)
+      >|= fun actual ->
+      actual = expected);
+
+  test "pread keep"
+    (fun () ->
+      let args = [|"dummy.exe"; "write"|] in
+      Lwt_process.pread ~stdin:`Keep ~stderr:`Keep ("./dummy.exe", args)
+      >|= fun actual ->
+      actual = expected);
+
+  test "pread nul"
+    (fun () ->
+      let args = [|"dummy.exe"; "write"|] in
+      Lwt_process.pread ~stdin:`Dev_null ~stderr:`Dev_null ("./dummy.exe", args)
+      >|= fun actual ->
+      actual = expected);
+
+  test "pwrite"
+    (fun () ->
+      let args = [|"dummy.exe"; "read"|] in
+      Lwt_process.pwrite ~stdout:`Close ~stderr:`Close ("./dummy.exe", args) expected
+      >>= fun () -> Lwt.return_true);
+
+  test "pwrite keep"
+    (fun () ->
+      let args = [|"dummy.exe"; "read"|] in
+      Lwt_process.pwrite ~stdout:`Keep ~stderr:`Keep ("./dummy.exe", args) expected
+      >>= fun () -> Lwt.return_true);
+
+  test "pwrite nul"
+    (fun () ->
+      let args = [|"dummy.exe"; "read"|] in
+      Lwt_process.pwrite ~stdout:`Dev_null ~stderr:`Dev_null ("./dummy.exe", args) expected
+      >>= fun () -> Lwt.return_true);
 ]
diff --git a/test/unix/test_lwt_unix.cppo.ml b/test/unix/test_lwt_unix.cppo.ml
index 0ed248d..eed608e 100644
--- a/test/unix/test_lwt_unix.cppo.ml
+++ b/test/unix/test_lwt_unix.cppo.ml
@@ -421,7 +421,7 @@ let readv_tests =
              `Bigarray (1, 4, 1)]
         in
 
-        let read_fd, write_fd = Lwt_unix.pipe () in
+        let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
 
         Lwt_list.for_all_s (fun t -> t ())
           [writer write_fd "foobar";
@@ -435,7 +435,7 @@ let readv_tests =
              `Bigarray (1, 4, 1)]
         in
 
-        let read_fd, write_fd = Lwt_unix.pipe () in
+        let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
         Lwt_unix.set_blocking read_fd true;
 
         Lwt_list.for_all_s (fun t -> t ())
@@ -450,7 +450,7 @@ let readv_tests =
         ]
       in
 
-      let read_fd, write_fd = Lwt_unix.pipe () in
+      let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
       Lwt_unix.set_blocking read_fd true;
 
       Lwt_unix.write_string write_fd "foo" 0 3 >>= fun _ ->
@@ -473,7 +473,7 @@ let readv_tests =
         in
         Lwt_unix.IO_vectors.drop io_vectors 2;
 
-        let read_fd, write_fd = Lwt_unix.pipe () in
+        let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
 
         Lwt_list.for_all_s (fun t -> t ())
           [writer write_fd "foobar";
@@ -500,14 +500,14 @@ let readv_tests =
 
         let expected = String.make limit 'a' in
 
-        let read_fd, write_fd = Lwt_unix.pipe () in
+        let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
 
         Lwt_list.for_all_s (fun t -> t ())
           [writer write_fd (expected ^ "a");
            reader read_fd io_vectors underlying limit (expected ^ "_")]);
 
     test "readv: windows" ~only_if:(fun () -> Sys.win32) begin fun () ->
-      let read_fd, write_fd = Lwt_unix.pipe () in
+      let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
 
       let io_vectors, underlying =
         make_io_vectors [
@@ -590,7 +590,7 @@ let writev_tests =
              `Bigarray ("baz", 0, 3)]
         in
 
-        let read_fd, write_fd = Lwt_unix.pipe () in
+        let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
 
         Lwt_list.for_all_s (fun t -> t ())
           [writer ~blocking:false write_fd io_vectors 9;
@@ -605,7 +605,7 @@ let writev_tests =
              `Bigarray ("baz", 0, 3)]
         in
 
-        let read_fd, write_fd = Lwt_unix.pipe () in
+        let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
         Lwt_unix.set_blocking write_fd true;
 
         Lwt_list.for_all_s (fun t -> t ())
@@ -620,7 +620,7 @@ let writev_tests =
         ]
       in
 
-      let read_fd, write_fd = Lwt_unix.pipe () in
+      let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
       Lwt_unix.set_blocking write_fd true;
 
       let retained = Lwt_unix.retained io_vectors in
@@ -637,7 +637,7 @@ let writev_tests =
         let io_vectors =
           make_io_vectors [`Bytes ("foo", 1, 2); `Bigarray ("bar", 1, 2)] in
 
-        let read_fd, write_fd = Lwt_unix.pipe () in
+        let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
 
         Lwt_list.for_all_s (fun t -> t ())
           [writer write_fd io_vectors 4;
@@ -652,7 +652,7 @@ let writev_tests =
              `Bigarray ("baz", 0, 3)]
         in
 
-        let read_fd, write_fd = Lwt_unix.pipe () in
+        let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
 
         let initially_empty = Lwt_unix.IO_vectors.is_empty io_vectors in
 
@@ -685,7 +685,7 @@ let writev_tests =
              `Bigarray ("bar", 0, 0)]
         in
 
-        let read_fd, write_fd = Lwt_unix.pipe () in
+        let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
 
         let initially_empty = Lwt_unix.IO_vectors.is_empty io_vectors in
 
@@ -705,7 +705,7 @@ let writev_tests =
         let negative_length' = make_io_vectors [`Bigarray ("foo", 0, -1)] in
         let out_of_bounds' = make_io_vectors [`Bigarray ("foo", 1, 3)] in
 
-        let read_fd, write_fd = Lwt_unix.pipe () in
+        let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
 
         let writer io_vectors =
           fun () ->
@@ -753,7 +753,7 @@ let writev_tests =
           loop (limit + 1)
         in
 
-        let read_fd, write_fd = Lwt_unix.pipe () in
+        let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
 
         Lwt_list.for_all_s (fun t -> t ())
           [writer write_fd io_vectors limit;
@@ -764,7 +764,7 @@ let writev_tests =
         let io_vectors = make_io_vectors [`Bytes ("foo", 0, 3)] in
         Lwt_unix.IO_vectors.drop io_vectors (-1);
 
-        let read_fd, write_fd = Lwt_unix.pipe () in
+        let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
 
         Lwt_list.for_all_s (fun t -> t ())
           [writer write_fd io_vectors 3;
@@ -782,7 +782,7 @@ let writev_tests =
         ]
       in
 
-      let read_fd, write_fd = Lwt_unix.pipe () in
+      let read_fd, write_fd = Lwt_unix.pipe ~cloexec:true () in
 
       Lwt_list.for_all_s (fun t -> t ()) [
         writer ~close:false write_fd io_vectors 3;
@@ -796,7 +796,7 @@ let writev_tests =
 let send_recv_msg_tests = [
   test "send_msg, recv_msg" ~only_if:(fun () -> not Sys.win32) begin fun () ->
     let socket_1, socket_2 = Lwt_unix.(socketpair PF_UNIX SOCK_STREAM 0) in
-    let pipe_read, pipe_write = Lwt_unix.pipe () in
+    let pipe_read, pipe_write = Lwt_unix.pipe ~cloexec:true () in
 
     let source_buffer = Bytes.of_string "_foo_bar_" in
     let source_iovecs = Lwt_unix.IO_vectors.create () in
@@ -854,7 +854,7 @@ let send_recv_msg_tests = [
       begin fun () ->
 
     let socket_1, socket_2 = Lwt_unix.(socketpair PF_UNIX SOCK_STREAM 0) in
-    let pipe_read, pipe_write = Lwt_unix.pipe () in
+    let pipe_read, pipe_write = Lwt_unix.pipe ~cloexec:true () in
 
     let source_buffer = Lwt_bytes.of_string "_foo_bar_" in
     let source_iovecs = Lwt_bytes.[