Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ctypes-foreign.opam
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
version: "0.23.0"
synopsis: "Dynamic access to foreign C libraries using Ctypes"
description: """

Expand Down
1 change: 1 addition & 0 deletions ctypes.opam
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
version: "0.23.0"
synopsis: "Combinators for binding to C libraries without writing any C"
description: """

Expand Down
2 changes: 1 addition & 1 deletion dune-project
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(lang dune 2.9)
(name ctypes)
(version 0.22.0)
(version 0.23.0)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to change the version

(formatting (enabled_for dune))
(use_standard_c_and_cxx_flags true)

Expand Down
6 changes: 3 additions & 3 deletions src/cstubs/cstubs_generate_ml.ml
Original file line number Diff line number Diff line change
Expand Up @@ -543,15 +543,15 @@ let rec wrapper_body : type a. concurrency:concurrency_policy -> errno:errno_pol
let lwt_bind = Ctypes_path.path_of_string "Lwt.bind"
let lwt_return = Ctypes_path.path_of_string "Lwt.return"
let box_lwt = Ctypes_path.path_of_string "box_lwt"
let use_value = Ctypes_path.path_of_string "CI.use_value"
let keep_alive = Ctypes_path.path_of_string "CI.keep_alive"

let return_result : args:lident list -> ml_exp =
fun ~args ->
let x = fresh_var () in
(* fun v -> CI.use_value (x1,x2,....xn); Lwt.return v *)
(* fun v -> CI.keep_alive (x1,x2,....xn); Lwt.return v *)
`Fun ([x],
`Seq
(`Appl (`Ident use_value,
(`Appl (`Ident keep_alive,
`Tuple
(ListLabels.map args
~f:(fun x -> `Ident (Ctypes_path.path_of_string x)))),
Expand Down
6 changes: 3 additions & 3 deletions src/ctypes-foreign/ctypes_ffi.ml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ struct
*)
let kept_alive_indefinitely = ref []
let keep_alive w ~while_live:v =
try Gc.finalise (fun _ -> Ctypes_memory_stubs.use_value w; ()) v
try Gc.finalise (fun _ -> Ctypes_memory.keep_alive w; ()) v
with Invalid_argument _ ->
kept_alive_indefinitely := Obj.repr w :: !kept_alive_indefinitely

Expand Down Expand Up @@ -114,7 +114,7 @@ struct
(fun buf arr -> List.iter (fun w -> r := w buf arr :: !r) writers)
read_return_value
in
Ctypes_memory_stubs.use_value r;
Ctypes_memory.keep_alive r;
v
| WriteArg (write, ccallspec) ->
let next = invoke name ccallspec in
Expand Down Expand Up @@ -154,7 +154,7 @@ struct
raise Ctypes_ffi_stubs.CallToExpiredClosure
in
let v = box (Ctypes_weak_ref.make f') in
let () = Gc.finalise (fun _ -> Ctypes_memory_stubs.use_value f') v in
let () = Gc.finalise (fun _ -> Ctypes_memory.keep_alive f') v in
v)

let rec write_arg : type a. a typ -> offset:int -> idx:int -> a ->
Expand Down
2 changes: 1 addition & 1 deletion src/ctypes/cstubs_internals.ml
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,4 @@ let build_enum_type name underlying ?(typedef=false) ?unexpected alist =
Printf.ksprintf failwith
"Enum type detected as floating type: %s" name

let use_value v = Ctypes_memory_stubs.use_value v
let keep_alive x = ignore (Sys.opaque_identity x)
2 changes: 1 addition & 1 deletion src/ctypes/cstubs_internals.mli
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,4 @@ val build_enum_type :
string -> Ctypes_static.arithmetic -> ?typedef:bool ->
?unexpected:(int64 -> 'a) -> ('a * int64) list -> 'a typ

val use_value : 'a -> unit
val keep_alive : 'a -> unit
18 changes: 18 additions & 0 deletions src/ctypes/ctypes.mli
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,24 @@ val allocate_n : ?finalise:('a ptr -> unit) -> 'a typ -> count:int -> 'a ptr
memory is allocated with libc's [calloc] and is guaranteed to be
zero-filled. *)

val keep_alive : 'a -> unit
(** Inserting [keep_alive x;] in a sequence of expressions ensures that
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(** Inserting [keep_alive x;] in a sequence of expressions ensures that
(** Inserting [keep_alive x] in a sequence of expressions ensures that

the garbage collector will not collect [x] until after that [keep_alive]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
the garbage collector will not collect [x] until after that [keep_alive]
the garbage collector will not collect [x] until [keep_alive]

has returned.

For example:
{[
let strchr = Foreign.foreign "strchr" (ptr char @-> char @-> returning (ptr char)) in
let p = CArray.of_string "abc" in
let q = strchr (CArray.start p) 'a' in
let () = Gc.compact () in
let () = Printf.printf "%c\n" !@q in
keep_alive p;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
keep_alive p;
keep_alive p

]}
Without the [keep_alive p] at the bottom, [p] could be collected during the
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Without the [keep_alive p] at the bottom, [p] could be collected during the
Without [keep_alive p], [p] could be collected during the

[Gc.compact ()] call, which would make [q] an invalid pointer into the
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[Gc.compact ()] call, which would make [q] an invalid pointer into the
[Gc.compact ()] call, making [q] an invalid pointer into the

now-collected [p], leading to undefined behavior. *)
Comment on lines +208 to +217
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice example, but I think it would be even stronger without the call to Gc.compact, because most programs don't call Gc functions.


val ptr_compare : 'a ptr -> 'a ptr -> int
(** If [p] and [q] are pointers to elements [i] and [j] of the same array then
[ptr_compare p q] compares the indexes of the elements. The result is
Expand Down
5 changes: 4 additions & 1 deletion src/ctypes/ctypes_bigarray.ml
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,7 @@ let view : type a b l m. (a, b, l) t -> (m option, _) Ctypes_ptr.Fat.t -> b =
| Dims3 (d1, d2, d3) -> view3 kind ~dims:[| d1; d2; d3 |] ptr layout in
match Ctypes_ptr.Fat.managed ptr with
| None -> ba
| Some src -> Gc.finalise (fun _ -> Ctypes_memory_stubs.use_value src) ba; ba
| Some src ->
(* To avoid a dependence on Ctypes_memory, we redefine keep_alive here. *)
let keep_alive x = ignore (Sys.opaque_identity x) in
Gc.finalise (fun _ -> keep_alive src) ba; ba
Comment on lines +135 to +137
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(* To avoid a dependence on Ctypes_memory, we redefine keep_alive here. *)
let keep_alive x = ignore (Sys.opaque_identity x) in
Gc.finalise (fun _ -> keep_alive src) ba; ba
Gc.finalise (fun _ -> ignore (Sys.opaque_identity src)) ba; ba

3 changes: 3 additions & 0 deletions src/ctypes/ctypes_memory.ml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ let allocate : type a. ?finalise:(a ptr -> unit) -> a typ -> a -> a ptr
p
end

let keep_alive : type a. a -> unit
= fun x -> ignore (Sys.opaque_identity x)

let ptr_compare (CPointer l) (CPointer r) = Fat.(compare l r)

let reference_type (CPointer p) = Fat.reftype p
Expand Down
3 changes: 0 additions & 3 deletions src/ctypes/ctypes_memory_stubs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,3 @@ external memcpy : dst:_ Fat.t -> src:_ Fat.t -> size:int -> unit
external string_of_array : _ Fat.t -> len:int -> string
= "ctypes_string_of_array"

(* Do nothing, concealing from the optimizer that nothing is being done. *)
external use_value : 'a -> unit
= "ctypes_use" [@@noalloc]
6 changes: 0 additions & 6 deletions src/ctypes/ctypes_roots.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,3 @@ value ctypes_caml_roots_release(value p_)
caml_stat_free(p);
return Val_unit;
}

/* 'a -> unit */
value ctypes_use(value v)
{
return Val_unit;
}
4 changes: 2 additions & 2 deletions tests/test-callback_lifetime/test_callback_lifetime.ml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ struct
let ret = Sys.opaque_identity (Better.make ~arg:(closure (int_of_string "3"))) in
Gc.full_major ();
assert_equal 15 (Better.get ret 5);
let _ = Ctypes_memory_stubs.use_value ret in
let _ = Ctypes_memory.keep_alive ret in

(* However, even with the careful implementation things can go wrong if we
keep a reference to ret beyond the lifetime of the pair. *)
Expand All @@ -111,7 +111,7 @@ struct
let ret = Careful.get (Careful.make ~arg:(closure (int_of_string "3"))) in
Gc.full_major ();
assert_equal 15 (ret 5);
let _ = Ctypes_memory_stubs.use_value ret in
let _ = Ctypes_memory.keep_alive ret in
()
end

Expand Down
4 changes: 2 additions & 2 deletions tests/test-cstdlib/test_cstdlib.ml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ struct
f x y
in
let () = qsort (to_voidp (start arr)) len size cmp in
let _ = Ctypes_memory_stubs.use_value cmp in
let _ = Ctypes_memory.keep_alive cmp in
to_list arr
in

Expand Down Expand Up @@ -256,7 +256,7 @@ struct
let () =
assert_equal None (find_month_by_name empty)

let _ = Ctypes_memory_stubs.use_value
let _ = Ctypes_memory.keep_alive
[jan; feb; mar; apr; may; jun;
jul; aug; sep; oct; nov; dec]
end in ()
Expand Down
2 changes: 1 addition & 1 deletion tests/test-oo_style/test_oo_style.ml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ struct
assert_equal c#humps 3;
end

let _ = Ctypes_memory_stubs.use_value (idfn, humpsfn, sayfn)
let _ = Ctypes_memory.keep_alive (idfn, humpsfn, sayfn)

(* Test that we can call a virtual method in a C-created subclass from
OCaml *)
Expand Down