Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting rid of Errno and WinError #8885

Merged
merged 29 commits into from
Mar 10, 2020

Conversation

waj
Copy link
Member

@waj waj commented Mar 6, 2020

This PR removes the usage of Errno and WinError as exceptions and instead provides some new classes to distinguish different kind of errors.

This is the hierarchy of the new or updated exceptions:

Exception
+- RuntimeError
+- IO::Error
   +- IO::TimeoutError
   +- File::Error
   |  +- File::NotFoundError
   |  +- File::AccessDeniedError
   |  +- File::AlreadyExistsError
   +- Socket::Error
      +- Socket::ConnectError
      +- Socket::BindError

More subclasses might be added in the future but I think this is a good starting point.

  • Exception now has an optional os_error containing the original error code that anyone can use in case the exception class hierarchy is not enough for proper error handling
  • IO::Timeout is renamed to IO::TimeoutError
  • Errno and Winerror are now enums
  • Exception.from_errno and Exception.from_winerror eases creation of exceptions from OS errors
  • RuntimeError is used, often when there is a system call that shouldn't fail in normal conditions

(closes #8827)

@straight-shoota
Copy link
Member

I don't like having os_error on Exception, spilling into the generic exception namespace when it's use is actually limited to a very small amount of exception types from stdlib and maybe a few exceptions in shards with C bindings. This propert doesn't make sense for the vast majority of exceptions in a typical Crystal program.
Can we only add it selectively on the exception types that use it?

src/exception.cr Outdated Show resolved Hide resolved
@waj
Copy link
Member Author

waj commented Mar 6, 2020

@straight-shoota actually I like it because it ensures uniformity. Many different new exceptions from shards or std might be created from system errors, and so many errors come from the system. I think we should properly document the from_errno and from_winerror methods and encourage their use. And to be honest, I don't feel that adding a field with a value type is spilling the API. It's convenient and unnoticed most of the time.

@jhass
Copy link
Member

jhass commented Mar 6, 2020

As a compromise we could also have module SystemError (or SyscallError or so) for it.

@waj
Copy link
Member Author

waj commented Mar 6, 2020

Ok, agreed. I moved the implementation to the SystemError module and included in RuntimeError and IO::Error. Also added some docs to that module 🙂

src/socket.cr Outdated Show resolved Hide resolved
@oprypin
Copy link
Member

oprypin commented Mar 6, 2020

At a glance, this seems strictly better than #5003
👍

@waj waj force-pushed the feature/no-errno-exceptions branch from 07ef485 to 95484a8 Compare March 9, 2020 13:33
@waj
Copy link
Member Author

waj commented Mar 9, 2020

Thanks @oprypin. I considered some of the ideas and the feedback received on that PR.

Copy link
Contributor

@RX14 RX14 left a comment

Choose a reason for hiding this comment

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

Apologies for the late (post-merge) review, I have been busy recently...

I really like this change! A lot! This is close to how I always wanted Crystal's exception hierarchy. I have a few small issues we might want to change before releasing though:

end

class File::Error < IO::Error
getter file : String
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be nice to have some documentation on these.

Copy link
Member Author

Choose a reason for hiding this comment

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

Of course. Documentation is still missing in many classes and methods.

@@ -142,7 +142,7 @@ class Process
# error:
errno = Errno.value
LibC.pthread_sigmask(LibC::SIG_SETMASK, pointerof(oldmask), nil)
raise Errno.new("fork", errno)
raise RuntimeError.from_errno("fork", errno)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be kind of nice if Process.new on a non-existing file raised File::NotFoundError, i.e. all file-related errors were handled like this, even in mixed-function syscalls.

EHOSTUNREACH ENOTEMPTY EUSERS EDQUOT ESTALE EREMOTE ENOLCK ENOSYS EOVERFLOW
ECANCELED EIDRM ENOMSG EILSEQ EBADMSG EMULTIHOP ENODATA ENOLINK ENOSR ENOSTR
EPROTO ETIME EOPNOTSUPP ENOTRECOVERABLE EOWNERDEAD) %}
{% if LibC.has_constant?(value) %}
Copy link
Contributor

Choose a reason for hiding this comment

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

We should either move this enum into Crystal:: so it is not public or have it be always the same list of errno despite the platform: this enum must have the same members regardless of platform.

Copy link
Member Author

Choose a reason for hiding this comment

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

But not all errno values exist in all platforms. Maybe we can map those to EINVAL. But there should be at least a warning if you try to use any of those values in a platform where the value doesn't exist.

Copy link
Contributor

@RX14 RX14 Mar 14, 2020

Choose a reason for hiding this comment

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

We decided a long time ago that the compile-time API must be identical between platforms so that everything compiles on every platform (as long as it uses documented API)

If the errno value doesn't exist on every platform, it just never gets used, never gets raised, so it can be any value.

I don't think we need warnings for this.

Choose a reason for hiding this comment

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

The existence of an Error number is one part of the problem. There is platform-specific value for some error codes. For example, grep -R "EDEADLK" src/lib_c/*

src/lib_c/aarch64-linux-gnu/c/errno.cr:        EDEADLK = 35
src/lib_c/aarch64-linux-musl/c/errno.cr:       EDEADLK = 35
src/lib_c/amd64-unknown-openbsd/c/errno.cr:    EDEADLK = 11
src/lib_c/arm-linux-gnueabihf/c/errno.cr:      EDEADLK = 35
src/lib_c/i386-linux-gnu/c/errno.cr:           EDEADLK = 35
src/lib_c/i386-linux-musl/c/errno.cr:          EDEADLK = 35
src/lib_c/i686-linux-gnu/c/errno.cr:           EDEADLK = 35
src/lib_c/i686-linux-musl/c/errno.cr:          EDEADLK = 35
src/lib_c/x86_64-darwin/c/errno.cr:            EDEADLK = 11
src/lib_c/x86_64-dragonfly/c/errno.cr:         EDEADLK = 11
src/lib_c/x86_64-freebsd/c/errno.cr:           EDEADLK = 11
src/lib_c/x86_64-linux-gnu/c/errno.cr:         EDEADLK = 35
src/lib_c/x86_64-linux-musl/c/errno.cr:        EDEADLK = 35
src/lib_c/x86_64-macosx-darwin/c/errno.cr:     EDEADLK = 11
src/lib_c/x86_64-openbsd/c/errno.cr:           EDEADLK = 11
src/lib_c/x86_64-portbld-freebsd/c/errno.cr:   EDEADLK = 11
src/lib_c/x86_64-unknown-freebsd/c/errno.cr:   EDEADLK = 11
src/lib_c/x86_64-windows-msvc/c/errno.cr:      EDEADLK = 36

io << ")"
end
raise Errno.new(error_message)
raise RuntimeError.from_errno
Copy link
Member

Choose a reason for hiding this comment

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

Why is this not from_error("execvp")?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think eventually we should make an effort to improve many error messages. I'm not sure it's a good idea to leak the syscall name that failed. Maybe the same error message we have above: "Error executing process"

Copy link
Member

Choose a reason for hiding this comment

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

Yes, that could work. "execvp" has always just been a stand-in for a proper message.

Using an explicit message here would make from_errno without arguments obsolete. I think it should be required to provide at least some message for context information.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is it leaking the syscall name or telling you exactly what failed, which manpage to read and what to search for?

Copy link
Member

Choose a reason for hiding this comment

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

I actually don't think leaking the name is an issue. It's really pretty concise about defining the context of the error. (Btw. it's a libc call, not a syscall)

But in other cases, I think we can perhaps be more expressive about the error context. There's a huge problem space with localization, though. So IDK, but using just the call looks like a pretty reasonable solution, at least for the time being.

paulcsmith added a commit to luckyframework/lucky_cli that referenced this pull request Apr 5, 2020
Errno was renamed in 0.34.x crystal-lang/crystal#8885

Instead we should just ignore non existent file. That way it works in
0.33 and 0.34
paulcsmith added a commit to luckyframework/lucky_cli that referenced this pull request Apr 7, 2020
Errno was renamed in 0.34.x crystal-lang/crystal#8885

Instead we should just ignore non existent file. That way it works in
0.33 and 0.34
paulcsmith added a commit to luckyframework/lucky_cli that referenced this pull request Apr 9, 2020
Errno was renamed in 0.34.x crystal-lang/crystal#8885

Instead we should just ignore non existent file. That way it works in
0.33 and 0.34
marcosdsanchez pushed a commit to marcosdsanchez/scry that referenced this pull request Apr 20, 2020
marcosdsanchez pushed a commit to marcosdsanchez/scry that referenced this pull request Apr 20, 2020
bew pushed a commit to crystal-lang-tools/scry that referenced this pull request Apr 27, 2020
* Errno was replaced by RuntimeError. See crystal-lang/crystal#8885

* Update Dockerfile to use crystal 0.34.0

* AtExitHandlers was moved to crystal namespace see crystal-lang/crystal#8883

* Use new Log
electrocucaracha added a commit to electrocucaracha/cnf-conformance that referenced this pull request May 22, 2020
The crystal 0.34.0 version includes the removal of Errno and WinError
exceptions used by Halite. This last one has released a new version.

crystal-lang/crystal#8885

Signed-off-by: Victor Morales <v.morales@samsung.com>
dscottboggs added a commit to dscottboggs/cossack that referenced this pull request Jul 8, 2020
[Crystal PR #8885](crystal-lang/crystal#8885) changed the name of `IO::Timeout` to `IO::TimeoutError`
This was referenced Dec 3, 2020
taylor pushed a commit to lfn-cnti/certification that referenced this pull request Apr 20, 2021
The crystal 0.34.0 version includes the removal of Errno and WinError
exceptions used by Halite. This last one has released a new version.

crystal-lang/crystal#8885

Signed-off-by: Victor Morales <v.morales@samsung.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[RFC] Getting rid of Errno
9 participants