Re: [PPL-devel] GMP memory allocation problem in SWI-Prolog 5.6.38 and following versions

Jan Wielemaker wrote:
Hi Roberto,
On Tuesday 02 October 2007 07:42, Roberto Bagnara wrote:
Starting from version 5.6.38 of SWI-Prolog, applications using SWI-Prolog and GMP started failing. The problem is due to the fact that SWI-Prolog overrides the memory allocation functions used by GMP by calling mp_set_memory_functions(). I have observed two instances of the problem, one of which was reported by users of the Parma Polyhedra Library:
- a variable allocated in the C++ code before SWI-Prolog changes the
allocation functions is destroyed after SWI-Prolog has done that (this results in a segmentation fault); 2) while executing the C++ code, the realloc function set by SWI-Prolog is called (this results into an invalid pointer detected by glibc).
Both instances are caused by the fact that memory allocated by GMP's default functions is reallocated/freed by SWI-Prolog's functions. Something that is guaranteed to cause problems. However, even if all memory allocation was done by the SWI-Prolog's functions, I am sure we would have other troubles. I am not sure what is the way out, but the problem is rather serious. All the best,
Rebinding the allocation functions is done to allow SWI-Prolog to detect and act upon GMP memory overflows. Unfortunately GMP is very weak in allowing applications to act gracefully on overflows. I see no reason for problems as long as you initialize Prolog before touching any GMP number.
This is not always possible, unfortunately.
If that should be the case anyway, we must add something to the foreign interface that allows for initializing Prolog without rebinding the GMP allocation.
This is certainly useful. But what happens if, in some computation in the C++ code that has nothing to do with SWI-Prolog, pl_abort() is called (I am looking at pl-gmp.c)? Perhaps I am missing something, but this will break the overall application completely. I think SWI-Prolog should have his own private version of GMP if it wants to change the allocation functions. In other words, setting the allocation functions on a shared GMP can only be done at the overall application level: if every component does that on its own, things are going to break. All the best,
Roberto

Roberto,
On Tuesday 02 October 2007 10:25, Roberto Bagnara wrote:
Jan Wielemaker wrote:
Rebinding the allocation functions is done to allow SWI-Prolog to detect and act upon GMP memory overflows. Unfortunately GMP is very weak in allowing applications to act gracefully on overflows. I see no reason for problems as long as you initialize Prolog before touching any GMP number.
This is not always possible, unfortunately.
Most of the time it is, certainly with open source :-)
If that should be the case anyway, we must add something to the foreign interface that allows for initializing Prolog without rebinding the GMP allocation.
This is certainly useful. But what happens if, in some computation in the C++ code that has nothing to do with SWI-Prolog, pl_abort() is called (I am looking at pl-gmp.c)?
Actually it never calls pl_abort(), unless something is really wrong. It does call longjmp() back to the Prolog context :-) Note that it only does so if it cannot allocate. The only thing you are allowed to do from the GMP allocation functions is stop the process. Prolog knows a few cases where it can safely do better (discussed on the GMP mailinglist), by throwing a Prolog resource_error exception.
Perhaps I am missing something, but this will break the overall application completely. I think SWI-Prolog should have his own private version of GMP if it wants to change the allocation functions.
Thats not possible, at least not using ELF binaries as in Linux. Unless you make a copy of the GMP sources and rename all global identifiers.
In other words, setting the allocation functions on a shared GMP can only be done at the overall application level: if every component does that on its own, things are going to break.
True. Same goes for signals and a few more of these things. The thing is, the way Prolog uses GMP, it *can* deal with GMP caused memory overflows. This can be exploited as long as Prolog is the only component in the application using GMP or at least it is possible to initialise Prolog GMP handling before GMP numbers are used elsewhere. This deals with most applications.
Anyway for the others, I added
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, TRUE)
if you want Prolog's GMP allocation initialised *now* (without initializing the rest of SWI-Prolog). or
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE)
If you want to stop Prolog from setting the allocation functions. Must be called before PL_initialise(), of course.
I also added some comments to the section of the manual dealing with GMP and Prolog. On Windows the situation is entirely different :-(
Please install from CVS. Do not forget a "make clean"!
Cheers --- Jan

Jan Wielemaker wrote:
Roberto,
On Tuesday 02 October 2007 10:25, Roberto Bagnara wrote:
Jan Wielemaker wrote:
Rebinding the allocation functions is done to allow SWI-Prolog to detect and act upon GMP memory overflows. Unfortunately GMP is very weak in allowing applications to act gracefully on overflows. I see no reason for problems as long as you initialize Prolog before touching any GMP number.
This is not always possible, unfortunately.
Most of the time it is, certainly with open source :-)
True, but having to change code just for that... Think about global variables. If you have something like
mpz_class n;
in C++ code I don't know a reliable way to initialize Prolog before the constructor is run.
If that should be the case anyway, we must add something to the foreign interface that allows for initializing Prolog without rebinding the GMP allocation.
This is certainly useful. But what happens if, in some computation in the C++ code that has nothing to do with SWI-Prolog, pl_abort() is called (I am looking at pl-gmp.c)?
Actually it never calls pl_abort(), unless something is really wrong. It does call longjmp() back to the Prolog context :-) Note that it only does so if it cannot allocate.
Yes. But I think we disagree with our definition of "really wrong." The PPL is exception-safe, meaning that the application can be programmed in a way that, whenever allocation fails (something that does happen when working with [big] collections of [big] polyhedra) all the memory used by the failed (sub)computation is automatically discarded, a simplification takes place, and computation restart. This is only possible if the overall application is the only entity allowed to change GMP's allocation function.
The only thing you are allowed to do from the GMP allocation functions is stop the process.
This is not my understanding: where did you get this restriction from?
Prolog knows a few cases where it can safely do better (discussed on the GMP mailinglist), by throwing a Prolog resource_error exception.
I am not contending this. What I am saying is that in a complex application, Prolog can be one of several components all using GMP. Thus, the decision about which allocation functions are adequate can only be taken at the application level, not at the component level.
True. Same goes for signals and a few more of these things. The thing is, the way Prolog uses GMP, it *can* deal with GMP caused memory overflows. This can be exploited as long as Prolog is the only component in the application using GMP or at least it is possible to initialise Prolog GMP handling before GMP numbers are used elsewhere. This deals with most applications.
OK: we agree on that.
Anyway for the others, I added
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, TRUE)
if you want Prolog's GMP allocation initialised *now* (without initializing the rest of SWI-Prolog). or
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE)
If you want to stop Prolog from setting the allocation functions. Must be called before PL_initialise(), of course.
I see how to use that in the foreign-calls-Prolog case. How can I achieve that in the Prolog-calls-foreign case (which is the one I am mostly interested in)?
I also added some comments to the section of the manual dealing with GMP and Prolog. On Windows the situation is entirely different :-(
Well, I am not so worried about Windows :-)
Please install from CVS. Do not forget a "make clean"!
Right. I hope people packaging SWI-Prolog for the various distributions do not select a version in the range 5.6.38-5.6.43. Cheers,
Roberto

On Wednesday 03 October 2007 14:37, Roberto Bagnara wrote:
Jan Wielemaker wrote:
Most of the time it is, certainly with open source :-)
True, but having to change code just for that... Think about global variables. If you have something like
mpz_class n;
in C++ code I don't know a reliable way to initialize Prolog before the constructor is run.
I never said I liked C++ :-)
Actually it never calls pl_abort(), unless something is really wrong. It does call longjmp() back to the Prolog context :-) Note that it only does so if it cannot allocate.
Yes. But I think we disagree with our definition of "really wrong." The PPL is exception-safe, meaning that the application can be programmed in a way that, whenever allocation fails (something that does happen when working with [big] collections of [big] polyhedra) all the memory used by the failed (sub)computation is automatically discarded, a simplification takes place, and computation restart. This is only possible if the overall application is the only entity allowed to change GMP's allocation function.
The only thing you are allowed to do from the GMP allocation functions is stop the process.
This is not my understanding: where did you get this restriction from?
Quoting from the GMP manual (GMP 4.2.1, SuSE RPM):
---------------------------------------------------------------- There's currently no defined way for the allocation functions to recover from an error such as out of memory, they must terminate program execution. A `longjmp' or throwing a C++ exception will have undefined results. This may change in the future. ----------------------------------------------------------------
So, I wonder how you safely recover. Surely if you call Prolog, which calls a GMP function, which gets you using longjmp or a C++ exception back to the C++ program, you haven't made Prolog particulary happy. Seems from the rest that you are calling C++ from Prolog, which might make things a bit better ...
Prolog knows a few cases where it can safely do better (discussed on the GMP mailinglist), by throwing a Prolog resource_error exception.
I am not contending this. What I am saying is that in a complex application, Prolog can be one of several components all using GMP. Thus, the decision about which allocation functions are adequate can only be taken at the application level, not at the component level.
True. The way GMP is designed doesn't make it easy for us though. Ideally I would of course call GMP functions, check some return code and map that to a Prolog exception if there is a problem. GMP however doesn't give error codes :-(
Anyway for the others, I added
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, TRUE)
if you want Prolog's GMP allocation initialised *now* (without initializing the rest of SWI-Prolog). or
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE)
If you want to stop Prolog from setting the allocation functions. Must be called before PL_initialise(), of course.
I see how to use that in the foreign-calls-Prolog case. How can I achieve that in the Prolog-calls-foreign case (which is the one I am mostly interested in)?
You mean you load a shared object into Prolog? Good! That means however that the C++ constructors are executed *after* the Prolog initialization, so Prolog can (and already has) changed the allocation functions. In fact it also created a couple of GMP numbers, but as these are used read-only throughout the process, this shouldn't matter. You should be able to change the allocation functions using the mp_set_memory_functions() at any point in time as long as Prolog is not doing any active GMP computation at the same time (which should be fine as long as you have no threads running). Prolog does GMP computation, but at the end it copies the result to the Prolog stack in a format that allows for direct use as *input* argument to GMP functions. Except during computation, it *never* has normally allocated GMP numbers around.
This is also the trick around errors: if it gets a GMP allocation error it will deallocate all GMP allocation for that thread. Maybe we should improve here, ensuring the Prolog allocation functions only do their work if the GMP operation is called from Prolog.
I also added some comments to the section of the manual dealing with GMP and Prolog. On Windows the situation is entirely different :-(
Well, I am not so worried about Windows :-)
Lucky you!
Please install from CVS. Do not forget a "make clean"!
Right. I hope people packaging SWI-Prolog for the various distributions do not select a version in the range 5.6.38-5.6.43.
Some of these problems cannot be avoided. Let us just try to fix them. I'm don't think we have right now :-( Hope you understand the Prolog viewpoint a bit better now.
Cheers --- Jan

Jan Wielemaker wrote:
The only thing you are allowed to do from the GMP allocation functions is stop the process.
This is not my understanding: where did you get this restriction from?
Quoting from the GMP manual (GMP 4.2.1, SuSE RPM):
There's currently no defined way for the allocation functions to recover from an error such as out of memory, they must terminate program execution. A `longjmp' or throwing a C++ exception will have undefined results. This may change in the future.
I see. Well, I think the situation is that they do not make promises (not now) but either they will need to do so if they want a decent C++ interface. My experience is that when you get an out of memory error from GMP it is perfectly safe to discard the GMP object involved and computation can continue: we are testing this all the time and it never ever failed.
So, I wonder how you safely recover. Surely if you call Prolog, which calls a GMP function, which gets you using longjmp or a C++ exception back to the C++ program, you haven't made Prolog particulary happy. Seems from the rest that you are calling C++ from Prolog, which might make things a bit better ...
Yes, we are calling C++ from Prolog.
Anyway for the others, I added
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, TRUE)
if you want Prolog's GMP allocation initialised *now* (without initializing the rest of SWI-Prolog). or
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE)
If you want to stop Prolog from setting the allocation functions. Must be called before PL_initialise(), of course.
I see how to use that in the foreign-calls-Prolog case. How can I achieve that in the Prolog-calls-foreign case (which is the one I am mostly interested in)?
You mean you load a shared object into Prolog? Good!
No, I am having problems with libraries statically linked to the SWI-Prolog system. These libraries (written in C++) use several statically allocated GMP numbers. When the system starts, the statically allocated variables are initialized with the GMP's memory allocation function. Then SWI-Prolog is given control and the allocation functions are changed. Then the system exits and the statically allocated variables are destroyed with the wrong (i.e., SWI-Prolog's) allocation function: which results into a segmentation fault. This change you have made to SWI-Prolog is really breaking everything here. Cheers,
Roberto

On Monday 08 October 2007 11:45, Roberto Bagnara wrote:
Jan Wielemaker wrote:
The only thing you are allowed to do from the GMP allocation functions is stop the process.
This is not my understanding: where did you get this restriction from?
Quoting from the GMP manual (GMP 4.2.1, SuSE RPM):
There's currently no defined way for the allocation functions to recover from an error such as out of memory, they must terminate program execution. A `longjmp' or throwing a C++ exception will have undefined results. This may change in the future.
I see. Well, I think the situation is that they do not make promises (not now) but either they will need to do so if they want a decent C++ interface. My experience is that when you get an out of memory error from GMP it is perfectly safe to discard the GMP object involved and computation can continue: we are testing this all the time and it never ever failed.
My guess is you will have some memory loss and possibly inconsistency in GMP objects who's modification caused the overflow.
So, I wonder how you safely recover. Surely if you call Prolog, which calls a GMP function, which gets you using longjmp or a C++ exception back to the C++ program, you haven't made Prolog particulary happy. Seems from the rest that you are calling C++ from Prolog, which might make things a bit better ...
Yes, we are calling C++ from Prolog.
Anyway for the others, I added
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, TRUE)
if you want Prolog's GMP allocation initialised *now* (without initializing the rest of SWI-Prolog). or
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE)
If you want to stop Prolog from setting the allocation functions. Must be called before PL_initialise(), of course.
I see how to use that in the foreign-calls-Prolog case. How can I achieve that in the Prolog-calls-foreign case (which is the one I am mostly interested in)?
You mean you load a shared object into Prolog? Good!
No, I am having problems with libraries statically linked to the SWI-Prolog system. These libraries (written in C++) use several statically allocated GMP numbers. When the system starts, the statically allocated variables are initialized with the GMP's memory allocation function. Then SWI-Prolog is given control and the allocation functions are changed. Then the system exits and the statically allocated variables are destroyed with the wrong (i.e., SWI-Prolog's) allocation function: which results into a segmentation fault. This change you have made to SWI-Prolog is really breaking everything here.
I'm not getting it. I think there are two situations: you embed Prolog, so you call PL_initialise() and you can call the PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE) before it or you keep the Prolog toplevel and you load your code as a .so file into Prolog. I think both work fine, even with global C++ initializers. What am I missing? Please describe how you link the two together and what is the main routine from C(++).
Cheers --- Jan

Jan Wielemaker wrote:
Anyway for the others, I added
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, TRUE)
if you want Prolog's GMP allocation initialised *now* (without initializing the rest of SWI-Prolog). or
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE)
If you want to stop Prolog from setting the allocation functions. Must be called before PL_initialise(), of course.
I see how to use that in the foreign-calls-Prolog case. How can I achieve that in the Prolog-calls-foreign case (which is the one I am mostly interested in)?
You mean you load a shared object into Prolog? Good!
No, I am having problems with libraries statically linked to the SWI-Prolog system. These libraries (written in C++) use several statically allocated GMP numbers. When the system starts, the statically allocated variables are initialized with the GMP's memory allocation function. Then SWI-Prolog is given control and the allocation functions are changed. Then the system exits and the statically allocated variables are destroyed with the wrong (i.e., SWI-Prolog's) allocation function: which results into a segmentation fault. This change you have made to SWI-Prolog is really breaking everything here.
I'm not getting it. I think there are two situations: you embed Prolog, so you call PL_initialise() and you can call the PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE) before it or you keep the Prolog toplevel and you load your code as a .so file into Prolog. I think both work fine, even with global C++ initializers. What am I missing?
Probably the fact that the constructors of global variables are all run (in unspecified order) before main() is given control. You can compile the following with
g++ -W -Wall test.cc
and see.
#include <iostream>
class Foo { public: Foo() { std::cout << "Foo::Foo() called." << std::endl; } };
Foo foo;
int main() { // Calling PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE) here would not help. std::cout << "main() called." << std::endl; }
Of course, in the real code we have mpz_class, mpq_class and so forth instead of Foo. All the best,
Roberto

On Monday 08 October 2007 14:23, Roberto Bagnara wrote:
Jan Wielemaker wrote:
Anyway for the others, I added
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, TRUE)
if you want Prolog's GMP allocation initialised *now* (without initializing the rest of SWI-Prolog). or
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE)
If you want to stop Prolog from setting the allocation functions. Must be called before PL_initialise(), of course.
I see how to use that in the foreign-calls-Prolog case. How can I achieve that in the Prolog-calls-foreign case (which is the one I am mostly interested in)?
You mean you load a shared object into Prolog? Good!
No, I am having problems with libraries statically linked to the SWI-Prolog system. These libraries (written in C++) use several statically allocated GMP numbers. When the system starts, the statically allocated variables are initialized with the GMP's memory allocation function. Then SWI-Prolog is given control and the allocation functions are changed. Then the system exits and the statically allocated variables are destroyed with the wrong (i.e., SWI-Prolog's) allocation function: which results into a segmentation fault. This change you have made to SWI-Prolog is really breaking everything here.
I'm not getting it. I think there are two situations: you embed Prolog, so you call PL_initialise() and you can call the PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE) before it or you keep the Prolog toplevel and you load your code as a .so file into Prolog. I think both work fine, even with global C++ initializers. What am I missing?
Probably the fact that the constructors of global variables are all run (in unspecified order) before main() is given control. You can compile the following with
This is the case if you embed SWI-Prolog, but in main() you call
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE)
and Prolog won't touch the GMP allocator anymore. If you link your code to a shared object the global constructores are executed at the moment you load the .so file into Prolog. Still puzzled ...
--- Jan

Jan Wielemaker wrote:
On Monday 08 October 2007 14:23, Roberto Bagnara wrote:
Jan Wielemaker wrote:
> Anyway for the others, I added > > PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, TRUE) > > if you want Prolog's GMP allocation initialised *now* (without > initializing the rest of SWI-Prolog). or > > PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE) > > If you want to stop Prolog from setting the allocation functions. > Must be called before PL_initialise(), of course. I see how to use that in the foreign-calls-Prolog case. How can I achieve that in the Prolog-calls-foreign case (which is the one I am mostly interested in)?
You mean you load a shared object into Prolog? Good!
No, I am having problems with libraries statically linked to the SWI-Prolog system. These libraries (written in C++) use several statically allocated GMP numbers. When the system starts, the statically allocated variables are initialized with the GMP's memory allocation function. Then SWI-Prolog is given control and the allocation functions are changed. Then the system exits and the statically allocated variables are destroyed with the wrong (i.e., SWI-Prolog's) allocation function: which results into a segmentation fault. This change you have made to SWI-Prolog is really breaking everything here.
I'm not getting it. I think there are two situations: you embed Prolog, so you call PL_initialise() and you can call the PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE) before it or you keep the Prolog toplevel and you load your code as a .so file into Prolog. I think both work fine, even with global C++ initializers. What am I missing?
Probably the fact that the constructors of global variables are all run (in unspecified order) before main() is given control. You can compile the following with
This is the case if you embed SWI-Prolog, but in main() you call
PL_action(PL_GMP_SET_ALLOC_FUNCTIONS, FALSE)
and Prolog won't touch the GMP allocator anymore. If you link your code to a shared object the global constructores are executed at the moment you load the .so file into Prolog. Still puzzled ...
I am not linking dynamically (this does not work everywhere), I am linking statically. The Automake rules I use are of the form
ppl_pl$(EXEEXT): ppl_swiprolog.lo ppl_pl.o plld -cc $(CC) -c++ $(CXX) -ld $(CXX) \ -ld-options`echo '' $(AM_CXXFLAGS) $(CXXFLAGS) | tr " " "/"` \ -o $@ ppl_swiprolog.o ppl_pl.o \ -L$(top_builddir)/src/.libs -lppl \ $(WATCHDOG_LINK_OPTIONS) \ @extra_libraries@

Jan Wielemaker wrote:
On Tuesday 02 October 2007 10:25, Roberto Bagnara wrote:
Perhaps I am missing something, but this will break the overall application completely. I think SWI-Prolog should have his own private version of GMP if it wants to change the allocation functions.
Thats not possible, at least not using ELF binaries as in Linux. Unless you make a copy of the GMP sources and rename all global identifiers.
It can be done without copying the sources: see
http://www.cs.unipr.it/pipermail/ppl-devel/2007-October/011244.html
I am more and more convinced that only the overall application should be allowed to invoke mp_memory_functions(): any component doing so can only do that reliably with a private GMP version. Cheers,
Roberto

Hi Roberto,
On Monday 08 October 2007 11:57, Roberto Bagnara wrote:
Jan Wielemaker wrote:
On Tuesday 02 October 2007 10:25, Roberto Bagnara wrote:
Perhaps I am missing something, but this will break the overall application completely. I think SWI-Prolog should have his own private version of GMP if it wants to change the allocation functions.
Thats not possible, at least not using ELF binaries as in Linux. Unless you make a copy of the GMP sources and rename all global identifiers.
It can be done without copying the sources: see
http://www.cs.unipr.it/pipermail/ppl-devel/2007-October/011244.html
You're free to do with GMP and/or SWI-Prolog whatever you wish, but I'm not going that route. Smells like a can or worms to turn this into a portable solution and 99% of the users simply want to start Prolog and use the system wide shared gmp object.
I am more and more convinced that only the overall application should be allowed to invoke mp_memory_functions(): any component doing so can only do that reliably with a private GMP version. Cheers,
Even that is probably generally not safe :-(
--- Jan
participants (2)
-
Jan Wielemaker
-
Roberto Bagnara