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

throw exception on C_NULL return from GSL allocation functions #9

Closed
stevengj opened this issue Apr 4, 2013 · 3 comments
Closed

throw exception on C_NULL return from GSL allocation functions #9

stevengj opened this issue Apr 4, 2013 · 3 comments

Comments

@stevengj
Copy link
Member

stevengj commented Apr 4, 2013

When a GSL allocation function (one of the many functions that returns a pointer to an opaque pointer) returns C_NULL, an exception (whose type is determined by checking the gsl_errno global variable) should be thrown.

@jiahao
Copy link
Collaborator

jiahao commented Apr 4, 2013

I can certainly modify the *_alloc functions to test the output of ccall for C_NULL, but I am not sure how to read the gsl_errno global variable to throw the proper exception. The other functions currently throw GSL_ERROR, which is a very simple Exception containing the error code. I'm also not familiar with how to use the Exception type within Julia to, say, automatically handle a GSL_ERROR by passing its errno to strerror to get a human-readable error.

@stevengj
Copy link
Member Author

stevengj commented Apr 4, 2013

Probably the best thing is to register a GSL error handler that throws an exception. Then you won't need to check the return values at all.

In fact, because looking more closely at the GSL manual, it seems that GSL calls abort by default on an error, which is definitely not what you want to do. So, you either have to declare an error handler which throws an exception or you have to call gsl_set_error_handler_off when the module is imported and then check return codes religiously.

The following code creates an error handler that converts a GSL error into a corresponding Julia exception and throws it:

function error_handler(reason::Ptr{Uint8}, file::Ptr{Uint8}, line, errno)
    if errno == 0; return; end # GSL_SUCCESS
    if errno == 1 # GSL_EDOM: input domain error, e.g sqrt(-1)
        throw(DomainError())
    elseif errno == 2 || errno == 16
        # GSL_ERANGE: output range error, e.g. exp(1e100)
        # or GSL_EOVRFLW: overflow
        throw(OverflowError())
    elseif errno == 4 # GSL_EINVAL: invalid argument supplied by user
        throw(ArgumentError(string(bytestring(reason), " at ",
                                   bytestring(file), ":", line)))
    elseif errno == 8 # GSL_ENOMEM: malloc failed
        throw(MemoryError())
    elseif errno == 12 # GSL_EZERODIV: tried to divide by zero
        throw(DivideByZeroError())
    elseif errno == 19 # GSL_EBADLEN: matrix, vector lengths are not conformant
        throw(BoundsError())
    elseif errno == 32 # GSL_EOF: end of file
        throw(EOFError())
    else # convert all other errors into generic ErrorException
        error(bytestring(ccall((:gsl_strerror,:libgsl), Ptr{Uint8}, (Cint,),
                               errno)), " -- ",
              bytestring(reason), " at ", bytestring(file), ":", line)
    end
    return
end

ccall((:gsl_set_error_handler,:libgsl), Ptr{Void}, (Ptr{Void},),
      cfunction(error_handler, Void, (Ptr{Uint8},Ptr{Uint8},Cint,Cint)))

For example, if you do:

ccall((:gsl_sf_pow_int,:libgsl), Cdouble, (Cdouble,Cint), 0.0, -1)

without the error handler above, then GSL aborts (i.e. crashes the program) with the message gsl: pow_int.c:45: ERROR: overflow. With the error handler, it throws an OverflowError.

(It's not completely clear to me if we want to throw an OverflowError on GSL_EOVRFLW, or have error_handler silently return and let GSL return whatever it wants. For example,in the above case, it will return Inf, which is the correct result, if you change error_handler to return. However, I'm not completely sure whether all GSL functions return a sensible result on GSL_EOVRFLW errors. It is probably safer to throw an exception for now, and revisit the issue if anyone complains.)

You can just insert the above code into one of the GSL.jl files so that it is executed when the GSL module is imported.

@jiahao
Copy link
Collaborator

jiahao commented Apr 4, 2013

This looks great, I'll put it in

jiahao added a commit that referenced this issue Apr 4, 2013
@jiahao jiahao closed this as completed Apr 4, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants