Deprecate Obj.set_tag#1725
Conversation
There should be other cases like this. Perhaps it would be better to dedicate a bit in the 64-bit header to indicate whether the GC should scan the data. |
I don't think so. Whether data should be scanned by the GC or not, is highly dependent on the data itself, there is no orthogonality here. |
More generally, can you describe how lazy blocks are supported in the multicore GC? Marking the block as a "forward" block and storing the forward pointer need to be done atomically from the point of view of other threads. How do you achieve that? The performance impact of the new fields should be evaluated. The size increase could be noticeable for lazy-heavy data structures , and even if lazy thunks are forced quickly the extra allocations can put more pressure on the GC. Also, scanning and other generic operations could become a bit slower.
I agree that adding intarray is a good idea, but I would refrain from exposing the fact that it's a subtype of "int array". This would allow creating two int array values with the same elements, but which would be different when compared with the generic equality operator. |
|
@alainfrisch Here is the issue that tracks Lazy proposal for Multicore OCaml ocaml-multicore/ocaml-multicore#188 |
dra27
left a comment
There was a problem hiding this comment.
I apologise hugely for requesting changes on a 1-line GPR!
- Adds Obj.with_tag as a partial replacement.
- Adds caml_obj_make_forward for use of Camlinternal{Lazy,Mod}
65b7714 to
51025d1
Compare
|
New implementation just pushed. This one keeps the current behaviour for A new primitive, |
| *) | ||
| external set_field : t -> int -> t -> unit = "%obj_set_field" | ||
| external set_tag : t -> int -> unit = "caml_obj_set_tag" | ||
| [@@ocaml.deprecated "Use with_tag instead."] |
There was a problem hiding this comment.
It's not necessarily a great idea to deprecate a feature at the same version that the replacement is added: a maintainer that would follow the deprecation advice must force its users to upgrade to the latest version (or use conditionals). Could we wait for one version between with_tag and set_tag's deprecation?
There was a problem hiding this comment.
Happy to follow whatever deprecation policy works best here, but I don't really understand the issue. I thought @@ocaml.deprecated was the way to signal that a replacement is available? Is the concern here programs compiled with -warn-error breaking?
There was a problem hiding this comment.
Normally, we would deprecate it now and remove it in the next deprecation purge, in about 5 years. We still don't have any mechanism to indicate degrees of deprecation (or how much time before it gets removed). Such a mechanism might be an intermediate step before deprecation.
In the case of Obj.set_tag I think we'll remove it out-of-cycle to avoid delaying multicore, so it's better to deprecate right now anyway.
There was a problem hiding this comment.
It's not necessarily a great idea to deprecate a feature at the same version that the replacement is added:
Regarding this comment about @gasche I fear a bit nothing is ever going to be deprecated (people will simply forget) if nothing special is done. One simple measure would be to have in the repo text file with a table of the form:
ID | vReplacement | vDeprecated notice | vRemove
--------------------------------------------------
Before a release one could then apply appropriate changes according to what the table says.
There was a problem hiding this comment.
@dbuenzli yes, that sounds like a good idea. I guess some of your merged PRs have some maybe-deprecate-later identifiers in flight, do you have enough to send a PR to populate that file? (Did we merge PrintExc successor yet?)
(The file should also point to the (M/G)PR that contains the deprecation action/discussion.)
There was a problem hiding this comment.
(Did we merge PrintExc successor yet?)
No, it is #2137, but it is not yet ready, more work needed.
There was a problem hiding this comment.
I could have a look but a bit busy® right now®.
|
I have an uneasy feeling when compiler code matches on the name of C functions. I suspect that the externals whose semantic is part of the language (enough that the compiler knows about them) should be |
|
I was also unsure about exposing this as |
|
Personally I think that all of Obj's primitives could/should be moved to I also don't have very strong feelings on the best deprecation policy. |
gasche
left a comment
There was a problem hiding this comment.
I reviewed the patch for correctness and I think the new primitive is a nice thing to have in any case. The only thing I'm unsure about is the idea of deprecating set_tag right away; for a more widely used API I would frown upon deprecated in the same version its replacement appears, but this might not make a big difference for such a low-level function?
I'm approving, but I think this deserves a third pair of eyes (and maybe a decision on deprecation policy) before merging.
|
I don't understand the AppVeyor failure. It's complaining about the new C primitive, but the Travis build is fine. Perhaps a bootstrap would fix it? I don't understand why this differs on windows. @dra27 ? |
|
It fails when building |
damiendoligez
left a comment
There was a problem hiding this comment.
Third pair of eyes here. Looks good except for a small (and optional) suggestion.
In this case I think it's OK to deprecate right away.
| *) | ||
| external set_field : t -> int -> t -> unit = "%obj_set_field" | ||
| external set_tag : t -> int -> unit = "caml_obj_set_tag" | ||
| [@@ocaml.deprecated "Use with_tag instead."] |
There was a problem hiding this comment.
Normally, we would deprecate it now and remove it in the next deprecation purge, in about 5 years. We still don't have any mechanism to indicate degrees of deprecation (or how much time before it gets removed). Such a mechanism might be an intermediate step before deprecation.
In the case of Obj.set_tag I think we'll remove it out-of-cycle to avoid delaying multicore, so it's better to deprecate right now anyway.
|
@stedolan: you should add Damien as reviewer and then it's good to merge. |
|
Merged, thanks! |
Co-authored-by: Thibaut Mattio <[email protected]>
Under the multicore GC, object headers (tags and lengths) are immutable. Not in the current sense of "don't change these unless you know what you're doing", but in the stronger sense of "if you ever change these, the GC will randomly segfault as it scans the object you're messing with from another thread". Consequently,
Obj.set_tagis unsupported. (Obj.truncatetoo, although this PR only talks about the former).Currently, this PR doesn't contain any code. Before doing a bunch of hacking, I'd like to discuss with the maintainers (cc @xavierleroy, @damiendoligez) whether (a) deprecating and later removing
set_tagis something they find acceptable in principle and (b) whether the below is a reasonable plan for doing it.This change will break code that uses
Obj.set_tag, although I'd argue that occasional breakage is the price of usingObj. Still, I did some grepping of the stdlib and the OPAM universe to find uses ofset_tag, and work out replacements for common uses:Lazy
CamlinternalLazyusesset_tagto changeLazy_tagtoForward_tagwhen thunks are forced.I suggest using an extra word in the
Lazy.tstructure to distinguish forced and unforced thunks, which will increase the size of unforced thunks from 2 to 3 words. For thunks that are eventually forced the increase is temporary, since the GC short-circuits forced thunks during tracing.Creating new blocks with unexpected tags
Some code (example) uses
set_tagto create a block with a tag other than what OCaml normally chooses. (@alainfrisch has also written about using this trick to get polymorphic comparison to understand hashconsing, although I haven't come across uses in the wild).For these uses, a new primitive
Obj.with_tag : int -> Obj.t -> Obj.tsuffices: likedup, but allows the user to specify an alternative tag for the copy. This still allows the construction of objects with unusual tags, but by creating a fresh object with the desired tag, rather than by mutation. If performance is a concern, an optimisation that detects whenwith_tagis applied directly to an allocation and elides the first allocation is easy enough to implement.Preventing the GC from scanning
int arrayAt runtime,
int arraylooks just like any other array, so gets needlessly scanned by the GC. Some programs (example) explicitly change the tag of integer arrays to prevent scanning.Unlike the other uses, this use of
set_tagcan be silently ignored without affecting correctness. It is a reasonable optimisation, though, and it would be nice to support it properly. Perhaps we could introduce a newintarraytype (matchingfloatarray) which has the same layout as ordinary arrays, but is tagged withNo_scan_tag. (Unlikefloatarray, the layout is always compatible with standard arrays, so= private int arrayis a reasonable definition of this type).Unfixable cases
Some code is not so easily fixable, either by making optimisations based on undocumented properties of the current runtime (example), or by targeting not the OCaml language but a runtime model that explictly includes
set_tag(example).