Skip to content

Getting rid of Errno and WinError#8885

Merged
waj merged 29 commits into
crystal-lang:masterfrom
waj:feature/no-errno-exceptions
Mar 10, 2020
Merged

Getting rid of Errno and WinError#8885
waj merged 29 commits into
crystal-lang:masterfrom
waj:feature/no-errno-exceptions

Conversation

@waj
Copy link
Copy Markdown
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
Copy Markdown
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?

Comment thread src/exception.cr Outdated
@waj
Copy link
Copy Markdown
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
Copy Markdown
Member

jhass commented Mar 6, 2020

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

@waj
Copy link
Copy Markdown
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 🙂

Comment thread src/crystal/system/win32/file.cr Outdated
Comment thread src/socket.cr Outdated
@oprypin
Copy link
Copy Markdown
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
Copy Markdown
Member Author

waj commented Mar 9, 2020

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

@waj waj added this to the 0.34.0 milestone Mar 10, 2020
@waj waj merged commit 6774221 into crystal-lang:master Mar 10, 2020
@waj waj deleted the feature/no-errno-exceptions branch March 10, 2020 14:54
Copy link
Copy Markdown
Member

@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:

Comment thread src/file/error.cr
end

class File::Error < IO::Error
getter file : String
Copy link
Copy Markdown
Member

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
Copy Markdown
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.

Comment thread src/process.cr
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
Copy Markdown
Member

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.

Comment thread src/errno.cr
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
Copy Markdown
Member

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
Copy Markdown
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
Copy Markdown
Member

@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.

Copy link
Copy Markdown

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

Comment thread src/process.cr
io << ")"
end
raise Errno.new(error_message)
raise RuntimeError.from_errno
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

@bcardiff bcardiff added the breaking-change May have breaking effect in some edge cases. Mostly negligible, but still noteworthy. label Mar 27, 2020
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
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

Labels

breaking-change May have breaking effect in some edge cases. Mostly negligible, but still noteworthy. kind:refactor topic:stdlib

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[RFC] Getting rid of Errno

9 participants