[gdal-dev] Potential dangerous use of CPLSetErrorHandler()
Even Rouault
even.rouault at mines-paris.org
Thu Feb 17 19:27:13 EST 2011
Le vendredi 18 février 2011 01:02:30, Even Rouault a écrit :
>
> So my suggestion would be not to use a global error handler set when RGDAL
> is initialized, but rather for each binding of the GDAL API, install a
> local error handler with CPLPushErrorHandler(__errorHandler) and uninstall
> it before returning to R with CPLPopErrorHandler()
>
> For example :
>
> SEXP
> RGDAL_GetDescription(SEXP sxpObj) {
>
> void *pGDALObj = getGDALObjPtr(sxpObj);
>
> CPLPushErrorHandler(__errorHandler);
>
> const char *desc = ((GDALMajorObject *)pGDALObj)->GetDescription();
>
> CPLPopErrorHandler();
>
> return(mkString_safe(desc));
>
> }
>
Actually, I'm just thinking that when an error actually triggers, the
CPLPopErrorHandler() will not be called due to the longjmp() done in the error
handler... So you probably needs to call CPLPopErrorHandler() in the error
handler before it to call error() (a quick review of port/cpl_error.cpp leads
me to think this is OK to do so, but this needs certainly some testing)
Or other possibility, it might be dangerous that the error handler interrupts
the GDAL code flow with the longjmp(). It could cause memory leaks or leaves
GDAL in an unstable state (unreleased locks, or whatever). What would be safer
would be that the error handler just stores the error code and/or message, and
just looks at it after the return from the GDAL code.
Something like this :
static CPLErr saved_eErrClass = CE_None;
static saved_err_no = 0;
static char saved_error_msg[2048];
static void
__errorHandler(CPLErr eErrClass, int err_no, const char *msg) {
saved_eErrClass = eErrClass;
saved_err_no = err_no;
/* a mutex could be usefull here to avoid a race condition if 2 threads
trigger the error handler at the same time */
strncpy(saved_error_msg, msg, sizeof(saved_error_msg));
saved_error_msg[sizeof(saved_error_msg)-1] = 0;
}
void installErrorHandler()
{
CPLPushErrorHandler(__errorHandler);
saved_err_no = 0;
}
void uninstallErrorHandlerAndTriggerRError()
{
CPLPopErrorHandler();
if (saved_err_no == CE_Warning) {
warning("\n\tNon-fatal GDAL Error %d: %s\n", saved_err_no,
saved_error_msg);
} else if (saved_err_no == CE_Failure) {
error("\n\tGDAL Error %d: %s\n", saved_err_no, saved_error_msg);
}
}
SEXP
RGDAL_GetDescription(SEXP sxpObj) {
void *pGDALObj = getGDALObjPtr(sxpObj);
installErrorHandler();
const char *desc = ((GDALMajorObject *)pGDALObj)->GetDescription();
uninstallErrorHandlerAndTriggerRError();
return(mkString_safe(desc));
}
Or you could also take the approach of
gdal/swig/include/python/python_exceptions.i where the installed error handler
just forwards CE_Fatal, CE_Warning and CE_Debug to the previous error handler
(or the default one if there's none). After the GDAL call, it just looks at
CPLGetLastErrorType() and if it is CE_Failure, it triggers a Python exception
with the error messages fetched with CPLGetLastErrorMsg().
More information about the gdal-dev
mailing list