-
Notifications
You must be signed in to change notification settings - Fork 653
unix, windows: always defer reporting runtime errors to callback #829
Comments
Why do you think it is bad to return an error code immediately? The negative stuff I can come up:
The positive stuff about immediately returning the error:
A rewrite of ALL tests would be needed, so this goes against making it return immediately I guess. I feel like the overhead is against the very grain of C, but it is not thaaaaat much overhead, because erroneous situations are not common. The change would make it definitely easier to write libuv C code, since all the errors checking code would be in the callback (only one place instead of two). I'm actually for this, maybe people would write more C code instead of javascript code. As far as it goes for binding simplicity, I have this: https://github.com/txdv/LibuvSharp/blob/master/LibuvSharp/Tcp.cs#L108-L130 its C#, but I guess you can still understand it... new ConnectRequest() might throw an exception, so I need to wrap it myself in a try { } and call the appropriate callback, not too tedious though. If I get an OK from the core team, I will write this patch. Any comments @bnoordhuis @saghul? |
I don't think it's bad as such, just that it's bad because it's inconsistent. In most places, libuv doesn't return run-time errors, it passes them on. It's API design 101: when faced with two options, pick one or the other but not both. |
Yes, I agree with you, I just wrote my observation in a more detailed way. How the current API affects the users code (double error checking everywhere) and it is not really reasonable to sacrifice API usability for the mediocre performance increase during error handling. |
@saghul questions:
I didn't write an RFC because I basically agreed with bnoordhuis, I wrote down the reasoning. I also wanted to checkout how it affects the code, because you can't just reason about it without actually changing it. So I picked uv_tcp_connect implemented it and took a look on it. All the other functions will be more or less the same.
The assert release/debug can be implemented in both cases, whether we deffer all error callbacks or not. That is a feature that I think is reasonable enough to implemented without further discussion. Now we need to make a decision about EINVAL. The idea of deferring everything to the callback is to avoid multiple places of checking error codes. We want all error checking code to be in one place - it makes it easier for the user. This has been always the case for the classic blocking APIs. With the current callback API we have 2 places to check for different errors and there is even some code which defers some error callbacks (because some platforms return then immediately others not, I already touched some code handling that in my uv_tcp_connect deffered callback preview) and this makes parity on all OSes really hard, because you need to run all the various test cases with specific error codes on all OSes. Having all errors in the callback makes life easier for us as well. Since we are making EINVAL assert fail in the release builds anyway, why should we bother with returning them immediately? As for uv_timer_start ... I mean, the same holds for uv_tcp_connect, if you do not provide a callback then you are willingly ignoring all the error codes ... I don't think uv_time_start should be different, it will just check the callback against NULL and not call it. We might go for a solution where we return EINVALS immediately, but everything else should be returned in the callback. I don't like the idea, thought, the user will still have to check in two places for error code, entirely defeats the purpose. @huxingyi comment
The APIs without a callback don't need a callback to be added. Our goal is to make it possible for the user to handle all error codes in one place - less code for the user. |
Just got a notification from @saghul that he wants assert fails in debug mode and returns in release mode... |
My bad, I wrote the opposite of what I wanted to say. My point still stands though, I'd want parameter errors to be returned. |
I don't think a "one fits" all solution is good here. If I call
I don't see how returning EINVAL for invalid parameters makes out lives any difficult.
No, errors like that shouldn't go unnoticed. If the user calls
This is the solution I like. It can be made consistent all across the code and it doesn't defeat the purpose IMHO, since you can igonre the return code if you know your parameters are correct. As an example: in pyuv I know the handle type is correct because it belongs to the Python object struct and nobody can change that, and I need to manually create /cc @indutny, @piscisaureus it would be nice to get your 2 cents on this too. |
So returning EINVAL immediately doesn't matter in bindings anyway. But the immediate return code makes code in C more tedious. What about ENOMEM? Should we return ENOMEM immediately? What if it is sometimes because when we try to resize some internal structure it fails not immediately and has to be deferred? |
If return value is kept just for the api consistency, I agree to keep it. I think should not return ENOMEM immediately. if need malloc something, we could let user provider the space like the req struct(e.g. uv_write_t *req). |
Not necessarily. If your application doesn't segfault in debug mode you can choose to ignore the return code in release builds.
I have yet to see an application that properly survives to memory exhaustion :-) I guess I'd defer it to the callback, but I'm not sure if we can in all cases. |
That's not possible, some stuff must be malloc-ed. Like the uv_but_t structs. |
Don't you work with SIP software? I think asteriks doesn't destroy itself when it has not enough. |
Yes, you are right. |
I can live with deffer everything except EINVAL, because that is something that won't ever happen anyway in bindings. Well, in bindings it is actually possible to just deffer it manually. However, it makes it more tedious for C developers, because we still do not get rid of the 2 place error handling. |
Right now, we're inconsistent. Most functions always defer
while others sometimes return an error code. Examples:
Only return errors for bad arguments (EINVAL) with a compile-time option to turn those into asserts for easier debugging.
Fix before v0.12, it lets us simplify error handling in node.
The text was updated successfully, but these errors were encountered: