Resource-safe C interface, part 0#9037
Conversation
a856314 to
a03cca9
Compare
a03cca9 to
c9ab845
Compare
|
@jhjourdan I can anticipate one remark: sometimes I call one set of callbacks without calling the other ones. This matches 4.9. An alternative is to always call all callbacks when I call some callbacks. This is debatable, let me know what you think. |
jhjourdan
left a comment
There was a problem hiding this comment.
Thanks for this new proposal. This goes to the right direction, we are almost there! I have two concerns:
@jhjourdan I can anticipate one remark: sometimes I call one set of callbacks without calling the other ones. This matches 4.9. An alternative is to always call all callbacks when I call some callbacks. This is debatable, let me know what you think.
I would indeed prefer not calling these functions directly anywhere, except in caml_process_pending_callbacks.
Apart from that, I would prefer if we could use the polling versions everywhere it is possible in the OCaml runtime. This include functions like Array.make: they are not designed to be called from C code, so that they just correspond to allocation from OCaml code, which we decided were polling.
|
About the question about removed polling points in primitives that allocate (which you introduced in order to call memprof callbacks), and your request:
So you were not convinced by my proposition to do it once and for all allocating primitives. This solution is not ready, but neither is memprof so I felt like there was no incentive to rush this into 4.10. But maybe you have other plans. I agree to add them (in the case of allocating primitives) and I trust you that it makes a real difference. You are going to great lengths to call the memprof callback immediately after the allocation. If so, isn't this an information that the memprof callback could use, i.e. a boolean saying whether the call was immediate or delayed? That distinction could also justify adding such calls, in which case it could take the form of a specific function rather than the generic process_pending_callbacks. (It does not need to be implemented right now.) I am done with my questions for this one! |
Ah, sorry, I don't remember this proposal (the discussions around these PRs really start being too long... and I am partly responsible for this, I know). Could you recall me your proposal? Note, also, that many of these
I'd be happy to add this Boolean, but it seems rather hard to compute to me. If you use a specific function for this, how do you distinguish in the memprof queue the callbacks that have just been added from the old ones? I'm open to discuss this, but let's focus for now on this PR. If you have ideas, I'd be happy if you give them in another channel. |
Have all non-noalloc C calls (from OCaml) poll when returning. This should be easy to implement as part of the work on better safe points. In addition, if in the future it is decided and enforced that memprof callbacks should not raise exceptions, it will also be possible to add more explicit calls to them independently of the possibly-raising callbacks.
I did not remove such calls (apart from the one discussed above which I did not interpret as such).
The idea was to distinguish the most recent one, but I trust you if you say it is more complicated than that. (Future evolution can influence what I do now.) Also, would you still be comfortable in calling caml_process_pending_callbacks instead of memprof callbacks if the order in which callbacks are called (memprof before anything else) was changed, or will this decision rigidify caml_process_pending_callbacks? (I was thinking that for latency in reaction to signals, it is better to do signals first). |
|
Here's my current impression: I am not opposed to calling memprof callbacks directly in allocating primitives in addition to a systematic polling on return (which I think is going to be there in any case in the end), however it seems premature to decide the exact form it takes: a generic process_pending_callbacks that will check signals first, a generic call to memprof callbacks with an enforced non-raising behaviour, a specific call to memprof callbacks that knows it is called first thing after the allocation... I do not think that it is urgent to discuss and decide this before 4.10, so the default is to keep it as it was in 4.9. (If you tell me they do make a difference for 4.10, I add them.) |
e13511e to
7b02c13
Compare
|
The new last commit concerns what still has pending questions above. (new check_urgent_gcs & polling ASAP after major allocations). For polling ASAP after major allocations, there is no one solution that satisfies all constraints so I came up with what I think is the best: call memprof callbacks directly. I will move the calls to signals handlers to first position in The drawback is that it breaks the rule that all callbacks are executed via process_pending_callbacks (which was already broken in the case of memprof_handle_postponed). In any case I did not take a decision there, its purpose is to continue the discussion and let you check that I took all other remarks into account. But I am satisfied with this one. (Edit: except for the part where the first callback in memprof_handle_postponed will poll at the next allocation... I'll try something else.) |
Alright. I confirm this is a promising idea, but for now this is not obvious this will work, and will need at least a few benchmarks to check that performance is not affected too much. Said otherwise, I agree that would be a solution to this problem, but for now we don't have it. And it does not cost much to do manual polling instead for now, and revert that once automatic polling will be available.
That's not only about 4.10. I'd like to have a version that works well in the trunk branch independently of how long the safe point change will take. I don't think it's so premature to doe this change, since we can revert it easily without creating incompatibilities. |
Which are the constraints? Is there a problem with polling signals there too?
Actually, I don't think. What we want in memprof is that the callback gets called during the call of e.g.,
And at that point, my proposal is to remove that call. But only then. |
7b02c13 to
509b284
Compare
509b284 to
d19bfb5
Compare
|
This new version seems good to me! I reviewed and added a few nitpicks, but that's ok to merge as-is. |
|
I'll approve (on @jhjourdan's behalf; I haven't reviewed myself) and merge once @gadmm is okay with it (he may want to take care of the nitpicks first) and the CI is green. Probably tomorrow. |
|
Alright, I was coming to the same conclusion after the failure of my idea of only calling memprof callbacks (which in addition you convinced me was useless), but your point about having trunk in good shape for experimenting convinced me it's the right approach. In the end we are very close to your original code (but avoid polling in sensitive places). I'll turn to #8993 now and deal with the nitpicks in the meanwhile, so yes please wait for my greenlight.
I am responsible for this as well, I need to be sure I understand everything, and maybe some parts would have been easier to discuss in person. I could try to pass by Paris more often. Thanks for the patient replies. |
|
The CI failure corresponds to the repetitive failures that we see on the Inria CI for a few days. I don't know what this is, but this is probably unrelated to this PR. |
Good to know. I was taking it personally! |
This is dead code since removal of vmthreads.
…on)" caml_raise_if_exception remains private; all public _exn functions will be provided with a directly-raising variant to keep supporting current programming styles.
Note: I think it would be nice to merge this one soon, if it's ready to be merged, instead of trying to get both this one and #8993 perfect at the same time. You can always rebase #8993 on top of the final version of the current PR, and you can also do some changes in #8993 to the changes that were done here if everyone agrees. But keeping PRs open and in-flight has a cost (it introduces a risk of conflicts for other PRs, etc.), and I think we should try to polish and merge quickly when people have converged on a PR. |
|
There is a conflict introduced by #8809. This is just a matter of renaming a |
a0f0d4b to
2a6d038
Compare
|
Just pushed one based on your latest suggestion. Let me now have a look at your proposed patch. |
|
I also added your bugfix from the other PR, thanks! |
|
I have added the change to |
2a6d038 to
a61ea89
Compare
|
Ok, thanks! Let's wait for CI, and if it succeeds, then I think this is good to merge, and I no longer have any more remarks (I think? ;) ). The only thing that you will need to do is, of course, squashing your "fixup" commit. |
a61ea89 to
a0c82c7
Compare
|
Done! Thanks. |
a0c82c7 to
5e57970
Compare
gasche
left a comment
There was a problem hiding this comment.
(approval on @jhjourdan's behalf; I did not review the code myself)
|
There's still a bug to fix. |
In 8691, caml_check_urgent_gc was merged with the function that runs asynchronous callbacks. The rationale was that caml_check_urgent_gc already runs finalisers, and so could have run any asynchronous callbacks. We agreed on a different strategy: we know that users could not rely on asynchronous callbacks being called at this point, so take the opportunity to make it callback-safe, like was done for allocation functions. The new check_urgent_gc no longer calls finalisers (nor any callbacks), and instead two internal functions are introduced: * caml_do_urgent_gc_and_callbacks : function to perform actions unconditionally. * caml_check_urgent_gc_and_callbacks : function that checks for something to do, and then executes all actions (GC and callbacks).
|
The following change which I forgot when deciding to directly call |
5e57970 to
2433b93
Compare
Alright. Another fix would be to call |
|
Is there any issue with this version? |
|
Not really :D That's mostly a matter of respecting abstraction barriers: only one entry point for async callbacks. |
|
One way to see it is that the GC, |
|
No, that's fine. Leave it as-is. |
|
Another failure of OCamlDebug on Windows.... I really don't know what this is, but this really looks like something we have to look at. |
|
However, it really seems unrelated to this PR, since it triggers repeatedly on different PR since Monday. Thus, it should not prevent merging this PR. I opened an issue for the CI failure : #9043. |
|
@gasche : could we merge this? |
|
It's really not easy for me, looking at the conversation, to tell that you have converged to a final state. (When I previously thought you had converged I was later told there was a bug, and I'm not completely sure which bug and how/whether it was solved.) I'm going to trust you on this. |
|
Thanks for merging. Indeed, we could have been clearer. I am very satisfied with the end result, in particular thanks to @jhjourdan's thorough review and explanations I think the good choices have been made. |
Sorry for that. I know this has not been clear, mostly my fault: I turned the green light on while I have not done my final, thorough review. |
This is the first part of #8993, where I apply the semantics change for
caml_check_urgent_gcagreed upon at #8691 (comment).In 4.9, the public function
caml_check_urgent_gcchecks for finalisers, but not other callbacks. In 4.10, the check for finalisers is removed from allocation functions called from C code at #8691. At the same time, execution of all pending callbacks is added tocaml_check_urgent_gc, and several new polling points are added. I think the motivation is to ensure, in many places, that memprof callbacks are called as soon as possible after an allocation.There is a desire to build on #8691 to make the C API safe in the presence of asynchronous exceptions both for the runtime and the user (#8993, #8997). This requires: reducing the number of implicit polling points in C, and for functions that need to poll, replace them with
_exnvariants that return the exception, to be used to perform appropriate resource clean-up. But this is hard to use in practice if the functions that can raise asynchronous exceptions are many.So, the current PR furthers #8691 by changing again
caml_check_urgent_gc, such that it does not call any callback. We take advantage that in 4.9, it is not yet a reliable polling location. The new code is roughly equivalent to the OCaml 4.9 behaviour modulo fewer checks for finalisers. Any place where the check for finaliser could be relied on has been kept as a polling location.Some polling points added explicitly with #8691 were in primitives, right before returning to OCaml. I removed them and propose a more robust strategy to ensure the memprof callback is called no later in those cases (not implemented in this PR): have non-noalloc C calls poll when returning (this should be easy to do during the work on better safe points). In addition, if in the future it is decided and enforced that memprof callbacks should not raise exceptions, it will also be possible to add more explicit calls to them independently of the possibly-raising callbacks.
Since memprof is not going to be ready for 4.10, there will still be some time to discuss the appropriate polling of memprof callbacks in the near future.
The first 3 clean-up commits have already been reviewed at #8993, they are included here to avoid me a tedious rebase.
This PR is targeted at 4.10 because it relies on the fact that polling points added in #8691 have not reached users yet, and also because #8993 is currently based on it (the current PR enabled a simpler public API at #8993).
(@jhjourdan, @stedolan)