Mantis Bug Tracker

View Issue Details Jump to Notes ] Issue History ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0006119OCaml~DO NOT USE (was: OCaml general)public2013-08-05 18:052015-12-11 19:26
Assigned Tofrisch 
PlatformOSOS Version
Product Version4.00.1 
Target Version4.01.1+devFixed in Version4.02.0+dev 
Summary0006119: wrong sharing of data between closures in bytecode
DescriptionWhen two functions share a reference, but are in two different files, the serialization of the two closures through the Marshal library is ok in native code but give a wrong execution in bytecode!!!

It seems that data sharing fails in bytecode, and that the virtual machine sees two separate references instead of one.

In the attached example, the first file defines:
  let x = ref 0
  let set = fun v -> x := v; printf "set x = %d@." !x

the second files defines:
  let print = fun () -> printf "x = %d@." !x

and test the behavior of the two function before and afer marshalling :
  type t = int ref * (int -> unit) * (unit -> unit)
  let y:t = x, set, print
  let test1 = print(); set 3; print ()

  let test2 =
    printf "after marshall/unmarshall@.";
    let marshall = Marshal.to_string y [Marshal.Closures] in
    let ((x, set, print) : t) = Marshal.from_string marshall 0 in
    print (); set 42; print ()

Behavior of test1 and test2 should be identical. It is the case in native code, but not in bytecode, as if option Marshal.No_sharing was activated.
Steps To Reproduce- unzip attached file
- "ocamlbuild bugMarshalClosure.native--" to check that execution is correct
- "ocamlbuild bugMarshalClosure.byte --" to see the bug : x = 3 instead of 42 when calling the unmarshalled set/print functions.
TagsNo tags attached.
Attached Fileszip file icon [^] (571 bytes) 2013-08-05 18:05

- Relationships

-  Notes
gerd (reporter)
2013-08-06 12:28
edited on: 2013-08-06 12:40

I could reproduce the bug. It seems to be essential that [print] and [x] are defined in distinct modules. An [x] defined in the same module is marshalled correctly.

Acutally, I'm not sure whether this is a bug, but it's certainly an inconsistency.

I used plain ocamlc to build in order to exclude any influence of ocamlbuild.

hnrgrgr (developer)
2013-10-14 17:46
edited on: 2013-10-14 17:54

Indeed, serialization of toplevel reference is unspecified. Changing this behaviour is probably a big change and I'm not sure it is worth.

What happens here, is that the closure representing 'set' in bytecode captures the references in its environment. While, the closure representing 'set' in native code and the 'print' closure in both byte- and native code make a direct access the global value in the BugClosure_lib module.

Hence, in bytecode, the unmarshalled 'x' value and 'set' function share the same reference (the marshalled one), while 'print' accesses the global value. In native code, the 'set' and 'print' access the global value, while the unmarshalled 'x' is copy of the global reference. Just add the following at the end of your example program to see the distinction:

  printf "x = %d@." !BugClosure_lib.x;
  printf "x = %d@." !x

Currently, in order to preserve consistency between bytecode and native code w.r.t to toplevel reference serialization, a workaround is to define toplevel references in a first module and define all functions using the reference on others modules. But this way, you will share the reference between the unmarshalled function and the original one.

If you really want to duplicate the reference, a workaround is to use local reference. That way you will always be consistent between byte- and native code:

  let set, print =
    let x = ref 0 in
    let set = fun v -> x := v; printf "set x = %d@." !x
    and print = fun () -> printf "x = %d@." !x in
    set, print

To be certain that all the marshalled function share the same 'local' reference they all should be defined at once. Otherwise, I think the behaviour is also unspecified.

I'm not sure what to do to fix this bug report.


daweil (reporter)
2013-10-14 20:33

My wish would be that an error is raised in the Marshall library when such a case is encountered.
If it is not possible, at least, enhance the documentation of the Marshall library.
frisch (developer)
2014-04-23 18:04

The documentation for Marshal has been extended to explain this behavior (commit 14670 on trunk). Comments on the text are welcome.

- Issue History
Date Modified Username Field Change
2013-08-05 18:05 daweil New Issue
2013-08-05 18:05 daweil File Added:
2013-08-06 12:28 gerd Note Added: 0010125
2013-08-06 12:40 gerd Note Edited: 0010125 View Revisions
2013-08-19 17:13 doligez Status new => acknowledged
2013-08-19 17:13 doligez Category OCaml runtime system => OCaml general
2013-08-19 17:13 doligez Target Version => 4.01.1+dev
2013-10-14 17:46 hnrgrgr Note Added: 0010478
2013-10-14 17:54 hnrgrgr Note Edited: 0010478 View Revisions
2013-10-14 20:33 daweil Note Added: 0010482
2014-04-23 18:04 frisch Note Added: 0011316
2014-04-23 18:04 frisch Status acknowledged => resolved
2014-04-23 18:04 frisch Fixed in Version => 4.02.0+dev
2014-04-23 18:04 frisch Resolution open => fixed
2014-04-23 18:04 frisch Assigned To => frisch
2015-12-11 19:26 xleroy Status resolved => closed
2017-02-23 16:36 doligez Category OCaml general => -OCaml general
2017-03-03 17:55 doligez Category -OCaml general => -(deprecated) general
2017-03-03 18:01 doligez Category -(deprecated) general => ~deprecated (was: OCaml general)
2017-03-06 17:04 doligez Category ~deprecated (was: OCaml general) => ~DO NOT USE (was: OCaml general)

Copyright © 2000 - 2011 MantisBT Group
Powered by Mantis Bugtracker